diff --git a/maven-core/src/main/java/org/apache/maven/graph/DefaultGraphBuilder.java b/maven-core/src/main/java/org/apache/maven/graph/DefaultGraphBuilder.java index b2f00adec2..67fad2b6a6 100644 --- a/maven-core/src/main/java/org/apache/maven/graph/DefaultGraphBuilder.java +++ b/maven-core/src/main/java/org/apache/maven/graph/DefaultGraphBuilder.java @@ -94,8 +94,10 @@ public class DefaultGraphBuilder implements GraphBuilder { Result result = null; if (session.getProjectDependencyGraph() != null || session.getProjects() != null) { - final ProjectDependencyGraph graph = - new DefaultProjectDependencyGraph(session.getAllProjects(), session.getProjects()); + ProjectDependencyGraph graph = new DefaultProjectDependencyGraph(session.getAllProjects()); + if (session.getProjects() != null) { + graph = new FilteredProjectDependencyGraph(graph, session.getProjects()); + } result = Result.success(graph); } diff --git a/maven-core/src/main/java/org/apache/maven/graph/FilteredProjectDependencyGraph.java b/maven-core/src/main/java/org/apache/maven/graph/FilteredProjectDependencyGraph.java index 96c008c78b..b05913c76f 100644 --- a/maven-core/src/main/java/org/apache/maven/graph/FilteredProjectDependencyGraph.java +++ b/maven-core/src/main/java/org/apache/maven/graph/FilteredProjectDependencyGraph.java @@ -24,6 +24,7 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; import org.apache.maven.execution.ProjectDependencyGraph; import org.apache.maven.project.MavenProject; @@ -35,11 +36,11 @@ import org.apache.maven.project.MavenProject; */ class FilteredProjectDependencyGraph implements ProjectDependencyGraph { - private ProjectDependencyGraph projectDependencyGraph; + private final ProjectDependencyGraph projectDependencyGraph; - private Map whiteList; + private final Map whiteList; - private List sortedProjects; + private final List sortedProjects; /** * Creates a new project dependency graph from the specified graph. @@ -51,46 +52,58 @@ class FilteredProjectDependencyGraph implements ProjectDependencyGraph { ProjectDependencyGraph projectDependencyGraph, Collection whiteList) { this.projectDependencyGraph = Objects.requireNonNull(projectDependencyGraph, "projectDependencyGraph cannot be null"); - - this.whiteList = new IdentityHashMap(); - + this.whiteList = new IdentityHashMap<>(); for (MavenProject project : whiteList) { this.whiteList.put(project, null); } + this.sortedProjects = projectDependencyGraph.getSortedProjects().stream() + .filter(this.whiteList::containsKey) + .collect(Collectors.toList()); } /** * @since 3.5.0 */ + @Override public List getAllProjects() { return this.projectDependencyGraph.getAllProjects(); } + @Override public List getSortedProjects() { - if (sortedProjects == null) { - sortedProjects = applyFilter(projectDependencyGraph.getSortedProjects()); - } - return new ArrayList<>(sortedProjects); } + @Override public List getDownstreamProjects(MavenProject project, boolean transitive) { - return applyFilter(projectDependencyGraph.getDownstreamProjects(project, transitive)); + return applyFilter(projectDependencyGraph.getDownstreamProjects(project, transitive), transitive, false); } + @Override public List getUpstreamProjects(MavenProject project, boolean transitive) { - return applyFilter(projectDependencyGraph.getUpstreamProjects(project, transitive)); + return applyFilter(projectDependencyGraph.getUpstreamProjects(project, transitive), transitive, true); } - private List applyFilter(Collection projects) { + /** + * Filter out whitelisted projects with a big twist: + * Assume we have all projects {@code a, b, c} while active are {@code a, c} and relation among all projects + * is {@code a -> b -> c}. This method handles well the case for transitive list. But, for non-transitive we need + * to "pull in" transitive dependencies of eliminated projects, as for case above, the properly filtered list would + * be {@code a -> c}. + *

+ * Original code would falsely report {@code a} project as "without dependencies", basically would lose link due + * filtering. This causes build ordering issues in concurrent builders. + */ + private List applyFilter( + Collection projects, boolean transitive, boolean upstream) { List filtered = new ArrayList<>(projects.size()); - for (MavenProject project : projects) { if (whiteList.containsKey(project)) { filtered.add(project); + } else if (!transitive) { + filtered.addAll(upstream ? getUpstreamProjects(project, false) : getDownstreamProjects(project, false)); } } - return filtered; } diff --git a/maven-core/src/test/java/org/apache/maven/graph/DefaultProjectDependencyGraphTest.java b/maven-core/src/test/java/org/apache/maven/graph/DefaultProjectDependencyGraphTest.java index ce48390b03..9a5a8568de 100644 --- a/maven-core/src/test/java/org/apache/maven/graph/DefaultProjectDependencyGraphTest.java +++ b/maven-core/src/test/java/org/apache/maven/graph/DefaultProjectDependencyGraphTest.java @@ -35,6 +35,10 @@ public class DefaultProjectDependencyGraphTest extends TestCase { private final MavenProject aProject = createA(); + private final MavenProject bProject = createProject(Arrays.asList(toDependency(aProject)), "bProject"); + + private final MavenProject cProject = createProject(Arrays.asList(toDependency(bProject)), "cProject"); + private final MavenProject depender1 = createProject(Arrays.asList(toDependency(aProject)), "depender1"); private final MavenProject depender2 = createProject(Arrays.asList(toDependency(aProject)), "depender2"); @@ -46,6 +50,17 @@ public class DefaultProjectDependencyGraphTest extends TestCase { private final MavenProject transitiveOnly = createProject(Arrays.asList(toDependency(depender3)), "depender5"); + public void testNonTransitiveFiltering() throws DuplicateProjectException, CycleDetectedException { + ProjectDependencyGraph graph = new FilteredProjectDependencyGraph( + new DefaultProjectDependencyGraph(Arrays.asList(aProject, bProject, cProject)), + Arrays.asList(aProject, cProject)); + final List sortedProjects = graph.getSortedProjects(); + assertEquals(aProject, sortedProjects.get(0)); + assertEquals(cProject, sortedProjects.get(1)); + + assertTrue(graph.getDownstreamProjects(aProject, false).contains(cProject)); + } + public void testGetSortedProjects() throws DuplicateProjectException, CycleDetectedException { ProjectDependencyGraph graph = new DefaultProjectDependencyGraph(Arrays.asList(depender1, aProject)); final List sortedProjects = graph.getSortedProjects();