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 bbd25389e2..d251d51950 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
@@ -29,7 +29,6 @@ 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;
@@ -43,6 +42,7 @@ import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.resolver.filter.CumulativeScopeArtifactFilter;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.MavenSession;
+import org.apache.maven.internal.MultilineMessageHelper;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.lifecycle.MissingProjectException;
import org.apache.maven.plugin.BuildPluginManager;
@@ -59,6 +59,8 @@ import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.StringUtils;
import org.eclipse.aether.SessionData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
*
@@ -76,12 +78,14 @@ import org.eclipse.aether.SessionData;
public class MojoExecutor
{
+ private static final Logger LOGGER = LoggerFactory.getLogger( MojoExecutor.class );
+
private final BuildPluginManager pluginManager;
private final MavenPluginManager mavenPluginManager;
private final LifecycleDependencyResolver lifeCycleDependencyResolver;
private final ExecutionEventCatapult eventCatapult;
- private final ReadWriteLock aggregatorLock = new ReentrantReadWriteLock();
+ private final OwnerReentrantReadWriteLock aggregatorLock = new OwnerReentrantReadWriteLock();
private final Provider mojosExecutionStrategy;
@@ -239,7 +243,7 @@ public class MojoExecutor
private class ProjectLock implements AutoCloseable
{
final Lock acquiredAggregatorLock;
- final Lock acquiredProjectLock;
+ final OwnerReentrantLock acquiredProjectLock;
ProjectLock( MavenSession session, MojoDescriptor mojoDescriptor )
{
@@ -249,8 +253,31 @@ public class MojoExecutor
boolean aggregator = mojoDescriptor.isAggregator();
acquiredAggregatorLock = aggregator ? aggregatorLock.writeLock() : aggregatorLock.readLock();
acquiredProjectLock = getProjectLock( session );
- acquiredAggregatorLock.lock();
- acquiredProjectLock.lock();
+ if ( !acquiredAggregatorLock.tryLock() )
+ {
+ Thread owner = aggregatorLock.getOwner();
+ MojoDescriptor ownerMojo = owner != null ? mojos.get( owner ) : null;
+ String str = ownerMojo != null ? " The " + ownerMojo.getId() : "An";
+ String msg = str + " aggregator mojo is already being executed "
+ + "in this parallel build, those kind of mojos require exclusive access to "
+ + "reactor to prevent race conditions. This mojo execution will be blocked "
+ + "until the aggregator mojo is done.";
+ warn( msg );
+ acquiredAggregatorLock.lock();
+ }
+ if ( !acquiredProjectLock.tryLock() )
+ {
+ Thread owner = acquiredProjectLock.getOwner();
+ MojoDescriptor ownerMojo = owner != null ? mojos.get( owner ) : null;
+ String str = ownerMojo != null ? " The " + ownerMojo.getId() : "A";
+ String msg = str + " mojo is already being executed "
+ + "on the project " + session.getCurrentProject().getGroupId()
+ + ":" + session.getCurrentProject().getArtifactId() + ". "
+ + "This mojo execution will be blocked "
+ + "until the mojo is done.";
+ warn( msg );
+ acquiredProjectLock.lock();
+ }
}
else
{
@@ -275,13 +302,13 @@ public class MojoExecutor
}
@SuppressWarnings( { "unchecked", "rawtypes" } )
- private Lock getProjectLock( MavenSession session )
+ private OwnerReentrantLock 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 locks = ( Map ) ((Map) data).computeIfAbsent(
// TODO: ProjectLock.class, l -> new ConcurrentHashMap<>() );
- Map locks = ( Map ) data.get( ProjectLock.class );
+ Map locks = ( Map ) data.get( ProjectLock.class );
// initialize the value if not already done (in case of a concurrent access) to the method
if ( locks == null )
{
@@ -289,7 +316,33 @@ public class MojoExecutor
data.set( ProjectLock.class, null, new ConcurrentHashMap<>() );
locks = ( Map ) data.get( ProjectLock.class );
}
- return locks.computeIfAbsent( session.getCurrentProject(), p -> new ReentrantLock() );
+ return locks.computeIfAbsent( session.getCurrentProject(), p -> new OwnerReentrantLock() );
+ }
+ }
+
+ static class OwnerReentrantLock extends ReentrantLock
+ {
+ @Override
+ public Thread getOwner()
+ {
+ return super.getOwner();
+ }
+ }
+
+ static class OwnerReentrantReadWriteLock extends ReentrantReadWriteLock
+ {
+ @Override
+ public Thread getOwner()
+ {
+ return super.getOwner();
+ }
+ }
+
+ private static void warn( String msg )
+ {
+ for ( String s : MultilineMessageHelper.format( msg ) )
+ {
+ LOGGER.warn( s );
}
}