[MNG-4642] Extended MojoDescriptor with threadSafe attribute

Added support for detecting @threadSafe annotation and a nice noisy, warning message.

git-svn-id: https://svn.apache.org/repos/asf/maven/maven-3/trunk@938443 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Kristian Rosenvold 2010-04-27 13:07:17 +00:00
parent f46dc2a538
commit 1a7361596b
8 changed files with 152 additions and 52 deletions

View File

@ -14,10 +14,7 @@
*/
package org.apache.maven.lifecycle;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.*;
import org.apache.maven.lifecycle.internal.BuildListCalculator;
import org.apache.maven.lifecycle.internal.ConcurrencyDependencyGraph;
import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;

View File

@ -20,16 +20,10 @@ package org.apache.maven.lifecycle;
*/
import org.apache.maven.lifecycle.internal.ExecutionPlanItem;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.MojoExecution;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
//TODO: lifecycles being executed
//TODO: what runs in each phase
@ -196,6 +190,21 @@ public class MavenExecutionPlan
return result;
}
public Set<Plugin> getNonThreadSafePlugins()
{
Set<Plugin> plugins = new HashSet<Plugin>();
for ( ExecutionPlanItem executionPlanItem : planItem )
{
final MojoExecution mojoExecution = executionPlanItem.getMojoExecution();
if ( !mojoExecution.getMojoDescriptor().isThreadSafe() )
{
plugins.add( mojoExecution.getPlugin() );
}
}
return plugins;
}
// Used by m2e but will be removed, really.
@SuppressWarnings({"UnusedDeclaration"})

View File

@ -24,13 +24,19 @@ import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.lifecycle.LifecycleNotFoundException;
import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException;
import org.apache.maven.lifecycle.MavenExecutionPlan;
import org.apache.maven.plugin.*;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.InvalidPluginDescriptorException;
import org.apache.maven.plugin.MojoNotFoundException;
import org.apache.maven.plugin.PluginDescriptorParsingException;
import org.apache.maven.plugin.PluginNotFoundException;
import org.apache.maven.plugin.PluginResolutionException;
import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
import org.apache.maven.plugin.version.PluginVersionResolutionException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
import java.util.Set;
@ -56,18 +62,24 @@ public class BuilderCommon
@Requirement
private ExecutionEventCatapult eventCatapult;
@Requirement
private Logger logger;
@SuppressWarnings({"UnusedDeclaration"})
public BuilderCommon()
{
}
public BuilderCommon( LifecycleDebugLogger lifecycleDebugLogger,
LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator,
LifecycleDependencyResolver lifecycleDependencyResolver )
public BuilderCommon(LifecycleDebugLogger lifecycleDebugLogger,
LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator,
LifecycleDependencyResolver lifecycleDependencyResolver, Logger logger)
{
this.lifecycleDebugLogger = lifecycleDebugLogger;
this.lifeCycleExecutionPlanCalculator = lifeCycleExecutionPlanCalculator;
this.lifecycleDependencyResolver = lifecycleDependencyResolver;
this.logger = logger;
}
public MavenExecutionPlan resolveBuildPlan( MavenSession session, MavenProject project, TaskSegment taskSegment,
@ -81,6 +93,29 @@ public class BuilderCommon
lifeCycleExecutionPlanCalculator.calculateExecutionPlan( session, project, taskSegment.getTasks() );
lifecycleDebugLogger.debugProjectPlan( project, executionPlan );
if ( session.getRequest().isThreadConfigurationPresent() )
{
final Set<Plugin> unsafePlugins = executionPlan.getNonThreadSafePlugins();
if ( !unsafePlugins.isEmpty() )
{
logger.warn( "*****************************************************************" );
logger.warn( "* Your build is requesting parallel execution, but project *" );
logger.warn( "* contains the following plugin(s) that are not marked as *" );
logger.warn( "* @threadSafe to support parallel building. *" );
logger.warn( "* While this /may/ work fine, please look for plugin updates *" );
logger.warn( "* and/or request plugins be made thread-safe. *" );
logger.warn( "* If reporting an issue, report it against the plugin in *" );
logger.warn( "* question, not against maven-core *" );
logger.warn( "*****************************************************************" );
logger.warn( "The following plugins are not marked @threadSafe in " + project.getName() + ":" );
for ( Plugin unsafePlugin : unsafePlugins )
{
logger.warn( unsafePlugin.getId() );
}
logger.warn( "*****************************************************************" );
}
}
// TODO: once we have calculated the build plan then we should accurately be able to download
// the project dependencies. Having it happen in the plugin manager is a tangled mess. We can optimize
// this later by looking at the build plan. Would be better to just batch download everything required

View File

@ -19,9 +19,11 @@ import junit.framework.TestCase;
import org.apache.maven.lifecycle.internal.ExecutionPlanItem;
import org.apache.maven.lifecycle.internal.stub.DefaultLifecyclesStub;
import org.apache.maven.lifecycle.internal.stub.LifecycleExecutionPlanCalculatorStub;
import org.apache.maven.model.Plugin;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* @author Kristian Rosenvold
@ -34,7 +36,7 @@ public class MavenExecutionPlanTest
{
final List<Scheduling> cycles = DefaultLifecyclesStub.getSchedulingList();
final Schedule schedule = cycles.get( 0 ).getSchedules().get( 0 );
assertNotNull( schedule);
assertNotNull( schedule );
}
@ -59,13 +61,25 @@ public class MavenExecutionPlanTest
assertNotNull( expected );
}
public void testThreadSafeMojos()
throws Exception
{
MavenExecutionPlan plan = LifecycleExecutionPlanCalculatorStub.getProjectAExceutionPlan();
final Set<Plugin> unSafePlugins = plan.getNonThreadSafePlugins();
// There is only a single threadsafe plugin here...
assertEquals( plan.size() - 1, unSafePlugins.size() );
}
public void testFindLastWhenFirst()
throws Exception
{
MavenExecutionPlan plan = LifecycleExecutionPlanCalculatorStub.getProjectAExceutionPlan();
ExecutionPlanItem beerPhase = plan.findLastInPhase( LifecycleExecutionPlanCalculatorStub.VALIDATE.getPhase()); // Beer comes straight after package in stub
assertNull ( beerPhase);
ExecutionPlanItem beerPhase = plan.findLastInPhase(
LifecycleExecutionPlanCalculatorStub.VALIDATE.getPhase() ); // Beer comes straight after package in stub
assertNull( beerPhase );
}
public void testFindLastInPhaseMisc()
@ -73,8 +87,8 @@ public class MavenExecutionPlanTest
{
MavenExecutionPlan plan = LifecycleExecutionPlanCalculatorStub.getProjectAExceutionPlan();
assertNull( plan.findLastInPhase( "pacXkage" ));
// Beer comes straight after package in stub, much like real life.
assertNotNull( plan.findLastInPhase( LifecycleExecutionPlanCalculatorStub.INITIALIZE.getPhase()));
assertNull( plan.findLastInPhase( "pacXkage" ) );
// Beer comes straight after package in stub, much like real life.
assertNotNull( plan.findLastInPhase( LifecycleExecutionPlanCalculatorStub.INITIALIZE.getPhase() ) );
}
}

View File

@ -70,7 +70,8 @@ public class BuilderCommonTest
final LifecycleDebugLogger logger = new LifecycleDebugLogger( new LoggerStub() );
final LifecycleDependencyResolver lifecycleDependencyResolver =
new LifecycleDependencyResolver( new ProjectDependenciesResolverStub(), new LoggerStub() );
return new BuilderCommon( logger, new LifecycleExecutionPlanCalculatorStub(), lifecycleDependencyResolver );
return new BuilderCommon( logger, new LifecycleExecutionPlanCalculatorStub(), lifecycleDependencyResolver,
new LoggerStub() );
}
}

View File

@ -20,7 +20,14 @@ import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.LifecycleNotFoundException;
import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException;
import org.apache.maven.lifecycle.internal.stub.*;
import org.apache.maven.lifecycle.internal.stub.CompletionServiceStub;
import org.apache.maven.lifecycle.internal.stub.ExecutionEventCatapultStub;
import org.apache.maven.lifecycle.internal.stub.LifecycleExecutionPlanCalculatorStub;
import org.apache.maven.lifecycle.internal.stub.LifecycleTaskSegmentCalculatorStub;
import org.apache.maven.lifecycle.internal.stub.LoggerStub;
import org.apache.maven.lifecycle.internal.stub.MojoExecutorStub;
import org.apache.maven.lifecycle.internal.stub.ProjectDependenciesResolverStub;
import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub;
import org.apache.maven.plugin.InvalidPluginDescriptorException;
import org.apache.maven.plugin.MojoNotFoundException;
import org.apache.maven.plugin.PluginDescriptorParsingException;
@ -127,6 +134,7 @@ public class LifecycleWeaveBuilderTest
final LifecycleDebugLogger logger = new LifecycleDebugLogger( new LoggerStub() );
final LifecycleDependencyResolver lifecycleDependencyResolver =
new LifecycleDependencyResolver( new ProjectDependenciesResolverStub(), new LoggerStub() );
return new BuilderCommon( logger, new LifecycleExecutionPlanCalculatorStub(), lifecycleDependencyResolver );
return new BuilderCommon( logger, new LifecycleExecutionPlanCalculatorStub(), lifecycleDependencyResolver,
new LoggerStub() );
}
}

View File

@ -50,22 +50,26 @@ public class LifecycleExecutionPlanCalculatorStub
implements LifecycleExecutionPlanCalculator
{
// clean
public final static MojoDescriptor PRE_CLEAN = createMojoDescriptor( "pre-clean" );
public final static MojoDescriptor CLEAN = createMojoDescriptor( "clean" );
public final static MojoDescriptor POST_CLEAN = createMojoDescriptor( "post-clean" );
// default (or at least some of them)
public final static MojoDescriptor VALIDATE = createMojoDescriptor( "validate" );
public final static MojoDescriptor INITIALIZE = createMojoDescriptor( "initialize" );
public final static MojoDescriptor TEST_COMPILE = createMojoDescriptor( "test-compile" );
public final static MojoDescriptor PROCESS_TEST_RESOURCES = createMojoDescriptor( "process-test-resources" );
public final static MojoDescriptor PROCESS_RESOURCES = createMojoDescriptor( "process-resources" );
public final static MojoDescriptor COMPILE = createMojoDescriptor( "compile" );
public final static MojoDescriptor COMPILE = createMojoDescriptor( "compile", true );
public final static MojoDescriptor TEST = createMojoDescriptor( "test" );
@ -73,13 +77,15 @@ public class LifecycleExecutionPlanCalculatorStub
public final static MojoDescriptor INSTALL = createMojoDescriptor( "install" );
// site
public final static MojoDescriptor PRE_SITE = createMojoDescriptor( "pre-site" );
public final static MojoDescriptor SITE = createMojoDescriptor( "site" );
public final static MojoDescriptor POST_SITE = createMojoDescriptor( "post-site" );
public final static MojoDescriptor SITE_DEPLOY = createMojoDescriptor( "site-deploy" );
public final static MojoDescriptor PRE_SITE = createMojoDescriptor( "pre-site" );
public final static MojoDescriptor SITE = createMojoDescriptor( "site" );
public final static MojoDescriptor POST_SITE = createMojoDescriptor( "post-site" );
public final static MojoDescriptor SITE_DEPLOY = createMojoDescriptor( "site-deploy" );
public int getNumberOfExceutions( ProjectBuildList projectBuildList )
@ -112,8 +118,8 @@ public class LifecycleExecutionPlanCalculatorStub
}
// The remaining are basically "for future expansion"
List<MojoExecution> me = new ArrayList<MojoExecution>();
me.add( createMojoExecution( new Plugin(), "resources", "default-resources", PROCESS_RESOURCES ) );
me.add( createMojoExecution( new Plugin(), "compile", "default-compile", COMPILE ) );
me.add( createMojoExecution( "resources", "default-resources", PROCESS_RESOURCES ) );
me.add( createMojoExecution( "compile", "default-compile", COMPILE ) );
return createExecutionPlan( project, me );
}
@ -123,30 +129,29 @@ public class LifecycleExecutionPlanCalculatorStub
NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException
{
List<MojoExecution> me = new ArrayList<MojoExecution>();
me.add( createMojoExecution( new Plugin(), "initialize", "default-initialize", INITIALIZE ) );
me.add( createMojoExecution( new Plugin(), "resources", "default-resources", PROCESS_RESOURCES ) );
me.add( createMojoExecution( new Plugin(), "compile", "default-compile", COMPILE ) );
me.add( createMojoExecution( new Plugin(), "testResources", "default-testResources", PROCESS_TEST_RESOURCES ) );
me.add( createMojoExecution( new Plugin(), "testCompile", "default-testCompile", TEST_COMPILE ) );
me.add( createMojoExecution( new Plugin(), "test", "default-test", TEST ) );
me.add( createMojoExecution( new Plugin(), "war", "default-war", PACKAGE ) );
me.add( createMojoExecution( new Plugin(), "install", "default-install", INSTALL ) );
me.add( createMojoExecution( "initialize", "default-initialize", INITIALIZE ) );
me.add( createMojoExecution( "resources", "default-resources", PROCESS_RESOURCES ) );
me.add( createMojoExecution( "compile", "default-compile", COMPILE ) );
me.add( createMojoExecution( "testResources", "default-testResources", PROCESS_TEST_RESOURCES ) );
me.add( createMojoExecution( "testCompile", "default-testCompile", TEST_COMPILE ) );
me.add( createMojoExecution( "test", "default-test", TEST ) );
me.add( createMojoExecution( "war", "default-war", PACKAGE ) );
me.add( createMojoExecution( "install", "default-install", INSTALL ) );
return createExecutionPlan( ProjectDependencyGraphStub.A.getExecutionProject(), me );
}
public static MavenExecutionPlan getProjectBExecutionPlan()
throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException,
PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException,
NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException
{
List<MojoExecution> me = new ArrayList<MojoExecution>();
me.add( createMojoExecution( new Plugin(), "enforce", "enforce-versions", VALIDATE ) );
me.add( createMojoExecution( new Plugin(), "resources", "default-resources", PROCESS_RESOURCES ) );
me.add( createMojoExecution( new Plugin(), "compile", "default-compile", COMPILE ) );
me.add( createMojoExecution( new Plugin(), "testResources", "default-testResources", PROCESS_TEST_RESOURCES ) );
me.add( createMojoExecution( new Plugin(), "testCompile", "default-testCompile", TEST_COMPILE ) );
me.add( createMojoExecution( new Plugin(), "test", "default-test", TEST ) );
me.add( createMojoExecution( "enforce", "enforce-versions", VALIDATE ) );
me.add( createMojoExecution( "resources", "default-resources", PROCESS_RESOURCES ) );
me.add( createMojoExecution( "compile", "default-compile", COMPILE ) );
me.add( createMojoExecution( "testResources", "default-testResources", PROCESS_TEST_RESOURCES ) );
me.add( createMojoExecution( "testCompile", "default-testCompile", TEST_COMPILE ) );
me.add( createMojoExecution( "test", "default-test", TEST ) );
return createExecutionPlan( ProjectDependencyGraphStub.B.getExecutionProject(), me );
}
@ -162,24 +167,35 @@ public class LifecycleExecutionPlanCalculatorStub
DefaultLifecyclesStub.createDefaultLifecycles() );
}
private static MojoExecution createMojoExecution( Plugin plugin, String goal, String executionId,
MojoDescriptor mojoDescriptor )
private static MojoExecution createMojoExecution( String goal, String executionId, MojoDescriptor mojoDescriptor )
{
final Plugin plugin = mojoDescriptor.getPluginDescriptor().getPlugin();
MojoExecution result = new MojoExecution( plugin, goal, executionId );
result.setConfiguration( new Xpp3Dom( executionId + "-" + goal ) );
result.setMojoDescriptor( mojoDescriptor );
result.setLifecyclePhase( mojoDescriptor.getPhase() );
result.setLifecyclePhase( mojoDescriptor.getPhase() );
return result;
}
public static MojoDescriptor createMojoDescriptor( String phaseName )
{
return createMojoDescriptor( phaseName, false );
}
public static MojoDescriptor createMojoDescriptor( String phaseName, boolean threadSafe )
{
final MojoDescriptor mojoDescriptor = new MojoDescriptor();
mojoDescriptor.setPhase( phaseName );
final PluginDescriptor descriptor = new PluginDescriptor();
Plugin plugin = new Plugin();
plugin.setArtifactId( "org.apache.maven.test.MavenExecutionPlan" );
plugin.setGroupId( "stub-plugin-" + phaseName );
descriptor.setPlugin( plugin );
descriptor.setArtifactId( "artifact." + phaseName );
mojoDescriptor.setPluginDescriptor( descriptor );
mojoDescriptor.setThreadSafe( threadSafe );
return mojoDescriptor;
}

View File

@ -117,6 +117,9 @@ public class MojoDescriptor
/** By default, the Mojo don't need reports to run */
private boolean requiresReports = false;
/** By default, mojos are not threadsafe */
private boolean threadSafe = false;
/**
* Default constructor.
*/
@ -642,6 +645,23 @@ public class MojoDescriptor
return executeGoal;
}
/**
* @return True if the <code>Mojo</code> is thread-safe and can be run safely in parallel
*/
public boolean isThreadSafe()
{
return threadSafe;
}
/**
* @param threadSafe indicates that the mojo is thread-safe and can be run safely in parallel
*/
public void setThreadSafe( boolean threadSafe )
{
this.threadSafe = threadSafe;
}
/**
* @return {@code true} if this mojo forks either a goal or the lifecycle, {@code false} otherwise.
*/