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 d4432620eb..e7661d7f32 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,6 +25,7 @@ import org.apache.maven.NoGoalsSpecifiedException;
import org.apache.maven.ProjectBuildFailureException;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
+import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ReactorManager;
import org.apache.maven.lifecycle.binding.MojoBindingFactory;
@@ -62,8 +63,15 @@ import java.util.List;
import java.util.Stack;
/**
+ * Responsible for orchestrating the process of building the ordered list of
+ * steps required to achieve the specified set of tasks passed into Maven, then
+ * executing these mojos in order. This class also manages the various error messages
+ * that may occur during this process, and directing the behavior of the build
+ * according to what's specified in {@link MavenExecutionRequest#getReactorFailureBehavior()}.
+ *
* @author Jason van Zyl
* @author Brett Porter
+ * @author jdcasey
* @version $Id$
* @todo because of aggregation, we ended up with cli-ish stuff in here (like line() and the project logging, without
* much of the event handling)
@@ -92,12 +100,7 @@ public class DefaultLifecycleExecutor
// ----------------------------------------------------------------------
/**
- * Execute a task. Each task may be a phase in the lifecycle or the execution of a mojo.
- *
- * @param session
- * @param reactorManager
- * @param dispatcher
- * @throws MojoFailureException
+ * {@inheritDoc}
*/
public void execute( final MavenSession session,
final ReactorManager reactorManager,
@@ -151,6 +154,16 @@ public class DefaultLifecycleExecutor
dispatcher );
}
+ /**
+ * After the list of goals from {@link MavenSession#getGoals()} is segmented into
+ * contiguous sets of aggregated and non-aggregated mojos and lifecycle phases,
+ * this method is used to execute each task-segment. Its logic has a top-level fork
+ * for each segment, which basically varies the project used to run the execution
+ * according to aggregation needs. If the segment is aggregated, the root project
+ * will be used to construct and execute the mojo bindings. Otherwise, this
+ * method will iterate through each project, and execute all the goals implied
+ * by the current task segment.
+ */
private void executeTaskSegments( final List taskSegments,
final ReactorManager reactorManager,
final MavenSession session,
@@ -216,7 +229,8 @@ public class DefaultLifecycleExecutor
event,
reactorManager,
buildStartTime,
- target );
+ target,
+ true );
}
catch ( MojoFailureException e )
{
@@ -317,7 +331,8 @@ public class DefaultLifecycleExecutor
{
executeGoalAndHandleFailures( binding, session, dispatcher,
event, reactorManager,
- buildStartTime, target );
+ buildStartTime, target,
+ false);
}
catch ( MojoFailureException e )
{
@@ -367,6 +382,17 @@ public class DefaultLifecycleExecutor
}
}
+ /**
+ * Since each project can have its own {@link ClassRealm} instance that inherits
+ * from the core Maven realm, and contains the specific build-extension
+ * components referenced in that project, the lookup realms must be managed for
+ * each project that's used to fire off a mojo execution. This helps ensure
+ * that unsafe {@link PlexusContainer#lookup(String)} and related calls will
+ * have access to these build-extension components.
+ *
+ * This method simply restores the original Maven-core lookup realm when a
+ * project-specific realm is in use.
+ */
private void restoreLookupRealm( ClassRealm oldLookupRealm )
{
if ( oldLookupRealm != null )
@@ -375,32 +401,28 @@ public class DefaultLifecycleExecutor
}
}
+ /**
+ * Since each project can have its own {@link ClassRealm} instance that inherits
+ * from the core Maven realm, and contains the specific build-extension
+ * components referenced in that project, the lookup realms must be managed for
+ * each project that's used to fire off a mojo execution. This helps ensure
+ * that unsafe {@link PlexusContainer#lookup(String)} and related calls will
+ * have access to these build-extension components.
+ *
+ * This method is meant to find a project-specific realm, if one exists, for
+ * use as the lookup realm for unsafe component lookups, using {@link PlexusContainer#setLookupRealm(ClassRealm)}.
+ */
private ClassRealm setProjectLookupRealm( MavenSession session,
MavenProject rootProject )
throws LifecycleExecutionException
{
-// MavenProjectSession projectSession;
-// try
-// {
-// projectSession = session.getProjectSession( rootProject );
-// }
-// catch ( PlexusContainerException e )
-// {
-// throw new LifecycleExecutionException(
-// "Failed to create project-specific session for: "
-// + rootProject.getId(),
-// rootProject, e );
-// }
-// if ( projectSession != null )
-// {
-// return container.setLookupRealm( projectSession.getProjectRealm() );
-// }
-// else
-// {
-// return null;
-// }
+ ClassRealm projectRealm = session.getRealmManager().getProjectRealm( rootProject.getGroupId(), rootProject.getArtifactId(), rootProject.getVersion() );
+
+ if ( projectRealm != null )
+ {
+ return container.setLookupRealm( projectRealm );
+ }
- // TODO: Fix this to use project-level realm!
return container.getLookupRealm();
}
@@ -444,13 +466,22 @@ public class DefaultLifecycleExecutor
return mojoBindings;
}
+ /**
+ * Lookup the plugin containing the referenced mojo, validate that it is
+ * allowed to execute in the current environment (according to whether
+ * it's a direct-invocation-only or aggregator mojo, and the allowAggregators
+ * flag), and execute the mojo. If any of these steps fails, this method will
+ * consult with the {@link ReactorManager} to determine whether the build
+ * should be stopped.
+ */
private void executeGoalAndHandleFailures( final MojoBinding mojoBinding,
final MavenSession session,
final EventDispatcher dispatcher,
final String event,
final ReactorManager rm,
final long buildStartTime,
- final String target )
+ final String target,
+ boolean allowAggregators )
throws LifecycleExecutionException, MojoFailureException
{
MavenProject project = session.getCurrentProject();
@@ -487,6 +518,9 @@ public class DefaultLifecycleExecutor
}
MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( mojoBinding.getGoal() );
+
+ validateMojoExecution( mojoBinding, mojoDescriptor, project, allowAggregators );
+
MojoExecution mojoExecution = new MojoExecution( mojoDescriptor );
mojoExecution.setConfiguration( (Xpp3Dom) mojoBinding.getConfiguration() );
@@ -546,6 +580,90 @@ public class DefaultLifecycleExecutor
}
}
+ /**
+ * Verify that the specified {@link MojoBinding} is legal for execution under
+ * the current circumstances. Currently, this mainly checks that aggregator
+ * mojos and direct-invocation-only mojos are not bound to lifecycle phases.
+ *
+ * If an invalid mojo is detected, and it is brought in via the user's POM
+ * (this will be checked using {@link MojoBinding#POM_ORIGIN} and {@link MojoBinding#getOrigin()}),
+ * then a {@link LifecycleExecutionException} will be thrown. Otherwise, the mojo
+ * was brought in via a lifecycle mapping or overlay, or as part of a forked execution.
+ * In these cases, the error will be logged to the console, using the ERROR log-level (since the
+ * user cannot fix this sort of problem easily).
+ */
+ private void validateMojoExecution( MojoBinding mojoBinding,
+ MojoDescriptor mojoDescriptor,
+ MavenProject project,
+ boolean allowAggregators )
+ throws LifecycleExecutionException
+ {
+ if ( mojoDescriptor.isAggregator() && !allowAggregators )
+ {
+ if ( MojoBinding.POM_ORIGIN.equals( mojoBinding.getOrigin() ) )
+ {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append( "\n\nDEPRECATED: Binding aggregator mojos to lifecycle phases in the POM is considered dangerous." );
+ buffer.append( "\nThis feature has been deprecated. Please adjust your POM files accordingly." );
+ buffer.append( "\n\nOffending mojo:\n\n" );
+ buffer.append( MojoBindingUtils.toString( mojoBinding ) );
+ buffer.append( "\n\nProject: " ).append( project.getId() );
+ buffer.append( "\nPOM File: " ).append( String.valueOf( project.getFile() ) );
+ buffer.append( "\n" );
+
+ getLogger().warn( buffer.toString() );
+ }
+ else
+ {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append( "\n\nDEPRECATED: An aggregator mojo has been bound to your project's build lifecycle." );
+ buffer.append( "\nThis feature is dangerous, and has been deprecated." );
+ buffer.append( "\n\nOffending mojo:\n\n" );
+ buffer.append( MojoBindingUtils.toString( mojoBinding ) );
+ buffer.append( "\n\nDirect binding of aggregator mojos to the lifecycle is not allowed, but this binding was not configured from within in your POM." );
+ buffer.append( "\n\nIts origin was: " ).append( mojoBinding.getOrigin() );
+ if ( mojoBinding.getOriginDescription() != null )
+ {
+ buffer.append( " (" ).append( mojoBinding.getOriginDescription() ).append( ")" );
+ }
+
+ buffer.append( "\n" );
+
+ getLogger().warn( buffer.toString() );
+ }
+ }
+ else if ( mojoDescriptor.isDirectInvocationOnly() && !MojoBinding.DIRECT_INVOCATION_ORIGIN.equals( mojoBinding.getOrigin() ) )
+ {
+ if ( MojoBinding.POM_ORIGIN.equals( mojoBinding.getOrigin() ) )
+ {
+ throw new LifecycleExecutionException( "Mojo:\n\n" + MojoBindingUtils.toString( mojoBinding ) + "\n\ncan only be invoked directly by the user. Binding it to lifecycle phases in the POM is not allowed.", project );
+ }
+ else
+ {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append( "\n\nSKIPPING execution of mojo:\n\n" ).append( MojoBindingUtils.toString( mojoBinding ) );
+ buffer.append( "\n\nIt specifies direct-invocation only, but has been bound to the build lifecycle." );
+ buffer.append( "\n\nDirect-invocation mojos can only be called by the user. This binding was not configured from within in your POM." );
+ buffer.append( "\n\nIts origin was: " ).append( mojoBinding.getOrigin() );
+ if ( mojoBinding.getOriginDescription() != null )
+ {
+ buffer.append( " (" ).append( mojoBinding.getOriginDescription() ).append( ")" );
+ }
+
+ buffer.append( "\n" );
+
+ getLogger().error( buffer.toString() );
+ }
+ }
+ }
+
+ /**
+ * In the event that an error occurs during executeGoalAndHandleFailure(..),
+ * this method is called to handle logging the error in the {@link ReactorManager},
+ * then determining (again, from the reactor-manager) whether to stop the build.
+ *
+ * @return true if the build should stop, false otherwise.
+ */
private boolean handleExecutionFailure( final ReactorManager rm,
final MavenProject project,
final Exception e,
@@ -571,6 +689,9 @@ public class DefaultLifecycleExecutor
return false;
}
+ /**
+ * {@inheritDoc}
+ */
public TaskValidationResult isTaskValid( String task,
MavenSession session,
MavenProject rootProject )
@@ -636,6 +757,15 @@ public class DefaultLifecycleExecutor
return new TaskValidationResult();
}
+ /**
+ * Split up the list of goals from {@link MavenSession#getGoals()} according
+ * to aggregation needs. Each adjacent goal in the list is included in a single
+ * task segment. When the next goal references a different type of mojo or
+ * lifecycle phase (eg. previous goal wasn't an aggregator, but next one is...or the reverse),
+ * a new task segment is started and the new goal is added to that.
+ *
+ * @return the list of task-segments, each flagged according to aggregation needs.
+ */
private List segmentTaskListByAggregationNeeds( final List tasks,
final MavenSession session,
final MavenProject rootProject )
@@ -735,6 +865,12 @@ public class DefaultLifecycleExecutor
return segments;
}
+ /**
+ * Retrieve the {@link MojoDescriptor} that corresponds to a given direct mojo
+ * invocation. This is used during the fail-fast method isTaskValid(..), and also
+ * during task-segmentation, to allow the lifecycle executor to determine whether
+ * the mojo is an aggregator.
+ */
private MojoDescriptor getMojoDescriptorForDirectInvocation( String task,
MavenSession session,
MavenProject project )
diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutor.java b/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutor.java
index 3ae1997224..4b0a8921e3 100644
--- a/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutor.java
+++ b/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutor.java
@@ -20,21 +20,39 @@ package org.apache.maven.lifecycle;
*/
import org.apache.maven.BuildFailureException;
+import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ReactorManager;
import org.apache.maven.monitor.event.EventDispatcher;
import org.apache.maven.project.MavenProject;
/**
+ * Responsible for orchestrating the process of building the ordered list of
+ * steps required to achieve the specified set of tasks passed into Maven, then
+ * executing these mojos in order. This class also manages the various error messages
+ * that may occur during this process, and directing the behavior of the build
+ * according to what's specified in {@link MavenExecutionRequest#getReactorFailureBehavior()}.
+ *
* @author Jason van Zyl
+ * @author jdcasey
* @version $Id$
*/
public interface LifecycleExecutor
{
String ROLE = LifecycleExecutor.class.getName();
+ /**
+ * Provides a fail-fast way to check that all goals specified in {@link MavenExecutionRequest#getGoals()}
+ * or {@link MavenSession#getGoals()} is valid.
+ */
TaskValidationResult isTaskValid( String task, MavenSession session, MavenProject rootProject );
+ /**
+ * Order and execute mojos associated with the current set of projects in the
+ * reactor. Specific lifecycle phases and mojo invocations that determine what
+ * phases and mojos this method will attempt to execute are provided in {@link MavenSession#getGoals()},
+ * which is populated from {@link MavenExecutionRequest#getGoals()}.
+ */
void execute( MavenSession session, ReactorManager rm, EventDispatcher dispatcher )
throws LifecycleExecutionException, BuildFailureException;
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
index 1379e78d8c..79d094fc73 100644
--- 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
@@ -189,7 +189,7 @@ public class DefaultLifecycleBindingManager
mojoBinding.setGoal( goal );
mojoBinding.setConfiguration( BindingUtils.mergeConfigurations( plugin, execution ) );
mojoBinding.setExecutionId( execution.getId() );
- mojoBinding.setOrigin( "POM" );
+ mojoBinding.setOrigin( MojoBinding.POM_ORIGIN );
logger.debug( "Mojo: " + MojoBindingUtils.toString( mojoBinding ) + ": determining binding phase." );
@@ -384,7 +384,8 @@ public class DefaultLifecycleBindingManager
}
binding.setConfiguration( configuration );
- binding.setOrigin( lifecycleId );
+ binding.setOrigin( MojoBinding.LIFECYCLE_MAPPING_ORIGIN );
+ binding.setOriginDescription( "Lifecycle overlay: " + lifecycleId );
LifecycleUtils.addMojoBinding( phase.getId(), binding, bindings );
phaseBindings.add( binding );
@@ -572,7 +573,7 @@ public class DefaultLifecycleBindingManager
binding.setVersion( pluginDescriptor.getVersion() );
binding.setGoal( mojoDescriptor.getGoal() );
binding.setExecutionId( id );
- binding.setOrigin( "POM" );
+ binding.setOrigin( MojoBinding.POM_ORIGIN );
BindingUtils.injectProjectConfiguration( binding, project );
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
index cc270486a4..b09cf8c250 100644
--- 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
@@ -132,6 +132,7 @@ public final class BuildPlanUtils
if ( extendedInfo )
{
listing.append( "\nOrigin: " ).append( binding.getOrigin() );
+ listing.append( "\nOrigin Description: " ).append( binding.getOriginDescription() );
listing.append( "\nConfiguration:\n\t" ).append(
String.valueOf( binding.getConfiguration() ).replaceAll(
"\\n",
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
index 24f31c6ab4..c4035297c8 100644
--- 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
@@ -126,6 +126,10 @@ public class DefaultBuildPlanner
project,
session,
true );
+
+ binding.setOrigin( MojoBinding.DIRECT_INVOCATION_ORIGIN );
+ binding.setOriginDescription( "Original reference from user: " + task );
+
plan.addDirectInvocationBinding( task, binding );
}
}
@@ -469,7 +473,8 @@ public class DefaultBuildPlanner
executeGoal,
project );
- binding.setOrigin( "Forked from " + referencingGoal );
+ binding.setOrigin( MojoBinding.FORKED_DIRECT_REFERENCE_ORIGIN );
+ binding.setOriginDescription( "Forked from: " + MojoBindingUtils.toString( mojoBinding ) );
plan.addForkedExecution( mojoBinding, Collections.singletonList( binding ) );
}
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
index 0673c359b6..bdbdcf1b39 100644
--- 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
@@ -5,9 +5,9 @@ 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
{
@@ -16,8 +16,6 @@ public final class StateManagementUtils
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";
@@ -46,7 +44,7 @@ public final class StateManagementUtils
binding.setArtifactId( ARTIFACT_ID );
binding.setVersion( VERSION );
binding.setGoal( START_FORKED_EXECUTION_GOAL );
- binding.setOrigin( ORIGIN );
+ binding.setOrigin( MojoBinding.INTERNAL_ORIGIN );
CURRENT_FORK_ID = (int) System.currentTimeMillis();
@@ -73,7 +71,7 @@ public final class StateManagementUtils
binding.setArtifactId( ARTIFACT_ID );
binding.setVersion( VERSION );
binding.setGoal( END_FORKED_EXECUTION_GOAL );
- binding.setOrigin( ORIGIN );
+ binding.setOrigin( MojoBinding.INTERNAL_ORIGIN );
Xpp3Dom config = new Xpp3Dom( "configuration" );
Xpp3Dom forkId = new Xpp3Dom( "forkId" );
@@ -98,7 +96,7 @@ public final class StateManagementUtils
binding.setArtifactId( ARTIFACT_ID );
binding.setVersion( VERSION );
binding.setGoal( CLEAR_FORKED_EXECUTION_GOAL );
- binding.setOrigin( ORIGIN );
+ binding.setOrigin( MojoBinding.INTERNAL_ORIGIN );
Xpp3Dom config = new Xpp3Dom( "configuration" );
Xpp3Dom forkId = new Xpp3Dom( "forkId" );
@@ -156,7 +154,7 @@ public final class StateManagementUtils
binding.setArtifactId( ARTIFACT_ID );
binding.setVersion( VERSION );
binding.setGoal( RESOLVE_LATE_BOUND_PLUGIN_GOAL );
- binding.setOrigin( ORIGIN );
+ binding.setOrigin( MojoBinding.INTERNAL_ORIGIN );
Xpp3Dom config = new Xpp3Dom( "configuration" );
Xpp3Dom param = new Xpp3Dom( "groupId" );
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 ab50dea925..ea2081438a 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
@@ -845,13 +845,25 @@ public class DefaultPluginManager
if ( param.getDeprecated() != null )
{
boolean warnOfDeprecation = false;
- if ( extractedMojoConfiguration.getChild( param.getName() ) != null )
+ PlexusConfiguration child = extractedMojoConfiguration.getChild( param.getName() );
+ try
{
- warnOfDeprecation = true;
+ if ( ( child != null ) && ( child.getValue() != null ) )
+ {
+ warnOfDeprecation = true;
+ }
+ else if ( param.getAlias() != null)
+ {
+ child = extractedMojoConfiguration.getChild( param.getAlias() );
+ if ( ( child != null ) && ( child.getValue() != null ) )
+ {
+ warnOfDeprecation = true;
+ }
+ }
}
- else if ( ( param.getAlias() != null ) && ( extractedMojoConfiguration.getChild( param.getAlias() ) != null ) )
+ catch ( PlexusConfigurationException e )
{
- warnOfDeprecation = true;
+ // forget it, this is just for deprecation checking, after all...
}
if ( warnOfDeprecation )
diff --git a/maven-lifecycle/src/main/mdo/maven-lifecycle.mdo b/maven-lifecycle/src/main/mdo/maven-lifecycle.mdo
index 0e85d19b24..0a398212d2 100644
--- a/maven-lifecycle/src/main/mdo/maven-lifecycle.mdo
+++ b/maven-lifecycle/src/main/mdo/maven-lifecycle.mdo
@@ -547,17 +547,8 @@
-
- LifecycleStep
- 1.0.0
-
-
MojoBinding
- LifecycleStep
1.0.0
A binding of one mojo to one lifecycle phase, possibly including configuration.
@@ -600,12 +591,6 @@
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
@@ -623,8 +608,63 @@
1.0.0