[MNG-7476] Display a warning when an aggregator mojo is locking other mojos executions

This commit is contained in:
Guillaume Nodet 2022-05-16 11:26:49 +02:00
parent 0b0a96782e
commit 0a94ff769c
1 changed files with 63 additions and 10 deletions

View File

@ -24,6 +24,7 @@ import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.resolver.filter.CumulativeScopeArtifactFilter; import org.apache.maven.artifact.resolver.filter.CumulativeScopeArtifactFilter;
import org.apache.maven.execution.ExecutionEvent; import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.MavenSession; import org.apache.maven.execution.MavenSession;
import org.apache.maven.internal.MultilineMessageHelper;
import org.apache.maven.lifecycle.LifecycleExecutionException; import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.lifecycle.MissingProjectException; import org.apache.maven.lifecycle.MissingProjectException;
import org.apache.maven.plugin.BuildPluginManager; import org.apache.maven.plugin.BuildPluginManager;
@ -44,6 +45,8 @@ import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.util.StringUtils; import org.codehaus.plexus.util.StringUtils;
import org.eclipse.aether.SessionData; import org.eclipse.aether.SessionData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -56,7 +59,6 @@ import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
@ -75,6 +77,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
public class MojoExecutor public class MojoExecutor
{ {
private static final Logger LOGGER = LoggerFactory.getLogger( MojoExecutor.class );
@Requirement @Requirement
private BuildPluginManager pluginManager; private BuildPluginManager pluginManager;
@ -87,7 +91,7 @@ public class MojoExecutor
@Requirement @Requirement
private ExecutionEventCatapult eventCatapult; private ExecutionEventCatapult eventCatapult;
private final ReadWriteLock aggregatorLock = new ReentrantReadWriteLock(); private final OwnerReentrantReadWriteLock aggregatorLock = new OwnerReentrantReadWriteLock();
@Requirement @Requirement
private PlexusContainer container; private PlexusContainer container;
@ -244,7 +248,7 @@ public class MojoExecutor
private class ProjectLock implements AutoCloseable private class ProjectLock implements AutoCloseable
{ {
final Lock acquiredAggregatorLock; final Lock acquiredAggregatorLock;
final Lock acquiredProjectLock; final OwnerReentrantLock acquiredProjectLock;
ProjectLock( MavenSession session, MojoDescriptor mojoDescriptor ) ProjectLock( MavenSession session, MojoDescriptor mojoDescriptor )
{ {
@ -254,8 +258,31 @@ public class MojoExecutor
boolean aggregator = mojoDescriptor.isAggregator(); boolean aggregator = mojoDescriptor.isAggregator();
acquiredAggregatorLock = aggregator ? aggregatorLock.writeLock() : aggregatorLock.readLock(); acquiredAggregatorLock = aggregator ? aggregatorLock.writeLock() : aggregatorLock.readLock();
acquiredProjectLock = getProjectLock( session ); acquiredProjectLock = getProjectLock( session );
acquiredAggregatorLock.lock(); if ( !acquiredAggregatorLock.tryLock() )
acquiredProjectLock.lock(); {
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 else
{ {
@ -280,10 +307,10 @@ public class MojoExecutor
} }
@SuppressWarnings( { "unchecked", "rawtypes" } ) @SuppressWarnings( { "unchecked", "rawtypes" } )
private Lock getProjectLock( MavenSession session ) private OwnerReentrantLock getProjectLock( MavenSession session )
{ {
SessionData data = session.getRepositorySession().getData(); SessionData data = session.getRepositorySession().getData();
ConcurrentMap<MavenProject, Lock> locks = ( ConcurrentMap ) data.get( ProjectLock.class ); ConcurrentMap<MavenProject, OwnerReentrantLock> locks = ( ConcurrentMap ) data.get( ProjectLock.class );
// initialize the value if not already done (in case of a concurrent access) to the method // initialize the value if not already done (in case of a concurrent access) to the method
if ( locks == null ) if ( locks == null )
{ {
@ -291,11 +318,11 @@ public class MojoExecutor
data.set( ProjectLock.class, null, new ConcurrentHashMap<>() ); data.set( ProjectLock.class, null, new ConcurrentHashMap<>() );
locks = ( ConcurrentMap ) data.get( ProjectLock.class ); locks = ( ConcurrentMap ) data.get( ProjectLock.class );
} }
Lock acquiredProjectLock = locks.get( session.getCurrentProject() ); OwnerReentrantLock acquiredProjectLock = locks.get( session.getCurrentProject() );
if ( acquiredProjectLock == null ) if ( acquiredProjectLock == null )
{ {
acquiredProjectLock = new ReentrantLock(); acquiredProjectLock = new OwnerReentrantLock();
Lock prev = locks.putIfAbsent( session.getCurrentProject(), acquiredProjectLock ); OwnerReentrantLock prev = locks.putIfAbsent( session.getCurrentProject(), acquiredProjectLock );
if ( prev != null ) if ( prev != null )
{ {
acquiredProjectLock = prev; acquiredProjectLock = prev;
@ -305,6 +332,32 @@ public class MojoExecutor
} }
} }
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 );
}
}
private void doExecute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex, private void doExecute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex,
DependencyContext dependencyContext ) DependencyContext dependencyContext )
throws LifecycleExecutionException throws LifecycleExecutionException