com.greenpepper.maven.runner.CommandLineRunner.java Source code

Java tutorial

Introduction

Here is the source code for com.greenpepper.maven.runner.CommandLineRunner.java

Source

/**
 * Copyright (c) 2008 Pyxis Technologies inc.
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA,
 * or see the FSF site: http://www.fsf.org.
 *
 * @author oaouattara
 * @version $Id: $Id
 */
package com.greenpepper.maven.runner;

import static com.greenpepper.util.CollectionUtil.toArray;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.manager.WagonManager;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.cli.ConsoleDownloadMonitor;
import org.apache.maven.embedder.MavenEmbedder;
import org.apache.maven.embedder.MavenEmbedderException;
import org.apache.maven.embedder.MavenEmbedderLogger;
import org.apache.maven.embedder.PlexusLoggerAdapter;
import org.apache.maven.model.Plugin;
import org.apache.maven.profiles.ProfileManager;
import org.apache.maven.project.DefaultProjectBuilderConfiguration;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilderConfiguration;
import org.apache.maven.project.interpolation.ModelInterpolationException;
import org.apache.maven.project.interpolation.StringSearchModelInterpolator;
import org.apache.maven.project.path.DefaultPathTranslator;
import org.apache.maven.settings.MavenSettingsBuilder;
import org.apache.maven.settings.Mirror;
import org.apache.maven.settings.Server;
import org.apache.maven.settings.Settings;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
import org.codehaus.plexus.util.xml.Xpp3Dom;

import com.greenpepper.maven.runner.resolver.ProjectFileResolver;
import com.greenpepper.maven.runner.util.ReflectionUtils;
import com.greenpepper.util.cli.ArgumentMissingException;

public class CommandLineRunner {

    private static final String PLUGIN_KEY = "com.github.strator-dev.greenpepper:greenpepper-maven-plugin";
    private boolean isDebug = false;
    private String projectDependencyDescriptor;
    /** Constant <code>CWD="System.getProperty(user.dir)"</code> */
    public static final String CWD = System.getProperty("user.dir");
    @SuppressWarnings("serial")
    private ArrayList<String> scopes = new ArrayList<String>() {

        {
            add("runtime");
            add("compile");
        }
    };

    private Logger logger;
    private MavenEmbedder embedder;

    private MavenProject project;
    private List<Artifact> artifacts = new ArrayList<Artifact>();
    private ArgumentsParser argumentsParser;
    private ProjectFileResolver resolvers;

    /**
     * <p>Constructor for CommandLineRunner.</p>
     */
    public CommandLineRunner() {
        this(System.out);
    }

    /**
     * <p>Constructor for CommandLineRunner.</p>
     *
     * @param out a {@link java.io.PrintStream} object.
     */
    public CommandLineRunner(PrintStream out) {
        this.logger = new Logger(out);
        this.argumentsParser = new ArgumentsParser(out);
    }

    /**
     * <p>run.</p>
     *
     * @param args a {@link java.lang.String} object.
     * @throws java.lang.Exception if any.
     */
    public void run(String... args) throws Exception {
        List<String> parameters = parseCommandLine(args);
        if (!parameters.isEmpty()) {
            runClassicRunner(parameters);
        }
    }

    @SuppressWarnings("unchecked")
    private List<String> parseCommandLine(String[] args)
            throws ArgumentMissingException, IOException, ParseException {
        List<String> parameters = new ArrayList<String>();
        CommandLine commandLine = argumentsParser.parse(args);
        if (commandLine != null) {
            Option[] options = commandLine.getOptions();
            for (Option option : options) {
                if ("v".equals(option.getOpt())) {
                    isDebug = true;
                    parameters.add("--debug");
                } else if ("p".equals(option.getOpt())) {
                    projectDependencyDescriptor = option.getValue();
                } else if ("m".equals(option.getOpt())) {
                    usingScopes(option.getValue());
                } else if ("o".equals(option.getOpt())) {
                    parameters.add("-" + option.getOpt());
                    parameters.add(option.getValue());
                } else if ("r".equals(option.getOpt())) {
                    parameters.add("-" + option.getOpt());
                    parameters.add(option.getValue());
                } else {
                    parameters.add("--" + option.getLongOpt());
                    if (option.hasArg()) {
                        parameters.add(option.getValue());
                    }
                }
            }
            parameters.addAll(commandLine.getArgList());
        }
        return parameters;
    }

    private void usingScopes(String values) {
        StringTokenizer st = new StringTokenizer(values, ";", false);

        while (st.hasMoreTokens()) {
            String scope = st.nextToken();

            if (!scopes.contains(scope)) {
                scopes.add(scope);
            }
        }
    }

    private void runClassicRunner(List<String> args) throws Exception {
        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();

        initMavenEmbedder(originalClassLoader);

        resolveProject();

        ProjectFileResolver.MavenGAV mavenGAV = resolvers.getMavenGAV();
        if (StringUtils.isNotEmpty(mavenGAV.getClassifier())) {
            Artifact artifactWithClassifier = embedder.createArtifactWithClassifier(project.getGroupId(),
                    project.getArtifactId(), project.getVersion(), mavenGAV.getPackaging(),
                    mavenGAV.getClassifier());
            resolve(artifactWithClassifier, artifacts);
        } else {
            resolveScopedArtifacts();

            resolveMavenPluginArtifact();

            resolveProjectArtifact();
        }

        URL[] classpaths = buildRuntimeClasspaths();

        URLClassLoader urlClassLoader = new URLClassLoader(classpaths, originalClassLoader);

        Thread.currentThread().setContextClassLoader(urlClassLoader);

        ReflectionUtils.setDebugEnabled(urlClassLoader, isDebug);
        ReflectionUtils.setSystemOutputs(urlClassLoader, logger.getOut(), System.err);

        Class<?> mainClass = urlClassLoader.loadClass("com.greenpepper.runner.Main");

        logger.debug("Invoking: com.greenpepper.runner.Main " + StringUtils.join(args, ' '));
        ReflectionUtils.invokeMain(mainClass, args);

        Thread.currentThread().setContextClassLoader(originalClassLoader);

    }

    private void resolveProject() throws Exception {
        File projectFile = resolveProjectFile();

        if (!projectFile.exists()) {
            throw new FileNotFoundException(String.format("Project dependency descriptor file not found: %s",
                    projectFile.getAbsolutePath()));
        }

        project = readProjectWithDependencies(projectFile);

        interpolateProject();

    }

    private void interpolateProject() throws InitializationException, Exception, ModelInterpolationException {
        StringSearchModelInterpolator interpolator = new StringSearchModelInterpolator(new DefaultPathTranslator());
        interpolator.enableLogging(new PlexusLoggerAdapter(embedder.getLogger()));
        interpolator.initialize();
        ProjectBuilderConfiguration config = new DefaultProjectBuilderConfiguration();
        config.setLocalRepository(getLocalRepository());
        config.setGlobalProfileManager(getProfileManager());
        interpolator.interpolate(project.getModel(), project.getBasedir(), config, logger.isDebugEnabled());
    }

    private MavenProject readProjectWithDependencies(File projectFile) throws Exception {
        return embedder.readProjectWithDependencies(projectFile, new ConsoleDownloadMonitor());
    }

    private File resolveProjectFile() throws Exception {
        resolvers = new ProjectFileResolver(embedder, logger);
        return resolvers.resolve(projectDependencyDescriptor);
    }

    @SuppressWarnings("unchecked")
    private void resolveScopedArtifacts() {
        artifacts = new ArrayList<Artifact>(project.getArtifacts().size());

        for (Iterator<Artifact> i = project.getArtifacts().iterator(); i.hasNext();) {
            Artifact a = i.next();

            if (scopes.contains(a.getScope())) {
                artifacts.add(a);
            }
        }
    }

    private void resolveProjectArtifact() {
        try {
            Artifact selfArtifact = project.getArtifact();

            if (selfArtifact.getFile() == null) {
                resolve(selfArtifact, artifacts);
            }
        } catch (Exception ex) {
            logger.error("Resolving project artifact", ex);
        }
    }

    private void resolveMavenPluginArtifact() {
        try {
            @SuppressWarnings("unchecked")
            Map<String, Artifact> plugins = project.getPluginArtifactMap();

            if (plugins.get(PLUGIN_KEY) != null) {
                Artifact fixtureArtifact = embedder.createArtifactWithClassifier(project.getGroupId(),
                        project.getArtifactId(), project.getVersion(), project.getPackaging(), "fixtures");

                resolve(fixtureArtifact, artifacts);
            }
        } catch (Exception ex) {
            logger.error("Resolving maven-plugin artifact", ex);
        }
    }

    private void resolve(Artifact artifact, List<Artifact> runtimeArtifacts) throws Exception {
        embedder.resolve(artifact, project.getRemoteArtifactRepositories(), embedder.getLocalRepository());

        if (artifact.getFile() != null) {
            runtimeArtifacts.add(artifact);
        }
    }

    private void initMavenEmbedder(ClassLoader classLoader) throws MavenEmbedderException {
        embedder = new MavenEmbedder();
        embedder.setClassLoader(classLoader);

        if (isDebug) {
            logger.setThreshold(MavenEmbedderLogger.LEVEL_DEBUG);
        }

        embedder.setLogger(logger);
        embedder.setAlignWithUserInstallation(true);

        embedder.start();

        initAuthenticationSettings();
    }

    private void initAuthenticationSettings() throws MavenEmbedderException {
        /**
         * GP-775 : MavenEmbedder does not read servers/mirrors configuration properly. Mimic the
         * resolveParameter
         * found in the DefaultMaven class to allow credentials to be taken into account.
         */
        try {
            WagonManager wagonManager = getWagonManager();
            Settings settings = getSettings();

            for (Object item : settings.getServers()) {
                Server server = (Server) item;

                wagonManager.addAuthenticationInfo(server.getId(), server.getUsername(), server.getPassword(),
                        server.getPrivateKey(), server.getPassphrase());

                wagonManager.addPermissionInfo(server.getId(), server.getFilePermissions(),
                        server.getDirectoryPermissions());

                if (server.getConfiguration() != null) {
                    wagonManager.addConfiguration(server.getId(), (Xpp3Dom) server.getConfiguration());
                }
            }

            for (Object item : settings.getMirrors()) {
                Mirror mirror = (Mirror) item;

                wagonManager.addMirror(mirror.getId(), mirror.getMirrorOf(), mirror.getUrl());
            }
        } catch (Exception ex) {
            throw new MavenEmbedderException("Failed to handle authentication settings", ex);
        }
    }

    private URL[] buildRuntimeClasspaths() throws IOException {
        final Map<String, File> actualClasspaths = getActualClasspaths();

        List<URL> classpaths = new ArrayList<URL>();

        for (Artifact artifact : artifacts) {
            File artifactFile = actualClasspaths.get(artifact.getFile().getName());

            if (artifactFile == null || !artifactFile.equals(artifact.getFile())) {
                logger.debug(String.format("Artifact: %s (%s)", artifact, artifact.getFile()));
                classpaths.add(artifact.getFile().toURI().toURL());
            }
        }

        // Add project folders, if any.
        String buildOutputDirectory = project.getBuild().getOutputDirectory();
        classpaths.add(new File(buildOutputDirectory).toURI().toURL());

        @SuppressWarnings("unchecked")
        List<Plugin> buildPlugins = project.getBuildPlugins();
        for (Plugin plugin : buildPlugins) {
            if (StringUtils.equals(PLUGIN_KEY, plugin.getKey())) {
                Xpp3Dom configuration = (Xpp3Dom) plugin.getConfiguration();
                if (configuration != null) {
                    Xpp3Dom fixtureOutputDirectory = configuration.getChild("fixtureOutputDirectory");
                    String relativeDirectory = fixtureOutputDirectory.getValue();
                    if (!StringUtils.isEmpty(relativeDirectory)) {
                        File directory = FileUtils.getFile(project.getBasedir(), relativeDirectory);
                        classpaths.add(directory.toURI().toURL());
                    }
                }
                break;
            }
        }

        logger.debug("Classpath used: " + classpaths.toString(), null);
        return toArray(classpaths, URL.class);
    }

    private Map<String, File> getActualClasspaths() {
        Map<String, File> classpaths = new HashMap<String, File>();

        String[] javaClassPath = System.getProperty("java.class.path").split(System.getProperty("path.separator"));

        for (String classPath : javaClassPath) {
            File file = new File(classPath);
            classpaths.put(file.getName(), file);
        }

        return classpaths;
    }

    private WagonManager getWagonManager() throws Exception {
        return (WagonManager) ReflectionUtils.getDeclaredFieldValue(embedder, "wagonManager");
    }

    private Settings getSettings() throws Exception {
        MavenSettingsBuilder mavenSettingsBuilder = getMavenSettingsBuilder();
        return mavenSettingsBuilder.buildSettings();
    }

    private MavenSettingsBuilder getMavenSettingsBuilder() throws Exception {
        return (MavenSettingsBuilder) ReflectionUtils.getDeclaredFieldValue(embedder, "settingsBuilder");
    }

    private ArtifactRepository getLocalRepository() throws Exception {
        return (ArtifactRepository) ReflectionUtils.getDeclaredFieldValue(embedder, "localRepository");
    }

    private ProfileManager getProfileManager() throws Exception {
        return (ProfileManager) ReflectionUtils.getDeclaredFieldValue(embedder, "profileManager");
    }
}