io.takari.maven.testing.Maven30xRuntime Maven / Gradle / Ivy
/**
* 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 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 readPluginXml(DefaultPlexusContainer container) throws Exception {
Map mojoDescriptors = new HashMap();
Enumeration 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.
*
* 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("\n");
w.write("\n");
w.write("4.0.0 \n");
w.write("test \n");
w.write("test \n");
w.write("1 \n");
w.write(" \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 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 lookup(Class role) throws ComponentLookupException {
return container.lookup(role);
}
}