diff --git a/build.properties b/build.properties index 0cca0f9001..279c30bc7c 100644 --- a/build.properties +++ b/build.properties @@ -16,6 +16,7 @@ # under the License. classworlds.version=1.2-alpha-7 +plexus-active-collections.version=1.0-SNAPSHOT plexus.version=1.0-alpha-20 plexus-archiver.version=1.0-alpha-8 plexus-utils.version=1.4.1 diff --git a/build.xml b/build.xml index e634adb353..8b63f7fd2d 100644 --- a/build.xml +++ b/build.xml @@ -116,6 +116,7 @@ under the License. + @@ -153,6 +154,7 @@ under the License. + @@ -226,6 +228,7 @@ under the License. + @@ -339,6 +342,9 @@ load ${bootstrapDir}/target/classes + diff --git a/maven-build-context/src/main/java/org/apache/maven/context/ScopedBuildContext.java b/maven-build-context/src/main/java/org/apache/maven/context/ScopedBuildContext.java new file mode 100644 index 0000000000..93f17c2d38 --- /dev/null +++ b/maven-build-context/src/main/java/org/apache/maven/context/ScopedBuildContext.java @@ -0,0 +1,109 @@ +package org.apache.maven.context; + +import java.util.HashMap; +import java.util.Map; + +public class ScopedBuildContext + implements BuildContext, ManagedBuildData +{ + + private final BuildContext parentBuildContext; + private final String scopeKey; + + private Map localContext; + + public ScopedBuildContext( String scopeKey, BuildContext parentBuildContext ) + { + this.scopeKey = scopeKey; + this.parentBuildContext = parentBuildContext; + + this.localContext = (Map) parentBuildContext.get( scopeKey ); + if ( localContext == null ) + { + this.localContext = new HashMap(); + parentBuildContext.store( this ); + } + } + + public Object delete( Object key ) + { + return localContext.remove( key ); + } + + public Object get( Object key ) + { + return localContext.get( key ); + } + + public void put( Object key, Object value ) + { + localContext.put( key, value ); + } + + /** + * @deprecated Use {@link BuildContext#store(ManagedBuildData)} instead. + */ + public void put( ManagedBuildData managedData ) + { + localContext.put( managedData.getStorageKey(), managedData.getData() ); + } + + public boolean retrieve( ManagedBuildData managedData ) + { + Map data = (Map) localContext.get( managedData.getStorageKey() ); + + if ( data != null ) + { + managedData.setData( data ); + return true; + } + else + { + return false; + } + } + + public void store( ManagedBuildData managedData ) + { + localContext.put( managedData.getStorageKey(), managedData.getData() ); + } + + public Map getData() + { + return localContext; + } + + public String getStorageKey() + { + return scopeKey; + } + + public void setData( Map data ) + { + this.localContext = data; + } + + public void storeContext( BuildContextManager buildContextManager ) + { + if ( parentBuildContext instanceof ScopedBuildContext ) + { + ((ScopedBuildContext) parentBuildContext).storeContext( buildContextManager ); + } + else + { + buildContextManager.storeBuildContext( parentBuildContext ); + } + } + + public BuildContext getParentBuildContext() + { + return parentBuildContext; + } + + public void delete( BuildContextManager buildContextManager ) + { + parentBuildContext.delete( scopeKey ); + storeContext( buildContextManager ); + } + +} diff --git a/maven-core/pom.xml b/maven-core/pom.xml index 927285799f..fa4f303960 100644 --- a/maven-core/pom.xml +++ b/maven-core/pom.xml @@ -30,6 +30,11 @@ under the License. maven-core Maven Core + + org.apache.maven + maven-lifecycle + 2.1-SNAPSHOT + org.apache.maven maven-build-context @@ -65,6 +70,10 @@ under the License. maven-artifact 2.1-SNAPSHOT + + org.codehaus.plexus + plexus-component-api + org.codehaus.plexus plexus-container-default @@ -148,5 +157,10 @@ under the License. plexus-classworlds 1.2-alpha-5 + + org.codehaus.plexus + plexus-active-collections + 1.0-SNAPSHOT + diff --git a/maven-core/src/main/java/org/apache/maven/DefaultArtifactFilterManager.java b/maven-core/src/main/java/org/apache/maven/DefaultArtifactFilterManager.java index 8afe244f54..ff83221dd7 100644 --- a/maven-core/src/main/java/org/apache/maven/DefaultArtifactFilterManager.java +++ b/maven-core/src/main/java/org/apache/maven/DefaultArtifactFilterManager.java @@ -48,6 +48,7 @@ public class DefaultArtifactFilterManager implements ArtifactFilterManager artifacts.add( "maven-build-context" ); artifacts.add( "maven-core" ); artifacts.add( "maven-error-diagnoser" ); + artifacts.add( "maven-lifecycle" ); artifacts.add( "maven-model" ); artifacts.add( "maven-monitor" ); artifacts.add( "maven-plugin-api" ); diff --git a/maven-core/src/main/java/org/apache/maven/DefaultMaven.java b/maven-core/src/main/java/org/apache/maven/DefaultMaven.java index ef1f377a3f..f7c5986095 100644 --- a/maven-core/src/main/java/org/apache/maven/DefaultMaven.java +++ b/maven-core/src/main/java/org/apache/maven/DefaultMaven.java @@ -33,6 +33,7 @@ import org.apache.maven.execution.MavenSession; import org.apache.maven.execution.ReactorManager; import org.apache.maven.execution.RuntimeInformation; +import org.apache.maven.execution.SessionContext; import org.apache.maven.extension.BuildExtensionScanner; import org.apache.maven.extension.ExtensionScanningException; import org.apache.maven.lifecycle.LifecycleExecutor; @@ -542,7 +543,12 @@ protected MavenSession createSession( MavenExecutionRequest request, ReactorManager rpm, EventDispatcher dispatcher ) { - return new MavenSession( container, request, dispatcher, rpm ); + MavenSession session = new MavenSession( container, request, dispatcher, rpm ); + + SessionContext ctx = new SessionContext( session ); + ctx.store( buildContextManager ); + + return session; } // ---------------------------------------------------------------------- diff --git a/maven-core/src/main/java/org/apache/maven/execution/MavenSession.java b/maven-core/src/main/java/org/apache/maven/execution/MavenSession.java index 40276f49fd..47024703b7 100644 --- a/maven-core/src/main/java/org/apache/maven/execution/MavenSession.java +++ b/maven-core/src/main/java/org/apache/maven/execution/MavenSession.java @@ -28,6 +28,7 @@ import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import java.util.Date; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; @@ -48,6 +49,8 @@ public class MavenSession private MavenExecutionRequest request; + private Map reports = new LinkedHashMap(); + public MavenSession( PlexusContainer container, MavenExecutionRequest request, EventDispatcher eventDispatcher, @@ -156,4 +159,5 @@ public MavenExecutionRequest getRequest() { return request; } + } \ No newline at end of file diff --git a/maven-core/src/main/java/org/apache/maven/execution/SessionContext.java b/maven-core/src/main/java/org/apache/maven/execution/SessionContext.java new file mode 100644 index 0000000000..f1f2720c38 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/execution/SessionContext.java @@ -0,0 +1,157 @@ +package org.apache.maven.execution; + +/* + * 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 org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.context.BuildContext; +import org.apache.maven.context.BuildContextManager; +import org.apache.maven.context.ManagedBuildData; +import org.apache.maven.monitor.event.EventDispatcher; +import org.apache.maven.settings.Settings; + +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * Build context object that supplies information about how Maven was invoked, including all of the + * information available in the MavenExecutionRequest (in read-only form). + */ +public class SessionContext + implements ManagedBuildData +{ + + public static final String BUILD_CONTEXT_KEY = SessionContext.class.getName(); + + private static final String REQUEST_KEY = "request"; + + private MavenSession session; + + private SessionContext() + { + } + + public SessionContext( MavenSession session ) + { + this.session = session; + } + + public String getStorageKey() + { + return BUILD_CONTEXT_KEY; + } + + public static SessionContext read( BuildContextManager buildContextManager ) + { + BuildContext buildContext = buildContextManager.readBuildContext( false ); + + SessionContext sessionContext = new SessionContext(); + + if ( buildContext != null ) + { + if ( !buildContext.retrieve( sessionContext ) ) + { + return null; + } + } + + return sessionContext; + } + + public void store( BuildContextManager buildContextManager ) + { + BuildContext buildContext = buildContextManager.readBuildContext( true ); + + buildContext.store( this ); + + buildContextManager.storeBuildContext( buildContext ); + } + + public Map getData() + { + return Collections.singletonMap( REQUEST_KEY, session ); + } + + public void setData( Map data ) + { + this.session = (MavenSession) data.get( REQUEST_KEY ); + } + + //------------------------------------------------------------ + // DELEGATED METHODS, USING DEFENSIVE COPIES WHERE POSSIBLE. + //------------------------------------------------------------ + + public EventDispatcher getEventDispatcher() + { + return session.getEventDispatcher(); + } + + public Properties getExecutionProperties() + { + return new Properties( session.getExecutionProperties() ); + } + + public String getExecutionRootDirectory() + { + return session.getExecutionRootDirectory(); + } + + public List getGoals() + { + return Collections.unmodifiableList( session.getGoals() ); + } + + public ArtifactRepository getLocalRepository() + { + return session.getLocalRepository(); + } + + public MavenExecutionRequest getRequest() + { + return session.getRequest(); + } + + public Settings getSettings() + { + return session.getSettings(); + } + + public List getSortedProjects() + { + return Collections.unmodifiableList( session.getSortedProjects() ); + } + + public Date getStartTime() + { + return new Date( session.getStartTime().getTime() ); + } + + public boolean isUsingPOMsFromFilesystem() + { + return session.isUsingPOMsFromFilesystem(); + } + + public MavenSession getSession() + { + return session; + } +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycleExecutor.java b/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycleExecutor.java index 5275cb6862..9b7f2b4250 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycleExecutor.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycleExecutor.java @@ -25,13 +25,16 @@ import org.apache.maven.artifact.resolver.ArtifactNotFoundException; import org.apache.maven.artifact.resolver.ArtifactResolutionException; import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.context.BuildContextManager; import org.apache.maven.execution.MavenSession; import org.apache.maven.execution.ReactorManager; -import org.apache.maven.lifecycle.mapping.LifecycleMapping; +import org.apache.maven.lifecycle.binding.LifecycleBindingManager; +import org.apache.maven.lifecycle.binding.MojoBindingFactory; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.lifecycle.plan.BuildPlan; +import org.apache.maven.lifecycle.plan.BuildPlanUtils; +import org.apache.maven.lifecycle.plan.BuildPlanner; import org.apache.maven.model.Plugin; -import org.apache.maven.model.PluginExecution; -import org.apache.maven.model.ReportPlugin; -import org.apache.maven.model.ReportSet; import org.apache.maven.monitor.event.EventDispatcher; import org.apache.maven.monitor.event.MavenEvents; import org.apache.maven.plugin.InvalidPluginException; @@ -44,29 +47,22 @@ import org.apache.maven.plugin.PluginNotFoundException; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; -import org.apache.maven.plugin.lifecycle.Execution; -import org.apache.maven.plugin.lifecycle.Phase; +import org.apache.maven.plugin.loader.PluginLoader; +import org.apache.maven.plugin.loader.PluginLoaderException; import org.apache.maven.plugin.version.PluginVersionNotFoundException; import org.apache.maven.plugin.version.PluginVersionResolutionException; import org.apache.maven.project.MavenProject; import org.apache.maven.project.artifact.InvalidDependencyVersionException; -import org.apache.maven.reporting.MavenReport; -import org.apache.maven.settings.Settings; import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.codehaus.plexus.logging.AbstractLogEnabled; -import org.codehaus.plexus.util.StringUtils; import org.codehaus.plexus.util.xml.Xpp3Dom; -import org.codehaus.plexus.util.xml.pull.XmlPullParserException; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Stack; -import java.util.StringTokenizer; /** * @author Jason van Zyl @@ -84,14 +80,18 @@ public class DefaultLifecycleExecutor // ---------------------------------------------------------------------- private PluginManager pluginManager; - - private List lifecycles; + + private PluginLoader pluginLoader; + + private BuildPlanner buildPlanner; private ArtifactHandlerManager artifactHandlerManager; - private List defaultReports; + private MojoBindingFactory mojoBindingFactory; - private Map phaseToLifecycleMap; + private LifecycleBindingManager lifecycleBindingManager; + + private BuildContextManager buildContextManager; // ---------------------------------------------------------------------- // @@ -165,26 +165,41 @@ private void executeTaskSegments( List taskSegments, ReactorManager rm, MavenSes getLogger().info( " " + segment ); line(); + + String target = rootProject.getId() + " ( " + segment + " )"; + getLogger().debug( "Constructing build plan for " + target ); + // !! This is ripe for refactoring to an aspect. // Event monitoring. String event = MavenEvents.PROJECT_EXECUTION; long buildStartTime = System.currentTimeMillis(); - String target = rootProject.getId() + " ( " + segment + " )"; - dispatcher.dispatchStart( event, target ); + // NEW: To support forked execution under the new lifecycle architecture, the current project + // is stored in a build-context managed data type. This context type holds the current project + // for the fork being executed, plus a stack of projects used in the ancestor execution contexts. + LifecycleExecutionContext ctx = new LifecycleExecutionContext( rootProject ); + ctx.store( buildContextManager ); + + // NEW: Build up the execution plan, including configuration. + List mojoBindings = getLifecycleBindings( segment.getTasks(), rootProject, target ); + + // NEW: Then, iterate over each binding in that plan, and execute the associated mojo. // only call once, with the top-level project (assumed to be provided as a parameter)... - for ( Iterator goalIterator = segment.getTasks().iterator(); goalIterator.hasNext(); ) + for ( Iterator mojoIterator = mojoBindings.iterator(); mojoIterator.hasNext(); ) { - String task = (String) goalIterator.next(); + MojoBinding binding = (MojoBinding) mojoIterator.next(); - executeGoalAndHandleFailures( task, session, rootProject, dispatcher, event, rm, buildStartTime, + executeGoalAndHandleFailures( binding, session, dispatcher, event, rm, buildStartTime, target ); } - + + // clean up the execution context, so we don't pollute for future project-executions. + LifecycleExecutionContext.delete( buildContextManager ); + rm.registerBuildSuccess( rootProject, System.currentTimeMillis() - buildStartTime ); dispatcher.dispatchEnd( event, target ); @@ -222,23 +237,32 @@ private void executeTaskSegments( List taskSegments, ReactorManager rm, MavenSes line(); + String target = currentProject.getId() + " ( " + segment + " )"; + // !! This is ripe for refactoring to an aspect. // Event monitoring. String event = MavenEvents.PROJECT_EXECUTION; long buildStartTime = System.currentTimeMillis(); - String target = currentProject.getId() + " ( " + segment + " )"; dispatcher.dispatchStart( event, target ); + + LifecycleExecutionContext ctx = new LifecycleExecutionContext( currentProject ); + ctx.store( buildContextManager ); - for ( Iterator goalIterator = segment.getTasks().iterator(); goalIterator.hasNext(); ) + List mojoBindings = getLifecycleBindings( segment.getTasks(), currentProject, target ); + + for ( Iterator mojoIterator = mojoBindings.iterator(); mojoIterator.hasNext(); ) { - String task = (String) goalIterator.next(); + MojoBinding mojoBinding = (MojoBinding) mojoIterator.next(); - executeGoalAndHandleFailures( task, session, currentProject, dispatcher, event, rm, + getLogger().debug( "Mojo: " + mojoBinding.getGoal() + " has config:\n" + mojoBinding.getConfiguration() ); + executeGoalAndHandleFailures( mojoBinding, session, dispatcher, event, rm, buildStartTime, target ); } + LifecycleExecutionContext.delete( buildContextManager ); + rm.registerBuildSuccess( currentProject, System.currentTimeMillis() - buildStartTime ); dispatcher.dispatchEnd( event, target ); @@ -261,20 +285,120 @@ private void executeTaskSegments( List taskSegments, ReactorManager rm, MavenSes } } - private void executeGoalAndHandleFailures( String task, MavenSession session, MavenProject project, - EventDispatcher dispatcher, String event, ReactorManager rm, + /** + * Retrieves the build plan for the current project, given the specified list of tasks. This + * build plan will consist of MojoBindings, each fully configured to execute, which enables us + * to enumerate the full build plan to the debug log-level, complete with the configuration each + * mojo will use. + */ + private List getLifecycleBindings( List tasks, MavenProject project, String targetDescription ) + throws LifecycleExecutionException + { + List mojoBindings; + try + { + BuildPlan plan = buildPlanner.constructBuildPlan( tasks, project ); + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "\n\nOur build plan is:\n" + BuildPlanUtils.listBuildPlan( plan, project, lifecycleBindingManager, false ) + "\n\n" ); + } + + mojoBindings = plan.getPlanMojoBindings( project, lifecycleBindingManager ); + } + catch ( LifecycleException e ) + { + throw new LifecycleExecutionException( "Failed to construct build plan for: " + targetDescription + ". Reason: " + + e.getMessage(), e ); + } + + return mojoBindings; + } + + private void executeGoalAndHandleFailures( MojoBinding mojoBinding, MavenSession session, EventDispatcher dispatcher, String event, ReactorManager rm, long buildStartTime, String target ) throws BuildFailureException, LifecycleExecutionException { + // NEW: Retrieve/use the current project stored in the execution context, for consistency. + LifecycleExecutionContext ctx = LifecycleExecutionContext.read( buildContextManager ); + MavenProject project = ctx.getCurrentProject(); + + // NEW: Since the MojoBinding instances are configured when the build plan is constructed, + // all that remains to be done here is to load the PluginDescriptor, construct a MojoExecution + // instance, and call PluginManager.executeMojo( execution ). The MojoExecutor is constructed + // using both the PluginDescriptor and the MojoBinding. try { - executeGoal( task, session, project ); + PluginDescriptor pluginDescriptor = null; + try + { + pluginDescriptor = pluginLoader.loadPlugin( mojoBinding, project ); + } + catch ( PluginLoaderException e ) + { + if ( mojoBinding.isOptional() ) + { + getLogger().debug( "Skipping optional mojo execution: " + MojoBindingUtils.toString( mojoBinding ) ); + } + else + { + throw new LifecycleExecutionException( "Failed to load plugin for: " + MojoBindingUtils.toString( mojoBinding ) + + ". Reason: " + e.getMessage(), e ); + } + } + + if ( pluginDescriptor != null ) + { + MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( mojoBinding.getGoal() ); + MojoExecution mojoExecution = new MojoExecution( mojoDescriptor ); + + mojoExecution.setConfiguration( (Xpp3Dom) mojoBinding.getConfiguration() ); + + try + { + pluginManager.executeMojo( project, mojoExecution, session ); + } + catch ( PluginManagerException e ) + { + throw new LifecycleExecutionException( "Internal error in the plugin manager executing goal '" + + mojoDescriptor.getId() + "': " + e.getMessage(), e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new LifecycleExecutionException( e.getMessage(), e ); + } + catch ( InvalidDependencyVersionException e ) + { + throw new LifecycleExecutionException( e.getMessage(), e ); + } + catch ( ArtifactResolutionException e ) + { + throw new LifecycleExecutionException( e.getMessage(), e ); + } + catch ( MojoFailureException e ) + { + throw new BuildFailureException( e.getMessage(), e ); + } + catch ( MojoExecutionException e ) + { + throw new LifecycleExecutionException( e.getMessage(), e ); + } + catch ( PluginConfigurationException e ) + { + throw new LifecycleExecutionException( e.getMessage(), e ); + } + } + else + { + throw new LifecycleExecutionException( "Failed to load plugin for: " + MojoBindingUtils.toString( mojoBinding ) + + ". Reason: unknown" ); + } } catch ( LifecycleExecutionException e ) { dispatcher.dispatchError( event, target, e ); - if ( handleExecutionFailure( rm, project, e, task, buildStartTime ) ) + if ( handleExecutionFailure( rm, project, e, mojoBinding, buildStartTime ) ) { throw e; } @@ -283,17 +407,17 @@ private void executeGoalAndHandleFailures( String task, MavenSession session, Ma { dispatcher.dispatchError( event, target, e ); - if ( handleExecutionFailure( rm, project, e, task, buildStartTime ) ) + if ( handleExecutionFailure( rm, project, e, mojoBinding, buildStartTime ) ) { throw e; } } } - private boolean handleExecutionFailure( ReactorManager rm, MavenProject project, Exception e, String task, + private boolean handleExecutionFailure( ReactorManager rm, MavenProject project, Exception e, MojoBinding mojoBinding, long buildStartTime ) { - rm.registerBuildFailure( project, e, task, System.currentTimeMillis() - buildStartTime ); + rm.registerBuildFailure( project, e, MojoBindingUtils.toString( mojoBinding ), System.currentTimeMillis() - buildStartTime ); if ( ReactorManager.FAIL_FAST.equals( rm.getFailureBehavior() ) ) { @@ -307,12 +431,12 @@ else if ( ReactorManager.FAIL_AT_END.equals( rm.getFailureBehavior() ) ) return false; } - private List segmentTaskListByAggregationNeeds( List tasks, MavenSession session, MavenProject project ) + private List segmentTaskListByAggregationNeeds( List tasks, MavenSession session, MavenProject rootProject ) throws LifecycleExecutionException, BuildFailureException { List segments = new ArrayList(); - if ( project != null ) + if ( rootProject != null ) { TaskSegment currentSegment = null; @@ -322,7 +446,7 @@ private List segmentTaskListByAggregationNeeds( List tasks, MavenSession session // if it's a phase, then we don't need to check whether it's an aggregator. // simply add it to the current task partition. - if ( getPhaseToLifecycleMap().containsKey( task ) ) + if ( LifecycleUtils.isValidPhaseName( task ) ) { if ( currentSegment != null && currentSegment.aggregate() ) { @@ -340,18 +464,33 @@ private List segmentTaskListByAggregationNeeds( List tasks, MavenSession session else { MojoDescriptor mojo = null; + // definitely a CLI goal, can use prefix try { - // definitely a CLI goal, can use prefix - mojo = getMojoDescriptor( task, session, project, task, true, false ); + mojo = getMojoDescriptorForDirectInvocation( task, session, rootProject ); } - catch ( PluginNotFoundException e ) + catch ( PluginLoaderException e ) { - // TODO: shouldn't hit this, investigate using the same resolution logic as otheres for plugins in the reactor + // TODO: shouldn't hit this, investigate using the same resolution logic as + // others for plugins in the reactor getLogger().info( "Cannot find mojo descriptor for: \'" + task + "\' - Treating as non-aggregator." ); + getLogger().debug( "", e ); } + catch ( LifecycleSpecificationException e ) + { + String message = "Invalid task '" + task + "': you must specify a valid lifecycle phase, or" + + " a goal in the format plugin:goal or pluginGroupId:pluginArtifactId:pluginVersion:goal"; + + throw new BuildFailureException( message, e ); + } + catch ( LifecycleLoaderException e ) + { + String message = "Cannot find plugin to match task '" + task + "'."; + + throw new BuildFailureException( message, e ); + } // if the mojo descriptor was found, determine aggregator status according to: // 1. whether the mojo declares itself an aggregator @@ -404,717 +543,6 @@ private List segmentTaskListByAggregationNeeds( List tasks, MavenSession session return segments; } - private void executeGoal( String task, MavenSession session, MavenProject project ) - throws LifecycleExecutionException, BuildFailureException - { - try - { - Stack forkEntryPoints = new Stack(); - if ( getPhaseToLifecycleMap().containsKey( task ) ) - { - Lifecycle lifecycle = getLifecycleForPhase( task ); - - // we have a lifecycle phase, so lets bind all the necessary goals - Map lifecycleMappings = constructLifecycleMappings( session, task, project, lifecycle ); - executeGoalWithLifecycle( task, forkEntryPoints, session, lifecycleMappings, project, lifecycle ); - } - else - { - executeStandaloneGoal( task, forkEntryPoints, session, project ); - } - } - catch ( PluginNotFoundException e ) - { - throw new BuildFailureException( "A required plugin was not found: " + e.getMessage(), e ); - } - } - - private void executeGoalWithLifecycle( String task, Stack forkEntryPoints, MavenSession session, - Map lifecycleMappings, MavenProject project, Lifecycle lifecycle ) - throws LifecycleExecutionException, BuildFailureException, PluginNotFoundException - { - List goals = processGoalChain( task, lifecycleMappings, lifecycle ); - - if ( !goals.isEmpty() ) - { - executeGoals( goals, forkEntryPoints, session, project ); - } - else - { - getLogger().info( "No goals needed for project - skipping" ); - } - } - - private void executeStandaloneGoal( String task, Stack forkEntryPoints, MavenSession session, MavenProject project ) - throws LifecycleExecutionException, BuildFailureException, PluginNotFoundException - { - // guaranteed to come from the CLI and not be part of a phase - MojoDescriptor mojoDescriptor = getMojoDescriptor( task, session, project, task, true, false ); - executeGoals( Collections.singletonList( new MojoExecution( mojoDescriptor ) ), forkEntryPoints, session, - project ); - } - - private void executeGoals( List goals, Stack forkEntryPoints, MavenSession session, MavenProject project ) - throws LifecycleExecutionException, BuildFailureException, PluginNotFoundException - { - for ( Iterator i = goals.iterator(); i.hasNext(); ) - { - MojoExecution mojoExecution = (MojoExecution) i.next(); - - MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); - - if ( mojoDescriptor.getExecutePhase() != null || mojoDescriptor.getExecuteGoal() != null ) - { - forkEntryPoints.push( mojoDescriptor ); - - forkLifecycle( mojoDescriptor, forkEntryPoints, session, project ); - - forkEntryPoints.pop(); - } - - if ( mojoDescriptor.isRequiresReports() ) - { - List reports = getReports( project, mojoExecution, session ); - - mojoExecution.setReports( reports ); - - for ( Iterator j = mojoExecution.getForkedExecutions().iterator(); j.hasNext(); ) - { - MojoExecution forkedExecution = (MojoExecution) j.next(); - MojoDescriptor descriptor = forkedExecution.getMojoDescriptor(); - - if ( descriptor.getExecutePhase() != null ) - { - forkEntryPoints.push( descriptor ); - - forkLifecycle( descriptor, forkEntryPoints, session, project ); - - forkEntryPoints.pop(); - } - } - } - - try - { - pluginManager.executeMojo( project, mojoExecution, session ); - } - catch ( PluginManagerException e ) - { - throw new LifecycleExecutionException( "Internal error in the plugin manager executing goal '" + - mojoDescriptor.getId() + "': " + e.getMessage(), e ); - } - catch ( ArtifactNotFoundException e ) - { - throw new LifecycleExecutionException( e.getMessage(), e ); - } - catch ( InvalidDependencyVersionException e ) - { - throw new LifecycleExecutionException( e.getMessage(), e ); - } - catch ( ArtifactResolutionException e ) - { - throw new LifecycleExecutionException( e.getMessage(), e ); - } - catch ( MojoFailureException e ) - { - throw new BuildFailureException( e.getMessage(), e ); - } - catch ( MojoExecutionException e ) - { - throw new LifecycleExecutionException( e.getMessage(), e ); - } - catch ( PluginConfigurationException e ) - { - throw new LifecycleExecutionException( e.getMessage(), e ); - } - } - } - - private List getReports( MavenProject project, MojoExecution mojoExecution, MavenSession session ) - throws LifecycleExecutionException, PluginNotFoundException - { - List reportPlugins = project.getReportPlugins(); - - if ( project.getModel().getReports() != null ) - { - getLogger().error( - "Plugin contains a section: this is IGNORED - please use instead." ); - } - - if ( project.getReporting() == null || !project.getReporting().isExcludeDefaults() ) - { - if ( reportPlugins == null ) - { - reportPlugins = new ArrayList(); - } - else - { - reportPlugins = new ArrayList( reportPlugins ); - } - - for ( Iterator i = defaultReports.iterator(); i.hasNext(); ) - { - String report = (String) i.next(); - - StringTokenizer tok = new StringTokenizer( report, ":" ); - if ( tok.countTokens() != 2 ) - { - getLogger().warn( "Invalid default report ignored: '" + report + "' (must be groupId:artifactId)" ); - } - else - { - String groupId = tok.nextToken(); - String artifactId = tok.nextToken(); - - boolean found = false; - for ( Iterator j = reportPlugins.iterator(); j.hasNext() && !found; ) - { - ReportPlugin reportPlugin = (ReportPlugin) j.next(); - if ( reportPlugin.getGroupId().equals( groupId ) && - reportPlugin.getArtifactId().equals( artifactId ) ) - { - found = true; - } - } - - if ( !found ) - { - ReportPlugin reportPlugin = new ReportPlugin(); - reportPlugin.setGroupId( groupId ); - reportPlugin.setArtifactId( artifactId ); - reportPlugins.add( reportPlugin ); - } - } - } - } - - List reports = new ArrayList(); - if ( reportPlugins != null ) - { - for ( Iterator it = reportPlugins.iterator(); it.hasNext(); ) - { - ReportPlugin reportPlugin = (ReportPlugin) it.next(); - - List reportSets = reportPlugin.getReportSets(); - - if ( reportSets == null || reportSets.isEmpty() ) - { - reports.addAll( getReports( reportPlugin, null, project, session, mojoExecution ) ); - } - else - { - for ( Iterator j = reportSets.iterator(); j.hasNext(); ) - { - ReportSet reportSet = (ReportSet) j.next(); - - reports.addAll( getReports( reportPlugin, reportSet, project, session, mojoExecution ) ); - } - } - } - } - return reports; - } - - private List getReports( ReportPlugin reportPlugin, ReportSet reportSet, MavenProject project, MavenSession session, - MojoExecution mojoExecution ) - throws LifecycleExecutionException, PluginNotFoundException - { - PluginDescriptor pluginDescriptor = verifyReportPlugin( reportPlugin, project, session ); - - List reports = new ArrayList(); - for ( Iterator i = pluginDescriptor.getMojos().iterator(); i.hasNext(); ) - { - MojoDescriptor mojoDescriptor = (MojoDescriptor) i.next(); - - // TODO: check ID is correct for reports - // if the POM configured no reports, give all from plugin - if ( reportSet == null || reportSet.getReports().contains( mojoDescriptor.getGoal() ) ) - { - String id = null; - if ( reportSet != null ) - { - id = reportSet.getId(); - } - - MojoExecution reportExecution = new MojoExecution( mojoDescriptor, id ); - - try - { - MavenReport reportMojo = pluginManager.getReport( project, reportExecution, session ); - - // Comes back null if it was a plugin, not a report - these are mojos in the reporting plugins that are not reports - if ( reportMojo != null ) - { - reports.add( reportMojo ); - mojoExecution.addMojoExecution( reportExecution ); - } - } - catch ( PluginManagerException e ) - { - throw new LifecycleExecutionException( - "Error getting reports from the plugin '" + reportPlugin.getKey() + "': " + e.getMessage(), e ); - } - catch ( PluginConfigurationException e ) - { - throw new LifecycleExecutionException( - "Error getting reports from the plugin '" + reportPlugin.getKey() + "'", e ); - } - catch ( ArtifactNotFoundException e ) - { - throw new LifecycleExecutionException( e.getMessage(), e ); - } - catch ( ArtifactResolutionException e ) - { - throw new LifecycleExecutionException( e.getMessage(), e ); - } - } - } - return reports; - } - - private void forkLifecycle( MojoDescriptor mojoDescriptor, Stack ancestorLifecycleForkers, MavenSession session, - MavenProject project ) - throws LifecycleExecutionException, BuildFailureException, PluginNotFoundException - { - PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor(); - getLogger().info( "Preparing " + pluginDescriptor.getGoalPrefix() + ":" + mojoDescriptor.getGoal() ); - - if ( mojoDescriptor.isAggregator() ) - { - for ( Iterator i = session.getSortedProjects().iterator(); i.hasNext(); ) - { - MavenProject reactorProject = (MavenProject) i.next(); - - line(); - - getLogger().info( "Building " + reactorProject.getName() ); - - line(); - - forkProjectLifecycle( mojoDescriptor, ancestorLifecycleForkers, session, reactorProject ); - } - } - else - { - forkProjectLifecycle( mojoDescriptor, ancestorLifecycleForkers, session, project ); - } - } - - private void forkProjectLifecycle( MojoDescriptor mojoDescriptor, Stack forkEntryPoints, MavenSession session, - MavenProject project ) - throws LifecycleExecutionException, BuildFailureException, PluginNotFoundException - { - forkEntryPoints.push( mojoDescriptor ); - - PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor(); - - String targetPhase = mojoDescriptor.getExecutePhase(); - - Map lifecycleMappings = null; - if ( targetPhase != null ) - { - Lifecycle lifecycle = getLifecycleForPhase( targetPhase ); - - // Create new lifecycle - lifecycleMappings = constructLifecycleMappings( session, targetPhase, project, lifecycle ); - - String executeLifecycle = mojoDescriptor.getExecuteLifecycle(); - if ( executeLifecycle != null ) - { - org.apache.maven.plugin.lifecycle.Lifecycle lifecycleOverlay; - try - { - lifecycleOverlay = pluginDescriptor.getLifecycleMapping( executeLifecycle ); - } - catch ( IOException e ) - { - throw new LifecycleExecutionException( "Unable to read lifecycle mapping file: " + e.getMessage(), - e ); - } - catch ( XmlPullParserException e ) - { - throw new LifecycleExecutionException( "Unable to parse lifecycle mapping file: " + e.getMessage(), - e ); - } - - if ( lifecycleOverlay == null ) - { - throw new LifecycleExecutionException( "Lifecycle '" + executeLifecycle + "' not found in plugin" ); - } - - for ( Iterator i = lifecycleOverlay.getPhases().iterator(); i.hasNext(); ) - { - Phase phase = (Phase) i.next(); - for ( Iterator j = phase.getExecutions().iterator(); j.hasNext(); ) - { - Execution exec = (Execution) j.next(); - - for ( Iterator k = exec.getGoals().iterator(); k.hasNext(); ) - { - String goal = (String) k.next(); - - PluginDescriptor lifecyclePluginDescriptor; - String lifecycleGoal; - - // Here we are looking to see if we have a mojo from an external plugin. - // If we do then we need to lookup the plugin descriptor for the externally - // referenced plugin so that we can overly the execution into the lifecycle. - // An example of this is the corbertura plugin that needs to call the surefire - // plugin in forking mode. - // - // - // test - // - // - // - // org.apache.maven.plugins:maven-surefire-plugin:test - // - // - // ${project.build.directory}/generated-classes/cobertura - // true - // once - // - // - // - // - - // ---------------------------------------------------------------------- - // - // ---------------------------------------------------------------------- - - if ( goal.indexOf( ":" ) > 0 ) - { - String[] s = StringUtils.split( goal, ":" ); - - String groupId = s[0]; - String artifactId = s[1]; - lifecycleGoal = s[2]; - - Plugin plugin = new Plugin(); - plugin.setGroupId( groupId ); - plugin.setArtifactId( artifactId ); - lifecyclePluginDescriptor = verifyPlugin( plugin, project, session ); - if ( lifecyclePluginDescriptor == null ) - { - throw new LifecycleExecutionException( - "Unable to find plugin " + groupId + ":" + artifactId ); - } - } - else - { - lifecyclePluginDescriptor = pluginDescriptor; - lifecycleGoal = goal; - } - - Xpp3Dom configuration = (Xpp3Dom) exec.getConfiguration(); - if ( phase.getConfiguration() != null ) - { - configuration = Xpp3Dom.mergeXpp3Dom( new Xpp3Dom( (Xpp3Dom) phase.getConfiguration() ), - configuration ); - } - - MojoDescriptor desc = getMojoDescriptor( lifecyclePluginDescriptor, lifecycleGoal ); - MojoExecution mojoExecution = new MojoExecution( desc, configuration ); - addToLifecycleMappings( lifecycleMappings, phase.getId(), mojoExecution, - session.getSettings() ); - } - } - - if ( phase.getConfiguration() != null ) - { - // Merge in general configuration for a phase. - // TODO: this is all kind of backwards from the POMM. Let's align it all under 2.1. - // We should create a new lifecycle executor for modelVersion >5.0.0 - for ( Iterator j = lifecycleMappings.values().iterator(); j.hasNext(); ) - { - List tasks = (List) j.next(); - - for ( Iterator k = tasks.iterator(); k.hasNext(); ) - { - MojoExecution exec = (MojoExecution) k.next(); - - Xpp3Dom configuration = Xpp3Dom.mergeXpp3Dom( - new Xpp3Dom( (Xpp3Dom) phase.getConfiguration() ), exec.getConfiguration() ); - - exec.setConfiguration( configuration ); - } - } - } - - } - } - - removeFromLifecycle( forkEntryPoints, lifecycleMappings ); - } - - MavenProject executionProject = new MavenProject( project ); - if ( targetPhase != null ) - { - Lifecycle lifecycle = getLifecycleForPhase( targetPhase ); - - executeGoalWithLifecycle( targetPhase, forkEntryPoints, session, lifecycleMappings, executionProject, - lifecycle ); - } - else - { - String goal = mojoDescriptor.getExecuteGoal(); - MojoDescriptor desc = getMojoDescriptor( pluginDescriptor, goal ); - executeGoals( Collections.singletonList( new MojoExecution( desc ) ), forkEntryPoints, session, - executionProject ); - } - project.setExecutionProject( executionProject ); - } - - private Lifecycle getLifecycleForPhase( String phase ) - throws BuildFailureException, LifecycleExecutionException - { - Lifecycle lifecycle = (Lifecycle) getPhaseToLifecycleMap().get( phase ); - - if ( lifecycle == null ) - { - throw new BuildFailureException( "Unable to find lifecycle for phase '" + phase + "'" ); - } - return lifecycle; - } - - private MojoDescriptor getMojoDescriptor( PluginDescriptor pluginDescriptor, String goal ) - throws LifecycleExecutionException - { - MojoDescriptor desc = pluginDescriptor.getMojo( goal ); - - if ( desc == null ) - { - String message = - "Required goal '" + goal + "' not found in plugin '" + pluginDescriptor.getGoalPrefix() + "'"; - int index = goal.indexOf( ':' ); - if ( index >= 0 ) - { - String prefix = goal.substring( index + 1 ); - if ( prefix.equals( pluginDescriptor.getGoalPrefix() ) ) - { - message = message + " (goals should not be prefixed - try '" + prefix + "')"; - } - } - throw new LifecycleExecutionException( message ); - } - return desc; - } - - private void removeFromLifecycle( Stack lifecycleForkers, Map lifecycleMappings ) - { - for ( Iterator it = lifecycleForkers.iterator(); it.hasNext(); ) - { - MojoDescriptor mojoDescriptor = (MojoDescriptor) it.next(); - - for ( Iterator lifecycleIterator = lifecycleMappings.values().iterator(); lifecycleIterator.hasNext(); ) - { - List tasks = (List) lifecycleIterator.next(); - - boolean removed = false; - for ( Iterator taskIterator = tasks.iterator(); taskIterator.hasNext(); ) - { - MojoExecution execution = (MojoExecution) taskIterator.next(); - - if ( mojoDescriptor.equals( execution.getMojoDescriptor() ) ) - { - taskIterator.remove(); - removed = true; - } - } - - if ( removed ) - { - getLogger().warn( "Removing: " + mojoDescriptor.getGoal() + - " from forked lifecycle, to prevent recursive invocation." ); - } - } - } - } - - private Map constructLifecycleMappings( MavenSession session, String selectedPhase, MavenProject project, - Lifecycle lifecycle ) - throws LifecycleExecutionException, BuildFailureException, PluginNotFoundException - { - // first, bind those associated with the packaging - Map lifecycleMappings = bindLifecycleForPackaging( session, selectedPhase, project, lifecycle ); - - // next, loop over plugins and for any that have a phase, bind it - for ( Iterator i = project.getBuildPlugins().iterator(); i.hasNext(); ) - { - Plugin plugin = (Plugin) i.next(); - - bindPluginToLifecycle( plugin, session, lifecycleMappings, project ); - } - - return lifecycleMappings; - } - - private Map bindLifecycleForPackaging( MavenSession session, String selectedPhase, MavenProject project, - Lifecycle lifecycle ) - throws LifecycleExecutionException, BuildFailureException, PluginNotFoundException - { - Map mappings = findMappingsForLifecycle( session, project, lifecycle ); - - List optionalMojos = findOptionalMojosForLifecycle( session, project, lifecycle ); - - Map lifecycleMappings = new HashMap(); - - for ( Iterator i = lifecycle.getPhases().iterator(); i.hasNext(); ) - { - String phase = (String) i.next(); - - String phaseTasks = (String) mappings.get( phase ); - - if ( phaseTasks != null ) - { - for ( StringTokenizer tok = new StringTokenizer( phaseTasks, "," ); tok.hasMoreTokens(); ) - { - String goal = tok.nextToken().trim(); - - // Not from the CLI, don't use prefix - MojoDescriptor mojoDescriptor = getMojoDescriptor( goal, session, project, selectedPhase, false, - optionalMojos.contains( goal ) ); - - if ( mojoDescriptor == null ) - { - continue; - } - - if ( mojoDescriptor.isDirectInvocationOnly() ) - { - throw new LifecycleExecutionException( "Mojo: \'" + goal + - "\' requires direct invocation. It cannot be used as part of lifecycle: \'" + - project.getPackaging() + "\'." ); - } - - addToLifecycleMappings( lifecycleMappings, phase, new MojoExecution( mojoDescriptor ), - session.getSettings() ); - } - } - - if ( phase.equals( selectedPhase ) ) - { - break; - } - } - - return lifecycleMappings; - } - - private Map findMappingsForLifecycle( MavenSession session, MavenProject project, Lifecycle lifecycle ) - throws LifecycleExecutionException, PluginNotFoundException - { - String packaging = project.getPackaging(); - Map mappings = null; - - LifecycleMapping m = (LifecycleMapping) findExtension( project, LifecycleMapping.ROLE, packaging, session ); - if ( m != null ) - { - mappings = m.getPhases( lifecycle.getId() ); - } - - Map defaultMappings = lifecycle.getDefaultPhases(); - - if ( mappings == null ) - { - try - { - m = (LifecycleMapping) session.lookup( LifecycleMapping.ROLE, packaging ); - mappings = m.getPhases( lifecycle.getId() ); - } - catch ( ComponentLookupException e ) - { - if ( defaultMappings == null ) - { - throw new LifecycleExecutionException( - "Cannot find lifecycle mapping for packaging: \'" + packaging + "\'.", e ); - } - } - } - - if ( mappings == null ) - { - if ( defaultMappings == null ) - { - throw new LifecycleExecutionException( - "Cannot find lifecycle mapping for packaging: \'" + packaging + "\', and there is no default" ); - } - else - { - mappings = defaultMappings; - } - } - - return mappings; - } - - private List findOptionalMojosForLifecycle( MavenSession session, MavenProject project, Lifecycle lifecycle ) - throws LifecycleExecutionException, PluginNotFoundException - { - String packaging = project.getPackaging(); - List optionalMojos = null; - - LifecycleMapping m = (LifecycleMapping) findExtension( project, LifecycleMapping.ROLE, packaging, session ); - - if ( m != null ) - { - optionalMojos = m.getOptionalMojos( lifecycle.getId() ); - } - - if ( optionalMojos == null ) - { - try - { - m = (LifecycleMapping) session.lookup( LifecycleMapping.ROLE, packaging ); - optionalMojos = m.getOptionalMojos( lifecycle.getId() ); - } - catch ( ComponentLookupException e ) - { - getLogger().debug( "Error looking up lifecycle mapping to retrieve optional mojos. Lifecycle ID: " + - lifecycle.getId() + ". Error: " + e.getMessage(), e ); - } - } - - if ( optionalMojos == null ) - { - optionalMojos = Collections.EMPTY_LIST; - } - - return optionalMojos; - } - - private Object findExtension( MavenProject project, String role, String roleHint, MavenSession session ) - throws LifecycleExecutionException, PluginNotFoundException - { - Object pluginComponent = null; - - for ( Iterator i = project.getBuildPlugins().iterator(); i.hasNext() && pluginComponent == null; ) - { - Plugin plugin = (Plugin) i.next(); - - if ( plugin.isExtensions() ) - { - verifyPlugin( plugin, project, session ); - - // TODO: if moved to the plugin manager we already have the descriptor from above and so do can lookup the container directly - try - { - pluginComponent = pluginManager.getPluginComponent( plugin, role, roleHint ); - } - catch ( ComponentLookupException e ) - { - getLogger().debug( "Unable to find the lifecycle component in the extension", e ); - } - catch ( PluginManagerException e ) - { - throw new LifecycleExecutionException( - "Error getting extensions from the plugin '" + plugin.getKey() + "': " + e.getMessage(), e ); - } - } - } - return pluginComponent; - } - /** * @todo Not particularly happy about this. Would like WagonManager and ArtifactTypeHandlerManager to be able to * lookup directly, or have them passed in @@ -1168,48 +596,6 @@ private Map findArtifactTypeHandlers( MavenSession session ) return map; } - /** - * Take each mojo contained with a plugin, look to see whether it contributes to a - * phase in the lifecycle and if it does place it at the end of the list of goals - * to execute for that given phase. - * - * @param project - * @param session - */ - private void bindPluginToLifecycle( Plugin plugin, MavenSession session, Map phaseMap, MavenProject project ) - throws LifecycleExecutionException, PluginNotFoundException - { - Settings settings = session.getSettings(); - - PluginDescriptor pluginDescriptor = - verifyPlugin( plugin, project, session ); - - if ( pluginDescriptor.getMojos() != null && !pluginDescriptor.getMojos().isEmpty() ) - { - // use the plugin if inherit was true in a base class, or it is in the current POM, otherwise use the default inheritence setting - if ( plugin.isInheritanceApplied() || pluginDescriptor.isInheritedByDefault() ) - { - if ( plugin.getGoals() != null ) - { - getLogger().error( - "Plugin contains a section: this is IGNORED - please use instead." ); - } - - List executions = plugin.getExecutions(); - - if ( executions != null ) - { - for ( Iterator it = executions.iterator(); it.hasNext(); ) - { - PluginExecution execution = (PluginExecution) it.next(); - - bindExecutionToLifecycle( pluginDescriptor, phaseMap, execution, settings ); - } - } - } - } - } - private PluginDescriptor verifyPlugin( Plugin plugin, MavenProject project, MavenSession session ) throws LifecycleExecutionException, PluginNotFoundException { @@ -1252,273 +638,15 @@ private PluginDescriptor verifyPlugin( Plugin plugin, MavenProject project, Mave return pluginDescriptor; } - private PluginDescriptor verifyReportPlugin( ReportPlugin plugin, MavenProject project, MavenSession session ) - throws LifecycleExecutionException, PluginNotFoundException + private MojoDescriptor getMojoDescriptorForDirectInvocation( String task, MavenSession session, MavenProject project ) + throws LifecycleSpecificationException, PluginLoaderException, LifecycleLoaderException { - PluginDescriptor pluginDescriptor; - try - { - pluginDescriptor = pluginManager.verifyReportPlugin( plugin, project, session ); - } - catch ( PluginManagerException e ) - { - throw new LifecycleExecutionException( - "Internal error in the plugin manager getting report '" + plugin.getKey() + "': " + e.getMessage(), e ); - } - catch ( PluginVersionResolutionException e ) - { - throw new LifecycleExecutionException( e.getMessage(), e ); - } - catch ( InvalidVersionSpecificationException e ) - { - throw new LifecycleExecutionException( e.getMessage(), e ); - } - catch ( InvalidPluginException e ) - { - throw new LifecycleExecutionException( e.getMessage(), e ); - } - catch ( ArtifactNotFoundException e ) - { - throw new LifecycleExecutionException( e.getMessage(), e ); - } - catch ( ArtifactResolutionException e ) - { - throw new LifecycleExecutionException( e.getMessage(), e ); - } - catch ( PluginVersionNotFoundException e ) - { - throw new LifecycleExecutionException( e.getMessage(), e ); - } - return pluginDescriptor; - } - - private void bindExecutionToLifecycle( PluginDescriptor pluginDescriptor, Map phaseMap, PluginExecution execution, - Settings settings ) - throws LifecycleExecutionException - { - for ( Iterator i = execution.getGoals().iterator(); i.hasNext(); ) - { - String goal = (String) i.next(); - - MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( goal ); - if ( mojoDescriptor == null ) - { - throw new LifecycleExecutionException( - "Goal '" + goal + "' was specified in an execution, but not found in plugin " + pluginDescriptor.getId() ); - } - - // We have to check to see that the inheritance rules have been applied before binding this mojo. - if ( execution.isInheritanceApplied() || mojoDescriptor.isInheritedByDefault() ) - { - MojoExecution mojoExecution = new MojoExecution( mojoDescriptor, execution.getId() ); - - String phase = execution.getPhase(); - - if ( phase == null ) - { - // if the phase was not in the configuration, use the phase in the descriptor - phase = mojoDescriptor.getPhase(); - } - - if ( phase != null ) - { - if ( mojoDescriptor.isDirectInvocationOnly() ) - { - throw new LifecycleExecutionException( "Mojo: \'" + goal + - "\' requires direct invocation. It cannot be used as part of the lifecycle (it was included via the POM)." ); - } - - addToLifecycleMappings( phaseMap, phase, mojoExecution, settings ); - } - } - } - } - - private void addToLifecycleMappings( Map lifecycleMappings, String phase, MojoExecution mojoExecution, - Settings settings ) - { - List goals = (List) lifecycleMappings.get( phase ); - - if ( goals == null ) - { - goals = new ArrayList(); - lifecycleMappings.put( phase, goals ); - } - - MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); - if ( settings.isOffline() && mojoDescriptor.isOnlineRequired() ) - { - String goal = mojoDescriptor.getGoal(); - getLogger().warn( goal + " requires online mode, but maven is currently offline. Disabling " + goal + "." ); - } - else - { - goals.add( mojoExecution ); - } - } - - private List processGoalChain( String task, Map phaseMap, Lifecycle lifecycle ) - { - List goals = new ArrayList(); - - // only execute up to the given phase - int index = lifecycle.getPhases().indexOf( task ); - - for ( int i = 0; i <= index; i++ ) - { - String p = (String) lifecycle.getPhases().get( i ); - - List phaseGoals = (List) phaseMap.get( p ); - - if ( phaseGoals != null ) - { - goals.addAll( phaseGoals ); - } - } - return goals; - } - - private MojoDescriptor getMojoDescriptor( String task, MavenSession session, MavenProject project, - String invokedVia, boolean canUsePrefix, boolean isOptionalMojo ) - throws BuildFailureException, LifecycleExecutionException, PluginNotFoundException - { - String goal; - Plugin plugin; - - PluginDescriptor pluginDescriptor = null; - - try - { - StringTokenizer tok = new StringTokenizer( task, ":" ); - int numTokens = tok.countTokens(); - - if ( numTokens == 2 ) - { - if ( !canUsePrefix ) - { - String msg = "Mapped-prefix lookup of mojos are only supported from direct invocation. " + - "Please use specification of the form groupId:artifactId[:version]:goal instead. " + - "(Offending mojo: \'" + task + "\', invoked via: \'" + invokedVia + "\')"; - throw new LifecycleExecutionException( msg ); - } - - String prefix = tok.nextToken(); - goal = tok.nextToken(); - - // Steps for retrieving the plugin model instance: - // 1. request directly from the plugin collector by prefix - pluginDescriptor = pluginManager.getPluginDescriptorForPrefix( prefix ); - - // 2. look in the repository via search groups - if ( pluginDescriptor == null ) - { - plugin = pluginManager.getPluginDefinitionForPrefix( prefix, session, project ); - } - else - { - plugin = new Plugin(); - - plugin.setGroupId( pluginDescriptor.getGroupId() ); - plugin.setArtifactId( pluginDescriptor.getArtifactId() ); - plugin.setVersion( pluginDescriptor.getVersion() ); - } - - // 3. search plugins in the current POM - if ( plugin == null ) - { - for ( Iterator i = project.getBuildPlugins().iterator(); i.hasNext(); ) - { - Plugin buildPlugin = (Plugin) i.next(); - - PluginDescriptor desc = verifyPlugin( buildPlugin, project, session ); - - if ( prefix.equals( desc.getGoalPrefix() ) ) - { - plugin = buildPlugin; - } - } - } - - // 4. default to o.a.m.plugins and maven--plugin - if ( plugin == null ) - { - plugin = new Plugin(); - plugin.setGroupId( PluginDescriptor.getDefaultPluginGroupId() ); - plugin.setArtifactId( PluginDescriptor.getDefaultPluginArtifactId( prefix ) ); - } - - for ( Iterator i = project.getBuildPlugins().iterator(); i.hasNext(); ) - { - Plugin buildPlugin = (Plugin) i.next(); - - if ( buildPlugin.getKey().equals( plugin.getKey() ) ) - { - plugin = buildPlugin; - break; - } - } - } - else if ( numTokens == 3 || numTokens == 4 ) - { - plugin = new Plugin(); - - plugin.setGroupId( tok.nextToken() ); - plugin.setArtifactId( tok.nextToken() ); - - if ( numTokens == 4 ) - { - plugin.setVersion( tok.nextToken() ); - } - - goal = tok.nextToken(); - } - else - { - String message = "Invalid task '" + task + "': you must specify a valid lifecycle phase, or" + - " a goal in the format plugin:goal or pluginGroupId:pluginArtifactId:pluginVersion:goal"; - throw new BuildFailureException( message ); - } - - project.injectPluginManagementInfo( plugin ); - - if ( pluginDescriptor == null ) - { - pluginDescriptor = verifyPlugin( plugin, project, session ); - } - - // this has been simplified from the old code that injected the plugin management stuff, since - // pluginManagement injection is now handled by the project method. - project.addPlugin( plugin ); - - MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( goal ); - if ( mojoDescriptor == null ) - { - if ( isOptionalMojo ) - { - getLogger().info( "Skipping missing optional mojo: " + task ); - } - else - { - throw new BuildFailureException( "Required goal not found: " + task ); - } - } - - return mojoDescriptor; - } - catch ( PluginNotFoundException e ) - { - if ( isOptionalMojo ) - { - getLogger().info( "Skipping missing optional mojo: " + task ); - getLogger().debug( "Mojo: " + task + " could not be found. Reason: " + e.getMessage(), e ); - } - else - { - throw e; - } - } - - return null; + MojoBinding binding = mojoBindingFactory.parseMojoBinding( task, project, true ); + + PluginDescriptor descriptor = pluginLoader.loadPlugin( binding, project ); + MojoDescriptor mojoDescriptor = descriptor.getMojo( binding.getGoal() ); + + return mojoDescriptor; } protected void line() @@ -1526,38 +654,6 @@ protected void line() getLogger().info( "----------------------------------------------------------------------------" ); } - public Map getPhaseToLifecycleMap() - throws LifecycleExecutionException - { - if ( phaseToLifecycleMap == null ) - { - phaseToLifecycleMap = new HashMap(); - - for ( Iterator i = lifecycles.iterator(); i.hasNext(); ) - { - Lifecycle lifecycle = (Lifecycle) i.next(); - - for ( Iterator p = lifecycle.getPhases().iterator(); p.hasNext(); ) - { - String phase = (String) p.next(); - - if ( phaseToLifecycleMap.containsKey( phase ) ) - { - Lifecycle prevLifecycle = (Lifecycle) phaseToLifecycleMap.get( phase ); - throw new LifecycleExecutionException( "Phase '" + phase + - "' is defined in more than one lifecycle: '" + lifecycle.getId() + "' and '" + - prevLifecycle.getId() + "'" ); - } - else - { - phaseToLifecycleMap.put( phase, lifecycle ); - } - } - } - } - return phaseToLifecycleMap; - } - private static class TaskSegment { private boolean aggregate; diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutionContext.java b/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutionContext.java new file mode 100644 index 0000000000..5529242428 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutionContext.java @@ -0,0 +1,187 @@ +package org.apache.maven.lifecycle; + +import org.apache.maven.context.BuildContext; +import org.apache.maven.context.BuildContextManager; +import org.apache.maven.context.ManagedBuildData; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.project.MavenProject; +import org.apache.maven.reporting.MavenReport; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +/** + * Build context that contains the current project used by the executing mojo, plus any parent + * project instances (not inheritance-wise, but fork-wise) in a stack that will be restored once the + * current forked-execution is completed. This class also tracks the reports executed for a project, + * for future reference by other mojos using the ${reports} expression. + * + * @author jdcasey + * + */ +public class LifecycleExecutionContext + implements ManagedBuildData +{ + + public static final String BUILD_CONTEXT_KEY = LifecycleExecutionContext.class.getName(); + + private static final String CURRENT_PROJECT_KEY = "current-project"; + private static final String PROJECT_STACK_KEY = "fork-project-stack"; + private static final String REPORTS_KEY = "reports"; + + private MavenProject currentProject; + private Stack forkedProjectStack = new Stack(); + + private Map reports = new HashMap(); + + public LifecycleExecutionContext( MavenProject project ) + { + this.currentProject = project; + } + + private LifecycleExecutionContext() + { + // used for retrieval. + } + + /** + * Serialize the data in this context for storage. Any class in maven-core or the bootclasspath is legal + * here as a datatype in the data map. + */ + public Map getData() + { + Map data = new HashMap(); + data.put( CURRENT_PROJECT_KEY, currentProject ); + data.put( PROJECT_STACK_KEY, forkedProjectStack ); + data.put( REPORTS_KEY, reports ); + + return data; + } + + /** + * Retrieve the master key under which the serialized data map for this context will be stored + * in the main {@link BuildContext}. + */ + public String getStorageKey() + { + return BUILD_CONTEXT_KEY; + } + + /** + * Deserialize the data for this context. Any class in maven-core or the bootclasspath is legal + * here as a datatype in the data map. + */ + public void setData( Map data ) + { + this.currentProject = (MavenProject) data.get( CURRENT_PROJECT_KEY ); + this.forkedProjectStack = (Stack) data.get( PROJECT_STACK_KEY ); + this.reports = (Map) data.get( REPORTS_KEY ); + } + + /** + * Push the existing currentProject onto the forked-project stack, and set the specified project + * as the new current project. This signifies the beginning of a new forked-execution context. + */ + public void addForkedProject( MavenProject project ) + { + forkedProjectStack.push( currentProject ); + currentProject = project; + } + + /** + * Peel off the last forked project from the stack, and restore it as the currentProject. This + * signifies the cleanup of a completed forked-execution context. + */ + public MavenProject removeForkedProject() + { + if ( !forkedProjectStack.isEmpty() ) + { + MavenProject lastCurrent = currentProject; + currentProject = (MavenProject) forkedProjectStack.pop(); + + return lastCurrent; + } + + return null; + } + + /** + * Return the current project for use in a mojo execution. + */ + public MavenProject getCurrentProject() + { + return currentProject; + } + + /** + * Convenience method to read the current context instance out of the main {@link BuildContext} provided + * by the specified {@link BuildContextManager}. If no current context exist, return null. + */ + public static LifecycleExecutionContext read( BuildContextManager buildContextManager ) + { + BuildContext buildContext = buildContextManager.readBuildContext( true ); + + LifecycleExecutionContext ctx = new LifecycleExecutionContext(); + if ( buildContext.retrieve( ctx ) ) + { + return ctx; + } + + return null; + } + + /** + * Remove the current lifecycle context from the main {@link BuildContext} provided by the + * specified {@link BuildContextManager}. + */ + public static void delete( BuildContextManager buildContextManager ) + { + BuildContext buildContext = buildContextManager.readBuildContext( false ); + + if ( buildContext != null ) + { + buildContext.delete( BUILD_CONTEXT_KEY ); + } + } + + /** + * Store this lifecycle context in the main {@link BuildContext} provided by the specified + * {@link BuildContextManager}. + */ + public void store( BuildContextManager buildContextManager ) + { + BuildContext buildContext = buildContextManager.readBuildContext( true ); + buildContext.store( this ); + buildContextManager.storeBuildContext( buildContext ); + } + + /** + * Retrieve the list of reports ({@link MavenReport} instances) that have been executed against + * this project, for use in another mojo's execution. + */ + public List getReports() + { + return new ArrayList( reports.values() ); + } + + /** + * Clear the reports for this project + */ + public void clearReports() + { + reports.clear(); + } + + /** + * Add a newly-executed report ({@link MavenReport} instance) to the reports collection, for + * future reference. + */ + public void addReport( MojoDescriptor mojoDescriptor, MavenReport report ) + { + reports.put( mojoDescriptor.getId(), report ); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/binding/BindingUtils.java b/maven-core/src/main/java/org/apache/maven/lifecycle/binding/BindingUtils.java new file mode 100644 index 0000000000..8d2f3c0cc8 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/binding/BindingUtils.java @@ -0,0 +1,269 @@ +package org.apache.maven.lifecycle.binding; + +import org.apache.maven.lifecycle.model.LifecycleBinding; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.lifecycle.model.Phase; +import org.apache.maven.model.Build; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginContainer; +import org.apache.maven.model.PluginExecution; +import org.apache.maven.model.PluginManagement; +import org.apache.maven.model.ReportPlugin; +import org.apache.maven.model.ReportSet; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ModelUtils; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Set of utilities used to create and manipulate MojoBindings, both singly and in collections that + * constitute LifecycleBindings instances. Some of the methods contained here have fairly generic + * names, but have a specialized purpose for this package (such as those that build plugin keys + * that lack the version); therefore, this class and all of its methods are package-scoped. + */ +final class BindingUtils +{ + + /** + * Builds a mapping of groupId:artifactId --> Plugin from the POM. If a plugin is listed + * without a groupId, the {@link BindingUtils#createPluginKey(Plugin)} method will fill it in + * using org.apache.maven.plugins. + */ + static Map buildPluginMap( MavenProject project ) + { + Map pluginMap = new HashMap(); + + if ( project != null ) + { + Build build = project.getBuild(); + if ( build != null ) + { + for ( Iterator it = build.getPlugins().iterator(); it.hasNext(); ) + { + Plugin plugin = (Plugin) it.next(); + + pluginMap.put( createPluginKey( plugin ), plugin ); + } + } + } + + return pluginMap; + } + + /** + * Builds a mapping of groupId:artifactId --> Plugin from a PluginContainer, such as the build + * or pluginManagement section of a POM. If a plugin is listed without a groupId, the + * {@link BindingUtils#createPluginKey(Plugin)} method will fill it in using org.apache.maven.plugins. + */ + static Map buildPluginMap( PluginContainer pluginContainer ) + { + Map pluginMap = new HashMap(); + + if ( pluginContainer != null ) + { + for ( Iterator it = pluginContainer.getPlugins().iterator(); it.hasNext(); ) + { + Plugin plugin = (Plugin) it.next(); + + pluginMap.put( createPluginKey( plugin ), plugin ); + } + } + + return pluginMap; + } + + /** + * Create a key for the given Plugin, for use in mappings. The key consists of groupId:artifactId, + * where groupId == org.apache.maven.plugins if the Plugin instance has a groupId == null. + */ + static String createPluginKey( Plugin plugin ) + { + return createPluginKey( plugin.getGroupId(), plugin.getArtifactId() ); + } + + /** + * Create a key for use in looking up Plugin instances from mappings. The key consists of + * groupId:artifactId, where groupId == org.apache.maven.plugins if the supplied groupId + * value == null. + */ + static String createPluginKey( String groupId, String artifactId ) + { + return ( groupId == null ? PluginDescriptor.getDefaultPluginGroupId() : groupId ) + ":" + artifactId; + } + + /** + * Merge the ReportPlugin and ReportSet configurations, with the ReportSet configuration taking + * precedence. + */ + static Object mergeConfigurations( ReportPlugin reportPlugin, ReportSet reportSet ) + { + if ( reportPlugin == null && reportSet == null ) + { + return null; + } + else if ( reportSet == null ) + { + return reportPlugin.getConfiguration(); + } + else if ( reportPlugin == null ) + { + return reportSet.getConfiguration(); + } + else + { + return mergeRawConfigurations( reportSet.getConfiguration(), reportPlugin.getConfiguration() ); + } + } + + /** + * Merge the Plugin and PluginExecution configurations, with the PluginExecution configuration + * taking precedence. + */ + static Object mergeConfigurations( Plugin plugin, PluginExecution execution ) + { + if ( plugin == null && execution == null ) + { + return null; + } + else if ( execution == null ) + { + return plugin.getConfiguration(); + } + else if ( plugin == null ) + { + return execution.getConfiguration(); + } + else + { + return mergeRawConfigurations( execution.getConfiguration(), plugin.getConfiguration() ); + } + } + + /** + * Merge two configurations, assuming they are Xpp3Dom instances. This method creates a defensive + * copy of the dominant configuration before merging, to avoid polluting the original dominant + * one. + */ + static Object mergeRawConfigurations( Object dominant, Object recessive ) + { + Xpp3Dom dominantConfig = (Xpp3Dom) dominant; + Xpp3Dom recessiveConfig = (Xpp3Dom) recessive; + + if ( recessiveConfig == null ) + { + return dominantConfig; + } + else if ( dominantConfig == null ) + { + return recessiveConfig; + } + else + { + return Xpp3Dom.mergeXpp3Dom( new Xpp3Dom( dominantConfig ), recessiveConfig ); + } + } + + /** + * Inject any plugin configuration available from the specified POM into the MojoBinding, after + * first merging in the applicable configuration from the POM's pluginManagement section. + */ + static void injectProjectConfiguration( MojoBinding binding, MavenProject project ) + { + Map pluginMap = buildPluginMap( project ); + Plugin plugin = (Plugin) pluginMap.get( createPluginKey( binding.getGroupId(), binding.getArtifactId() ) ); + + if ( plugin == null ) + { + plugin = new Plugin(); + plugin.setGroupId( binding.getGroupId() ); + plugin.setArtifactId( binding.getArtifactId() ); + } + + injectPluginManagementInfo( plugin, project ); + + PluginExecution exec = (PluginExecution) plugin.getExecutionsAsMap().get( binding.getExecutionId() ); + + binding.setConfiguration( mergeConfigurations( plugin, exec ) ); + } + + /** + * Inject any plugin configuration available from the specified POM into the MojoBindings + * present in the given LifecycleBindings instance, after first merging in the configuration + * from the POM's pluginManagement section. + */ + static void injectProjectConfiguration( LifecycleBindings bindings, MavenProject project ) + { + Map pluginsByVersionlessKey = buildPluginMap( project ); + + for ( Iterator lifecycleIt = bindings.getBindingList().iterator(); lifecycleIt.hasNext(); ) + { + LifecycleBinding binding = (LifecycleBinding) lifecycleIt.next(); + + for ( Iterator phaseIt = binding.getPhasesInOrder().iterator(); phaseIt.hasNext(); ) + { + Phase phase = (Phase) phaseIt.next(); + + for ( Iterator mojoIt = phase.getBindings().iterator(); mojoIt.hasNext(); ) + { + MojoBinding mojo = (MojoBinding) mojoIt.next(); + + String pluginKey = createPluginKey( mojo.getGroupId(), mojo.getArtifactId() ); + Plugin plugin = (Plugin) pluginsByVersionlessKey.get( pluginKey ); + + if ( plugin == null ) + { + plugin = new Plugin(); + plugin.setGroupId( mojo.getGroupId() ); + plugin.setArtifactId( mojo.getArtifactId() ); + } + + injectPluginManagementInfo( plugin, project ); + + PluginExecution exec = (PluginExecution) plugin.getExecutionsAsMap().get( mojo.getExecutionId() ); + + mojo.setConfiguration( mergeConfigurations( plugin, exec ) ); + } + } + } + } + + /** + * Inject any applicable configuration available from the POM's pluginManagement section into the + * specified Plugin instance. + */ + static void injectPluginManagementInfo( Plugin plugin, MavenProject project ) + { + if ( project == null ) + { + return; + } + + Build build = project.getBuild(); + if ( build == null ) + { + return; + } + + PluginManagement plugMgmt = build.getPluginManagement(); + if ( plugMgmt == null ) + { + return; + } + + Map plugMgmtMap = buildPluginMap( plugMgmt ); + + String key = createPluginKey( plugin ); + Plugin mgmtPlugin = (Plugin) plugMgmtMap.get( key ); + + if ( mgmtPlugin != null ) + { + ModelUtils.mergePluginDefinitions( plugin, mgmtPlugin, false ); + } + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/binding/DefaultLifecycleBindingManager.java b/maven-core/src/main/java/org/apache/maven/lifecycle/binding/DefaultLifecycleBindingManager.java new file mode 100644 index 0000000000..81de4f78e4 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/binding/DefaultLifecycleBindingManager.java @@ -0,0 +1,736 @@ +package org.apache.maven.lifecycle.binding; + +import org.apache.maven.lifecycle.LifecycleBindingLoader; +import org.apache.maven.lifecycle.LifecycleLoaderException; +import org.apache.maven.lifecycle.LifecycleSpecificationException; +import org.apache.maven.lifecycle.LifecycleUtils; +import org.apache.maven.lifecycle.mapping.LifecycleMapping; +import org.apache.maven.lifecycle.model.LifecycleBinding; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.lifecycle.plan.DirectInvocationModifier; +import org.apache.maven.lifecycle.plan.LifecyclePlannerException; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginExecution; +import org.apache.maven.model.ReportPlugin; +import org.apache.maven.model.ReportSet; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.lifecycle.Execution; +import org.apache.maven.plugin.lifecycle.Lifecycle; +import org.apache.maven.plugin.loader.PluginLoader; +import org.apache.maven.plugin.loader.PluginLoaderException; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.collections.ActiveMap; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.logging.LogEnabled; +import org.codehaus.plexus.logging.Logger; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * Responsible for the gross construction of LifecycleBindings, or mappings of MojoBinding instances + * to different parts of the three lifecycles: clean, build, and site. Also, handles transcribing + * these LifecycleBindings instances into lists of MojoBinding's, which can be consumed by the + * LifecycleExecutor. + * + * @author jdcasey + * + */ +public class DefaultLifecycleBindingManager + implements LifecycleBindingManager, LogEnabled +{ + + private ActiveMap bindingsByPackaging; + + private ActiveMap legacyMappingsByPackaging; + + private PluginLoader pluginLoader; + + private MojoBindingFactory mojoBindingFactory; + + private LegacyLifecycleMappingParser legacyLifecycleMappingParser; + + private Logger logger; + + // configured. Moved out of DefaultLifecycleExecutor... + private List legacyLifecycles; + + // configured. Moved out of DefaultLifecycleExecutor... + private List defaultReports; + + /** + * Retrieve the LifecycleBindings given by the lifecycle mapping component/file for the project's + * packaging. Any applicable mojo configuration will be injected into the LifecycleBindings from + * the POM. + */ + public LifecycleBindings getBindingsForPackaging( MavenProject project ) + throws LifecycleLoaderException, LifecycleSpecificationException + { + String packaging = project.getPackaging(); + + LifecycleBindings bindings = null; + + LifecycleBindingLoader loader = (LifecycleBindingLoader) bindingsByPackaging.get( packaging ); + if ( loader != null ) + { + bindings = loader.getBindings(); + } + + // TODO: Remove this once we no longer have to support legacy-style lifecycle mappings + if ( bindings == null ) + { + LifecycleMapping mapping = (LifecycleMapping) legacyMappingsByPackaging.get( packaging ); + if ( mapping != null ) + { + bindings = legacyLifecycleMappingParser.parseMappings( mapping, packaging ); + } + } + + if ( bindings == null ) + { + bindings = searchPluginsWithExtensions( project ); + } + else + { + BindingUtils.injectProjectConfiguration( bindings, project ); + } + + if ( bindings == null ) + { + bindings = getDefaultBindings( project ); + } + + return bindings; + } + + /** + * Search all plugins configured in the POM that have extensions == true, looking for either a + * {@link LifecycleBindingLoader} instance, or a {@link LifecycleMapping} instance that matches + * the project's packaging. For the first match found, construct the corresponding LifecycleBindings + * instance and return it after POM configurations have been injected into any appropriate + * MojoBinding instances contained within. + */ + private LifecycleBindings searchPluginsWithExtensions( MavenProject project ) + throws LifecycleLoaderException, LifecycleSpecificationException + { + List plugins = project.getBuildPlugins(); + String packaging = project.getPackaging(); + + LifecycleBindings bindings = null; + + for ( Iterator it = plugins.iterator(); it.hasNext(); ) + { + Plugin plugin = (Plugin) it.next(); + if ( plugin.isExtensions() ) + { + LifecycleBindingLoader loader = null; + + try + { + loader = (LifecycleBindingLoader) pluginLoader.loadPluginComponent( LifecycleBindingLoader.ROLE, packaging, + plugin, project ); + } + catch ( ComponentLookupException e ) + { + logger.debug( LifecycleBindingLoader.ROLE + " for packaging: " + packaging + + " could not be retrieved from plugin: " + plugin.getKey() + ".\nReason: " + e.getMessage(), e ); + } + catch ( PluginLoaderException e ) + { + throw new LifecycleLoaderException( "Failed to load plugin: " + plugin.getKey() + ". Reason: " + + e.getMessage(), e ); + } + + if ( loader != null ) + { + bindings = loader.getBindings(); + } + + // TODO: Remove this once we no longer have to support legacy-style lifecycle mappings + if ( bindings == null ) + { + LifecycleMapping mapping = null; + try + { + mapping = (LifecycleMapping) pluginLoader.loadPluginComponent( LifecycleMapping.ROLE, packaging, plugin, + project ); + } + catch ( ComponentLookupException e ) + { + logger.debug( LifecycleMapping.ROLE + " for packaging: " + packaging + + " could not be retrieved from plugin: " + plugin.getKey() + ".\nReason: " + e.getMessage(), e ); + } + catch ( PluginLoaderException e ) + { + throw new LifecycleLoaderException( "Failed to load plugin: " + plugin.getKey() + ". Reason: " + + e.getMessage(), e ); + } + + if ( mapping != null ) + { + bindings = legacyLifecycleMappingParser.parseMappings( mapping, packaging ); + } + } + + if ( bindings != null ) + { + break; + } + } + } + + if ( bindings != null ) + { + BindingUtils.injectProjectConfiguration( bindings, project ); + } + + return bindings; + } + + /** + * Construct the LifecycleBindings for the default lifecycle mappings, including injection of + * configuration from the project into each MojoBinding, where appropriate. + */ + public LifecycleBindings getDefaultBindings( MavenProject project ) + throws LifecycleSpecificationException + { + LifecycleBindings bindings = legacyLifecycleMappingParser.parseDefaultMappings( legacyLifecycles ); + + BindingUtils.injectProjectConfiguration( bindings, project ); + + return bindings; + } + + public void enableLogging( Logger logger ) + { + this.logger = logger; + } + + /** + * Construct the LifecycleBindings that constitute the extra mojos bound to the lifecycle within + * the POM itself. + */ + public LifecycleBindings getProjectCustomBindings( MavenProject project ) + throws LifecycleLoaderException, LifecycleSpecificationException + { + String projectId = project.getId(); + + LifecycleBindings bindings = new LifecycleBindings(); + bindings.setPackaging( project.getPackaging() ); + + List plugins = project.getBuildPlugins(); + if ( plugins != null ) + { + for ( Iterator it = plugins.iterator(); it.hasNext(); ) + { + Plugin plugin = (Plugin) it.next(); + BindingUtils.injectPluginManagementInfo( plugin, project ); + + PluginDescriptor pluginDescriptor = null; + + List executions = plugin.getExecutions(); + if ( executions != null ) + { + for ( Iterator execIt = executions.iterator(); execIt.hasNext(); ) + { + PluginExecution execution = (PluginExecution) execIt.next(); + + List goals = execution.getGoals(); + if ( goals != null && !goals.isEmpty() ) + { + for ( Iterator goalIterator = goals.iterator(); goalIterator.hasNext(); ) + { + String goal = (String) goalIterator.next(); + + if ( goal == null ) + { + logger.warn( "Execution: " + execution.getId() + " in plugin: " + plugin.getKey() + " in the POM has a null goal." ); + continue; + } + + MojoBinding mojoBinding = new MojoBinding(); + + mojoBinding.setGroupId( plugin.getGroupId() ); + mojoBinding.setArtifactId( plugin.getArtifactId() ); + mojoBinding.setVersion( plugin.getVersion() ); + mojoBinding.setGoal( goal ); + mojoBinding.setConfiguration( BindingUtils.mergeConfigurations( plugin, execution ) ); + mojoBinding.setExecutionId( execution.getId() ); + mojoBinding.setOrigin( "POM" ); + + String phase = execution.getPhase(); + if ( phase == null ) + { + if ( pluginDescriptor == null ) + { + try + { + pluginDescriptor = pluginLoader.loadPlugin( plugin, project ); + } + catch ( PluginLoaderException e ) + { + throw new LifecycleLoaderException( "Failed to load plugin: " + plugin + ". Reason: " + + e.getMessage(), e ); + } + } + + if ( pluginDescriptor.getMojos() == null ) + { + logger.error( "Somehow, the PluginDescriptor for plugin: " + plugin.getKey() + " contains no mojos. This is highly irregular. Ignoring..." ); + continue; + } + + MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( goal ); + phase = mojoDescriptor.getPhase(); + + if ( phase == null ) + { + throw new LifecycleSpecificationException( "No phase specified for goal: " + goal + + " in plugin: " + plugin.getKey() + " from POM: " + projectId ); + } + } + + LifecycleUtils.addMojoBinding( phase, mojoBinding, bindings ); + } + } + } + } + } + } + + LifecycleUtils.setOrigin( bindings, "POM" ); + + return bindings; + } + + /** + * Construct the LifecycleBindings that constitute the mojos mapped to the lifecycles by an overlay + * specified in a plugin. Inject mojo configuration from the POM into all appropriate MojoBinding + * instances. + */ + public LifecycleBindings getPluginLifecycleOverlay( PluginDescriptor pluginDescriptor, String lifecycleId, + MavenProject project ) + throws LifecycleLoaderException, LifecycleSpecificationException + { + Lifecycle lifecycleOverlay = null; + + try + { + lifecycleOverlay = pluginDescriptor.getLifecycleMapping( lifecycleId ); + } + catch ( IOException e ) + { + throw new LifecycleLoaderException( "Unable to read lifecycle mapping file: " + e.getMessage(), e ); + } + catch ( XmlPullParserException e ) + { + throw new LifecycleLoaderException( "Unable to parse lifecycle mapping file: " + e.getMessage(), e ); + } + + if ( lifecycleOverlay == null ) + { + throw new LifecycleLoaderException( "LegacyLifecycle '" + lifecycleId + "' not found in plugin" ); + } + + LifecycleBindings bindings = new LifecycleBindings(); + + for ( Iterator i = lifecycleOverlay.getPhases().iterator(); i.hasNext(); ) + { + org.apache.maven.plugin.lifecycle.Phase phase = (org.apache.maven.plugin.lifecycle.Phase) i.next(); + List phaseBindings = new ArrayList(); + + for ( Iterator j = phase.getExecutions().iterator(); j.hasNext(); ) + { + Execution exec = (Execution) j.next(); + + for ( Iterator k = exec.getGoals().iterator(); k.hasNext(); ) + { + String goal = (String) k.next(); + + // Here we are looking to see if we have a mojo from an external plugin. + // If we do then we need to lookup the plugin descriptor for the externally + // referenced plugin so that we can overly the execution into the lifecycle. + // An example of this is the corbertura plugin that needs to call the surefire + // plugin in forking mode. + // + // + // test + // + // + // + // org.apache.maven.plugins:maven-surefire-plugin:test + // + // + // ${project.build.directory}/generated-classes/cobertura + // true + // once + // + // + // + // + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + MojoBinding binding; + if ( goal.indexOf( ":" ) > 0 ) + { + binding = mojoBindingFactory.parseMojoBinding( goal, project, false ); + } + else + { + binding = new MojoBinding(); + binding.setGroupId( pluginDescriptor.getGroupId() ); + binding.setArtifactId( pluginDescriptor.getArtifactId() ); + binding.setVersion( pluginDescriptor.getVersion() ); + binding.setGoal( goal ); + } + + Xpp3Dom configuration = (Xpp3Dom) exec.getConfiguration(); + if ( phase.getConfiguration() != null ) + { + configuration = Xpp3Dom.mergeXpp3Dom( new Xpp3Dom( (Xpp3Dom) phase.getConfiguration() ), configuration ); + } + + binding.setConfiguration( configuration ); + binding.setOrigin( lifecycleId ); + + LifecycleUtils.addMojoBinding( phase.getId(), binding, bindings ); + phaseBindings.add( binding ); + } + } + + if ( phase.getConfiguration() != null ) + { + // Merge in general configuration for a phase. + // TODO: this is all kind of backwards from the POMM. Let's align it all under 2.1. + // We should create a new lifecycle executor for modelVersion >5.0.0 + // [jdcasey; 08-March-2007] Not sure what the above to-do references...how _should_ + // this work?? + for ( Iterator j = phaseBindings.iterator(); j.hasNext(); ) + { + MojoBinding binding = (MojoBinding) j.next(); + + Xpp3Dom configuration = Xpp3Dom.mergeXpp3Dom( new Xpp3Dom( (Xpp3Dom) phase.getConfiguration() ), + (Xpp3Dom) binding.getConfiguration() ); + + binding.setConfiguration( configuration ); + } + } + + } + + return bindings; + } + + /** + * Retrieve the list of MojoBinding instances that correspond to the reports configured for the + * specified project. Inject all appropriate configuration from the POM for each MojoBinding, using + * the following precedence rules: + *
+ *
    + *
  1. report-set-level configuration
  2. + *
  3. reporting-level configuration
  4. + *
  5. execution-level configuration
  6. + *
  7. plugin-level configuration
  8. + *
+ */ + public List getReportBindings( MavenProject project ) + throws LifecycleLoaderException, LifecycleSpecificationException + { + if ( project.getModel().getReports() != null ) + { + logger.error( "Plugin contains a section: this is IGNORED - please use instead." ); + } + + List reportPlugins = getReportPluginsForProject( project ); + + List reports = new ArrayList(); + if ( reportPlugins != null ) + { + for ( Iterator it = reportPlugins.iterator(); it.hasNext(); ) + { + ReportPlugin reportPlugin = (ReportPlugin) it.next(); + + List reportSets = reportPlugin.getReportSets(); + + if ( reportSets == null || reportSets.isEmpty() ) + { + reports.addAll( getReportsForPlugin( reportPlugin, null, project ) ); + } + else + { + for ( Iterator j = reportSets.iterator(); j.hasNext(); ) + { + ReportSet reportSet = (ReportSet) j.next(); + + reports.addAll( getReportsForPlugin( reportPlugin, reportSet, project ) ); + } + } + } + } + return reports; + } + + /** + * Retrieve the ReportPlugin instances referenced in the specified POM. + */ + private List getReportPluginsForProject( MavenProject project ) + { + List reportPlugins = project.getReportPlugins(); + + if ( project.getReporting() == null || !project.getReporting().isExcludeDefaults() ) + { + if ( reportPlugins == null ) + { + reportPlugins = new ArrayList(); + } + else + { + reportPlugins = new ArrayList( reportPlugins ); + } + + for ( Iterator i = defaultReports.iterator(); i.hasNext(); ) + { + String report = (String) i.next(); + + StringTokenizer tok = new StringTokenizer( report, ":" ); + if ( tok.countTokens() != 2 ) + { + logger.warn( "Invalid default report ignored: '" + report + "' (must be groupId:artifactId)" ); + } + else + { + String groupId = tok.nextToken(); + String artifactId = tok.nextToken(); + + boolean found = false; + for ( Iterator j = reportPlugins.iterator(); j.hasNext() && !found; ) + { + ReportPlugin reportPlugin = (ReportPlugin) j.next(); + if ( reportPlugin.getGroupId().equals( groupId ) && reportPlugin.getArtifactId().equals( artifactId ) ) + { + found = true; + } + } + + if ( !found ) + { + ReportPlugin reportPlugin = new ReportPlugin(); + reportPlugin.setGroupId( groupId ); + reportPlugin.setArtifactId( artifactId ); + reportPlugins.add( reportPlugin ); + } + } + } + } + + return reportPlugins; + } + + /** + * Retrieve any reports from the specified ReportPlugin which are referenced in the specified POM. + */ + private List getReportsForPlugin( ReportPlugin reportPlugin, ReportSet reportSet, MavenProject project ) + throws LifecycleLoaderException + { + PluginDescriptor pluginDescriptor; + try + { + pluginDescriptor = pluginLoader.loadReportPlugin( reportPlugin, project ); + } + catch ( PluginLoaderException e ) + { + throw new LifecycleLoaderException( "Failed to load report plugin: " + reportPlugin.getKey() + ". Reason: " + + e.getMessage(), e ); + } + + String pluginKey = BindingUtils.createPluginKey( reportPlugin.getGroupId(), reportPlugin.getArtifactId() ); + Plugin plugin = (Plugin) BindingUtils.buildPluginMap( project ).get( pluginKey ); + + List reports = new ArrayList(); + for ( Iterator i = pluginDescriptor.getMojos().iterator(); i.hasNext(); ) + { + MojoDescriptor mojoDescriptor = (MojoDescriptor) i.next(); + + // TODO: check ID is correct for reports + // if the POM configured no reports, give all from plugin + if ( reportSet == null || reportSet.getReports().contains( mojoDescriptor.getGoal() ) ) + { + String id = null; + if ( reportSet != null ) + { + id = reportSet.getId(); + } + + MojoBinding binding = new MojoBinding(); + binding.setGroupId( pluginDescriptor.getGroupId() ); + binding.setArtifactId( pluginDescriptor.getArtifactId() ); + binding.setVersion( pluginDescriptor.getVersion() ); + binding.setGoal( mojoDescriptor.getGoal() ); + binding.setExecutionId( id ); + binding.setOrigin( "POM" ); + + Object reportConfig = BindingUtils.mergeConfigurations( reportPlugin, reportSet ); + + if ( plugin == null ) + { + plugin = new Plugin(); + plugin.setGroupId( pluginDescriptor.getGroupId() ); + plugin.setArtifactId( pluginDescriptor.getArtifactId() ); + } + + BindingUtils.injectPluginManagementInfo( plugin, project ); + + Map execMap = plugin.getExecutionsAsMap(); + PluginExecution exec = (PluginExecution) execMap.get( id ); + + Object pluginConfig = plugin.getConfiguration(); + if ( exec != null ) + { + pluginConfig = BindingUtils.mergeConfigurations( plugin, exec ); + } + + reportConfig = BindingUtils.mergeRawConfigurations( reportConfig, pluginConfig ); + + binding.setConfiguration( reportConfig ); + + reports.add( binding ); + } + } + return reports; + } + + /** + * Determine whether the first list contains all of the same MojoBinding instances, in the same + * order, starting at index zero, as the second list. If so, it is either a perfect super-list + * or an equal list, and return true. Return false otherwise. + */ + private static boolean isSameOrSuperListOfMojoBindings( List superCandidate, List check ) + { + if ( superCandidate == null || check == null ) + { + return false; + } + + if ( superCandidate.size() < check.size() ) + { + return false; + } + + List superKeys = new ArrayList( superCandidate.size() ); + for ( Iterator it = superCandidate.iterator(); it.hasNext(); ) + { + MojoBinding binding = (MojoBinding) it.next(); + + superKeys.add( LifecycleUtils.createMojoBindingKey( binding, true ) ); + } + + List checkKeys = new ArrayList( check.size() ); + for ( Iterator it = check.iterator(); it.hasNext(); ) + { + MojoBinding binding = (MojoBinding) it.next(); + + checkKeys.add( LifecycleUtils.createMojoBindingKey( binding, true ) ); + } + + return superKeys.subList( 0, checkKeys.size() ).equals( checkKeys ); + } + + /** + * Traverse the specified LifecycleBindings instance for all of the specified tasks. If the task + * is found to be a phase name, construct the list of all MojoBindings that lead up to that phase + * in that lifecycle, and add them to the master MojoBinding list. If the task is not a phase name, + * treat it as a direct mojo invocation, parse it into a MojoBinding (resolving the plugin prefix + * first if necessary), and add it to the master MojoBinding list. Finally, return the master list. + */ + public List assembleMojoBindingList( List tasks, LifecycleBindings bindings, MavenProject project ) + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException + { + return assembleMojoBindingList( tasks, bindings, Collections.EMPTY_MAP, project ); + } + + /** + * Traverse the specified LifecycleBindings instance for all of the specified tasks. If the task + * is found to be a phase name, construct the list of all MojoBindings that lead up to that phase + * in that lifecycle, and add them to the master MojoBinding list. If the task is not a phase name, + * treat it as a direct mojo invocation, parse it into a MojoBinding (resolving the plugin prefix + * first if necessary), and add it to the master MojoBinding list. + * + * Then, iterate through all MojoBindings in the master list, and for each one that maps to an + * entry in directInvocationModifiers, substitute the resultant MojoBinding list from that + * modifier in place of the original MojoBinding. + * + * Finally, return the modified master list. + */ + public List assembleMojoBindingList( List tasks, LifecycleBindings lifecycleBindings, Map directInvocationModifiers, + MavenProject project ) + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException + { + List planBindings = new ArrayList(); + + List lastMojoBindings = null; + for ( Iterator it = tasks.iterator(); it.hasNext(); ) + { + String task = (String) it.next(); + + LifecycleBinding binding = LifecycleUtils.findLifecycleBindingForPhase( task, lifecycleBindings ); + if ( binding != null ) + { + List mojoBindings = LifecycleUtils.getMojoBindingListForLifecycle( task, binding ); + + // save these so we can reference the originals... + List originalMojoBindings = mojoBindings; + + // if these mojo bindings are a superset of the last bindings, only add the difference. + if ( isSameOrSuperListOfMojoBindings( mojoBindings, lastMojoBindings ) ) + { + List revised = new ArrayList( mojoBindings ); + revised.removeAll( lastMojoBindings ); + + if ( revised.isEmpty() ) + { + continue; + } + + mojoBindings = revised; + } + + planBindings.addAll( mojoBindings ); + lastMojoBindings = originalMojoBindings; + } + else + { + MojoBinding mojoBinding = mojoBindingFactory.parseMojoBinding( task, project, true ); + BindingUtils.injectProjectConfiguration( mojoBinding, project ); + + mojoBinding.setOrigin( "direct invocation" ); + + String key = LifecycleUtils.createMojoBindingKey( mojoBinding, true ); + DirectInvocationModifier modifier = (DirectInvocationModifier) directInvocationModifiers.get( key ); + + if ( modifier != null ) + { + planBindings.addAll( modifier.getModifiedBindings( project, this ) ); + } + else + { + planBindings.add( mojoBinding ); + } + } + } + + return planBindings; + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/binding/DefaultMojoBindingFactory.java b/maven-core/src/main/java/org/apache/maven/lifecycle/binding/DefaultMojoBindingFactory.java new file mode 100644 index 0000000000..0e6b554565 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/binding/DefaultMojoBindingFactory.java @@ -0,0 +1,135 @@ +package org.apache.maven.lifecycle.binding; + +import org.apache.maven.lifecycle.LifecycleLoaderException; +import org.apache.maven.lifecycle.LifecycleSpecificationException; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.loader.PluginLoader; +import org.apache.maven.plugin.loader.PluginLoaderException; +import org.apache.maven.project.MavenProject; + +import java.util.StringTokenizer; + +/** + * Responsible for constructing or parsing MojoBinding instances from one of several sources, potentially + * using the {@link PluginLoader} to resolve any plugin prefixes first. + * + * @author jdcasey + * + */ +public class DefaultMojoBindingFactory + implements MojoBindingFactory +{ + + PluginLoader pluginLoader; + + /** + * Parse the specified mojo string into a MojoBinding, optionally allowing plugin-prefix references. + * If a plugin-prefix is allowed and used, resolve the prefix and use the resulting PluginDescriptor + * to set groupId and artifactId on the MojoBinding instance. + */ + public MojoBinding parseMojoBinding( String bindingSpec, MavenProject project, boolean allowPrefixReference ) + throws LifecycleSpecificationException, LifecycleLoaderException + { + StringTokenizer tok = new StringTokenizer( bindingSpec, ":" ); + int numTokens = tok.countTokens(); + + MojoBinding binding = null; + + if ( numTokens == 2 ) + { + if ( !allowPrefixReference ) + { + String msg = "Mapped-prefix lookup of mojos are only supported from direct invocation. " + + "Please use specification of the form groupId:artifactId[:version]:goal instead."; + + throw new LifecycleSpecificationException( msg ); + } + + String prefix = tok.nextToken(); + + PluginDescriptor pluginDescriptor; + try + { + pluginDescriptor = pluginLoader.findPluginForPrefix( prefix, project ); + } + catch ( PluginLoaderException e ) + { + throw new LifecycleLoaderException( + "Failed to find plugin for prefix: " + prefix + ". Reason: " + e.getMessage(), + e ); + } + + binding = createMojoBinding( pluginDescriptor.getGroupId(), pluginDescriptor.getArtifactId(), + pluginDescriptor.getVersion(), tok.nextToken(), project ); + } + else if ( numTokens == 3 || numTokens == 4 ) + { + binding = new MojoBinding(); + + String groupId = tok.nextToken(); + String artifactId = tok.nextToken(); + + String version = null; + if ( numTokens == 4 ) + { + version = tok.nextToken(); + } + + String goal = tok.nextToken(); + + binding = createMojoBinding( groupId, artifactId, version, goal, project ); + } + else + { + String message = "Invalid task '" + bindingSpec + "': you must specify a valid lifecycle phase, or" + + " a goal in the format plugin:goal or pluginGroupId:pluginArtifactId:pluginVersion:goal"; + + throw new LifecycleSpecificationException( message ); + } + + return binding; + } + + /** + * Create a new MojoBinding instance with the specified information, and inject POM configurations + * appropriate to that mojo before returning it. + */ + public MojoBinding createMojoBinding( String groupId, String artifactId, String version, String goal, MavenProject project ) + { + MojoBinding binding = new MojoBinding(); + + binding.setGroupId( groupId ); + binding.setArtifactId( artifactId ); + binding.setVersion( version ); + binding.setGoal( goal ); + + BindingUtils.injectProjectConfiguration( binding, project ); + + return binding; + } + + /** + * Simplified version of {@link MojoBindingFactory#parseMojoBinding(String, MavenProject, boolean)} + * which assumes the project is null and prefixes are not allowed. This method will never + * result in the {@link PluginLoader} being used to resolve the PluginDescriptor. + */ + public MojoBinding parseMojoBinding( String bindingSpec ) + throws LifecycleSpecificationException + { + try + { + return parseMojoBinding( bindingSpec, null, false ); + } + catch ( LifecycleLoaderException e ) + { + IllegalStateException error = new IllegalStateException( e.getMessage() + + "\n\nTHIS SHOULD BE IMPOSSIBLE DUE TO THE USAGE OF THE PLUGIN-LOADER." ); + + error.initCause( e ); + + throw error; + } + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/Lifecycle.java b/maven-core/src/main/java/org/apache/maven/lifecycle/binding/LegacyLifecycle.java similarity index 95% rename from maven-core/src/main/java/org/apache/maven/lifecycle/Lifecycle.java rename to maven-core/src/main/java/org/apache/maven/lifecycle/binding/LegacyLifecycle.java index 2101ec0f10..6c0b7dec6b 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/Lifecycle.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/binding/LegacyLifecycle.java @@ -1,4 +1,4 @@ -package org.apache.maven.lifecycle; +package org.apache.maven.lifecycle.binding; /* * Licensed to the Apache Software Foundation (ASF) under one @@ -24,9 +24,9 @@ import java.util.Map; /** - * Class Lifecycle. + * Class LegacyLifecycle. */ -public class Lifecycle +public class LegacyLifecycle { /** * Field id diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/binding/LegacyLifecycleMappingParser.java b/maven-core/src/main/java/org/apache/maven/lifecycle/binding/LegacyLifecycleMappingParser.java new file mode 100644 index 0000000000..dadffc5c5a --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/binding/LegacyLifecycleMappingParser.java @@ -0,0 +1,177 @@ +package org.apache.maven.lifecycle.binding; + +import org.apache.maven.lifecycle.LifecycleSpecificationException; +import org.apache.maven.lifecycle.LifecycleUtils; +import org.apache.maven.lifecycle.mapping.LifecycleMapping; +import org.apache.maven.lifecycle.model.BuildBinding; +import org.apache.maven.lifecycle.model.CleanBinding; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.lifecycle.model.Phase; +import org.apache.maven.lifecycle.model.SiteBinding; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * Responsible for parsing the Maven-2.0.x lifecycle-definition syntaxes. This class is partitioned + * from the others, because this syntax should be deprecated and removed from support, eventually. + * + * @author jdcasey + * + */ +public class LegacyLifecycleMappingParser +{ + + public static final String ROLE = LegacyLifecycleMappingParser.class.getName(); + + private MojoBindingFactory mojoBindingFactory; + + public LifecycleBindings parseDefaultMappings( List lifecycles ) + throws LifecycleSpecificationException + { + LifecycleBindings bindings = new LifecycleBindings(); + + bindings.setPackaging( "unmatched" ); + + for ( Iterator it = lifecycles.iterator(); it.hasNext(); ) + { + LegacyLifecycle lifecycle = (LegacyLifecycle) it.next(); + + if ( "clean".equals( lifecycle.getId() ) ) + { + bindings.setCleanBinding( parseCleanBindings( lifecycle.getDefaultPhases(), Collections.EMPTY_LIST ) ); + } + else if ( "site".equals( lifecycle.getId() ) ) + { + bindings.setSiteBinding( parseSiteBindings( lifecycle.getDefaultPhases(), Collections.EMPTY_LIST ) ); + } + else if ( "default".equals( lifecycle.getId() ) ) + { + bindings.setBuildBinding( parseBuildBindings( lifecycle.getDefaultPhases(), Collections.EMPTY_LIST ) ); + } + else + { + throw new LifecycleSpecificationException( "Unrecognized lifecycle: " + lifecycle.getId() ); + } + } + + LifecycleUtils.setOrigin( bindings, "default lifecycle" ); + + return bindings; + } + + public LifecycleBindings parseMappings( LifecycleMapping mapping, String packaging ) + throws LifecycleSpecificationException + { + LifecycleBindings bindings = new LifecycleBindings(); + bindings.setPackaging( packaging ); + + bindings.setCleanBinding( parseCleanBindings( mapping.getPhases( "clean" ), mapping.getOptionalMojos( "clean" ) ) ); + bindings.setBuildBinding( parseBuildBindings( mapping.getPhases( "default" ), mapping.getOptionalMojos( "default" ) ) ); + bindings.setSiteBinding( parseSiteBindings( mapping.getPhases( "site" ), mapping.getOptionalMojos( "site" ) ) ); + + LifecycleUtils.setOrigin( bindings, "packaging: " + packaging ); + + return bindings; + } + + private BuildBinding parseBuildBindings( Map phases, List optionalKeys ) + throws LifecycleSpecificationException + { + BuildBinding binding = new BuildBinding(); + + if ( phases != null ) + { + binding.setValidate( parsePhaseBindings( (String) phases.get( "validate" ), optionalKeys ) ); + binding.setInitialize( parsePhaseBindings( (String) phases.get( "initialize" ), optionalKeys ) ); + binding.setGenerateSources( parsePhaseBindings( (String) phases.get( "generate-sources" ), optionalKeys ) ); + binding.setProcessSources( parsePhaseBindings( (String) phases.get( "process-sources" ), optionalKeys ) ); + binding.setGenerateResources( parsePhaseBindings( (String) phases.get( "generate-resources" ), optionalKeys ) ); + binding.setProcessResources( parsePhaseBindings( (String) phases.get( "process-resources" ), optionalKeys ) ); + binding.setCompile( parsePhaseBindings( (String) phases.get( "compile" ), optionalKeys ) ); + binding.setProcessClasses( parsePhaseBindings( (String) phases.get( "process-classes" ), optionalKeys ) ); + binding.setGenerateTestSources( parsePhaseBindings( (String) phases.get( "generate-test-sources" ), optionalKeys ) ); + binding.setProcessTestSources( parsePhaseBindings( (String) phases.get( "process-test-sources" ), optionalKeys ) ); + binding.setGenerateTestResources( parsePhaseBindings( (String) phases.get( "generate-test-resources" ), optionalKeys ) ); + binding.setProcessTestResources( parsePhaseBindings( (String) phases.get( "process-test-resources" ), optionalKeys ) ); + binding.setTestCompile( parsePhaseBindings( (String) phases.get( "test-compile" ), optionalKeys ) ); + binding.setProcessTestClasses( parsePhaseBindings( (String) phases.get( "process-test-classes" ), optionalKeys ) ); + binding.setTest( parsePhaseBindings( (String) phases.get( "test" ), optionalKeys ) ); + binding.setPreparePackage( parsePhaseBindings( (String) phases.get( "prepare-package" ), optionalKeys ) ); + binding.setCreatePackage( parsePhaseBindings( (String) phases.get( "package" ), optionalKeys ) ); + binding.setPreIntegrationTest( parsePhaseBindings( (String) phases.get( "pre-integration-test" ), optionalKeys ) ); + binding.setIntegrationTest( parsePhaseBindings( (String) phases.get( "integration-test" ), optionalKeys ) ); + binding.setPostIntegrationTest( parsePhaseBindings( (String) phases.get( "post-integration-test" ), optionalKeys ) ); + binding.setVerify( parsePhaseBindings( (String) phases.get( "verify" ), optionalKeys ) ); + binding.setInstall( parsePhaseBindings( (String) phases.get( "install" ), optionalKeys ) ); + binding.setDeploy( parsePhaseBindings( (String) phases.get( "deploy" ), optionalKeys ) ); + } + + return binding; + } + + private CleanBinding parseCleanBindings( Map phaseMappings, List optionalKeys ) + throws LifecycleSpecificationException + { + CleanBinding binding = new CleanBinding(); + + if ( phaseMappings != null ) + { + binding.setPreClean( parsePhaseBindings( (String) phaseMappings.get( "pre-clean" ), optionalKeys ) ); + binding.setClean( parsePhaseBindings( (String) phaseMappings.get( "clean" ), optionalKeys ) ); + binding.setPostClean( parsePhaseBindings( (String) phaseMappings.get( "post-clean" ), optionalKeys ) ); + } + + return binding; + } + + private Phase parsePhaseBindings( String bindingList, List optionalKeys ) + throws LifecycleSpecificationException + { + Phase phase = new Phase(); + + if ( bindingList != null ) + { + for ( StringTokenizer tok = new StringTokenizer( bindingList, "," ); tok.hasMoreTokens(); ) + { + String rawBinding = tok.nextToken().trim(); + + MojoBinding binding = mojoBindingFactory.parseMojoBinding( rawBinding ); + if ( optionalKeys != null && optionalKeys.contains( rawBinding ) ) + { + binding.setOptional( true ); + } + + if ( binding == null ) + { + continue; + } + + phase.addBinding( binding ); + } + } + + return phase; + } + + private SiteBinding parseSiteBindings( Map phases, List optionalKeys ) + throws LifecycleSpecificationException + { + SiteBinding binding = new SiteBinding(); + + if ( phases != null ) + { + binding.setPreSite( parsePhaseBindings( (String) phases.get( "pre-site" ), optionalKeys ) ); + binding.setSite( parsePhaseBindings( (String) phases.get( "site" ), optionalKeys ) ); + binding.setPostSite( parsePhaseBindings( (String) phases.get( "post-site" ), optionalKeys ) ); + binding.setSiteDeploy( parsePhaseBindings( (String) phases.get( "site-deploy" ), optionalKeys ) ); + } + + return binding; + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/binding/LifecycleBindingManager.java b/maven-core/src/main/java/org/apache/maven/lifecycle/binding/LifecycleBindingManager.java new file mode 100644 index 0000000000..642b82bc20 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/binding/LifecycleBindingManager.java @@ -0,0 +1,98 @@ +package org.apache.maven.lifecycle.binding; + +import org.apache.maven.lifecycle.LifecycleLoaderException; +import org.apache.maven.lifecycle.LifecycleSpecificationException; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.plan.LifecyclePlannerException; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.project.MavenProject; + +import java.util.List; +import java.util.Map; + +/** + * Responsible for the gross construction of LifecycleBindings, or mappings of MojoBinding instances + * to different parts of the three lifecycles: clean, build, and site. Also, handles transcribing + * these LifecycleBindings instances into lists of MojoBinding's, which can be consumed by the + * LifecycleExecutor. + * + * @author jdcasey + * + */ +public interface LifecycleBindingManager +{ + + String ROLE = LifecycleBindingManager.class.getName(); + + /** + * Construct the LifecycleBindings for the default lifecycle mappings, including injection of + * configuration from the project into each MojoBinding, where appropriate. + */ + LifecycleBindings getDefaultBindings( MavenProject project ) + throws LifecycleSpecificationException; + + /** + * Retrieve the LifecycleBindings given by the lifecycle mapping component/file for the project's + * packaging. Any applicable mojo configuration will be injected into the LifecycleBindings from + * the POM. + */ + LifecycleBindings getBindingsForPackaging( MavenProject project ) + throws LifecycleLoaderException, LifecycleSpecificationException; + + /** + * Construct the LifecycleBindings that constitute the extra mojos bound to the lifecycle within + * the POM itself. + */ + LifecycleBindings getProjectCustomBindings( MavenProject project ) + throws LifecycleLoaderException, LifecycleSpecificationException; + + /** + * Construct the LifecycleBindings that constitute the mojos mapped to the lifecycles by an overlay + * specified in a plugin. Inject mojo configuration from the POM into all appropriate MojoBinding + * instances. + */ + LifecycleBindings getPluginLifecycleOverlay( PluginDescriptor pluginDescriptor, String lifecycleId, MavenProject project ) + throws LifecycleLoaderException, LifecycleSpecificationException; + + /** + * Retrieve the list of MojoBinding instances that correspond to the reports configured for the + * specified project. Inject all appropriate configuration from the POM for each MojoBinding, using + * the following precedence rules: + *
+ *
    + *
  1. report-set-level configuration
  2. + *
  3. reporting-level configuration
  4. + *
  5. execution-level configuration
  6. + *
  7. plugin-level configuration
  8. + *
+ */ + List getReportBindings( MavenProject project ) + throws LifecycleLoaderException, LifecycleSpecificationException; + + /** + * Traverse the specified LifecycleBindings instance for all of the specified tasks. If the task + * is found to be a phase name, construct the list of all MojoBindings that lead up to that phase + * in that lifecycle, and add them to the master MojoBinding list. If the task is not a phase name, + * treat it as a direct mojo invocation, parse it into a MojoBinding (resolving the plugin prefix + * first if necessary), and add it to the master MojoBinding list. + * + * Then, iterate through all MojoBindings in the master list, and for each one that maps to an + * entry in directInvocationModifiers, substitute the resultant MojoBinding list from that + * modifier in place of the original MojoBinding. + * + * Finally, return the modified master list. + */ + List assembleMojoBindingList( List tasks, LifecycleBindings bindings, Map directInvocationModifiers, MavenProject project ) + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException; + + /** + * Traverse the specified LifecycleBindings instance for all of the specified tasks. If the task + * is found to be a phase name, construct the list of all MojoBindings that lead up to that phase + * in that lifecycle, and add them to the master MojoBinding list. If the task is not a phase name, + * treat it as a direct mojo invocation, parse it into a MojoBinding (resolving the plugin prefix + * first if necessary), and add it to the master MojoBinding list. Finally, return the master list. + */ + List assembleMojoBindingList( List tasks, LifecycleBindings lifecycleBindings, MavenProject project ) + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException; + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/binding/MojoBindingFactory.java b/maven-core/src/main/java/org/apache/maven/lifecycle/binding/MojoBindingFactory.java new file mode 100644 index 0000000000..95dab64cec --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/binding/MojoBindingFactory.java @@ -0,0 +1,43 @@ +package org.apache.maven.lifecycle.binding; + +import org.apache.maven.lifecycle.LifecycleLoaderException; +import org.apache.maven.lifecycle.LifecycleSpecificationException; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.plugin.loader.PluginLoader; +import org.apache.maven.project.MavenProject; + +/** + * Responsible for constructing or parsing MojoBinding instances from one of several sources, potentially + * using the {@link PluginLoader} to resolve any plugin prefixes first. + * + * @author jdcasey + * + */ +public interface MojoBindingFactory +{ + + String ROLE = MojoBindingFactory.class.getName(); + + /** + * Parse the specified mojo string into a MojoBinding, optionally allowing plugin-prefix references. + * If a plugin-prefix is allowed and used, resolve the prefix and use the resulting PluginDescriptor + * to set groupId and artifactId on the MojoBinding instance. + */ + MojoBinding parseMojoBinding( String bindingSpec, MavenProject project, boolean allowPrefixReference ) + throws LifecycleSpecificationException, LifecycleLoaderException; + + /** + * Create a new MojoBinding instance with the specified information, and inject POM configurations + * appropriate to that mojo before returning it. + */ + MojoBinding createMojoBinding( String groupId, String artifactId, String version, String goal, MavenProject project ); + + /** + * Simplified version of {@link MojoBindingFactory#parseMojoBinding(String, MavenProject, boolean)} + * which assumes the project is null and prefixes are not allowed. This method will never + * result in the {@link PluginLoader} being used to resolve the PluginDescriptor. + */ + MojoBinding parseMojoBinding( String bindingSpec ) + throws LifecycleSpecificationException; + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/mapping/DefaultLifecycleMapping.java b/maven-core/src/main/java/org/apache/maven/lifecycle/mapping/DefaultLifecycleMapping.java index 0ac078c198..b048cde8fb 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/mapping/DefaultLifecycleMapping.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/mapping/DefaultLifecycleMapping.java @@ -25,7 +25,7 @@ import java.util.Map; /** - * Lifecycle mapping for a POM. + * LegacyLifecycle mapping for a POM. * * @author Brett Porter * @version $Id$ diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/mapping/Lifecycle.java b/maven-core/src/main/java/org/apache/maven/lifecycle/mapping/Lifecycle.java index 3e9923e0af..0529d3cfb9 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/mapping/Lifecycle.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/mapping/Lifecycle.java @@ -24,7 +24,7 @@ import java.util.Map; /** - * Class Lifecycle. + * Class LegacyLifecycle. */ public class Lifecycle { diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/plan/BuildPlan.java b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/BuildPlan.java new file mode 100644 index 0000000000..9608fe6f48 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/BuildPlan.java @@ -0,0 +1,19 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.LifecycleLoaderException; +import org.apache.maven.lifecycle.LifecycleSpecificationException; +import org.apache.maven.lifecycle.binding.LifecycleBindingManager; +import org.apache.maven.project.MavenProject; + +import java.util.List; + +public interface BuildPlan + extends ModifiablePlanElement +{ + + List getPlanMojoBindings(MavenProject project, LifecycleBindingManager bindingManager) + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException; + + List getTasks(); + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/plan/BuildPlanModifier.java b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/BuildPlanModifier.java new file mode 100644 index 0000000000..7a08bdfe48 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/BuildPlanModifier.java @@ -0,0 +1,20 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.model.LifecycleBindings; + +/** + * Modifies an existing set of lifecycle mojo bindings, in order to inject extra behavior, such as + * forked executions, reporting, etc. + */ +public interface BuildPlanModifier extends ModifiablePlanElement +{ + + /** + * Inject any modifications into the given LifecycleBindings provided by the build plan. In some + * cases, it may be necessary to regenerate the LifecycleBindings instance, so the altered instance + * is returned separately. + */ + LifecycleBindings modifyBindings( LifecycleBindings bindings ) + throws LifecyclePlannerException; + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/plan/BuildPlanUtils.java b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/BuildPlanUtils.java new file mode 100644 index 0000000000..95a9d38ae6 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/BuildPlanUtils.java @@ -0,0 +1,180 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.LifecycleLoaderException; +import org.apache.maven.lifecycle.LifecycleSpecificationException; +import org.apache.maven.lifecycle.LifecycleUtils; +import org.apache.maven.lifecycle.MojoBindingUtils; +import org.apache.maven.lifecycle.binding.LifecycleBindingManager; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.lifecycle.statemgmt.StateManagementUtils; +import org.apache.maven.project.MavenProject; + +import java.util.Iterator; +import java.util.List; + +/** + * Collection of static utility methods used to work with LifecycleBindings and other collections + * of MojoBinding instances that make up a build plan. + */ +public final class BuildPlanUtils +{ + + private BuildPlanUtils() + { + } + + /** + * Inject a set of {@link BuildPlanModifier} instances into an existing LifecycleBindings instance. + * This is a generalization of a piece of code present in almost all scenarios where a build + * plan contains modifiers and is asked to produce an effective list of MojoBinding instances + * that make up the build process. Simply iterate through the modifiers, and apply each one, + * replacing the previous LifecycleBindings instance with the result of the current modifier. + */ + public static LifecycleBindings modifyPlanBindings( LifecycleBindings bindings, List planModifiers ) + throws LifecyclePlannerException + { + LifecycleBindings result; + + // if the bindings are completely empty, passing in null avoids an extra instance creation + // for the purposes of cloning... + if ( bindings != null ) + { + result = LifecycleUtils.cloneBindings( bindings ); + } + else + { + result = new LifecycleBindings(); + } + + for ( Iterator it = planModifiers.iterator(); it.hasNext(); ) + { + BuildPlanModifier modifier = (BuildPlanModifier) it.next(); + + result = modifier.modifyBindings( result ); + } + + return result; + } + + /** + * Render an entire build plan to a String. + * If extendedInfo == true, include each MojoBinding's configuration in the output. + */ + public static String listBuildPlan( BuildPlan plan, MavenProject project, LifecycleBindingManager lifecycleBindingManager, boolean extendedInfo ) + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException + { + List mojoBindings = plan.getPlanMojoBindings( project, lifecycleBindingManager ); + + return listBuildPlan( mojoBindings, extendedInfo ); + } + + /** + * Render a list containing the MojoBinding instances for an entire build plan to a String. + * If extendedInfo == true, include each MojoBinding's configuration in the output. + */ + public static String listBuildPlan( List mojoBindings, boolean extendedInfo ) + throws LifecycleSpecificationException, LifecyclePlannerException + { + StringBuffer listing = new StringBuffer(); + int indentLevel = 0; + + int counter = 1; + for ( Iterator it = mojoBindings.iterator(); it.hasNext(); ) + { + MojoBinding binding = (MojoBinding) it.next(); + + if ( StateManagementUtils.isForkedExecutionStartMarker( binding ) ) + { + newListingLine( listing, indentLevel, counter ); + listing.append( "[fork start]" ); + + if ( extendedInfo ) + { + listing.append( ' ' ).append( formatMojoListing( binding, indentLevel, extendedInfo ) ); + } + + indentLevel++; + } + else if ( StateManagementUtils.isForkedExecutionClearMarker( binding ) ) + { + indentLevel--; + + newListingLine( listing, indentLevel, counter ); + listing.append( "[fork cleanup]" ); + + if ( extendedInfo ) + { + listing.append( ' ' ).append( formatMojoListing( binding, indentLevel, extendedInfo ) ); + } + } + else if ( StateManagementUtils.isForkedExecutionEndMarker( binding ) ) + { + indentLevel--; + + newListingLine( listing, indentLevel, counter ); + listing.append( "[fork end]" ); + + if ( extendedInfo ) + { + listing.append( ' ' ).append( formatMojoListing( binding, indentLevel, extendedInfo ) ); + } + + indentLevel++; + } + else + { + newListingLine( listing, indentLevel, counter ); + listing.append( formatMojoListing( binding, indentLevel, extendedInfo ) ); + } + + counter++; + } + + return listing.toString(); + } + + /** + * Append a newline character, add the next line's number, and indent the new line to the + * appropriate level (which tracks separate forked executions). + */ + private static void newListingLine( StringBuffer listing, int indentLevel, int counter ) + { + listing.append( '\n' ); + + listing.append( counter ).append( "." ); + + for ( int i = 0; i < indentLevel; i++ ) + { + listing.append( " " ); + } + + // adding a minimal offset from the counter (line-number) of the listing. + listing.append( ' ' ); + + } + + /** + * Format a single MojoBinding for inclusion in a build plan listing. If extendedInfo == true, + * include the MojoBinding's configuration in the output. + */ + public static String formatMojoListing( MojoBinding binding, int indentLevel, boolean extendedInfo ) + { + StringBuffer listing = new StringBuffer(); + + listing.append( MojoBindingUtils.toString( binding ) ); + listing.append( " [executionId: " ).append( binding.getExecutionId() ).append( "]" ); + + if ( extendedInfo ) + { + listing.append( "\nOrigin: " ).append( binding.getOrigin() ); + listing.append( "\nConfiguration:\n\t" ).append( + String.valueOf( binding.getConfiguration() ).replaceAll( "\\n", + "\n\t" ) ).append( + '\n' ); + } + + return listing.toString(); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/plan/BuildPlanner.java b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/BuildPlanner.java new file mode 100644 index 0000000000..7ed86dd20f --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/BuildPlanner.java @@ -0,0 +1,27 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.LifecycleLoaderException; +import org.apache.maven.lifecycle.LifecycleSpecificationException; +import org.apache.maven.project.MavenProject; + +import java.util.List; + +/** + * Responsible for creating a plan of execution for a given project and list of tasks. This build plan + * consists of MojoBinding instances that carry all the information necessary to execute a mojo, + * including configuration from the POM and other sources. NOTE: the build plan may be constructed + * of a main lifecycle binding-set, plus any number of lifecycle modifiers and direct-invocation + * modifiers, to handle cases of forked execution. + * + * @author jdcasey + * + */ +public interface BuildPlanner +{ + + /** + * Orchestrates construction of the build plan which will be used by the user of LifecycleExecutor. + */ + BuildPlan constructBuildPlan( List tasks, MavenProject project ) + throws LifecycleLoaderException, LifecycleSpecificationException, LifecyclePlannerException; +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/plan/DefaultBuildPlanner.java b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/DefaultBuildPlanner.java new file mode 100644 index 0000000000..d00264f5a0 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/DefaultBuildPlanner.java @@ -0,0 +1,408 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.LifecycleLoaderException; +import org.apache.maven.lifecycle.LifecycleSpecificationException; +import org.apache.maven.lifecycle.LifecycleUtils; +import org.apache.maven.lifecycle.MojoBindingUtils; +import org.apache.maven.lifecycle.binding.LifecycleBindingManager; +import org.apache.maven.lifecycle.binding.MojoBindingFactory; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.loader.PluginLoader; +import org.apache.maven.plugin.loader.PluginLoaderException; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.logging.LogEnabled; +import org.codehaus.plexus.logging.Logger; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * Responsible for creating a plan of execution for a given project and list of tasks. This build plan + * consists of MojoBinding instances that carry all the information necessary to execute a mojo, + * including configuration from the POM and other sources. NOTE: the build plan may be constructed + * of a main lifecycle binding-set, plus any number of lifecycle modifiers and direct-invocation + * modifiers, to handle cases of forked execution. + * + * @author jdcasey + * + */ +public class DefaultBuildPlanner + implements BuildPlanner, LogEnabled +{ + + private Logger logger; + + private PluginLoader pluginLoader; + + private LifecycleBindingManager lifecycleBindingManager; + + private MojoBindingFactory mojoBindingFactory; + + /** + * Orchestrates construction of the build plan which will be used by the user of LifecycleExecutor. + */ + public BuildPlan constructBuildPlan( List tasks, MavenProject project ) + throws LifecycleLoaderException, LifecycleSpecificationException, LifecyclePlannerException + { + LifecycleBindings defaultBindings = lifecycleBindingManager.getDefaultBindings( project ); + LifecycleBindings packagingBindings = lifecycleBindingManager.getBindingsForPackaging( project ); + LifecycleBindings projectBindings = lifecycleBindingManager.getProjectCustomBindings( project ); + + LifecycleBindings merged = LifecycleUtils.mergeBindings( packagingBindings, projectBindings, defaultBindings, true, false ); + + // foreach task, find the binding list from the merged lifecycle-bindings. + // if the binding list is a super-set of a previous task, forget the previous task/binding + // list, and use the new one. + // if the binding list is null, treat it like a one-off mojo invocation, and parse/validate + // that it can be called as such. + // as binding lists accumulate, push them onto an aggregated "plan" listing... + BuildPlan plan = new LifecycleBuildPlan( tasks, merged ); + + // Inject forked lifecycles as plan modifiers for each mojo that has @execute in it. + addForkedLifecycleModifiers( plan, merged, project, tasks ); + addReportingLifecycleModifiers( plan, merged, project, tasks ); + + // TODO: Inject relative-ordered project/plugin executions as plan modifiers. + + return plan; + } + + public void enableLogging( Logger logger ) + { + this.logger = logger; + } + + /** + * Traverses all MojoBinding instances discovered from the POM and its packaging-mappings, and + * orchestrates the process of injecting any modifiers that are necessary to accommodate forked + * execution. + */ + private void addForkedLifecycleModifiers( ModifiablePlanElement planElement, LifecycleBindings lifecycleBindings, + MavenProject project, List tasks ) + throws LifecyclePlannerException, LifecycleSpecificationException, LifecycleLoaderException + { + List planBindings = lifecycleBindingManager.assembleMojoBindingList( tasks, lifecycleBindings, project ); + + for ( Iterator it = planBindings.iterator(); it.hasNext(); ) + { + MojoBinding mojoBinding = (MojoBinding) it.next(); + + PluginDescriptor pluginDescriptor; + try + { + pluginDescriptor = pluginLoader.loadPlugin( mojoBinding, project ); + } + catch ( PluginLoaderException e ) + { + throw new LifecyclePlannerException( e.getMessage(), e ); + } + + MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( mojoBinding.getGoal() ); + if ( mojoDescriptor == null ) + { + throw new LifecyclePlannerException( "Mojo: " + mojoBinding.getGoal() + " does not exist in plugin: " + + pluginDescriptor.getId() + "." ); + } + + findForkModifiers( mojoBinding, pluginDescriptor, planElement, lifecycleBindings, project, new LinkedList(), tasks ); + } + } + + /** + * Traverses all MojoBinding instances discovered from the POM and its packaging-mappings, and + * orchestrates the process of injecting any modifiers that are necessary to accommodate mojos + * that require access to the project's configured reports. + */ + private void addReportingLifecycleModifiers( ModifiablePlanElement planElement, LifecycleBindings lifecycleBindings, + MavenProject project, List tasks ) + throws LifecyclePlannerException, LifecycleSpecificationException, LifecycleLoaderException + { + List planBindings = lifecycleBindingManager.assembleMojoBindingList( tasks, lifecycleBindings, project ); + + for ( Iterator it = planBindings.iterator(); it.hasNext(); ) + { + MojoBinding mojoBinding = (MojoBinding) it.next(); + + PluginDescriptor pluginDescriptor; + try + { + pluginDescriptor = pluginLoader.loadPlugin( mojoBinding, project ); + } + catch ( PluginLoaderException e ) + { + throw new LifecyclePlannerException( e.getMessage(), e ); + } + + MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( mojoBinding.getGoal() ); + if ( mojoDescriptor == null ) + { + throw new LifecyclePlannerException( "Mojo: " + mojoBinding.getGoal() + " does not exist in plugin: " + + pluginDescriptor.getId() + "." ); + } + + if ( mojoDescriptor.isRequiresReports() ) + { + List reportBindings = lifecycleBindingManager.getReportBindings( project ); + + BuildPlanModifier modder = new ReportingPlanModifier( mojoBinding, reportBindings ); + + planElement.addModifier( modder ); + + // NOTE: the first sighting of a mojo requiring reports should satisfy this condition. + // therefore, we can break out as soon as we find one. + break; + } + } + } + + /** + * Explores a single MojoBinding, and injects any necessary plan modifiers to accommodate any + * of the three types of forked execution, along with any new mojos/lifecycles that entails. + */ + private void findForkModifiers( MojoBinding mojoBinding, PluginDescriptor pluginDescriptor, + ModifiablePlanElement planElement, LifecycleBindings mergedBindings, MavenProject project, + LinkedList forkingBindings, List tasks ) + throws LifecyclePlannerException, LifecycleSpecificationException, LifecycleLoaderException + { + forkingBindings.addLast( mojoBinding ); + + try + { + String referencingGoal = mojoBinding.getGoal(); + + MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( referencingGoal ); + + if ( mojoDescriptor.getExecuteGoal() != null ) + { + recurseSingleMojoFork( mojoBinding, pluginDescriptor, planElement, mergedBindings, project, forkingBindings, + tasks ); + } + else if ( mojoDescriptor.getExecutePhase() != null ) + { + recursePhaseMojoFork( mojoBinding, pluginDescriptor, planElement, mergedBindings, project, forkingBindings, tasks ); + } + } + finally + { + forkingBindings.removeLast(); + } + } + + /** + * Handles exploration of a single-mojo forked execution for further forkings, and also performs + * the actual build-plan modification for that single-mojo forked execution. + */ + private void modifyBuildPlanForForkedDirectInvocation( MojoBinding invokedBinding, MojoBinding invokedVia, + PluginDescriptor pluginDescriptor, ModifiablePlanElement planElement, + LifecycleBindings mergedBindings, MavenProject project, + LinkedList forkingBindings, List tasks ) + throws LifecyclePlannerException, LifecycleSpecificationException, LifecycleLoaderException + { + if ( planElement instanceof DirectInvocationOriginElement ) + { + List noTasks = Collections.EMPTY_LIST; + + LifecycleBindings forkedBindings = new LifecycleBindings(); + LifecycleBuildPlan forkedPlan = new LifecycleBuildPlan( noTasks, forkedBindings ); + + forkingBindings.addLast( invokedBinding ); + try + { + findForkModifiers( invokedBinding, pluginDescriptor, forkedPlan, forkedBindings, project, + forkingBindings, tasks ); + } + finally + { + forkingBindings.removeLast(); + } + + List forkedMojos = new ArrayList(); + forkedMojos.addAll( lifecycleBindingManager.assembleMojoBindingList( noTasks, forkedBindings, project ) ); + forkedMojos.add( invokedBinding ); + + DirectInvocationModifier modifier = new ForkedDirectInvocationModifier( invokedVia, forkedMojos ); + + ( (DirectInvocationOriginElement) planElement ).addDirectInvocationModifier( modifier ); + } + else + { + throw new LifecyclePlannerException( "Mojo: " + MojoBindingUtils.toString( invokedVia ) + + " is not bound to the lifecycle; you cannot attach this mojo to a build-plan modifier." ); + } + } + + /** + * Handles exploration of a lifecycle-based forked execution for further forkings, and also performs + * the actual build-plan modification for that lifecycle-based forked execution. + */ + private void modifyBuildPlanForForkedLifecycle( MojoBinding mojoBinding, PluginDescriptor pluginDescriptor, + ModifiablePlanElement planElement, LifecycleBindings bindings, + MavenProject project, LinkedList forkingBindings, List tasks ) + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException + { + MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( mojoBinding.getGoal() ); + String phase = mojoDescriptor.getExecutePhase(); + + List forkedPhaseBindingList = lifecycleBindingManager.assembleMojoBindingList( Collections.singletonList( phase ), + bindings, project ); + + ModifiablePlanElement mpe; + + // setup the ModifiablePlanElement, into which we'll recurse to find further modifications. + if ( LifecycleUtils.findPhaseForMojoBinding( mojoBinding, bindings, true ) != null ) + { + mpe = new ForkPlanModifier( mojoBinding, forkedPhaseBindingList ); + } + else if ( planElement instanceof BuildPlan ) + { + mpe = new SubLifecycleBuildPlan( phase, bindings ); + } + else + { + throw new LifecyclePlannerException( "Mojo: " + MojoBindingUtils.toString( mojoBinding ) + + " is not bound to the lifecycle; you cannot attach this mojo to a build-plan modifier." ); + } + + // recurse, to find further modifications, using the ModifiablePlanElement from above, along + // with the modified task list (which doesn't contain the direct-invocation task that landed + // us here... + for ( Iterator it = forkedPhaseBindingList.iterator(); it.hasNext(); ) + { + MojoBinding forkedBinding = (MojoBinding) it.next(); + + PluginDescriptor forkedPluginDescriptor; + try + { + forkedPluginDescriptor = pluginLoader.loadPlugin( forkedBinding, project ); + } + catch ( PluginLoaderException e ) + { + throw new LifecyclePlannerException( e.getMessage(), e ); + } + + findForkModifiers( forkedBinding, forkedPluginDescriptor, mpe, bindings, project, forkingBindings, tasks ); + } + + // now that we've discovered any deeper modifications, add the current MPE to the parent MPE + // in the appropriate location. + if ( LifecycleUtils.findPhaseForMojoBinding( mojoBinding, bindings, true ) != null ) + { + planElement.addModifier( (BuildPlanModifier) mpe ); + } + else if ( planElement instanceof DirectInvocationOriginElement ) + { + List planMojoBindings = ((BuildPlan) mpe).getPlanMojoBindings( project, lifecycleBindingManager ); + + ForkedDirectInvocationModifier modifier = new ForkedDirectInvocationModifier( mojoBinding, planMojoBindings ); + + ( (DirectInvocationOriginElement) planElement ).addDirectInvocationModifier( modifier ); + } + } + + /** + * Constructs the lifecycle bindings used to execute a particular fork, given the forking mojo + * binding. If the mojo binding specifies a lifecycle overlay, this method will add that into + * the forked lifecycle, and calculate the bindings to inject based on the phase in that new + * lifecycle which should be executed. + * + * Hands off to the {@link DefaultBuildPlanner#modifyBuildPlanForForkedLifecycle(MojoBinding, PluginDescriptor, ModifiablePlanElement, LifecycleBindings, MavenProject, LinkedList, List)} + * method to handle the actual plan modification. + */ + private void recursePhaseMojoFork( MojoBinding mojoBinding, PluginDescriptor pluginDescriptor, + ModifiablePlanElement planElement, LifecycleBindings mergedBindings, MavenProject project, + LinkedList forkingBindings, List tasks ) + throws LifecyclePlannerException, LifecycleSpecificationException, LifecycleLoaderException + { + String referencingGoal = mojoBinding.getGoal(); + + MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( referencingGoal ); + + String phase = mojoDescriptor.getExecutePhase(); + + if ( phase == null ) + { + return; + } + + if ( LifecycleUtils.findLifecycleBindingForPhase( phase, mergedBindings ) == null ) + { + throw new LifecyclePlannerException( "Cannot find lifecycle for phase: " + phase ); + } + + LifecycleBindings cloned; + if ( mojoDescriptor.getExecuteLifecycle() != null ) + { + String executeLifecycle = mojoDescriptor.getExecuteLifecycle(); + + LifecycleBindings overlayBindings; + try + { + overlayBindings = lifecycleBindingManager.getPluginLifecycleOverlay( pluginDescriptor, executeLifecycle, project ); + } + catch ( LifecycleLoaderException e ) + { + throw new LifecyclePlannerException( "Failed to load overlay lifecycle: " + executeLifecycle + ". Reason: " + + e.getMessage(), e ); + } + + cloned = LifecycleUtils.cloneBindings( mergedBindings ); + cloned = LifecycleUtils.mergeBindings( overlayBindings, cloned, null, true, true ); + } + else + { + cloned = LifecycleUtils.cloneBindings( mergedBindings ); + } + + LifecycleUtils.removeMojoBindings( forkingBindings, cloned, false ); + + modifyBuildPlanForForkedLifecycle( mojoBinding, pluginDescriptor, planElement, cloned, project, forkingBindings, tasks ); + } + + /** + * Retrieves the information necessary to create a new MojoBinding for a single-mojo forked + * execution, then hands off to the {@link DefaultBuildPlanner#modifyBuildPlanForForkedDirectInvocation(MojoBinding, MojoBinding, PluginDescriptor, ModifiablePlanElement, LifecycleBindings, MavenProject, LinkedList, List)} + * method to actually inject the modification. + */ + private void recurseSingleMojoFork( MojoBinding mojoBinding, PluginDescriptor pluginDescriptor, + ModifiablePlanElement planElement, LifecycleBindings mergedBindings, + MavenProject project, LinkedList forkingBindings, List tasks ) + throws LifecyclePlannerException, LifecycleSpecificationException, LifecycleLoaderException + { + String referencingGoal = mojoBinding.getGoal(); + + MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( referencingGoal ); + + String executeGoal = mojoDescriptor.getExecuteGoal(); + + if ( executeGoal == null ) + { + return; + } + + MojoDescriptor otherDescriptor = pluginDescriptor.getMojo( executeGoal ); + if ( otherDescriptor == null ) + { + throw new LifecyclePlannerException( "Mojo: " + executeGoal + " (referenced by: " + referencingGoal + + ") does not exist in plugin: " + pluginDescriptor.getId() + "." ); + } + + MojoBinding binding = mojoBindingFactory.createMojoBinding( pluginDescriptor.getGroupId(), + pluginDescriptor.getArtifactId(), + pluginDescriptor.getVersion(), executeGoal, project ); + + binding.setOrigin( "Forked from " + referencingGoal ); + + if ( !LifecycleUtils.isMojoBindingPresent( binding, forkingBindings, false ) ) + { + modifyBuildPlanForForkedDirectInvocation( binding, mojoBinding, pluginDescriptor, planElement, mergedBindings, + project, forkingBindings, tasks ); + } + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/plan/DirectInvocationModifier.java b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/DirectInvocationModifier.java new file mode 100644 index 0000000000..23e5b622c2 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/DirectInvocationModifier.java @@ -0,0 +1,32 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.binding.LifecycleBindingManager; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.project.MavenProject; + +import java.util.List; + +/** + * Modifier that alters a build plan to substitute a set of MojoBindings in place of a single, + * direct-invocation MojoBinding. These bindings are not impacted by {@link BuildPlanModifier}s, + * since they don't exist in a {@link LifecycleBindings} instance. + * + * @author jdcasey + * + */ +public interface DirectInvocationModifier +{ + + /** + * The MojoBinding which should be modified. + */ + MojoBinding getBindingToModify(); + + /** + * Return the list of MojoBindings which should replace the modified binding in the master + * build plan. + */ + List getModifiedBindings( MavenProject project, LifecycleBindingManager bindingManager ); + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/plan/DirectInvocationOriginElement.java b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/DirectInvocationOriginElement.java new file mode 100644 index 0000000000..761fd43e41 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/DirectInvocationOriginElement.java @@ -0,0 +1,17 @@ +package org.apache.maven.lifecycle.plan; + +/** + * Instantiates MojoBindings for direct invocation, which may be subject to modification. + * + * @author jdcasey + * + */ +public interface DirectInvocationOriginElement +{ + + /** + * Add a new direct-invocation binding modifier. + */ + void addDirectInvocationModifier( DirectInvocationModifier modifier ); + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/plan/ForkPlanModifier.java b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/ForkPlanModifier.java new file mode 100644 index 0000000000..1156a79140 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/ForkPlanModifier.java @@ -0,0 +1,113 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.LifecycleUtils; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.lifecycle.model.Phase; +import org.apache.maven.lifecycle.statemgmt.StateManagementUtils; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * Inject a list of forked-execution bindings at the point where the modification point is bound to + * the supplied LifecycleBindings, bracketed by special mojo bindings to control the forked-execution + * context. + * + * @author jdcasey + * + */ +public class ForkPlanModifier + implements BuildPlanModifier +{ + + private final MojoBinding modificationPoint; + private List planModifiers = new ArrayList(); + + private final List mojoBindings; + + public ForkPlanModifier( MojoBinding modificationPoint, List mojoBindings ) + { + this.modificationPoint = modificationPoint; + this.mojoBindings = mojoBindings; + } + + /** + * Retrieve the MojoBinding which serves as the injection point for the forked bindings. + */ + public MojoBinding getModificationPoint() + { + return modificationPoint; + } + + /** + * Modify the LifeycleBindings from a BuildPlan by locating the modification point MojoBinding, + * and prepending the forked-execution bindings in the plan, bracketed by mojos that control the + * forked-execution context. + */ + public LifecycleBindings modifyBindings( LifecycleBindings bindings ) + throws LifecyclePlannerException + { + Phase phase = LifecycleUtils.findPhaseForMojoBinding( getModificationPoint(), bindings, true ); + + String modificationKey = LifecycleUtils.createMojoBindingKey( getModificationPoint(), true ); + + if ( phase == null ) + { + throw new LifecyclePlannerException( "Failed to modify plan. No phase found containing mojoBinding: " + + modificationKey ); + } + + int stopIndex = -1; + int insertionIndex = -1; + List phaseBindings = phase.getBindings(); + + for ( int i = 0; i < phaseBindings.size(); i++ ) + { + MojoBinding candidate = (MojoBinding) phaseBindings.get( i ); + + String key = LifecycleUtils.createMojoBindingKey( candidate, true ); + if ( key.equals( modificationKey ) ) + { + insertionIndex = i; + stopIndex = i + 1; + break; + } + } + + phaseBindings.add( stopIndex, StateManagementUtils.createClearForkedExecutionMojoBinding() ); + + phaseBindings.add( insertionIndex, StateManagementUtils.createEndForkedExecutionMojoBinding() ); + phaseBindings.addAll( insertionIndex, mojoBindings ); + phaseBindings.add( insertionIndex, StateManagementUtils.createStartForkedExecutionMojoBinding() ); + + phase.setBindings( phaseBindings ); + + for ( Iterator it = planModifiers.iterator(); it.hasNext(); ) + { + BuildPlanModifier modifier = (BuildPlanModifier) it.next(); + + modifier.modifyBindings( bindings ); + } + + return bindings; + } + + /** + * Add a new modifier to further adjust the LifecycleBindings which are modified here. + */ + public void addModifier( BuildPlanModifier planModifier ) + { + planModifiers.add( planModifier ); + } + + /** + * Return true if this modifier itself has modifiers. + */ + public boolean hasModifiers() + { + return !planModifiers.isEmpty(); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/plan/ForkedDirectInvocationModifier.java b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/ForkedDirectInvocationModifier.java new file mode 100644 index 0000000000..9c03761d04 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/ForkedDirectInvocationModifier.java @@ -0,0 +1,56 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.binding.LifecycleBindingManager; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.lifecycle.statemgmt.StateManagementUtils; +import org.apache.maven.project.MavenProject; + +import java.util.ArrayList; +import java.util.List; + +/** + * Inject a list of MojoBindings in place of the forking binding, bracketing the forked bindings with + * special mojos to control the forked-execution context. + * + * @author jdcasey + * + */ +public class ForkedDirectInvocationModifier + implements DirectInvocationModifier +{ + + private final List forkedBindings; + private final MojoBinding forkingBinding; + + public ForkedDirectInvocationModifier( MojoBinding forkingBinding, List forkedBindings ) + { + this.forkingBinding = forkingBinding; + this.forkedBindings = forkedBindings; + } + + /** + * Return a list containing forked-execution context control MojoBindings, the forked-execution + * bindings themselves, and finally the binding that forked off a new execution branch. + */ + public List getModifiedBindings( MavenProject project, LifecycleBindingManager bindingManager ) + { + List result = new ArrayList(); + + result.add( StateManagementUtils.createStartForkedExecutionMojoBinding() ); + result.addAll( forkedBindings ); + result.add( StateManagementUtils.createEndForkedExecutionMojoBinding() ); + result.add( forkingBinding ); + result.add( StateManagementUtils.createClearForkedExecutionMojoBinding() ); + + return result; + } + + /** + * Return the MojoBinding that forks execution to include the bindings in this modifier. + */ + public MojoBinding getBindingToModify() + { + return forkingBinding; + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/plan/LifecycleBuildPlan.java b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/LifecycleBuildPlan.java new file mode 100644 index 0000000000..869ecd31a4 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/LifecycleBuildPlan.java @@ -0,0 +1,91 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.LifecycleLoaderException; +import org.apache.maven.lifecycle.LifecycleSpecificationException; +import org.apache.maven.lifecycle.LifecycleUtils; +import org.apache.maven.lifecycle.binding.LifecycleBindingManager; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.project.MavenProject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Construct a list of MojoBinding instances that accomplish all of the tasks specified. For lifecycle + * phases, construct a list of mojos to execute up to and including the specified phase, and add them + * to the list. For direct invocations, construct a new MojoBinding instance and add it to the list. + * + * All of these bindings are subject to lifecycle modifications due to forking, reporting, or other + * factors, and also to forked-execution modification of direct invocations. + * + * @author jdcasey + * + */ +public class LifecycleBuildPlan + implements BuildPlan, DirectInvocationOriginElement +{ + + private final List tasks; + + private final LifecycleBindings lifecycleBindings; + + private List planModifiers = new ArrayList(); + + private Map directInvocationModifiers = new HashMap(); + + public LifecycleBuildPlan( List tasks, LifecycleBindings lifecycleBindings ) + { + this.tasks = tasks; + this.lifecycleBindings = lifecycleBindings; + } + + /** + * Build the master execution list necessary to accomplish the specified tasks, given the + * specified set of mojo bindings to different parts of the lifecycle. + */ + public List getPlanMojoBindings( MavenProject project, LifecycleBindingManager bindingManager ) + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException + { + LifecycleBindings cloned = BuildPlanUtils.modifyPlanBindings( lifecycleBindings, planModifiers ); + + return bindingManager.assembleMojoBindingList( tasks, cloned, directInvocationModifiers, project ); + } + + /** + * Retrieve the set of tasks that this build plan is responsible for. + */ + public List getTasks() + { + return tasks; + } + + /** + * Add a new build-plan modifier to inject reporting, forked-execution, or other altered behavior + * into the "vanilla" lifecycle that was specified at instance construction. + */ + public void addModifier( BuildPlanModifier planModifier ) + { + planModifiers.add( planModifier ); + } + + /** + * Return true if build-plan modifiers exist (these are lifecycle-only modifiers, not direct + * invocation modifiers). + */ + public boolean hasModifiers() + { + return !planModifiers.isEmpty(); + } + + /** + * Add a new modifier for a direct-invocation MojoBinding in the build plan resulting from this + * instance. + */ + public void addDirectInvocationModifier( DirectInvocationModifier modifier ) + { + directInvocationModifiers.put( LifecycleUtils.createMojoBindingKey( modifier.getBindingToModify(), true ), modifier ); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/plan/LifecyclePlannerException.java b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/LifecyclePlannerException.java new file mode 100644 index 0000000000..2fdb5f995d --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/LifecyclePlannerException.java @@ -0,0 +1,25 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.LifecycleException; + +/** + * Signals an error during build-plan construction. + * + * @author jdcasey + * + */ +public class LifecyclePlannerException + extends LifecycleException +{ + + public LifecyclePlannerException( String message, Throwable cause ) + { + super( message, cause ); + } + + public LifecyclePlannerException( String message ) + { + super( message ); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/plan/ModifiablePlanElement.java b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/ModifiablePlanElement.java new file mode 100644 index 0000000000..e7c72215ca --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/ModifiablePlanElement.java @@ -0,0 +1,23 @@ +package org.apache.maven.lifecycle.plan; + +/** + * Any element of a build plan that contains or handles a LifecycleBindings instance which is subject + * to modification. + * + * @author jdcasey + * + */ +public interface ModifiablePlanElement +{ + + /** + * Add a new lifecycle modifier to this build-plan element. + */ + void addModifier( BuildPlanModifier planModifier ); + + /** + * Return true if this element has lifecycle modifiers + */ + boolean hasModifiers(); + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/plan/ReportingPlanModifier.java b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/ReportingPlanModifier.java new file mode 100644 index 0000000000..19050c1fe8 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/ReportingPlanModifier.java @@ -0,0 +1,94 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.LifecycleUtils; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.lifecycle.model.Phase; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * Inject the MojoBindings necessary to execute and make available the report instances that another + * mojo in the build plan needs. + * + * @author jdcasey + * + */ +public class ReportingPlanModifier + implements BuildPlanModifier +{ + + private List planModifiers = new ArrayList(); + private final MojoBinding targetMojoBinding; + private final List reportBindings; + + public ReportingPlanModifier( MojoBinding mojoBinding, List reportBindings ) + { + this.targetMojoBinding = mojoBinding; + this.reportBindings = reportBindings; + } + + /** + * Find the mojo that requested reports, and inject the reporting MojoBinding instances just + * ahead of it in the lifecycle bindings. + */ + public LifecycleBindings modifyBindings( LifecycleBindings bindings ) + throws LifecyclePlannerException + { + Phase phase = LifecycleUtils.findPhaseForMojoBinding( targetMojoBinding, bindings, true ); + + String modificationKey = LifecycleUtils.createMojoBindingKey( targetMojoBinding, true ); + + if ( phase == null ) + { + throw new LifecyclePlannerException( "Failed to modify plan. No phase found containing mojoBinding: " + + modificationKey ); + } + + int insertionIndex = -1; + List phaseBindings = phase.getBindings(); + + for ( int i = 0; i < phaseBindings.size(); i++ ) + { + MojoBinding candidate = (MojoBinding) phaseBindings.get( i ); + + String key = LifecycleUtils.createMojoBindingKey( candidate, true ); + if ( key.equals( modificationKey ) ) + { + insertionIndex = i; + break; + } + } + + phaseBindings.addAll( insertionIndex, reportBindings ); + phase.setBindings( phaseBindings ); + + for ( Iterator it = planModifiers.iterator(); it.hasNext(); ) + { + BuildPlanModifier modifier = (BuildPlanModifier) it.next(); + + modifier.modifyBindings( bindings ); + } + + return bindings; + } + + /** + * Add further lifecycle modifications to this report-injecting modifier. + */ + public void addModifier( BuildPlanModifier planModifier ) + { + planModifiers.add( planModifier ); + } + + /** + * Return true if this report-injecting modifier contains further modifications for the lifecycle. + */ + public boolean hasModifiers() + { + return !planModifiers.isEmpty(); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/plan/SubLifecycleBuildPlan.java b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/SubLifecycleBuildPlan.java new file mode 100644 index 0000000000..83738c9f74 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/plan/SubLifecycleBuildPlan.java @@ -0,0 +1,70 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.LifecycleLoaderException; +import org.apache.maven.lifecycle.LifecycleSpecificationException; +import org.apache.maven.lifecycle.binding.LifecycleBindingManager; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.project.MavenProject; + +import java.util.Collections; +import java.util.List; + +/** + * Constructs a build plan using a LifecycleBindings instance, but only allowing one phase for that + * lifecycle, as opposed to the parent class which allows a list of tasks (some of which may not be + * lifecycle phases at all). + * + * This build plan cannot produce direct-invocation MojoBindings. + * + * @author jdcasey + * + */ +public class SubLifecycleBuildPlan + implements BuildPlan +{ + + private LifecycleBuildPlan delegate; + private final String phase; + + public SubLifecycleBuildPlan( String phase, LifecycleBindings bindings ) + { + this.phase = phase; + delegate = new LifecycleBuildPlan( Collections.singletonList( phase ), bindings ); + } + + /** + * Retrieve the build plan binding list from the delegate {@link LifecycleBuildPlan} instance, + * and return them. + */ + public List getPlanMojoBindings(MavenProject project, LifecycleBindingManager bindingManager) + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException + { + return delegate.getPlanMojoBindings(project, bindingManager); + } + + /** + * Return a list containing a single item: the lifecycle phase that this plan is concerned with + * accomplishing. + */ + public List getTasks() + { + return Collections.singletonList( phase ); + } + + /** + * Add a build-plan modifier to the delegate {@link LifecycleBuildPlan} instance. + */ + public void addModifier( BuildPlanModifier planModifier ) + { + delegate.addModifier( planModifier ); + } + + /** + * Return true if the delegate {@link LifecycleBuildPlan} instance contains build-plan modifiers. + */ + public boolean hasModifiers() + { + return delegate.hasModifiers(); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/statemgmt/ClearForkedContextMojo.java b/maven-core/src/main/java/org/apache/maven/lifecycle/statemgmt/ClearForkedContextMojo.java new file mode 100644 index 0000000000..3aae33b9bc --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/statemgmt/ClearForkedContextMojo.java @@ -0,0 +1,30 @@ +package org.apache.maven.lifecycle.statemgmt; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; + +/** + * Remove the execution-project used during the fork, now that the forking mojo is finished executing. + * + * @author jdcasey + * + */ +public class ClearForkedContextMojo + extends AbstractMojo +{ + + private MavenProject project; + + private int forkId = -1; + + public void execute() + throws MojoExecutionException, MojoFailureException + { + getLog().info( "Cleaning up forked execution context [fork id: " + forkId + "]" ); + + project.clearExecutionProject(); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/statemgmt/EndForkedExecutionMojo.java b/maven-core/src/main/java/org/apache/maven/lifecycle/statemgmt/EndForkedExecutionMojo.java new file mode 100644 index 0000000000..3f170992f4 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/statemgmt/EndForkedExecutionMojo.java @@ -0,0 +1,39 @@ +package org.apache.maven.lifecycle.statemgmt; + +import org.apache.maven.context.BuildContextManager; +import org.apache.maven.lifecycle.LifecycleExecutionContext; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; + +/** + * Restore the lifecycle execution context's current-project, and set the project instance from the + * forked execution to project.getExecutionProject() for the forking mojo to use. + * + * @author jdcasey + * + */ +public class EndForkedExecutionMojo + extends AbstractMojo +{ + + private int forkId = -1; + + private BuildContextManager buildContextManager; + + public void execute() + throws MojoExecutionException, MojoFailureException + { + getLog().info( "Ending forked execution [fork id: " + forkId + "]" ); + + LifecycleExecutionContext ctx = LifecycleExecutionContext.read( buildContextManager ); + MavenProject executionProject = ctx.removeForkedProject(); + + MavenProject project = ctx.getCurrentProject(); + project.setExecutionProject( executionProject ); + + ctx.store( buildContextManager ); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/statemgmt/StartForkedExecutionMojo.java b/maven-core/src/main/java/org/apache/maven/lifecycle/statemgmt/StartForkedExecutionMojo.java new file mode 100644 index 0000000000..5d881f076e --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/statemgmt/StartForkedExecutionMojo.java @@ -0,0 +1,36 @@ +package org.apache.maven.lifecycle.statemgmt; + +import org.apache.maven.context.BuildContextManager; +import org.apache.maven.lifecycle.LifecycleExecutionContext; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; + +/** + * Setup a new project instance for the forked executions to use. + * + * @author jdcasey + * + */ +public class StartForkedExecutionMojo + extends AbstractMojo +{ + + private MavenProject project; + + private int forkId = -1; + + private BuildContextManager buildContextManager; + + public void execute() + throws MojoExecutionException, MojoFailureException + { + getLog().info( "Starting forked execution [fork id: " + forkId + "]" ); + + LifecycleExecutionContext ctx = LifecycleExecutionContext.read( buildContextManager ); + ctx.addForkedProject( new MavenProject( project ) ); + ctx.store( buildContextManager ); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/statemgmt/StateManagementUtils.java b/maven-core/src/main/java/org/apache/maven/lifecycle/statemgmt/StateManagementUtils.java new file mode 100644 index 0000000000..e5ab8231df --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/statemgmt/StateManagementUtils.java @@ -0,0 +1,149 @@ +package org.apache.maven.lifecycle.statemgmt; + +import org.apache.maven.lifecycle.model.MojoBinding; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Constructs and matches MojoBinding instances that refer to the forked-execution context manager + * mojos. + * + * @author jdcasey + * + */ +public final class StateManagementUtils +{ + + public static final String GROUP_ID = "org.apache.maven.plugins.internal"; + + public static final String ARTIFACT_ID = "maven-state-management"; + + public static final String ORIGIN = "Maven build-state management"; + + public static final String END_FORKED_EXECUTION_GOAL = "end-fork"; + + public static final String START_FORKED_EXECUTION_GOAL = "start-fork"; + + public static final String VERSION = "2.1"; + + public static final String CLEAR_FORKED_EXECUTION_GOAL = "clear-fork-context"; + + private static int CURRENT_FORK_ID = 0; + + private StateManagementUtils() + { + } + + /** + * Create a new MojoBinding instance that refers to the internal mojo used to setup a new + * forked-execution context. Also, set the configuration to contain the forkId for this new + * context. + */ + public static MojoBinding createStartForkedExecutionMojoBinding() + { + MojoBinding binding = new MojoBinding(); + + binding.setGroupId( GROUP_ID ); + binding.setArtifactId( ARTIFACT_ID ); + binding.setVersion( VERSION ); + binding.setGoal( START_FORKED_EXECUTION_GOAL ); + binding.setOrigin( ORIGIN ); + + CURRENT_FORK_ID = (int) System.currentTimeMillis(); + + Xpp3Dom config = new Xpp3Dom( "configuration" ); + Xpp3Dom forkId = new Xpp3Dom( "forkId" ); + forkId.setValue( "" + CURRENT_FORK_ID ); + + config.addChild( forkId ); + + binding.setConfiguration( config ); + + return binding; + } + + /** + * Create a new MojoBinding instance that refers to the internal mojo used to end a + * forked-execution context. Also, set the configuration to contain the forkId for this new + * context. + */ + public static MojoBinding createEndForkedExecutionMojoBinding() + { + MojoBinding binding = new MojoBinding(); + + binding.setGroupId( GROUP_ID ); + binding.setArtifactId( ARTIFACT_ID ); + binding.setVersion( VERSION ); + binding.setGoal( END_FORKED_EXECUTION_GOAL ); + binding.setOrigin( ORIGIN ); + + Xpp3Dom config = new Xpp3Dom( "configuration" ); + Xpp3Dom forkId = new Xpp3Dom( "forkId" ); + forkId.setValue( "" + CURRENT_FORK_ID ); + + config.addChild( forkId ); + + binding.setConfiguration( config ); + + return binding; + } + + /** + * Create a new MojoBinding instance that refers to the internal mojo used to cleanup a completed + * forked-execution context. Also, set the configuration to contain the forkId for this new + * context. + */ + public static MojoBinding createClearForkedExecutionMojoBinding() + { + MojoBinding binding = new MojoBinding(); + + binding.setGroupId( GROUP_ID ); + binding.setArtifactId( ARTIFACT_ID ); + binding.setVersion( VERSION ); + binding.setGoal( CLEAR_FORKED_EXECUTION_GOAL ); + binding.setOrigin( ORIGIN ); + + Xpp3Dom config = new Xpp3Dom( "configuration" ); + Xpp3Dom forkId = new Xpp3Dom( "forkId" ); + forkId.setValue( "" + CURRENT_FORK_ID ); + + config.addChild( forkId ); + + binding.setConfiguration( config ); + + return binding; + } + + /** + * Return true if the specified MojoBinding refers to the internal mojo used to setup a new + * forked-execution context. This is useful for formatting when listing the build plan, when + * expression of these actual mojo names isn't necessarily useful, and can be confusing. + */ + public static boolean isForkedExecutionStartMarker( MojoBinding binding ) + { + return GROUP_ID.equals( binding.getGroupId() ) && ARTIFACT_ID.equals( binding.getArtifactId() ) + && START_FORKED_EXECUTION_GOAL.equals( binding.getGoal() ); + } + + /** + * Return true if the specified MojoBinding refers to the internal mojo used to end a + * forked-execution context. This is useful for formatting when listing the build plan, when + * expression of these actual mojo names isn't necessarily useful, and can be confusing. + */ + public static boolean isForkedExecutionEndMarker( MojoBinding binding ) + { + return GROUP_ID.equals( binding.getGroupId() ) && ARTIFACT_ID.equals( binding.getArtifactId() ) + && END_FORKED_EXECUTION_GOAL.equals( binding.getGoal() ); + } + + /** + * Return true if the specified MojoBinding refers to the internal mojo used to clean up a completed + * forked-execution context. This is useful for formatting when listing the build plan, when + * expression of these actual mojo names isn't necessarily useful, and can be confusing. + */ + public static boolean isForkedExecutionClearMarker( MojoBinding binding ) + { + return GROUP_ID.equals( binding.getGroupId() ) && ARTIFACT_ID.equals( binding.getArtifactId() ) + && CLEAR_FORKED_EXECUTION_GOAL.equals( binding.getGoal() ); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginManager.java b/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginManager.java index c4d623add3..3df8e5a707 100644 --- a/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginManager.java +++ b/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginManager.java @@ -36,8 +36,11 @@ import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.context.BuildContextManager; import org.apache.maven.execution.MavenSession; import org.apache.maven.execution.RuntimeInformation; +import org.apache.maven.lifecycle.LifecycleExecutionContext; +import org.apache.maven.lifecycle.statemgmt.StateManagementUtils; import org.apache.maven.model.Plugin; import org.apache.maven.model.ReportPlugin; import org.apache.maven.monitor.event.EventDispatcher; @@ -92,6 +95,17 @@ public class DefaultPluginManager extends AbstractLogEnabled implements PluginManager, Contextualizable { + private static final List RESERVED_GROUP_IDS; + + static + { + List rgids = new ArrayList(); + + rgids.add( StateManagementUtils.GROUP_ID ); + + RESERVED_GROUP_IDS = rgids; + } + protected PlexusContainer container; protected PluginDescriptorBuilder pluginDescriptorBuilder; @@ -119,6 +133,8 @@ public class DefaultPluginManager protected PluginMappingManager pluginMappingManager; + private BuildContextManager buildContextManager; + // END component requirements public DefaultPluginManager() @@ -157,6 +173,7 @@ public PluginDescriptor verifyPlugin( Plugin plugin, // All version-resolution logic has been moved to DefaultPluginVersionManager. if ( plugin.getVersion() == null ) { + getLogger().debug( "Resolving version for plugin: " + plugin.getKey() ); String version = pluginVersionManager.resolvePluginVersion( plugin.getGroupId(), plugin.getArtifactId(), project, session ); plugin.setVersion( version ); @@ -189,27 +206,38 @@ private PluginDescriptor verifyVersionedPlugin( Plugin plugin, MavenProject proj // the 'Can't find plexus container for plugin: xxx' error. try { - VersionRange versionRange = VersionRange.createFromVersionSpec( plugin.getVersion() ); + // if the groupId is internal, don't try to resolve it... + if ( !RESERVED_GROUP_IDS.contains( plugin.getGroupId() ) ) + { + VersionRange versionRange = VersionRange.createFromVersionSpec( plugin.getVersion() ); - List remoteRepositories = new ArrayList(); + List remoteRepositories = new ArrayList(); - remoteRepositories.addAll( project.getPluginArtifactRepositories() ); + remoteRepositories.addAll( project.getPluginArtifactRepositories() ); - remoteRepositories.addAll( project.getRemoteArtifactRepositories() ); + remoteRepositories.addAll( project.getRemoteArtifactRepositories() ); - checkRequiredMavenVersion( plugin, localRepository, remoteRepositories ); + checkRequiredMavenVersion( plugin, localRepository, remoteRepositories ); - Artifact pluginArtifact = - artifactFactory.createPluginArtifact( plugin.getGroupId(), plugin.getArtifactId(), versionRange ); + Artifact pluginArtifact = + artifactFactory.createPluginArtifact( plugin.getGroupId(), plugin.getArtifactId(), versionRange ); - pluginArtifact = project.replaceWithActiveArtifact( pluginArtifact ); + pluginArtifact = project.replaceWithActiveArtifact( pluginArtifact ); - artifactResolver.resolve( pluginArtifact, project.getPluginArtifactRepositories(), localRepository ); + artifactResolver.resolve( pluginArtifact, project.getPluginArtifactRepositories(), localRepository ); -// if ( !pluginCollector.isPluginInstalled( plugin ) ) -// { -// } - addPlugin( plugin, pluginArtifact, project, localRepository ); +// if ( !pluginCollector.isPluginInstalled( plugin ) ) +// { +// } + addPlugin( plugin, pluginArtifact, project, localRepository ); + } + else + { + getLogger().debug( "Skipping resolution for Maven built-in plugin: " + plugin.getKey() ); + + PluginDescriptor pd = pluginCollector.getPluginDescriptor( plugin ); + pd.setClassRealm( container.getContainerRealm() ); + } project.addPlugin( plugin ); } @@ -236,7 +264,9 @@ else if ( groupId.equals( e.getGroupId() ) && artifactId.equals( e.getArtifactId } } - return pluginCollector.getPluginDescriptor( plugin ); + PluginDescriptor pluginDescriptor = pluginCollector.getPluginDescriptor( plugin ); + + return pluginDescriptor; } /** @@ -548,25 +578,13 @@ public void executeMojo( MavenProject project, PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor(); - String goalId = mojoDescriptor.getGoal(); - - String groupId = pluginDescriptor.getGroupId(); - - String artifactId = pluginDescriptor.getArtifactId(); - - String executionId = mojoExecution.getExecutionId(); - - Xpp3Dom dom = project.getGoalConfiguration( groupId, artifactId, executionId, goalId ); - - Xpp3Dom reportDom = project.getReportConfiguration( groupId, artifactId, executionId ); - - dom = Xpp3Dom.mergeXpp3Dom( dom, reportDom ); - - if ( mojoExecution.getConfiguration() != null ) + Xpp3Dom dom = (Xpp3Dom) mojoExecution.getConfiguration(); + if ( dom != null ) { - dom = Xpp3Dom.mergeXpp3Dom( dom, mojoExecution.getConfiguration() ); + // make a defensive copy, to keep things from getting polluted. + dom = new Xpp3Dom( dom ); } - + plugin = getConfiguredMojo( session, dom, project, false, mojoExecution ); // Event monitoring. @@ -596,7 +614,21 @@ public void executeMojo( MavenProject project, ClassRealm oldRealm = container.setLookupRealm( pluginRealm ); plugin.execute(); - + + // NEW: If the mojo that just executed is a report, store it in the LifecycleExecutionContext + // for reference by future mojos. + if ( plugin instanceof MavenReport ) + { + LifecycleExecutionContext ctx = LifecycleExecutionContext.read( buildContextManager ); + if ( ctx == null ) + { + ctx = new LifecycleExecutionContext( project ); + } + + ctx.addReport( mojoDescriptor, (MavenReport) plugin ); + ctx.store( buildContextManager ); + } + container.setLookupRealm( oldRealm ); dispatcher.dispatchEnd( event, goalExecId ); @@ -757,7 +789,7 @@ private Mojo getConfiguredMojo( MavenSession session, { pomConfiguration = new XmlPlexusConfiguration( dom ); } - + // Validate against non-editable (@readonly) parameters, to make sure users aren't trying to // override in the POM. validatePomConfiguration( mojoDescriptor, pomConfiguration ); @@ -768,9 +800,16 @@ private Mojo getConfiguredMojo( MavenSession session, // PlexusConfiguration mergedConfiguration = mergeConfiguration( pomConfiguration, // mojoDescriptor.getConfiguration() ); - ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator( session, mojoExecution, - pathTranslator, getLogger(), - project, + // NEW: Pass in the LifecycleExecutionContext so we have access to the current project, + // forked project stack (future), and reports. + LifecycleExecutionContext ctx = LifecycleExecutionContext.read( buildContextManager ); + if ( ctx == null ) + { + ctx = new LifecycleExecutionContext( project ); + } + + ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator( session, mojoExecution, pathTranslator, + ctx, getLogger(), session.getExecutionProperties() ); PlexusConfiguration extractedMojoConfiguration = @@ -1181,7 +1220,7 @@ public static String createPluginParameterRequiredMessage( MojoDescriptor mojo, } // ---------------------------------------------------------------------- - // Lifecycle + // LegacyLifecycle // ---------------------------------------------------------------------- public void contextualize( Context context ) diff --git a/maven-core/src/main/java/org/apache/maven/plugin/MojoExecution.java b/maven-core/src/main/java/org/apache/maven/plugin/MojoExecution.java index b201f43cf5..907e60b82d 100644 --- a/maven-core/src/main/java/org/apache/maven/plugin/MojoExecution.java +++ b/maven-core/src/main/java/org/apache/maven/plugin/MojoExecution.java @@ -39,10 +39,6 @@ public class MojoExecution private Xpp3Dom configuration; - private List forkedExecutions = new ArrayList(); - - private List reports; - public MojoExecution( MojoDescriptor mojoDescriptor ) { this.mojoDescriptor = mojoDescriptor; @@ -79,26 +75,6 @@ public Xpp3Dom getConfiguration() return configuration; } - public void addMojoExecution( MojoExecution execution ) - { - forkedExecutions.add( execution ); - } - - public void setReports( List reports ) - { - this.reports = reports; - } - - public List getReports() - { - return reports; - } - - public List getForkedExecutions() - { - return forkedExecutions; - } - public void setConfiguration( Xpp3Dom configuration ) { this.configuration = configuration; diff --git a/maven-core/src/main/java/org/apache/maven/plugin/PluginParameterExpressionEvaluator.java b/maven-core/src/main/java/org/apache/maven/plugin/PluginParameterExpressionEvaluator.java index 78ef5e3dec..211710aaad 100644 --- a/maven-core/src/main/java/org/apache/maven/plugin/PluginParameterExpressionEvaluator.java +++ b/maven-core/src/main/java/org/apache/maven/plugin/PluginParameterExpressionEvaluator.java @@ -20,6 +20,7 @@ */ import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.LifecycleExecutionContext; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.project.MavenProject; @@ -74,6 +75,49 @@ public class PluginParameterExpressionEvaluator private final Properties properties; + private final LifecycleExecutionContext lifecycleExecutionContext; + + public PluginParameterExpressionEvaluator( MavenSession context, + MojoExecution mojoExecution, + PathTranslator pathTranslator, + LifecycleExecutionContext lifecycleExecutionContext, + Logger logger, + Properties properties ) + { + this.context = context; + this.mojoExecution = mojoExecution; + this.pathTranslator = pathTranslator; + this.lifecycleExecutionContext = lifecycleExecutionContext; + this.logger = logger; + this.properties = properties; + + this.project = lifecycleExecutionContext.getCurrentProject(); + + String basedir = null; + + if ( project != null ) + { + File projectFile = project.getFile(); + + // this should always be the case for non-super POM instances... + if ( projectFile != null ) + { + basedir = projectFile.getParentFile().getAbsolutePath(); + } + } + + if ( basedir == null ) + { + basedir = System.getProperty( "user.dir" ); + } + + this.basedir = basedir; + } + + /** + * @deprecated Use {@link PluginParameterExpressionEvaluator#PluginParameterExpressionEvaluator(MavenSession, MojoExecution, PathTranslator, LifecycleExecutionContext, Logger, Properties)} + * instead. + */ public PluginParameterExpressionEvaluator( MavenSession context, MojoExecution mojoExecution, PathTranslator pathTranslator, @@ -84,9 +128,11 @@ public PluginParameterExpressionEvaluator( MavenSession context, this.context = context; this.mojoExecution = mojoExecution; this.pathTranslator = pathTranslator; + this.lifecycleExecutionContext = new LifecycleExecutionContext( project ); this.logger = logger; - this.project = project; this.properties = properties; + + this.project = project; String basedir = null; @@ -192,7 +238,7 @@ else if ( "reactorProjects".equals( expression ) ) } else if ( "reports".equals( expression ) ) { - value = mojoExecution.getReports(); + value = lifecycleExecutionContext.getReports(); } else if ( "project".equals( expression ) ) { diff --git a/maven-core/src/main/java/org/apache/maven/plugin/loader/DefaultPluginLoader.java b/maven-core/src/main/java/org/apache/maven/plugin/loader/DefaultPluginLoader.java new file mode 100644 index 0000000000..13a1fbaccb --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/plugin/loader/DefaultPluginLoader.java @@ -0,0 +1,349 @@ +package org.apache.maven.plugin.loader; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.context.BuildContextManager; +import org.apache.maven.execution.SessionContext; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.ReportPlugin; +import org.apache.maven.plugin.InvalidPluginException; +import org.apache.maven.plugin.MavenPluginCollector; +import org.apache.maven.plugin.PluginManager; +import org.apache.maven.plugin.PluginManagerException; +import org.apache.maven.plugin.PluginMappingManager; +import org.apache.maven.plugin.PluginNotFoundException; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.version.PluginVersionNotFoundException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.project.MavenProject; +import org.apache.maven.settings.Settings; +import org.codehaus.classworlds.ClassRealm; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.logging.LogEnabled; +import org.codehaus.plexus.logging.Logger; + +import java.util.Iterator; + +/** + * Responsible for loading plugins, reports, and any components contained therein. Will resolve + * plugin versions and plugin prefixes as necessary for plugin resolution. + * + * @author jdcasey + * + */ +public class DefaultPluginLoader + implements PluginLoader, LogEnabled +{ + + private Logger logger; + + // FIXME: Move the functionality used from this into the PluginLoader when PluginManager refactor is complete. + private PluginManager pluginManager; + + private PluginMappingManager pluginMappingManager; + + private BuildContextManager buildContextManager; + + private MavenPluginCollector pluginCollector; + + /** + * Lookup a component with the specified role + roleHint in the plugin's {@link ClassRealm}. + * Load the plugin first. + */ + public Object loadPluginComponent( String role, String roleHint, Plugin plugin, MavenProject project ) + throws ComponentLookupException, PluginLoaderException + { + loadPlugin( plugin, project ); + + try + { + return pluginManager.getPluginComponent( plugin, role, roleHint ); + } + catch ( PluginManagerException e ) + { + Throwable cause = e.getCause(); + + if ( cause != null && ( cause instanceof ComponentLookupException ) ) + { + StringBuffer message = new StringBuffer(); + message.append( "ComponentLookupException in PluginManager while looking up a component in the realm of: " ); + message.append( plugin.getKey() ); + message.append( ".\nReason: " ); + message.append( cause.getMessage() ); + message.append( "\n\nStack-Trace inside of PluginManager was:\n\n" ); + + StackTraceElement[] elements = e.getStackTrace(); + for ( int i = 0; i < elements.length; i++ ) + { + if ( elements[i].getClassName().indexOf( "PluginManager" ) < 0 ) + { + break; + } + + message.append( elements[i] ); + } + + logger.debug( message.toString() + "\n" ); + + throw (ComponentLookupException) cause; + } + else + { + throw new PluginLoaderException( plugin, "Failed to lookup plugin component. Reason: " + e.getMessage(), e ); + } + } + } + + /** + * Load the {@link PluginDescriptor} instance for the plugin implied by the specified MojoBinding, + * using the project for {@link ArtifactRepository} and other supplemental plugin information as + * necessary. + */ + public PluginDescriptor loadPlugin( MojoBinding mojoBinding, MavenProject project ) + throws PluginLoaderException + { + PluginDescriptor pluginDescriptor = null; + + Plugin plugin = new Plugin(); + plugin.setGroupId( mojoBinding.getGroupId() ); + plugin.setArtifactId( mojoBinding.getArtifactId() ); + plugin.setVersion( mojoBinding.getVersion() ); + + pluginDescriptor = loadPlugin( plugin, project ); + + // fill in any blanks once we know more about this plugin. + if ( pluginDescriptor != null ) + { + mojoBinding.setGroupId( pluginDescriptor.getGroupId() ); + mojoBinding.setArtifactId( pluginDescriptor.getArtifactId() ); + mojoBinding.setVersion( pluginDescriptor.getVersion() ); + } + + return pluginDescriptor; + } + + /** + * Determine the appropriate {@link PluginDescriptor} instance for use with the specified plugin + * prefix, using the following strategies (in order): + *
+ *
    + *
  1. Search for a plugin that has already been loaded with the specified prefix
  2. + *
  3. Search for a plugin configured in the POM that has a matching prefix
  4. + *
  5. Search the pluginGroups specified in the settings.xml for a matching plugin
  6. + *
  7. Use groupId == org.apache.maven.plugins, and artifactId == maven-<prefix>-plugin, + * and try to resolve based on that.
  8. + *
+ */ + public PluginDescriptor findPluginForPrefix( String prefix, MavenProject project ) + throws PluginLoaderException + { + PluginDescriptor pluginDescriptor = pluginCollector.getPluginDescriptorForPrefix( prefix ); + + if ( pluginDescriptor == null ) + { + pluginDescriptor = loadFromProject( prefix, project ); + } + + if ( pluginDescriptor == null ) + { + pluginDescriptor = loadByPrefix( prefix, project ); + } + + if ( pluginDescriptor == null ) + { + Plugin plugin = new Plugin(); + plugin.setArtifactId( PluginDescriptor.getDefaultPluginArtifactId( prefix ) ); + + pluginDescriptor = loadPlugin( plugin, project ); + } + + if ( pluginDescriptor == null ) + { + throw new PluginLoaderException( "Cannot find plugin with prefix: " + prefix ); + } + + return pluginDescriptor; + } + + /** + * Look for a plugin configured in the current project that has a prefix matching the one + * specified. Return the {@link PluginDescriptor} if a match is found. + */ + private PluginDescriptor loadFromProject( String prefix, MavenProject project ) + throws PluginLoaderException + { + PluginDescriptor result = null; + + for ( Iterator it = project.getBuildPlugins().iterator(); it.hasNext(); ) + { + Plugin plugin = (Plugin) it.next(); + + PluginDescriptor pluginDescriptor = loadPlugin( plugin, project ); + if ( prefix.equals( pluginDescriptor.getGoalPrefix() ) ) + { + result = pluginDescriptor; + break; + } + } + + return result; + } + + /** + * Look for a plugin in the pluginGroups specified in the settings.xml that has a prefix + * matching the one specified. Return the {@link PluginDescriptor} if a match is found. + */ + private PluginDescriptor loadByPrefix( String prefix, MavenProject project ) + throws PluginLoaderException + { + SessionContext ctx = SessionContext.read( buildContextManager ); + Settings settings = ctx.getSettings(); + + Plugin plugin = pluginMappingManager.getByPrefix( prefix, settings.getPluginGroups(), + project.getPluginArtifactRepositories(), ctx.getLocalRepository() ); + + PluginDescriptor pluginDescriptor = null; + if ( plugin != null ) + { + pluginDescriptor = loadPlugin( plugin, project ); + } + + return pluginDescriptor; + } + + /** + * Load the {@link PluginDescriptor} instance for the specified plugin, using the project for + * the {@link ArtifactRepository} and other supplemental plugin information as necessary. + */ + public PluginDescriptor loadPlugin( Plugin plugin, MavenProject project ) + throws PluginLoaderException + { + if ( plugin.getGroupId() == null ) + { + plugin.setGroupId( PluginDescriptor.getDefaultPluginGroupId() ); + } + + SessionContext ctx = SessionContext.read( buildContextManager ); + + try + { + PluginDescriptor result = pluginManager.verifyPlugin( plugin, project, ctx.getSession() ); + + // this has been simplified from the old code that injected the plugin management stuff, since + // pluginManagement injection is now handled by the project method. + project.addPlugin( plugin ); + + return result; + } + catch ( ArtifactResolutionException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + catch ( PluginNotFoundException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + catch ( PluginVersionResolutionException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + catch ( InvalidPluginException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + catch ( PluginManagerException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + catch ( PluginVersionNotFoundException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + } + + public void enableLogging( Logger logger ) + { + this.logger = logger; + } + + /** + * Load the {@link PluginDescriptor} instance for the report plugin implied by the specified MojoBinding, + * using the project for {@link ArtifactRepository} and other supplemental report/plugin information as + * necessary. + */ + public PluginDescriptor loadReportPlugin( MojoBinding mojoBinding, MavenProject project ) + throws PluginLoaderException + { + ReportPlugin plugin = new ReportPlugin(); + plugin.setGroupId( mojoBinding.getGroupId() ); + plugin.setArtifactId( mojoBinding.getArtifactId() ); + plugin.setVersion( mojoBinding.getVersion() ); + + PluginDescriptor pluginDescriptor = loadReportPlugin( plugin, project ); + + mojoBinding.setVersion( pluginDescriptor.getVersion() ); + + return pluginDescriptor; + } + + /** + * Load the {@link PluginDescriptor} instance for the specified report plugin, using the project for + * the {@link ArtifactRepository} and other supplemental report/plugin information as necessary. + */ + public PluginDescriptor loadReportPlugin( ReportPlugin plugin, MavenProject project ) + throws PluginLoaderException + { + // TODO: Shouldn't we be injecting pluginManagement info here?? + + SessionContext ctx = SessionContext.read( buildContextManager ); + + try + { + return pluginManager.verifyReportPlugin( plugin, project, ctx.getSession() ); + } + catch ( ArtifactResolutionException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + catch ( PluginNotFoundException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + catch ( PluginVersionResolutionException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + catch ( InvalidPluginException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + catch ( PluginManagerException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + catch ( PluginVersionNotFoundException e ) + { + throw new PluginLoaderException( plugin, "Failed to load plugin. Reason: " + e.getMessage(), e ); + } + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/plugin/loader/PluginLoader.java b/maven-core/src/main/java/org/apache/maven/plugin/loader/PluginLoader.java new file mode 100644 index 0000000000..a59cd8527e --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/plugin/loader/PluginLoader.java @@ -0,0 +1,74 @@ +package org.apache.maven.plugin.loader; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.ReportPlugin; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.project.MavenProject; +import org.codehaus.classworlds.ClassRealm; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; + +/** + * Responsible for loading plugins, reports, and any components contained therein. Will resolve + * plugin versions and plugin prefixes as necessary for plugin resolution. + * + * @author jdcasey + * + */ +public interface PluginLoader +{ + + /** + * Lookup a component with the specified role + roleHint in the plugin's {@link ClassRealm}. + * Load the plugin first. + */ + Object loadPluginComponent( String role, String roleHint, Plugin plugin, MavenProject project ) + throws ComponentLookupException, PluginLoaderException; + + /** + * Load the {@link PluginDescriptor} instance for the specified plugin, using the project for + * the {@link ArtifactRepository} and other supplemental plugin information as necessary. + */ + PluginDescriptor loadPlugin( Plugin plugin, MavenProject project ) + throws PluginLoaderException; + + /** + * Load the {@link PluginDescriptor} instance for the plugin implied by the specified MojoBinding, + * using the project for {@link ArtifactRepository} and other supplemental plugin information as + * necessary. + */ + PluginDescriptor loadPlugin( MojoBinding mojoBinding, MavenProject project ) + throws PluginLoaderException; + + /** + * Load the {@link PluginDescriptor} instance for the specified report plugin, using the project for + * the {@link ArtifactRepository} and other supplemental report/plugin information as necessary. + */ + PluginDescriptor loadReportPlugin( ReportPlugin reportPlugin, MavenProject project ) + throws PluginLoaderException; + + /** + * Load the {@link PluginDescriptor} instance for the report plugin implied by the specified MojoBinding, + * using the project for {@link ArtifactRepository} and other supplemental report/plugin information as + * necessary. + */ + PluginDescriptor loadReportPlugin( MojoBinding mojoBinding, MavenProject project ) + throws PluginLoaderException; + + /** + * Determine the appropriate {@link PluginDescriptor} instance for use with the specified plugin + * prefix, using the following strategies (in order): + *
+ *
    + *
  1. Search for a plugin that has already been loaded with the specified prefix
  2. + *
  3. Search for a plugin configured in the POM that has a matching prefix
  4. + *
  5. Search the pluginGroups specified in the settings.xml for a matching plugin
  6. + *
  7. Use groupId == org.apache.maven.plugins, and artifactId == maven-<prefix>-plugin, + * and try to resolve based on that.
  8. + *
+ */ + PluginDescriptor findPluginForPrefix( String prefix, MavenProject project ) + throws PluginLoaderException; + +} diff --git a/maven-core/src/main/java/org/apache/maven/plugin/loader/PluginLoaderException.java b/maven-core/src/main/java/org/apache/maven/plugin/loader/PluginLoaderException.java new file mode 100644 index 0000000000..be2d088c54 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/plugin/loader/PluginLoaderException.java @@ -0,0 +1,58 @@ +package org.apache.maven.plugin.loader; + +import org.apache.maven.model.Plugin; +import org.apache.maven.model.ReportPlugin; + +/** + * Signifies a failure to load a plugin. This is used to abstract the specific errors which may be + * encountered at lower levels, and provide a dependable interface to the plugin-loading framework. + * + * @author jdcasey + * + */ +public class PluginLoaderException + extends Exception +{ + + private String pluginKey; + + public PluginLoaderException( Plugin plugin, String message, Throwable cause ) + { + super( message, cause ); + this.pluginKey = plugin.getKey(); + } + + public PluginLoaderException( Plugin plugin, String message ) + { + super( message ); + this.pluginKey = plugin.getKey(); + } + + public PluginLoaderException( String message ) + { + super( message ); + } + + public PluginLoaderException( String message, Throwable cause ) + { + super( message, cause ); + } + + public PluginLoaderException( ReportPlugin plugin, String message, Throwable cause ) + { + super( message, cause ); + this.pluginKey = plugin.getKey(); + } + + public PluginLoaderException( ReportPlugin plugin, String message ) + { + super( message ); + this.pluginKey = plugin.getKey(); + } + + public String getPluginKey() + { + return pluginKey; + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/plugin/version/DefaultPluginVersionManager.java b/maven-core/src/main/java/org/apache/maven/plugin/version/DefaultPluginVersionManager.java index 556ee31a3d..6822241734 100644 --- a/maven-core/src/main/java/org/apache/maven/plugin/version/DefaultPluginVersionManager.java +++ b/maven-core/src/main/java/org/apache/maven/plugin/version/DefaultPluginVersionManager.java @@ -90,6 +90,7 @@ private String resolvePluginVersion( String groupId, // first pass...if the plugin is specified in the pom, try to retrieve the version from there. String version = getVersionFromPluginConfig( groupId, artifactId, project, resolveAsReportPlugin ); + getLogger().debug( "Version from POM: " + version ); // NOTE: We CANNOT check the current project version here, so delay it until later. // It will prevent plugins from building themselves, if they are part of the lifecycle mapping. @@ -107,6 +108,7 @@ private String resolvePluginVersion( String groupId, } } } + getLogger().debug( "Version from another POM in the reactor: " + version ); // third pass...we're always checking for latest install/deploy, so retrieve the version for LATEST metadata and // also set that resolved version as the in settings.xml. @@ -114,6 +116,7 @@ private String resolvePluginVersion( String groupId, { // 1. resolve the version to be used version = resolveMetaVersion( groupId, artifactId, project, localRepository, Artifact.LATEST_VERSION ); + getLogger().debug( "Version from LATEST metadata: " + version ); } // final pass...retrieve the version for RELEASE and also set that resolved version as the @@ -122,6 +125,7 @@ private String resolvePluginVersion( String groupId, { // 1. resolve the version to be used version = resolveMetaVersion( groupId, artifactId, project, localRepository, Artifact.RELEASE_VERSION ); + getLogger().debug( "Version from RELEASE metadata: " + version ); } // if we're still empty here, and the current project matches the plugin in question, use the current project's @@ -130,6 +134,7 @@ private String resolvePluginVersion( String groupId, project.getArtifactId().equals( artifactId ) ) { version = project.getVersion(); + getLogger().debug( "Version from POM itself (this project IS the plugin project): " + version ); } // if we still haven't found a version, then fail early before we get into the update goop. diff --git a/maven-core/src/main/resources/META-INF/maven/plugin.xml b/maven-core/src/main/resources/META-INF/maven/plugin.xml new file mode 100644 index 0000000000..e3b26656e2 --- /dev/null +++ b/maven-core/src/main/resources/META-INF/maven/plugin.xml @@ -0,0 +1,117 @@ + + Maven Internal State-Management Plugins + org.apache.maven.plugins.internal + maven-state-management + 2.1 + statemgmt + false + true + + + start-fork + Setup the appropriate build state to initiate a forked execution. + false + true + false + false + false + true + org.apache.maven.lifecycle.statemgmt.StartForkedExecutionMojo + java + per-lookup + once-per-session + + + project + org.apache.maven.project.MavenProject + true + false + The current MavenProject instance, which will have a new executionProject set after execution. + + + forkId + int + true + true + The current fork identifier. + + + + + ${forkId} + + + + org.apache.maven.context.BuildContextManager + buildContextManager + + + + + end-fork + Restore the non-fork currentProject instance, for use in the forking mojo. + false + true + false + false + false + true + org.apache.maven.lifecycle.statemgmt.EndForkedExecutionMojo + java + per-lookup + once-per-session + + + forkId + int + true + true + The current fork identifier. + + + + ${forkId} + + + + org.apache.maven.context.BuildContextManager + buildContextManager + + + + + clear-fork-context + Tear down any build state used during the previous forked execution. + false + true + false + false + false + true + org.apache.maven.lifecycle.statemgmt.ClearForkedContextMojo + java + per-lookup + once-per-session + + + project + org.apache.maven.project.MavenProject + true + false + The current MavenProject instance, which will have the current executionProject cleared after execution. + + + forkId + int + true + true + The current fork identifier. + + + + + ${forkId} + + + + \ No newline at end of file diff --git a/maven-core/src/main/resources/META-INF/plexus/components.xml b/maven-core/src/main/resources/META-INF/plexus/components.xml index 5e69612549..c6f5bb3dcc 100644 --- a/maven-core/src/main/resources/META-INF/plexus/components.xml +++ b/maven-core/src/main/resources/META-INF/plexus/components.xml @@ -52,6 +52,9 @@ under the License. org.apache.maven.plugin.PluginManager org.apache.maven.plugin.DefaultPluginManager + + org.apache.maven.context.BuildContextManager + org.apache.maven.ArtifactFilterManager @@ -260,102 +263,28 @@ under the License. org.apache.maven.lifecycle.LifecycleExecutor org.apache.maven.lifecycle.DefaultLifecycleExecutor + + org.apache.maven.context.BuildContextManager + + + org.apache.maven.lifecycle.binding.LifecycleBindingManager + + + org.apache.maven.plugin.loader.PluginLoader + org.apache.maven.plugin.PluginManager org.apache.maven.artifact.handler.manager.ArtifactHandlerManager + + org.apache.maven.lifecycle.plan.BuildPlanner + + + org.apache.maven.lifecycle.binding.MojoBindingFactory + - - - - default - - - validate - initialize - generate-sources - process-sources - generate-resources - process-resources - compile - process-classes - generate-test-sources - process-test-sources - generate-test-resources - process-test-resources - test-compile - process-test-classes - test - prepare-package - package - pre-integration-test - integration-test - post-integration-test - verify - install - deploy - - - - - clean - - pre-clean - clean - post-clean - - - org.apache.maven.plugins:maven-clean-plugin:clean - - - - site - - pre-site - site - post-site - site-deploy - - - org.apache.maven.plugins:maven-site-plugin:site - org.apache.maven.plugins:maven-site-plugin:deploy - - - - - - org.apache.maven.plugins:maven-project-info-reports-plugin - - - - - - - + + validate + initialize + generate-sources + process-sources + generate-resources + process-resources + compile + process-classes + generate-test-sources + process-test-sources + generate-test-resources + process-test-resources + test-compile + process-test-classes + test + prepare-package + package + pre-integration-test + integration-test + post-integration-test + verify + install + deploy + + + + + clean + + pre-clean + clean + post-clean + + + org.apache.maven.plugins:maven-clean-plugin:clean + + + + site + + pre-site + site + post-site + site-deploy + + + org.apache.maven.plugins:maven-site-plugin:site + org.apache.maven.plugins:maven-site-plugin:deploy + + + + + + org.apache.maven.plugins:maven-project-info-reports-plugin + + + + + + + + org.apache.maven.lifecycle.plan.BuildPlanner + default + org.apache.maven.lifecycle.plan.DefaultBuildPlanner + + + org.apache.maven.plugin.loader.PluginLoader + + + org.apache.maven.lifecycle.binding.LifecycleBindingManager + + + org.apache.maven.lifecycle.binding.MojoBindingFactory + + + + + + org.apache.maven.lifecycle.binding.MojoBindingFactory + default + org.apache.maven.lifecycle.binding.DefaultMojoBindingFactory + + + org.apache.maven.plugin.loader.PluginLoader + + + + + + org.apache.maven.lifecycle.binding.LegacyLifecycleMappingParser + default + org.apache.maven.lifecycle.binding.LegacyLifecycleMappingParser + + + org.apache.maven.lifecycle.binding.MojoBindingFactory + + + + diff --git a/maven-core/src/site/resources/design/2.1-lifecycle-refactor-class-diagram.png b/maven-core/src/site/resources/design/2.1-lifecycle-refactor-class-diagram.png new file mode 100755 index 0000000000..37d666c352 Binary files /dev/null and b/maven-core/src/site/resources/design/2.1-lifecycle-refactor-class-diagram.png differ diff --git a/maven-core/src/site/resources/design/2.1-lifecycle-refactor-sequence-diagram.png b/maven-core/src/site/resources/design/2.1-lifecycle-refactor-sequence-diagram.png new file mode 100755 index 0000000000..30f19553f1 Binary files /dev/null and b/maven-core/src/site/resources/design/2.1-lifecycle-refactor-sequence-diagram.png differ diff --git a/maven-core/src/site/resources/design/2.1-lifecycle-refactor.graffle b/maven-core/src/site/resources/design/2.1-lifecycle-refactor.graffle new file mode 100755 index 0000000000..722bc031f8 --- /dev/null +++ b/maven-core/src/site/resources/design/2.1-lifecycle-refactor.graffle @@ -0,0 +1,4024 @@ + + + + + CreationDate + 2007-03-15 15:23:17 -0400 + Creator + John.alt + GraphDocumentVersion + 5 + GuidesLocked + NO + GuidesVisible + YES + ImageCounter + 1 + LinksVisible + NO + MagnetsVisible + NO + MasterSheets + + + ActiveLayerIndex + 0 + AutoAdjust + + CanvasColor + + w + 1 + + CanvasOrigin + {0, 0} + CanvasScale + 1 + ColumnAlign + 1 + ColumnSpacing + 36 + DisplayScale + 1 in = 1 in + GraphicsList + + GridInfo + + HPages + 1 + IsPalette + NO + KeepToScale + + Layers + + + Lock + NO + Name + Layer 1 + Print + YES + View + YES + + + LayoutInfo + + Orientation + 2 + OutlineStyle + Basic + RowAlign + 1 + RowSpacing + 36 + SheetTitle + Master 1 + UniqueID + 1 + VPages + 1 + + + ModificationDate + 2007-03-15 16:57:24 -0400 + Modifier + John.alt + NotesVisible + NO + OriginVisible + NO + PageBreaks + YES + PrintInfo + + NSBottomMargin + + float + 0 + + NSLeftMargin + + float + 0 + + NSOrientation + + int + 1 + + NSPaperSize + + size + {792, 612} + + NSRightMargin + + float + 0 + + NSTopMargin + + float + 0 + + + ReadOnly + NO + Sheets + + + ActiveLayerIndex + 0 + AutoAdjust + + CanvasColor + + w + 1 + + CanvasOrigin + {0, 0} + CanvasScale + 1 + ColumnAlign + 1 + ColumnSpacing + 36 + DisplayScale + 1 in = 1 in + GraphicsList + + + Bounds + {{607.473, 151.036}, {112, 28}} + Class + ShapedGraphic + FitText + YES + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 86 + Line + + ID + 72 + Position + 0.31921392679214478 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Oblique;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\i\fs24 \cf0 deprecated,\ +marked for refactor} + + + + Bounds + {{584.742, 252.673}, {104, 28}} + Class + ShapedGraphic + FitText + YES + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 85 + Line + + ID + 84 + Position + 0.45520344376564026 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Oblique;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\i\fs24 \cf0 if binding spec \ +uses plugin prefix} + + + + Class + LineGraphic + Head + + ID + 39 + + ID + 84 + Points + + {623.954, 304} + {652.046, 222} + + Style + + stroke + + HeadArrow + FilledArrow + HopLines + + HopType + 1 + Pattern + 1 + TailArrow + 0 + + + Tail + + ID + 51 + + + + Class + LineGraphic + Head + + ID + 51 + + ID + 83 + Points + + {488, 317} + {555, 317} + + Style + + stroke + + HeadArrow + FilledArrow + HopLines + + HopType + 1 + TailArrow + 0 + + + Tail + + ID + 48 + + + + Class + LineGraphic + Head + + ID + 54 + + ID + 82 + Points + + {453.532, 330} + {575.468, 366} + + Style + + stroke + + HeadArrow + FilledArrow + HopLines + + HopType + 1 + TailArrow + 0 + + + Tail + + ID + 48 + + + + Class + LineGraphic + Head + + ID + 42 + + ID + 81 + Points + + {83.5916, 196} + {84, 138} + {256, 138} + {466, 138} + {656, 138} + {671.008, 99} + + Style + + stroke + + HeadArrow + FilledArrow + HopLines + + HopType + 1 + TailArrow + 0 + + + Tail + + ID + 33 + + + + Class + LineGraphic + Head + + ID + 39 + + ID + 80 + Points + + {83.6857, 196} + {84, 174} + {256, 174} + {466, 174} + {598, 198.252} + + Style + + stroke + + HeadArrow + FilledArrow + HopLines + + HopType + 1 + TailArrow + 0 + + + Tail + + ID + 33 + + + + Class + LineGraphic + Head + + ID + 45 + + ID + 66 + Points + + {142, 221.044} + {195, 231.956} + + Style + + stroke + + HeadArrow + FilledArrow + HopLines + + HopType + 1 + TailArrow + 0 + + + Tail + + ID + 33 + + + + Class + LineGraphic + Head + + ID + 48 + + ID + 79 + Points + + {106.976, 222} + {237, 294} + {331, 306.533} + + Style + + stroke + + HeadArrow + FilledArrow + HopLines + + HopType + 1 + TailArrow + 0 + + + Tail + + ID + 33 + + + + Class + LineGraphic + Head + + ID + 39 + + ID + 78 + Points + + {419.845, 304} + {466, 246} + {598, 220.362} + + Style + + stroke + + HeadArrow + FilledArrow + HopLines + + HopType + 1 + TailArrow + 0 + + + Tail + + ID + 48 + + + + Class + LineGraphic + Head + + ID + 39 + + ID + 77 + Points + + {254.456, 231} + {256, 210} + {466, 210} + {598, 209.307} + + Style + + stroke + + HeadArrow + FilledArrow + HopLines + + HopType + 1 + TailArrow + 0 + + + Tail + + ID + 45 + + + + Class + LineGraphic + Head + + ID + 42 + + ID + 72 + Points + + {658.562, 196} + {673.948, 99} + + Style + + stroke + + HeadArrow + FilledArrow + LineType + 1 + Pattern + 1 + TailArrow + 0 + + + Tail + + ID + 39 + + + + Class + LineGraphic + Head + + ID + 48 + + ID + 71 + Points + + {281.281, 257} + {381.719, 304} + + Style + + stroke + + HeadArrow + FilledArrow + LineType + 1 + TailArrow + 0 + + + Tail + + ID + 45 + + + + Class + TableGroup + Graphics + + + Bounds + {{524, 366}, {191, 14}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 55 + Shape + Rectangle + Style + + fill + + GradientAngle + 304 + GradientCenter + {-0.294118, -0.264706} + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 LegacyLifecycleMappingParser} + + TextPlacement + 0 + + + Bounds + {{524, 380}, {191, 12}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 56 + Shape + Rectangle + Style + + fill + + GradientAngle + 304 + GradientCenter + {-0.294118, -0.264706} + + + Text + + Align + 0 + + TextPlacement + 0 + + + GridH + + 55 + 56 + + + GroupConnect + YES + ID + 54 + + + Class + TableGroup + Graphics + + + Bounds + {{555, 304}, {129, 14}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 52 + Shape + Rectangle + Style + + fill + + GradientAngle + 304 + GradientCenter + {-0.294118, -0.264706} + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 MojoBindingFactory} + + TextPlacement + 0 + + + Bounds + {{555, 318}, {129, 12}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 53 + Shape + Rectangle + Style + + fill + + GradientAngle + 304 + GradientCenter + {-0.294118, -0.264706} + + + Text + + Align + 0 + + TextPlacement + 0 + + + GridH + + 52 + 53 + + + GroupConnect + YES + ID + 51 + + + Class + TableGroup + Graphics + + + Bounds + {{331, 304}, {157, 14}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 49 + Shape + Rectangle + Style + + fill + + GradientAngle + 304 + GradientCenter + {-0.294118, -0.264706} + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 LifecycleBindingManager} + + TextPlacement + 0 + + + Bounds + {{331, 318}, {157, 12}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 50 + Shape + Rectangle + Style + + fill + + GradientAngle + 304 + GradientCenter + {-0.294118, -0.264706} + + + Text + + Align + 0 + + TextPlacement + 0 + + + GridH + + 49 + 50 + + + GroupConnect + YES + ID + 48 + + + Class + TableGroup + Graphics + + + Bounds + {{195, 231}, {117, 14}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 46 + Shape + Rectangle + Style + + fill + + GradientAngle + 304 + GradientCenter + {-0.294118, -0.264706} + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 BuildPlanner} + + TextPlacement + 0 + + + Bounds + {{195, 245}, {117, 12}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 47 + Shape + Rectangle + Style + + fill + + GradientAngle + 304 + GradientCenter + {-0.294118, -0.264706} + + + Text + + Align + 0 + + TextPlacement + 0 + + + GridH + + 46 + 47 + + + GroupConnect + YES + ID + 45 + + + Class + TableGroup + Graphics + + + Bounds + {{617.51, 73}, {117, 14}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 43 + Shape + Rectangle + Style + + fill + + GradientAngle + 304 + GradientCenter + {-0.294118, -0.264706} + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 PluginManager} + + TextPlacement + 0 + + + Bounds + {{617.51, 87}, {117, 12}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 44 + Shape + Rectangle + Style + + fill + + GradientAngle + 304 + GradientCenter + {-0.294118, -0.264706} + + + Text + + Align + 0 + + TextPlacement + 0 + + + GridH + + 43 + 44 + + + GroupConnect + YES + ID + 42 + + + Class + TableGroup + Graphics + + + Bounds + {{598, 196}, {117, 14}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 40 + Shape + Rectangle + Style + + fill + + GradientAngle + 304 + GradientCenter + {-0.294118, -0.264706} + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 PluginLoader} + + TextPlacement + 0 + + + Bounds + {{598, 210}, {117, 12}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 41 + Shape + Rectangle + Style + + fill + + GradientAngle + 304 + GradientCenter + {-0.294118, -0.264706} + + + Text + + Align + 0 + + TextPlacement + 0 + + + GridH + + 40 + 41 + + + GroupConnect + YES + ID + 39 + + + Class + TableGroup + Graphics + + + Bounds + {{25, 196}, {117, 14}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 34 + Shape + Rectangle + Style + + fill + + GradientAngle + 304 + GradientCenter + {-0.294118, -0.264706} + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 LifecycleExecutor} + + TextPlacement + 0 + + + Bounds + {{25, 210}, {117, 12}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 38 + Shape + Rectangle + Style + + fill + + GradientAngle + 304 + GradientCenter + {-0.294118, -0.264706} + + + Text + + Align + 0 + + TextPlacement + 0 + + + GridH + + 34 + 38 + + + GroupConnect + YES + ID + 33 + + + GridInfo + + HPages + 1 + IsPalette + NO + KeepToScale + + Layers + + + Lock + NO + Name + Layer 1 + Print + YES + View + YES + + + LayoutInfo + + ChildOrdering + 0 + HierarchicalOrientation + 0 + + MasterSheet + Master 1 + Orientation + 2 + OutlineStyle + Basic + RowAlign + 1 + RowSpacing + 36 + SheetTitle + Canvas 1 + UniqueID + 1 + VPages + 1 + + + ActiveLayerIndex + 0 + AutoAdjust + + CanvasColor + + w + 1 + + CanvasOrigin + {0, 0} + CanvasScale + 1 + ColumnAlign + 1 + ColumnSpacing + 36 + DisplayScale + 1 in = 1 in + GraphicsList + + + Bounds + {{521.5, 182.853}, {75, 14}} + Class + ShapedGraphic + FitText + YES + Flow + Resize + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 126 + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 loadPlugin()} + + Wrap + NO + + + Bounds + {{437, 221.123}, {92, 14}} + Class + ShapedGraphic + FitText + YES + ID + 125 + Line + + ID + 124 + Offset + 18.181819915771484 + Position + 0.55852556228637695 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Align + 0 + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\fs24 \cf0 binding, project} + + + + AllowLabelDrop + + Class + LineGraphic + Head + + ID + 123 + + ID + 124 + Points + + {398, 209.12} + {550.5, 210.592} + + Style + + stroke + + HeadArrow + FilledArrow + HeadScale + 1.4285709857940674 + TailArrow + StickArrow + TailScale + 1.5 + + + Tail + + ID + 79 + + + + Bounds + {{550.5, 203.842}, {17, 27}} + Class + ShapedGraphic + HFlip + YES + ID + 123 + Magnets + + {1, 0.5} + {1, -0.5} + {-1, 0.5} + {-1, -0.5} + {0.5, 1} + {-0.5, 1} + {0.5, -1} + {-0.5, -1} + + Shape + Rectangle + Text + + Align + 0 + + + + Bounds + {{521.5, 114.705}, {75, 14}} + Class + ShapedGraphic + FitText + YES + Flow + Resize + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 122 + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 loadPlugin()} + + Wrap + NO + + + Bounds + {{436.814, 155.115}, {92, 14}} + Class + ShapedGraphic + FitText + YES + ID + 121 + Line + + ID + 120 + Offset + 18.181819915771484 + Position + 0.55852556228637695 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Align + 0 + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\fs24 \cf0 binding, project} + + + + AllowLabelDrop + + Class + LineGraphic + Head + + ID + 119 + + ID + 120 + Points + + {397.5, 143.273} + {550.5, 144.455} + + Style + + stroke + + HeadArrow + FilledArrow + HeadScale + 1.4285709857940674 + TailArrow + StickArrow + TailScale + 1.5 + + + Tail + + ID + 73 + Info + 4 + + + + Bounds + {{550.5, 137.705}, {17, 27}} + Class + ShapedGraphic + HFlip + YES + ID + 119 + Magnets + + {1, 0.5} + {1, -0.5} + {-1, 0.5} + {-1, -0.5} + {0.5, 1} + {-0.5, 1} + {0.5, -1} + {-0.5, -1} + + Shape + Rectangle + Text + + Align + 0 + + + + Bounds + {{26, 636}, {131, 14}} + Class + ShapedGraphic + FitText + YES + Flow + Resize + ID + 118 + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Align + 0 + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\fs24 \cf0 foreach binding in plan} + + Wrap + NO + + + AllowLabelDrop + + Class + LineGraphic + Head + + ID + 4 + Info + 5 + + ID + 117 + OrthogonalBarAutomatic + + OrthogonalBarPosition + 19.371429443359375 + Points + + {75.5, 478} + {26, 482} + {26, 575} + {79.125, 616} + + Style + + stroke + + HeadArrow + 0 + HeadScale + 0.86000001430511475 + LineType + 2 + TailArrow + FilledArrow + TailScale + 0.85714292526245117 + + + Tail + + ID + 4 + Info + 1 + + + + Bounds + {{310.653, 581.444}, {179, 14}} + Class + ShapedGraphic + FitText + YES + ID + 116 + Line + + ID + 115 + Offset + 18.181819915771484 + Position + 0.52437615394592285 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Align + 0 + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\fs24 \cf0 mojoExecution, project, session} + + + + AllowLabelDrop + + Class + LineGraphic + Head + + ID + 113 + Info + 2 + + ID + 115 + Points + + {90, 570} + {681.5, 570.5} + + Style + + stroke + + HeadArrow + FilledArrow + HeadScale + 1.4285709857940674 + TailArrow + StickArrow + TailScale + 1.5 + + + Tail + + ID + 4 + Info + 12 + + + + Bounds + {{646.5, 539}, {87, 14}} + Class + ShapedGraphic + FitText + YES + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 114 + Line + + ID + 111 + Position + 0.8310316801071167 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 executeMojo()} + + + + Bounds + {{681.5, 559}, {17, 46}} + Class + ShapedGraphic + HFlip + YES + ID + 113 + Magnets + + {1, 0.5} + {1, -0.5} + {-1, 0.5} + {-1, -0.5} + {0.5, 1} + {-0.5, 1} + {0.5, -1} + {-0.5, -1} + + Shape + Rectangle + Text + + Align + 0 + + + + Class + Group + Graphics + + + AllowLabelDrop + + Class + LineGraphic + ID + 111 + Points + + {690, 34.5} + {690, 650} + + Style + + stroke + + HeadArrow + 0 + HeadScale + 1.4285709857940674 + Pattern + 1 + TailArrow + 0 + TailScale + 0.5 + + + Tail + + ID + 112 + + + + Bounds + {{636, 20}, {108, 14}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 112 + Shape + Rectangle + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 \ul \ulc0 PluginManager} + + + + ID + 110 + + + Bounds + {{287.086, 525.522}, {92, 14}} + Class + ShapedGraphic + FitText + YES + ID + 109 + Line + + ID + 108 + Offset + 18.181819915771484 + Position + 0.52776741981506348 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Align + 0 + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\fs24 \cf0 binding, project} + + + + AllowLabelDrop + + Class + LineGraphic + Head + + ID + 106 + Info + 2 + + ID + 108 + Points + + {90, 515} + {550.5, 513.75} + + Style + + stroke + + HeadArrow + FilledArrow + HeadScale + 1.4285709857940674 + TailArrow + StickArrow + TailScale + 1.5 + + + Tail + + ID + 4 + Info + 3 + + + + Bounds + {{521.5, 485}, {75, 14}} + Class + ShapedGraphic + FitText + YES + Flow + Resize + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 107 + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 loadPlugin()} + + Wrap + NO + + + Bounds + {{550.5, 507}, {17, 27}} + Class + ShapedGraphic + HFlip + YES + ID + 106 + Magnets + + {1, 0.5} + {0.5, -0.25} + {-1, 0.5} + {-1, -0.5} + {0.5, 1} + {-0.5, 1} + {0.5, -1} + {-0.5, -1} + + Shape + Rectangle + Text + + Align + 0 + + + + Bounds + {{130.5, 275}, {33, 14}} + Class + ShapedGraphic + FitText + YES + Flow + Resize + ID + 105 + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Align + 0 + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\fs24 \cf0 plan} + + Wrap + NO + + + Class + LineGraphic + Head + + ID + 4 + + ID + 66 + Points + + {205.5, 270} + {86.375, 270} + + Style + + stroke + + HeadArrow + FilledArrow + HopLines + + HopType + 1 + TailArrow + 0 + + + Tail + + ID + 19 + Info + 16 + + + + Bounds + {{521.5, 302}, {75, 14}} + Class + ShapedGraphic + FitText + YES + Flow + Resize + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 104 + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 loadPlugin()} + + Wrap + NO + + + Bounds + {{349.413, 453.786}, {92, 14}} + Class + ShapedGraphic + FitText + YES + ID + 103 + Line + + ID + 102 + Offset + 18.181819915771484 + Position + 0.52776741981506348 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Align + 0 + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\fs24 \cf0 binding, project} + + + + AllowLabelDrop + + Class + LineGraphic + Head + + ID + 101 + + ID + 102 + Points + + {222, 443} + {550.5, 442.25} + + Style + + stroke + + HeadArrow + FilledArrow + HeadScale + 1.4285709857940674 + TailArrow + StickArrow + TailScale + 1.5 + + + Tail + + ID + 86 + + + + Bounds + {{550.5, 422}, {17, 27}} + Class + ShapedGraphic + HFlip + YES + ID + 101 + Magnets + + {1, 0.5} + {0.5, -0.25} + {-1, 0.5} + {-1, -0.5} + {0.5, 1} + {-0.5, 1} + {0.5, -1} + {-0.5, -1} + + Shape + Rectangle + Text + + Align + 0 + + + + Bounds + {{521.5, 396.749}, {75, 14}} + Class + ShapedGraphic + FitText + YES + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 100 + Line + + ID + 93 + Position + 0.59991699457168579 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 loadPlugin()} + + + + Bounds + {{349.564, 356.057}, {92, 14}} + Class + ShapedGraphic + FitText + YES + ID + 99 + Line + + ID + 98 + Offset + 18.181819915771484 + Position + 0.52776741981506348 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Align + 0 + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\fs24 \cf0 binding, project} + + + + AllowLabelDrop + + Class + LineGraphic + Head + + ID + 95 + + ID + 98 + Points + + {222.5, 344.456} + {550.5, 345.25} + + Style + + stroke + + HeadArrow + FilledArrow + HeadScale + 1.4285709857940674 + TailArrow + StickArrow + TailScale + 1.5 + + + Tail + + ID + 83 + Info + 9 + + + + AllowLabelDrop + + Class + LineGraphic + Head + + ID + 86 + Info + 3 + + ID + 97 + OrthogonalBarAutomatic + + OrthogonalBarPosition + -1 + Points + + {222.5, 254.5} + {309.5, 254} + {327, 344} + {222.5, 454.079} + + Style + + stroke + + HeadArrow + 0 + HeadScale + 0.86000001430511475 + LineType + 2 + TailArrow + StickArrow + TailScale + 0.85714292526245117 + + + Tail + + ID + 19 + Info + 15 + + + + AllowLabelDrop + + Class + LineGraphic + Head + + ID + 83 + Info + 3 + + ID + 96 + OrthogonalBarAutomatic + + OrthogonalBarPosition + -1 + Points + + {222, 237} + {295.5, 237} + {295.5, 337} + {222.5, 356.792} + + Style + + stroke + + HeadArrow + 0 + HeadScale + 0.86000001430511475 + LineType + 2 + TailArrow + StickArrow + TailScale + 0.85714292526245117 + + + Tail + + ID + 19 + Info + 14 + + + + Bounds + {{550.5, 325}, {17, 27}} + Class + ShapedGraphic + HFlip + YES + ID + 95 + Magnets + + {1, 0.5} + {1, -0.5} + {-1, 0.5} + {-1, -0.5} + {0.5, 1} + {-0.5, 1} + {0.5, -1} + {-0.5, -1} + + Shape + Rectangle + Text + + Align + 0 + + + + Class + Group + Graphics + + + AllowLabelDrop + + Class + LineGraphic + ID + 93 + Points + + {559, 34.5} + {559, 650} + + Style + + stroke + + HeadArrow + 0 + HeadScale + 1.4285709857940674 + Pattern + 1 + TailArrow + 0 + TailScale + 0.5 + + + Tail + + ID + 94 + + + + Bounds + {{505, 20}, {108, 14}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 94 + Shape + Rectangle + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 \ul \ulc0 PluginLoader} + + + + ID + 92 + + + AllowLabelDrop + + Class + LineGraphic + Head + + ID + 19 + Info + 13 + + ID + 91 + OrthogonalBarAutomatic + + OrthogonalBarPosition + -1 + Points + + {222.5, 426.798} + {321, 420} + {301, 249} + {222.5, 248.833} + + Style + + stroke + + HeadArrow + 0 + HeadScale + 0.86000001430511475 + LineType + 2 + TailArrow + FilledArrow + TailScale + 0.85714292526245117 + + + Tail + + ID + 86 + Info + 4 + + + + AllowLabelDrop + + Class + LineGraphic + Head + + ID + 19 + Info + 12 + + ID + 10 + OrthogonalBarAutomatic + + OrthogonalBarPosition + -1 + Points + + {222.5, 330.264} + {288.5, 330} + {288.5, 230} + {222.5, 230} + + Style + + stroke + + HeadArrow + 0 + HeadScale + 0.86000001430511475 + LineType + 2 + TailArrow + FilledArrow + TailScale + 0.85714292526245117 + + + Tail + + ID + 83 + Info + 4 + + + + Bounds + {{132.5, 396.158}, {163, 14}} + Class + ShapedGraphic + FitText + YES + Flow + Resize + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 87 + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 addReportingPlanModifiers()} + + Wrap + NO + + + Bounds + {{205.5, 413.158}, {17, 54.5618}} + Class + ShapedGraphic + HFlip + YES + ID + 86 + Magnets + + {1, 0.5} + {1, -0.5} + {-1, 0.5} + {-1, -0.5} + {0.5, 1} + {-0.5, 1} + {0.5, -1} + {-0.5, -1} + {-0.529412, 0.0497093} + + Shape + Rectangle + Text + + Align + 0 + + + + Bounds + {{139.5, 297.124}, {149, 14}} + Class + ShapedGraphic + FitText + YES + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 84 + Line + + ID + 17 + Position + 0.43805667757987976 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 addForkedPlanModifiers()} + + + + Bounds + {{205.5, 317}, {17, 53.056}} + Class + ShapedGraphic + HFlip + YES + ID + 83 + Magnets + + {1, 0.5} + {1, -0.5} + {-1, 0.5} + {-1, -0.5} + {0.5, 1} + {-0.5, 1} + {0.5, -1} + {-0.5, -1} + {-0.529412, 0.0185184} + + Shape + Rectangle + Text + + Align + 0 + + + + Bounds + {{305.436, 220.259}, {47, 14}} + Class + ShapedGraphic + FitText + YES + ID + 82 + Line + + ID + 81 + Offset + 18.181819915771484 + Position + 0.67475664615631104 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Align + 0 + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\fs24 \cf0 project} + + + + AllowLabelDrop + + Class + LineGraphic + Head + + ID + 79 + Info + 2 + + ID + 81 + Points + + {222, 209} + {380.5, 209.115} + + Style + + stroke + + HeadArrow + FilledArrow + HeadScale + 1.4285709857940674 + TailArrow + StickArrow + TailScale + 1.5 + + + Tail + + ID + 19 + Info + 3 + + + + Bounds + {{311.5, 120.556}, {155, 14}} + Class + ShapedGraphic + FitText + YES + Flow + Resize + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 80 + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 getBindingsForPackaging()} + + Wrap + NO + + + Bounds + {{380.5, 203.842}, {17, 21.0909}} + Class + ShapedGraphic + HFlip + YES + ID + 79 + Magnets + + {1, 0.5} + {1, -0.5} + {-1, 0.5} + {-1, -0.5} + {0.5, 1} + {-0.5, 1} + {0.5, -1} + {-0.5, -1} + + Shape + Rectangle + Text + + Align + 0 + + + + Bounds + {{260.247, 156.369}, {47, 14}} + Class + ShapedGraphic + FitText + YES + ID + 76 + Line + + ID + 75 + Offset + 18.181819915771484 + Position + 0.38291174173355103 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Align + 0 + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\fs24 \cf0 project} + + + + AllowLabelDrop + + Class + LineGraphic + ID + 75 + Points + + {222.5, 145.7} + {382.054, 144.364} + + Style + + stroke + + HeadArrow + FilledArrow + HeadScale + 1.4285709857940674 + TailArrow + StickArrow + TailScale + 1.5 + + + Tail + + ID + 19 + + + + Bounds + {{309, 182.853}, {160, 14}} + Class + ShapedGraphic + FitText + YES + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 74 + Line + + ID + 69 + Position + 0.25240078568458557 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 getProjectCustomBindings()} + + + + Bounds + {{380.5, 138}, {17, 21.0909}} + Class + ShapedGraphic + HFlip + YES + ID + 73 + Magnets + + {1, 0.5} + {1, -0.5} + {-1, 0.5} + {-1, -0.5} + {0.5, 1} + {-0.5, 1} + {0.5, -1} + {-0.5, -1} + + Shape + Rectangle + Text + + Align + 0 + + + + Bounds + {{259.56, 110.107}, {47, 14}} + Class + ShapedGraphic + FitText + YES + ID + 29 + Line + + ID + 7 + Offset + 18.181819915771484 + Position + 0.38198670744895935 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Align + 0 + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\fs24 \cf0 project} + + + + AllowLabelDrop + + Class + LineGraphic + Head + + ID + 71 + + ID + 7 + Points + + {222.5, 99.6111} + {380.5, 97.8182} + + Style + + stroke + + HeadArrow + FilledArrow + HeadScale + 1.4285709857940674 + TailArrow + StickArrow + TailScale + 1.5 + + + Tail + + ID + 19 + Info + 10 + + + + Bounds + {{329.5, 75.973}, {119, 14}} + Class + ShapedGraphic + FitText + YES + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 72 + Line + + ID + 69 + Position + 0.078753873705863953 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 getDefaultBindings()} + + + + Bounds + {{380.5, 92.5454}, {17, 21.0909}} + Class + ShapedGraphic + HFlip + YES + ID + 71 + Magnets + + {1, 0.5} + {1, -0.5} + {-1, 0.5} + {-1, -0.5} + {0.5, 1} + {-0.5, 1} + {0.5, -1} + {-0.5, -1} + + Shape + Rectangle + Text + + Align + 0 + + + + Class + Group + Graphics + + + AllowLabelDrop + + Class + LineGraphic + ID + 69 + Points + + {389, 34.5} + {389, 650} + + Style + + stroke + + HeadArrow + 0 + HeadScale + 1.4285709857940674 + Pattern + 1 + TailArrow + 0 + TailScale + 0.5 + + + Tail + + ID + 70 + + + + Bounds + {{313.5, 20}, {151, 14}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 70 + Shape + Rectangle + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 \ul \ulc0 LifecyleBindingManager} + + + + ID + 68 + + + Bounds + {{108.828, 81.9091}, {81, 14}} + Class + ShapedGraphic + FitText + YES + ID + 30 + Line + + ID + 6 + Offset + 10.909090042114258 + Position + 0.52006888389587402 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Align + 0 + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\fs24 \cf0 tasks, project} + + + + AllowLabelDrop + + Class + LineGraphic + ID + 6 + Points + + {89, 78} + {205, 78} + + Style + + stroke + + HeadArrow + StickArrow + HeadScale + 1.4285709857940674 + TailArrow + 0 + TailScale + 0.5 + + + + + Bounds + {{155, 53.596}, {118, 14}} + Class + ShapedGraphic + FitText + YES + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 20 + Line + + ID + 17 + Position + 0.042397733777761459 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 constructBuildPlan()} + + + + Bounds + {{205.5, 74}, {17, 208}} + Class + ShapedGraphic + HFlip + YES + ID + 19 + Magnets + + {1, 0.5} + {1, -0.5} + {-0.470589, 0.149038} + {-0.5, -0.197115} + {0.5, 1} + {-0.5, 1} + {0.5, -1} + {-0.5, -1} + {-0.5, -0.155288} + {-0.5, -0.37687} + {-0.5, -0.155288} + {-0.5, 0.25} + {-0.529412, 0.360577} + {-0.470589, 0.283654} + {-0.529412, 0.389423} + {0.5, 0.442308} + + Shape + Rectangle + Text + + Align + 0 + + + + Class + Group + Graphics + + + AllowLabelDrop + + Class + LineGraphic + ID + 17 + Points + + {214, 34.5002} + {214, 650} + + Style + + stroke + + HeadArrow + 0 + HeadScale + 1.4285709857940674 + Pattern + 1 + TailArrow + 0 + TailScale + 0.5 + + + Tail + + ID + 18 + + + + Bounds + {{160, 20.0002}, {108, 14}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 18 + Shape + Rectangle + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 \ul \ulc0 BuildPlanner} + + + + ID + 16 + + + Bounds + {{53.5, 46.7192}, {61, 14}} + Class + ShapedGraphic + FitText + YES + FontInfo + + Color + + w + 0 + + Font + Helvetica + Size + 12 + + ID + 5 + Line + + ID + 2 + Position + 0.031224979087710381 + RotationType + 0 + + Shape + Rectangle + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\fs24 \cf0 execute()} + + + + Bounds + {{75.5, 64}, {14.5, 552}} + Class + ShapedGraphic + HFlip + YES + ID + 4 + Magnets + + {1, 0.5} + {1, -0.5} + {-0.5, 0.317029} + {-1, -0.5} + {0.5, 1} + {-0.5, 1} + {0.5, -1} + {-0.5, -1} + {-0.25, -0.126812} + {-0.568965, 0.302536} + {-0.637931, 0.400362} + {-0.5, 0.416667} + + Shape + Rectangle + Text + + Align + 0 + + + + Class + Group + Graphics + + + AllowLabelDrop + + Class + LineGraphic + ID + 2 + Points + + {84, 34.5003} + {84, 650} + + Style + + stroke + + HeadArrow + 0 + HeadScale + 1.4285709857940674 + Pattern + 1 + TailArrow + 0 + TailScale + 0.5 + + + Tail + + ID + 3 + + + + Bounds + {{26, 20.0002}, {116, 14}} + Class + ShapedGraphic + FitText + Vertical + Flow + Resize + ID + 3 + Shape + Rectangle + Text + + Text + {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 \ul \ulc0 LifecycleExecutor} + + + + ID + 1 + + + GridInfo + + HPages + 2 + IsPalette + NO + KeepToScale + + Layers + + + Lock + NO + Name + Layer 1 + Print + YES + View + YES + + + LayoutInfo + + ChildOrdering + 0 + HierarchicalOrientation + 0 + + MasterSheet + Master 1 + Orientation + 2 + OutlineStyle + Basic + RowAlign + 1 + RowSpacing + 36 + SheetTitle + Canvas 2 + UniqueID + 2 + VPages + 2 + + + SmartAlignmentGuidesActive + YES + SmartDistanceGuidesActive + YES + UseEntirePage + + WindowInfo + + CurrentSheet + 0 + DrawerOpen + + DrawerTab + Outline + DrawerWidth + 209 + FitInWindow + + Frame + {{6, 123}, {956, 590}} + ShowRuler + + ShowStatusBar + + VisibleRegion + {{-94, 0}, {941, 476}} + Zoom + 1 + + + diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/LegacyLifecycleMappingParserTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/LegacyLifecycleMappingParserTest.java new file mode 100644 index 0000000000..ee51b1aa00 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/LegacyLifecycleMappingParserTest.java @@ -0,0 +1,153 @@ +package org.apache.maven.lifecycle; + +import org.apache.maven.lifecycle.binding.LegacyLifecycleMappingParser; +import org.apache.maven.lifecycle.binding.LegacyLifecycleParsingTestComponent; +import org.apache.maven.lifecycle.mapping.LifecycleMapping; +import org.apache.maven.lifecycle.model.BuildBinding; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.codehaus.plexus.PlexusTestCase; + +import java.util.List; + +public class LegacyLifecycleMappingParserTest + extends PlexusTestCase +{ + + private LegacyLifecycleParsingTestComponent testComponent; + + private LifecycleMapping testMapping; + + private LifecycleMapping testMapping2; + + private LegacyLifecycleMappingParser parser; + + public void setUp() + throws Exception + { + super.setUp(); + + parser = (LegacyLifecycleMappingParser) lookup( LegacyLifecycleMappingParser.ROLE, "default" ); + + testComponent = (LegacyLifecycleParsingTestComponent) lookup( LegacyLifecycleParsingTestComponent.ROLE, "default" ); + testMapping = (LifecycleMapping) lookup( LifecycleMapping.ROLE, "test-mapping" ); + testMapping2 = (LifecycleMapping) lookup( LifecycleMapping.ROLE, "test-mapping2" ); + } + + public void tearDown() + throws Exception + { + release( testComponent ); + release( testMapping ); + release( testMapping2 ); + + super.tearDown(); + } + + public void testParseDefaultMappings_UsingExistingDefaultMappings() + throws LifecycleSpecificationException + { + List lifecycles = testComponent.getLifecycles(); + LifecycleBindings bindings = parser.parseDefaultMappings( lifecycles ); + + // org.apache.maven.plugins:maven-clean-plugin:clean + List cleanPhase = bindings.getCleanBinding().getClean().getBindings(); + assertEquals( 1, cleanPhase.size() ); + + MojoBinding binding = (MojoBinding) cleanPhase.get( 0 ); + assertMojo( "org.apache.maven.plugins", "maven-clean-plugin", "clean", binding ); + + // org.apache.maven.plugins:maven-site-plugin:site + List sitePhase = bindings.getSiteBinding().getSite().getBindings(); + assertEquals( 1, sitePhase.size() ); + + binding = (MojoBinding) sitePhase.get( 0 ); + assertMojo( "org.apache.maven.plugins", "maven-site-plugin", "site", binding ); + + // org.apache.maven.plugins:maven-site-plugin:deploy + List siteDeployPhase = bindings.getSiteBinding().getSiteDeploy().getBindings(); + assertEquals( 1, siteDeployPhase.size() ); + + binding = (MojoBinding) siteDeployPhase.get( 0 ); + assertMojo( "org.apache.maven.plugins", "maven-site-plugin", "deploy", binding ); + } + + private void assertMojo( String groupId, String artifactId, String goal, MojoBinding binding ) + { + assertEquals( groupId, binding.getGroupId() ); + assertEquals( artifactId, binding.getArtifactId() ); + assertEquals( goal, binding.getGoal() ); + } + + public void testParseMappings_SparselyPopulatedMappings() + throws LifecycleSpecificationException + { + LifecycleBindings bindings = parser.parseMappings( testMapping, "test-mapping" ); + + BuildBinding bb = bindings.getBuildBinding(); + assertNotNull( bb ); + + // + // org.apache.maven.plugins:maven-site-plugin:attach-descriptor + // org.apache.maven.plugins:maven-install-plugin:install + // org.apache.maven.plugins:maven-deploy-plugin:deploy + // + // + // org.apache.maven.plugins:maven-site-plugin:attach-descriptor + // + List mojos = bb.getCreatePackage().getBindings(); + + assertEquals( 1, mojos.size() ); + assertMojo( "org.apache.maven.plugins", "maven-site-plugin", "attach-descriptor", (MojoBinding) mojos.get( 0 ) ); + assertTrue( ( (MojoBinding) mojos.get( 0 ) ).isOptional() ); + + mojos = bb.getInstall().getBindings(); + + assertEquals( 1, mojos.size() ); + assertMojo( "org.apache.maven.plugins", "maven-install-plugin", "install", (MojoBinding) mojos.get( 0 ) ); + assertFalse( ( (MojoBinding) mojos.get( 0 ) ).isOptional() ); + + mojos = bb.getDeploy().getBindings(); + + assertEquals( 1, mojos.size() ); + assertMojo( "org.apache.maven.plugins", "maven-deploy-plugin", "deploy", (MojoBinding) mojos.get( 0 ) ); + assertFalse( ( (MojoBinding) mojos.get( 0 ) ).isOptional() ); + } + + public void testParseMappings_MappingsWithTwoBindingsInOnePhase() + throws LifecycleSpecificationException + { + LifecycleBindings bindings = parser.parseMappings( testMapping2, "test-mapping2" ); + + BuildBinding bb = bindings.getBuildBinding(); + assertNotNull( bb ); + + // + // org.apache.maven.plugins:maven-site-plugin:attach-descriptor + // org.apache.maven.plugins:maven-install-plugin:install + // org.apache.maven.plugins:maven-deploy-plugin:deploy + // + // + // org.apache.maven.plugins:maven-site-plugin:attach-descriptor + // + List mojos = bb.getCreatePackage().getBindings(); + + assertEquals( 2, mojos.size() ); + assertMojo( "org.apache.maven.plugins", "maven-site-plugin", "attach-descriptor", (MojoBinding) mojos.get( 0 ) ); + assertTrue( ( (MojoBinding) mojos.get( 0 ) ).isOptional() ); + assertMojo( "org.apache.maven.plugins", "maven-clean-plugin", "clean", (MojoBinding) mojos.get( 1 ) ); + + mojos = bb.getInstall().getBindings(); + + assertEquals( 1, mojos.size() ); + assertMojo( "org.apache.maven.plugins", "maven-install-plugin", "install", (MojoBinding) mojos.get( 0 ) ); + assertFalse( ( (MojoBinding) mojos.get( 0 ) ).isOptional() ); + + mojos = bb.getDeploy().getBindings(); + + assertEquals( 1, mojos.size() ); + assertMojo( "org.apache.maven.plugins", "maven-deploy-plugin", "deploy", (MojoBinding) mojos.get( 0 ) ); + assertFalse( ( (MojoBinding) mojos.get( 0 ) ).isOptional() ); + } + +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/binding/DefaultLifecycleBindingManagerTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/binding/DefaultLifecycleBindingManagerTest.java new file mode 100644 index 0000000000..920e6ad1a9 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/binding/DefaultLifecycleBindingManagerTest.java @@ -0,0 +1,362 @@ +package org.apache.maven.lifecycle.binding; + +import org.apache.maven.lifecycle.LifecycleLoaderException; +import org.apache.maven.lifecycle.LifecycleSpecificationException; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.lifecycle.plan.LifecyclePlannerException; +import org.apache.maven.model.Build; +import org.apache.maven.model.Model; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginExecution; +import org.apache.maven.model.PluginManagement; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.PlexusTestCase; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +public class DefaultLifecycleBindingManagerTest + extends PlexusTestCase +{ + + private LifecycleBindingManager mgr; + + public void setUp() + throws Exception + { + super.setUp(); + + this.mgr = (LifecycleBindingManager) lookup( LifecycleBindingManager.ROLE, "default" ); + } + + public void testLookup() + { + assertNotNull( mgr ); + } + + public void testGetBindingsForPackaging_TestMergePluginConfigToBinding() + throws LifecycleLoaderException, LifecycleSpecificationException + { + Model model = new Model(); + model.setGroupId( "group" ); + model.setArtifactId( "artifact" ); + model.setVersion( "1" ); + + Build build = new Build(); + model.setBuild( build ); + + Plugin plugin = new Plugin(); + build.addPlugin( plugin ); + + plugin.setGroupId( "org.apache.maven.plugins" ); + plugin.setArtifactId( "maven-compiler-plugin" ); + + Properties pluginConfig = new Properties(); + + pluginConfig.setProperty( "test", "value" ); + pluginConfig.setProperty( "test2", "other-value" ); + + plugin.setConfiguration( createConfiguration( pluginConfig ) ); + + MavenProject project = new MavenProject( model ); + + LifecycleBindings lifecycleBindings = mgr.getBindingsForPackaging( project ); + + List bindings = lifecycleBindings.getBuildBinding().getCompile().getBindings(); + + assertNotNull( bindings ); + assertEquals( 1, bindings.size() ); + + MojoBinding binding = (MojoBinding) bindings.get( 0 ); + + Xpp3Dom config = (Xpp3Dom) binding.getConfiguration(); + + assertNotNull( config ); + + assertEquals( "value", config.getChild( "test" ).getValue() ); + assertEquals( "other-value", config.getChild( "test2" ).getValue() ); + } + + public void testGetBindingsForPackaging_TestMergePluginManagementConfigToBinding() + throws LifecycleLoaderException, LifecycleSpecificationException + { + Model model = new Model(); + model.setGroupId( "group" ); + model.setArtifactId( "artifact" ); + model.setVersion( "1" ); + + Build build = new Build(); + model.setBuild( build ); + + PluginManagement plugMgmt = new PluginManagement(); + build.setPluginManagement( plugMgmt ); + + Plugin plugin = new Plugin(); + plugMgmt.addPlugin( plugin ); + + plugin.setGroupId( "org.apache.maven.plugins" ); + plugin.setArtifactId( "maven-compiler-plugin" ); + + Properties pluginConfig = new Properties(); + + pluginConfig.setProperty( "test", "value" ); + pluginConfig.setProperty( "test2", "other-value" ); + + plugin.setConfiguration( createConfiguration( pluginConfig ) ); + + MavenProject project = new MavenProject( model ); + + LifecycleBindings lifecycleBindings = mgr.getBindingsForPackaging( project ); + + List bindings = lifecycleBindings.getBuildBinding().getCompile().getBindings(); + + assertNotNull( bindings ); + assertEquals( 1, bindings.size() ); + + MojoBinding binding = (MojoBinding) bindings.get( 0 ); + + Xpp3Dom config = (Xpp3Dom) binding.getConfiguration(); + + assertNotNull( config ); + + assertEquals( "value", config.getChild( "test" ).getValue() ); + assertEquals( "other-value", config.getChild( "test2" ).getValue() ); + } + + public void testGetProjectCustomBindings_ExecutionConfigShouldOverridePluginConfig() + throws LifecycleLoaderException, LifecycleSpecificationException + { + Model model = new Model(); + model.setGroupId( "group" ); + model.setArtifactId( "artifact" ); + model.setVersion( "1" ); + + Build build = new Build(); + model.setBuild( build ); + + Plugin plugin = new Plugin(); + build.addPlugin( plugin ); + + plugin.setGroupId( "plugin.group" ); + plugin.setArtifactId( "plugin-artifact" ); + plugin.setVersion( "1" ); + + Properties pluginConfig = new Properties(); + + pluginConfig.setProperty( "test", "value" ); + pluginConfig.setProperty( "test2", "other-value" ); + + plugin.setConfiguration( createConfiguration( pluginConfig ) ); + + PluginExecution exec = new PluginExecution(); + plugin.addExecution( exec ); + + exec.setId( "test-execution" ); + exec.setPhase( "validate" ); + exec.setGoals( Collections.singletonList( "goal" ) ); + + Properties execConfig = new Properties(); + execConfig.setProperty( "test", "value2" ); + + exec.setConfiguration( createConfiguration( execConfig ) ); + + MavenProject project = new MavenProject( model ); + + LifecycleBindings lifecycleBindings = mgr.getProjectCustomBindings( project ); + + List bindings = lifecycleBindings.getBuildBinding().getValidate().getBindings(); + + assertNotNull( bindings ); + assertEquals( 1, bindings.size() ); + + MojoBinding binding = (MojoBinding) bindings.get( 0 ); + + Xpp3Dom config = (Xpp3Dom) binding.getConfiguration(); + + assertEquals( "value2", config.getChild( "test" ).getValue() ); + assertEquals( "other-value", config.getChild( "test2" ).getValue() ); + } + + private Object createConfiguration( Properties configProperties ) + { + Xpp3Dom config = new Xpp3Dom( "configuration" ); + for ( Iterator it = configProperties.keySet().iterator(); it.hasNext(); ) + { + String key = (String) it.next(); + String value = configProperties.getProperty( key ); + + Xpp3Dom child = new Xpp3Dom( key ); + child.setValue( value ); + + config.addChild( child ); + } + + return config; + } + + public void testAssembleMojoBindingList_ReturnBindingsUpToStopPhaseForSinglePhaseTaskList() + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException + { + LifecycleBindings bindings = new LifecycleBindings(); + bindings.getCleanBinding().getPreClean().addBinding( newMojoBinding( "goal", "artifact", "pre-clean" ) ); + bindings.getCleanBinding().getClean().addBinding( newMojoBinding( "goal", "artifact", "clean" ) ); + bindings.getCleanBinding().getPostClean().addBinding( newMojoBinding( "goal", "artifact", "post-clean" ) ); + + List result = mgr.assembleMojoBindingList( Collections.singletonList( "clean" ), bindings, new MavenProject( new Model() ) ); + + assertEquals( 2, result.size() ); + + MojoBinding binding = (MojoBinding) result.get( 0 ); + + assertEquals( "goal", binding.getGroupId() ); + assertEquals( "artifact", binding.getArtifactId() ); + assertEquals( "pre-clean", binding.getGoal() ); + + binding = (MojoBinding) result.get( 1 ); + + assertEquals( "goal", binding.getGroupId() ); + assertEquals( "artifact", binding.getArtifactId() ); + assertEquals( "clean", binding.getGoal() ); + } + + public void testAssembleMojoBindingList_CombinePreviousBindingsWhenSubsetOfNextBindingsForTwoPhaseTaskList() + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException + { + LifecycleBindings bindings = new LifecycleBindings(); + bindings.getCleanBinding().getPreClean().addBinding( newMojoBinding( "goal", "artifact", "pre-clean" ) ); + bindings.getCleanBinding().getClean().addBinding( newMojoBinding( "goal", "artifact", "clean" ) ); + bindings.getCleanBinding().getClean().addBinding( newMojoBinding( "goal", "artifact", "post-clean" ) ); + + List tasks = new ArrayList( 2 ); + tasks.add( "clean" ); + tasks.add( "post-clean" ); + + List result = mgr.assembleMojoBindingList( tasks, bindings, new MavenProject( new Model() ) ); + + assertEquals( 3, result.size() ); + + MojoBinding binding = (MojoBinding) result.get( 0 ); + + assertEquals( "goal", binding.getGroupId() ); + assertEquals( "artifact", binding.getArtifactId() ); + assertEquals( "pre-clean", binding.getGoal() ); + + binding = (MojoBinding) result.get( 1 ); + + assertEquals( "goal", binding.getGroupId() ); + assertEquals( "artifact", binding.getArtifactId() ); + assertEquals( "clean", binding.getGoal() ); + + binding = (MojoBinding) result.get( 2 ); + + assertEquals( "goal", binding.getGroupId() ); + assertEquals( "artifact", binding.getArtifactId() ); + assertEquals( "post-clean", binding.getGoal() ); + } + + public void testAssembleMojoBindingList_IgnoreSuccessiveBindingsWhenSameAsPreviousOnesForTwoPhaseTaskList() + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException + { + LifecycleBindings bindings = new LifecycleBindings(); + bindings.getCleanBinding().getPreClean().addBinding( newMojoBinding( "goal", "artifact", "pre-clean" ) ); + bindings.getCleanBinding().getClean().addBinding( newMojoBinding( "goal", "artifact", "clean" ) ); + + List tasks = new ArrayList( 2 ); + tasks.add( "clean" ); + tasks.add( "post-clean" ); + + List result = mgr.assembleMojoBindingList( tasks, bindings, new MavenProject( new Model() ) ); + + assertEquals( 2, result.size() ); + + MojoBinding binding = (MojoBinding) result.get( 0 ); + + assertEquals( "goal", binding.getGroupId() ); + assertEquals( "artifact", binding.getArtifactId() ); + assertEquals( "pre-clean", binding.getGoal() ); + + binding = (MojoBinding) result.get( 1 ); + + assertEquals( "goal", binding.getGroupId() ); + assertEquals( "artifact", binding.getArtifactId() ); + assertEquals( "clean", binding.getGoal() ); + } + + public void testAssembleMojoBindingList_ReturnBindingsUpToStopPhasesForTwoPhaseTaskList() + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException + { + LifecycleBindings bindings = new LifecycleBindings(); + + bindings.getCleanBinding().getPreClean().addBinding( newMojoBinding( "goal", "artifact", "pre-clean" ) ); + bindings.getCleanBinding().getClean().addBinding( newMojoBinding( "goal", "artifact", "clean" ) ); + bindings.getCleanBinding().getPostClean().addBinding( newMojoBinding( "goal", "artifact", "post-clean" ) ); + + bindings.getBuildBinding().getInitialize().addBinding( newMojoBinding( "goal", "artifact", "initialize" ) ); + bindings.getBuildBinding().getCompile().addBinding( newMojoBinding( "goal", "artifact", "compile" ) ); + bindings.getBuildBinding().getCreatePackage().addBinding( newMojoBinding( "goal", "artifact", "package" ) ); + + List tasks = new ArrayList( 2 ); + tasks.add( "clean" ); + tasks.add( "compile" ); + + List result = mgr.assembleMojoBindingList( tasks, bindings, new MavenProject( new Model() ) ); + + assertEquals( 4, result.size() ); + + MojoBinding binding = (MojoBinding) result.get( 0 ); + + assertEquals( "goal", binding.getGroupId() ); + assertEquals( "artifact", binding.getArtifactId() ); + assertEquals( "pre-clean", binding.getGoal() ); + + binding = (MojoBinding) result.get( 1 ); + + assertEquals( "goal", binding.getGroupId() ); + assertEquals( "artifact", binding.getArtifactId() ); + assertEquals( "clean", binding.getGoal() ); + + binding = (MojoBinding) result.get( 2 ); + + assertEquals( "goal", binding.getGroupId() ); + assertEquals( "artifact", binding.getArtifactId() ); + assertEquals( "initialize", binding.getGoal() ); + + binding = (MojoBinding) result.get( 3 ); + + assertEquals( "goal", binding.getGroupId() ); + assertEquals( "artifact", binding.getArtifactId() ); + assertEquals( "compile", binding.getGoal() ); + + } + + public void testAssembleMojoBindingList_ThrowErrorForInvalidPhaseNameAsSingletonTaskList() + throws LifecyclePlannerException, LifecycleLoaderException + { + try + { + mgr.assembleMojoBindingList( Collections.singletonList( "dud" ), new LifecycleBindings(), + new MavenProject( new Model() ) ); + + fail( "Should fail with LifecycleSpecificationException due to invalid phase/direct mojo reference." ); + } + catch ( LifecycleSpecificationException e ) + { + // expected. + } + } + + private MojoBinding newMojoBinding( String groupId, String artifactId, String goal ) + { + MojoBinding binding = new MojoBinding(); + binding.setGroupId( groupId ); + binding.setArtifactId( artifactId ); + binding.setGoal( goal ); + + return binding; + } +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/binding/DefaultMojoBindingFactoryTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/binding/DefaultMojoBindingFactoryTest.java new file mode 100644 index 0000000000..2d08af079a --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/binding/DefaultMojoBindingFactoryTest.java @@ -0,0 +1,83 @@ +package org.apache.maven.lifecycle.binding; + +import org.apache.maven.lifecycle.LifecycleLoaderException; +import org.apache.maven.lifecycle.LifecycleSpecificationException; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.model.Model; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.PlexusTestCase; + +public class DefaultMojoBindingFactoryTest + extends PlexusTestCase +{ + + private MojoBindingFactory factory; + + public void setUp() throws Exception + { + super.setUp(); + + factory = (MojoBindingFactory) lookup( MojoBindingFactory.ROLE, "default" ); + } + + public void testPrefixGoalSpec_PrefixReferenceNotAllowed() + throws LifecycleLoaderException + { + String spec = "prefix:goal"; + + try + { + factory.parseMojoBinding( spec, new MavenProject( new Model() ), false ); + + fail( "Should fail when prefix references are not allowed." ); + } + catch ( LifecycleSpecificationException e ) + { + // expected. + } + } + + public void testGroupIdArtifactIdGoalSpec_ShouldParseCorrectly() + throws LifecycleSpecificationException, LifecycleLoaderException + { + String spec = "group:artifact:goal"; + + MojoBinding binding = factory.parseMojoBinding( spec, new MavenProject( new Model() ), false ); + + assertEquals( "group", binding.getGroupId() ); + assertEquals( "artifact", binding.getArtifactId() ); + assertNull( binding.getVersion() ); + assertEquals( "goal", binding.getGoal() ); + } + + public void testGroupIdArtifactIdVersionGoalSpec_ShouldParseCorrectly() + throws LifecycleSpecificationException, LifecycleLoaderException + { + String spec = "group:artifact:version:goal"; + + MojoBinding binding = factory.parseMojoBinding( spec, new MavenProject( new Model() ), false ); + + assertEquals( "group", binding.getGroupId() ); + assertEquals( "artifact", binding.getArtifactId() ); + assertEquals( "version", binding.getVersion() ); + assertEquals( "goal", binding.getGoal() ); + } + + public void testSpecWithTooManyParts_ShouldFail() + throws LifecycleLoaderException + { + String spec = "group:artifact:version:type:goal"; + + try + { + factory.parseMojoBinding( spec, new MavenProject( new Model() ), false ); + + fail( "Should fail because spec has too many parts (type part is not allowed)." ); + } + catch ( LifecycleSpecificationException e ) + { + // expected + } + } + +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/binding/LegacyLifecycleParsingTestComponent.java b/maven-core/src/test/java/org/apache/maven/lifecycle/binding/LegacyLifecycleParsingTestComponent.java new file mode 100644 index 0000000000..c7bc985182 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/binding/LegacyLifecycleParsingTestComponent.java @@ -0,0 +1,16 @@ +package org.apache.maven.lifecycle.binding; + +import java.util.List; + +public class LegacyLifecycleParsingTestComponent +{ + + public static final String ROLE = LegacyLifecycleParsingTestComponent.class.getName(); + private List legacyLifecycles; + + public List getLifecycles() + { + return legacyLifecycles; + } + +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/plan/ForkPlanModifierTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/plan/ForkPlanModifierTest.java new file mode 100644 index 0000000000..a96f4df3cd --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/plan/ForkPlanModifierTest.java @@ -0,0 +1,197 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.lifecycle.statemgmt.StateManagementUtils; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import junit.framework.TestCase; + +public class ForkPlanModifierTest + extends TestCase +{ + + public void testModifyBindings_AddTwoMojosAheadOfCompileMojo() + throws LifecyclePlannerException + { + MojoBinding mojo = newMojo( "org.apache.maven.plugins", "maven-compiler-plugin", "compile" ); + + List additions = new ArrayList(); + additions.add( newMojo( "group", "artifact", "clean" ) ); + additions.add( newMojo( "group", "artifact", "compile" ) ); + + LifecycleBindings target = new LifecycleBindings(); + + assertEquals( 0, target.getBuildBinding().getCompile().getBindings().size() ); + + target.getBuildBinding().getCompile().addBinding( mojo ); + + assertEquals( 1, target.getBuildBinding().getCompile().getBindings().size() ); + + target = new ForkPlanModifier( mojo, additions ).modifyBindings( target ); + + assertEquals( 6, target.getBuildBinding().getCompile().getBindings().size() ); + + Iterator it = target.getBuildBinding().getCompile().getBindings().iterator(); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.START_FORKED_EXECUTION_GOAL, + (MojoBinding) it.next() ); + + assertMojo( "group", "artifact", "clean", (MojoBinding) it.next() ); + + assertMojo( "group", "artifact", "compile", (MojoBinding) it.next() ); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.END_FORKED_EXECUTION_GOAL, + (MojoBinding) it.next() ); + + assertMojo( mojo.getGroupId(), mojo.getArtifactId(), mojo.getGoal(), + (MojoBinding) it.next() ); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.CLEAR_FORKED_EXECUTION_GOAL, (MojoBinding) it.next() ); + + } + + public void testModifyBindings_AddTwoMojosBetweenTwoExistingCompileMojos() + throws LifecyclePlannerException + { + MojoBinding mojo = newMojo( "org.apache.maven.plugins", "maven-compiler-plugin", "compile" ); + MojoBinding mojo2 = newMojo( "org.apache.maven.plugins", "maven-compiler-plugin", "compile2" ); + + List additions = new ArrayList(); + additions.add( newMojo( "group", "artifact", "clean" ) ); + additions.add( newMojo( "group", "artifact", "compile" ) ); + + LifecycleBindings target = new LifecycleBindings(); + + assertEquals( 0, target.getBuildBinding().getCompile().getBindings().size() ); + + target.getBuildBinding().getCompile().addBinding( mojo ); + target.getBuildBinding().getCompile().addBinding( mojo2 ); + + assertEquals( 2, target.getBuildBinding().getCompile().getBindings().size() ); + + target = new ForkPlanModifier( mojo2, additions ).modifyBindings( target ); + + assertEquals( 7, target.getBuildBinding().getCompile().getBindings().size() ); + + Iterator it = target.getBuildBinding().getCompile().getBindings().iterator(); + + assertMojo( mojo.getGroupId(), mojo.getArtifactId(), mojo.getGoal(), + (MojoBinding) it.next() ); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.START_FORKED_EXECUTION_GOAL, + (MojoBinding) it.next() ); + + assertMojo( "group", "artifact", "clean", (MojoBinding) it.next() ); + + assertMojo( "group", "artifact", "compile", (MojoBinding) it.next() ); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.END_FORKED_EXECUTION_GOAL, + (MojoBinding) it.next() ); + + assertMojo( mojo2.getGroupId(), mojo2.getArtifactId(), mojo2.getGoal(), + (MojoBinding) it.next() ); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.CLEAR_FORKED_EXECUTION_GOAL, (MojoBinding) it.next() ); + + } + + public void testModifyBindings_AddTwoNormalPlusTwoModifierModifiedMojosWithTwoExistingCompileMojos() + throws LifecyclePlannerException + { + MojoBinding mojo = newMojo( "org.apache.maven.plugins", "maven-compiler-plugin", "compile" ); + MojoBinding mojo2 = newMojo( "org.apache.maven.plugins", "maven-compiler-plugin", "compile2" ); + + List modAdditions = new ArrayList(); + modAdditions.add( newMojo( "group2", "artifact", "clean" ) ); + modAdditions.add( newMojo( "group2", "artifact", "compile" ) ); + + MojoBinding mojo3 = newMojo( "group", "artifact", "clean" ); + + List additions = new ArrayList(); + additions.add( mojo3 ); + additions.add( newMojo( "group", "artifact", "compile" ) ); + + LifecycleBindings target = new LifecycleBindings(); + + assertEquals( 0, target.getBuildBinding().getCompile().getBindings().size() ); + + target.getBuildBinding().getCompile().addBinding( mojo ); + target.getBuildBinding().getCompile().addBinding( mojo2 ); + + assertEquals( 2, target.getBuildBinding().getCompile().getBindings().size() ); + + BuildPlanModifier modder = new ForkPlanModifier( mojo, additions ); + modder.addModifier( new ForkPlanModifier( mojo3, modAdditions ) ); + + target = modder.modifyBindings( target ); + + assertEquals( 12, target.getBuildBinding().getCompile().getBindings().size() ); + + Iterator it = target.getBuildBinding().getCompile().getBindings().iterator(); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.START_FORKED_EXECUTION_GOAL, + (MojoBinding) it.next() ); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.START_FORKED_EXECUTION_GOAL, + (MojoBinding) it.next() ); + + assertMojo( "group2", "artifact", "clean", (MojoBinding) it.next() ); + + assertMojo( "group2", "artifact", "compile", (MojoBinding) it.next() ); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.END_FORKED_EXECUTION_GOAL, + (MojoBinding) it.next() ); + + assertMojo( mojo3.getGroupId(), mojo3.getArtifactId(), mojo3.getGoal(), + (MojoBinding) it.next() ); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.CLEAR_FORKED_EXECUTION_GOAL, (MojoBinding) it.next() ); + + assertMojo( "group", "artifact", "compile", (MojoBinding) it.next() ); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.END_FORKED_EXECUTION_GOAL, + (MojoBinding) it.next() ); + + assertMojo( mojo.getGroupId(), mojo.getArtifactId(), mojo.getGoal(), + (MojoBinding) it.next() ); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.CLEAR_FORKED_EXECUTION_GOAL, (MojoBinding) it.next() ); + + assertMojo( mojo2.getGroupId(), mojo2.getArtifactId(), mojo2.getGoal(), + (MojoBinding) it.next() ); + + } + + private void assertMojo( String groupId, String artifactId, String goal, MojoBinding binding ) + { + assertEquals( groupId, binding.getGroupId() ); + assertEquals( artifactId, binding.getArtifactId() ); + assertEquals( goal, binding.getGoal() ); + } + + private MojoBinding newMojo( String groupId, String artifactId, String goal ) + { + MojoBinding binding = new MojoBinding(); + binding.setGroupId( groupId ); + binding.setArtifactId( artifactId ); + binding.setGoal( goal ); + + return binding; + } +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/plan/LifecycleBuildPlanTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/plan/LifecycleBuildPlanTest.java new file mode 100644 index 0000000000..b38976683b --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/plan/LifecycleBuildPlanTest.java @@ -0,0 +1,122 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.LifecycleLoaderException; +import org.apache.maven.lifecycle.LifecycleSpecificationException; +import org.apache.maven.lifecycle.binding.LifecycleBindingManager; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.lifecycle.statemgmt.StateManagementUtils; +import org.codehaus.plexus.PlexusTestCase; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +public class LifecycleBuildPlanTest + extends PlexusTestCase +{ + + private LifecycleBindingManager bindingManager; + + public void setUp() + throws Exception + { + super.setUp(); + + bindingManager = (LifecycleBindingManager) lookup( LifecycleBindingManager.ROLE, "default" ); + } + + public void testSingleTask_TwoMojoBindings() + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException + { + LifecycleBindings bindings = new LifecycleBindings(); + bindings.getCleanBinding().getPreClean().addBinding( newMojo( "group", "artifact", "pre-clean" ) ); + bindings.getCleanBinding().getClean().addBinding( newMojo( "group", "artifact", "clean" ) ); + + List plan = new LifecycleBuildPlan( Collections.singletonList( "clean" ), bindings ).getPlanMojoBindings( null, + bindingManager ); + + assertEquals( 2, plan.size() ); + assertMojo( "group", "artifact", "pre-clean", (MojoBinding) plan.get( 0 ) ); + assertMojo( "group", "artifact", "clean", (MojoBinding) plan.get( 1 ) ); + } + + public void testTwoAdditiveTasksInOrder_ThreeMojoBindings_NoDupes() + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException + { + LifecycleBindings bindings = new LifecycleBindings(); + bindings.getCleanBinding().getPreClean().addBinding( newMojo( "group", "artifact", "pre-clean" ) ); + bindings.getCleanBinding().getClean().addBinding( newMojo( "group", "artifact", "clean" ) ); + bindings.getCleanBinding().getClean().addBinding( newMojo( "group", "artifact", "post-clean" ) ); + + List tasks = new ArrayList(); + tasks.add( "clean" ); + tasks.add( "post-clean" ); + + List plan = new LifecycleBuildPlan( tasks, bindings ).getPlanMojoBindings( null, bindingManager ); + + assertEquals( 3, plan.size() ); + assertMojo( "group", "artifact", "pre-clean", (MojoBinding) plan.get( 0 ) ); + assertMojo( "group", "artifact", "clean", (MojoBinding) plan.get( 1 ) ); + assertMojo( "group", "artifact", "post-clean", (MojoBinding) plan.get( 2 ) ); + } + + public void testTwoAdditiveTasksInOrder_TwoMojoBindings_OneMojoModifierInsertedBetween_NoDupes() + throws LifecycleSpecificationException, LifecyclePlannerException, LifecycleLoaderException + { + MojoBinding clean = newMojo( "group", "artifact", "clean" ); + + List mods = Collections.singletonList( newMojo( "group", "artifact", "pre-clean" ) ); + + BuildPlanModifier modder = new ForkPlanModifier( clean, mods ); + + LifecycleBindings bindings = new LifecycleBindings(); + bindings.getCleanBinding().getPreClean().addBinding( clean ); + bindings.getCleanBinding().getClean().addBinding( newMojo( "group", "artifact", "post-clean" ) ); + + List tasks = new ArrayList(); + tasks.add( "clean" ); + tasks.add( "post-clean" ); + + BuildPlan lifecyclePlan = new LifecycleBuildPlan( tasks, bindings ); + lifecyclePlan.addModifier( modder ); + + List plan = lifecyclePlan.getPlanMojoBindings( null, bindingManager ); + + assertEquals( 6, plan.size() ); + Iterator it = plan.iterator(); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.START_FORKED_EXECUTION_GOAL, (MojoBinding) it.next() ); + + assertMojo( "group", "artifact", "pre-clean", (MojoBinding) it.next() ); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.END_FORKED_EXECUTION_GOAL, (MojoBinding) it.next() ); + + assertMojo( "group", "artifact", "clean", (MojoBinding) it.next() ); + + assertMojo( StateManagementUtils.GROUP_ID, StateManagementUtils.ARTIFACT_ID, + StateManagementUtils.CLEAR_FORKED_EXECUTION_GOAL, (MojoBinding) it.next() ); + + assertMojo( "group", "artifact", "post-clean", (MojoBinding) it.next() ); + } + + private void assertMojo( String groupId, String artifactId, String goal, MojoBinding binding ) + { + assertEquals( groupId, binding.getGroupId() ); + assertEquals( artifactId, binding.getArtifactId() ); + assertEquals( goal, binding.getGoal() ); + } + + private MojoBinding newMojo( String groupId, String artifactId, String goal ) + { + MojoBinding binding = new MojoBinding(); + binding.setGroupId( groupId ); + binding.setArtifactId( artifactId ); + binding.setGoal( goal ); + + return binding; + } +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/plan/ReportingPlanModifierTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/plan/ReportingPlanModifierTest.java new file mode 100644 index 0000000000..26b0f772ed --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/plan/ReportingPlanModifierTest.java @@ -0,0 +1,146 @@ +package org.apache.maven.lifecycle.plan; + +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import junit.framework.TestCase; + +public class ReportingPlanModifierTest + extends TestCase +{ + + public void testModifyBindings_AddTwoMojosAheadOfCompileMojo() + throws LifecyclePlannerException + { + MojoBinding mojo = newMojo( "org.apache.maven.plugins", "maven-compiler-plugin", "compile" ); + + List additions = new ArrayList(); + additions.add( newMojo( "group", "artifact", "clean" ) ); + additions.add( newMojo( "group", "artifact", "compile" ) ); + + LifecycleBindings target = new LifecycleBindings(); + + assertEquals( 0, target.getBuildBinding().getCompile().getBindings().size() ); + + target.getBuildBinding().getCompile().addBinding( mojo ); + + assertEquals( 1, target.getBuildBinding().getCompile().getBindings().size() ); + + target = new ReportingPlanModifier( mojo, additions ).modifyBindings( target ); + + assertEquals( 3, target.getBuildBinding().getCompile().getBindings().size() ); + + Iterator it = target.getBuildBinding().getCompile().getBindings().iterator(); + + assertMojo( "group", "artifact", "clean", (MojoBinding) it.next() ); + + assertMojo( "group", "artifact", "compile", (MojoBinding) it.next() ); + + assertMojo( mojo.getGroupId(), mojo.getArtifactId(), mojo.getGoal(), (MojoBinding) it.next() ); + } + + public void testModifyBindings_AddTwoMojosBetweenTwoExistingCompileMojos() + throws LifecyclePlannerException + { + MojoBinding mojo = newMojo( "org.apache.maven.plugins", "maven-compiler-plugin", "compile" ); + MojoBinding mojo2 = newMojo( "org.apache.maven.plugins", "maven-compiler-plugin", "compile2" ); + + List additions = new ArrayList(); + additions.add( newMojo( "group", "artifact", "clean" ) ); + additions.add( newMojo( "group", "artifact", "compile" ) ); + + LifecycleBindings target = new LifecycleBindings(); + + assertEquals( 0, target.getBuildBinding().getCompile().getBindings().size() ); + + target.getBuildBinding().getCompile().addBinding( mojo ); + target.getBuildBinding().getCompile().addBinding( mojo2 ); + + assertEquals( 2, target.getBuildBinding().getCompile().getBindings().size() ); + + target = new ReportingPlanModifier( mojo2, additions ).modifyBindings( target ); + + assertEquals( 4, target.getBuildBinding().getCompile().getBindings().size() ); + + Iterator it = target.getBuildBinding().getCompile().getBindings().iterator(); + + assertMojo( mojo.getGroupId(), mojo.getArtifactId(), mojo.getGoal(), (MojoBinding) it.next() ); + + assertMojo( "group", "artifact", "clean", (MojoBinding) it.next() ); + + assertMojo( "group", "artifact", "compile", (MojoBinding) it.next() ); + + assertMojo( mojo2.getGroupId(), mojo2.getArtifactId(), mojo2.getGoal(), (MojoBinding) it.next() ); + + } + + public void testModifyBindings_AddTwoNormalPlusTwoModifierModifiedMojosWithTwoExistingCompileMojos() + throws LifecyclePlannerException + { + MojoBinding mojo = newMojo( "org.apache.maven.plugins", "maven-compiler-plugin", "compile" ); + MojoBinding mojo2 = newMojo( "org.apache.maven.plugins", "maven-compiler-plugin", "compile2" ); + + List modAdditions = new ArrayList(); + modAdditions.add( newMojo( "group2", "artifact", "clean" ) ); + modAdditions.add( newMojo( "group2", "artifact", "compile" ) ); + + MojoBinding mojo3 = newMojo( "group", "artifact", "clean" ); + + List additions = new ArrayList(); + additions.add( mojo3 ); + additions.add( newMojo( "group", "artifact", "compile" ) ); + + LifecycleBindings target = new LifecycleBindings(); + + assertEquals( 0, target.getBuildBinding().getCompile().getBindings().size() ); + + target.getBuildBinding().getCompile().addBinding( mojo ); + target.getBuildBinding().getCompile().addBinding( mojo2 ); + + assertEquals( 2, target.getBuildBinding().getCompile().getBindings().size() ); + + BuildPlanModifier modder = new ReportingPlanModifier( mojo, additions ); + modder.addModifier( new ReportingPlanModifier( mojo3, modAdditions ) ); + + target = modder.modifyBindings( target ); + + assertEquals( 6, target.getBuildBinding().getCompile().getBindings().size() ); + + Iterator it = target.getBuildBinding().getCompile().getBindings().iterator(); + + assertMojo( "group2", "artifact", "clean", (MojoBinding) it.next() ); + + assertMojo( "group2", "artifact", "compile", (MojoBinding) it.next() ); + + assertMojo( mojo3.getGroupId(), mojo3.getArtifactId(), mojo3.getGoal(), (MojoBinding) it.next() ); + + assertMojo( "group", "artifact", "compile", (MojoBinding) it.next() ); + + assertMojo( mojo.getGroupId(), mojo.getArtifactId(), mojo.getGoal(), (MojoBinding) it.next() ); + + assertMojo( mojo2.getGroupId(), mojo2.getArtifactId(), mojo2.getGoal(), (MojoBinding) it.next() ); + + } + + private void assertMojo( String groupId, String artifactId, String goal, MojoBinding binding ) + { + assertEquals( groupId, binding.getGroupId() ); + assertEquals( artifactId, binding.getArtifactId() ); + assertEquals( goal, binding.getGoal() ); + } + + private MojoBinding newMojo( String groupId, String artifactId, String goal ) + { + MojoBinding binding = new MojoBinding(); + binding.setGroupId( groupId ); + binding.setArtifactId( artifactId ); + binding.setGoal( goal ); + + return binding; + } + +} diff --git a/maven-core/src/test/resources/META-INF/maven/org.apache.maven/maven-core/pom.properties b/maven-core/src/test/resources/META-INF/maven/org.apache.maven/maven-core/pom.properties new file mode 100644 index 0000000000..ef45373a89 --- /dev/null +++ b/maven-core/src/test/resources/META-INF/maven/org.apache.maven/maven-core/pom.properties @@ -0,0 +1 @@ +version=2.1-SNAPSHOT \ No newline at end of file diff --git a/maven-core/src/test/resources/META-INF/plexus/components.xml b/maven-core/src/test/resources/META-INF/plexus/components.xml new file mode 100644 index 0000000000..d4782490a7 --- /dev/null +++ b/maven-core/src/test/resources/META-INF/plexus/components.xml @@ -0,0 +1,112 @@ + + + + org.apache.maven.lifecycle.binding.LegacyLifecycleParsingTestComponent + default + org.apache.maven.lifecycle.binding.LegacyLifecycleParsingTestComponent + + + + default + + validate + initialize + generate-sources + process-sources + generate-resources + process-resources + compile + process-classes + generate-test-sources + process-test-sources + generate-test-resources + process-test-resources + test-compile + process-test-classes + test + prepare-package + package + pre-integration-test + integration-test + post-integration-test + verify + install + deploy + + + + clean + + pre-clean + clean + post-clean + + + org.apache.maven.plugins:maven-clean-plugin:clean + + + + site + + pre-site + site + post-site + site-deploy + + + org.apache.maven.plugins:maven-site-plugin:site + org.apache.maven.plugins:maven-site-plugin:deploy + + + + + + + org.apache.maven.lifecycle.mapping.LifecycleMapping + test-mapping + org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping + + + + default + + + org.apache.maven.plugins:maven-site-plugin:attach-descriptor + org.apache.maven.plugins:maven-install-plugin:install + org.apache.maven.plugins:maven-deploy-plugin:deploy + + + org.apache.maven.plugins:maven-site-plugin:attach-descriptor + + + + + + + + org.apache.maven.lifecycle.mapping.LifecycleMapping + test-mapping2 + org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping + + + + default + + + + org.apache.maven.plugins:maven-site-plugin:attach-descriptor, + org.apache.maven.plugins:maven-clean-plugin:clean + + org.apache.maven.plugins:maven-install-plugin:install + org.apache.maven.plugins:maven-deploy-plugin:deploy + + + org.apache.maven.plugins:maven-site-plugin:attach-descriptor + + + + + + + + \ No newline at end of file diff --git a/maven-embedder/src/main/java/org/apache/maven/embedder/MavenEmbedder.java b/maven-embedder/src/main/java/org/apache/maven/embedder/MavenEmbedder.java index 39e823726e..b132b4650f 100644 --- a/maven-embedder/src/main/java/org/apache/maven/embedder/MavenEmbedder.java +++ b/maven-embedder/src/main/java/org/apache/maven/embedder/MavenEmbedder.java @@ -41,7 +41,7 @@ import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenExecutionResult; import org.apache.maven.execution.MavenSession; -import org.apache.maven.lifecycle.LifecycleExecutor; +import org.apache.maven.lifecycle.LifecycleUtils; import org.apache.maven.model.Model; import org.apache.maven.model.Plugin; import org.apache.maven.model.io.jdom.MavenJDOMWriter; @@ -67,17 +67,15 @@ import org.apache.maven.settings.validation.SettingsValidator; import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.MutablePlexusContainer; -import org.codehaus.plexus.PlexusContainerException; import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.PlexusContainerException; import org.codehaus.plexus.classworlds.ClassWorld; import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.classworlds.realm.DuplicateRealmException; import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; -import org.codehaus.plexus.component.repository.ComponentDescriptor; import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException; import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.codehaus.plexus.component.repository.exception.ComponentRepositoryException; -import org.codehaus.plexus.configuration.PlexusConfiguration; import org.codehaus.plexus.configuration.PlexusConfigurationException; import org.codehaus.plexus.logging.LoggerManager; import org.codehaus.plexus.util.StringUtils; @@ -94,13 +92,11 @@ import java.io.Reader; import java.io.Writer; import java.net.URL; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Arrays; /** * Class intended to be used by clients who wish to embed Maven into their applications @@ -489,34 +485,37 @@ public ArtifactHandler getArtifactHandler( Artifact artifact ) // Execution of phases/goals // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- - // Lifecycle information + // LegacyLifecycle information // ---------------------------------------------------------------------- public List getLifecyclePhases() - throws MavenEmbedderException { - List phases = new ArrayList(); + return getBuildLifecyclePhases(); + } + + public List getAllLifecyclePhases() + { + return LifecycleUtils.getValidPhaseNames(); + } + + public List getDefaultLifecyclePhases() + { + return getBuildLifecyclePhases(); + } + + public List getBuildLifecyclePhases() + { + return LifecycleUtils.getValidBuildPhaseNames(); + } - ComponentDescriptor descriptor = container.getComponentDescriptor( LifecycleExecutor.ROLE ); + public List getCleanLifecyclePhases() + { + return LifecycleUtils.getValidCleanPhaseNames(); + } - PlexusConfiguration configuration = descriptor.getConfiguration(); - - PlexusConfiguration[] phasesConfigurations = - configuration.getChild( "lifecycles" ).getChild( 0 ).getChild( "phases" ).getChildren( "phase" ); - - try - { - for ( int i = 0; i < phasesConfigurations.length; i++ ) - { - phases.add( phasesConfigurations[i].getValue() ); - } - } - catch ( PlexusConfigurationException e ) - { - throw new MavenEmbedderException( "Cannot retrieve default lifecycle phasesConfigurations.", e ); - } - - return phases; + public List getSiteLifecyclePhases() + { + return LifecycleUtils.getValidSitePhaseNames(); } // ---------------------------------------------------------------------- @@ -524,7 +523,7 @@ public List getLifecyclePhases() // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- - // Lifecycle + // LegacyLifecycle // ---------------------------------------------------------------------- private void start( Configuration configuration ) @@ -645,7 +644,7 @@ private void start( Configuration configuration ) } // ---------------------------------------------------------------------- - // Lifecycle + // LegacyLifecycle // ---------------------------------------------------------------------- private void handleExtensions( List extensions ) diff --git a/maven-embedder/src/main/java/org/apache/maven/embedder/execution/DefaultMavenExecutionRequestDefaultsPopulator.java b/maven-embedder/src/main/java/org/apache/maven/embedder/execution/DefaultMavenExecutionRequestDefaultsPopulator.java index 808b7b817b..62c427373b 100644 --- a/maven-embedder/src/main/java/org/apache/maven/embedder/execution/DefaultMavenExecutionRequestDefaultsPopulator.java +++ b/maven-embedder/src/main/java/org/apache/maven/embedder/execution/DefaultMavenExecutionRequestDefaultsPopulator.java @@ -202,7 +202,7 @@ private void resolveParameters( Settings settings ) } // ---------------------------------------------------------------------------- - // Lifecycle + // LegacyLifecycle // ---------------------------------------------------------------------------- public void contextualize( Context context ) diff --git a/maven-embedder/src/test/java/org/apache/maven/embedder/AbstractEmbedderExecutionTestCase.java b/maven-embedder/src/test/java/org/apache/maven/embedder/AbstractEmbedderExecutionTestCase.java index e442146247..e05d767d3c 100644 --- a/maven-embedder/src/test/java/org/apache/maven/embedder/AbstractEmbedderExecutionTestCase.java +++ b/maven-embedder/src/test/java/org/apache/maven/embedder/AbstractEmbedderExecutionTestCase.java @@ -26,6 +26,7 @@ import java.io.File; import java.util.Arrays; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Properties; @@ -36,20 +37,20 @@ public abstract class AbstractEmbedderExecutionTestCase protected File runWithProject( String goal ) throws Exception { - return runWithProject( goal, null ); + return runWithProject( Collections.singletonList( goal ), null ); } protected File runWithProject( String goal, Properties properties ) throws Exception { - return runWithProject( new String[]{goal}, properties ); + return runWithProject( Collections.singletonList( goal ), properties ); } protected File runWithProject( String[] goals ) throws Exception { - return runWithProject( goals, null ); + return runWithProject( Arrays.asList( goals ), null ); } protected File runWithProject( String[] goals, @@ -83,6 +84,8 @@ protected File runWithProject( List goals, FileUtils.copyDirectoryStructure( testDirectory, targetDirectory ); MavenExecutionRequest request = new DefaultMavenExecutionRequest() + .setShowErrors( true ) + .setLoggingLevel( MavenExecutionRequest.LOGGING_LEVEL_DEBUG ) .setBaseDirectory( targetDirectory ) .setGoals( goals ); diff --git a/maven-embedder/src/test/java/org/apache/maven/embedder/MavenEmbedderTest.java b/maven-embedder/src/test/java/org/apache/maven/embedder/MavenEmbedderTest.java index 573d34f265..754e55156a 100644 --- a/maven-embedder/src/test/java/org/apache/maven/embedder/MavenEmbedderTest.java +++ b/maven-embedder/src/test/java/org/apache/maven/embedder/MavenEmbedderTest.java @@ -63,6 +63,11 @@ protected void setUp() super.setUp(); basedir = System.getProperty( "basedir" ); + + if ( basedir == null ) + { + basedir = new File( "." ).getCanonicalPath(); + } ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); @@ -202,7 +207,7 @@ public void testExecutionUsingAProfileWhichSetsAProperty() } // ---------------------------------------------------------------------- - // Lifecycle phases + // LegacyLifecycle phases // ---------------------------------------------------------------------- public void testRetrievingLifecyclePhases() diff --git a/maven-lifecycle/pom.xml b/maven-lifecycle/pom.xml new file mode 100644 index 0000000000..8dca77b076 --- /dev/null +++ b/maven-lifecycle/pom.xml @@ -0,0 +1,41 @@ + + + maven + org.apache.maven + 2.1-SNAPSHOT + + 4.0.0 + maven-lifecycle + Maven Lifecycle Model + + + + org.codehaus.plexus + plexus-component-api + test + + + org.codehaus.plexus + plexus-container-default + test + + + org.codehaus.plexus + plexus-utils + + + + + + + org.codehaus.modello + modello-maven-plugin + + 1.0.0 + src/main/mdo/maven-lifecycle.mdo + + + + + + diff --git a/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/ClassLoaderXmlBindingLoader.java b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/ClassLoaderXmlBindingLoader.java new file mode 100644 index 0000000000..2cad2aa63f --- /dev/null +++ b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/ClassLoaderXmlBindingLoader.java @@ -0,0 +1,84 @@ +package org.apache.maven.lifecycle; + +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.io.xpp3.LifecycleBindingsXpp3Reader; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; + +/** + * @author jdcasey + */ +public class ClassLoaderXmlBindingLoader + implements LifecycleBindingLoader +{ + + // configuration. + private String path; + + public ClassLoaderXmlBindingLoader() + { + // for plexus init. + } + + public ClassLoaderXmlBindingLoader( String path ) + { + this.path = path; + } + + public LifecycleBindings getBindings() + throws LifecycleLoaderException, LifecycleSpecificationException + { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + URL url = loader.getResource( getPath() ); + + if ( url == null ) + { + throw new LifecycleLoaderException( "Classpath resource: " + getPath() + " could not be found." ); + } + + InputStreamReader reader; + try + { + reader = new InputStreamReader( url.openStream() ); + } + catch ( IOException e ) + { + throw new LifecycleLoaderException( "Failed to open stream for classpath resource: " + getPath() + ". Reason: " + + e.getMessage(), e ); + } + + LifecycleBindings bindings; + try + { + bindings = new LifecycleBindingsXpp3Reader().read( reader ); + } + catch ( IOException e ) + { + throw new LifecycleLoaderException( "Classpath resource: " + getPath() + " could not be read. Reason: " + + e.getMessage(), e ); + } + catch ( XmlPullParserException e ) + { + throw new LifecycleLoaderException( "Classpath resource: " + getPath() + " could not be parsed. Reason: " + + e.getMessage(), e ); + } + + LifecycleUtils.setOrigin( bindings, url.toExternalForm() ); + + return bindings; + } + + public String getPath() + { + return path; + } + + public void setPath( String path ) + { + this.path = path; + } + +} diff --git a/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleBindingLoader.java b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleBindingLoader.java new file mode 100644 index 0000000000..9f38975f25 --- /dev/null +++ b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleBindingLoader.java @@ -0,0 +1,13 @@ +package org.apache.maven.lifecycle; + +import org.apache.maven.lifecycle.model.LifecycleBindings; + +public interface LifecycleBindingLoader +{ + + String ROLE = LifecycleBindingLoader.class.getName(); + + LifecycleBindings getBindings() + throws LifecycleLoaderException, LifecycleSpecificationException; + +} diff --git a/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleException.java b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleException.java new file mode 100644 index 0000000000..5a594265ec --- /dev/null +++ b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleException.java @@ -0,0 +1,17 @@ +package org.apache.maven.lifecycle; + +public class LifecycleException + extends Exception +{ + + public LifecycleException( String message, Throwable cause ) + { + super( message, cause ); + } + + public LifecycleException( String message ) + { + super( message ); + } + +} diff --git a/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleLoaderException.java b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleLoaderException.java new file mode 100644 index 0000000000..f8f7202f7c --- /dev/null +++ b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleLoaderException.java @@ -0,0 +1,17 @@ +package org.apache.maven.lifecycle; + +public class LifecycleLoaderException + extends LifecycleException +{ + + public LifecycleLoaderException( String message, Throwable cause ) + { + super( message, cause ); + } + + public LifecycleLoaderException( String message ) + { + super( message ); + } + +} diff --git a/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleSpecificationException.java b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleSpecificationException.java new file mode 100644 index 0000000000..9d0a4d826e --- /dev/null +++ b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleSpecificationException.java @@ -0,0 +1,17 @@ +package org.apache.maven.lifecycle; + +public class LifecycleSpecificationException + extends LifecycleException +{ + + public LifecycleSpecificationException( String message, Throwable cause ) + { + super( message, cause ); + } + + public LifecycleSpecificationException( String message ) + { + super( message ); + } + +} diff --git a/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleUtils.java b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleUtils.java new file mode 100644 index 0000000000..d5eef58ece --- /dev/null +++ b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/LifecycleUtils.java @@ -0,0 +1,671 @@ +package org.apache.maven.lifecycle; + +import org.apache.maven.lifecycle.model.BuildBinding; +import org.apache.maven.lifecycle.model.CleanBinding; +import org.apache.maven.lifecycle.model.LifecycleBinding; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.lifecycle.model.Phase; +import org.apache.maven.lifecycle.model.SiteBinding; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class LifecycleUtils +{ + + private LifecycleUtils() + { + } + + public static void setOrigin( LifecycleBindings bindings, String origin ) + { + for ( Iterator bindingIt = bindings.getBindingList().iterator(); bindingIt.hasNext(); ) + { + LifecycleBinding binding = (LifecycleBinding) bindingIt.next(); + + if ( binding == null ) + { + continue; + } + + for ( Iterator phaseIt = binding.getPhasesInOrder().iterator(); phaseIt.hasNext(); ) + { + Phase phase = (Phase) phaseIt.next(); + + if ( phase == null ) + { + continue; + } + + for ( Iterator mojoIt = phase.getBindings().iterator(); mojoIt.hasNext(); ) + { + MojoBinding mojoBinding = (MojoBinding) mojoIt.next(); + + mojoBinding.setOrigin( origin ); + } + } + } + } + + public static List getMojoBindingListForLifecycle( String stopPhase, LifecycleBindings bindings ) + throws NoSuchPhaseException + { + LifecycleBinding binding = findLifecycleBindingForPhase( stopPhase, bindings ); + + if ( binding == null ) + { + throw new NoSuchPhaseException( stopPhase, "Phase not found in any lifecycle." ); + } + + return getMojoBindingListForLifecycle( stopPhase, binding ); + } + + public static List getMojoBindingListForLifecycle( String stopPhase, LifecycleBinding lifecycle ) + throws NoSuchPhaseException + { + List phaseNames = lifecycle.getPhaseNamesInOrder(); + + int idx = phaseNames.indexOf( stopPhase ); + + if ( idx < 0 ) + { + throw new NoSuchPhaseException( stopPhase, "Phase not found in lifecycle: " + lifecycle.getId() ); + } + + List phases = lifecycle.getPhasesInOrder(); + + List bindings = new ArrayList(); + for ( int i = 0; i <= idx; i++ ) + { + Phase phase = (Phase) phases.get( i ); + List phaseBindings = phase.getBindings(); + + if ( phaseBindings != null && !phaseBindings.isEmpty() ) + { + bindings.addAll( phaseBindings ); + } + } + + return bindings; + } + + /** + * @return null if the phase is not contained in any of the lifecycles. + */ + public static LifecycleBinding findLifecycleBindingForPhase( String phaseName, LifecycleBindings lifecycles ) + { + List lifecyclesAvailable = lifecycles.getBindingList(); + + for ( Iterator it = lifecyclesAvailable.iterator(); it.hasNext(); ) + { + LifecycleBinding lifecycle = (LifecycleBinding) it.next(); + + if ( lifecycle.getPhaseNamesInOrder().indexOf( phaseName ) > -1 ) + { + return lifecycle; + } + } + + return null; + } + + public static void removeMojoBinding( String phaseName, MojoBinding mojoBinding, LifecycleBinding lifecycleBinding, + boolean considerExecutionId ) + throws NoSuchPhaseException + { + List phaseNames = lifecycleBinding.getPhaseNamesInOrder(); + + int idx = phaseNames.indexOf( phaseName ); + + if ( idx < 0 ) + { + throw new NoSuchPhaseException( phaseName, "Phase: " + phaseName + " not found in lifecycle: " + + lifecycleBinding.getId() ); + } + + List phases = lifecycleBinding.getPhasesInOrder(); + + Phase phase = (Phase) phases.get( idx ); + + if ( phase != null ) + { + List mojoBindings = phase.getBindings(); + + String targetKey = createMojoBindingKey( mojoBinding, considerExecutionId ); + + for ( Iterator it = mojoBindings.iterator(); it.hasNext(); ) + { + MojoBinding candidate = (MojoBinding) it.next(); + + String candidateKey = createMojoBindingKey( candidate, considerExecutionId ); + if ( candidateKey.equals( targetKey ) ) + { + it.remove(); + } + } + + phase.setBindings( mojoBindings ); + } + } + + public static void addMojoBinding( String phaseName, MojoBinding mojoBinding, LifecycleBinding lifecycleBinding ) + throws NoSuchPhaseException + { + List phaseNames = lifecycleBinding.getPhaseNamesInOrder(); + + int idx = phaseNames.indexOf( phaseName ); + + if ( idx < 0 ) + { + throw new NoSuchPhaseException( phaseName, "Phase: " + phaseName + " not found in lifecycle: " + + lifecycleBinding.getId() ); + } + + List phases = lifecycleBinding.getPhasesInOrder(); + + Phase phase = (Phase) phases.get( idx ); + phase.addBinding( mojoBinding ); + } + + public static void addMojoBinding( String phaseName, MojoBinding mojo, LifecycleBindings bindings ) + throws LifecycleSpecificationException + { + LifecycleBinding binding = findLifecycleBindingForPhase( phaseName, bindings ); + + if ( binding == null ) + { + throw new NoSuchPhaseException( phaseName, "Phase not found in any lifecycle: " + phaseName ); + } + + addMojoBinding( phaseName, mojo, binding ); + } + + public static LifecycleBindings mergeBindings( LifecycleBindings existingBindings, LifecycleBindings newBindings, + LifecycleBindings defaultBindings, boolean mergeConfigIfExecutionIdMatches ) + { + return mergeBindings( existingBindings, newBindings, defaultBindings, mergeConfigIfExecutionIdMatches, false ); + } + + public static LifecycleBindings mergeBindings( LifecycleBindings existingBindings, LifecycleBindings newBindings, + LifecycleBindings defaultBindings, boolean mergeConfigIfExecutionIdMatches, + boolean reverseConfigMergeDirection ) + { + LifecycleBindings result = new LifecycleBindings(); + result.setPackaging( newBindings.getPackaging() ); + + CleanBinding cb = (CleanBinding) cloneBinding( existingBindings.getCleanBinding() ); + if ( defaultBindings != null && isNullOrEmpty( cb ) ) + { + cb = (CleanBinding) cloneBinding( defaultBindings.getCleanBinding() ); + } + + if ( cb == null ) + { + cb = new CleanBinding(); + } + + result.setCleanBinding( cb ); + + BuildBinding bb = (BuildBinding) cloneBinding( existingBindings.getBuildBinding() ); + if ( defaultBindings != null && isNullOrEmpty( bb ) ) + { + bb = (BuildBinding) cloneBinding( defaultBindings.getBuildBinding() ); + } + + if ( bb == null ) + { + bb = new BuildBinding(); + } + + result.setBuildBinding( bb ); + + SiteBinding sb = (SiteBinding) cloneBinding( existingBindings.getSiteBinding() ); + if ( defaultBindings != null && isNullOrEmpty( sb ) ) + { + sb = (SiteBinding) cloneBinding( defaultBindings.getSiteBinding() ); + } + + if ( sb == null ) + { + sb = new SiteBinding(); + } + + result.setSiteBinding( sb ); + + for ( Iterator bindingIt = newBindings.getBindingList().iterator(); bindingIt.hasNext(); ) + { + LifecycleBinding lifecycleBinding = (LifecycleBinding) bindingIt.next(); + + if ( lifecycleBinding != null ) + { + List phaseNames = lifecycleBinding.getPhaseNamesInOrder(); + List phases = lifecycleBinding.getPhasesInOrder(); + + for ( int i = 0; i < phases.size(); i++ ) + { + Phase phase = (Phase) phases.get( i ); + String name = (String) phaseNames.get( i ); + + if ( phase != null && phase.getBindings() != null && !phase.getBindings().isEmpty() ) + { + for ( Iterator phaseIt = phase.getBindings().iterator(); phaseIt.hasNext(); ) + { + MojoBinding mojoBinding = (MojoBinding) phaseIt.next(); + + mojoBinding = cloneMojoBinding( mojoBinding ); + + if ( mergeConfigIfExecutionIdMatches ) + { + MojoBinding matchingBinding = findMatchingMojoBinding( mojoBinding, existingBindings, true ); + + if ( matchingBinding != null ) + { + Xpp3Dom existingConfig = new Xpp3Dom( (Xpp3Dom) matchingBinding.getConfiguration() ); + + Xpp3Dom configuration; + if ( reverseConfigMergeDirection ) + { + configuration = Xpp3Dom.mergeXpp3Dom( existingConfig, (Xpp3Dom) mojoBinding.getConfiguration() ); + } + else + { + configuration = Xpp3Dom.mergeXpp3Dom( (Xpp3Dom) mojoBinding.getConfiguration(), existingConfig ); + } + + mojoBinding.setConfiguration( configuration ); + + if ( mojoBinding.getOrigin() == null && matchingBinding.getOrigin() != null ) + { + mojoBinding.setOrigin( matchingBinding.getOrigin() ); + } + + LifecycleBinding resultBinding = findLifecycleBindingForPhase( name, result ); + + try + { + removeMojoBinding( name, matchingBinding, resultBinding, true ); + } + catch ( NoSuchPhaseException e ) + { + IllegalStateException error = new IllegalStateException( + e.getMessage() + + "\nSomething strange is going on. Merging should not encounter such inconsistencies." ); + + error.initCause( e ); + + throw error; + } + } + } + + try + { + addMojoBinding( name, mojoBinding, result ); + } + catch ( LifecycleSpecificationException e ) + { + // NOTE: this shouldn't happen as long as normal components are used + // to create/read these LifecycleBindings instances. + IllegalArgumentException error = new IllegalArgumentException( + "Project bindings are invalid. Reason: " + + e.getMessage() ); + + error.initCause( e ); + + throw error; + } + } + } + } + } + } + + return result; + } + + private static boolean isNullOrEmpty( LifecycleBinding binding ) + { + if ( binding == null ) + { + return true; + } + + for ( Iterator it = binding.getPhasesInOrder().iterator(); it.hasNext(); ) + { + Phase phase = (Phase) it.next(); + + if ( !phase.getBindings().isEmpty() ) + { + return false; + } + } + + return true; + } + + public static MojoBinding findMatchingMojoBinding( MojoBinding mojoBinding, LifecycleBindings inBindings, + boolean considerExecutionId ) + { + String key = createMojoBindingKey( mojoBinding, considerExecutionId ); + + return (MojoBinding) mapMojoBindingsByKey( inBindings, considerExecutionId ).get( key ); + } + + private static Map mapMojoBindingsByKey( LifecycleBindings bindings, boolean considerExecutionId ) + { + Map byKey = new HashMap(); + + for ( Iterator bindingIt = bindings.getBindingList().iterator(); bindingIt.hasNext(); ) + { + LifecycleBinding binding = (LifecycleBinding) bindingIt.next(); + + if ( binding != null ) + { + for ( Iterator phaseIt = binding.getPhasesInOrder().iterator(); phaseIt.hasNext(); ) + { + Phase phase = (Phase) phaseIt.next(); + + if ( phase != null ) + { + for ( Iterator mojoIt = phase.getBindings().iterator(); mojoIt.hasNext(); ) + { + MojoBinding mojoBinding = (MojoBinding) mojoIt.next(); + + byKey.put( createMojoBindingKey( mojoBinding, considerExecutionId ), mojoBinding ); + } + } + } + } + } + + return byKey; + } + + public static void removeMojoBindings( List toRemove, LifecycleBindings bindings, boolean considerExecutionId ) + throws NoSuchPhaseException + { + if ( bindings.getCleanBinding() != null ) + { + removeMojoBindings( toRemove, bindings.getCleanBinding(), considerExecutionId ); + } + + if ( bindings.getBuildBinding() != null ) + { + removeMojoBindings( toRemove, bindings.getBuildBinding(), considerExecutionId ); + } + + if ( bindings.getSiteBinding() != null ) + { + removeMojoBindings( toRemove, bindings.getSiteBinding(), considerExecutionId ); + } + } + + public static void removeMojoBindings( List toRemove, LifecycleBinding removeFrom, boolean considerExecutionId ) + throws NoSuchPhaseException + { + // remove where gid:aid:goal matches. + List targets = new ArrayList(); + for ( Iterator it = toRemove.iterator(); it.hasNext(); ) + { + MojoBinding binding = (MojoBinding) it.next(); + + targets.add( createMojoBindingKey( binding, considerExecutionId ) ); + } + + List phases = removeFrom.getPhasesInOrder(); + + for ( int i = 0; i < phases.size(); i++ ) + { + Phase phase = (Phase) phases.get( i ); + List phaseBindings = phase.getBindings(); + + for ( Iterator mojoIt = phaseBindings.iterator(); mojoIt.hasNext(); ) + { + MojoBinding binding = (MojoBinding) mojoIt.next(); + String key = createMojoBindingKey( binding, considerExecutionId ); + if ( targets.contains( key ) ) + { + mojoIt.remove(); + } + } + + phase.setBindings( phaseBindings ); + } + } + + public static String createMojoBindingKey( MojoBinding mojoBinding, boolean considerExecutionId ) + { + String key = mojoBinding.getGroupId() + ":" + mojoBinding.getArtifactId() + ":" + mojoBinding.getGoal(); + + if ( considerExecutionId ) + { + key += ":" + mojoBinding.getExecutionId(); + } + + return key; + } + + public static LifecycleBindings cloneBindings( LifecycleBindings bindings ) + { + LifecycleBindings result = new LifecycleBindings(); + + if ( bindings.getCleanBinding() != null ) + { + result.setCleanBinding( (CleanBinding) cloneBinding( bindings.getCleanBinding() ) ); + } + + if ( bindings.getBuildBinding() != null ) + { + result.setBuildBinding( (BuildBinding) cloneBinding( bindings.getBuildBinding() ) ); + } + + if ( bindings.getSiteBinding() != null ) + { + result.setSiteBinding( (SiteBinding) cloneBinding( bindings.getSiteBinding() ) ); + } + + return result; + } + + public static LifecycleBinding cloneBinding( LifecycleBinding binding ) + { + if ( binding == null ) + { + return null; + } + + LifecycleBinding result; + if ( binding instanceof CleanBinding ) + { + result = new CleanBinding(); + } + else if ( binding instanceof SiteBinding ) + { + result = new SiteBinding(); + } + else if ( binding instanceof BuildBinding ) + { + result = new BuildBinding(); + } + else + { + throw new IllegalArgumentException( "Unrecognized LifecycleBinding type: " + binding.getClass().getName() + + "; cannot clone." ); + } + + List phases = binding.getPhasesInOrder(); + List names = binding.getPhaseNamesInOrder(); + + for ( int i = 0; i < phases.size(); i++ ) + { + Phase phase = (Phase) phases.get( i ); + String phaseName = (String) names.get( i ); + + for ( Iterator mojoIt = phase.getBindings().iterator(); mojoIt.hasNext(); ) + { + MojoBinding originalBinding = (MojoBinding) mojoIt.next(); + + MojoBinding newBinding = cloneMojoBinding( originalBinding ); + + try + { + addMojoBinding( phaseName, newBinding, result ); + } + catch ( NoSuchPhaseException e ) + { + IllegalStateException error = new IllegalStateException( e.getMessage() + + "\nSomething strange is going on. Cloning should not encounter such inconsistencies." ); + + error.initCause( e ); + + throw error; + } + } + } + + return result; + } + + public static MojoBinding cloneMojoBinding( MojoBinding binding ) + { + MojoBinding result = new MojoBinding(); + + result.setGroupId( binding.getGroupId() ); + result.setArtifactId( binding.getArtifactId() ); + result.setVersion( binding.getVersion() ); + result.setConfiguration( binding.getConfiguration() ); + result.setExecutionId( binding.getExecutionId() ); + result.setGoal( binding.getGoal() ); + result.setOrigin( binding.getOrigin() ); + + return result; + } + + public static Phase findPhaseForMojoBinding( MojoBinding mojoBinding, LifecycleBindings lifecycleBindings, + boolean considerExecutionId ) + { + String targetKey = createMojoBindingKey( mojoBinding, considerExecutionId ); + + for ( Iterator lifecycleIt = lifecycleBindings.getBindingList().iterator(); lifecycleIt.hasNext(); ) + { + LifecycleBinding binding = (LifecycleBinding) lifecycleIt.next(); + + for ( Iterator phaseIt = binding.getPhasesInOrder().iterator(); phaseIt.hasNext(); ) + { + Phase phase = (Phase) phaseIt.next(); + + for ( Iterator mojoIt = phase.getBindings().iterator(); mojoIt.hasNext(); ) + { + MojoBinding candidate = (MojoBinding) mojoIt.next(); + String key = createMojoBindingKey( candidate, considerExecutionId ); + if ( key.equals( targetKey ) ) + { + return phase; + } + } + } + } + + return null; + } + + public static boolean isMojoBindingPresent( MojoBinding binding, List candidates, boolean considerExecutionId ) + { + String key = createMojoBindingKey( binding, considerExecutionId ); + + for ( Iterator it = candidates.iterator(); it.hasNext(); ) + { + MojoBinding candidate = (MojoBinding) it.next(); + + String candidateKey = createMojoBindingKey( candidate, considerExecutionId ); + + if ( candidateKey.equals( key ) ) + { + return true; + } + } + + return false; + } + + public static boolean isValidPhaseName( String phaseName ) + { + LifecycleBindings test = new LifecycleBindings(); + for ( Iterator it = test.getBindingList().iterator(); it.hasNext(); ) + { + LifecycleBinding binding = (LifecycleBinding) it.next(); + + if ( binding.getPhaseNamesInOrder().contains( phaseName ) ) + { + return true; + } + } + + return false; + } + + public static List getValidPhaseNames() + { + List phaseNames = new ArrayList(); + + LifecycleBindings bindings = new LifecycleBindings(); + for ( Iterator bindingIt = bindings.getBindingList().iterator(); bindingIt.hasNext(); ) + { + LifecycleBinding binding = (LifecycleBinding) bindingIt.next(); + + for ( Iterator phaseNameIt = binding.getPhaseNamesInOrder().iterator(); phaseNameIt.hasNext(); ) + { + phaseNames.add( phaseNameIt.next() ); + } + } + + return phaseNames; + } + + public static List getValidBuildPhaseNames() + { + List phaseNames = new ArrayList(); + + LifecycleBinding binding = new BuildBinding(); + + for ( Iterator phaseNameIt = binding.getPhaseNamesInOrder().iterator(); phaseNameIt.hasNext(); ) + { + phaseNames.add( phaseNameIt.next() ); + } + + return phaseNames; + } + + public static List getValidCleanPhaseNames() + { + List phaseNames = new ArrayList(); + + LifecycleBinding binding = new CleanBinding(); + + for ( Iterator phaseNameIt = binding.getPhaseNamesInOrder().iterator(); phaseNameIt.hasNext(); ) + { + phaseNames.add( phaseNameIt.next() ); + } + + return phaseNames; + } + + public static List getValidSitePhaseNames() + { + List phaseNames = new ArrayList(); + + LifecycleBinding binding = new SiteBinding(); + + for ( Iterator phaseNameIt = binding.getPhaseNamesInOrder().iterator(); phaseNameIt.hasNext(); ) + { + phaseNames.add( phaseNameIt.next() ); + } + + return phaseNames; + } +} diff --git a/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/MojoBindingUtils.java b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/MojoBindingUtils.java new file mode 100644 index 0000000000..5c39bac65a --- /dev/null +++ b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/MojoBindingUtils.java @@ -0,0 +1,18 @@ +package org.apache.maven.lifecycle; + +import org.apache.maven.lifecycle.model.MojoBinding; + +public final class MojoBindingUtils +{ + + private MojoBindingUtils() + { + } + + public static String toString( MojoBinding mojoBinding ) + { + return mojoBinding.getGroupId() + ":" + mojoBinding.getArtifactId() + ":" + + ( mojoBinding.getVersion() == null ? "" : mojoBinding.getVersion() + ":" ) + mojoBinding.getGoal(); + } + +} diff --git a/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/NoSuchLifecycleException.java b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/NoSuchLifecycleException.java new file mode 100644 index 0000000000..3868bd4584 --- /dev/null +++ b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/NoSuchLifecycleException.java @@ -0,0 +1,26 @@ +package org.apache.maven.lifecycle; + +public class NoSuchLifecycleException + extends LifecycleSpecificationException +{ + + private final String packaging; + + public NoSuchLifecycleException( String packaging, String message, Throwable cause ) + { + super( message, cause ); + this.packaging = packaging; + } + + public NoSuchLifecycleException( String phase, String message ) + { + super( message ); + this.packaging = phase; + } + + public String getPackaging() + { + return packaging; + } + +} diff --git a/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/NoSuchPhaseException.java b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/NoSuchPhaseException.java new file mode 100644 index 0000000000..5808faf772 --- /dev/null +++ b/maven-lifecycle/src/main/java/org/apache/maven/lifecycle/NoSuchPhaseException.java @@ -0,0 +1,26 @@ +package org.apache.maven.lifecycle; + +public class NoSuchPhaseException + extends LifecycleSpecificationException +{ + + private final String phase; + + public NoSuchPhaseException( String phase, String message, Throwable cause ) + { + super( message, cause ); + this.phase = phase; + } + + public NoSuchPhaseException( String phase, String message ) + { + super( message ); + this.phase = phase; + } + + public String getPhase() + { + return phase; + } + +} diff --git a/maven-lifecycle/src/main/mdo/maven-lifecycle.mdo b/maven-lifecycle/src/main/mdo/maven-lifecycle.mdo new file mode 100644 index 0000000000..a2adb708f3 --- /dev/null +++ b/maven-lifecycle/src/main/mdo/maven-lifecycle.mdo @@ -0,0 +1,620 @@ + + + + + + + + build-lifecycle + LifecycleBindings + Model for lifecycle specifications starting in Maven 2.1 + + + package + org.apache.maven.lifecycle.model + + + + + LifecycleBindings + 1.0.0 + Specifies phase bindings for clean, site, and default lifecycles. + + + 1.0.0 + packaging + String + true + POM packaging to which this lifecycle specification applies. + + + 1.0.0 + cleanBinding + new CleanBinding() + The binding for the clean lifecycle + + CleanBinding + + + + 1.0.0 + buildBinding + new BuildBinding() + The binding for the main build (default) lifecycle + + BuildBinding + + + + 1.0.0 + siteBinding + new SiteBinding() + The binding for the site lifecycle + + SiteBinding + + + + + + 1.0.0 + + + + + + LifecycleBinding + 1.0.0 + Base-class for all lifecycle bindings. + + + 1.0.0 + + + + + + CleanBinding + LifecycleBinding + 1.0.0 + + + preClean + 1.0.0 + new Phase() + + Phase + + + + clean + 1.0.0 + new Phase() + + Phase + + + + postClean + 1.0.0 + new Phase() + + Phase + + + + + + 1.0.0 + + + + + + BuildBinding + LifecycleBinding + 1.0.0 + + + validate + 1.0.0 + new Phase() + + Phase + + + + initialize + 1.0.0 + new Phase() + + Phase + + + + generateSources + 1.0.0 + new Phase() + + Phase + + + + processSources + 1.0.0 + new Phase() + + Phase + + + + generateResources + 1.0.0 + new Phase() + + Phase + + + + processResources + 1.0.0 + new Phase() + + Phase + + + + compile + 1.0.0 + new Phase() + + Phase + + + + processClasses + 1.0.0 + new Phase() + + Phase + + + + generateTestSources + 1.0.0 + new Phase() + + Phase + + + + processTestSources + 1.0.0 + new Phase() + + Phase + + + + generateTestResources + 1.0.0 + new Phase() + + Phase + + + + processTestResources + 1.0.0 + new Phase() + + Phase + + + + testCompile + 1.0.0 + new Phase() + + Phase + + + + processTestClasses + 1.0.0 + new Phase() + + Phase + + + + test + 1.0.0 + new Phase() + + Phase + + + + preparePackage + 1.0.0 + new Phase() + + Phase + + + + createPackage + 1.0.0 + new Phase() + + Phase + + + + preIntegrationTest + 1.0.0 + new Phase() + + Phase + + + + integrationTest + 1.0.0 + new Phase() + + Phase + + + + postIntegrationTest + 1.0.0 + new Phase() + + Phase + + + + verify + 1.0.0 + new Phase() + + Phase + + + + install + 1.0.0 + new Phase() + + Phase + + + + deploy + 1.0.0 + new Phase() + + Phase + + + + + + 1.0.0 + + + + + + SiteBinding + LifecycleBinding + 1.0.0 + + + preSite + 1.0.0 + new Phase() + + Phase + + + + site + 1.0.0 + new Phase() + + Phase + + + + postSite + 1.0.0 + new Phase() + + Phase + + + + siteDeploy + 1.0.0 + new Phase() + + Phase + + + + + + 1.0.0 + + + + + + Phase + 1.0.0 + Contains a series of mojo bindings for a given phase of a lifecycle. + + + bindings + 1.0.0 + Collection of mojo bindings for a phase. + + MojoBinding + * + + + + + + LifecycleStep + 1.0.0 + + + + MojoBinding + LifecycleStep + 1.0.0 + A binding of one mojo to one lifecycle phase, possibly including configuration. + + + groupId + true + 1.0.0 + Plugin's groupId. + String + + + artifactId + true + 1.0.0 + Plugin's artifactId. + String + + + version + true + 1.0.0 + Plugin's version. + String + + + goal + true + 1.0.0 + Mojo's goal name. + String + + + executionId + 1.0.0 + default + A name for this mojo binding, for purposes of merging configurations via inheritance, etc. + String + + + 1.0.0 + origin + String + Specific location from which this set of mojo binding was loaded. + + + configuration + 1.0.0 + Mojo binding's configuration. + DOM + + + optional + 1.0.0 + Marks a mojo binding as optional (not required for execution of the lifecycle). + boolean + + + + + diff --git a/maven-lifecycle/src/test/java/org/apache/maven/lifecycle/ClassLoaderXmlBindingLoaderTest.java b/maven-lifecycle/src/test/java/org/apache/maven/lifecycle/ClassLoaderXmlBindingLoaderTest.java new file mode 100644 index 0000000000..3d346bea80 --- /dev/null +++ b/maven-lifecycle/src/test/java/org/apache/maven/lifecycle/ClassLoaderXmlBindingLoaderTest.java @@ -0,0 +1,61 @@ +package org.apache.maven.lifecycle; + +import org.apache.maven.lifecycle.model.CleanBinding; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.lifecycle.model.Phase; +import org.codehaus.plexus.PlexusTestCase; + +import java.util.List; + +public class ClassLoaderXmlBindingLoaderTest + extends PlexusTestCase +{ + + public void testBeanAccess_ParseSingleCleanBinding() + throws LifecycleLoaderException, LifecycleSpecificationException + { + LifecycleBindings bindings = new ClassLoaderXmlBindingLoader( "single-clean-mapping.xml" ).getBindings(); + + CleanBinding cleanBinding = bindings.getCleanBinding(); + assertNotNull( cleanBinding ); + + Phase preClean = cleanBinding.getPreClean(); + assertNotNull( preClean ); + + List mojos = preClean.getBindings(); + assertNotNull( mojos ); + assertEquals( 1, mojos.size() ); + + MojoBinding mojo = (MojoBinding) mojos.get( 0 ); + assertEquals( "group", mojo.getGroupId() ); + assertEquals( "artifact", mojo.getArtifactId() ); + assertNull( mojo.getVersion() ); + assertEquals( "goalOne", mojo.getGoal() ); + } + + public void testComponentAccess_ParseSingleCleanBinding() + throws Exception + { + LifecycleBindingLoader loader = (LifecycleBindingLoader) lookup( LifecycleBindingLoader.ROLE, "single-clean-mapping" ); + + LifecycleBindings bindings = loader.getBindings(); + + CleanBinding cleanBinding = bindings.getCleanBinding(); + assertNotNull( cleanBinding ); + + Phase preClean = cleanBinding.getPreClean(); + assertNotNull( preClean ); + + List mojos = preClean.getBindings(); + assertNotNull( mojos ); + assertEquals( 1, mojos.size() ); + + MojoBinding mojo = (MojoBinding) mojos.get( 0 ); + assertEquals( "group", mojo.getGroupId() ); + assertEquals( "artifact", mojo.getArtifactId() ); + assertNull( mojo.getVersion() ); + assertEquals( "goalOne", mojo.getGoal() ); + } + +} diff --git a/maven-lifecycle/src/test/java/org/apache/maven/lifecycle/LifecycleUtilsTest.java b/maven-lifecycle/src/test/java/org/apache/maven/lifecycle/LifecycleUtilsTest.java new file mode 100644 index 0000000000..31e2b58412 --- /dev/null +++ b/maven-lifecycle/src/test/java/org/apache/maven/lifecycle/LifecycleUtilsTest.java @@ -0,0 +1,1371 @@ +package org.apache.maven.lifecycle; + +import org.apache.maven.lifecycle.model.BuildBinding; +import org.apache.maven.lifecycle.model.CleanBinding; +import org.apache.maven.lifecycle.model.LifecycleBinding; +import org.apache.maven.lifecycle.model.LifecycleBindings; +import org.apache.maven.lifecycle.model.MojoBinding; +import org.apache.maven.lifecycle.model.Phase; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +public class LifecycleUtilsTest + extends TestCase +{ + + public void testSetOrigin_ShouldSetMojoBindingOrigin() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + binding.setOrigin( "original" ); + + LifecycleBindings bindings = new LifecycleBindings(); + bindings.getCleanBinding().getClean().addBinding( binding ); + + LifecycleUtils.setOrigin( bindings, "changed" ); + + assertEquals( "changed", binding.getOrigin() ); + } + + public void testCreateMojoBindingKey_NoExecId() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + String key = LifecycleUtils.createMojoBindingKey( binding, false ); + + assertEquals( "group:artifact:goal", key ); + } + + public void testCreateMojoBindingKey_WithExecId() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + binding.setExecutionId( "execution" ); + + String key = LifecycleUtils.createMojoBindingKey( binding, true ); + + assertEquals( "group:artifact:goal:execution", key ); + } + + public void testFindLifecycleBindingForPhase_ShouldFindMojoBindingInPhase() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + LifecycleBindings bindings = new LifecycleBindings(); + bindings.getCleanBinding().getClean().addBinding( binding ); + + LifecycleBinding result = LifecycleUtils.findLifecycleBindingForPhase( "clean", bindings ); + + assertTrue( result instanceof CleanBinding ); + + CleanBinding cb = (CleanBinding) result; + Phase clean = cb.getClean(); + + assertNotNull( clean ); + assertEquals( 1, clean.getBindings().size() ); + + MojoBinding resultBinding = (MojoBinding) clean.getBindings().get( 0 ); + + assertEquals( "group", resultBinding.getGroupId() ); + assertEquals( "artifact", resultBinding.getArtifactId() ); + assertEquals( "goal", resultBinding.getGoal() ); + } + + public void testFindLifecycleBindingForPhase_ShouldReturnNullForInvalidPhase() + { + LifecycleBindings bindings = new LifecycleBindings(); + + LifecycleBinding result = LifecycleUtils.findLifecycleBindingForPhase( "dud", bindings ); + + assertNull( result ); + } + + public void testFindMatchingMojoBinding_ShouldFindMatchWithoutExecId() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + LifecycleBindings bindings = new LifecycleBindings(); + + BuildBinding bb = new BuildBinding(); + + Phase phase = new Phase(); + phase.addBinding( binding ); + + bb.setCompile( phase ); + + bindings.setBuildBinding( bb ); + + MojoBinding binding2 = LifecycleUtils.findMatchingMojoBinding( binding, bindings, false ); + + assertNotNull( binding2 ); + assertEquals( "goal", binding2.getGoal() ); + assertEquals( "group", binding2.getGroupId() ); + assertEquals( "artifact", binding2.getArtifactId() ); + } + + public void testFindMatchingMojoBinding_ShouldFindMatchWithExecId() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + binding.setExecutionId( "non-default" ); + + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal" ); + binding2.setExecutionId( "non-default" ); + + LifecycleBindings bindings = new LifecycleBindings(); + + BuildBinding bb = new BuildBinding(); + + Phase phase = new Phase(); + phase.addBinding( binding ); + + bb.setCompile( phase ); + + bindings.setBuildBinding( bb ); + + MojoBinding binding3 = LifecycleUtils.findMatchingMojoBinding( binding2, bindings, true ); + + assertNotNull( binding3 ); + assertEquals( "goal", binding3.getGoal() ); + assertEquals( "group", binding3.getGroupId() ); + assertEquals( "artifact", binding3.getArtifactId() ); + assertEquals( "non-default", binding3.getExecutionId() ); + } + + public void testFindMatchingMojoBinding_ShouldReturnNullNoMatchWithoutExecId() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + MojoBinding binding2 = newMojoBinding( "group2", "artifact", "goal" ); + + LifecycleBindings bindings = new LifecycleBindings(); + + BuildBinding bb = new BuildBinding(); + + Phase phase = new Phase(); + phase.addBinding( binding ); + + bb.setCompile( phase ); + + bindings.setBuildBinding( bb ); + + MojoBinding binding3 = LifecycleUtils.findMatchingMojoBinding( binding2, bindings, true ); + + assertNull( binding3 ); + } + + public void testFindMatchingMojoBinding_ShouldReturnNullWhenExecIdsDontMatch() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + // default executionId == 'default' + + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal" ); + binding2.setExecutionId( "execution" ); + + LifecycleBindings bindings = new LifecycleBindings(); + + BuildBinding bb = new BuildBinding(); + + Phase phase = new Phase(); + phase.addBinding( binding ); + + bb.setCompile( phase ); + + bindings.setBuildBinding( bb ); + + MojoBinding binding3 = LifecycleUtils.findMatchingMojoBinding( binding2, bindings, true ); + + assertNull( binding3 ); + } + + public void testRemoveMojoBinding_ThrowErrorWhenPhaseNotInLifecycleBinding() + { + CleanBinding cleanBinding = new CleanBinding(); + + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + String phase = "phase"; + + try + { + LifecycleUtils.removeMojoBinding( phase, binding, cleanBinding, false ); + + fail( "Should fail when phase doesn't exist in lifecycle binding." ); + } + catch ( NoSuchPhaseException e ) + { + // expected + } + } + + public void testRemoveMojoBinding_DoNothingWhenMojoBindingNotInLifecycleBinding() + throws NoSuchPhaseException + { + CleanBinding cleanBinding = new CleanBinding(); + + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal2" ); + + Phase phase = new Phase(); + phase.addBinding( binding2 ); + + cleanBinding.setClean( phase ); + + String phaseName = "clean"; + + LifecycleUtils.removeMojoBinding( phaseName, binding, cleanBinding, false ); + + Phase result = cleanBinding.getClean(); + assertEquals( 1, result.getBindings().size() ); + } + + public void testRemoveMojoBinding_DoNothingWhenMojoPhaseIsNullInLifecycleBinding() + throws NoSuchPhaseException + { + CleanBinding cleanBinding = new CleanBinding(); + + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal2" ); + + Phase phase = new Phase(); + phase.addBinding( binding2 ); + + cleanBinding.setPreClean( phase ); + + String phaseName = "clean"; + + LifecycleUtils.removeMojoBinding( phaseName, binding, cleanBinding, false ); + + Phase result = cleanBinding.getPreClean(); + assertEquals( 1, result.getBindings().size() ); + } + + public void testRemoveMojoBinding_RemoveMojoBindingWhenFoundInLifecycleBinding() + throws NoSuchPhaseException + { + CleanBinding cleanBinding = new CleanBinding(); + + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + Phase phase = new Phase(); + phase.addBinding( binding ); + + cleanBinding.setClean( phase ); + + String phaseName = "clean"; + + LifecycleUtils.removeMojoBinding( phaseName, binding, cleanBinding, false ); + + Phase result = cleanBinding.getClean(); + assertEquals( 0, result.getBindings().size() ); + } + + public void testMergeBindings_SingleMojoCloneFromEachIsPresentInResult_EmptyDefaults() + { + LifecycleBindings bOrig = new LifecycleBindings(); + CleanBinding cbOrig = bOrig.getCleanBinding(); + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + cbOrig.getClean().addBinding( binding ); + + LifecycleBindings bOrig2 = new LifecycleBindings(); + CleanBinding cbOrig2 = bOrig2.getCleanBinding(); + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal2" ); + cbOrig2.getClean().addBinding( binding2 ); + + LifecycleBindings result = LifecycleUtils.mergeBindings( bOrig, bOrig2, new LifecycleBindings(), false, false ); + + assertNotNull( result ); + + CleanBinding cbResult = result.getCleanBinding(); + assertNotSame( cbOrig, cbResult ); + + List mojos = cbResult.getClean().getBindings(); + assertNotNull( mojos ); + assertEquals( 2, mojos.size() ); + + MojoBinding bResult = (MojoBinding) mojos.get( 0 ); + + assertNotSame( binding, bResult ); + + assertEquals( "group", bResult.getGroupId() ); + assertEquals( "artifact", bResult.getArtifactId() ); + assertEquals( "goal", bResult.getGoal() ); + + bResult = (MojoBinding) mojos.get( 1 ); + + assertNotSame( binding2, bResult ); + + assertEquals( "group", bResult.getGroupId() ); + assertEquals( "artifact", bResult.getArtifactId() ); + assertEquals( "goal2", bResult.getGoal() ); + } + + public void testMergeBindings_SingleMojoCloneIsPresentFromExistingAndNotFromDefaultsInResult() + { + LifecycleBindings bOrig = new LifecycleBindings(); + CleanBinding cbOrig = bOrig.getCleanBinding(); + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + cbOrig.getClean().addBinding( binding ); + + LifecycleBindings bOrig2 = new LifecycleBindings(); + CleanBinding cbOrig2 = bOrig2.getCleanBinding(); + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal2" ); + cbOrig2.getClean().addBinding( binding2 ); + + LifecycleBindings result = LifecycleUtils.mergeBindings( bOrig, new LifecycleBindings(), bOrig2, false, false ); + + assertNotNull( result ); + + CleanBinding cbResult = result.getCleanBinding(); + assertNotSame( cbOrig, cbResult ); + + List mojos = cbResult.getClean().getBindings(); + assertNotNull( mojos ); + assertEquals( 1, mojos.size() ); + + MojoBinding bResult = (MojoBinding) mojos.get( 0 ); + + assertNotSame( binding, bResult ); + + assertEquals( "group", bResult.getGroupId() ); + assertEquals( "artifact", bResult.getArtifactId() ); + assertEquals( "goal", bResult.getGoal() ); + } + + public void testMergeBindings_MojoCloneIsPresentFromDefaultsInResult() + { + LifecycleBindings bOrig = new LifecycleBindings(); + CleanBinding cbOrig = bOrig.getCleanBinding(); + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + cbOrig.getClean().addBinding( binding ); + + LifecycleBindings bOrig2 = new LifecycleBindings(); + BuildBinding bbOrig = bOrig2.getBuildBinding(); + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal2" ); + bbOrig.getCompile().addBinding( binding2 ); + + LifecycleBindings result = LifecycleUtils.mergeBindings( bOrig, new LifecycleBindings(), bOrig2, false, false ); + + assertNotNull( result ); + + CleanBinding cbResult = result.getCleanBinding(); + assertNotSame( cbOrig, cbResult ); + + List mojos = cbResult.getClean().getBindings(); + assertNotNull( mojos ); + assertEquals( 1, mojos.size() ); + + MojoBinding bResult = (MojoBinding) mojos.get( 0 ); + + assertNotSame( binding, bResult ); + + assertEquals( "group", bResult.getGroupId() ); + assertEquals( "artifact", bResult.getArtifactId() ); + assertEquals( "goal", bResult.getGoal() ); + + BuildBinding bbResult = result.getBuildBinding(); + assertNotSame( bbOrig, bbResult ); + + mojos = bbResult.getCompile().getBindings(); + assertNotNull( mojos ); + assertEquals( 1, mojos.size() ); + + bResult = (MojoBinding) mojos.get( 0 ); + + assertNotSame( binding, bResult ); + + assertEquals( "group", bResult.getGroupId() ); + assertEquals( "artifact", bResult.getArtifactId() ); + assertEquals( "goal2", bResult.getGoal() ); + } + + public void testMergeBindings_MergeConfigsWithNewAsDominant_EmptyDefaults() + { + LifecycleBindings bOrig = new LifecycleBindings(); + CleanBinding cbOrig = bOrig.getCleanBinding(); + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + binding.setOrigin( "non-default" ); + + Xpp3Dom config = new Xpp3Dom( "configuration" ); + Xpp3Dom child = new Xpp3Dom( "child" ); + child.setValue( "value" ); + config.addChild( child ); + + binding.setConfiguration( config ); + + cbOrig.getClean().addBinding( binding ); + + LifecycleBindings bOrig2 = new LifecycleBindings(); + CleanBinding cbOrig2 = bOrig2.getCleanBinding(); + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal" ); + + Xpp3Dom config2 = new Xpp3Dom( "configuration" ); + + Xpp3Dom child2 = new Xpp3Dom( "child" ); + child2.setValue( "value2" ); + + Xpp3Dom child3 = new Xpp3Dom( "key" ); + child3.setValue( "val" ); + + config2.addChild( child2 ); + config2.addChild( child3 ); + + binding2.setConfiguration( config2 ); + + cbOrig2.getClean().addBinding( binding2 ); + + LifecycleBindings result = LifecycleUtils.mergeBindings( bOrig, bOrig2, new LifecycleBindings(), true, false ); + + assertNotNull( result ); + + CleanBinding cbResult = result.getCleanBinding(); + assertNotSame( cbOrig, cbResult ); + + List mojos = cbResult.getClean().getBindings(); + assertNotNull( mojos ); + assertEquals( 1, mojos.size() ); + + MojoBinding bResult = (MojoBinding) mojos.get( 0 ); + + assertNotSame( binding, bResult ); + + assertEquals( "group", bResult.getGroupId() ); + assertEquals( "artifact", bResult.getArtifactId() ); + assertEquals( "goal", bResult.getGoal() ); + assertEquals( "non-default", bResult.getOrigin() ); + + Xpp3Dom cResult = (Xpp3Dom) bResult.getConfiguration(); + + assertNotNull( cResult ); + assertEquals( "value2", cResult.getChild( "child" ).getValue() ); + assertEquals( "val", cResult.getChild( "key" ).getValue() ); + } + + public void testMergeBindings_MergeConfigsWithExistingAsDominant_EmptyDefaults() + { + LifecycleBindings bOrig = new LifecycleBindings(); + CleanBinding cbOrig = bOrig.getCleanBinding(); + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + binding.setOrigin( "non-default" ); + + Xpp3Dom config = new Xpp3Dom( "configuration" ); + Xpp3Dom child = new Xpp3Dom( "child" ); + child.setValue( "value" ); + config.addChild( child ); + + binding.setConfiguration( config ); + + cbOrig.getClean().addBinding( binding ); + + LifecycleBindings bOrig2 = new LifecycleBindings(); + CleanBinding cbOrig2 = bOrig2.getCleanBinding(); + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal" ); + + Xpp3Dom config2 = new Xpp3Dom( "configuration" ); + + Xpp3Dom child2 = new Xpp3Dom( "child" ); + child2.setValue( "value2" ); + + Xpp3Dom child3 = new Xpp3Dom( "key" ); + child3.setValue( "val" ); + + config2.addChild( child2 ); + config2.addChild( child3 ); + + binding2.setConfiguration( config2 ); + + cbOrig2.getClean().addBinding( binding2 ); + + LifecycleBindings result = LifecycleUtils.mergeBindings( bOrig, bOrig2, new LifecycleBindings(), true, true ); + + assertNotNull( result ); + + CleanBinding cbResult = result.getCleanBinding(); + assertNotSame( cbOrig, cbResult ); + + List mojos = cbResult.getClean().getBindings(); + assertNotNull( mojos ); + assertEquals( 1, mojos.size() ); + + MojoBinding bResult = (MojoBinding) mojos.get( 0 ); + + assertNotSame( binding, bResult ); + + assertEquals( "group", bResult.getGroupId() ); + assertEquals( "artifact", bResult.getArtifactId() ); + assertEquals( "goal", bResult.getGoal() ); + assertEquals( "non-default", bResult.getOrigin() ); + + Xpp3Dom cResult = (Xpp3Dom) bResult.getConfiguration(); + + assertNotNull( cResult ); + assertEquals( "value", cResult.getChild( "child" ).getValue() ); + assertEquals( "val", cResult.getChild( "key" ).getValue() ); + } + + public void testCloneBinding_SingleMojoCloneIsPresentInNewInstance() + { + CleanBinding cbOrig = new CleanBinding(); + MojoBinding bOrig = newMojoBinding( "group", "artifact", "goal" ); + cbOrig.getClean().addBinding( bOrig ); + + LifecycleBinding result = LifecycleUtils.cloneBinding( cbOrig ); + + assertNotNull( result ); + assertTrue( result instanceof CleanBinding ); + assertNotSame( cbOrig, result ); + + List mojos = ( (CleanBinding) result ).getClean().getBindings(); + assertNotNull( mojos ); + assertEquals( 1, mojos.size() ); + + MojoBinding bResult = (MojoBinding) mojos.get( 0 ); + assertNotSame( bOrig, bResult ); + + assertEquals( "group", bResult.getGroupId() ); + assertEquals( "artifact", bResult.getArtifactId() ); + assertEquals( "goal", bResult.getGoal() ); + } + + public void testCloneBinding_OrderIsPreservedBetweenTwoMojoBindingsInNewInstance() + { + CleanBinding cbOrig = new CleanBinding(); + MojoBinding bOrig = newMojoBinding( "group", "artifact", "goal" ); + cbOrig.getClean().addBinding( bOrig ); + + MojoBinding bOrig2 = newMojoBinding( "group", "artifact", "goal2" ); + cbOrig.getClean().addBinding( bOrig2 ); + + LifecycleBinding result = LifecycleUtils.cloneBinding( cbOrig ); + + assertNotNull( result ); + assertTrue( result instanceof CleanBinding ); + assertNotSame( cbOrig, result ); + + List mojos = ( (CleanBinding) result ).getClean().getBindings(); + assertNotNull( mojos ); + assertEquals( 2, mojos.size() ); + + MojoBinding bResult = (MojoBinding) mojos.get( 0 ); + assertNotSame( bOrig, bResult ); + + assertEquals( bOrig.getGroupId(), bResult.getGroupId() ); + assertEquals( bOrig.getArtifactId(), bResult.getArtifactId() ); + assertEquals( bOrig.getGoal(), bResult.getGoal() ); + + MojoBinding bResult2 = (MojoBinding) mojos.get( 1 ); + assertNotSame( bOrig2, bResult2 ); + + assertEquals( bOrig2.getGroupId(), bResult2.getGroupId() ); + assertEquals( bOrig2.getArtifactId(), bResult2.getArtifactId() ); + assertEquals( bOrig2.getGoal(), bResult2.getGoal() ); + } + + public void testCloneBindings_SingleMojoCloneIsPresentInNewInstance() + { + LifecycleBindings bindings = new LifecycleBindings(); + + CleanBinding cbOrig = bindings.getCleanBinding(); + MojoBinding bOrig = newMojoBinding( "group", "artifact", "goal" ); + cbOrig.getClean().addBinding( bOrig ); + + LifecycleBindings result = LifecycleUtils.cloneBindings( bindings ); + + assertNotNull( result ); + assertNotSame( bindings, result ); + + CleanBinding cbResult = result.getCleanBinding(); + + assertNotNull( cbResult ); + assertNotSame( cbOrig, cbResult ); + + List mojos = cbResult.getClean().getBindings(); + assertNotNull( mojos ); + assertEquals( 1, mojos.size() ); + + MojoBinding bResult = (MojoBinding) mojos.get( 0 ); + assertNotSame( bOrig, bResult ); + + assertEquals( "group", bResult.getGroupId() ); + assertEquals( "artifact", bResult.getArtifactId() ); + assertEquals( "goal", bResult.getGoal() ); + } + + public void testCloneBindings_OrderIsPreservedBetweenTwoMojoBindingsInNewInstance() + { + LifecycleBindings bindings = new LifecycleBindings(); + CleanBinding cbOrig = bindings.getCleanBinding(); + MojoBinding bOrig = newMojoBinding( "group", "artifact", "goal" ); + cbOrig.getClean().addBinding( bOrig ); + + MojoBinding bOrig2 = newMojoBinding( "group", "artifact", "goal2" ); + cbOrig.getClean().addBinding( bOrig2 ); + + LifecycleBindings result = LifecycleUtils.cloneBindings( bindings ); + + assertNotNull( result ); + assertNotSame( bindings, result ); + + CleanBinding cbResult = result.getCleanBinding(); + + assertNotNull( cbResult ); + assertNotSame( cbOrig, cbResult ); + + List mojos = cbResult.getClean().getBindings(); + assertNotNull( mojos ); + assertEquals( 2, mojos.size() ); + + MojoBinding bResult = (MojoBinding) mojos.get( 0 ); + assertNotSame( bOrig, bResult ); + + assertEquals( bOrig.getGroupId(), bResult.getGroupId() ); + assertEquals( bOrig.getArtifactId(), bResult.getArtifactId() ); + assertEquals( bOrig.getGoal(), bResult.getGoal() ); + + MojoBinding bResult2 = (MojoBinding) mojos.get( 1 ); + assertNotSame( bOrig2, bResult2 ); + + assertEquals( bOrig2.getGroupId(), bResult2.getGroupId() ); + assertEquals( bOrig2.getArtifactId(), bResult2.getArtifactId() ); + assertEquals( bOrig2.getGoal(), bResult2.getGoal() ); + } + + public void testCloneMojoBinding_NullVersionIsPropagated() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + MojoBinding binding2 = LifecycleUtils.cloneMojoBinding( binding ); + + assertNotNull( binding2 ); + assertEquals( "goal", binding2.getGoal() ); + assertEquals( "group", binding2.getGroupId() ); + assertEquals( "artifact", binding2.getArtifactId() ); + assertNull( binding.getVersion() ); + assertNull( binding2.getVersion() ); + } + + public void testCloneMojoBinding_ExecutionIdIsPropagated() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + binding.setExecutionId( "non-default" ); + + MojoBinding binding2 = LifecycleUtils.cloneMojoBinding( binding ); + + assertNotNull( binding2 ); + assertEquals( "goal", binding2.getGoal() ); + assertEquals( "group", binding2.getGroupId() ); + assertEquals( "artifact", binding2.getArtifactId() ); + assertEquals( "non-default", binding2.getExecutionId() ); + } + + public void testCloneMojoBinding_VersionIsPropagated() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + binding.setVersion( "version" ); + + MojoBinding binding2 = LifecycleUtils.cloneMojoBinding( binding ); + + assertNotNull( binding2 ); + assertEquals( "goal", binding2.getGoal() ); + assertEquals( "group", binding2.getGroupId() ); + assertEquals( "artifact", binding2.getArtifactId() ); + assertEquals( "version", binding2.getVersion() ); + assertEquals( "default", binding2.getExecutionId() ); + } + + public void testAddMojoBinding_LifecycleBinding_AddOneMojoBindingToEmptyLifecycle() + throws NoSuchPhaseException + { + CleanBinding cleanBinding = new CleanBinding(); + + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + LifecycleUtils.addMojoBinding( "clean", binding, cleanBinding ); + + Phase clean = cleanBinding.getClean(); + assertEquals( 1, clean.getBindings().size() ); + } + + public void testAddMojoBinding_LifecycleBinding_ThrowErrorWhenPhaseDoesntExist() + { + CleanBinding cleanBinding = new CleanBinding(); + + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + try + { + LifecycleUtils.addMojoBinding( "compile", binding, cleanBinding ); + + fail( "Should fail because compile phase isn't in the clean lifecycle." ); + } + catch ( NoSuchPhaseException e ) + { + // expected + } + } + + public void testAddMojoBinding_LifecycleBindings_AddOneMojoBindingToEmptyLifecycle() + throws LifecycleSpecificationException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + LifecycleBindings bindings = new LifecycleBindings(); + + LifecycleUtils.addMojoBinding( "clean", binding, bindings ); + + CleanBinding cleanBinding = bindings.getCleanBinding(); + assertNotNull( cleanBinding ); + + Phase clean = cleanBinding.getClean(); + assertNotNull( clean ); + assertEquals( 1, clean.getBindings().size() ); + } + + public void testAddMojoBinding_LifecycleBindings_ThrowErrorWhenPhaseDoesntExist() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + try + { + LifecycleUtils.addMojoBinding( "dud", binding, new LifecycleBindings() ); + + fail( "Should fail because dud phase isn't in the any lifecycle." ); + } + catch ( LifecycleSpecificationException e ) + { + // expected + } + } + + public void testGetMojoBindingListForLifecycle_LifecycleBinding_FailWithInvalidStopPhase() + { + try + { + LifecycleUtils.getMojoBindingListForLifecycle( "dud", new CleanBinding() ); + + fail( "Should fail when asked for an invalid phase." ); + } + catch ( NoSuchPhaseException e ) + { + // expected + } + } + + public void testGetMojoBindingListForLifecycle_LifecycleBinding_RetrieveMojoBindingInStopPhase() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + CleanBinding cleanBinding = new CleanBinding(); + cleanBinding.getClean().addBinding( binding ); + + List result = LifecycleUtils.getMojoBindingListForLifecycle( "clean", cleanBinding ); + + assertNotNull( result ); + assertEquals( 1, result.size() ); + + MojoBinding resultBinding = (MojoBinding) result.get( 0 ); + + assertEquals( "group", resultBinding.getGroupId() ); + assertEquals( "artifact", resultBinding.getArtifactId() ); + assertEquals( "goal", resultBinding.getGoal() ); + } + + public void testGetMojoBindingListForLifecycle_LifecycleBinding_RetrieveMojoBindingInPreviousPhase() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + CleanBinding cleanBinding = new CleanBinding(); + cleanBinding.getPreClean().addBinding( binding ); + + List result = LifecycleUtils.getMojoBindingListForLifecycle( "clean", cleanBinding ); + + assertNotNull( result ); + assertEquals( 1, result.size() ); + + MojoBinding resultBinding = (MojoBinding) result.get( 0 ); + + assertEquals( "group", resultBinding.getGroupId() ); + assertEquals( "artifact", resultBinding.getArtifactId() ); + assertEquals( "goal", resultBinding.getGoal() ); + } + + public void testGetMojoBindingListForLifecycle_LifecycleBinding_RetrieveTwoMojoBindings() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal2" ); + + CleanBinding cleanBinding = new CleanBinding(); + cleanBinding.getClean().addBinding( binding ); + cleanBinding.getPreClean().addBinding( binding2 ); + + List result = LifecycleUtils.getMojoBindingListForLifecycle( "clean", cleanBinding ); + + assertNotNull( result ); + assertEquals( 2, result.size() ); + + MojoBinding resultBinding2 = (MojoBinding) result.get( 0 ); + + assertEquals( "group", resultBinding2.getGroupId() ); + assertEquals( "artifact", resultBinding2.getArtifactId() ); + assertEquals( "goal2", resultBinding2.getGoal() ); + + MojoBinding resultBinding = (MojoBinding) result.get( 1 ); + + assertEquals( "group", resultBinding.getGroupId() ); + assertEquals( "artifact", resultBinding.getArtifactId() ); + assertEquals( "goal", resultBinding.getGoal() ); + } + + public void testGetMojoBindingListForLifecycle_LifecycleBinding_DontRetrieveMojoBindingsInPhaseAfterStopPhase() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal2" ); + MojoBinding binding3 = newMojoBinding( "group", "artifact", "goal3" ); + + CleanBinding cleanBinding = new CleanBinding(); + cleanBinding.getClean().addBinding( binding ); + cleanBinding.getPreClean().addBinding( binding2 ); + cleanBinding.getPostClean().addBinding( binding3 ); + + List result = LifecycleUtils.getMojoBindingListForLifecycle( "clean", cleanBinding ); + + assertNotNull( result ); + assertEquals( 2, result.size() ); + + MojoBinding resultBinding2 = (MojoBinding) result.get( 0 ); + + assertEquals( "group", resultBinding2.getGroupId() ); + assertEquals( "artifact", resultBinding2.getArtifactId() ); + assertEquals( "goal2", resultBinding2.getGoal() ); + + MojoBinding resultBinding = (MojoBinding) result.get( 1 ); + + assertEquals( "group", resultBinding.getGroupId() ); + assertEquals( "artifact", resultBinding.getArtifactId() ); + assertEquals( "goal", resultBinding.getGoal() ); + } + + public void testGetMojoBindingListForLifecycle_LifecycleBindings_RetrieveMojoBindingInStopPhase() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + LifecycleBindings bindings = new LifecycleBindings(); + bindings.getCleanBinding().getClean().addBinding( binding ); + + List result = LifecycleUtils.getMojoBindingListForLifecycle( "clean", bindings ); + + assertNotNull( result ); + assertEquals( 1, result.size() ); + + MojoBinding resultBinding = (MojoBinding) result.get( 0 ); + + assertEquals( "group", resultBinding.getGroupId() ); + assertEquals( "artifact", resultBinding.getArtifactId() ); + assertEquals( "goal", resultBinding.getGoal() ); + } + + public void testGetMojoBindingListForLifecycle_LifecycleBindings_FailWithInvalidStopPhase() + { + try + { + LifecycleUtils.getMojoBindingListForLifecycle( "dud", new LifecycleBindings() ); + + fail( "Should fail when asked for an invalid phase." ); + } + catch ( NoSuchPhaseException e ) + { + // expected + } + } + + public void testGetMojoBindingListForLifecycle_LifecycleBindings_RetrieveMojoBindingInPreviousPhase() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + LifecycleBindings bindings = new LifecycleBindings(); + bindings.getCleanBinding().getPreClean().addBinding( binding ); + + List result = LifecycleUtils.getMojoBindingListForLifecycle( "clean", bindings ); + + assertNotNull( result ); + assertEquals( 1, result.size() ); + + MojoBinding resultBinding = (MojoBinding) result.get( 0 ); + + assertEquals( "group", resultBinding.getGroupId() ); + assertEquals( "artifact", resultBinding.getArtifactId() ); + assertEquals( "goal", resultBinding.getGoal() ); + } + + public void testGetMojoBindingListForLifecycle_LifecycleBindings_RetrieveTwoMojoBindings() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal2" ); + + LifecycleBindings bindings = new LifecycleBindings(); + CleanBinding cleanBinding = bindings.getCleanBinding(); + cleanBinding.getClean().addBinding( binding ); + cleanBinding.getPreClean().addBinding( binding2 ); + + List result = LifecycleUtils.getMojoBindingListForLifecycle( "clean", bindings ); + + assertNotNull( result ); + assertEquals( 2, result.size() ); + + MojoBinding resultBinding2 = (MojoBinding) result.get( 0 ); + + assertEquals( "group", resultBinding2.getGroupId() ); + assertEquals( "artifact", resultBinding2.getArtifactId() ); + assertEquals( "goal2", resultBinding2.getGoal() ); + + MojoBinding resultBinding = (MojoBinding) result.get( 1 ); + + assertEquals( "group", resultBinding.getGroupId() ); + assertEquals( "artifact", resultBinding.getArtifactId() ); + assertEquals( "goal", resultBinding.getGoal() ); + } + + public void testGetMojoBindingListForLifecycle_LifecycleBindings_DontRetrieveMojoBindingsInPhaseAfterStopPhase() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal2" ); + MojoBinding binding3 = newMojoBinding( "group", "artifact", "goal3" ); + + LifecycleBindings bindings = new LifecycleBindings(); + CleanBinding cleanBinding = bindings.getCleanBinding(); + cleanBinding.getClean().addBinding( binding ); + cleanBinding.getPreClean().addBinding( binding2 ); + cleanBinding.getPostClean().addBinding( binding3 ); + + List result = LifecycleUtils.getMojoBindingListForLifecycle( "clean", bindings ); + + assertNotNull( result ); + assertEquals( 2, result.size() ); + + MojoBinding resultBinding2 = (MojoBinding) result.get( 0 ); + + assertEquals( "group", resultBinding2.getGroupId() ); + assertEquals( "artifact", resultBinding2.getArtifactId() ); + assertEquals( "goal2", resultBinding2.getGoal() ); + + MojoBinding resultBinding = (MojoBinding) result.get( 1 ); + + assertEquals( "group", resultBinding.getGroupId() ); + assertEquals( "artifact", resultBinding.getArtifactId() ); + assertEquals( "goal", resultBinding.getGoal() ); + } + + public void testIsMojoBindingPresent_ReturnFalseWhenMojoBindingIsMissing_WithoutExecIdCompare() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + assertFalse( LifecycleUtils.isMojoBindingPresent( binding, new ArrayList(), false ) ); + } + + public void testIsMojoBindingPresent_ReturnFalseWhenMojoBindingIsMissing_WithExecIdCompare() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + assertFalse( LifecycleUtils.isMojoBindingPresent( binding, new ArrayList(), true ) ); + } + + public void testIsMojoBindingPresent_ReturnTrueWhenMojoBindingExecIdDoesntMatch_WithoutExecIdCompare() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + binding.setExecutionId( "non-default" ); + + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal" ); + + List mojos = new ArrayList(); + mojos.add( binding ); + + assertTrue( LifecycleUtils.isMojoBindingPresent( binding2, mojos, false ) ); + } + + public void testIsMojoBindingPresent_ReturnFalseWhenMojoBindingExecIdDoesntMatch_WithExecIdCompare() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + binding.setExecutionId( "non-default" ); + + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal" ); + + List mojos = new ArrayList(); + mojos.add( binding ); + + assertFalse( LifecycleUtils.isMojoBindingPresent( binding2, mojos, true ) ); + } + + public void testFindPhaseForMojoBinding_ReturnNullIfBindingNotFound_WithoutExecIdCompare() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + LifecycleBindings bindings = new LifecycleBindings(); + Phase phase = LifecycleUtils.findPhaseForMojoBinding( binding, bindings, false ); + + assertNull( phase ); + } + + public void testFindPhaseForMojoBinding_ReturnPhaseContainingBinding_WithoutExecIdCompare() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + LifecycleBindings bindings = new LifecycleBindings(); + Phase cleanPhase = bindings.getCleanBinding().getClean(); + cleanPhase.addBinding( binding ); + + Phase phase = LifecycleUtils.findPhaseForMojoBinding( binding, bindings, false ); + + assertNotNull( phase ); + assertSame( cleanPhase, phase ); + } + + public void testFindPhaseForMojoBinding_ReturnPhaseContainingSimilarBindingWithOtherExecId_WithoutExecIdCompare() + { + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal" ); + binding2.setExecutionId( "non-default" ); + + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + LifecycleBindings bindings = new LifecycleBindings(); + Phase cleanPhase = bindings.getCleanBinding().getClean(); + cleanPhase.addBinding( binding ); + + Phase phase = LifecycleUtils.findPhaseForMojoBinding( binding2, bindings, false ); + + assertNotNull( phase ); + assertSame( cleanPhase, phase ); + } + + public void testFindPhaseForMojoBinding_ReturnNullWhenBindingExecIdsDontMatch_WithExecIdCompare() + { + MojoBinding binding2 = newMojoBinding( "group", "artifact", "goal" ); + binding2.setExecutionId( "non-default" ); + + MojoBinding binding = newMojoBinding( "group", "artifact", "goal" ); + + LifecycleBindings bindings = new LifecycleBindings(); + Phase cleanPhase = bindings.getCleanBinding().getClean(); + cleanPhase.addBinding( binding ); + + Phase phase = LifecycleUtils.findPhaseForMojoBinding( binding2, bindings, true ); + + assertNull( phase ); + } + + public void testRemoveMojoBinding_ReturnLifecycleWithoutMojo_WithoutExecIdCompare() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "clean" ); + + MojoBinding binding2 = newMojoBinding( "group", "artifact", "clean" ); + binding2.setExecutionId( "non-default" ); + + CleanBinding cb = new CleanBinding(); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + cb.getClean().addBinding( binding ); + + assertEquals( 1, cb.getClean().getBindings().size() ); + + LifecycleUtils.removeMojoBinding( "clean", binding2, cb, false ); + + assertEquals( 0, cb.getClean().getBindings().size() ); + } + + public void testRemoveMojoBinding_ReturnLifecycleWithoutMojo_WithExecIdCompare() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "clean" ); + binding.setExecutionId( "non-default" ); + + MojoBinding binding2 = newMojoBinding( "group", "artifact", "clean" ); + binding2.setExecutionId( "non-default" ); + + CleanBinding cb = new CleanBinding(); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + cb.getClean().addBinding( binding ); + + assertEquals( 1, cb.getClean().getBindings().size() ); + + LifecycleUtils.removeMojoBinding( "clean", binding2, cb, true ); + + assertEquals( 0, cb.getClean().getBindings().size() ); + } + + public void testRemoveMojoBinding_DontRemoveMojoIfExecIdDoesntMatch_WithExecIdCompare() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "clean" ); + + MojoBinding binding2 = newMojoBinding( "group", "artifact", "clean" ); + binding2.setExecutionId( "non-default" ); + + CleanBinding cb = new CleanBinding(); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + cb.getClean().addBinding( binding ); + + assertEquals( 1, cb.getClean().getBindings().size() ); + + LifecycleUtils.removeMojoBinding( "clean", binding2, cb, true ); + + assertEquals( 1, cb.getClean().getBindings().size() ); + } + + public void testRemoveMojoBinding_FailOnInvalidPhaseName() + { + MojoBinding binding = newMojoBinding( "group", "artifact", "clean" ); + + CleanBinding cb = new CleanBinding(); + + try + { + LifecycleUtils.removeMojoBinding( "dud", binding, cb, false ); + + fail( "Should fail because phase does not exist in the clean lifecycle." ); + } + catch ( NoSuchPhaseException e ) + { + // expected + } + } + + public void testRemoveMojoBindings_LifecycleBinding_RemoveOneMojo_WithoutExecIdCompare() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "clean" ); + + MojoBinding binding2 = newMojoBinding( "group", "artifact", "clean" ); + binding2.setExecutionId( "non-default" ); + + CleanBinding cb = new CleanBinding(); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + cb.getClean().addBinding( binding ); + + assertEquals( 1, cb.getClean().getBindings().size() ); + + LifecycleUtils.removeMojoBindings( Collections.singletonList( binding2 ), cb, false ); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + } + + public void testRemoveMojoBindings_LifecycleBinding_RemoveOneMojo_WithExecIdCompare() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "clean" ); + binding.setExecutionId( "non-default" ); + + MojoBinding binding2 = newMojoBinding( "group", "artifact", "clean" ); + binding2.setExecutionId( "non-default" ); + + CleanBinding cb = new CleanBinding(); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + cb.getClean().addBinding( binding ); + + assertEquals( 1, cb.getClean().getBindings().size() ); + + LifecycleUtils.removeMojoBindings( Collections.singletonList( binding2 ), cb, true ); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + } + + public void testRemoveMojoBindings_LifecycleBinding_RemoveTwoMojos_WithoutExecIdCompare() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "clean" ); + MojoBinding binding2 = newMojoBinding( "group", "artifact", "clean2" ); + MojoBinding binding3 = newMojoBinding( "group", "artifact", "clean" ); + MojoBinding binding4 = newMojoBinding( "group", "artifact", "clean4" ); + + CleanBinding cb = new CleanBinding(); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + cb.getClean().addBinding( binding2 ); + cb.getClean().addBinding( binding3 ); + cb.getClean().addBinding( binding4 ); + + assertEquals( 3, cb.getClean().getBindings().size() ); + + LifecycleUtils.removeMojoBindings( Collections.singletonList( binding ), cb, false ); + + List cleanBindings = cb.getClean().getBindings(); + + assertEquals( 2, cleanBindings.size() ); + + assertEquals( binding2.getGoal(), ( (MojoBinding) cleanBindings.get( 0 ) ).getGoal() ); + assertEquals( binding4.getGoal(), ( (MojoBinding) cleanBindings.get( 1 ) ).getGoal() ); + } + + public void testRemoveMojoBindings_LifecycleBinding_DontRemoveIfNoExecIdMatch_WithExecIdCompare() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "clean" ); + + MojoBinding binding2 = newMojoBinding( "group", "artifact", "clean" ); + binding2.setExecutionId( "non-default" ); + + CleanBinding cb = new CleanBinding(); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + cb.getClean().addBinding( binding ); + + assertEquals( 1, cb.getClean().getBindings().size() ); + + LifecycleUtils.removeMojoBindings( Collections.singletonList( binding2 ), cb, true ); + + assertEquals( 1, cb.getClean().getBindings().size() ); + + } + + public void testRemoveMojoBindings_LifecycleBindings_RemoveOneMojo_WithoutExecIdCompare() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "clean" ); + + MojoBinding binding2 = newMojoBinding( "group", "artifact", "clean" ); + binding2.setExecutionId( "non-default" ); + + LifecycleBindings bindings = new LifecycleBindings(); + CleanBinding cb = bindings.getCleanBinding(); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + cb.getClean().addBinding( binding ); + + assertEquals( 1, cb.getClean().getBindings().size() ); + + LifecycleUtils.removeMojoBindings( Collections.singletonList( binding2 ), bindings, false ); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + } + + public void testRemoveMojoBindings_LifecycleBindings_RemoveOneMojo_WithExecIdCompare() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "clean" ); + binding.setExecutionId( "non-default" ); + + MojoBinding binding2 = newMojoBinding( "group", "artifact", "clean" ); + binding2.setExecutionId( "non-default" ); + + LifecycleBindings bindings = new LifecycleBindings(); + CleanBinding cb = bindings.getCleanBinding(); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + cb.getClean().addBinding( binding ); + + assertEquals( 1, cb.getClean().getBindings().size() ); + + LifecycleUtils.removeMojoBindings( Collections.singletonList( binding2 ), bindings, true ); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + } + + public void testRemoveMojoBindings_LifecycleBindings_RemoveTwoMojos_WithoutExecIdCompare() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "clean" ); + MojoBinding binding2 = newMojoBinding( "group", "artifact", "clean2" ); + MojoBinding binding3 = newMojoBinding( "group", "artifact", "clean" ); + MojoBinding binding4 = newMojoBinding( "group", "artifact", "clean4" ); + + LifecycleBindings bindings = new LifecycleBindings(); + CleanBinding cb = bindings.getCleanBinding(); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + cb.getClean().addBinding( binding2 ); + cb.getClean().addBinding( binding3 ); + cb.getClean().addBinding( binding4 ); + + assertEquals( 3, cb.getClean().getBindings().size() ); + + LifecycleUtils.removeMojoBindings( Collections.singletonList( binding ), bindings, false ); + + List cleanBindings = cb.getClean().getBindings(); + + assertEquals( 2, cleanBindings.size() ); + + assertEquals( binding2.getGoal(), ( (MojoBinding) cleanBindings.get( 0 ) ).getGoal() ); + assertEquals( binding4.getGoal(), ( (MojoBinding) cleanBindings.get( 1 ) ).getGoal() ); + } + + public void testRemoveMojoBindings_LifecycleBindings_DontRemoveIfNoExecIdMatch_WithExecIdCompare() + throws NoSuchPhaseException + { + MojoBinding binding = newMojoBinding( "group", "artifact", "clean" ); + + MojoBinding binding2 = newMojoBinding( "group", "artifact", "clean" ); + binding2.setExecutionId( "non-default" ); + + LifecycleBindings bindings = new LifecycleBindings(); + CleanBinding cb = bindings.getCleanBinding(); + + assertEquals( 0, cb.getClean().getBindings().size() ); + + cb.getClean().addBinding( binding ); + + assertEquals( 1, cb.getClean().getBindings().size() ); + + LifecycleUtils.removeMojoBindings( Collections.singletonList( binding2 ), bindings, true ); + + assertEquals( 1, cb.getClean().getBindings().size() ); + + } + + public void testIsValidPhaseName_ReturnTrueForPhaseInCleanLifecycle() + { + assertTrue( LifecycleUtils.isValidPhaseName( "clean" ) ); + } + + public void testIsValidPhaseName_ReturnTrueForPhaseInBuildLifecycle() + { + assertTrue( LifecycleUtils.isValidPhaseName( "compile" ) ); + } + + public void testIsValidPhaseName_ReturnTrueForPhaseInSiteLifecycle() + { + assertTrue( LifecycleUtils.isValidPhaseName( "site" ) ); + } + + public void testIsValidPhaseName_ReturnFalseForInvalidPhaseName() + { + assertFalse( LifecycleUtils.isValidPhaseName( "dud" ) ); + } + + private MojoBinding newMojoBinding( String groupId, String artifactId, String goal ) + { + MojoBinding binding = new MojoBinding(); + binding.setGroupId( groupId ); + binding.setArtifactId( artifactId ); + binding.setGoal( goal ); + + return binding; + } + +} diff --git a/maven-lifecycle/src/test/resources/META-INF/plexus/components.xml b/maven-lifecycle/src/test/resources/META-INF/plexus/components.xml new file mode 100644 index 0000000000..50133333d2 --- /dev/null +++ b/maven-lifecycle/src/test/resources/META-INF/plexus/components.xml @@ -0,0 +1,12 @@ + + + + org.apache.maven.lifecycle.LifecycleBindingLoader + single-clean-mapping + org.apache.maven.lifecycle.ClassLoaderXmlBindingLoader + + single-clean-mapping.xml + + + + \ No newline at end of file diff --git a/maven-lifecycle/src/test/resources/single-clean-mapping.xml b/maven-lifecycle/src/test/resources/single-clean-mapping.xml new file mode 100644 index 0000000000..fe499aa2ac --- /dev/null +++ b/maven-lifecycle/src/test/resources/single-clean-mapping.xml @@ -0,0 +1,15 @@ + + + + + + + + group + artifact + goalOne + + + + + \ No newline at end of file diff --git a/maven-project/src/main/java/org/apache/maven/context/ProjectScopedContext.java b/maven-project/src/main/java/org/apache/maven/context/ProjectScopedContext.java new file mode 100644 index 0000000000..9bc3b90f96 --- /dev/null +++ b/maven-project/src/main/java/org/apache/maven/context/ProjectScopedContext.java @@ -0,0 +1,14 @@ +package org.apache.maven.context; + +import org.apache.maven.project.MavenProject; + +public class ProjectScopedContext + extends ScopedBuildContext +{ + + public ProjectScopedContext( MavenProject project, BuildContext parentBuildContext ) + { + super( project.getId(), parentBuildContext ); + } + +} diff --git a/maven-project/src/main/java/org/apache/maven/project/MavenProject.java b/maven-project/src/main/java/org/apache/maven/project/MavenProject.java index ba0ee8b57a..7c54d35351 100644 --- a/maven-project/src/main/java/org/apache/maven/project/MavenProject.java +++ b/maven-project/src/main/java/org/apache/maven/project/MavenProject.java @@ -64,6 +64,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.Stack; /** * The concern of the project is provide runtime values based on the model.

@@ -147,6 +148,8 @@ public class MavenProject private Map moduleAdjustments; + private Stack previousExecutionProjects = new Stack(); + public MavenProject() { Model model = new Model(); @@ -1425,11 +1428,16 @@ public Xpp3Dom getReportConfiguration( String pluginGroupId, String pluginArtifa public MavenProject getExecutionProject() { - return executionProject; + return ( executionProject == null ? this : executionProject ); } public void setExecutionProject( MavenProject executionProject ) { + if ( this.executionProject != null ) + { + previousExecutionProjects.push( this.executionProject ); + } + this.executionProject = executionProject; } @@ -1650,4 +1658,16 @@ private void addArtifactPath(Artifact a, List list) throws DependencyResolutionR list.add( file.getPath() ); } } + + public void clearExecutionProject() + { + if ( !previousExecutionProjects.isEmpty() ) + { + executionProject = (MavenProject) previousExecutionProjects.pop(); + } + else + { + executionProject = null; + } + } } diff --git a/pom.xml b/pom.xml index eb5180b9ce..5a1cbf0a84 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ +