Skip to content

Commit 4520134

Browse files
authored
[MNG-8256] FilteredProjectDependencyGraph fix for non-transitive case (#1724)
The `FilteredProjectDependencyGraph` class fix for non-transitive case and an UT. Also contains some improvement in classes, making them immutable as intended. --- https://issues.apache.org/jira/browse/MNG-8256
1 parent 4c059c4 commit 4520134

File tree

3 files changed

+47
-17
lines changed

3 files changed

+47
-17
lines changed

maven-core/src/main/java/org/apache/maven/graph/DefaultGraphBuilder.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,10 @@ private Result<ProjectDependencyGraph> sessionDependencyGraph(final MavenSession
9494
Result<ProjectDependencyGraph> result = null;
9595

9696
if (session.getProjectDependencyGraph() != null || session.getProjects() != null) {
97-
final ProjectDependencyGraph graph =
98-
new DefaultProjectDependencyGraph(session.getAllProjects(), session.getProjects());
97+
ProjectDependencyGraph graph = new DefaultProjectDependencyGraph(session.getAllProjects());
98+
if (session.getProjects() != null) {
99+
graph = new FilteredProjectDependencyGraph(graph, session.getProjects());
100+
}
99101

100102
result = Result.success(graph);
101103
}

maven-core/src/main/java/org/apache/maven/graph/FilteredProjectDependencyGraph.java

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.List;
2525
import java.util.Map;
2626
import java.util.Objects;
27+
import java.util.stream.Collectors;
2728

2829
import org.apache.maven.execution.ProjectDependencyGraph;
2930
import org.apache.maven.project.MavenProject;
@@ -35,11 +36,11 @@
3536
*/
3637
class FilteredProjectDependencyGraph implements ProjectDependencyGraph {
3738

38-
private ProjectDependencyGraph projectDependencyGraph;
39+
private final ProjectDependencyGraph projectDependencyGraph;
3940

40-
private Map<MavenProject, ?> whiteList;
41+
private final Map<MavenProject, ?> whiteList;
4142

42-
private List<MavenProject> sortedProjects;
43+
private final List<MavenProject> sortedProjects;
4344

4445
/**
4546
* Creates a new project dependency graph from the specified graph.
@@ -51,46 +52,58 @@ class FilteredProjectDependencyGraph implements ProjectDependencyGraph {
5152
ProjectDependencyGraph projectDependencyGraph, Collection<? extends MavenProject> whiteList) {
5253
this.projectDependencyGraph =
5354
Objects.requireNonNull(projectDependencyGraph, "projectDependencyGraph cannot be null");
54-
55-
this.whiteList = new IdentityHashMap<MavenProject, Object>();
56-
55+
this.whiteList = new IdentityHashMap<>();
5756
for (MavenProject project : whiteList) {
5857
this.whiteList.put(project, null);
5958
}
59+
this.sortedProjects = projectDependencyGraph.getSortedProjects().stream()
60+
.filter(this.whiteList::containsKey)
61+
.collect(Collectors.toList());
6062
}
6163

6264
/**
6365
* @since 3.5.0
6466
*/
67+
@Override
6568
public List<MavenProject> getAllProjects() {
6669
return this.projectDependencyGraph.getAllProjects();
6770
}
6871

72+
@Override
6973
public List<MavenProject> getSortedProjects() {
70-
if (sortedProjects == null) {
71-
sortedProjects = applyFilter(projectDependencyGraph.getSortedProjects());
72-
}
73-
7474
return new ArrayList<>(sortedProjects);
7575
}
7676

77+
@Override
7778
public List<MavenProject> getDownstreamProjects(MavenProject project, boolean transitive) {
78-
return applyFilter(projectDependencyGraph.getDownstreamProjects(project, transitive));
79+
return applyFilter(projectDependencyGraph.getDownstreamProjects(project, transitive), transitive, false);
7980
}
8081

82+
@Override
8183
public List<MavenProject> getUpstreamProjects(MavenProject project, boolean transitive) {
82-
return applyFilter(projectDependencyGraph.getUpstreamProjects(project, transitive));
84+
return applyFilter(projectDependencyGraph.getUpstreamProjects(project, transitive), transitive, true);
8385
}
8486

85-
private List<MavenProject> applyFilter(Collection<? extends MavenProject> projects) {
87+
/**
88+
* Filter out whitelisted projects with a big twist:
89+
* Assume we have all projects {@code a, b, c} while active are {@code a, c} and relation among all projects
90+
* is {@code a -> b -> c}. This method handles well the case for transitive list. But, for non-transitive we need
91+
* to "pull in" transitive dependencies of eliminated projects, as for case above, the properly filtered list would
92+
* be {@code a -> c}.
93+
* <p>
94+
* Original code would falsely report {@code a} project as "without dependencies", basically would lose link due
95+
* filtering. This causes build ordering issues in concurrent builders.
96+
*/
97+
private List<MavenProject> applyFilter(
98+
Collection<? extends MavenProject> projects, boolean transitive, boolean upstream) {
8699
List<MavenProject> filtered = new ArrayList<>(projects.size());
87-
88100
for (MavenProject project : projects) {
89101
if (whiteList.containsKey(project)) {
90102
filtered.add(project);
103+
} else if (!transitive) {
104+
filtered.addAll(upstream ? getUpstreamProjects(project, false) : getDownstreamProjects(project, false));
91105
}
92106
}
93-
94107
return filtered;
95108
}
96109

maven-core/src/test/java/org/apache/maven/graph/DefaultProjectDependencyGraphTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ public class DefaultProjectDependencyGraphTest extends TestCase {
3535

3636
private final MavenProject aProject = createA();
3737

38+
private final MavenProject bProject = createProject(Arrays.asList(toDependency(aProject)), "bProject");
39+
40+
private final MavenProject cProject = createProject(Arrays.asList(toDependency(bProject)), "cProject");
41+
3842
private final MavenProject depender1 = createProject(Arrays.asList(toDependency(aProject)), "depender1");
3943

4044
private final MavenProject depender2 = createProject(Arrays.asList(toDependency(aProject)), "depender2");
@@ -46,6 +50,17 @@ public class DefaultProjectDependencyGraphTest extends TestCase {
4650

4751
private final MavenProject transitiveOnly = createProject(Arrays.asList(toDependency(depender3)), "depender5");
4852

53+
public void testNonTransitiveFiltering() throws DuplicateProjectException, CycleDetectedException {
54+
ProjectDependencyGraph graph = new FilteredProjectDependencyGraph(
55+
new DefaultProjectDependencyGraph(Arrays.asList(aProject, bProject, cProject)),
56+
Arrays.asList(aProject, cProject));
57+
final List<MavenProject> sortedProjects = graph.getSortedProjects();
58+
assertEquals(aProject, sortedProjects.get(0));
59+
assertEquals(cProject, sortedProjects.get(1));
60+
61+
assertTrue(graph.getDownstreamProjects(aProject, false).contains(cProject));
62+
}
63+
4964
public void testGetSortedProjects() throws DuplicateProjectException, CycleDetectedException {
5065
ProjectDependencyGraph graph = new DefaultProjectDependencyGraph(Arrays.asList(depender1, aProject));
5166
final List<MavenProject> sortedProjects = graph.getSortedProjects();

0 commit comments

Comments
 (0)