diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java index b78f54dc42f..313478ca730 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java @@ -48,6 +48,9 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** *

@@ -76,8 +79,14 @@ public class MojoExecutor @Requirement private ExecutionEventCatapult eventCatapult; + private final Lock aggregatorReadLock; + private final Lock aggregatorWriteLock; + public MojoExecutor() { + ReadWriteLock aggregatorLock = new ReentrantReadWriteLock( true ); + aggregatorReadLock = aggregatorLock.readLock(); + aggregatorWriteLock = aggregatorLock.writeLock(); } public DependencyContext newDependencyContext( MavenSession session, List mojoExecutions ) @@ -197,37 +206,57 @@ private void execute( MavenSession session, MojoExecution mojoExecution, Project } } - List forkedProjects = executeForkedExecutions( mojoExecution, session, projectIndex ); - - ensureDependenciesAreResolved( mojoDescriptor, session, dependencyContext ); - - eventCatapult.fire( ExecutionEvent.Type.MojoStarted, session, mojoExecution ); + // Aggregating mojo executions (possibly) modify all MavenProjects, including those that are currently in use + // by concurrently running mojo executions. To prevent race conditions, an aggregating execution will block + // all other executions until finished. + Lock accquiredAggregatorLock = null; + if ( session.getRequest().getDegreeOfConcurrency() > 1 ) + { + accquiredAggregatorLock = mojoDescriptor.isAggregator() ? aggregatorWriteLock : aggregatorReadLock; + accquiredAggregatorLock.lock(); + } try { + List forkedProjects = executeForkedExecutions( mojoExecution, session, projectIndex ); + + ensureDependenciesAreResolved( mojoDescriptor, session, dependencyContext ); + + eventCatapult.fire( ExecutionEvent.Type.MojoStarted, session, mojoExecution ); + try { - pluginManager.executeMojo( session, mojoExecution ); + try + { + pluginManager.executeMojo( session, mojoExecution ); + } + catch ( MojoFailureException | PluginManagerException | PluginConfigurationException + | MojoExecutionException e ) + { + throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); + } + + eventCatapult.fire( ExecutionEvent.Type.MojoSucceeded, session, mojoExecution ); + } + catch ( LifecycleExecutionException e ) + { + eventCatapult.fire( ExecutionEvent.Type.MojoFailed, session, mojoExecution, e ); + + throw e; } - catch ( MojoFailureException | PluginManagerException | PluginConfigurationException - | MojoExecutionException e ) + finally { - throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); + for ( MavenProject forkedProject : forkedProjects ) + { + forkedProject.setExecutionProject( null ); + } } - - eventCatapult.fire( ExecutionEvent.Type.MojoSucceeded, session, mojoExecution ); - } - catch ( LifecycleExecutionException e ) - { - eventCatapult.fire( ExecutionEvent.Type.MojoFailed, session, mojoExecution, e ); - - throw e; } finally { - for ( MavenProject forkedProject : forkedProjects ) + if ( accquiredAggregatorLock != null ) { - forkedProject.setExecutionProject( null ); + accquiredAggregatorLock.unlock(); } } }