From fe3f779094153380512df0448bd01d35caf63155 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Thu, 18 Jun 2026 15:50:36 +0200 Subject: [PATCH] Cache delegate recipes per recipe instance to avoid repeated construction The Gradle/Maven dependency wrapper recipes (`AddDependency`, `ChangeDependency`, `UpgradeDependencyVersion`, `UpgradeTransitiveDependencyVersion`) each delegate to a Gradle and a Maven `ScanningRecipe`. Previously a fresh delegate instance was constructed on every `getInitialValue`/`getScanner`/`getVisitor`/`validate` call (and, in `AddDependency`, once per source file inside the scanner's `visit`). Each `ScanningRecipe` construction runs `UUID.randomUUID()` for its accumulator-message key, which draws from `SecureRandom` and dominated profiles of recipe-heavy runs. Memoize each delegate lazily on the wrapper recipe instance (a `final transient AtomicReference` that is excluded from the generated constructor and from `equals`/`hashCode`) so it is built at most once per wrapper instance and reused across the scanning and editing phases. The delegate is derived from the wrapper's options, so it belongs to the recipe instance rather than the accumulator: callers such as `rewrite-spring` deliberately reuse a single accumulator across two wrapper instances that have different options, so caching the delegate on the accumulator would reuse the wrong options. The `Accumulator` types are left unchanged, preserving their public constructors for downstream callers that build them directly. --- .../java/dependencies/AddDependency.java | 47 ++++++++++++++----- .../java/dependencies/ChangeDependency.java | 31 +++++++++--- .../UpgradeDependencyVersion.java | 22 ++++++++- .../UpgradeTransitiveDependencyVersion.java | 24 +++++++++- 4 files changed, 102 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/openrewrite/java/dependencies/AddDependency.java b/src/main/java/org/openrewrite/java/dependencies/AddDependency.java index f3733a00..8e50a48b 100644 --- a/src/main/java/org/openrewrite/java/dependencies/AddDependency.java +++ b/src/main/java/org/openrewrite/java/dependencies/AddDependency.java @@ -15,12 +15,16 @@ */ package org.openrewrite.java.dependencies; +import lombok.AccessLevel; import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.Value; import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.maven.tree.Scope; +import java.util.concurrent.atomic.AtomicReference; + @EqualsAndHashCode(callSuper = false) @Value public class AddDependency extends ScanningRecipe { @@ -141,10 +145,13 @@ public Accumulator getInitialValue(ExecutionContext ctx) { @Override public TreeVisitor getScanner(Accumulator acc) { return new TreeVisitor() { + final TreeVisitor gradleAddDep = gradleAddDep().getScanner(acc.gradleAccumulator); + final TreeVisitor mavenAddDep = mavenAddDep().getScanner(acc.mavenAccumulator); + @Override public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) { - gradleAddDep().getScanner(acc.gradleAccumulator).visit(tree, ctx); - mavenAddDep().getScanner(acc.mavenAccumulator).visit(tree, ctx); + gradleAddDep.visit(tree, ctx); + mavenAddDep.visit(tree, ctx); return tree; } }; @@ -184,20 +191,36 @@ public static class Accumulator { org.openrewrite.maven.AddDependency.Scanned mavenAccumulator; } + @Getter(AccessLevel.NONE) + private final transient AtomicReference gradleDelegate = new AtomicReference<>(); + + @Getter(AccessLevel.NONE) + private final transient AtomicReference mavenDelegate = new AtomicReference<>(); + private org.openrewrite.gradle.AddDependency gradleAddDep() { - String configurationName = null; - if(configuration != null) { - configurationName = configuration; - } else if(scope != null) { - configurationName = Scope.asGradleConfigurationName(Scope.fromName(scope)); + org.openrewrite.gradle.AddDependency recipe = gradleDelegate.get(); + if (recipe == null) { + String configurationName = null; + if(configuration != null) { + configurationName = configuration; + } else if(scope != null) { + configurationName = Scope.asGradleConfigurationName(Scope.fromName(scope)); + } + recipe = new org.openrewrite.gradle.AddDependency(groupId, artifactId, version, versionPattern, + configurationName, onlyIfUsing, classifier, extension, familyPattern, acceptTransitive); + gradleDelegate.set(recipe); } - return new org.openrewrite.gradle.AddDependency(groupId, artifactId, version, versionPattern, - configurationName, onlyIfUsing, classifier, extension, familyPattern, acceptTransitive); + return recipe; } private org.openrewrite.maven.AddDependency mavenAddDep() { - return new org.openrewrite.maven.AddDependency(groupId, artifactId, version != null ? version : "latest.release", - versionPattern, scope, releasesOnly, onlyIfUsing, type, classifier, optional, familyPattern, - acceptTransitive); + org.openrewrite.maven.AddDependency recipe = mavenDelegate.get(); + if (recipe == null) { + recipe = new org.openrewrite.maven.AddDependency(groupId, artifactId, version != null ? version : "latest.release", + versionPattern, scope, releasesOnly, onlyIfUsing, type, classifier, optional, familyPattern, + acceptTransitive); + mavenDelegate.set(recipe); + } + return recipe; } } diff --git a/src/main/java/org/openrewrite/java/dependencies/ChangeDependency.java b/src/main/java/org/openrewrite/java/dependencies/ChangeDependency.java index 2b5e78d4..28bf5eb4 100644 --- a/src/main/java/org/openrewrite/java/dependencies/ChangeDependency.java +++ b/src/main/java/org/openrewrite/java/dependencies/ChangeDependency.java @@ -15,6 +15,7 @@ */ package org.openrewrite.java.dependencies; +import lombok.AccessLevel; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -22,6 +23,8 @@ import org.jspecify.annotations.Nullable; import org.openrewrite.*; +import java.util.concurrent.atomic.AtomicReference; + import static java.util.Objects.requireNonNull; @EqualsAndHashCode(callSuper = false) @@ -152,16 +155,32 @@ public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) { }; } + @Getter(AccessLevel.NONE) + private final transient AtomicReference mavenDelegate = new AtomicReference<>(); + + @Getter(AccessLevel.NONE) + private final transient AtomicReference gradleDelegate = new AtomicReference<>(); + org.openrewrite.maven.ChangeDependencyGroupIdAndArtifactId getMavenChangeDependency() { - return new org.openrewrite.maven.ChangeDependencyGroupIdAndArtifactId( - oldGroupId, oldArtifactId, newGroupId, newArtifactId, - newVersion, versionPattern, overrideManagedVersion, changeManagedDependency); + org.openrewrite.maven.ChangeDependencyGroupIdAndArtifactId recipe = mavenDelegate.get(); + if (recipe == null) { + recipe = new org.openrewrite.maven.ChangeDependencyGroupIdAndArtifactId( + oldGroupId, oldArtifactId, newGroupId, newArtifactId, + newVersion, versionPattern, overrideManagedVersion, changeManagedDependency); + mavenDelegate.set(recipe); + } + return recipe; } org.openrewrite.gradle.ChangeDependency getGradleChangeDependency() { - return new org.openrewrite.gradle.ChangeDependency( - oldGroupId, oldArtifactId, newGroupId, newArtifactId, - newVersion, versionPattern, overrideManagedVersion, true); + org.openrewrite.gradle.ChangeDependency recipe = gradleDelegate.get(); + if (recipe == null) { + recipe = new org.openrewrite.gradle.ChangeDependency( + oldGroupId, oldArtifactId, newGroupId, newArtifactId, + newVersion, versionPattern, overrideManagedVersion, true); + gradleDelegate.set(recipe); + } + return recipe; } @Data diff --git a/src/main/java/org/openrewrite/java/dependencies/UpgradeDependencyVersion.java b/src/main/java/org/openrewrite/java/dependencies/UpgradeDependencyVersion.java index b2c0e14a..b3d41021 100644 --- a/src/main/java/org/openrewrite/java/dependencies/UpgradeDependencyVersion.java +++ b/src/main/java/org/openrewrite/java/dependencies/UpgradeDependencyVersion.java @@ -15,6 +15,7 @@ */ package org.openrewrite.java.dependencies; +import lombok.AccessLevel; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -23,6 +24,7 @@ import org.openrewrite.*; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import static java.util.Objects.requireNonNull; @@ -139,12 +141,28 @@ public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) { }; } + @Getter(AccessLevel.NONE) + private final transient AtomicReference mavenDelegate = new AtomicReference<>(); + + @Getter(AccessLevel.NONE) + private final transient AtomicReference gradleDelegate = new AtomicReference<>(); + org.openrewrite.maven.UpgradeDependencyVersion getUpgradeMavenDependencyVersion() { - return new org.openrewrite.maven.UpgradeDependencyVersion(groupId, artifactId, newVersion, versionPattern, overrideManagedVersion, retainVersions); + org.openrewrite.maven.UpgradeDependencyVersion recipe = mavenDelegate.get(); + if (recipe == null) { + recipe = new org.openrewrite.maven.UpgradeDependencyVersion(groupId, artifactId, newVersion, versionPattern, overrideManagedVersion, retainVersions); + mavenDelegate.set(recipe); + } + return recipe; } public org.openrewrite.gradle.UpgradeDependencyVersion getUpgradeGradleDependencyVersion() { - return new org.openrewrite.gradle.UpgradeDependencyVersion(groupId, artifactId, newVersion, versionPattern); + org.openrewrite.gradle.UpgradeDependencyVersion recipe = gradleDelegate.get(); + if (recipe == null) { + recipe = new org.openrewrite.gradle.UpgradeDependencyVersion(groupId, artifactId, newVersion, versionPattern); + gradleDelegate.set(recipe); + } + return recipe; } @Data diff --git a/src/main/java/org/openrewrite/java/dependencies/UpgradeTransitiveDependencyVersion.java b/src/main/java/org/openrewrite/java/dependencies/UpgradeTransitiveDependencyVersion.java index a12e0bcd..250ee033 100644 --- a/src/main/java/org/openrewrite/java/dependencies/UpgradeTransitiveDependencyVersion.java +++ b/src/main/java/org/openrewrite/java/dependencies/UpgradeTransitiveDependencyVersion.java @@ -15,12 +15,16 @@ */ package org.openrewrite.java.dependencies; +import lombok.AccessLevel; import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.Value; import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.maven.AddManagedDependency; +import java.util.concurrent.atomic.AtomicReference; + @EqualsAndHashCode(callSuper = false) @Value public class UpgradeTransitiveDependencyVersion extends ScanningRecipe { @@ -154,11 +158,27 @@ public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) { }; } + @Getter(AccessLevel.NONE) + private final transient AtomicReference gradleDelegate = new AtomicReference<>(); + + @Getter(AccessLevel.NONE) + private final transient AtomicReference mavenDelegate = new AtomicReference<>(); + private org.openrewrite.gradle.UpgradeTransitiveDependencyVersion getGradleUpgradeTransitive() { - return new org.openrewrite.gradle.UpgradeTransitiveDependencyVersion(groupId, artifactId, version, versionPattern, because, null); + org.openrewrite.gradle.UpgradeTransitiveDependencyVersion recipe = gradleDelegate.get(); + if (recipe == null) { + recipe = new org.openrewrite.gradle.UpgradeTransitiveDependencyVersion(groupId, artifactId, version, versionPattern, because, null); + gradleDelegate.set(recipe); + } + return recipe; } private org.openrewrite.maven.UpgradeTransitiveDependencyVersion getMavenUpgradeTransitive() { - return new org.openrewrite.maven.UpgradeTransitiveDependencyVersion(groupId, artifactId, version, scope, type, classifier, versionPattern, releasesOnly, onlyIfUsing, addToRootPom, because); + org.openrewrite.maven.UpgradeTransitiveDependencyVersion recipe = mavenDelegate.get(); + if (recipe == null) { + recipe = new org.openrewrite.maven.UpgradeTransitiveDependencyVersion(groupId, artifactId, version, scope, type, classifier, versionPattern, releasesOnly, onlyIfUsing, addToRootPom, because); + mavenDelegate.set(recipe); + } + return recipe; } }