com.qq.tars.maven.gensrc.TarsBuildMojo.java Source code

Java tutorial

Introduction

Here is the source code for com.qq.tars.maven.gensrc.TarsBuildMojo.java

Source

/**
 * Tencent is pleased to support the open source community by making Tars available.
 *
 * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * https://opensource.org/licenses/BSD-3-Clause
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.qq.tars.maven.gensrc;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DefaultArtifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
import org.apache.maven.artifact.resolver.ArtifactCollector;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Execute;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import org.apache.maven.shared.dependency.tree.DependencyNode;
import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
import org.apache.maven.shared.mapping.MappingUtils;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.UnArchiver;
import org.codehaus.plexus.archiver.manager.ArchiverManager;
import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.ContextException;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.InterpolationFilterReader;

import com.qq.tars.maven.model.Dependency;
import com.qq.tars.maven.script.Platform;
import com.qq.tars.maven.util.ArchiveEntryUtils;
import com.qq.tars.maven.util.XMLConfigElement;
import com.qq.tars.maven.util.XMLConfigFile;

@Mojo(name = "build", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true)
@Execute(phase = LifecyclePhase.PACKAGE)
public class TarsBuildMojo extends AbstractMojo implements Contextualizable {

    @Parameter(defaultValue = "${project}")
    private MavenProject project;

    @Parameter(defaultValue = "${session}")
    private MavenSession session;

    @Parameter(alias = "", defaultValue = "${project.build.directory}/${project.build.finalName}")
    private File war;

    @Parameter
    protected String outputFileNameMapping;

    @Parameter(defaultValue = "default")
    protected String repositoryLayout;

    @Parameter(defaultValue = "false")
    protected boolean useTimestampInSnapshotFileName;

    @Parameter(defaultValue = "${localRepository}", readonly = true, required = true)
    protected ArtifactRepository localRepository;

    @Parameter(defaultValue = "${project.remoteArtifactRepositories}", readonly = true, required = true)
    protected List remoteRepositories;

    @Component
    private ArtifactFactory artifactFactory;
    @Component
    protected ArtifactResolver artifactResolver;
    @Component
    protected ArtifactRepositoryFactory artifactRepositoryFactory;
    @Component
    protected ArtifactMetadataSource artifactMetadataSource;
    @Component
    protected MavenProjectBuilder mavenProjectBuilder;
    @Component
    protected DependencyTreeBuilder dependencyTreeBuilder;
    @Component
    protected ArtifactCollector artifactCollector;
    @Component
    protected ArchiverManager archiverManager;

    protected PlexusContainer container;

    @Parameter
    private String platformName;

    @Parameter
    private String tarsGroupId;
    @Parameter
    private String tarsArtifactId;
    @Parameter
    private String tarsVersion;

    private static final String TARS_PROTOCOL = "tars";
    private static final String TUP_PROTOCOL = "tup";
    private static final String MAIN_CLASS = "com.qq.tars.server.startup.Main";

    // -----------------------------------------------------------------------
    // Plexus Implementation
    // -----------------------------------------------------------------------
    /**
     * {@inheritDoc}
     */
    public void contextualize(Context context) throws ContextException {
        container = (PlexusContainer) context.get(PlexusConstants.PLEXUS_KEY);
    }

    protected static final String SCOPE_DEFAULT_PATTERN = "^((?!test).)*$";
    private String includedScope = SCOPE_DEFAULT_PATTERN;

    private List<Dependency> dependencyList = new ArrayList<Dependency>();

    private Pattern pattern;

    protected boolean includedScope(final String scope) {
        if (pattern == null) {
            pattern = Pattern.compile(includedScope, Pattern.CASE_INSENSITIVE);
        }
        return scope != null ? pattern.matcher(scope).matches() : Boolean.TRUE;
    }

    public void execute() throws MojoExecutionException, MojoFailureException {
        getLog().info("Start run build...");
        try {
            if (platformName == null) {
                String osName = System.getProperty("os.name");
                if (osName.toLowerCase().contains(Platform.WINDOWS_NAME)) {
                    platformName = Platform.WINDOWS_NAME;
                } else {
                    platformName = Platform.UNIX_NAME;
                }
            }

            File targetDir = new File(project.getBuild().getDirectory());
            File tarsDir = new File(targetDir, "tars");
            if (tarsDir.exists()) {
                FileUtils.forceDelete(tarsDir);
            }
            tarsDir.mkdir();

            File binDir = new File(tarsDir, "bin");
            binDir.mkdir();

            File confDir = new File(tarsDir, "conf");
            confDir.mkdir();

            File dataDir = new File(tarsDir, "data");
            dataDir.mkdir();

            File binAppsDir = new File(binDir, "apps" + File.separator + "ROOT");
            binAppsDir.mkdir();
            FileUtils.copyDirectory(war, binAppsDir);

            File binConfDir = new File(binDir, "conf");
            binConfDir.mkdir();

            File binLogDir = new File(binDir, "log");
            binLogDir.mkdir();

            File libDir = new File(binDir, "lib");
            libDir.mkdir();

            String app = getApp();
            String server = getServer();

            if (app == null || server == null) {
                throw new MojoExecutionException("Failed to build app server is null.");
            }

            String configFileName = String.format("%s.%s.%s", app, server, "config.conf");
            File configFile = new File(confDir, configFileName);
            getLog().info(String.format("Create config file %s ", configFile.getCanonicalPath()));
            createConfigScript(binDir, dataDir, binLogDir, configFile);

            installLibDependency(libDir);

            getLog().info(String.format("Create platform %s start script...", platformName));
            Platform platform = Platform.getInstance(platformName);
            String binscript = createBinScript(platform, libDir, binDir, configFile);
            getLog().info("Start Script in " + binscript);
        } catch (Exception e) {
            getLog().error(e);
            throw new MojoExecutionException("Failed to tars build", e);
        }
    }

    private void installLibDependency(File libDir) throws Exception {
        ArtifactRepository artifactRepository = getArtifactRepository(libDir.getAbsolutePath());

        getLog().info("Install tars artifact...");
        Set<Artifact> projectDependency = project.getDependencyArtifacts();
        Artifact tarsArtifact = null;
        for (Artifact artifact : projectDependency) {
            if (artifact.getArtifactId().contains("tars")) {
                getLog().info(String.format("Use tars artifact %s-%s-%s", artifact.getGroupId(),
                        artifact.getArtifactId(), artifact.getVersion()));
                tarsArtifact = artifact;
                break;
            }
        }

        if (tarsArtifact == null) {
            tarsArtifact = artifactFactory.createArtifact(tarsGroupId, tarsArtifactId, tarsVersion,
                    Artifact.SCOPE_COMPILE, "jar");
        }
        artifactResolver.resolve(tarsArtifact, remoteRepositories, localRepository);
        installArtifact(tarsArtifact, libDir, artifactRepository, useTimestampInSnapshotFileName);

        MavenProject coreProject = mavenProjectBuilder.buildFromRepository(tarsArtifact, remoteRepositories,
                localRepository);
        final ArtifactFilter artifactFilter = new ScopeArtifactFilter(DefaultArtifact.SCOPE_COMPILE) {

            @Override
            public boolean include(Artifact artifact) {
                return includedScope(artifact.getScope());
            }
        };

        final Set<Artifact> artifacts = new HashSet<Artifact>();
        final DependencyNode rootNode = dependencyTreeBuilder.buildDependencyTree(coreProject, localRepository,
                artifactFactory, artifactMetadataSource, artifactFilter, artifactCollector);

        for (final Iterator<?> iterator = rootNode.getChildren().iterator(); iterator.hasNext();) {
            final DependencyNode child = (DependencyNode) iterator.next();
            collect(child, artifacts);
        }
        if (artifacts == null || artifacts.isEmpty()) {
            throw new MojoExecutionException("Unable to install tars artifact dependency.");
        }
        for (final Artifact artifact : artifacts) {
            try {
                artifactResolver.resolve(artifact, remoteRepositories, localRepository);
            } catch (final ArtifactResolutionException e) {
                throw new MojoExecutionException("Unable to resolve " + artifact, e);
            } catch (final ArtifactNotFoundException e) {
                throw new MojoExecutionException("Unable to resolve " + artifact, e);
            }
            installArtifact(artifact, libDir, artifactRepository, useTimestampInSnapshotFileName);
        }
    }

    /**
     * extract specified artifact. 
     *  
     * @param destination destination folder 
     * @param artifactFile 
     * @throws NoSuchArchiverException 
     * @throws ArchiverException 
     * @throws MojoExecutionException 
     */
    private void unArchiveArtifact(final File destination, final File artifactFile)
            throws MojoExecutionException, IOException {
        try {
            final UnArchiver unArchiver = archiverManager.getUnArchiver(artifactFile);
            unArchiver.setSourceFile(artifactFile);
            unArchiver.setDestDirectory(destination);
            unArchiver.setOverwrite(true);
            unArchiver.extract();
        } catch (final NoSuchArchiverException e) {
            throw new MojoExecutionException("Unable to unarchive " + artifactFile.getName(), e);
        } catch (final ArchiverException e) {
            throw new MojoExecutionException("Unable to unarchive " + artifactFile.getName(), e);
        }
    }

    private void collect(final DependencyNode root, final Set<Artifact> artifacts) {
        Stack<DependencyNode> stack = new Stack<DependencyNode>();
        stack.push(root);
        while (!stack.isEmpty()) {
            DependencyNode node = stack.pop();
            if (node.getState() == DependencyNode.INCLUDED) {
                final Artifact artifact = node.getArtifact();
                if (includedScope(artifact.getScope())) {
                    getLog().info("Adding Artefact: " + artifact.toString());
                    artifacts.add(artifact);
                    // check children 
                    if (!node.getChildren().isEmpty()) {
                        stack.addAll(node.getChildren());
                    }
                }
            }
        }
    }

    private ArtifactRepository getArtifactRepository(final String outputDirectory)
            throws ArtifactNotFoundException, MojoFailureException {
        return artifactRepositoryFactory.createDeploymentArtifactRepository("tars", "file://" + outputDirectory,
                getArtifactRepositoryLayout(), false);
    }

    protected ArtifactRepositoryLayout getArtifactRepositoryLayout() throws MojoFailureException {
        try {
            ArtifactRepositoryLayout artifactRepositoryLayout = null;
            artifactRepositoryLayout = (ArtifactRepositoryLayout) container.lookup(
                    "org.apache.maven.artifact." + "repository.layout.ArtifactRepositoryLayout", repositoryLayout);
            if (artifactRepositoryLayout == null) {
                throw new MojoFailureException("Unknown repository layout '" + repositoryLayout + "'.");
            }
            return artifactRepositoryLayout;
        } catch (ComponentLookupException e) {
            throw new MojoFailureException("Unable to lookup the repository layout component '" + repositoryLayout
                    + "': " + e.getMessage());
        }
    }

    protected void installArtifact(Artifact artifact, File libDir, ArtifactRepository artifactRepository,
            boolean useTimestampInSnapshotFileName) throws MojoExecutionException {
        if (artifact != null && artifact.getFile() != null) {
            try {
                File source = artifact.getFile();

                File destination = libDir; //

                if (!source.isDirectory()) {
                    if (outputFileNameMapping != null) {
                        String fileName = MappingUtils.evaluateFileNameMapping(outputFileNameMapping, artifact);
                        destination = new File(destination, fileName);
                    } else {
                        destination = new File(destination, source.getName());
                    }

                    FileUtils.copyFile(source, destination);
                    Dependency dependency = new Dependency(artifact.getGroupId(), artifact.getArtifactId(),
                            artifact.getVersion(), artifact.getClassifier());
                    dependency.setRelativePath(destination.getCanonicalPath());
                    dependencyList.add(dependency);
                }
                getLog().info("Installing artifact " + source.getPath() + " to " + destination);
            } catch (IOException e) {
                throw new MojoExecutionException("Failed to copy artifact.", e);
            } catch (InterpolationException e) {
                throw new MojoExecutionException("Failed to map file name.", e);
            }
        }
    }

    private void createConfigScript(File binDir, File dataDir, File binLogDir, File configFile) throws Exception {
        InputStream in = null;
        FileWriter out = null;
        try {

            File servicesXMLFile = new File(war, "WEB-INF" + File.separator + "servants.xml");
            if (!servicesXMLFile.exists()) {
                throw new MojoExecutionException(
                        "failed to find WEB-INF/servants.xml, " + " servants will be disabled");
            }

            XMLConfigFile cfg = new XMLConfigFile();
            cfg.parse(new FileInputStream(servicesXMLFile));
            XMLConfigElement root = cfg.getRootElement();
            ArrayList<XMLConfigElement> elements = root.getChildList();

            in = getConfigTemplate();
            InputStreamReader reader = new InputStreamReader(getConfigTemplate());

            Map<Object, Object> context = new HashMap<Object, Object>();

            context.put("LOCALIP", getLocalIp());
            context.put("APP", getApp());
            context.put("SERVER", getServer());
            context.put("BASEPATH", binDir.getCanonicalPath());
            context.put("DATAPATH", dataDir.getCanonicalFile());
            context.put("LOGPATH", binLogDir.getCanonicalPath());
            context.put("JVMPARAMS", getJvmParams());
            context.put("MAINCLASS", MAIN_CLASS);

            context.put("CONFIG", configFile.getAbsolutePath());

            StringBuffer suf = new StringBuffer();
            for (XMLConfigElement element : elements) {
                if ("servant".equals(element.getName())) {
                    if (suf.length() > 0) {
                        suf.append("\n");
                    }
                    Servant servant = new Servant();
                    servant.setName(element.getStringAttribute("name"));
                    servant.setProtocol(element.getStringAttribute("protocol", TARS_PROTOCOL));
                    servant.setPort(element.getStringAttribute("port"));
                    suf.append(readServantScript(servant));
                    if (servant.getProtocol() != null && isJceOrWup(servant.getProtocol())) {
                        context.put("ADMINPORT", servant.getPort());
                    }
                }
            }

            context.put("SERVANTADAPTER", suf.toString());

            InterpolationFilterReader interpolationFilterReader = new InterpolationFilterReader(reader, context,
                    "$", "$");

            out = new FileWriter(configFile);
            IOUtil.copy(interpolationFilterReader, out);
        } catch (FileNotFoundException e) {
            throw new MojoExecutionException("Failed to get template for config file.", e);
        } catch (IOException e) {
            throw new MojoExecutionException("Failed to write config file.", e);
        } finally {
            IOUtil.close(out);
            IOUtil.close(in);
        }
    }

    private String getLocalIp() throws UnknownHostException {
        return System.getProperty("localIp", InetAddress.getLocalHost().getHostAddress());
    }

    private String getApp() {
        return System.getProperty("app", null);
    }

    private String getServer() {
        return System.getProperty("server", null);
    }

    private boolean isPackage() {
        return Boolean.parseBoolean(System.getProperty("package", "false"));
    }

    private String getJvmParams() {
        return System.getProperty("jvmParams", "-Xms1024m -Xmx1024m");
    }

    private boolean isJceOrWup(String protocol) {
        return TARS_PROTOCOL.equalsIgnoreCase(protocol) || TUP_PROTOCOL.equalsIgnoreCase(protocol);
    }

    private String readServantScript(Servant servant) throws Exception {
        InputStream in = null;
        try {
            in = getServantTemplate();
            Map<Object, Object> context = new HashMap<Object, Object>();
            context.put("LOCALIP", getLocalIp());
            context.put("APP", getApp());
            context.put("SERVER", getServer());
            context.put("SERVANTNAME", servant.getName());
            context.put("PORT", servant.getPort());
            context.put("PROTOCOL",
                    servant.getProtocol() == null || isJceOrWup(servant.getProtocol()) ? "tars" : "no_tars");

            InterpolationFilterReader interpolationFilterReader = new InterpolationFilterReader(
                    new InputStreamReader(in), context, "$", "$");
            return IOUtil.toString(interpolationFilterReader);
        } catch (FileNotFoundException e) {
            throw new MojoExecutionException("Failed to get template for servant file.", e);
        } catch (IOException e) {
            throw new MojoExecutionException("Failed to write servant file.", e);
        } finally {
            IOUtil.close(in);
        }
    }

    private String createBinScript(Platform platform, File libDir, File binDir, File configFile) throws Exception {

        InputStream in = null;

        FileWriter out = null;
        File binFile;

        try {
            in = getScriptTemplate(platform.getName());

            Map<Object, Object> context = new HashMap<Object, Object>();
            context.put("JVMPARAMS", getJvmParams());
            context.put("MAINCLASS", MAIN_CLASS);
            context.put("CONFIG", configFile.getAbsolutePath());
            context.put("CLASSPATH", platform.getClassPath(dependencyList));
            if (platform.isShowConsoleWindow()) {
                context.put("JAVA_BINARY", "java");
                context.put("UNIX_BACKGROUND", "");
            } else {
                context.put("JAVA_BINARY", "start /min javaw");
                context.put("UNIX_BACKGROUND", " &");
            }

            String interpolationToken = platform.getInterpolationToken();
            InterpolationFilterReader interpolationFilterReader = new InterpolationFilterReader(
                    new InputStreamReader(in), context, interpolationToken, interpolationToken);

            String programName = "tars_start";

            FileUtils.forceMkdir(binDir);
            binFile = new File(binDir, programName + platform.getBinFileExtension());
            if (Platform.UNIX_NAME.equals(platform.getName())) {
                if (binFile.exists()) {
                    try {
                        ArchiveEntryUtils.chmod(binFile, 0777, getLog(), true);
                    } catch (ArchiverException ae) {
                        throw new MojoExecutionException("Failed to change permission for bin file.", ae);
                    }
                }
            }

            out = new FileWriter(binFile);
            getLog().debug("Writing shell file for platform '" + platform.getName() + "' to '"
                    + binFile.getAbsolutePath() + "'.");

            IOUtil.copy(interpolationFilterReader, out);
        } catch (FileNotFoundException e) {
            throw new MojoExecutionException("Failed to get template for bin file.", e);
        } catch (IOException e) {
            throw new MojoExecutionException("Failed to write bin file.", e);
        } finally {
            IOUtil.close(out);
            IOUtil.close(in);
        }

        if (Platform.UNIX_NAME.equals(platform.getName())) {
            try {
                ArchiveEntryUtils.chmod(binFile, 0777, getLog(), true);
            } catch (ArchiverException ae) {
                throw new MojoExecutionException("Failed to change permission for bin file.", ae);
            }
        }
        return binFile.getCanonicalPath();
    }

    private InputStream getScriptTemplate(String platformName) throws MojoExecutionException {
        InputStream is = null;
        try {
            is = Platform.class.getResourceAsStream(platformName + "BinTemplate");
            if (is == null) {
                throw new MojoExecutionException(
                        "Unable to load internal template resource: " + platformName + "BinTemplate");
            }
        } catch (Exception e) {
            throw new MojoExecutionException("Unable to load external template file", e);
        }
        return is;
    }

    private InputStream getConfigTemplate() throws MojoExecutionException {
        InputStream is = null;
        try {
            is = Platform.class.getResourceAsStream("configTemplate");
            if (is == null) {
                throw new MojoExecutionException("Unable to load internal template resource: configTemplate");
            }
        } catch (Exception e) {
            throw new MojoExecutionException("Unable to load external template file", e);
        }
        return is;
    }

    private InputStream getServantTemplate() throws MojoExecutionException {
        InputStream is = null;
        try {
            is = Platform.class.getResourceAsStream("servantTemplate");
            if (is == null) {
                throw new MojoExecutionException("Unable to load internal template resource: configTemplate");
            }
        } catch (Exception e) {
            throw new MojoExecutionException("Unable to load external template file", e);
        }
        return is;
    }
}