Skip to content

Commit 7ea795b

Browse files
authored
[MNG-8256] FilteredProjectDependencyGraph fix for non-transitive case (#1723)
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 2b13a43 commit 7ea795b

File tree

3 files changed

+47
-16
lines changed

3 files changed

+47
-16
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
@@ -120,8 +120,10 @@ private Result<ProjectDependencyGraph> sessionDependencyGraph(final MavenSession
120120
Result<ProjectDependencyGraph> result = null;
121121

122122
if (session.getProjectDependencyGraph() != null || session.getProjects() != null) {
123-
final ProjectDependencyGraph graph =
124-
new DefaultProjectDependencyGraph(session.getAllProjects(), session.getProjects());
123+
ProjectDependencyGraph graph = new DefaultProjectDependencyGraph(session.getAllProjects());
124+
if (session.getProjects() != null) {
125+
graph = new FilteredProjectDependencyGraph(graph, session.getProjects());
126+
}
125127

126128
result = Result.success(graph);
127129
}

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

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@
3434
*/
3535
class FilteredProjectDependencyGraph implements ProjectDependencyGraph {
3636

37-
private ProjectDependencyGraph projectDependencyGraph;
37+
private final ProjectDependencyGraph projectDependencyGraph;
3838

39-
private Map<MavenProject, ?> whiteList;
39+
private final Map<MavenProject, ?> whiteList;
4040

41-
private List<MavenProject> sortedProjects;
41+
private final List<MavenProject> sortedProjects;
4242

4343
/**
4444
* Creates a new project dependency graph from the specified graph.
@@ -50,46 +50,58 @@ class FilteredProjectDependencyGraph implements ProjectDependencyGraph {
5050
ProjectDependencyGraph projectDependencyGraph, Collection<? extends MavenProject> whiteList) {
5151
this.projectDependencyGraph =
5252
Objects.requireNonNull(projectDependencyGraph, "projectDependencyGraph cannot be null");
53-
5453
this.whiteList = new IdentityHashMap<>();
55-
5654
for (MavenProject project : whiteList) {
5755
this.whiteList.put(project, null);
5856
}
57+
this.sortedProjects = projectDependencyGraph.getSortedProjects().stream()
58+
.filter(this.whiteList::containsKey)
59+
.toList();
5960
}
6061

6162
/**
6263
* @since 3.5.0
6364
*/
65+
@Override
6466
public List<MavenProject> getAllProjects() {
6567
return this.projectDependencyGraph.getAllProjects();
6668
}
6769

70+
@Override
6871
public List<MavenProject> getSortedProjects() {
69-
if (sortedProjects == null) {
70-
sortedProjects = applyFilter(projectDependencyGraph.getSortedProjects());
71-
}
72-
7372
return new ArrayList<>(sortedProjects);
7473
}
7574

75+
@Override
7676
public List<MavenProject> getDownstreamProjects(MavenProject project, boolean transitive) {
77-
return applyFilter(projectDependencyGraph.getDownstreamProjects(project, transitive));
77+
return applyFilter(projectDependencyGraph.getDownstreamProjects(project, transitive), transitive, false);
7878
}
7979

80+
@Override
8081
public List<MavenProject> getUpstreamProjects(MavenProject project, boolean transitive) {
81-
return applyFilter(projectDependencyGraph.getUpstreamProjects(project, transitive));
82+
return applyFilter(projectDependencyGraph.getUpstreamProjects(project, transitive), transitive, true);
8283
}
8384

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

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,18 @@
2929
import org.junit.jupiter.api.Test;
3030

3131
import static org.junit.jupiter.api.Assertions.assertEquals;
32+
import static org.junit.jupiter.api.Assertions.assertTrue;
3233

3334
/**
3435
*/
3536
class DefaultProjectDependencyGraphTest {
3637

3738
private final MavenProject aProject = createA();
3839

40+
private final MavenProject bProject = createProject(Arrays.asList(toDependency(aProject)), "bProject");
41+
42+
private final MavenProject cProject = createProject(Arrays.asList(toDependency(bProject)), "cProject");
43+
3944
private final MavenProject depender1 = createProject(Arrays.asList(toDependency(aProject)), "depender1");
4045

4146
private final MavenProject depender2 = createProject(Arrays.asList(toDependency(aProject)), "depender2");
@@ -47,6 +52,18 @@ class DefaultProjectDependencyGraphTest {
4752

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

55+
@Test
56+
void testNonTransitiveFiltering() throws DuplicateProjectException, CycleDetectedException {
57+
ProjectDependencyGraph graph = new FilteredProjectDependencyGraph(
58+
new DefaultProjectDependencyGraph(Arrays.asList(aProject, bProject, cProject)),
59+
Arrays.asList(aProject, cProject));
60+
final List<MavenProject> sortedProjects = graph.getSortedProjects();
61+
assertEquals(aProject, sortedProjects.get(0));
62+
assertEquals(cProject, sortedProjects.get(1));
63+
64+
assertTrue(graph.getDownstreamProjects(aProject, false).contains(cProject));
65+
}
66+
5067
@Test
5168
void testGetSortedProjects() throws DuplicateProjectException, CycleDetectedException {
5269
ProjectDependencyGraph graph = new DefaultProjectDependencyGraph(Arrays.asList(depender1, aProject));

0 commit comments

Comments
 (0)