io.takari.maven.testing.Maven30xRuntime.java Source code

Java tutorial

Introduction

Here is the source code for io.takari.maven.testing.Maven30xRuntime.java

Source

/**
 * Copyright (c) 2014 Takari, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package io.takari.maven.testing;

//some of the code was originally copied from org.apache.maven.plugin.testing.AbstractMojoTestCase and org.apache.maven.plugin.testing.MojoRule

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 */

import static org.junit.Assert.assertNotNull;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.maven.DefaultMaven;
import org.apache.maven.Maven;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.DefaultMavenExecutionRequest;
import org.apache.maven.execution.DefaultMavenExecutionResult;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionRequestPopulator;
import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.internal.MojoDescriptorCreator;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.Mojo;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.Parameter;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.repository.RepositorySystem;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
import org.apache.maven.settings.building.SettingsBuilder;
import org.apache.maven.settings.building.SettingsBuildingRequest;
import org.codehaus.plexus.ContainerConfiguration;
import org.codehaus.plexus.DefaultContainerConfiguration;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.component.configurator.ComponentConfigurator;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
import org.codehaus.plexus.component.repository.ComponentDescriptor;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
import org.codehaus.plexus.util.InterpolationFilterReader;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.XmlStreamReader;
import org.codehaus.plexus.util.xml.Xpp3Dom;

import com.google.inject.Module;

class Maven30xRuntime implements MavenRuntime {
    private static final String PATH_PLUGINXML = "META-INF/maven/plugin.xml";

    protected final TestProperties properties = new TestProperties();

    protected final DefaultPlexusContainer container;
    protected final Map<String, MojoDescriptor> mojoDescriptors;

    public Maven30xRuntime(Module[] modules) throws Exception {
        ClassWorld classWorld = new ClassWorld("plexus.core", Thread.currentThread().getContextClassLoader());
        ContainerConfiguration cc = new DefaultContainerConfiguration() //
                .setClassWorld(classWorld) //
                .setClassPathScanning(PlexusConstants.SCANNING_INDEX) //
                .setAutoWiring(true) //
                .setName("maven");
        this.container = new DefaultPlexusContainer(cc, modules);
        this.mojoDescriptors = Collections.unmodifiableMap(readPluginXml(container));
    }

    private Map<String, MojoDescriptor> readPluginXml(DefaultPlexusContainer container) throws Exception {
        Map<String, MojoDescriptor> mojoDescriptors = new HashMap<String, MojoDescriptor>();
        Enumeration<URL> resources = getClass().getClassLoader().getResources(PATH_PLUGINXML);
        while (resources.hasMoreElements()) {
            InputStream is = resources.nextElement().openStream();

            if (is == null) {
                return Collections.emptyMap();
            }

            XmlStreamReader reader = new XmlStreamReader(is);

            @SuppressWarnings("rawtypes")
            Map contextData = container.getContext().getContextData();
            @SuppressWarnings("unchecked")
            InterpolationFilterReader interpolationFilterReader = new InterpolationFilterReader(
                    new BufferedReader(reader), contextData);

            PluginDescriptor pluginDescriptor = new PluginDescriptorBuilder().build(interpolationFilterReader);

            Artifact artifact = container.lookup(RepositorySystem.class) //
                    .createArtifact(pluginDescriptor.getGroupId(), pluginDescriptor.getArtifactId(),
                            pluginDescriptor.getVersion(), ".jar");

            artifact.setFile(getPluginArtifactFile());
            pluginDescriptor.setPluginArtifact(artifact);
            pluginDescriptor.setArtifacts(Arrays.asList(artifact));

            for (ComponentDescriptor<?> desc : pluginDescriptor.getComponents()) {
                container.addComponentDescriptor(desc);
            }

            for (MojoDescriptor mojoDescriptor : pluginDescriptor.getMojos()) {
                // TODO decide how to handle duplicate goals
                // this is possible when one plugin extends another
                // for example, Tycho 'compile' goal is an extension of standard maven 'compile'
                mojoDescriptors.put(mojoDescriptor.getGoal(), mojoDescriptor);
            }
        }
        return mojoDescriptors;
    }

    /**
     * Returns best-effort plugin artifact file.
     * <p>
     * First, attempts to determine parent directory of META-INF directory holding the plugin descriptor. If META-INF parent directory cannot be determined, falls back to test basedir.
     */
    private File getPluginArtifactFile() throws IOException {
        final String pluginDescriptorLocation = PATH_PLUGINXML;
        final URL resource = getClass().getResource("/" + pluginDescriptorLocation);

        File file = null;

        // attempt to resolve relative to META-INF/maven/plugin.xml first
        if (resource != null) {
            if ("file".equalsIgnoreCase(resource.getProtocol())) {
                String path = resource.getPath();
                if (path.endsWith(pluginDescriptorLocation)) {
                    file = new File(path.substring(0, path.length() - pluginDescriptorLocation.length()));
                }
            } else if ("jar".equalsIgnoreCase(resource.getProtocol())) {
                // TODO is there a helper for this somewhere?
                try {
                    URL jarfile = new URL(resource.getPath());
                    if ("file".equalsIgnoreCase(jarfile.getProtocol())) {
                        String path = jarfile.getPath();
                        if (path.endsWith(pluginDescriptorLocation)) {
                            file = new File(
                                    path.substring(0, path.length() - pluginDescriptorLocation.length() - 2));
                        }
                    }
                } catch (MalformedURLException e) {
                    // not jar:file:/ URL, too bad
                }
            }
        }

        // fallback to test project basedir if couldn't resolve relative to META-INF/maven/plugin.xml
        if (file == null || !file.exists()) {
            file = new File("").getCanonicalFile();
        }

        return file.getCanonicalFile();
    }

    @Override
    public void shutdown() {
        container.dispose();
    }

    @Override
    public MavenProject readMavenProject(File basedir) throws Exception {
        File pom = new File(basedir, "pom.xml");
        MavenExecutionRequest request = newExecutionRequest();
        request.setBaseDirectory(basedir);
        ProjectBuildingRequest configuration = getProjectBuildingRequest(request);
        return container.lookup(ProjectBuilder.class).build(getPomFile(pom), configuration).getProject();
    }

    protected ProjectBuildingRequest getProjectBuildingRequest(MavenExecutionRequest request)
            throws ComponentLookupException {
        // TODO populate repository system session... if somebody asks for it really nicely
        return request.getProjectBuildingRequest();
    }

    protected File getPomFile(File pom) throws IOException {
        if (!pom.exists()) {
            try (BufferedWriter w = new BufferedWriter(
                    new OutputStreamWriter(new FileOutputStream(pom), "UTF-8"))) {
                w.write("<?xml version='1.0' encoding='UTF-8'?>\n");
                w.write("<project>\n");
                w.write("<modelVersion>4.0.0</modelVersion>\n");
                w.write("<groupId>test</groupId>\n");
                w.write("<artifactId>test</artifactId>\n");
                w.write("<version>1</version>\n");
                w.write("</project>\n");
            }
        }
        return pom;
    }

    @Override
    public MojoExecution newMojoExecution(String goal) {
        MojoDescriptor mojoDescriptor = mojoDescriptors.get(goal);
        assertNotNull(String.format("The MojoDescriptor for the goal %s cannot be null.", goal), mojoDescriptor);
        MojoExecution execution = new MojoExecution(mojoDescriptor);
        execution.setConfiguration(new Xpp3Dom("configuration"));
        return execution;
    }

    // copy&paste from
    // org.apache.maven.lifecycle.internal.DefaultLifecycleExecutionPlanCalculator.finalizeMojoConfiguration(MojoExecution)
    protected void finalizeMojoConfiguration(MojoExecution mojoExecution) {
        MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();

        Xpp3Dom executionConfiguration = mojoExecution.getConfiguration();
        if (executionConfiguration == null) {
            executionConfiguration = new Xpp3Dom("configuration");
        }

        Xpp3Dom defaultConfiguration = MojoDescriptorCreator.convert(mojoDescriptor);

        Xpp3Dom finalConfiguration = new Xpp3Dom("configuration");

        if (mojoDescriptor.getParameters() != null) {
            for (Parameter parameter : mojoDescriptor.getParameters()) {
                Xpp3Dom parameterConfiguration = executionConfiguration.getChild(parameter.getName());

                if (parameterConfiguration == null) {
                    parameterConfiguration = executionConfiguration.getChild(parameter.getAlias());
                }

                Xpp3Dom parameterDefaults = defaultConfiguration.getChild(parameter.getName());

                parameterConfiguration = Xpp3Dom.mergeXpp3Dom(parameterConfiguration, parameterDefaults,
                        Boolean.TRUE);

                if (parameterConfiguration != null) {
                    parameterConfiguration = new Xpp3Dom(parameterConfiguration, parameter.getName());

                    if (StringUtils.isEmpty(parameterConfiguration.getAttribute("implementation"))
                            && StringUtils.isNotEmpty(parameter.getImplementation())) {
                        parameterConfiguration.setAttribute("implementation", parameter.getImplementation());
                    }

                    finalConfiguration.addChild(parameterConfiguration);
                }
            }
        }

        mojoExecution.setConfiguration(finalConfiguration);
    }

    @Override
    public MavenSession newMavenSession(File basedir) throws Exception {
        MavenExecutionRequest request = newExecutionRequest();
        MavenExecutionResult result = new DefaultMavenExecutionResult();
        DefaultMaven maven = (DefaultMaven) container.lookup(Maven.class);
        try {
            Object repositorySession = maven.getClass()
                    .getMethod("newRepositorySession", MavenExecutionRequest.class).invoke(maven, request);
            Class<?> repositorySessionClass = repositorySession.getClass().getClassLoader()
                    .loadClass("org.sonatype.aether.RepositorySystemSession");
            Constructor<MavenSession> constructor = MavenSession.class.getConstructor(PlexusContainer.class,
                    repositorySessionClass, MavenExecutionRequest.class, MavenExecutionResult.class);
            return constructor.newInstance(container, repositorySession, request, result);
        } catch (ReflectiveOperationException | SecurityException e) {
            throw new RuntimeException(e);
        }
    }

    @SuppressWarnings("deprecation")
    protected MavenExecutionRequest newExecutionRequest() throws Exception {

        // system properties
        Properties buildProperties = getMavenBuildProperties();
        String mavenVersion = buildProperties.getProperty("version");
        Properties systemProperties = new Properties();
        systemProperties.putAll(System.getProperties()); // TODO not thread safe
        systemProperties.setProperty("maven.version", mavenVersion);
        systemProperties.setProperty("maven.build.version", mavenVersion);

        // request with initial configuration
        MavenExecutionRequest request = new DefaultMavenExecutionRequest();
        request.setLocalRepositoryPath(properties.getLocalRepository());
        request.setUserSettingsFile(properties.getUserSettings());
        request.setGlobalSettingsFile(properties.getGlobalSettings());
        request.setOffline(properties.getOffline());
        request.setUpdateSnapshots(properties.getUpdateSnapshots());
        request.setSystemProperties(systemProperties);

        // read settings
        SettingsBuildingRequest settingsRequest = new DefaultSettingsBuildingRequest();
        settingsRequest.setGlobalSettingsFile(request.getGlobalSettingsFile());
        settingsRequest.setUserSettingsFile(request.getUserSettingsFile());
        settingsRequest.setSystemProperties(request.getSystemProperties());
        settingsRequest.setUserProperties(request.getUserProperties());
        Settings settings = lookup(SettingsBuilder.class).build(settingsRequest).getEffectiveSettings();

        MavenExecutionRequestPopulator populator = container.lookup(MavenExecutionRequestPopulator.class);
        request = populator.populateFromSettings(request, settings);
        return populator.populateDefaults(request);
    }

    private Properties getMavenBuildProperties() {
        Properties properties = new Properties();
        try (InputStream is = getClass()
                .getResourceAsStream("/META-INF/maven/org.apache.maven/maven-core/pom.properties")) {
            properties.load(is);
        } catch (IOException e) {
            // ignore
        }
        return properties;
    }

    @Override
    public Mojo executeMojo(MavenSession session, MavenProject project, MojoExecution execution) throws Exception {
        Mojo mojo = lookupConfiguredMojo(session, execution);
        mojo.execute();
        return mojo;
    }

    @Override
    public Mojo lookupConfiguredMojo(MavenSession session, MojoExecution execution) throws Exception {
        MavenProject project = session.getCurrentProject();
        MojoDescriptor mojoDescriptor = execution.getMojoDescriptor();

        Mojo mojo = container.lookup(Mojo.class, mojoDescriptor.getRoleHint());

        ExpressionEvaluator evaluator = new PluginParameterExpressionEvaluator(session, execution);

        Xpp3Dom configuration = null;
        Plugin plugin = project.getPlugin(mojoDescriptor.getPluginDescriptor().getPluginLookupKey());
        if (plugin != null) {
            configuration = (Xpp3Dom) plugin.getConfiguration();
        }
        if (configuration == null) {
            configuration = new Xpp3Dom("configuration");
        }
        configuration = Xpp3Dom.mergeXpp3Dom(configuration, execution.getConfiguration());
        execution.setConfiguration(configuration);
        finalizeMojoConfiguration(execution);

        PlexusConfiguration pluginConfiguration = new XmlPlexusConfiguration(execution.getConfiguration());

        String configuratorHint = "basic";
        if (mojoDescriptor.getComponentConfigurator() != null) {
            configuratorHint = mojoDescriptor.getComponentConfigurator();
        }

        ComponentConfigurator configurator = container.lookup(ComponentConfigurator.class, configuratorHint);

        configurator.configureComponent(mojo, pluginConfiguration, evaluator, container.getContainerRealm());

        return mojo;
    }

    @Override
    public DefaultPlexusContainer getContainer() {
        return container;
    }

    @Override
    public <T> T lookup(Class<T> role) throws ComponentLookupException {
        return container.lookup(role);
    }

}