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 946bffb0a104..a06cd9f40efd 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 @@ -27,6 +27,11 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.inject.Inject; import javax.inject.Named; @@ -50,6 +55,7 @@ import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.StringUtils; +import org.eclipse.aether.SessionData; /** *
@@ -72,6 +78,8 @@ public class MojoExecutor
private final LifecycleDependencyResolver lifeCycleDependencyResolver;
private final ExecutionEventCatapult eventCatapult;
+ private final ReadWriteLock aggregatorLock = new ReentrantReadWriteLock();
+
@Inject
public MojoExecutor(
BuildPluginManager pluginManager,
@@ -202,6 +210,81 @@ private void execute( MavenSession session, MojoExecution mojoExecution, Project
}
}
+ try ( ProjectLock lock = new ProjectLock( session, mojoDescriptor, aggregatorLock ) )
+ {
+ doExecute( session, mojoExecution, projectIndex, dependencyContext );
+ }
+ }
+
+ /**
+ * 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.
+ * We also lock on a given project to forbid a forked lifecycle to be executed concurrently with the project.
+ * TODO: ideally, the builder should take care of the ordering in a smarter way
+ * TODO: and concurrency issues fixed with MNG-7157
+ */
+ private static class ProjectLock implements AutoCloseable
+ {
+ final Lock acquiredAggregatorLock;
+ final Lock acquiredProjectLock;
+
+ ProjectLock( MavenSession session, MojoDescriptor mojoDescriptor, ReadWriteLock aggregatorLock )
+ {
+ if ( session.getRequest().getDegreeOfConcurrency() > 1 )
+ {
+ boolean aggregator = mojoDescriptor.isAggregator();
+ acquiredAggregatorLock = aggregator ? aggregatorLock.writeLock() : aggregatorLock.readLock();
+ acquiredProjectLock = getProjectLock( session );
+ acquiredAggregatorLock.lock();
+ acquiredProjectLock.lock();
+ }
+ else
+ {
+ acquiredAggregatorLock = null;
+ acquiredProjectLock = null;
+ }
+ }
+
+ @Override
+ public void close()
+ {
+ // release the lock in the reverse order of the acquisition
+ if ( acquiredProjectLock != null )
+ {
+ acquiredProjectLock.unlock();
+ }
+ if ( acquiredAggregatorLock != null )
+ {
+ acquiredAggregatorLock.unlock();
+ }
+ }
+
+ @SuppressWarnings( { "unchecked", "rawtypes" } )
+ private Lock getProjectLock( MavenSession session )
+ {
+ SessionData data = session.getRepositorySession().getData();
+ // TODO: when resolver 1.7.3 is released, the code below should be changed to
+ // TODO: Map