blob: a1c7a66502eb90664a7cef833e77945ac4760a62 [file] [log] [blame]
package org.apache.maven.plugin.testing;
/*
* 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 java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.input.XmlStreamReader;
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.MavenExecutionResult;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.internal.MojoDescriptorCreator;
import org.apache.maven.model.Plugin;
import org.apache.maven.monitor.logging.DefaultLog;
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.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.apache.maven.repository.RepositorySystem;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
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.PlexusContainerException;
import org.codehaus.plexus.PlexusTestCase;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
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.configuration.PlexusConfiguration;
import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
import org.codehaus.plexus.logging.LoggerManager;
import org.codehaus.plexus.util.InterpolationFilterReader;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.ReflectionUtils;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
import com.google.inject.Module;
/**
* TODO: add a way to use the plugin POM for the lookup so that the user doesn't have to provide the a:g:v:goal
* as the role hint for the mojo lookup.
* TODO: standardize the execution of the mojo and looking at the results, but could simply have a template method
* for verifying the state of the mojo post execution
* TODO: need a way to look at the state of the mojo without adding getters, this could be where we finally specify
* the expressions which extract values from the mojo.
* TODO: create a standard directory structure for picking up POMs to make this even easier, we really just need a testing
* descriptor and make this entirely declarative!
*
* @author jesse
* @version $Id$
*/
public abstract class AbstractMojoTestCase
extends PlexusTestCase
{
private ComponentConfigurator configurator;
private PlexusContainer container;
private Map<String, MojoDescriptor> mojoDescriptors;
/*
* for the harness I think we have decided against going the route of using the maven project builder.
* instead I think we are going to try and make an instance of the localrespository and assign that
* to either the project stub or into the mojo directly with injection...not sure yet though.
*/
//private MavenProjectBuilder projectBuilder;
protected void setUp()
throws Exception
{
configurator = getContainer().lookup( ComponentConfigurator.class, "basic" );
InputStream is = getClass().getResourceAsStream( "/" + getPluginDescriptorLocation() );
XmlStreamReader reader = new XmlStreamReader( is );
InterpolationFilterReader interpolationFilterReader =
new InterpolationFilterReader( new BufferedReader( reader ), container.getContext().getContextData() );
PluginDescriptor pluginDescriptor = new PluginDescriptorBuilder().build( interpolationFilterReader );
Artifact artifact =
lookup( RepositorySystem.class ).createArtifact( pluginDescriptor.getGroupId(),
pluginDescriptor.getArtifactId(),
pluginDescriptor.getVersion(), ".jar" );
artifact.setFile( new File( getBasedir() ).getCanonicalFile() );
pluginDescriptor.setPluginArtifact( artifact );
pluginDescriptor.setArtifacts( Arrays.asList( artifact ) );
for ( ComponentDescriptor<?> desc : pluginDescriptor.getComponents() )
{
getContainer().addComponentDescriptor( desc );
}
mojoDescriptors = new HashMap<String, MojoDescriptor>();
for ( MojoDescriptor mojoDescriptor : pluginDescriptor.getMojos() )
{
mojoDescriptors.put( mojoDescriptor.getGoal(), mojoDescriptor );
}
}
protected InputStream getPublicDescriptorStream()
throws Exception
{
return new FileInputStream( new File( getPluginDescriptorPath() ) );
}
protected String getPluginDescriptorPath()
{
return getBasedir() + "/target/classes/META-INF/maven/plugin.xml";
}
protected String getPluginDescriptorLocation()
{
return "META-INF/maven/plugin.xml";
}
protected void setupContainer()
{
ContainerConfiguration cc = setupContainerConfiguration();
try
{
List<Module> modules = new ArrayList<Module>();
addGuiceModules( modules );
container = new DefaultPlexusContainer( cc, modules.toArray( new Module[modules.size()] ) );
}
catch ( PlexusContainerException e )
{
e.printStackTrace();
fail( "Failed to create plexus container." );
}
}
/**
* @since 3.0.0
*/
protected void addGuiceModules( List<Module> modules )
{
// no custom guice modules by default
}
protected ContainerConfiguration setupContainerConfiguration()
{
ClassWorld classWorld = new ClassWorld( "plexus.core", Thread.currentThread().getContextClassLoader() );
ContainerConfiguration cc = new DefaultContainerConfiguration()
.setClassWorld( classWorld )
.setClassPathScanning( PlexusConstants.SCANNING_INDEX )
.setAutoWiring( true )
.setName( "maven" );
return cc;
}
protected PlexusContainer getContainer()
{
if ( container == null )
{
setupContainer();
}
return container;
}
/**
* Lookup the mojo leveraging the subproject pom
*
* @param goal
* @param pluginPom
* @return a Mojo instance
* @throws Exception
*/
protected Mojo lookupMojo( String goal, String pluginPom )
throws Exception
{
return lookupMojo( goal, new File( pluginPom ) );
}
/**
* Lookup an empty mojo
*
* @param goal
* @param pluginPom
* @return a Mojo instance
* @throws Exception
*/
protected Mojo lookupEmptyMojo( String goal, String pluginPom )
throws Exception
{
return lookupEmptyMojo( goal, new File( pluginPom ) );
}
/**
* Lookup the mojo leveraging the actual subprojects pom
*
* @param goal
* @param pom
* @return a Mojo instance
* @throws Exception
*/
protected Mojo lookupMojo( String goal, File pom )
throws Exception
{
File pluginPom = new File( getBasedir(), "pom.xml" );
Xpp3Dom pluginPomDom = Xpp3DomBuilder.build( ReaderFactory.newXmlReader( pluginPom ) );
String artifactId = pluginPomDom.getChild( "artifactId" ).getValue();
String groupId = resolveFromRootThenParent( pluginPomDom, "groupId" );
String version = resolveFromRootThenParent( pluginPomDom, "version" );
PlexusConfiguration pluginConfiguration = extractPluginConfiguration( artifactId, pom );
return lookupMojo( groupId, artifactId, version, goal, pluginConfiguration );
}
/**
* Lookup the mojo leveraging the actual subprojects pom
*
* @param goal
* @param pom
* @return a Mojo instance
* @throws Exception
*/
protected Mojo lookupEmptyMojo( String goal, File pom )
throws Exception
{
File pluginPom = new File( getBasedir(), "pom.xml" );
Xpp3Dom pluginPomDom = Xpp3DomBuilder.build( ReaderFactory.newXmlReader( pluginPom ) );
String artifactId = pluginPomDom.getChild( "artifactId" ).getValue();
String groupId = resolveFromRootThenParent( pluginPomDom, "groupId" );
String version = resolveFromRootThenParent( pluginPomDom, "version" );
return lookupMojo( groupId, artifactId, version, goal, null );
}
/*
protected Mojo lookupMojo( String groupId, String artifactId, String version, String goal, File pom )
throws Exception
{
PlexusConfiguration pluginConfiguration = extractPluginConfiguration( artifactId, pom );
return lookupMojo( groupId, artifactId, version, goal, pluginConfiguration );
}
*/
/**
* lookup the mojo while we have all of the relavent information
*
* @param groupId
* @param artifactId
* @param version
* @param goal
* @param pluginConfiguration
* @return a Mojo instance
* @throws Exception
*/
protected Mojo lookupMojo( String groupId, String artifactId, String version, String goal,
PlexusConfiguration pluginConfiguration )
throws Exception
{
validateContainerStatus();
// pluginkey = groupId : artifactId : version : goal
Mojo mojo = (Mojo) lookup( Mojo.ROLE, groupId + ":" + artifactId + ":" + version + ":" + goal );
LoggerManager loggerManager = (LoggerManager) getContainer().lookup( LoggerManager.class );
Log mojoLogger = new DefaultLog( loggerManager.getLoggerForComponent( Mojo.ROLE ) );
mojo.setLog( mojoLogger );
if ( pluginConfiguration != null )
{
/* requires v10 of plexus container for lookup on expression evaluator
ExpressionEvaluator evaluator = (ExpressionEvaluator) getContainer().lookup( ExpressionEvaluator.ROLE,
"stub-evaluator" );
*/
ExpressionEvaluator evaluator = new ResolverExpressionEvaluatorStub();
configurator.configureComponent( mojo, pluginConfiguration, evaluator, getContainer().getContainerRealm() );
}
return mojo;
}
/**
*
* @param project
* @param goal
* @return
* @throws Exception
* @since 2.0
*/
protected Mojo lookupConfiguredMojo( MavenProject project, String goal )
throws Exception
{
return lookupConfiguredMojo( newMavenSession( project ), newMojoExecution( goal ) );
}
/**
*
* @param session
* @param execution
* @return
* @throws Exception
* @throws ComponentConfigurationException
* @since 2.0
*/
protected Mojo lookupConfiguredMojo( MavenSession session, MojoExecution execution )
throws Exception, ComponentConfigurationException
{
MavenProject project = session.getCurrentProject();
MojoDescriptor mojoDescriptor = execution.getMojoDescriptor();
Mojo mojo = (Mojo) lookup( mojoDescriptor.getRole(), 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( execution.getConfiguration(), configuration );
PlexusConfiguration pluginConfiguration = new XmlPlexusConfiguration( configuration );
configurator.configureComponent( mojo, pluginConfiguration, evaluator, getContainer().getContainerRealm() );
return mojo;
}
/**
*
* @param project
* @return
* @since 2.0
*/
protected MavenSession newMavenSession( MavenProject project )
{
MavenExecutionRequest request = new DefaultMavenExecutionRequest();
MavenExecutionResult result = new DefaultMavenExecutionResult();
MavenSession session = new MavenSession( container, MavenRepositorySystemUtils.newSession(), request, result );
session.setCurrentProject( project );
session.setProjects( Arrays.asList( project ) );
return session;
}
/**
*
* @param goal
* @return
* @since 2.0
*/
protected 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 );
finalizeMojoConfiguration( execution );
return execution;
}
// copy&paste from org.apache.maven.lifecycle.internal.DefaultLifecycleExecutionPlanCalculator.finalizeMojoConfiguration(MojoExecution)
private 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 );
}
/**
* @param artifactId
* @param pom
* @return the plexus configuration
* @throws Exception
*/
protected PlexusConfiguration extractPluginConfiguration( String artifactId, File pom )
throws Exception
{
Reader reader = ReaderFactory.newXmlReader( pom );
Xpp3Dom pomDom = Xpp3DomBuilder.build( reader );
return extractPluginConfiguration( artifactId, pomDom );
}
/**
* @param artifactId
* @param pomDom
* @return the plexus configuration
* @throws Exception
*/
protected PlexusConfiguration extractPluginConfiguration( String artifactId, Xpp3Dom pomDom )
throws Exception
{
Xpp3Dom pluginConfigurationElement = null;
Xpp3Dom buildElement = pomDom.getChild( "build" );
if ( buildElement != null )
{
Xpp3Dom pluginsRootElement = buildElement.getChild( "plugins" );
if ( pluginsRootElement != null )
{
Xpp3Dom[] pluginElements = pluginsRootElement.getChildren();
for ( Xpp3Dom pluginElement : pluginElements )
{
String pluginElementArtifactId = pluginElement.getChild( "artifactId" ).getValue();
if ( pluginElementArtifactId.equals( artifactId ) )
{
pluginConfigurationElement = pluginElement.getChild( "configuration" );
break;
}
}
if ( pluginConfigurationElement == null )
{
throw new ConfigurationException( "Cannot find a configuration element for a plugin with an "
+ "artifactId of " + artifactId + "." );
}
}
}
if ( pluginConfigurationElement == null )
{
throw new ConfigurationException( "Cannot find a configuration element for a plugin with an artifactId of "
+ artifactId + "." );
}
return new XmlPlexusConfiguration( pluginConfigurationElement );
}
/**
* Configure the mojo
*
* @param mojo
* @param artifactId
* @param pom
* @return a Mojo instance
* @throws Exception
*/
protected Mojo configureMojo( Mojo mojo, String artifactId, File pom )
throws Exception
{
validateContainerStatus();
PlexusConfiguration pluginConfiguration = extractPluginConfiguration( artifactId, pom );
ExpressionEvaluator evaluator = new ResolverExpressionEvaluatorStub();
configurator.configureComponent( mojo, pluginConfiguration, evaluator, getContainer().getContainerRealm() );
return mojo;
}
/**
* Configure the mojo with the given plexus configuration
*
* @param mojo
* @param pluginConfiguration
* @return a Mojo instance
* @throws Exception
*/
protected Mojo configureMojo( Mojo mojo, PlexusConfiguration pluginConfiguration )
throws Exception
{
validateContainerStatus();
ExpressionEvaluator evaluator = new ResolverExpressionEvaluatorStub();
configurator.configureComponent( mojo, pluginConfiguration, evaluator, getContainer().getContainerRealm() );
return mojo;
}
/**
* Convenience method to obtain the value of a variable on a mojo that might not have a getter.
*
* NOTE: the caller is responsible for casting to to what the desired type is.
*
* @param object
* @param variable
* @return object value of variable
* @throws IllegalArgumentException
*/
protected Object getVariableValueFromObject( Object object, String variable )
throws IllegalAccessException
{
Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses( variable, object.getClass() );
field.setAccessible( true );
return field.get( object );
}
/**
* Convenience method to obtain all variables and values from the mojo (including its superclasses)
*
* Note: the values in the map are of type Object so the caller is responsible for casting to desired types.
*
* @param object
* @return map of variable names and values
*/
protected Map<String, Object> getVariablesAndValuesFromObject( Object object )
throws IllegalAccessException
{
return getVariablesAndValuesFromObject( object.getClass(), object );
}
/**
* Convenience method to obtain all variables and values from the mojo (including its superclasses)
*
* Note: the values in the map are of type Object so the caller is responsible for casting to desired types.
*
* @param clazz
* @param object
* @return map of variable names and values
*/
protected Map<String, Object> getVariablesAndValuesFromObject( Class<?> clazz, Object object )
throws IllegalAccessException
{
Map<String, Object> map = new HashMap<String, Object>();
Field[] fields = clazz.getDeclaredFields();
AccessibleObject.setAccessible( fields, true );
for ( Field field : fields )
{
map.put( field.getName(), field.get( object ) );
}
Class<?> superclass = clazz.getSuperclass();
if ( !Object.class.equals( superclass ) )
{
map.putAll( getVariablesAndValuesFromObject( superclass, object ) );
}
return map;
}
/**
* Convenience method to set values to variables in objects that don't have setters
*
* @param object
* @param variable
* @param value
* @throws IllegalAccessException
*/
protected void setVariableValueToObject( Object object, String variable, Object value )
throws IllegalAccessException
{
Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses( variable, object.getClass() );
field.setAccessible( true );
field.set( object, value );
}
/**
* sometimes the parent element might contain the correct value so generalize that access
*
* TODO find out where this is probably done elsewhere
*
* @param pluginPomDom
* @param element
* @return
* @throws Exception
*/
private String resolveFromRootThenParent( Xpp3Dom pluginPomDom, String element )
throws Exception
{
Xpp3Dom elementDom = pluginPomDom.getChild( element );
// parent might have the group Id so resolve it
if ( elementDom == null )
{
Xpp3Dom pluginParentDom = pluginPomDom.getChild( "parent" );
if ( pluginParentDom != null )
{
elementDom = pluginParentDom.getChild( element );
if ( elementDom == null )
{
throw new Exception( "unable to determine " + element );
}
return elementDom.getValue();
}
throw new Exception( "unable to determine " + element );
}
return elementDom.getValue();
}
/**
* We should make sure this is called in each method that makes use of the container,
* otherwise we throw ugly NPE's
*
* crops up when the subclassing code defines the setUp method but doesn't call super.setUp()
*
* @throws Exception
*/
private void validateContainerStatus()
throws Exception
{
if ( getContainer() != null )
{
return;
}
throw new Exception( "container is null, make sure super.setUp() is called" );
}
}