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:
+ *
+ *
+ * - report-set-level configuration
+ * - reporting-level configuration
+ * - execution-level configuration
+ * - plugin-level configuration
+ *
+ */
+ 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:
+ *
+ *
+ * - report-set-level configuration
+ * - reporting-level configuration
+ * - execution-level configuration
+ * - plugin-level configuration
+ *
+ */
+ 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):
+ *
+ *
+ * - Search for a plugin that has already been loaded with the specified prefix
+ * - Search for a plugin configured in the POM that has a matching prefix
+ * - Search the pluginGroups specified in the settings.xml for a matching plugin
+ * - Use groupId == org.apache.maven.plugins, and artifactId == maven-<prefix>-plugin,
+ * and try to resolve based on that.
+ *
+ */
+ 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):
+ *
+ *
+ * - Search for a plugin that has already been loaded with the specified prefix
+ * - Search for a plugin configured in the POM that has a matching prefix
+ * - Search the pluginGroups specified in the settings.xml for a matching plugin
+ * - Use groupId == org.apache.maven.plugins, and artifactId == maven-<prefix>-plugin,
+ * and try to resolve based on that.
+ *
+ */
+ 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 @@
+