Index: maven-core/src/main/java/org/apache/maven/execution/MavenSession.java =================================================================== --- maven-core/src/main/java/org/apache/maven/execution/MavenSession.java (revision 540247) +++ maven-core/src/main/java/org/apache/maven/execution/MavenSession.java (working copy) @@ -20,12 +20,17 @@ */ import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.lifecycle.dispatcher.LinearProjectDispatcher; +import org.apache.maven.lifecycle.dispatcher.ParallelProjectDispatcher; +import org.apache.maven.lifecycle.dispatcher.ProjectDispatcher; import org.apache.maven.monitor.event.EventDispatcher; import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.project.DuplicateProjectException; import org.apache.maven.project.MavenProject; import org.apache.maven.settings.Settings; import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.util.dag.CycleDetectedException; import java.util.Date; import java.util.List; @@ -59,6 +64,8 @@ private final Date startTime; + private ProjectDispatcher projectDispatcher; + public MavenSession( PlexusContainer container, Settings settings, ArtifactRepository localRepository, EventDispatcher eventDispatcher, ReactorManager reactorManager, List goals, String executionRootDir, Properties executionProperties, Date startTime ) @@ -150,6 +157,26 @@ return reactorManager.getSortedProjects(); } + public ProjectDispatcher getProjectDispatcher() + { + try { + if( this.projectDispatcher == null ) + { + this.projectDispatcher = new ParallelProjectDispatcher( getSortedProjects() ); + } + + return this.projectDispatcher; + + } catch (CycleDetectedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (DuplicateProjectException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return null; + } + public String getExecutionRootDirectory() { return executionRootDir; Index: maven-core/src/main/java/org/apache/maven/execution/ReactorManager.java =================================================================== --- maven-core/src/main/java/org/apache/maven/execution/ReactorManager.java (revision 540247) +++ maven-core/src/main/java/org/apache/maven/execution/ReactorManager.java (working copy) @@ -53,9 +53,15 @@ private Map buildSuccessesByProject = new HashMap(); + /** + * List of MavenProjects + */ + List projects; + public ReactorManager( List projects ) throws CycleDetectedException, DuplicateProjectException { + this.projects = projects; this.sorter = new ProjectSorter( projects ); } @@ -192,4 +198,8 @@ { return buildFailuresByProject.size() + buildSuccessesByProject.size() > 1; } + + public ProjectSorter getSorter() { + return sorter; + } } Index: maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycleExecutor.java =================================================================== --- maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycleExecutor.java (revision 540247) +++ maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycleExecutor.java (working copy) @@ -29,6 +29,7 @@ import org.apache.maven.execution.MavenSession; import org.apache.maven.execution.ReactorManager; import org.apache.maven.extension.ExtensionManager; +import org.apache.maven.lifecycle.dispatcher.ProjectDispatcher; import org.apache.maven.lifecycle.mapping.LifecycleMapping; import org.apache.maven.model.Extension; import org.apache.maven.model.Plugin; @@ -186,7 +187,7 @@ } } - private void executeTaskSegments( List taskSegments, ReactorManager rm, MavenSession session, + protected void executeTaskSegments( List taskSegments, ReactorManager rm, MavenSession session, MavenProject rootProject, EventDispatcher dispatcher ) throws LifecycleExecutionException, BuildFailureException { @@ -245,63 +246,43 @@ } else { - List sortedProjects = session.getSortedProjects(); + buildProjects(rm, session, segment, dispatcher); + + } + } + } - // iterate over projects, and execute on each... - for ( Iterator projectIterator = sortedProjects.iterator(); projectIterator.hasNext(); ) - { - MavenProject currentProject = (MavenProject) projectIterator.next(); + protected void buildProjects(ReactorManager rm, MavenSession session, TaskSegment segment, EventDispatcher dispatcher) throws BuildFailureException, LifecycleExecutionException { + ProjectDispatcher pd = session.getProjectDispatcher(); + - if ( !rm.isBlackListed( currentProject ) ) - { - line(); + // iterate over projects, and execute on each... + while ( pd.hasMore() ) + { + MavenProject currentProject = pd.dispatchProject(); - getLogger().info( "Building " + currentProject.getName() ); + if ( !rm.isBlackListed( currentProject ) ) + { + buildProject(currentProject, rm, session, segment, dispatcher); + } + else + { + line(); - getLogger().info( " " + segment ); + getLogger().info( "SKIPPING " + currentProject.getName() ); - line(); + getLogger().info( " " + segment ); - // !! This is ripe for refactoring to an aspect. - // Event monitoring. - String event = MavenEvents.PROJECT_EXECUTION; + getLogger().info( + "This project has been banned from further executions due to previous failures." ); - long buildStartTime = System.currentTimeMillis(); + line(); + } + pd.returnProject(currentProject); + } + } - String target = currentProject.getId() + " ( " + segment + " )"; - dispatcher.dispatchStart( event, target ); - - for ( Iterator goalIterator = segment.getTasks().iterator(); goalIterator.hasNext(); ) - { - String task = (String) goalIterator.next(); - - executeGoalAndHandleFailures( task, session, currentProject, dispatcher, event, rm, - buildStartTime, target ); - } - - rm.registerBuildSuccess( currentProject, System.currentTimeMillis() - buildStartTime ); - - dispatcher.dispatchEnd( event, target ); - } - else - { - line(); - - getLogger().info( "SKIPPING " + currentProject.getName() ); - - getLogger().info( " " + segment ); - - getLogger().info( - "This project has been banned from further executions due to previous failures." ); - - line(); - } - } - } - } - } - - private void executeGoalAndHandleFailures( String task, MavenSession session, MavenProject project, + protected void executeGoalAndHandleFailures( String task, MavenSession session, MavenProject project, EventDispatcher dispatcher, String event, ReactorManager rm, long buildStartTime, String target ) throws BuildFailureException, LifecycleExecutionException @@ -1594,8 +1575,43 @@ return phaseToLifecycleMap; } - private static class TaskSegment + protected void buildProject(MavenProject currentProject, ReactorManager rm, MavenSession session, TaskSegment segment, EventDispatcher dispatcher) + throws BuildFailureException, LifecycleExecutionException { + line(); + + getLogger().info("Building " + currentProject.getName()); + + getLogger().info(" " + segment); + + line(); + + // !! This is ripe for refactoring to an aspect. + // Event monitoring. + String event = MavenEvents.PROJECT_EXECUTION; + + long buildStartTime = System.currentTimeMillis(); + + String target = currentProject.getId() + " ( " + segment + " )"; + dispatcher.dispatchStart(event, target); + + for (Iterator goalIterator = segment.getTasks().iterator(); goalIterator + .hasNext();) { + String task = (String) goalIterator.next(); + + executeGoalAndHandleFailures(task, session, currentProject, + dispatcher, event, rm, buildStartTime, target); + } + + rm.registerBuildSuccess(currentProject, System + .currentTimeMillis() + - buildStartTime); + + dispatcher.dispatchEnd(event, target); + } + + static class TaskSegment + { private boolean aggregate; private List tasks = new ArrayList(); Index: maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/LinearProjectDispatcher.java =================================================================== --- maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/LinearProjectDispatcher.java (revision 0) +++ maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/LinearProjectDispatcher.java (revision 0) @@ -0,0 +1,45 @@ +package org.apache.maven.lifecycle.dispatcher; + +import java.util.Iterator; +import java.util.List; + +import org.apache.maven.project.DuplicateProjectException; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectSorter; +import org.codehaus.plexus.util.dag.CycleDetectedException; + +public class LinearProjectDispatcher implements ProjectDispatcher +{ + private final ProjectSorter sorter; + private Iterator projectIterator; + private int inDispatch = 0; + + public LinearProjectDispatcher(List projects) throws CycleDetectedException, DuplicateProjectException + { + this.sorter = new ProjectSorter( projects ); + this.projectIterator = this.sorter.getSortedProjects().iterator(); + this.inDispatch = 0; + } + + public boolean dispatchAvailable() { + return (inDispatch == 0); + } + + public MavenProject dispatchProject() { + MavenProject mp = (MavenProject) projectIterator.next(); + inDispatch++; + return mp; + } + + public boolean hasMore() { + return projectIterator.hasNext(); + } + + public void returnProject(MavenProject mavenProject) { + inDispatch--; + } + + public int projectsInDispatch() { + return inDispatch; + } +} Index: maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/ParallelProjectDispatcher.java =================================================================== --- maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/ParallelProjectDispatcher.java (revision 0) +++ maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/ParallelProjectDispatcher.java (revision 0) @@ -0,0 +1,127 @@ +package org.apache.maven.lifecycle.dispatcher; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.maven.artifact.ArtifactUtils; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.DuplicateProjectException; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectGraph; +import org.codehaus.plexus.util.dag.CycleDetectedException; +import org.codehaus.plexus.util.dag.Vertex; + +public class ParallelProjectDispatcher implements ProjectDispatcher +{ + private ProjectGraph graph; + private List nonDispatchedProjects; + private List allProjects; + private int inDispatch = 0; + + public ParallelProjectDispatcher(List projects) throws CycleDetectedException, DuplicateProjectException + { + this.graph = new ProjectGraph( projects ); + nonDispatchedProjects = new ArrayList(projects); + allProjects = new ArrayList(projects); + inDispatch = 0; + } + + + public boolean dispatchAvailable() { + synchronized(this) + { + return(getReadyList().size() > 0); + } + } + + public MavenProject dispatchProject() + { + synchronized(this) + { + List readyList = getReadyList(); + + for(Iterator i = readyList.iterator();i.hasNext();) + { + MavenProject project = ((MavenProject)i.next()); + System.out.println("CANDIDATE : " + ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ) ); + } + + MavenProject project = (MavenProject)readyList.get(0); + nonDispatchedProjects.remove(project); + inDispatch++; + return project; + } + } + + private List getReadyList() + { + + List readyList = new ArrayList(); + + List vtxs = this.graph.getDag().getVerticies(); + + for(Iterator i = vtxs.iterator(); i.hasNext(); ) + { + Vertex v = (Vertex)i.next(); + MavenProject project = getProjectById(v.getLabel()); + if( v.getChildren().size() == 0 && nonDispatchedProjects.contains(project)) + { + readyList.add(project); + } + } + + return readyList; + + } + + private MavenProject getProjectById(String id) + { + for(Iterator i = allProjects.iterator();i.hasNext();) + { + MavenProject project = (MavenProject)i.next(); + String thisId = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ); + + if( thisId.equals(id) ) + return project; + } + return null; + } + + /** + * Any more to be processed + */ + public boolean hasMore() { + synchronized(this) + { + return ( nonDispatchedProjects.size() > 0); + } + } + + public void returnProject(MavenProject project) { + synchronized(this) + { + nonDispatchedProjects.remove(project); + + String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ); + + Vertex v = graph.getDag().getVertex(id); + + for(Iterator vp = v.getParents().iterator(); vp.hasNext(); ) + { + Vertex parent = (Vertex)vp.next(); + parent.removeEdgeTo(v); + } + inDispatch --; + } + } + + + public int projectsInDispatch() { + synchronized(this) + { + return inDispatch; + } + } + +} Index: maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/ProjectDispatcher.java =================================================================== --- maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/ProjectDispatcher.java (revision 0) +++ maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/ProjectDispatcher.java (revision 0) @@ -0,0 +1,33 @@ +package org.apache.maven.lifecycle.dispatcher; + +import org.apache.maven.project.MavenProject; + +public interface ProjectDispatcher +{ + /** + * Are there any more projects to be dispatched + * @return + */ + boolean hasMore(); + + /** + * Is a dispatch available right now? + * @return + */ + boolean dispatchAvailable(); + + /** + * Dispatch a project + * @return + */ + MavenProject dispatchProject(); + + /** + * Return a project. + * @param mavenProject + */ + void returnProject(MavenProject mavenProject); + + int projectsInDispatch(); + +} Index: maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/LinearProjectDispatcher.java =================================================================== --- maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/LinearProjectDispatcher.java (revision 0) +++ maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/LinearProjectDispatcher.java (revision 0) @@ -0,0 +1,45 @@ +package org.apache.maven.lifecycle.dispatcher; + +import java.util.Iterator; +import java.util.List; + +import org.apache.maven.project.DuplicateProjectException; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectSorter; +import org.codehaus.plexus.util.dag.CycleDetectedException; + +public class LinearProjectDispatcher implements ProjectDispatcher +{ + private final ProjectSorter sorter; + private Iterator projectIterator; + private int inDispatch = 0; + + public LinearProjectDispatcher(List projects) throws CycleDetectedException, DuplicateProjectException + { + this.sorter = new ProjectSorter( projects ); + this.projectIterator = this.sorter.getSortedProjects().iterator(); + this.inDispatch = 0; + } + + public boolean dispatchAvailable() { + return (inDispatch == 0); + } + + public MavenProject dispatchProject() { + MavenProject mp = (MavenProject) projectIterator.next(); + inDispatch++; + return mp; + } + + public boolean hasMore() { + return projectIterator.hasNext(); + } + + public void returnProject(MavenProject mavenProject) { + inDispatch--; + } + + public int projectsInDispatch() { + return inDispatch; + } +} Index: maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/ParallelProjectDispatcher.java =================================================================== --- maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/ParallelProjectDispatcher.java (revision 0) +++ maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/ParallelProjectDispatcher.java (revision 0) @@ -0,0 +1,127 @@ +package org.apache.maven.lifecycle.dispatcher; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.maven.artifact.ArtifactUtils; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.DuplicateProjectException; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectGraph; +import org.codehaus.plexus.util.dag.CycleDetectedException; +import org.codehaus.plexus.util.dag.Vertex; + +public class ParallelProjectDispatcher implements ProjectDispatcher +{ + private ProjectGraph graph; + private List nonDispatchedProjects; + private List allProjects; + private int inDispatch = 0; + + public ParallelProjectDispatcher(List projects) throws CycleDetectedException, DuplicateProjectException + { + this.graph = new ProjectGraph( projects ); + nonDispatchedProjects = new ArrayList(projects); + allProjects = new ArrayList(projects); + inDispatch = 0; + } + + + public boolean dispatchAvailable() { + synchronized(this) + { + return(getReadyList().size() > 0); + } + } + + public MavenProject dispatchProject() + { + synchronized(this) + { + List readyList = getReadyList(); + + for(Iterator i = readyList.iterator();i.hasNext();) + { + MavenProject project = ((MavenProject)i.next()); + System.out.println("CANDIDATE : " + ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ) ); + } + + MavenProject project = (MavenProject)readyList.get(0); + nonDispatchedProjects.remove(project); + inDispatch++; + return project; + } + } + + private List getReadyList() + { + + List readyList = new ArrayList(); + + List vtxs = this.graph.getDag().getVerticies(); + + for(Iterator i = vtxs.iterator(); i.hasNext(); ) + { + Vertex v = (Vertex)i.next(); + MavenProject project = getProjectById(v.getLabel()); + if( v.getChildren().size() == 0 && nonDispatchedProjects.contains(project)) + { + readyList.add(project); + } + } + + return readyList; + + } + + private MavenProject getProjectById(String id) + { + for(Iterator i = allProjects.iterator();i.hasNext();) + { + MavenProject project = (MavenProject)i.next(); + String thisId = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ); + + if( thisId.equals(id) ) + return project; + } + return null; + } + + /** + * Any more to be processed + */ + public boolean hasMore() { + synchronized(this) + { + return ( nonDispatchedProjects.size() > 0); + } + } + + public void returnProject(MavenProject project) { + synchronized(this) + { + nonDispatchedProjects.remove(project); + + String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ); + + Vertex v = graph.getDag().getVertex(id); + + for(Iterator vp = v.getParents().iterator(); vp.hasNext(); ) + { + Vertex parent = (Vertex)vp.next(); + parent.removeEdgeTo(v); + } + inDispatch --; + } + } + + + public int projectsInDispatch() { + synchronized(this) + { + return inDispatch; + } + } + +} Index: maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/ProjectDispatcher.java =================================================================== --- maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/ProjectDispatcher.java (revision 0) +++ maven-core/src/main/java/org/apache/maven/lifecycle/dispatcher/ProjectDispatcher.java (revision 0) @@ -0,0 +1,33 @@ +package org.apache.maven.lifecycle.dispatcher; + +import org.apache.maven.project.MavenProject; + +public interface ProjectDispatcher +{ + /** + * Are there any more projects to be dispatched + * @return + */ + boolean hasMore(); + + /** + * Is a dispatch available right now? + * @return + */ + boolean dispatchAvailable(); + + /** + * Dispatch a project + * @return + */ + MavenProject dispatchProject(); + + /** + * Return a project. + * @param mavenProject + */ + void returnProject(MavenProject mavenProject); + + int projectsInDispatch(); + +} Index: maven-core/src/main/java/org/apache/maven/lifecycle/ParallelLifecycleExecutor.java =================================================================== --- maven-core/src/main/java/org/apache/maven/lifecycle/ParallelLifecycleExecutor.java (revision 0) +++ maven-core/src/main/java/org/apache/maven/lifecycle/ParallelLifecycleExecutor.java (revision 0) @@ -0,0 +1,135 @@ +package org.apache.maven.lifecycle; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.maven.BuildFailureException; +import org.apache.maven.artifact.ArtifactUtils; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.execution.ReactorManager; +import org.apache.maven.lifecycle.DefaultLifecycleExecutor.TaskSegment; +import org.apache.maven.lifecycle.dispatcher.ProjectDispatcher; +import org.apache.maven.monitor.event.EventDispatcher; +import org.apache.maven.monitor.event.MavenEvents; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.logging.Logger; +import org.codehaus.plexus.util.dag.DAG; +import org.codehaus.plexus.util.dag.Vertex; + +public class ParallelLifecycleExecutor extends DefaultLifecycleExecutor { + + class BuildRunner implements Runnable + { + MavenProject currentProject; + ReactorManager rm; + MavenSession session; + TaskSegment segment; + EventDispatcher dispatcher; + Logger logger; + + public BuildRunner(ProjectDispatcher pd, MavenProject currentProject, ReactorManager rm, MavenSession session, TaskSegment segment, EventDispatcher dispatcher) + { + this.currentProject = currentProject; + this.rm = rm; + this.session = session; + this.segment = segment; + this.dispatcher = dispatcher; + } + + public void run() { + try { + getLogger().info("Parallel Started"); + buildProject(currentProject, rm, session, segment, dispatcher); + getLogger().info("Parallel Built"); + completedProject(currentProject, session); + getLogger().info("Parallel Finished"); + } catch (BuildFailureException e) { + getLogger().fatalError(e.getMessage(), e); + } catch (LifecycleExecutionException e) { + getLogger().fatalError(e.getMessage(), e); + } + } + + + + public void setLogger(Logger logger) { + this.logger = logger; + + } + + } + + protected void buildProject(MavenProject currentProject, ReactorManager rm, MavenSession session, TaskSegment segment, EventDispatcher dispatcher) + throws BuildFailureException, LifecycleExecutionException + { + + super.buildProject(currentProject, rm, session, segment, dispatcher); + } + + private void completedProject(MavenProject currentProject, MavenSession session) + { + ProjectDispatcher pd = session.getProjectDispatcher(); + pd.returnProject(currentProject); + + } + + protected void buildProjects(ReactorManager rm, MavenSession session, + TaskSegment segment, EventDispatcher dispatcher) + throws BuildFailureException, LifecycleExecutionException + { + + ProjectDispatcher pd = session.getProjectDispatcher(); + + + // while the DAG has some projects in it + while( pd.hasMore() ) + { + while(!pd.dispatchAvailable()) + try { + Thread.sleep(100); + } catch (InterruptedException e) { + throw new BuildFailureException(e.getMessage(), e); + } + + MavenProject currentProject = pd.dispatchProject(); + + if (!rm.isBlackListed(currentProject)) + { + BuildRunner br = new BuildRunner(pd, currentProject, rm, session, segment, dispatcher); + br.setLogger(getLogger()); + Thread thr = new Thread(br); + thr.start(); + + } else { + line(); + + getLogger().info("SKIPPING " + currentProject.getName()); + + getLogger().info(" " + segment); + + getLogger() + .info( + "This project has been banned from further executions due to previous failures."); + + line(); + } + + + + } + + while(pd.projectsInDispatch() > 0) + { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + + +} Index: maven-core/src/main/resources/META-INF/plexus/components.xml =================================================================== --- maven-core/src/main/resources/META-INF/plexus/components.xml (revision 540247) +++ maven-core/src/main/resources/META-INF/plexus/components.xml (working copy) @@ -213,7 +213,7 @@ --> org.apache.maven.lifecycle.LifecycleExecutor - org.apache.maven.lifecycle.DefaultLifecycleExecutor + org.apache.maven.lifecycle.ParallelLifecycleExecutor org.apache.maven.plugin.PluginManager Index: maven-project/src/main/java/org/apache/maven/project/ProjectGraph.java =================================================================== --- maven-project/src/main/java/org/apache/maven/project/ProjectGraph.java (revision 0) +++ maven-project/src/main/java/org/apache/maven/project/ProjectGraph.java (revision 0) @@ -0,0 +1,152 @@ +package org.apache.maven.project; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.maven.artifact.ArtifactUtils; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Extension; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.ReportPlugin; +import org.codehaus.plexus.util.dag.CycleDetectedException; +import org.codehaus.plexus.util.dag.DAG; + +public class ProjectGraph { + + private final DAG dag; + private Map projectMap; + + + public ProjectGraph(List projects) throws CycleDetectedException, DuplicateProjectException + { + dag = new DAG(); + + projectMap = new HashMap(); + + for ( Iterator i = projects.iterator(); i.hasNext(); ) + { + MavenProject project = (MavenProject) i.next(); + + String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ); + + if ( dag.getVertex( id ) != null ) + { + throw new DuplicateProjectException( "Project '" + id + "' is duplicated in the reactor" ); + } + + dag.addVertex( id ); + + projectMap.put( id, project ); + } + + for ( Iterator i = projects.iterator(); i.hasNext(); ) + { + MavenProject project = (MavenProject) i.next(); + + String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ); + + for ( Iterator j = project.getDependencies().iterator(); j.hasNext(); ) + { + Dependency dependency = (Dependency) j.next(); + + String dependencyId = ArtifactUtils + .versionlessKey( dependency.getGroupId(), dependency.getArtifactId() ); + + if ( dag.getVertex( dependencyId ) != null ) + { + project.addProjectReference( (MavenProject) projectMap.get( dependencyId ) ); + + dag.addEdge( id, dependencyId ); + } + } + + MavenProject parent = project.getParent(); + if ( parent != null ) + { + String parentId = ArtifactUtils.versionlessKey( parent.getGroupId(), parent.getArtifactId() ); + if ( dag.getVertex( parentId ) != null ) + { + // Parent is added as an edge, but must not cause a cycle - so we remove any other edges it has in conflict + if ( dag.hasEdge( parentId, id ) ) + { + dag.removeEdge( parentId, id ); + } + dag.addEdge( id, parentId ); + } + } + + List buildPlugins = project.getBuildPlugins(); + if ( buildPlugins != null ) + { + for ( Iterator j = buildPlugins.iterator(); j.hasNext(); ) + { + Plugin plugin = (Plugin) j.next(); + String pluginId = ArtifactUtils.versionlessKey( plugin.getGroupId(), plugin.getArtifactId() ); + if ( dag.getVertex( pluginId ) != null && !pluginId.equals( id ) ) + { + addEdgeWithParentCheck( projectMap, pluginId, project, id ); + } + } + } + + List reportPlugins = project.getReportPlugins(); + if ( reportPlugins != null ) + { + for ( Iterator j = reportPlugins.iterator(); j.hasNext(); ) + { + ReportPlugin plugin = (ReportPlugin) j.next(); + String pluginId = ArtifactUtils.versionlessKey( plugin.getGroupId(), plugin.getArtifactId() ); + if ( dag.getVertex( pluginId ) != null && !pluginId.equals( id ) ) + { + addEdgeWithParentCheck( projectMap, pluginId, project, id ); + } + } + } + + for ( Iterator j = project.getBuildExtensions().iterator(); j.hasNext(); ) + { + Extension extension = (Extension) j.next(); + String extensionId = ArtifactUtils.versionlessKey( extension.getGroupId(), extension.getArtifactId() ); + if ( dag.getVertex( extensionId ) != null ) + { + addEdgeWithParentCheck( projectMap, extensionId, project, id ); + } + } + } + } + + private void addEdgeWithParentCheck( Map projectMap, String projectRefId, MavenProject project, String id ) + throws CycleDetectedException + { + MavenProject extProject = (MavenProject) projectMap.get( projectRefId ); + + if ( extProject == null ) + { + return; + } + + project.addProjectReference( extProject ); + + MavenProject extParent = extProject.getParent(); + if ( extParent != null ) + { + String parentId = ArtifactUtils.versionlessKey( extParent.getGroupId(), extParent.getArtifactId() ); + // Don't add edge from parent to extension if a reverse edge already exists + if ( !dag.hasEdge( projectRefId, id ) || !parentId.equals( id ) ) + { + dag.addEdge( id, projectRefId ); + } + } + } + + public DAG getDag() { + return dag; + } + + + protected MavenProject getProjectById(String id) { + return (MavenProject)projectMap.get(id); + } +} Index: maven-project/src/main/java/org/apache/maven/project/ProjectSorter.java =================================================================== --- maven-project/src/main/java/org/apache/maven/project/ProjectSorter.java (revision 540247) +++ maven-project/src/main/java/org/apache/maven/project/ProjectSorter.java (working copy) @@ -41,10 +41,8 @@ * @author Brett Porter * @version $Id$ */ -public class ProjectSorter -{ - private final DAG dag; - +public class ProjectSorter extends ProjectGraph +{ private final List sortedProjects; private MavenProject topLevelProject; @@ -64,137 +62,22 @@ public ProjectSorter( List projects ) throws CycleDetectedException, DuplicateProjectException { - dag = new DAG(); - - Map projectMap = new HashMap(); - - for ( Iterator i = projects.iterator(); i.hasNext(); ) - { - MavenProject project = (MavenProject) i.next(); - - String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ); - - if ( dag.getVertex( id ) != null ) - { - throw new DuplicateProjectException( "Project '" + id + "' is duplicated in the reactor" ); - } - - dag.addVertex( id ); - - projectMap.put( id, project ); - } - - for ( Iterator i = projects.iterator(); i.hasNext(); ) - { - MavenProject project = (MavenProject) i.next(); - - String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ); - - for ( Iterator j = project.getDependencies().iterator(); j.hasNext(); ) - { - Dependency dependency = (Dependency) j.next(); - - String dependencyId = ArtifactUtils - .versionlessKey( dependency.getGroupId(), dependency.getArtifactId() ); - - if ( dag.getVertex( dependencyId ) != null ) - { - project.addProjectReference( (MavenProject) projectMap.get( dependencyId ) ); - - dag.addEdge( id, dependencyId ); - } - } - - MavenProject parent = project.getParent(); - if ( parent != null ) - { - String parentId = ArtifactUtils.versionlessKey( parent.getGroupId(), parent.getArtifactId() ); - if ( dag.getVertex( parentId ) != null ) - { - // Parent is added as an edge, but must not cause a cycle - so we remove any other edges it has in conflict - if ( dag.hasEdge( parentId, id ) ) - { - dag.removeEdge( parentId, id ); - } - dag.addEdge( id, parentId ); - } - } - - List buildPlugins = project.getBuildPlugins(); - if ( buildPlugins != null ) - { - for ( Iterator j = buildPlugins.iterator(); j.hasNext(); ) - { - Plugin plugin = (Plugin) j.next(); - String pluginId = ArtifactUtils.versionlessKey( plugin.getGroupId(), plugin.getArtifactId() ); - if ( dag.getVertex( pluginId ) != null && !pluginId.equals( id ) ) - { - addEdgeWithParentCheck( projectMap, pluginId, project, id ); - } - } - } - - List reportPlugins = project.getReportPlugins(); - if ( reportPlugins != null ) - { - for ( Iterator j = reportPlugins.iterator(); j.hasNext(); ) - { - ReportPlugin plugin = (ReportPlugin) j.next(); - String pluginId = ArtifactUtils.versionlessKey( plugin.getGroupId(), plugin.getArtifactId() ); - if ( dag.getVertex( pluginId ) != null && !pluginId.equals( id ) ) - { - addEdgeWithParentCheck( projectMap, pluginId, project, id ); - } - } - } - - for ( Iterator j = project.getBuildExtensions().iterator(); j.hasNext(); ) - { - Extension extension = (Extension) j.next(); - String extensionId = ArtifactUtils.versionlessKey( extension.getGroupId(), extension.getArtifactId() ); - if ( dag.getVertex( extensionId ) != null ) - { - addEdgeWithParentCheck( projectMap, extensionId, project, id ); - } - } - } - + super( projects ); + List sortedProjects = new ArrayList(); - for ( Iterator i = TopologicalSorter.sort( dag ).iterator(); i.hasNext(); ) + for ( Iterator i = TopologicalSorter.sort( getDag() ).iterator(); i.hasNext(); ) { String id = (String) i.next(); - sortedProjects.add( projectMap.get( id ) ); + sortedProjects.add( getProjectById( id ) ); } this.sortedProjects = Collections.unmodifiableList( sortedProjects ); } - private void addEdgeWithParentCheck( Map projectMap, String projectRefId, MavenProject project, String id ) - throws CycleDetectedException - { - MavenProject extProject = (MavenProject) projectMap.get( projectRefId ); - - if ( extProject == null ) - { - return; - } + - project.addProjectReference( extProject ); - - MavenProject extParent = extProject.getParent(); - if ( extParent != null ) - { - String parentId = ArtifactUtils.versionlessKey( extParent.getGroupId(), extParent.getArtifactId() ); - // Don't add edge from parent to extension if a reverse edge already exists - if ( !dag.hasEdge( projectRefId, id ) || !parentId.equals( id ) ) - { - dag.addEdge( id, projectRefId ); - } - } - } - // TODO: !![jc; 28-jul-2005] check this; if we're using '-r' and there are aggregator tasks, this will result in weirdness. public MavenProject getTopLevelProject() { @@ -225,6 +108,6 @@ public List getDependents( String id ) { - return dag.getParentLabels( id ); + return getDag().getParentLabels( id ); } }