From baf74820eff8f1261b88ede729bf54dc6f9a53f6 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Mon, 29 Jul 2024 18:14:46 -0700 Subject: [PATCH] Cleanup UpgradeExplicitSpringBootDependencies --- ...UpgradeExplicitSpringBootDependencies.java | 197 +++++++++++------- ...adeExplicitSpringBootDependenciesTest.java | 45 ++-- 2 files changed, 137 insertions(+), 105 deletions(-) diff --git a/src/main/java/org/openrewrite/maven/spring/UpgradeExplicitSpringBootDependencies.java b/src/main/java/org/openrewrite/maven/spring/UpgradeExplicitSpringBootDependencies.java index 45fe76385..ce5e4ce39 100644 --- a/src/main/java/org/openrewrite/maven/spring/UpgradeExplicitSpringBootDependencies.java +++ b/src/main/java/org/openrewrite/maven/spring/UpgradeExplicitSpringBootDependencies.java @@ -16,13 +16,13 @@ package org.openrewrite.maven.spring; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.Value; import org.openrewrite.*; -import org.openrewrite.groovy.tree.G; -import org.openrewrite.internal.lang.NonNull; +import org.openrewrite.gradle.marker.GradleProject; import org.openrewrite.internal.lang.Nullable; +import org.openrewrite.java.dependencies.UpgradeDependencyVersion; import org.openrewrite.marker.SearchResult; import org.openrewrite.maven.MavenDownloadingException; import org.openrewrite.maven.MavenIsoVisitor; @@ -33,40 +33,48 @@ import java.util.*; +import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; +@Value @EqualsAndHashCode(callSuper = false) -public class UpgradeExplicitSpringBootDependencies extends Recipe { +public class UpgradeExplicitSpringBootDependencies extends ScanningRecipe { private static final String SPRINGBOOT_GROUP = "org.springframework.boot"; private static final String SPRING_BOOT_DEPENDENCIES = "spring-boot-dependencies"; - private transient final Map springBootDependenciesMap = new HashMap<>(); - - @Option(displayName = "From Spring Version", + @Option(displayName = "From Spring version", description = "XRage pattern for spring version used to limit which projects should be updated", example = " 2.7.+") - private final String fromVersion; + String fromVersion; - @Option(displayName = "To Spring Version", + @Option(displayName = "To Spring version", description = "Upgrade version of `org.springframework.boot`", example = "3.0.0-M3") - private final String toVersion; - - @JsonCreator - public UpgradeExplicitSpringBootDependencies(@JsonProperty("fromVersion") String fromVersion, @JsonProperty("toVersion") String toVersion) { - this.fromVersion = fromVersion; - this.toVersion = toVersion; - } + String toVersion; @Override public String getDisplayName() { - return "Upgrade un-managed spring project dependencies"; + return "Upgrade Spring dependencies"; } @Override public String getDescription() { - return "Upgrades un-managed spring-boot project dependencies according to the specified spring-boot version."; + return "Upgrades dependencies according to the specified version of spring boot. " + + "Spring boot has many direct and transitive dependencies. When a module has an explicit dependency on " + + "one of these it may also need to be upgraded to match the version used by spring boot."; + } + + @Data + public static class Accumulator { + UpgradeDependencyVersion.Accumulator udvAcc = new UpgradeDependencyVersion.Accumulator( + new org.openrewrite.maven.UpgradeDependencyVersion.Accumulator(), + new org.openrewrite.gradle.UpgradeDependencyVersion.DependencyVersionState() + ); + List repositories = new ArrayList<>(); + Map springBootDependenciesMap = new HashMap<>(); + @Nullable + MavenDownloadingException mavenDownloadingException = null; } private TreeVisitor precondition() { @@ -78,7 +86,7 @@ public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { ResolvedManagedDependency managedDependency = findManagedDependency(resultTag); if (managedDependency != null && managedDependency.getGroupId().equals(SPRINGBOOT_GROUP) && satisfiesOldVersionPattern(managedDependency.getVersion())) { - return applyThisRecipe(resultTag); + return SearchResult.found(resultTag); } } @@ -86,17 +94,12 @@ && satisfiesOldVersionPattern(managedDependency.getVersion())) { ResolvedDependency dependency = findDependency(resultTag); if ((dependency != null) && dependency.getGroupId().equals(SPRINGBOOT_GROUP) && satisfiesOldVersionPattern(dependency.getVersion())) { - return applyThisRecipe(resultTag); + return SearchResult.found(resultTag); } } return resultTag; } - @NonNull - private Xml.Tag applyThisRecipe(Xml.Tag resultTag) { - return resultTag.withMarkers(resultTag.getMarkers().addIfAbsent(new SearchResult(UUID.randomUUID(), "SpringBoot dependency"))); - } - private boolean satisfiesOldVersionPattern(@Nullable String version) { return version != null && XRange.build(fromVersion, version).isValid(); } @@ -104,14 +107,92 @@ private boolean satisfiesOldVersionPattern(@Nullable String version) { } @Override - public TreeVisitor getVisitor() { + public Accumulator getInitialValue(ExecutionContext ctx) { + return new Accumulator(); + } + + @Override + public TreeVisitor getScanner(Accumulator acc) { + //noinspection NullableProblems + return new TreeVisitor() { + @Override + public Tree visit(Tree tree, ExecutionContext ctx) { + TreeVisitor udvScanner = new UpgradeDependencyVersion("", "", "", null, null, null) + .getScanner(acc.getUdvAcc()); + if (udvScanner.isAcceptable((SourceFile) tree, ctx)) { + udvScanner.visit(tree, ctx); + } + + Optional maybeGp = tree.getMarkers() + .findFirst(GradleProject.class); + if (maybeGp.isPresent()) { + GradleProject gp = maybeGp.get(); + acc.repositories.addAll(gp.getMavenRepositories()); + } + Optional maybeMrr = tree.getMarkers() + .findFirst(MavenResolutionResult.class); + if (maybeMrr.isPresent()) { + MavenResolutionResult mrr = maybeMrr.get(); + acc.repositories.addAll(mrr.getPom().getRepositories()); + } + + return tree; + } + }; + } + + @Override + public Collection generate(Accumulator acc, Collection generatedInThisCycle, ExecutionContext ctx) { + List repositories = acc.getRepositories(); + repositories.add(MavenRepository.builder() + .id("repository.spring.milestone") + .uri("https://repo.spring.io/milestone") + .releases(true) + .snapshots(true) + .build()); + repositories.add(MavenRepository.builder() + .id("spring-snapshot") + .uri("https://repo.spring.io/snapshot") + .releases(false) + .snapshots(true) + .build()); + repositories.add(MavenRepository.builder() + .id("spring-release") + .uri("https://repo.spring.io/release") + .releases(true) + .snapshots(false) + .build()); + + MavenPomDownloader downloader = new MavenPomDownloader(emptyMap(), ctx); + GroupArtifactVersion gav = new GroupArtifactVersion(SPRINGBOOT_GROUP, SPRING_BOOT_DEPENDENCIES, toVersion); + String relativePath = ""; + + try { + Pom pom = downloader.download(gav, relativePath, null, repositories); + ResolvedPom resolvedPom = pom.resolve(emptyList(), downloader, repositories, ctx); + List dependencyManagement = resolvedPom.getDependencyManagement(); + dependencyManagement + .stream() + .filter(d -> d.getVersion() != null) + .forEach(d -> acc.getSpringBootDependenciesMap().put(d.getGroupId() + ":" + d.getArtifactId().toLowerCase(), d.getVersion())); + } catch (MavenDownloadingException e) { + acc.mavenDownloadingException = e; + } + return emptyList(); + } + + @Override + public Collection generate(Accumulator acc, ExecutionContext ctx) { + return super.generate(acc, ctx); + } + + @Override + public TreeVisitor getVisitor(Accumulator acc) { return Preconditions.check(precondition(), new MavenIsoVisitor() { @Override public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) { - try { - buildDependencyMap(ctx); - } catch (MavenDownloadingException e) { - return e.warn(document); + if(acc.mavenDownloadingException != null) { + return acc.mavenDownloadingException.warn(document); } return super.visitDocument(document, ctx); } @@ -122,71 +203,29 @@ public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { if (isManagedDependencyTag()) { ResolvedManagedDependency managedDependency = findManagedDependency(resultTag); if (managedDependency != null) { - mayBeUpdateVersion(managedDependency.getGroupId(), managedDependency.getArtifactId(), resultTag); + mayBeUpdateVersion(acc, managedDependency.getGroupId(), managedDependency.getArtifactId(), resultTag); } } if (isDependencyTag()) { ResolvedDependency dependency = findDependency(resultTag); if (dependency != null) { - mayBeUpdateVersion(dependency.getGroupId(), dependency.getArtifactId(), resultTag); + mayBeUpdateVersion(acc, dependency.getGroupId(), dependency.getArtifactId(), resultTag); } } return resultTag; } - private void mayBeUpdateVersion(String groupId, String artifactId, Xml.Tag tag) { - String dependencyVersion = springBootDependenciesMap.get(groupId + ":" + artifactId); + private void mayBeUpdateVersion(Accumulator acc, String groupId, String artifactId, Xml.Tag tag) { + String dependencyVersion = acc.springBootDependenciesMap.get(groupId + ":" + artifactId); if (dependencyVersion != null) { Optional version = tag.getChild("version"); if (!version.isPresent() || !version.get().getValue().isPresent()) { return; } - // TODO: we could use the org.openrewrite.java.dependencies.UpgradeDependencyVersion if we implement there a getVisitor with a similar logic than here, - // but right now it's just a list of recipes, and the getVisitor is the default from Recipe and does nothing - SourceFile sourceFile = getCursor().firstEnclosing(SourceFile.class); - if (sourceFile instanceof Xml.Document) { - doAfterVisit(new org.openrewrite.maven.UpgradeDependencyVersion(groupId, artifactId, dependencyVersion, null, null, null).getVisitor()); - } else if (sourceFile instanceof G.CompilationUnit) { - doAfterVisit(new org.openrewrite.gradle.UpgradeDependencyVersion(groupId, artifactId, dependencyVersion, null).getVisitor()); - } + doAfterVisit(new org.openrewrite.java.dependencies.UpgradeDependencyVersion(groupId, artifactId, dependencyVersion, null, true, null) + .getVisitor(acc.getUdvAcc())); } } - - private void buildDependencyMap(ExecutionContext ctx) throws MavenDownloadingException { - if (springBootDependenciesMap.isEmpty()) { - MavenPomDownloader downloader = new MavenPomDownloader(emptyMap(), ctx, - getResolutionResult().getMavenSettings(), getResolutionResult().getActiveProfiles()); - GroupArtifactVersion gav = new GroupArtifactVersion(SPRINGBOOT_GROUP, SPRING_BOOT_DEPENDENCIES, toVersion); - String relativePath = ""; - List repositories = new ArrayList<>(); - repositories.add(MavenRepository.builder() - .id("repository.spring.milestone") - .uri("https://repo.spring.io/milestone") - .releases(true) - .snapshots(true) - .build()); - repositories.add(MavenRepository.builder() - .id("spring-snapshot") - .uri("https://repo.spring.io/snapshot") - .releases(false) - .snapshots(true) - .build()); - repositories.add(MavenRepository.builder() - .id("spring-release") - .uri("https://repo.spring.io/release") - .releases(true) - .snapshots(false) - .build()); - Pom pom = downloader.download(gav, relativePath, null, repositories); - ResolvedPom resolvedPom = pom.resolve(Collections.emptyList(), downloader, repositories, ctx); - List dependencyManagement = resolvedPom.getDependencyManagement(); - dependencyManagement - .stream() - .filter(d -> d.getVersion() != null) - .forEach(d -> springBootDependenciesMap.put(d.getGroupId() + ":" + d.getArtifactId().toLowerCase(), d.getVersion())); - } - } - }); } } diff --git a/src/testWithSpringBoot_2_4/java/org/openrewrite/maven/spring/UpgradeExplicitSpringBootDependenciesTest.java b/src/testWithSpringBoot_2_4/java/org/openrewrite/maven/spring/UpgradeExplicitSpringBootDependenciesTest.java index af491933e..66cf6a733 100755 --- a/src/testWithSpringBoot_2_4/java/org/openrewrite/maven/spring/UpgradeExplicitSpringBootDependenciesTest.java +++ b/src/testWithSpringBoot_2_4/java/org/openrewrite/maven/spring/UpgradeExplicitSpringBootDependenciesTest.java @@ -33,6 +33,7 @@ void shouldUpdateExplicitDependenciesTo30() { srcMainJava( java("class A{}") ), + //language=xml pomXml( """ - + org.springframework.boot @@ -101,7 +102,7 @@ void shouldUpdateExplicitDependenciesTo30() { - + org.springframework.boot @@ -151,7 +152,7 @@ void shouldNotUpdateIfNoSpringDependencies() { 17 17 - + spring-milestone @@ -161,7 +162,7 @@ void shouldNotUpdateIfNoSpringDependencies() { - + io.dropwizard.metrics @@ -200,7 +201,7 @@ void shouldNotUpdateForOldVersion() { 17 17 - + spring-milestone @@ -210,7 +211,7 @@ void shouldNotUpdateForOldVersion() { - + org.springframework.boot @@ -223,7 +224,7 @@ void shouldNotUpdateForOldVersion() { 4.2.8 - + @@ -259,7 +260,7 @@ void shouldNotUpdateIfParentAndNoExplicitDeps() { 2.7.1 - + com.example explicit-deps-app 0.0.1-SNAPSHOT @@ -270,7 +271,7 @@ void shouldNotUpdateIfParentAndNoExplicitDeps() { 17 17 - + spring-milestone @@ -280,7 +281,7 @@ void shouldNotUpdateIfParentAndNoExplicitDeps() { - + org.springframework.boot @@ -291,7 +292,7 @@ void shouldNotUpdateIfParentAndNoExplicitDeps() { metrics-annotation - + @@ -317,8 +318,8 @@ void shouldUpdateIfSpringParentAndExplicitDependency() { srcMainJava( java("class A{}") ), + //language=xml pomXml( - //language=xml """ @@ -329,7 +330,7 @@ void shouldUpdateIfSpringParentAndExplicitDependency() { 2.7.1 - + com.example explicit-deps-app 0.0.1-SNAPSHOT @@ -341,7 +342,7 @@ void shouldUpdateIfSpringParentAndExplicitDependency() { 17 2.7.3 - + spring-milestone @@ -351,7 +352,7 @@ void shouldUpdateIfSpringParentAndExplicitDependency() { - + org.springframework.boot @@ -375,7 +376,7 @@ void shouldUpdateIfSpringParentAndExplicitDependency() { 2.7.1 - + com.example explicit-deps-app 0.0.1-SNAPSHOT @@ -387,7 +388,7 @@ void shouldUpdateIfSpringParentAndExplicitDependency() { 17 3.0.0-M3 - + spring-milestone @@ -397,7 +398,7 @@ void shouldUpdateIfSpringParentAndExplicitDependency() { - + org.springframework.boot @@ -614,7 +615,6 @@ void shouldUpdateWithVersionsAsProperty() { 2.7.3 4.2.8 - spring-milestone @@ -624,7 +624,6 @@ void shouldUpdateWithVersionsAsProperty() { - org.springframework.boot @@ -661,7 +660,6 @@ void shouldUpdateWithVersionsAsProperty() { 3.0.0-M3 4.2.9 - spring-milestone @@ -671,7 +669,6 @@ void shouldUpdateWithVersionsAsProperty() { - org.springframework.boot @@ -724,7 +721,6 @@ void shouldNotTouchNewerVersions() { 2.7.3 4.2.12 - spring-milestone @@ -734,7 +730,6 @@ void shouldNotTouchNewerVersions() { - org.springframework.boot @@ -771,7 +766,6 @@ void shouldNotTouchNewerVersions() { 3.0.0-M3 4.2.12 - spring-milestone @@ -781,7 +775,6 @@ void shouldNotTouchNewerVersions() { - org.springframework.boot