MNG-5557: Constrain the set of projects that are passed into the ReactorReader as specified by --projects

- Add some documentation about the lifecycle within DefaultMaven
- Remove the use of DelegatingLocalArtifactRepository in DefaultMaven as it has been replaced with the use of the
  ReactorReader which is an implementation of Aether's WorkspaceReader
- Localize the manipulation of the ProjectBuildingRequest to getProjectsFromReactor()
- Deprecated getLogger()
This commit is contained in:
Jason van Zyl 2014-01-06 10:37:17 -05:00
parent 3929f55c31
commit 7c3052ddeb
3 changed files with 177 additions and 133 deletions

View File

@ -59,7 +59,6 @@
import org.apache.maven.project.ProjectBuildingRequest; import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.project.ProjectBuildingResult; import org.apache.maven.project.ProjectBuildingResult;
import org.apache.maven.project.ProjectSorter; import org.apache.maven.project.ProjectSorter;
import org.apache.maven.repository.DelegatingLocalArtifactRepository;
import org.apache.maven.repository.LocalRepositoryNotAccessibleException; import org.apache.maven.repository.LocalRepositoryNotAccessibleException;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils; import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.apache.maven.settings.Mirror; import org.apache.maven.settings.Mirror;
@ -162,8 +161,8 @@ public MavenExecutionResult execute( MavenExecutionRequest request )
catch ( RuntimeException e ) catch ( RuntimeException e )
{ {
result = result =
addExceptionToResult( new DefaultMavenExecutionResult(), addExceptionToResult( new DefaultMavenExecutionResult(), new InternalErrorException( "Internal error: "
new InternalErrorException( "Internal error: " + e, e ) ); + e, e ) );
} }
finally finally
{ {
@ -173,9 +172,34 @@ public MavenExecutionResult execute( MavenExecutionRequest request )
return result; return result;
} }
//
// 1) Setup initial properties.
//
// 2) Validate local repository directory is accessible.
//
// 3) Create RepositorySystemSession.
//
// 4) Create MavenSession.
//
// 5) Execute AbstractLifecycleParticipant.afterSessionStart(session)
//
// 6) Get reactor projects looking for read errors, and duplicate declarations
//
// 7) Create ProjectDependencyGraph using trimming which takes into account --projects and reactor mode. This ensures
// that the projects passed into the ReactorReader are only those specified.
//
// 8) Create ReactorReader with the project map created in 7)
//
// 9) Execute AbstractLifecycleParticipant.afterProjectsRead(session)
//
// 10) Create ProjectDependencyGraph without trimming (as trimming was done in 7). A new topological sort is required after
// the execution of 9) as the AbstractLifecycleParticipants are free to mutate the MavenProject instances, which may change
// dependencies which can, in turn, affect the build order.
//
// 11) Execute LifecycleStarter.start()
//
private MavenExecutionResult doExecute( MavenExecutionRequest request ) private MavenExecutionResult doExecute( MavenExecutionRequest request )
{ {
//TODO: Need a general way to inject standard properties
if ( request.getStartTime() != null ) if ( request.getStartTime() != null )
{ {
request.getSystemProperties().put( "${build.timestamp}", request.getSystemProperties().put( "${build.timestamp}",
@ -195,11 +219,6 @@ private MavenExecutionResult doExecute( MavenExecutionRequest request )
return addExceptionToResult( result, e ); return addExceptionToResult( result, e );
} }
DelegatingLocalArtifactRepository delegatingLocalArtifactRepository =
new DelegatingLocalArtifactRepository( request.getLocalRepository() );
request.setLocalRepository( delegatingLocalArtifactRepository );
DefaultRepositorySystemSession repoSession = (DefaultRepositorySystemSession) newRepositorySession( request ); DefaultRepositorySystemSession repoSession = (DefaultRepositorySystemSession) newRepositorySession( request );
MavenSession session = new MavenSession( container, repoSession, request, result ); MavenSession session = new MavenSession( container, repoSession, request, result );
@ -219,46 +238,50 @@ private MavenExecutionResult doExecute( MavenExecutionRequest request )
eventCatapult.fire( ExecutionEvent.Type.ProjectDiscoveryStarted, session, null ); eventCatapult.fire( ExecutionEvent.Type.ProjectDiscoveryStarted, session, null );
request.getProjectBuildingRequest().setRepositorySession( session.getRepositorySession() );
//TODO: optimize for the single project or no project
List<MavenProject> projects; List<MavenProject> projects;
try try
{ {
projects = getProjectsForMavenReactor( request ); projects = getProjectsForMavenReactor( session );
} }
catch ( ProjectBuildingException e ) catch ( ProjectBuildingException e )
{ {
return addExceptionToResult( result, e ); return addExceptionToResult( result, e );
} }
session.setProjects( projects ); //
// This creates the graph and trims the projects down based on the user request using something like:
//
// -pl project0,project2 eclipse:eclipse
//
ProjectDependencyGraph projectDependencyGraph = createProjectDependencyGraph( projects, request, result, true );
result.setTopologicallySortedProjects( session.getProjects() ); session.setProjects( projectDependencyGraph.getSortedProjects() );
result.setProject( session.getTopLevelProject() ); if ( result.hasExceptions() )
try
{ {
Map<String, MavenProject> projectMap; return result;
projectMap = getProjectMap( session.getProjects() ); }
//
// Desired order of precedence for local artifact repositories // Desired order of precedence for local artifact repositories
// //
// Reactor // Reactor
// Workspace // Workspace
// User Local Repository // User Local Repository
ReactorReader reactorRepository = new ReactorReader( projectMap ); //
ReactorReader reactorRepository = null;
repoSession.setWorkspaceReader( ChainedWorkspaceReader.newInstance( reactorRepository, try
repoSession.getWorkspaceReader() ) ); {
reactorRepository = new ReactorReader( session, getProjectMap( session.getProjects() ) );
} }
catch ( DuplicateProjectException e ) catch ( DuplicateProjectException e )
{ {
return addExceptionToResult( result, e ); return addExceptionToResult( result, e );
} }
repoSession.setWorkspaceReader( ChainedWorkspaceReader.newInstance( reactorRepository,
repoSession.getWorkspaceReader() ) );
repoSession.setReadOnly(); repoSession.setReadOnly();
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
@ -280,40 +303,29 @@ private MavenExecutionResult doExecute( MavenExecutionRequest request )
Thread.currentThread().setContextClassLoader( originalClassLoader ); Thread.currentThread().setContextClassLoader( originalClassLoader );
} }
try //
{ // The projects need to be topologically after the participants have run their afterProjectsRead(session)
ProjectSorter projectSorter = new ProjectSorter( session.getProjects() ); // because the participant is free to change the dependencies of a project which can potentially change the
// topological order of the projects, and therefore can potentially change the build order.
ProjectDependencyGraph projectDependencyGraph = createDependencyGraph( projectSorter, request ); //
// Note that participants may affect the topological order of the projects but it is
session.setProjects( projectDependencyGraph.getSortedProjects() ); // not expected that a participant will add or remove projects from the session.
//
session.setProjectDependencyGraph( projectDependencyGraph ); projectDependencyGraph = createProjectDependencyGraph( session.getProjects(), request, result, false );
}
catch ( CycleDetectedException e )
{
String message = "The projects in the reactor contain a cyclic reference: " + e.getMessage();
ProjectCycleException error = new ProjectCycleException( message, e );
return addExceptionToResult( result, error );
}
catch ( org.apache.maven.project.DuplicateProjectException e )
{
return addExceptionToResult( result, e );
}
catch ( MavenExecutionException e )
{
return addExceptionToResult( result, e );
}
result.setTopologicallySortedProjects( session.getProjects() );
if ( result.hasExceptions() ) if ( result.hasExceptions() )
{ {
return result; return result;
} }
session.setProjects( projectDependencyGraph.getSortedProjects() );
session.setProjectDependencyGraph( projectDependencyGraph );
result.setTopologicallySortedProjects( session.getProjects() );
result.setProject( session.getTopLevelProject() );
lifecycleStarter.execute( session ); lifecycleStarter.execute( session );
validateActivatedProfiles( session.getProjects(), request.getActiveProfiles() ); validateActivatedProfiles( session.getProjects(), request.getActiveProfiles() );
@ -465,8 +477,7 @@ else if ( request.isUpdateSnapshots() )
private String getUserAgent() private String getUserAgent()
{ {
return "Apache-Maven/" + getMavenVersion() return "Apache-Maven/" + getMavenVersion() + " (Java " + System.getProperty( "java.version" ) + "; "
+ " (Java " + System.getProperty( "java.version" ) + "; "
+ System.getProperty( "os.name" ) + " " + System.getProperty( "os.version" ) + ")"; + System.getProperty( "os.name" ) + " " + System.getProperty( "os.version" ) + ")";
} }
@ -564,9 +575,13 @@ private MavenExecutionResult addExceptionToResult( MavenExecutionResult result,
return result; return result;
} }
private List<MavenProject> getProjectsForMavenReactor( MavenExecutionRequest request ) private List<MavenProject> getProjectsForMavenReactor( MavenSession session )
throws ProjectBuildingException throws ProjectBuildingException
{ {
MavenExecutionRequest request = session.getRequest();
request.getProjectBuildingRequest().setRepositorySession( session.getRepositorySession() );
List<MavenProject> projects = new ArrayList<MavenProject>(); List<MavenProject> projects = new ArrayList<MavenProject>();
// We have no POM file. // We have no POM file.
@ -587,7 +602,49 @@ private List<MavenProject> getProjectsForMavenReactor( MavenExecutionRequest req
return projects; return projects;
} }
private Map<String, MavenProject> getProjectMap( List<MavenProject> projects ) private void collectProjects( List<MavenProject> projects, List<File> files, MavenExecutionRequest request )
throws ProjectBuildingException
{
ProjectBuildingRequest projectBuildingRequest = request.getProjectBuildingRequest();
List<ProjectBuildingResult> results =
projectBuilder.build( files, request.isRecursive(), projectBuildingRequest );
boolean problems = false;
for ( ProjectBuildingResult result : results )
{
projects.add( result.getProject() );
if ( !result.getProblems().isEmpty() && logger.isWarnEnabled() )
{
logger.warn( "" );
logger.warn( "Some problems were encountered while building the effective model for "
+ result.getProject().getId() );
for ( ModelProblem problem : result.getProblems() )
{
String location = ModelProblemUtils.formatLocation( problem, result.getProjectId() );
logger.warn( problem.getMessage() + ( StringUtils.isNotEmpty( location ) ? " @ " + location : "" ) );
}
problems = true;
}
}
if ( problems )
{
logger.warn( "" );
logger.warn( "It is highly recommended to fix these problems"
+ " because they threaten the stability of your build." );
logger.warn( "" );
logger.warn( "For this reason, future Maven versions might no"
+ " longer support building such malformed projects." );
logger.warn( "" );
}
}
private Map<String, MavenProject> getProjectMap( Collection<MavenProject> projects )
throws DuplicateProjectException throws DuplicateProjectException
{ {
Map<String, MavenProject> index = new LinkedHashMap<String, MavenProject>(); Map<String, MavenProject> index = new LinkedHashMap<String, MavenProject>();
@ -629,47 +686,6 @@ private Map<String, MavenProject> getProjectMap( List<MavenProject> projects )
return index; return index;
} }
private void collectProjects( List<MavenProject> projects, List<File> files, MavenExecutionRequest request )
throws ProjectBuildingException
{
ProjectBuildingRequest projectBuildingRequest = request.getProjectBuildingRequest();
List<ProjectBuildingResult> results = projectBuilder.build( files, request.isRecursive(), projectBuildingRequest );
boolean problems = false;
for ( ProjectBuildingResult result : results )
{
projects.add( result.getProject() );
if ( !result.getProblems().isEmpty() && logger.isWarnEnabled() )
{
logger.warn( "" );
logger.warn( "Some problems were encountered while building the effective model for "
+ result.getProject().getId() );
for ( ModelProblem problem : result.getProblems() )
{
String location = ModelProblemUtils.formatLocation( problem, result.getProjectId() );
logger.warn( problem.getMessage() + ( StringUtils.isNotEmpty( location ) ? " @ " + location : "" ) );
}
problems = true;
}
}
if ( problems )
{
logger.warn( "" );
logger.warn( "It is highly recommended to fix these problems"
+ " because they threaten the stability of your build." );
logger.warn( "" );
logger.warn( "For this reason, future Maven versions might no"
+ " longer support building such malformed projects." );
logger.warn( "" );
}
}
private void validateActivatedProfiles( List<MavenProject> projects, List<String> activeProfileIds ) private void validateActivatedProfiles( List<MavenProject> projects, List<String> activeProfileIds )
{ {
Collection<String> notActivatedProfileIds = new LinkedHashSet<String>( activeProfileIds ); Collection<String> notActivatedProfileIds = new LinkedHashSet<String>( activeProfileIds );
@ -689,27 +705,55 @@ private void validateActivatedProfiles( List<MavenProject> projects, List<String
} }
} }
@Deprecated // 5 January 2014
protected Logger getLogger() protected Logger getLogger()
{ {
return logger; return logger;
} }
private ProjectDependencyGraph createDependencyGraph( ProjectSorter sorter, MavenExecutionRequest request ) private ProjectDependencyGraph createProjectDependencyGraph( Collection<MavenProject> projects, MavenExecutionRequest request,
throws MavenExecutionException MavenExecutionResult result, boolean trimming )
{ {
ProjectDependencyGraph graph = new DefaultProjectDependencyGraph( sorter ); ProjectDependencyGraph projectDependencyGraph = null;
List<MavenProject> activeProjects = sorter.getSortedProjects(); try
{
ProjectSorter projectSorter = new ProjectSorter( projects );
activeProjects = trimSelectedProjects( activeProjects, graph, request ); projectDependencyGraph = new DefaultProjectDependencyGraph( projectSorter );
if ( trimming )
{
List<MavenProject> activeProjects = projectSorter.getSortedProjects();
activeProjects = trimSelectedProjects( activeProjects, projectDependencyGraph, request );
activeProjects = trimResumedProjects( activeProjects, request ); activeProjects = trimResumedProjects( activeProjects, request );
if ( activeProjects.size() != sorter.getSortedProjects().size() ) if ( activeProjects.size() != projectSorter.getSortedProjects().size() )
{ {
graph = new FilteredProjectDependencyGraph( graph, activeProjects ); projectDependencyGraph =
new FilteredProjectDependencyGraph( projectDependencyGraph, activeProjects );
}
}
}
catch ( CycleDetectedException e )
{
String message = "The projects in the reactor contain a cyclic reference: " + e.getMessage();
ProjectCycleException error = new ProjectCycleException( message, e );
addExceptionToResult( result, error );
}
catch ( org.apache.maven.project.DuplicateProjectException e )
{
addExceptionToResult( result, e );
}
catch ( MavenExecutionException e )
{
addExceptionToResult( result, e );
} }
return graph; return projectDependencyGraph;
} }
private List<MavenProject> trimSelectedProjects( List<MavenProject> projects, ProjectDependencyGraph graph, private List<MavenProject> trimSelectedProjects( List<MavenProject> projects, ProjectDependencyGraph graph,

View File

@ -30,6 +30,7 @@
import java.util.Map; import java.util.Map;
import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProject;
import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.repository.WorkspaceReader; import org.eclipse.aether.repository.WorkspaceReader;
@ -52,7 +53,7 @@ class ReactorReader
private WorkspaceRepository repository; private WorkspaceRepository repository;
public ReactorReader( Map<String, MavenProject> reactorProjects ) public ReactorReader( MavenSession session, Map<String, MavenProject> reactorProjects )
{ {
projectsByGAV = reactorProjects; projectsByGAV = reactorProjects;
@ -186,9 +187,7 @@ private boolean hasBeenPackaged( MavenProject project )
* *
* @param project The project to try to resolve the artifact from, must not be <code>null</code>. * @param project The project to try to resolve the artifact from, must not be <code>null</code>.
* @param requestedArtifact The artifact to resolve, must not be <code>null</code>. * @param requestedArtifact The artifact to resolve, must not be <code>null</code>.
* @return The matching artifact from the project or <code>null</code> if not found. * @return The matching artifact from the project or <code>null</code> if not found. Note that this
*
* Note that this
*/ */
private Artifact findMatchingArtifact( MavenProject project, Artifact requestedArtifact ) private Artifact findMatchingArtifact( MavenProject project, Artifact requestedArtifact )
{ {
@ -202,7 +201,7 @@ private Artifact findMatchingArtifact( MavenProject project, Artifact requestedA
for ( Artifact attachedArtifact : RepositoryUtils.toArtifacts( project.getAttachedArtifacts() ) ) for ( Artifact attachedArtifact : RepositoryUtils.toArtifacts( project.getAttachedArtifacts() ) )
{ {
if ( attachedArtifactComparison ( requestedArtifact, attachedArtifact ) ) if ( attachedArtifactComparison( requestedArtifact, attachedArtifact ) )
{ {
return attachedArtifact; return attachedArtifact;
} }

View File

@ -20,6 +20,7 @@
*/ */
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
@ -70,7 +71,7 @@ public class ProjectSorter
// In this case, both the verify and the report goals are called // In this case, both the verify and the report goals are called
// in a different lifecycle. Though the compiler-plugin has a valid usecase, although // in a different lifecycle. Though the compiler-plugin has a valid usecase, although
// that seems to work fine. We need to take versions and lifecycle into account. // that seems to work fine. We need to take versions and lifecycle into account.
public ProjectSorter( List<MavenProject> projects ) public ProjectSorter( Collection<MavenProject> projects )
throws CycleDetectedException, DuplicateProjectException throws CycleDetectedException, DuplicateProjectException
{ {
dag = new DAG(); dag = new DAG();