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 ); } }