Skip to content

Commit faf2c03

Browse files
Sort ResolvedPom#dependencyManagement lazily (#5928)
The `ResolvedPom#dependencyManagement` list is sorted to allow `getManagedDependency()` to use binary search. Before this list was eagerly sorted, including in the constructor to remain backwards compatible with old data. Now there is instead a boolean `dependencyManagementSorted` flag field to track whether the data is sorted or not. This flag also gets set to `false` when the contents are modified. Instead, the data is lazily sorted as part of the getter.
1 parent 35fff49 commit faf2c03

File tree

2 files changed

+47
-32
lines changed

2 files changed

+47
-32
lines changed

rewrite-maven/src/main/java/org/openrewrite/maven/tree/Pom.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ public ResolvedPom resolve(Iterable<String> activeProfiles,
207207
activeProfiles,
208208
properties,
209209
emptyList(),
210+
true,
210211
concatAll(initialRepositories, getEffectiveRepositories()),
211212
repositories,
212213
pluginRepositories,

rewrite-maven/src/main/java/org/openrewrite/maven/tree/ResolvedPom.java

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,12 @@
1515
*/
1616
package org.openrewrite.maven.tree;
1717

18-
import com.fasterxml.jackson.annotation.JsonCreator;
19-
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
20-
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
18+
import com.fasterxml.jackson.annotation.*;
2119
import com.fasterxml.jackson.databind.JsonNode;
2220
import com.fasterxml.jackson.databind.node.ArrayNode;
2321
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
2422
import com.fasterxml.jackson.databind.node.ObjectNode;
25-
import lombok.Builder;
26-
import lombok.Getter;
27-
import lombok.Value;
28-
import lombok.With;
23+
import lombok.*;
2924
import lombok.experimental.NonFinal;
3025
import org.jspecify.annotations.Nullable;
3126
import org.openrewrite.ExecutionContext;
@@ -47,11 +42,14 @@
4742
import java.util.function.UnaryOperator;
4843

4944
import static java.util.Collections.*;
50-
import static java.util.stream.Collectors.toList;
5145
import static java.util.stream.Collectors.toMap;
5246
import static org.openrewrite.internal.StringUtils.matchesGlob;
5347

5448
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@ref")
49+
// make sure `dependencyManagement` is serialized before `dependencyManagementSorted`
50+
@JsonPropertyOrder({"requested", "activeProfiles", "properties", "dependencyManagement",
51+
"dependencyManagementSorted", "initialRepositories", "repositories",
52+
"pluginRepositories", "requestedDependencies", "plugins", "pluginManagement", "subprojects"})
5553
@Getter
5654
@Builder
5755
public class ResolvedPom {
@@ -77,17 +75,16 @@ public class ResolvedPom {
7775
Iterable<String> activeProfiles = emptyList();
7876

7977
public ResolvedPom(Pom requested, Iterable<String> activeProfiles) {
80-
this(requested, activeProfiles, emptyMap(), emptyList(), null, emptyList(), emptyList(), emptyList(), emptyList(), emptyList(), emptyList());
78+
this(requested, activeProfiles, emptyMap(), emptyList(), true, null, emptyList(), emptyList(), emptyList(), emptyList(), emptyList(), emptyList());
8179
}
8280

8381
@JsonCreator
84-
ResolvedPom(Pom requested, Iterable<String> activeProfiles, Map<String, String> properties, List<ResolvedManagedDependency> dependencyManagement, @Nullable List<MavenRepository> initialRepositories, List<MavenRepository> repositories, List<MavenRepository> pluginRepositories, List<Dependency> requestedDependencies, List<Plugin> plugins, List<Plugin> pluginManagement, List<String> subprojects) {
82+
ResolvedPom(Pom requested, Iterable<String> activeProfiles, Map<String, String> properties, List<ResolvedManagedDependency> dependencyManagement, boolean dependencyManagementSorted, @Nullable List<MavenRepository> initialRepositories, List<MavenRepository> repositories, List<MavenRepository> pluginRepositories, List<Dependency> requestedDependencies, List<Plugin> plugins, List<Plugin> pluginManagement, List<String> subprojects) {
8583
this.requested = requested;
8684
this.activeProfiles = activeProfiles;
8785
this.properties = properties;
8886
this.dependencyManagement = dependencyManagement;
89-
// TO-BE-REMOVED(2025-09-01): sorting added for backwards compatibility with older LSTs
90-
this.dependencyManagement.sort(MANAGED_DEPENDENCY_COMPARATOR);
87+
this.dependencyManagementSorted = dependencyManagementSorted;
9188
if (initialRepositories != null) {
9289
this.initialRepositories = initialRepositories;
9390
}
@@ -107,6 +104,11 @@ public ResolvedPom(Pom requested, Iterable<String> activeProfiles) {
107104
@Builder.Default
108105
List<ResolvedManagedDependency> dependencyManagement = emptyList();
109106

107+
@NonFinal
108+
@Builder.Default
109+
@Getter(AccessLevel.NONE)
110+
boolean dependencyManagementSorted = false;
111+
110112
@NonFinal
111113
@Builder.Default
112114
List<MavenRepository> initialRepositories = emptyList();
@@ -136,6 +138,16 @@ public ResolvedPom(Pom requested, Iterable<String> activeProfiles) {
136138
@Nullable // on older LSTs, this field is not yet present
137139
List<String> subprojects = emptyList();
138140

141+
// Annotation present to ensure that serialized data is always sorted
142+
@JsonGetter
143+
public List<ResolvedManagedDependency> getDependencyManagement() {
144+
if (!dependencyManagementSorted) {
145+
dependencyManagement.sort(MANAGED_DEPENDENCY_COMPARATOR);
146+
dependencyManagementSorted = true;
147+
}
148+
return dependencyManagement;
149+
}
150+
139151
/**
140152
* Deduplicate dependencies.
141153
*
@@ -184,6 +196,7 @@ public ResolvedPom resolve(ExecutionContext ctx, MavenPomDownloader downloader)
184196
activeProfiles,
185197
emptyMap(),
186198
emptyList(),
199+
true,
187200
initialRepositories,
188201
emptyList(),
189202
emptyList(),
@@ -210,12 +223,13 @@ public ResolvedPom resolve(ExecutionContext ctx, MavenPomDownloader downloader)
210223
}
211224

212225
List<ResolvedManagedDependency> resolvedDependencyManagement = resolved.getDependencyManagement();
213-
if (dependencyManagement == null || dependencyManagement.size() != resolvedDependencyManagement.size()) {
226+
List<ResolvedManagedDependency> currentDependencyManagement = getDependencyManagement();
227+
if (currentDependencyManagement.size() != resolvedDependencyManagement.size()) {
214228
return resolved;
215229
}
216230
for (int i = 0; i < resolvedDependencyManagement.size(); i++) {
217231
// TODO does ResolvedPom's equals work well enough to match on BOM imports?
218-
if (!dependencyManagement.get(i).equals(resolvedDependencyManagement.get(i))) {
232+
if (!currentDependencyManagement.get(i).equals(resolvedDependencyManagement.get(i))) {
219233
return resolved;
220234
}
221235
}
@@ -362,13 +376,14 @@ public List<GroupArtifact> getManagedExclusions(String groupId, String artifactI
362376
}
363377

364378
public @Nullable ResolvedManagedDependency getManagedDependency(@Nullable String groupId, String artifactId, @Nullable String type, @Nullable String classifier) {
365-
if (dependencyManagement.isEmpty()) {
379+
List<ResolvedManagedDependency> deps = getDependencyManagement();
380+
if (deps.isEmpty()) {
366381
return null;
367382
}
368383

369384
ResolvedManagedDependency searchKey = createSearchKey(groupId, artifactId, type, classifier);
370-
int index = binarySearch(dependencyManagement, searchKey, MANAGED_DEPENDENCY_COMPARATOR);
371-
return index >= 0 ? dependencyManagement.get(index) : null;
385+
int index = binarySearch(deps, searchKey, MANAGED_DEPENDENCY_COMPARATOR);
386+
return index >= 0 ? deps.get(index) : null;
372387
}
373388

374389
private static ResolvedManagedDependency createSearchKey(@Nullable String groupId, String artifactId, @Nullable String type, @Nullable String classifier) {
@@ -474,9 +489,8 @@ private void resolveParentDependenciesRecursively(List<Pom> pomAncestry) throws
474489

475490
resolveParentDependenciesRecursively(pomAncestry, managedDependencyMap);
476491
if (!managedDependencyMap.isEmpty()) {
477-
dependencyManagement = managedDependencyMap.values().stream()
478-
.sorted(MANAGED_DEPENDENCY_COMPARATOR)
479-
.collect(toList());
492+
dependencyManagement = new ArrayList<>(managedDependencyMap.values());
493+
dependencyManagementSorted = false;
480494
}
481495
}
482496

@@ -576,7 +590,7 @@ private void mergeRequestedDependencies(List<Dependency> incomingRequestedDepend
576590
boolean found = false;
577591
for (Dependency reqDep : requestedDependencies) {
578592
if (reqDep.getGav().getGroupId().equals(incReqDep.getGav().getGroupId()) &&
579-
reqDep.getArtifactId().equals(incReqDep.getArtifactId())) {
593+
reqDep.getArtifactId().equals(incReqDep.getArtifactId())) {
580594
found = true;
581595
break;
582596
}
@@ -717,7 +731,7 @@ private List<Plugin.Execution> mergePluginExecutions(List<Plugin.Execution> curr
717731
// PHASE
718732
String mergedPhase = currentExecution.getPhase();
719733
if (incomingExecution.getPhase() != null &&
720-
!Objects.equals(mergedPhase, incomingExecution.getPhase())) {
734+
!Objects.equals(mergedPhase, incomingExecution.getPhase())) {
721735
mergedPhase = incomingExecution.getPhase();
722736
}
723737
// CONFIGURATION
@@ -957,8 +971,8 @@ private boolean isAlreadyResolved(GroupArtifactVersion groupArtifactVersion, Lis
957971
Pom pom = pomAncestry.get(i);
958972
ResolvedGroupArtifactVersion alreadyResolvedGav = pom.getGav();
959973
if (alreadyResolvedGav.getGroupId().equals(groupArtifactVersion.getGroupId()) &&
960-
alreadyResolvedGav.getArtifactId().equals(groupArtifactVersion.getArtifactId()) &&
961-
alreadyResolvedGav.getVersion().equals(groupArtifactVersion.getVersion())) {
974+
alreadyResolvedGav.getArtifactId().equals(groupArtifactVersion.getArtifactId()) &&
975+
alreadyResolvedGav.getVersion().equals(groupArtifactVersion.getVersion())) {
962976
return true;
963977
}
964978
}
@@ -1000,9 +1014,9 @@ public List<ResolvedDependency> resolveDependencies(Scope scope, Map<GroupArtifa
10001014
try {
10011015
if (depth == 0 && d.getVersion() == null) {
10021016
String coordinates = d.getGav() +
1003-
(d.getClassifier() == null ? "" : ":" + d.getClassifier()) +
1004-
(d.getType() == null ? "" : ":" + d.getType()) +
1005-
(d.getScope() == null ? "" : ":" + d.getScope());
1017+
(d.getClassifier() == null ? "" : ":" + d.getClassifier()) +
1018+
(d.getType() == null ? "" : ":" + d.getType()) +
1019+
(d.getScope() == null ? "" : ":" + d.getScope());
10061020
throw new MavenDownloadingException("No version provided for direct dependency " + coordinates, null, dd.getDependency().getGav());
10071021
}
10081022
if (d.getVersion() == null || (d.getType() != null && (!"jar".equals(d.getType()) && !"pom".equals(d.getType()) && !"zip".equals(d.getType()) && !"bom".equals(d.getType()) && !"tgz".equals(d.getType())))) {
@@ -1046,8 +1060,8 @@ public List<ResolvedDependency> resolveDependencies(Scope scope, Map<GroupArtifa
10461060
}
10471061

10481062
if ((d.getGav().getGroupId() != null && d.getGav().getGroupId().startsWith("${") && d.getGav().getGroupId().endsWith("}")) ||
1049-
(d.getGav().getArtifactId().startsWith("${") && d.getGav().getArtifactId().endsWith("}")) ||
1050-
(d.getGav().getVersion() != null && d.getGav().getVersion().startsWith("${") && d.getGav().getVersion().endsWith("}"))) {
1063+
(d.getGav().getArtifactId().startsWith("${") && d.getGav().getArtifactId().endsWith("}")) ||
1064+
(d.getGav().getVersion() != null && d.getGav().getVersion().startsWith("${") && d.getGav().getVersion().endsWith("}"))) {
10511065
throw new MavenDownloadingException("Could not resolve property", null, d.getGav());
10521066
}
10531067

@@ -1057,7 +1071,7 @@ public List<ResolvedDependency> resolveDependencies(Scope scope, Map<GroupArtifa
10571071
ResolvedPom resolvedPom = cache.getResolvedDependencyPom(dPom.getGav());
10581072
if (resolvedPom == null) {
10591073
resolvedPom = new ResolvedPom(dPom, getActiveProfiles(), emptyMap(),
1060-
emptyList(), initialRepositories, emptyList(), emptyList(),
1074+
emptyList(), true, initialRepositories, emptyList(), emptyList(),
10611075
emptyList(), emptyList(), emptyList(), emptyList());
10621076
resolvedPom.resolver(ctx, downloader).resolveParentsRecursively(dPom);
10631077
cache.putResolvedDependencyPom(dPom.getGav(), resolvedPom);
@@ -1102,7 +1116,7 @@ public List<ResolvedDependency> resolveDependencies(Scope scope, Map<GroupArtifa
11021116
d2 = d2.withExclusions(ListUtils.concatAll(d2.getExclusions(), d.getExclusions()));
11031117
for (GroupArtifact exclusion : d.getExclusions()) {
11041118
if (matchesGlob(getValue(d2.getGroupId()), getValue(exclusion.getGroupId())) &&
1105-
matchesGlob(getValue(d2.getArtifactId()), getValue(exclusion.getArtifactId()))) {
1119+
matchesGlob(getValue(d2.getArtifactId()), getValue(exclusion.getArtifactId()))) {
11061120
if (resolved.getEffectiveExclusions().isEmpty()) {
11071121
resolved.unsafeSetEffectiveExclusions(new ArrayList<>());
11081122
}
@@ -1141,7 +1155,7 @@ public List<ResolvedDependency> resolveDependencies(Scope scope, Map<GroupArtifa
11411155
private boolean contains(List<ResolvedDependency> dependencies, GroupArtifact ga, @Nullable String classifier) {
11421156
for (ResolvedDependency it : dependencies) {
11431157
if (it.getGroupId().equals(ga.getGroupId()) && it.getArtifactId().equals(ga.getArtifactId()) &&
1144-
(Objects.equals(classifier, it.getClassifier()))) {
1158+
(Objects.equals(classifier, it.getClassifier()))) {
11451159
return true;
11461160
}
11471161
}

0 commit comments

Comments
 (0)