MNG-5575: Separate distinct build strategies into their own new Builder implementation. The interface is not ideal yet, but gives us a cleaner

separation of implementations and gives us a path forward.
This commit is contained in:
Jason van Zyl 2014-02-05 22:43:04 -05:00
parent 3bf568eda6
commit 09961c3150
40 changed files with 2145 additions and 168 deletions

45
MavenLifecyle.md Normal file
View File

@ -0,0 +1,45 @@
Maven (DefaultMaven) : doExecute
- add a build timestamp to the request properties
- validateLocalRepository: validate local repository (check to make sure it exists and is writable)
- newRepositorySession: create the RepositorySystemSession
- AbstractLifecycleParticipant.afterSessionStart
- event: ExecutionEvent.Type.ProjectDiscoveryStarted
- set the RepositorySystemSession in the ProjectBuildingRequest
- getProjectsForMavenReactor
- this triggers the reading of all the POMs: (NOTE: this could be optimized looking at the POMs if they don't change)
- create the ReactorReader
- set RepositorySystemSession.setWorkspaceReader()
- make the RepositorySystemSession readonly
? Can we remove all the @Deprecated methods from MavenSession (4 years, 7 months)
? Can we make the MavenSession read-only, do we need a way to share state better
- AbstractLifecycleParticipant.afterProjectsRead()
- lifecycleStarter.execute(session)
- validateProfiles (NOTE: why does this happen at the end?)
# Things to document
- How the ClassRealms are setup at each stage
- Having meaningful visuals for the lifecycle and stages
- explain forked executions
- remove aggregators, and what are they exactly
# Questions
? forked executions
? aggregators
? All the different resolvers: project, plugin, lifecycle
? Turn if all into JSR330
# Things to refactor
# Isolate project dependency downloads from build execution
- project dependencies are resolved from the mojo executor
- separate dependency resolution from build execution, right now they are intertwined
- if separated then a calculation can be made for the whole set of dependencies
- make sure dependency resolution works before the build executes
- not sure there would be much of a speed impact if one assumes the best speeds will happen when everything is downloaded and the
conflation of these modes and the complexity it creates is not worth it
- turn all to JSR330
- use LifecycleModuleBuilder consistently instead of BuilderCommon
- the MavenExecution is calculated in each of the builders instead of once, the implication of this is that project dependency resolution will happen in parallel which means the local repository needs to be safe, and resolution in general.
- the weave builder uses BuilderCommon directly, should be used the same way the other builders work

View File

@ -113,13 +113,15 @@ class DefaultProjectDependencyGraph
private List<MavenProject> getProjects( Collection<String> projectIds )
{
List<MavenProject> projects = new ArrayList<MavenProject>();
List<MavenProject> projects = new ArrayList<MavenProject>( projectIds.size() );
for ( MavenProject p : sorter.getSortedProjects() )
for ( String projectId : projectIds )
{
if ( projectIds.contains( ProjectSorter.getId( p ) ) )
MavenProject project = sorter.getProjectMap().get( projectId );
if ( project != null )
{
projects.add( p );
projects.add( project );
}
}

View File

@ -134,9 +134,9 @@ public class DefaultMavenExecutionRequest
private ExecutionListener executionListener;
private String threadCount;
private int degreeOfConcurrency = 1;
private boolean perCoreThreadCount;
private String builderId = "singlethreaded";
/**
* Suppress SNAPSHOT updates.
@ -169,8 +169,7 @@ public class DefaultMavenExecutionRequest
copy.setUserSettingsFile( original.getUserSettingsFile() );
copy.setGlobalSettingsFile( original.getGlobalSettingsFile() );
copy.setUserToolchainsFile( original.getUserToolchainsFile() );
copy.setBaseDirectory( ( original.getBaseDirectory() != null )
? new File( original.getBaseDirectory() ) : null );
copy.setBaseDirectory( ( original.getBaseDirectory() != null ) ? new File( original.getBaseDirectory() ) : null );
copy.setGoals( original.getGoals() );
copy.setRecursive( original.isRecursive() );
copy.setPom( original.getPom() );
@ -1046,29 +1045,14 @@ public class DefaultMavenExecutionRequest
return this;
}
public String getThreadCount()
public void setDegreeOfConcurrency( final int degreeOfConcurrency )
{
return threadCount;
this.degreeOfConcurrency = degreeOfConcurrency;
}
public void setThreadCount( String threadCount )
public int getDegreeOfConcurrency()
{
this.threadCount = threadCount;
}
public boolean isThreadConfigurationPresent()
{
return getThreadCount() != null;
}
public boolean isPerCoreThreadCount()
{
return perCoreThreadCount;
}
public void setPerCoreThreadCount( boolean perCoreThreadCount )
{
this.perCoreThreadCount = perCoreThreadCount;
return degreeOfConcurrency;
}
public WorkspaceReader getWorkspaceReader()
@ -1114,4 +1098,15 @@ public class DefaultMavenExecutionRequest
this.useSimpleLocalRepositoryManager = useSimpleLocalRepositoryManager;
return this;
}
public MavenExecutionRequest setBuilderId( String builderId )
{
this.builderId = builderId;
return this;
}
public String getBuilderId()
{
return builderId;
}
}

View File

@ -28,6 +28,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.maven.project.DependencyResolutionResult;
import org.apache.maven.project.MavenProject;
import com.google.common.collect.Maps;
/** @author Jason van Zyl */
public class DefaultMavenExecutionResult
implements MavenExecutionResult
@ -40,7 +42,7 @@ public class DefaultMavenExecutionResult
private List<Throwable> exceptions = new CopyOnWriteArrayList<Throwable>();
private Map<MavenProject, BuildSummary> buildSummaries;
private Map<MavenProject, BuildSummary> buildSummaries = Maps.newIdentityHashMap();
public MavenExecutionResult setProject( MavenProject project )
{
@ -63,7 +65,8 @@ public class DefaultMavenExecutionResult
public List<MavenProject> getTopologicallySortedProjects()
{
return null == topologicallySortedProjects ? Collections.<MavenProject> emptyList() : topologicallySortedProjects;
return null == topologicallySortedProjects ? Collections.<MavenProject> emptyList()
: topologicallySortedProjects;
}
public DependencyResolutionResult getDependencyResolutionResult()
@ -108,5 +111,4 @@ public class DefaultMavenExecutionResult
}
buildSummaries.put( summary.getProject(), summary );
}
}

View File

@ -91,14 +91,17 @@ public interface MavenExecutionRequest
// Base directory
MavenExecutionRequest setBaseDirectory( File basedir );
String getBaseDirectory();
// Timing (remove this)
MavenExecutionRequest setStartTime( Date start );
Date getStartTime();
// Goals
MavenExecutionRequest setGoals( List<String> goals );
List<String> getGoals();
// Properties
@ -141,132 +144,180 @@ public interface MavenExecutionRequest
// Reactor
MavenExecutionRequest setReactorFailureBehavior( String failureBehavior );
String getReactorFailureBehavior();
MavenExecutionRequest setSelectedProjects( List<String> projects );
List<String> getSelectedProjects();
/**
*
* @param projects the projects to exclude
* @return this MavenExecutionRequest
* @since 3.2
*/
MavenExecutionRequest setExcludedProjects( List<String> projects );
/**
*
* @return the excluded projects, never {@code null}
* @since 3.2
*/
List<String> getExcludedProjects();
MavenExecutionRequest setResumeFrom( String project );
String getResumeFrom();
MavenExecutionRequest setMakeBehavior( String makeBehavior );
String getMakeBehavior();
void setThreadCount( String threadCount );
String getThreadCount();
boolean isThreadConfigurationPresent();
void setPerCoreThreadCount( boolean perCoreThreadCount );
boolean isPerCoreThreadCount();
/**
* Set's the parallel degree of concurrency used by the build.
*
* @param degreeOfConcurrency
*/
public void setDegreeOfConcurrency( final int degreeOfConcurrency );
/**
* @return the degree of concurrency for the build.
*/
public int getDegreeOfConcurrency();
// Recursive (really to just process the top-level POM)
MavenExecutionRequest setRecursive( boolean recursive );
boolean isRecursive();
MavenExecutionRequest setPom( File pom );
File getPom();
// Errors
MavenExecutionRequest setShowErrors( boolean showErrors );
boolean isShowErrors();
// Transfer listeners
MavenExecutionRequest setTransferListener( TransferListener transferListener );
TransferListener getTransferListener();
// Logging
MavenExecutionRequest setLoggingLevel( int loggingLevel );
int getLoggingLevel();
// Update snapshots
MavenExecutionRequest setUpdateSnapshots( boolean updateSnapshots );
boolean isUpdateSnapshots();
MavenExecutionRequest setNoSnapshotUpdates( boolean noSnapshotUpdates );
boolean isNoSnapshotUpdates();
// Checksum policy
MavenExecutionRequest setGlobalChecksumPolicy( String globalChecksumPolicy );
String getGlobalChecksumPolicy();
// Local repository
MavenExecutionRequest setLocalRepositoryPath( String localRepository );
MavenExecutionRequest setLocalRepositoryPath( File localRepository );
File getLocalRepositoryPath();
MavenExecutionRequest setLocalRepository( ArtifactRepository repository );
ArtifactRepository getLocalRepository();
// Interactive
MavenExecutionRequest setInteractiveMode( boolean interactive );
boolean isInteractiveMode();
// Offline
MavenExecutionRequest setOffline( boolean offline );
boolean isOffline();
boolean isCacheTransferError();
MavenExecutionRequest setCacheTransferError( boolean cacheTransferError );
boolean isCacheNotFound();
MavenExecutionRequest setCacheNotFound( boolean cacheNotFound );
// Profiles
List<Profile> getProfiles();
MavenExecutionRequest addProfile( Profile profile );
MavenExecutionRequest setProfiles( List<Profile> profiles );
MavenExecutionRequest addActiveProfile( String profile );
MavenExecutionRequest addActiveProfiles( List<String> profiles );
MavenExecutionRequest setActiveProfiles( List<String> profiles );
List<String> getActiveProfiles();
MavenExecutionRequest addInactiveProfile( String profile );
MavenExecutionRequest addInactiveProfiles( List<String> profiles );
MavenExecutionRequest setInactiveProfiles( List<String> profiles );
List<String> getInactiveProfiles();
// Proxies
List<Proxy> getProxies();
MavenExecutionRequest setProxies( List<Proxy> proxies );
MavenExecutionRequest addProxy( Proxy proxy );
// Servers
List<Server> getServers();
MavenExecutionRequest setServers( List<Server> servers );
MavenExecutionRequest addServer( Server server );
// Mirrors
List<Mirror> getMirrors();
MavenExecutionRequest setMirrors( List<Mirror> mirrors );
MavenExecutionRequest addMirror( Mirror mirror );
// Plugin groups
List<String> getPluginGroups();
MavenExecutionRequest setPluginGroups( List<String> pluginGroups );
MavenExecutionRequest addPluginGroup( String pluginGroup );
MavenExecutionRequest addPluginGroups( List<String> pluginGroups );
boolean isProjectPresent();
MavenExecutionRequest setProjectPresent( boolean isProjectPresent );
File getUserSettingsFile();
MavenExecutionRequest setUserSettingsFile( File userSettingsFile );
File getGlobalSettingsFile();
MavenExecutionRequest setGlobalSettingsFile( File globalSettingsFile );
MavenExecutionRequest addRemoteRepository( ArtifactRepository repository );
MavenExecutionRequest addPluginArtifactRepository( ArtifactRepository repository );
/**
@ -278,21 +329,27 @@ public interface MavenExecutionRequest
* @return This request, never {@code null}.
*/
MavenExecutionRequest setRemoteRepositories( List<ArtifactRepository> repositories );
List<ArtifactRepository> getRemoteRepositories();
MavenExecutionRequest setPluginArtifactRepositories( List<ArtifactRepository> repositories );
List<ArtifactRepository> getPluginArtifactRepositories();
MavenExecutionRequest setRepositoryCache( RepositoryCache repositoryCache );
RepositoryCache getRepositoryCache();
WorkspaceReader getWorkspaceReader();
MavenExecutionRequest setWorkspaceReader( WorkspaceReader workspaceReader );
File getUserToolchainsFile();
MavenExecutionRequest setUserToolchainsFile( File userToolchainsFile );
ExecutionListener getExecutionListener();
MavenExecutionRequest setExecutionListener( ExecutionListener executionListener );
ProjectBuildingRequest getProjectBuildingRequest();
@ -307,4 +364,18 @@ public interface MavenExecutionRequest
*/
MavenExecutionRequest setUseLegacyLocalRepository( boolean useLegacyLocalRepository );
/**
* Controls the {@link Builder} used by Maven by specification of the builder's id.
*
* @since 3.2.0
*/
MavenExecutionRequest setBuilderId( String builderId );
/**
* Controls the {@link Builder} used by Maven by specification of the builders id.
*
* @since 3.2.0
*/
String getBuilderId();
}

View File

@ -67,5 +67,4 @@ public interface MavenExecutionResult
* @param summary The build summary to add, must not be {@code null}.
*/
void addBuildSummary( BuildSummary summary );
}

View File

@ -254,6 +254,7 @@ public class MavenSession
return request.getBaseDirectory();
}
@Deprecated
public boolean isUsingPOMsFromFilesystem()
{
return request.isProjectPresent();

View File

@ -176,11 +176,6 @@ public class ReactorManager
return sorter.getSortedProjects();
}
public MavenProject getTopLevelProject()
{
return sorter.getTopLevelProject();
}
public boolean hasBuildSuccess( MavenProject project )
{
return buildSuccessesByProject.containsKey( getProjectKey( project ) );

View File

@ -19,8 +19,8 @@ package org.apache.maven.lifecycle;
* under the License.
*/
import org.apache.maven.lifecycle.internal.BuilderCommon;
import org.apache.maven.lifecycle.internal.ExecutionPlanItem;
import org.apache.maven.lifecycle.internal.builder.BuilderCommon;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;

View File

@ -20,6 +20,7 @@ package org.apache.maven.lifecycle.internal;
*/
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.internal.builder.BuilderCommon;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.annotations.Component;

View File

@ -0,0 +1,20 @@
package org.apache.maven.lifecycle.internal;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Simple {@link ThreadFactory} implementation that ensures the corresponding threads have a meaningful name.
*/
public class BuildThreadFactory
implements ThreadFactory
{
private final AtomicInteger ID = new AtomicInteger();
private String PREFIX = "BuilderThread";
public Thread newThread( Runnable r )
{
return new Thread( r, String.format( "%s %d", PREFIX, ID.getAndIncrement() ) );
}
}

View File

@ -36,6 +36,7 @@ import org.apache.maven.lifecycle.Lifecycle;
import org.apache.maven.lifecycle.LifecycleNotFoundException;
import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException;
import org.apache.maven.lifecycle.MavenExecutionPlan;
import org.apache.maven.lifecycle.internal.builder.BuilderCommon;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.plugin.BuildPluginManager;

View File

@ -22,6 +22,7 @@ package org.apache.maven.lifecycle.internal;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ProjectDependencyGraph;
import org.apache.maven.lifecycle.MavenExecutionPlan;
import org.apache.maven.lifecycle.internal.builder.BuilderCommon;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.project.MavenProject;

View File

@ -29,6 +29,7 @@ import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ProjectExecutionEvent;
import org.apache.maven.execution.ProjectExecutionListener;
import org.apache.maven.lifecycle.MavenExecutionPlan;
import org.apache.maven.lifecycle.internal.builder.BuilderCommon;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.annotations.Component;
@ -111,8 +112,7 @@ public class LifecycleModuleBuilder
projectExecutionListener.afterProjectExecutionSuccess( new ProjectExecutionEvent( session, currentProject,
mojoExecutions ) );
reactorContext.getResult().addBuildSummary(
new BuildSuccess( currentProject, buildEndTime - buildStartTime ) );
reactorContext.getResult().addBuildSummary( new BuildSuccess( currentProject, buildEndTime - buildStartTime ) );
eventCatapult.fire( ExecutionEvent.Type.ProjectSucceeded, session, null );
}
@ -120,7 +120,8 @@ public class LifecycleModuleBuilder
{
builderCommon.handleBuildError( reactorContext, rootSession, session, currentProject, e, buildStartTime );
projectExecutionListener.afterProjectExecutionFailure( new ProjectExecutionEvent( session, currentProject, e ) );
projectExecutionListener.afterProjectExecutionFailure( new ProjectExecutionEvent( session, currentProject,
e ) );
}
finally
{

View File

@ -19,25 +19,24 @@ package org.apache.maven.lifecycle.internal;
* under the License.
*/
import java.util.List;
import java.util.Map;
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.lifecycle.DefaultLifecycles;
import org.apache.maven.lifecycle.MissingProjectException;
import org.apache.maven.lifecycle.NoGoalSpecifiedException;
import org.apache.maven.lifecycle.internal.builder.Builder;
import org.apache.maven.lifecycle.internal.builder.BuilderNotFoundException;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
import java.util.List;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Starts the build life cycle
*
* @author Jason van Zyl
* @author Benjamin Bentmann
* @author Kristian Rosenvold
@ -45,7 +44,6 @@ import java.util.concurrent.TimeUnit;
@Component( role = LifecycleStarter.class )
public class LifecycleStarter
{
@Requirement
private ExecutionEventCatapult eventCatapult;
@ -55,15 +53,6 @@ public class LifecycleStarter
@Requirement
private Logger logger;
@Requirement
private LifecycleModuleBuilder lifecycleModuleBuilder;
@Requirement
private LifecycleWeaveBuilder lifeCycleWeaveBuilder;
@Requirement
private LifecycleThreadedBuilder lifecycleThreadedBuilder;
@Requirement
private BuildListCalculator buildListCalculator;
@ -74,30 +63,27 @@ public class LifecycleStarter
private LifecycleTaskSegmentCalculator lifecycleTaskSegmentCalculator;
@Requirement
private ThreadConfigurationService threadConfigService;
private Map<String, Builder> builders;
public void execute( MavenSession session )
{
eventCatapult.fire( ExecutionEvent.Type.SessionStarted, session, null );
ReactorContext reactorContext = null;
ProjectBuildList projectBuilds = null;
MavenExecutionResult result = session.getResult();
try
{
if ( !session.isUsingPOMsFromFilesystem() && lifecycleTaskSegmentCalculator.requiresProject( session ) )
if ( buildExecutionRequiresProject( session ) && projectIsNotPresent( session ) )
{
throw new MissingProjectException( "The goal you specified requires a project to execute"
+ " but there is no POM in this directory (" + session.getExecutionRootDirectory() + ")."
+ " Please verify you invoked Maven from the correct directory." );
}
final MavenExecutionRequest executionRequest = session.getRequest();
boolean isThreaded = executionRequest.isThreadConfigurationPresent();
session.setParallel( isThreaded );
List<TaskSegment> taskSegments = lifecycleTaskSegmentCalculator.calculateTaskSegments( session );
ProjectBuildList projectBuilds = buildListCalculator.calculateProjectBuilds( session, taskSegments );
projectBuilds = buildListCalculator.calculateProjectBuilds( session, taskSegments );
if ( projectBuilds.isEmpty() )
{
@ -115,51 +101,19 @@ public class LifecycleStarter
}
ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
ReactorBuildStatus reactorBuildStatus = new ReactorBuildStatus( session.getProjectDependencyGraph() );
ReactorContext callableContext =
new ReactorContext( result, projectIndex, oldContextClassLoader, reactorBuildStatus );
reactorContext = new ReactorContext( result, projectIndex, oldContextClassLoader, reactorBuildStatus );
if ( isThreaded )
{
ExecutorService executor =
threadConfigService.getExecutorService( executionRequest.getThreadCount(),
executionRequest.isPerCoreThreadCount(),
session.getProjects().size() );
try
String builderId = session.getRequest().getBuilderId();
Builder builder = builders.get( builderId );
if ( builder == null )
{
throw new BuilderNotFoundException( String.format( "The builder requested using id = %s cannot be found", builderId ) );
}
final boolean isWeaveMode = LifecycleWeaveBuilder.isWeaveMode( executionRequest );
if ( isWeaveMode )
{
lifecycleDebugLogger.logWeavePlan( session );
lifeCycleWeaveBuilder.build( projectBuilds, callableContext, taskSegments, session, executor,
reactorBuildStatus );
}
else
{
ConcurrencyDependencyGraph analyzer =
new ConcurrencyDependencyGraph( projectBuilds, session.getProjectDependencyGraph() );
CompletionService<ProjectSegment> service =
new ExecutorCompletionService<ProjectSegment>( executor );
lifecycleThreadedBuilder.build( session, callableContext, projectBuilds, taskSegments, analyzer,
service );
}
}
finally
{
executor.shutdown();
// If the builder has terminated with an exception we want to catch any stray threads before going
// to System.exit in the mavencli.
executor.awaitTermination( 5, TimeUnit.SECONDS ) ;
}
}
else
{
singleThreadedBuild( session, callableContext, projectBuilds, taskSegments, reactorBuildStatus );
}
logger.info( "" );
logger.info( String.format( "Using the builder %s", builder.getClass().getName() ) );
builder.build( session, reactorContext, projectBuilds, taskSegments, reactorBuildStatus );
}
catch ( Exception e )
@ -170,29 +124,13 @@ public class LifecycleStarter
eventCatapult.fire( ExecutionEvent.Type.SessionEnded, session, null );
}
private void singleThreadedBuild( MavenSession session, ReactorContext callableContext,
ProjectBuildList projectBuilds, List<TaskSegment> taskSegments,
ReactorBuildStatus reactorBuildStatus )
private boolean buildExecutionRequiresProject( MavenSession session )
{
for ( TaskSegment taskSegment : taskSegments )
{
for ( ProjectSegment projectBuild : projectBuilds.getByTaskSegment( taskSegment ) )
{
try
{
lifecycleModuleBuilder.buildProject( session, callableContext, projectBuild.getProject(),
taskSegment );
if ( reactorBuildStatus.isHalted() )
{
break;
}
}
catch ( Exception e )
{
break; // Why are we just ignoring this exception? Are exceptions are being used for flow control
return lifecycleTaskSegmentCalculator.requiresProject( session );
}
}
}
private boolean projectIsNotPresent( MavenSession session )
{
return !session.getRequest().isProjectPresent();
}
}

View File

@ -141,4 +141,16 @@ public class ProjectBuildList
{
return items.isEmpty();
}
/**
* @return a set of all the projects managed by the build
*/
public Set<MavenProject> getProjects() {
Set<MavenProject> projects = new HashSet<MavenProject>();
for (ProjectSegment s : items) {
projects.add(s.getProject());
}
return projects;
}
}

View File

@ -19,6 +19,7 @@ package org.apache.maven.lifecycle.internal;
* under the License.
*/
import org.apache.maven.lifecycle.internal.builder.BuilderCommon;
import org.apache.maven.project.MavenProject;
import java.util.HashMap;

View File

@ -20,6 +20,7 @@ package org.apache.maven.lifecycle.internal;
*/
import org.apache.maven.execution.ProjectDependencyGraph;
import org.apache.maven.lifecycle.internal.builder.BuilderCommon;
import org.apache.maven.project.MavenProject;
import java.util.Collection;

View File

@ -0,0 +1,28 @@
package org.apache.maven.lifecycle.internal.builder;
import java.util.List;
import java.util.concurrent.ExecutionException;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.internal.ProjectBuildList;
import org.apache.maven.lifecycle.internal.ReactorBuildStatus;
import org.apache.maven.lifecycle.internal.ReactorContext;
import org.apache.maven.lifecycle.internal.TaskSegment;
/**
* This is provisional API and is very likely to change in the near future. If you implement a builder expect it to
* change.
*
* @author jvanzyl
*
*/
public interface Builder
{
//
// Be nice to whittle this down to Session, maybe add task segments to the session. The session really is the
// the place to store reactor related information.
//
public void build( MavenSession session, ReactorContext reactorContext, ProjectBuildList projectBuilds,
List<TaskSegment> taskSegments, ReactorBuildStatus reactorBuildStatus )
throws ExecutionException, InterruptedException;
}

View File

@ -0,0 +1,195 @@
package org.apache.maven.lifecycle.internal.builder;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.maven.InternalErrorException;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.BuildFailure;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenSession;
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.lifecycle.internal.ExecutionEventCatapult;
import org.apache.maven.lifecycle.internal.LifecycleDebugLogger;
import org.apache.maven.lifecycle.internal.LifecycleExecutionPlanCalculator;
import org.apache.maven.lifecycle.internal.ReactorContext;
import org.apache.maven.lifecycle.internal.TaskSegment;
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.descriptor.MojoDescriptor;
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;
/**
* Common code that is shared by the LifecycleModuleBuilder and the LifeCycleWeaveBuilder
*
* @since 3.0
* @author Kristian Rosenvold
* Builds one or more lifecycles for a full module
* NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
*/
@Component( role = BuilderCommon.class )
public class BuilderCommon
{
@Requirement
private LifecycleDebugLogger lifecycleDebugLogger;
@Requirement
private LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator;
@Requirement
private ExecutionEventCatapult eventCatapult;
@Requirement
private Logger logger;
public BuilderCommon()
{
}
public BuilderCommon( LifecycleDebugLogger lifecycleDebugLogger,
LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator, Logger logger )
{
this.lifecycleDebugLogger = lifecycleDebugLogger;
this.lifeCycleExecutionPlanCalculator = lifeCycleExecutionPlanCalculator;
this.logger = logger;
}
public MavenExecutionPlan resolveBuildPlan( MavenSession session, MavenProject project, TaskSegment taskSegment,
Set<Artifact> projectArtifacts )
throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException,
PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException,
NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException,
LifecycleExecutionException
{
MavenExecutionPlan executionPlan =
lifeCycleExecutionPlanCalculator.calculateExecutionPlan( session, project, taskSegment.getTasks() );
lifecycleDebugLogger.debugProjectPlan( project, executionPlan );
if ( session.getRequest().getDegreeOfConcurrency() > 1 )
{
final Set<Plugin> unsafePlugins = executionPlan.getNonThreadSafePlugins();
if ( !unsafePlugins.isEmpty() && logger.isDebugEnabled() )
{
logger.warn( "*****************************************************************" );
logger.warn( "* Your build is requesting parallel execution, but project *" );
logger.warn( "* contains the following plugin(s) that have goals not marked *" );
logger.warn( "* as @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( "*****************************************************************" );
if ( logger.isDebugEnabled() )
{
final Set<MojoDescriptor> unsafeGoals = executionPlan.getNonThreadSafeMojos();
logger.warn( "The following goals are not marked @threadSafe in " + project.getName() + ":" );
for ( MojoDescriptor unsafeGoal : unsafeGoals )
{
logger.warn( unsafeGoal.getId() );
}
}
else
{
logger.warn( "The following plugins are not marked @threadSafe in " + project.getName() + ":" );
for ( Plugin unsafePlugin : unsafePlugins )
{
logger.warn( unsafePlugin.getId() );
}
logger.warn( "Enable debug to see more precisely which goals are not marked @threadSafe." );
}
logger.warn( "*****************************************************************" );
}
}
return executionPlan;
}
public void handleBuildError( final ReactorContext buildContext, final MavenSession rootSession,
final MavenSession currentSession, final MavenProject mavenProject, Exception e,
final long buildStartTime )
{
if ( e instanceof RuntimeException )
{
e = new InternalErrorException( "Internal error: " + e, e );
}
buildContext.getResult().addException( e );
long buildEndTime = System.currentTimeMillis();
buildContext.getResult().addBuildSummary( new BuildFailure( mavenProject, buildEndTime - buildStartTime, e ) );
eventCatapult.fire( ExecutionEvent.Type.ProjectFailed, currentSession, null, e );
if ( MavenExecutionRequest.REACTOR_FAIL_NEVER.equals( rootSession.getReactorFailureBehavior() ) )
{
// continue the build
}
else if ( MavenExecutionRequest.REACTOR_FAIL_AT_END.equals( rootSession.getReactorFailureBehavior() ) )
{
// continue the build but ban all projects that depend on the failed one
buildContext.getReactorBuildStatus().blackList( mavenProject );
}
else if ( MavenExecutionRequest.REACTOR_FAIL_FAST.equals( rootSession.getReactorFailureBehavior() ) )
{
buildContext.getReactorBuildStatus().halt();
}
else
{
throw new IllegalArgumentException(
"invalid reactor failure behavior " + rootSession.getReactorFailureBehavior() );
}
}
public static void attachToThread( MavenProject currentProject )
{
ClassRealm projectRealm = currentProject.getClassRealm();
if ( projectRealm != null )
{
Thread.currentThread().setContextClassLoader( projectRealm );
}
}
// Todo: I'm really wondering where this method belongs; smells like it should be on MavenProject, but for some reason
// it isn't ? This localization is kind-of a code smell.
public static String getKey( MavenProject project )
{
return project.getGroupId() + ':' + project.getArtifactId() + ':' + project.getVersion();
}
}

View File

@ -0,0 +1,9 @@
package org.apache.maven.lifecycle.internal.builder;
public class BuilderNotFoundException extends Exception
{
public BuilderNotFoundException(String message)
{
super(message);
}
}

View File

@ -0,0 +1,153 @@
package org.apache.maven.lifecycle.internal.builder.multithreaded;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.maven.execution.ProjectDependencyGraph;
import org.apache.maven.lifecycle.internal.ProjectBuildList;
import org.apache.maven.lifecycle.internal.ProjectSegment;
import org.apache.maven.project.MavenProject;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Presents a view of the Dependency Graph that is suited for concurrent building.
*
* @since 3.0
* @author Kristian Rosenvold
* <p/>
* NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
*/
public class ConcurrencyDependencyGraph
{
private final ProjectBuildList projectBuilds;
private final ProjectDependencyGraph projectDependencyGraph;
private final HashSet<MavenProject> finishedProjects = new HashSet<MavenProject>();
public ConcurrencyDependencyGraph( ProjectBuildList projectBuilds, ProjectDependencyGraph projectDependencyGraph )
{
this.projectDependencyGraph = projectDependencyGraph;
this.projectBuilds = projectBuilds;
}
public int getNumberOfBuilds()
{
return projectBuilds.size();
}
/**
* Gets all the builds that have no reactor-dependencies
*
* @return A list of all the initial builds
*/
public List<MavenProject> getRootSchedulableBuilds()
{
List<MavenProject> result = new ArrayList<MavenProject>();
for ( ProjectSegment projectBuild : projectBuilds )
{
if ( projectDependencyGraph.getUpstreamProjects( projectBuild.getProject(), false ).size() == 0 )
{
result.add( projectBuild.getProject() );
}
}
return result;
}
/**
* Marks the provided project as finished. Returns a list of
*
* @param mavenProject The project
* @return The list of builds that are eligible for starting now that the provided project is done
*/
public List<MavenProject> markAsFinished( MavenProject mavenProject )
{
finishedProjects.add( mavenProject );
return getSchedulableNewProcesses( mavenProject );
}
private List<MavenProject> getSchedulableNewProcesses( MavenProject finishedProject )
{
List<MavenProject> result = new ArrayList<MavenProject>();
// schedule dependent projects, if all of their requirements are met
for ( MavenProject dependentProject : projectDependencyGraph.getDownstreamProjects( finishedProject, false ) )
{
final List<MavenProject> upstreamProjects =
projectDependencyGraph.getUpstreamProjects( dependentProject, false );
if ( finishedProjects.containsAll( upstreamProjects ) )
{
result.add( dependentProject );
}
}
return result;
}
/**
* @return set of projects that have yet to be processed successfully by the build.
*/
public Set<MavenProject> getUnfinishedProjects()
{
Set<MavenProject> unfinished = new HashSet<MavenProject>( projectBuilds.getProjects() );
unfinished.remove( finishedProjects );
return unfinished;
}
/**
* @return set of projects that have been successfully processed by the build.
*/
protected Set<MavenProject> getFinishedProjects()
{
return finishedProjects;
}
protected ProjectBuildList getProjectBuilds()
{
return projectBuilds;
}
/**
* For the given {@link MavenProject} {@code p}, return all of {@code p}'s dependencies.
*
* @param p
* @return List of prerequisite projects
*/
protected List<MavenProject> getDependencies( MavenProject p )
{
return projectDependencyGraph.getUpstreamProjects( p, false );
}
/**
* For the given {@link MavenProject} {@code p} return {@code p}'s uncompleted dependencies.
*
* @param p
* @return List of uncompleted prerequisite projects
*/
public List<MavenProject> getActiveDependencies( MavenProject p )
{
List<MavenProject> activeDependencies = projectDependencyGraph.getUpstreamProjects( p, false );
activeDependencies.removeAll( finishedProjects );
return activeDependencies;
}
}

View File

@ -0,0 +1,193 @@
package org.apache.maven.lifecycle.internal.builder.multithreaded;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.internal.BuildThreadFactory;
import org.apache.maven.lifecycle.internal.LifecycleModuleBuilder;
import org.apache.maven.lifecycle.internal.ProjectBuildList;
import org.apache.maven.lifecycle.internal.ProjectSegment;
import org.apache.maven.lifecycle.internal.ReactorBuildStatus;
import org.apache.maven.lifecycle.internal.ReactorContext;
import org.apache.maven.lifecycle.internal.TaskSegment;
import org.apache.maven.lifecycle.internal.ThreadOutputMuxer;
import org.apache.maven.lifecycle.internal.builder.Builder;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
/**
* Builds the full lifecycle in weave-mode (phase by phase as opposed to project-by-project)
*
* @since 3.0
* @author Kristian Rosenvold
* Builds one or more lifecycles for a full module
* <p/>
* NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
*/
@Component( role = Builder.class, hint = "multithreaded" )
public class MultiThreadedBuilder implements Builder
{
@Requirement
private Logger logger;
@Requirement
private LifecycleModuleBuilder lifecycleModuleBuilder;
public MultiThreadedBuilder()
{
}
@Override
public void build( MavenSession session, ReactorContext reactorContext, ProjectBuildList projectBuilds,
List<TaskSegment> taskSegments, ReactorBuildStatus reactorBuildStatus )
throws ExecutionException, InterruptedException
{
ExecutorService executor = Executors.newFixedThreadPool(Math.min(session.getRequest().getDegreeOfConcurrency(), session.getProjects().size()), new BuildThreadFactory());
CompletionService<ProjectSegment> service = new ExecutorCompletionService<ProjectSegment>(executor);
ConcurrencyDependencyGraph analyzer = new ConcurrencyDependencyGraph(projectBuilds, session.getProjectDependencyGraph());
// Currently disabled
ThreadOutputMuxer muxer = null; // new ThreadOutputMuxer( analyzer.getProjectBuilds(), System.out );
for ( TaskSegment taskSegment : taskSegments )
{
Map<MavenProject, ProjectSegment> projectBuildMap = projectBuilds.selectSegment( taskSegment );
try
{
multiThreadedProjectTaskSegmentBuild( analyzer, reactorContext, session, service, taskSegment,
projectBuildMap, muxer );
if ( reactorContext.getReactorBuildStatus().isHalted() )
{
break;
}
}
catch ( Exception e )
{
session.getResult().addException( e );
break;
}
}
}
private void multiThreadedProjectTaskSegmentBuild( ConcurrencyDependencyGraph analyzer,
ReactorContext reactorContext, MavenSession rootSession,
CompletionService<ProjectSegment> service,
TaskSegment taskSegment,
Map<MavenProject, ProjectSegment> projectBuildList,
ThreadOutputMuxer muxer )
{
// schedule independent projects
for ( MavenProject mavenProject : analyzer.getRootSchedulableBuilds() )
{
ProjectSegment projectSegment = projectBuildList.get( mavenProject );
logger.debug( "Scheduling: " + projectSegment.getProject() );
Callable<ProjectSegment> cb =
createBuildCallable( rootSession, projectSegment, reactorContext, taskSegment, muxer );
service.submit( cb );
}
// for each finished project
for ( int i = 0; i < analyzer.getNumberOfBuilds(); i++ )
{
try
{
ProjectSegment projectBuild = service.take().get();
if ( reactorContext.getReactorBuildStatus().isHalted() )
{
break;
}
final List<MavenProject> newItemsThatCanBeBuilt =
analyzer.markAsFinished( projectBuild.getProject() );
for ( MavenProject mavenProject : newItemsThatCanBeBuilt )
{
ProjectSegment scheduledDependent = projectBuildList.get( mavenProject );
logger.debug( "Scheduling: " + scheduledDependent );
Callable<ProjectSegment> cb =
createBuildCallable( rootSession, scheduledDependent, reactorContext, taskSegment, muxer );
service.submit( cb );
}
}
catch ( InterruptedException e )
{
rootSession.getResult().addException( e );
break;
}
catch ( ExecutionException e )
{
rootSession.getResult().addException( e );
break;
}
}
// cancel outstanding builds (if any) - this can happen if an exception is thrown in above block
Future<ProjectSegment> unprocessed;
while ( ( unprocessed = service.poll() ) != null )
{
try
{
unprocessed.get();
}
catch ( InterruptedException e )
{
throw new RuntimeException( e );
}
catch ( ExecutionException e )
{
throw new RuntimeException( e );
}
}
}
private Callable<ProjectSegment> createBuildCallable( final MavenSession rootSession,
final ProjectSegment projectBuild,
final ReactorContext reactorContext,
final TaskSegment taskSegment, final ThreadOutputMuxer muxer )
{
return new Callable<ProjectSegment>()
{
public ProjectSegment call()
{
// muxer.associateThreadWithProjectSegment( projectBuild );
lifecycleModuleBuilder.buildProject( projectBuild.getSession(), rootSession, reactorContext,
projectBuild.getProject(), taskSegment );
// muxer.setThisModuleComplete( projectBuild );
return projectBuild;
}
};
}
}

View File

@ -0,0 +1,46 @@
package org.apache.maven.lifecycle.internal.builder.singlethreaded;
import java.util.List;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.internal.LifecycleModuleBuilder;
import org.apache.maven.lifecycle.internal.ProjectBuildList;
import org.apache.maven.lifecycle.internal.ProjectSegment;
import org.apache.maven.lifecycle.internal.ReactorBuildStatus;
import org.apache.maven.lifecycle.internal.ReactorContext;
import org.apache.maven.lifecycle.internal.TaskSegment;
import org.apache.maven.lifecycle.internal.builder.Builder;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
@Component( role = Builder.class, hint = "singlethreaded" )
public class SingleThreadedBuilder
implements Builder
{
@Requirement
private LifecycleModuleBuilder lifecycleModuleBuilder;
public void build( MavenSession session, ReactorContext reactorContext, ProjectBuildList projectBuilds,
List<TaskSegment> taskSegments, ReactorBuildStatus reactorBuildStatus )
{
for ( TaskSegment taskSegment : taskSegments )
{
for ( ProjectSegment projectBuild : projectBuilds.getByTaskSegment( taskSegment ) )
{
try
{
lifecycleModuleBuilder.buildProject( session, reactorContext, projectBuild.getProject(),
taskSegment );
if ( reactorBuildStatus.isHalted() )
{
break;
}
}
catch ( Exception e )
{
break; // Why are we just ignoring this exception? Are exceptions are being used for flow control
}
}
}
}
}

View File

@ -0,0 +1,221 @@
package org.apache.maven.lifecycle.internal.builder.weave;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.maven.lifecycle.internal.ExecutionPlanItem;
import org.apache.maven.project.MavenProject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @since 3.0
* @author Kristian Rosenvold
* NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
*/
public class BuildLogItem
{
private final ExecutionPlanItem executionPlanItem;
private final MavenProject project;
private final long startTime;
private long endTime;
private final List<DependencyLogEntry> dependencies =
Collections.synchronizedList( new ArrayList<DependencyLogEntry>() );
public BuildLogItem( MavenProject project, ExecutionPlanItem executionPlanItem )
{
this.executionPlanItem = executionPlanItem;
this.project = project;
startTime = System.currentTimeMillis();
}
public MavenProject getProject()
{
return project;
}
public void setComplete()
{
endTime = System.currentTimeMillis();
}
public void addWait( MavenProject upstreamProject, ExecutionPlanItem inSchedule, long startWait )
{
long now = System.currentTimeMillis();
dependencies.add( new DependencyLogEntry( upstreamProject, inSchedule, startWait, now, null ) );
}
public void addDependency( MavenProject upstreamProject, String message )
{
dependencies.add( new DependencyLogEntry( upstreamProject, message ) );
}
public String toString( long rootStart )
{
StringBuilder result = new StringBuilder();
result.append( String.format( "%1d %2d ", startTime - rootStart, endTime - rootStart ) );
result.append( project.getName() );
result.append( " " );
result.append( getMojoExecutionDescription( executionPlanItem ) );
if ( dependencies.size() > 0 )
{
result.append( "\n" );
for ( DependencyLogEntry waitLogEntry : dependencies )
{
result.append( " " );
result.append( waitLogEntry.toString() );
result.append( "\n" );
}
}
return result.toString();
}
public Object toGraph( long rootStart )
{
StringBuilder result = new StringBuilder();
if ( dependencies.size() > 0 )
{
for ( DependencyLogEntry waitLogEntry : dependencies )
{
result.append( " " );
result.append( nodeKey( project, executionPlanItem ) );
result.append( " -> " );
result.append( waitLogEntry.toNodeKey() );
result.append( waitLogEntry.toNodeDescription( rootStart ) );
result.append( "\n" );
}
}
else
{
result.append( " " );
result.append( nodeKey( project, executionPlanItem ) );
result.append( "\n" );
}
return result.toString();
}
private static String nodeKey( MavenProject mavenProject, ExecutionPlanItem executionPlanItem )
{
String key = mavenProject.getArtifactId();
if ( executionPlanItem != null )
{
key += "_" + getMojoExecutionDescription( executionPlanItem );
}
return key.replace( ".", "_" ).replace( ":", "_" );
}
private static String getMojoExecutionDescription( ExecutionPlanItem executionPlanItem )
{
if ( executionPlanItem.getMojoExecution() != null )
{
return executionPlanItem.getMojoExecution().getArtifactId() + getLifeCyclePhase( executionPlanItem );
}
else
{
return "";
}
}
private static String getLifeCyclePhase( ExecutionPlanItem executionPlanItem )
{
return executionPlanItem.getLifecyclePhase() != null ? "[" + executionPlanItem.getLifecyclePhase() + "]" : "";
}
class DependencyLogEntry
{
private final ExecutionPlanItem executionPlanItem;
private final MavenProject upstreamProject;
private final Long start;
private final Long stop;
private final String message;
DependencyLogEntry( MavenProject upstreamProject, ExecutionPlanItem executionPlanItem, Long start, Long stop,
String message )
{
this.upstreamProject = upstreamProject;
this.executionPlanItem = executionPlanItem;
this.start = start;
this.stop = stop;
this.message = message;
}
DependencyLogEntry( MavenProject upstreamProject, String message )
{
this( upstreamProject, null, null, null, message );
}
public String toString()
{
return upstreamProject.getName() + ":" + getExecutionPlanItem() + getElapsed() + getMessage();
}
public String toNodeKey()
{
return nodeKey( upstreamProject, executionPlanItem );
}
public String toNodeDescription( long rootStart )
{
return "";
}
private String getMessage()
{
return message != null ? message : "";
}
private String getExecutionPlanItem()
{
if ( executionPlanItem != null )
{
return getMojoExecutionDescription( executionPlanItem );
}
else
{
return "";
}
}
private String getElapsed()
{
if ( start != null && stop != null )
{
long elapsed = stop - start;
return elapsed > 0 ? ", wait=" + elapsed : "";
}
return "";
}
}
}

View File

@ -0,0 +1,124 @@
package org.apache.maven.lifecycle.internal.builder.weave;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.maven.lifecycle.internal.ExecutionPlanItem;
import org.apache.maven.project.MavenProject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Handles all concurrency-related logging.
* <p/>
* The logging/diagnostic needs of a concurrent build are different from a linear build. This
* delta required to analyze a concurrent build is located here.
* <p/>
* NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
*
* @since 3.0
* @author Kristian Rosenvold
*/
public class ConcurrentBuildLogger
{
private final long startTime;
private final Map<MavenProject, Thread> threadMap = new ConcurrentHashMap<MavenProject, Thread>();
public ConcurrentBuildLogger()
{
startTime = System.currentTimeMillis();
}
List<BuildLogItem> items = Collections.synchronizedList( new ArrayList<BuildLogItem>() );
public BuildLogItem createBuildLogItem( MavenProject project, ExecutionPlanItem current )
{
threadMap.put( project, Thread.currentThread() );
BuildLogItem result = new BuildLogItem( project, current );
items.add( result );
return result;
}
public String toString()
{
StringBuilder result = new StringBuilder();
for ( Map.Entry<MavenProject, Thread> mavenProjectThreadEntry : threadMap.entrySet() )
{
result.append( mavenProjectThreadEntry.getKey().getName() );
result.append( " ran on " );
result.append( mavenProjectThreadEntry.getValue().getName() );
result.append( "\n" );
}
for ( BuildLogItem builtLogItem : items )
{
result.append( builtLogItem.toString( startTime ) );
result.append( "\n" );
}
return result.toString();
}
public String toGraph()
{
StringBuilder result = new StringBuilder();
Map<MavenProject, Collection<BuildLogItem>> multiMap = new HashMap<MavenProject, Collection<BuildLogItem>>();
for ( BuildLogItem builtLogItem : items )
{
MavenProject project = builtLogItem.getProject();
Collection<BuildLogItem> bag = multiMap.get( project );
if ( bag == null )
{
bag = new ArrayList<BuildLogItem>();
multiMap.put( project, bag );
}
bag.add( builtLogItem );
}
result.append( "digraph build" );
result.append( " {\n " );
for ( MavenProject mavenProject : multiMap.keySet() )
{
final Collection<BuildLogItem> builtLogItems = multiMap.get( mavenProject );
result.append( " subgraph " );
result.append( mavenProject.getArtifactId() );
result.append( " {\n" );
for ( BuildLogItem builtLogItem : builtLogItems )
{
result.append( builtLogItem.toGraph( startTime ) );
}
result.append( "\n }\n" );
}
result.append( "\n}\n " );
return result.toString();
}
}

View File

@ -0,0 +1,44 @@
package org.apache.maven.lifecycle.internal.builder.weave;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Knows the phase the current thread is executing.
* <p/>
* This class is used in weave-mode only , there may be better ways of doing this once the dust settles.
*
* @since 3.0
* @author Kristian Rosenvold
*/
class CurrentPhaseForThread
{
private static final InheritableThreadLocal<String> THREAD_PHASE = new InheritableThreadLocal<String>();
public static void setPhase( String phase )
{
THREAD_PHASE.set( phase );
}
public static boolean isPhase( String phase )
{
return phase.equals( THREAD_PHASE.get() );
}
}

View File

@ -0,0 +1,320 @@
package org.apache.maven.lifecycle.internal.builder.weave;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.artifact.metadata.ArtifactMetadata;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
import org.apache.maven.artifact.versioning.VersionRange;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* An artifact that conditionally suspends on getFile for anything but the thread it is locked to.
*
* @since 3.0
*/
class ThreadLockedArtifact
implements Artifact
{
private final Artifact real;
private final CountDownLatch artifactLocked = new CountDownLatch( 1 );
ThreadLockedArtifact( Artifact real )
{
this.real = real;
}
public boolean hasReal()
{
return real != null
&& ( !( real instanceof ThreadLockedArtifact ) || ( (ThreadLockedArtifact) real ).hasReal() );
}
public String getGroupId()
{
return real.getGroupId();
}
public String getArtifactId()
{
return real.getArtifactId();
}
public String getVersion()
{
return real.getVersion();
}
public void setVersion( String version )
{
real.setVersion( version );
}
public String getScope()
{
return real.getScope();
}
public String getType()
{
return real.getType();
}
public String getClassifier()
{
return real.getClassifier();
}
public boolean hasClassifier()
{
return real.hasClassifier();
}
private static final InheritableThreadLocal<ThreadLockedArtifact> THREAD_ARTIFACT =
new InheritableThreadLocal<ThreadLockedArtifact>();
public void attachToThread()
{
THREAD_ARTIFACT.set( this );
}
public File getFile()
{
final ThreadLockedArtifact lockedArtifact = THREAD_ARTIFACT.get();
if ( lockedArtifact != null && this != lockedArtifact && mustLock() )
{
try
{
artifactLocked.await();
}
catch ( InterruptedException e )
{
// Ignore and go on to real.getFile();
}
}
return real.getFile();
}
private boolean mustLock()
{
boolean dontNeedLock = CurrentPhaseForThread.isPhase( "compile" ) || CurrentPhaseForThread.isPhase( "test" );
return !dontNeedLock;
}
public void setFile( File destination )
{
if ( destination != null && destination.exists() && destination.isFile() )
{
artifactLocked.countDown();
}
real.setFile( destination );
}
public String getBaseVersion()
{
return real.getBaseVersion();
}
public void setBaseVersion( String baseVersion )
{
real.setBaseVersion( baseVersion );
}
public String getId()
{
return real.getId();
}
public String getDependencyConflictId()
{
return real.getDependencyConflictId();
}
public void addMetadata( ArtifactMetadata metadata )
{
real.addMetadata( metadata );
}
public Collection<ArtifactMetadata> getMetadataList()
{
return real.getMetadataList();
}
public void setRepository( ArtifactRepository remoteRepository )
{
real.setRepository( remoteRepository );
}
public ArtifactRepository getRepository()
{
return real.getRepository();
}
public void updateVersion( String version, ArtifactRepository localRepository )
{
real.updateVersion( version, localRepository );
}
public String getDownloadUrl()
{
return real.getDownloadUrl();
}
public void setDownloadUrl( String downloadUrl )
{
real.setDownloadUrl( downloadUrl );
}
public ArtifactFilter getDependencyFilter()
{
return real.getDependencyFilter();
}
public void setDependencyFilter( ArtifactFilter artifactFilter )
{
real.setDependencyFilter( artifactFilter );
}
public ArtifactHandler getArtifactHandler()
{
return real.getArtifactHandler();
}
public List<String> getDependencyTrail()
{
return real.getDependencyTrail();
}
public void setDependencyTrail( List<String> dependencyTrail )
{
real.setDependencyTrail( dependencyTrail );
}
public void setScope( String scope )
{
real.setScope( scope );
}
public VersionRange getVersionRange()
{
return real.getVersionRange();
}
public void setVersionRange( VersionRange newRange )
{
real.setVersionRange( newRange );
}
public void selectVersion( String version )
{
real.selectVersion( version );
}
public void setGroupId( String groupId )
{
real.setGroupId( groupId );
}
public void setArtifactId( String artifactId )
{
real.setArtifactId( artifactId );
}
public boolean isSnapshot()
{
return real.isSnapshot();
}
public void setResolved( boolean resolved )
{
real.setResolved( resolved );
}
public boolean isResolved()
{
return real.isResolved();
}
public void setResolvedVersion( String version )
{
real.setResolvedVersion( version );
}
public void setArtifactHandler( ArtifactHandler handler )
{
real.setArtifactHandler( handler );
}
public boolean isRelease()
{
return real.isRelease();
}
public void setRelease( boolean release )
{
real.setRelease( release );
}
public List<ArtifactVersion> getAvailableVersions()
{
return real.getAvailableVersions();
}
public void setAvailableVersions( List<ArtifactVersion> versions )
{
real.setAvailableVersions( versions );
}
public boolean isOptional()
{
return real.isOptional();
}
public void setOptional( boolean optional )
{
real.setOptional( optional );
}
public ArtifactVersion getSelectedVersion()
throws OverConstrainedVersionException
{
return real.getSelectedVersion();
}
public boolean isSelectedVersionKnown()
throws OverConstrainedVersionException
{
return real.isSelectedVersionKnown();
}
public int compareTo( Artifact o )
{
return real.compareTo( o );
}
}

View File

@ -0,0 +1,521 @@
package org.apache.maven.lifecycle.internal.builder.weave;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.execution.BuildSuccess;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.lifecycle.MavenExecutionPlan;
import org.apache.maven.lifecycle.Schedule;
import org.apache.maven.lifecycle.internal.BuildThreadFactory;
import org.apache.maven.lifecycle.internal.DependencyContext;
import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
import org.apache.maven.lifecycle.internal.ExecutionPlanItem;
import org.apache.maven.lifecycle.internal.LifecycleDebugLogger;
import org.apache.maven.lifecycle.internal.MojoExecutor;
import org.apache.maven.lifecycle.internal.PhaseRecorder;
import org.apache.maven.lifecycle.internal.ProjectBuildList;
import org.apache.maven.lifecycle.internal.ProjectSegment;
import org.apache.maven.lifecycle.internal.ReactorBuildStatus;
import org.apache.maven.lifecycle.internal.ReactorContext;
import org.apache.maven.lifecycle.internal.TaskSegment;
import org.apache.maven.lifecycle.internal.builder.Builder;
import org.apache.maven.lifecycle.internal.builder.BuilderCommon;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* Builds the full lifecycle in weave-mode (phase by phase as opposed to project-by-project)
* <p/>
* NOTE: Weave mode is still experimental. It may be either promoted to first class citizen at some later point in time,
* and it may also be removed entirely. Weave mode has much more aggressive concurrency behaviour than regular threaded
* mode, and as such is still under test wrt cross platform stability.
* <p/>
* To remove weave mode from m3, the following should be removed: ExecutionPlanItem.schedule w/setters and getters
* DefaultLifeCycles.getScheduling() and all its use ReactorArtifactRepository has a reference to isWeave too. This
* class and its usage
*
* @since 3.0
* @author Kristian Rosenvold Builds one or more lifecycles for a full module
* <p/>
* NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
*/
@Component( role = Builder.class, hint = "weave" )
public class WeaveBuilder
implements Builder
{
@Requirement
private MojoExecutor mojoExecutor;
@Requirement
private BuilderCommon builderCommon;
@Requirement
private Logger logger;
@Requirement
private ExecutionEventCatapult eventCatapult;
@Requirement
private LifecycleDebugLogger lifecycleDebugLogger;
private Map<MavenProject, MavenExecutionPlan> executionPlans = new HashMap<MavenProject, MavenExecutionPlan>();
public WeaveBuilder()
{
}
public WeaveBuilder( MojoExecutor mojoExecutor, BuilderCommon builderCommon, Logger logger,
ExecutionEventCatapult eventCatapult, LifecycleDebugLogger lifecycleDebugLogger )
{
this.mojoExecutor = mojoExecutor;
this.builderCommon = builderCommon;
this.logger = logger;
this.eventCatapult = eventCatapult;
this.lifecycleDebugLogger = lifecycleDebugLogger;
}
public void build( MavenSession session, ReactorContext buildContext, ProjectBuildList projectBuilds,
List<TaskSegment> taskSegments, ReactorBuildStatus reactorBuildStatus )
throws ExecutionException, InterruptedException
{
lifecycleDebugLogger.logWeavePlan( session );
ExecutorService executor =
Executors.newFixedThreadPool( Math.min( session.getRequest().getDegreeOfConcurrency(),
session.getProjects().size() ), new BuildThreadFactory() );
try
{
ConcurrentBuildLogger concurrentBuildLogger = new ConcurrentBuildLogger();
CompletionService<ProjectSegment> service = new ExecutorCompletionService<ProjectSegment>( executor );
try
{
for ( MavenProject mavenProject : session.getProjects() )
{
Artifact mainArtifact = mavenProject.getArtifact();
if ( mainArtifact != null && !( mainArtifact instanceof ThreadLockedArtifact ) )
{
ThreadLockedArtifact threadLockedArtifact = new ThreadLockedArtifact( mainArtifact );
mavenProject.setArtifact( threadLockedArtifact );
}
}
final List<Future<ProjectSegment>> futures = new ArrayList<Future<ProjectSegment>>();
final Map<ProjectSegment, Future<MavenExecutionPlan>> plans =
new HashMap<ProjectSegment, Future<MavenExecutionPlan>>();
for ( TaskSegment taskSegment : taskSegments )
{
ProjectBuildList segmentChunks = projectBuilds.getByTaskSegment( taskSegment );
Set<Artifact> projectArtifacts = new HashSet<Artifact>();
for ( ProjectSegment segmentChunk : segmentChunks )
{
Artifact artifact = segmentChunk.getProject().getArtifact();
if ( artifact != null )
{
projectArtifacts.add( artifact );
}
}
for ( ProjectSegment projectBuild : segmentChunks )
{
plans.put( projectBuild, executor.submit( createEPFuture( projectBuild, projectArtifacts ) ) );
}
for ( ProjectSegment projectSegment : plans.keySet() )
{
executionPlans.put( projectSegment.getProject(), plans.get( projectSegment ).get() );
}
for ( ProjectSegment projectBuild : segmentChunks )
{
try
{
final MavenExecutionPlan executionPlan = plans.get( projectBuild ).get();
DependencyContext dependencyContext =
mojoExecutor.newDependencyContext( session, executionPlan.getMojoExecutions() );
final Callable<ProjectSegment> projectBuilder =
createCallableForBuildingOneFullModule( buildContext, session, reactorBuildStatus,
executionPlan, projectBuild, dependencyContext,
concurrentBuildLogger );
futures.add( service.submit( projectBuilder ) );
}
catch ( Exception e )
{
throw new ExecutionException( e );
}
}
for ( Future<ProjectSegment> buildFuture : futures )
{
buildFuture.get(); // At this point, this build *is* finished.
// Do not leak threads past here or evil gremlins will get you!
}
futures.clear();
}
}
finally
{
projectBuilds.closeAll();
}
logger.info( concurrentBuildLogger.toString() );
}
finally
{
executor.shutdown();
// If the builder has terminated with an exception we want to catch any stray threads before going
// to System.exit in the mavencli.
executor.awaitTermination( 5, TimeUnit.SECONDS );
}
}
private Callable<MavenExecutionPlan> createEPFuture( final ProjectSegment projectSegment,
final Set<Artifact> projectArtifacts )
{
return new Callable<MavenExecutionPlan>()
{
public MavenExecutionPlan call()
throws Exception
{
return builderCommon.resolveBuildPlan( projectSegment.getSession(), projectSegment.getProject(),
projectSegment.getTaskSegment(), projectArtifacts );
}
};
}
private Callable<ProjectSegment> createCallableForBuildingOneFullModule( final ReactorContext reactorContext,
final MavenSession rootSession,
final ReactorBuildStatus reactorBuildStatus,
final MavenExecutionPlan executionPlan,
final ProjectSegment projectBuild,
final DependencyContext dependencyContext,
final ConcurrentBuildLogger concurrentBuildLogger )
{
return new Callable<ProjectSegment>()
{
public ProjectSegment call()
throws Exception
{
Iterator<ExecutionPlanItem> planItems = executionPlan.iterator();
ExecutionPlanItem current = planItems.hasNext() ? planItems.next() : null;
ThreadLockedArtifact threadLockedArtifact =
(ThreadLockedArtifact) projectBuild.getProject().getArtifact();
if ( threadLockedArtifact != null )
{
threadLockedArtifact.attachToThread();
}
long buildStartTime = System.currentTimeMillis();
// muxer.associateThreadWithProjectSegment( projectBuild );
if ( reactorBuildStatus.isHaltedOrBlacklisted( projectBuild.getProject() ) )
{
eventCatapult.fire( ExecutionEvent.Type.ProjectSkipped, projectBuild.getSession(), null );
return null;
}
eventCatapult.fire( ExecutionEvent.Type.ProjectStarted, projectBuild.getSession(), null );
Collection<ArtifactLink> dependencyLinks = getUpstreamReactorDependencies( projectBuild );
try
{
PhaseRecorder phaseRecorder = new PhaseRecorder( projectBuild.getProject() );
long totalMojoTime = 0;
long mojoStart;
while ( current != null && !reactorBuildStatus.isHaltedOrBlacklisted( projectBuild.getProject() ) )
{
BuildLogItem builtLogItem =
concurrentBuildLogger.createBuildLogItem( projectBuild.getProject(), current );
final Schedule schedule = current.getSchedule();
mojoStart = System.currentTimeMillis();
buildExecutionPlanItem( current, phaseRecorder, schedule, reactorContext, projectBuild,
dependencyContext );
totalMojoTime += ( System.currentTimeMillis() - mojoStart );
current.setComplete();
builtLogItem.setComplete();
ExecutionPlanItem nextPlanItem = planItems.hasNext() ? planItems.next() : null;
if ( nextPlanItem != null && phaseRecorder.isDifferentPhase( nextPlanItem.getMojoExecution() ) )
{
final Schedule scheduleOfNext = nextPlanItem.getSchedule();
if ( scheduleOfNext == null || !scheduleOfNext.isParallel() )
{
waitForAppropriateUpstreamExecutionsToFinish( builtLogItem, nextPlanItem, projectBuild,
scheduleOfNext );
}
for ( ArtifactLink dependencyLink : dependencyLinks )
{
dependencyLink.resolveFromUpstream();
}
}
current = nextPlanItem;
}
final BuildSuccess summary = new BuildSuccess( projectBuild.getProject(), totalMojoTime ); // -
// waitingTime
reactorContext.getResult().addBuildSummary( summary );
eventCatapult.fire( ExecutionEvent.Type.ProjectSucceeded, projectBuild.getSession(), null );
}
catch ( Exception e )
{
builderCommon.handleBuildError( reactorContext, rootSession, projectBuild.getSession(),
projectBuild.getProject(), e, buildStartTime );
}
finally
{
if ( current != null )
{
executionPlan.forceAllComplete();
}
// muxer.setThisModuleComplete( projectBuild );
}
return null;
}
};
}
private void waitForAppropriateUpstreamExecutionsToFinish( BuildLogItem builtLogItem,
ExecutionPlanItem nextPlanItem,
ProjectSegment projectBuild, Schedule scheduleOfNext )
throws InterruptedException
{
for ( MavenProject upstreamProject : projectBuild.getImmediateUpstreamProjects() )
{
final MavenExecutionPlan upstreamPlan = executionPlans.get( upstreamProject );
final String nextPhase =
scheduleOfNext != null && scheduleOfNext.hasUpstreamPhaseDefined() ? scheduleOfNext.getUpstreamPhase()
: nextPlanItem.getLifecyclePhase();
final ExecutionPlanItem upstream = upstreamPlan.findLastInPhase( nextPhase );
if ( upstream != null )
{
long startWait = System.currentTimeMillis();
upstream.waitUntilDone();
builtLogItem.addWait( upstreamProject, upstream, startWait );
}
else if ( !upstreamPlan.containsPhase( nextPhase ) )
{
// Still a bit of a kludge; if we cannot connect in a sensible way to
// the upstream build plan we just revert to waiting for it all to
// complete. Real problem is per-mojo phase->lifecycle mapping
builtLogItem.addDependency( upstreamProject, "No phase tracking possible " );
upstreamPlan.waitUntilAllDone();
}
else
{
builtLogItem.addDependency( upstreamProject, "No schedule" );
}
}
}
private Collection<ArtifactLink> getUpstreamReactorDependencies( ProjectSegment projectBuild )
{
Collection<ArtifactLink> result = new ArrayList<ArtifactLink>();
for ( MavenProject upstreamProject : projectBuild.getTransitiveUpstreamProjects() )
{
Artifact upStreamArtifact = upstreamProject.getArtifact();
if ( upStreamArtifact != null )
{
Artifact dependencyArtifact = findDependency( projectBuild.getProject(), upStreamArtifact );
if ( dependencyArtifact != null )
{
result.add( new ArtifactLink( dependencyArtifact, upStreamArtifact ) );
}
}
Artifact upStreamTestScopedArtifact = findTestScopedArtifact( upstreamProject );
if ( upStreamTestScopedArtifact != null )
{
Artifact dependencyArtifact = findDependency( projectBuild.getProject(), upStreamArtifact );
if ( dependencyArtifact != null )
{
result.add( new ArtifactLink( dependencyArtifact, upStreamTestScopedArtifact ) );
}
}
}
return result;
}
private Artifact findTestScopedArtifact( MavenProject upstreamProject )
{
if ( upstreamProject == null )
{
return null;
}
List<Artifact> artifactList = upstreamProject.getAttachedArtifacts();
for ( Artifact artifact : artifactList )
{
if ( Artifact.SCOPE_TEST.equals( artifact.getScope() ) )
{
return artifact;
}
}
return null;
}
private static boolean isThreadLockedAndEmpty( Artifact artifact )
{
return artifact instanceof ThreadLockedArtifact && !( (ThreadLockedArtifact) artifact ).hasReal();
}
private static Artifact findDependency( MavenProject project, Artifact upStreamArtifact )
{
if ( upStreamArtifact == null || isThreadLockedAndEmpty( upStreamArtifact ) )
{
return null;
}
String key =
ArtifactUtils.key( upStreamArtifact.getGroupId(), upStreamArtifact.getArtifactId(),
upStreamArtifact.getVersion() );
final Set<Artifact> deps = project.getDependencyArtifacts();
for ( Artifact dep : deps )
{
String depKey = ArtifactUtils.key( dep.getGroupId(), dep.getArtifactId(), dep.getVersion() );
if ( key.equals( depKey ) )
{
return dep;
}
}
return null;
}
private void buildExecutionPlanItem( ExecutionPlanItem current, PhaseRecorder phaseRecorder, Schedule schedule,
ReactorContext reactorContext, ProjectSegment projectBuild,
DependencyContext dependencyContext )
throws LifecycleExecutionException
{
if ( schedule != null && schedule.isMojoSynchronized() )
{
synchronized ( current.getPlugin() )
{
buildExecutionPlanItem( reactorContext, current, projectBuild, dependencyContext, phaseRecorder );
}
}
else
{
buildExecutionPlanItem( reactorContext, current, projectBuild, dependencyContext, phaseRecorder );
}
}
private void buildExecutionPlanItem( ReactorContext reactorContext, ExecutionPlanItem node,
ProjectSegment projectBuild, DependencyContext dependencyContext,
PhaseRecorder phaseRecorder )
throws LifecycleExecutionException
{
MavenProject currentProject = projectBuild.getProject();
long buildStartTime = System.currentTimeMillis();
CurrentPhaseForThread.setPhase( node.getLifecyclePhase() );
MavenSession sessionForThisModule = projectBuild.getSession();
try
{
if ( reactorContext.getReactorBuildStatus().isHaltedOrBlacklisted( currentProject ) )
{
return;
}
BuilderCommon.attachToThread( currentProject );
mojoExecutor.execute( sessionForThisModule, node.getMojoExecution(), reactorContext.getProjectIndex(),
dependencyContext, phaseRecorder );
final BuildSuccess summary = new BuildSuccess( currentProject, System.currentTimeMillis() - buildStartTime );
reactorContext.getResult().addBuildSummary( summary );
}
finally
{
Thread.currentThread().setContextClassLoader( reactorContext.getOriginalContextClassLoader() );
}
}
public static boolean isWeaveMode( MavenExecutionRequest request )
{
return "true".equals( request.getUserProperties().getProperty( "maven3.weaveMode" ) );
}
public static void setWeaveMode( Properties properties )
{
properties.setProperty( "maven3.weaveMode", "true" );
}
static class ArtifactLink
{
private final Artifact artifactInThis;
private final Artifact upstream;
ArtifactLink( Artifact artifactInThis, Artifact upstream )
{
this.artifactInThis = artifactInThis;
this.upstream = upstream;
}
public void resolveFromUpstream()
{
artifactInThis.setFile( upstream.getFile() );
artifactInThis.setRepository( upstream.getRepository() );
artifactInThis.setResolved( true ); // Or maybe upstream.isResolved()....
}
}
}

View File

@ -16,6 +16,7 @@
package org.apache.maven.lifecycle;
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;

View File

@ -16,9 +16,11 @@ package org.apache.maven.lifecycle.internal;
*/
import junit.framework.TestCase;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.MavenExecutionPlan;
import org.apache.maven.lifecycle.internal.builder.BuilderCommon;
import org.apache.maven.lifecycle.internal.stub.LifecycleExecutionPlanCalculatorStub;
import org.apache.maven.lifecycle.internal.stub.LoggerStub;
import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub;

View File

@ -19,6 +19,7 @@ import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ProjectDependencyGraph;
import org.apache.maven.lifecycle.LifecycleNotFoundException;
import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException;
import org.apache.maven.lifecycle.internal.builder.multithreaded.ConcurrencyDependencyGraph;
import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub;
import org.apache.maven.plugin.InvalidPluginDescriptorException;
import org.apache.maven.plugin.MojoNotFoundException;

View File

@ -15,14 +15,17 @@ package org.apache.maven.lifecycle.internal;
* the License.
*/
import java.util.Iterator;
import junit.framework.TestCase;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.MavenExecutionPlan;
import org.apache.maven.lifecycle.internal.builder.weave.BuildLogItem;
import org.apache.maven.lifecycle.internal.builder.weave.ConcurrentBuildLogger;
import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub;
import org.apache.maven.project.MavenProject;
import java.util.Iterator;
/**
* @author Kristian Rosenvold
*/

View File

@ -16,6 +16,7 @@ package org.apache.maven.lifecycle.internal;
*/
import junit.framework.TestCase;
import org.apache.maven.lifecycle.Schedule;
import org.apache.maven.lifecycle.internal.stub.MojoExecutorStub;
import org.apache.maven.plugin.MojoExecution;

View File

@ -16,11 +16,14 @@ package org.apache.maven.lifecycle.internal;
*/
import junit.framework.TestCase;
import org.apache.maven.execution.DefaultMavenExecutionResult;
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.builder.BuilderCommon;
import org.apache.maven.lifecycle.internal.builder.weave.WeaveBuilder;
import org.apache.maven.lifecycle.internal.stub.ExecutionEventCatapultStub;
import org.apache.maven.lifecycle.internal.stub.LifecycleExecutionPlanCalculatorStub;
import org.apache.maven.lifecycle.internal.stub.LifecycleTaskSegmentCalculatorStub;
@ -93,10 +96,10 @@ public class LifecycleWeaveBuilderTest
ProjectBuildList projectBuildList = buildListCalculator.calculateProjectBuilds( session, taskSegments );
final MojoExecutorStub mojoExecutorStub = new MojoExecutorStub();
final LifecycleWeaveBuilder builder = getWeaveBuilder( mojoExecutorStub );
final WeaveBuilder builder = getWeaveBuilder( mojoExecutorStub );
final ReactorContext buildContext = createBuildContext( session );
ReactorBuildStatus reactorBuildStatus = new ReactorBuildStatus( session.getProjectDependencyGraph() );
builder.build( projectBuildList, buildContext, taskSegments, session, service, reactorBuildStatus );
builder.build( session, buildContext, projectBuildList, taskSegments, reactorBuildStatus );
LifecycleExecutionPlanCalculatorStub lifecycleExecutionPlanCalculatorStub =
new LifecycleExecutionPlanCalculatorStub();
@ -123,11 +126,11 @@ public class LifecycleWeaveBuilderTest
return new ReactorContext( mavenExecutionResult, null, null, reactorBuildStatus );
}
private LifecycleWeaveBuilder getWeaveBuilder( MojoExecutor mojoExecutor )
private WeaveBuilder getWeaveBuilder( MojoExecutor mojoExecutor )
{
final BuilderCommon builderCommon = getBuilderCommon();
final LoggerStub loggerStub = new LoggerStub();
return new LifecycleWeaveBuilder( mojoExecutor, builderCommon, loggerStub, new ExecutionEventCatapultStub() );
return new WeaveBuilder( mojoExecutor, builderCommon, loggerStub, new ExecutionEventCatapultStub(), new LifecycleDebugLogger( loggerStub ) );
}
private BuilderCommon getBuilderCommon()

View File

@ -16,6 +16,7 @@ package org.apache.maven.lifecycle.internal;
*/
import junit.framework.TestCase;
import org.apache.maven.lifecycle.MavenExecutionPlan;
import org.apache.maven.lifecycle.internal.stub.LifecycleExecutionPlanCalculatorStub;
import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub;

View File

@ -216,6 +216,7 @@ public class ProjectDependencyGraphStub
MavenExecutionRequest mavenExecutionRequest = new DefaultMavenExecutionRequest();
mavenExecutionRequest.setExecutionListener( new AbstractExecutionListener() );
mavenExecutionRequest.setGoals( Arrays.asList( "clean", "aggr", "install" ) );
mavenExecutionRequest.setDegreeOfConcurrency( 1 );
final MavenSession session = new MavenSession( null, null, mavenExecutionRequest, defaultMavenExecutionResult );
final ProjectDependencyGraphStub dependencyGraphStub = new ProjectDependencyGraphStub();
session.setProjectDependencyGraph( dependencyGraphStub );

View File

@ -99,6 +99,8 @@ public class CLIManager
public static final String LEGACY_LOCAL_REPOSITORY = "llr";
public static final String BUILDER = "b";
protected Options options;
@SuppressWarnings( "static-access" )
@ -136,6 +138,7 @@ public class CLIManager
options.addOption( OptionBuilder.withLongOpt( "encrypt-password" ).hasOptionalArg().withDescription( "Encrypt server password" ).create( ENCRYPT_PASSWORD ) );
options.addOption( OptionBuilder.withLongOpt( "threads" ).hasArg().withDescription( "Thread count, for instance 2.0C where C is core multiplied" ).create( THREADS ) );
options.addOption( OptionBuilder.withLongOpt( "legacy-local-repository" ).withDescription( "Use Maven 2 Legacy Local Repository behaviour, ie no use of _remote.repositories. Can also be activated by using -Dmaven.legacyLocalRepo=true" ).create( LEGACY_LOCAL_REPOSITORY ) );
options.addOption( OptionBuilder.withLongOpt( "builder" ).hasArg().withDescription( "The id of the build strategy to use." ).create( BUILDER ) );
// Adding this back in for compatibility with the verifier that hard codes this option.
options.addOption( OptionBuilder.withLongOpt( "no-plugin-registry" ).withDescription( "Ineffective, only kept for backward compatibility" ).create( "npr" ) );

View File

@ -56,7 +56,6 @@ import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionRequestPopulator;
import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.lifecycle.internal.LifecycleWeaveBuilder;
import org.apache.maven.model.building.ModelProcessor;
import org.apache.maven.project.MavenProject;
import org.apache.maven.properties.internal.EnvironmentUtils;
@ -1069,6 +1068,18 @@ public class MavenCli
request.setLocalRepositoryPath( localRepoProperty );
}
request.setCacheNotFound( true );
request.setCacheTransferError( false );
//
// Builder, concurrency and parallelism
//
// We preserve the existing methods for builder selection which is to look for various inputs in the threading
// configuration. We don't have an easy way to allow a pluggable builder to provide its own configuration parameters
// but this is sufficient for now. Ultimately we want components like Builders to provide a way to extend the command
// line to accept its own configuration parameters.
//
final String threadConfiguration = commandLine.hasOption( CLIManager.THREADS )
? commandLine.getOptionValue( CLIManager.THREADS )
: request.getSystemProperties().getProperty(
@ -1076,16 +1087,26 @@ public class MavenCli
if ( threadConfiguration != null )
{
request.setPerCoreThreadCount( threadConfiguration.contains( "C" ) );
if ( threadConfiguration.contains( "W" ) )
{
LifecycleWeaveBuilder.setWeaveMode( request.getUserProperties() );
request.setBuilderId( "weave" );
}
request.setThreadCount( threadConfiguration.replace( "C", "" ).replace( "W", "" ).replace( "auto", "" ) );
else
{
request.setBuilderId( "multithreaded" );
}
request.setCacheNotFound( true );
request.setCacheTransferError( false );
int threads =
threadConfiguration.contains( "C" ) ? Integer.valueOf( threadConfiguration.replace( "C", "" ) )
* Runtime.getRuntime().availableProcessors() : Integer.valueOf( threadConfiguration );
request.setDegreeOfConcurrency(threads);
}
if ( commandLine.hasOption( CLIManager.BUILDER ) )
{
request.setBuilderId( commandLine.getOptionValue( CLIManager.BUILDER ) );
}
return request;
}

View File

@ -212,7 +212,7 @@ public class ExecutionEventLogger
long time = finish.getTime() - session.getRequest().getStartTime().getTime();
String wallClock = session.getRequest().isThreadConfigurationPresent() ? " (Wall Clock)" : "";
String wallClock = session.getRequest().getDegreeOfConcurrency() > 1 ? " (Wall Clock)" : "";
logger.info( "Total time: " + getFormattedTime( time ) + wallClock );