1515 */
1616package 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 .*;
2119import com .fasterxml .jackson .databind .JsonNode ;
2220import com .fasterxml .jackson .databind .node .ArrayNode ;
2321import com .fasterxml .jackson .databind .node .JsonNodeFactory ;
2422import 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 .*;
2924import lombok .experimental .NonFinal ;
3025import org .jspecify .annotations .Nullable ;
3126import org .openrewrite .ExecutionContext ;
4742import java .util .function .UnaryOperator ;
4843
4944import static java .util .Collections .*;
50- import static java .util .stream .Collectors .toList ;
5145import static java .util .stream .Collectors .toMap ;
5246import 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
5755public 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