diff --git a/src/main/java/org/openrewrite/java/spring/boot3/MigrateHooksToReactorContextProperty.java b/src/main/java/org/openrewrite/java/spring/boot3/MigrateHooksToReactorContextProperty.java
new file mode 100644
index 000000000..236462e3c
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/spring/boot3/MigrateHooksToReactorContextProperty.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Moderne Source Available License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://docs.moderne.io/licensing/moderne-source-available-license
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.spring.boot3;
+
+import lombok.EqualsAndHashCode;
+import org.openrewrite.*;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.MethodMatcher;
+import org.openrewrite.java.RemoveMethodInvocationsVisitor;
+import org.openrewrite.java.marker.JavaProject;
+import org.openrewrite.java.search.UsesMethod;
+import org.openrewrite.java.search.UsesType;
+import org.openrewrite.java.spring.AddSpringProperty;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaSourceFile;
+
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+
+import static java.util.Collections.singletonList;
+import static org.openrewrite.Preconditions.and;
+
+public class MigrateHooksToReactorContextProperty extends ScanningRecipe {
+ @Override
+ public String getDisplayName() {
+ return "Use `spring.reactor.context-propagation` property";
+ }
+
+ @Override
+ public String getDescription() {
+ return "Replace `Hooks.enableAutomaticContextPropagation()` with `spring.reactor.context-propagation=true`.";
+ }
+
+ @Override
+ public ProjectsWithHooks getInitialValue(ExecutionContext ctx) {
+ return new ProjectsWithHooks();
+ }
+
+ private static final String SPRING_BOOT_APPLICATION_FQN = "org.springframework.boot.autoconfigure.SpringBootApplication";
+ private static final String HOOKS_PATTERN = "reactor.core.publisher.Hooks enableAutomaticContextPropagation()";
+ private static final MethodMatcher HOOKS_MATCHER = new MethodMatcher(HOOKS_PATTERN);
+
+ @Override
+ public TreeVisitor, ExecutionContext> getScanner(ProjectsWithHooks acc) {
+ return Preconditions.check(
+ and(
+ new UsesType<>(SPRING_BOOT_APPLICATION_FQN, true),
+ new UsesMethod<>(HOOKS_MATCHER)),
+ new JavaIsoVisitor() {
+ @Override
+ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
+ J.MethodInvocation mi = super.visitMethodInvocation(method, ctx);
+ if (HOOKS_MATCHER.matches(mi)) {
+ JavaSourceFile sourceFile = getCursor().firstEnclosing(JavaSourceFile.class);
+ if (sourceFile != null) {
+ sourceFile.getMarkers().findFirst(JavaProject.class)
+ .ifPresent(project -> acc.projectsWithHooks.add(project));
+ }
+ }
+ return mi;
+ }
+ }
+ );
+ }
+
+ @Override
+ public TreeVisitor, ExecutionContext> getVisitor(ProjectsWithHooks acc) {
+ if (acc.projectsWithHooks.isEmpty()) {
+ return TreeVisitor.noop();
+ }
+ return new TreeVisitor() {
+ @Override
+ public Tree preVisit(Tree tree, ExecutionContext ctx) {
+ stopAfterPreVisit();
+
+ // Only process files in projects where Hooks were found
+ Optional currentProject = tree.getMarkers().findFirst(JavaProject.class);
+ if (!acc.projectsWithHooks.contains(currentProject.orElse(null))) {
+ return tree;
+ }
+
+ // Remove Hooks.enableAutomaticContextPropagation() calls from Java source files
+ if (tree instanceof JavaSourceFile) {
+ return new RemoveMethodInvocationsVisitor(singletonList(HOOKS_PATTERN)).visitNonNull(tree, ctx);
+ }
+
+ return new AddSpringProperty("spring.reactor.context-propagation", "true", null, null)
+ .getVisitor()
+ .visitNonNull(tree, ctx);
+ }
+ };
+ }
+
+ @EqualsAndHashCode
+ public static class ProjectsWithHooks {
+ Set projectsWithHooks = new HashSet<>();
+ }
+}
diff --git a/src/main/resources/META-INF/rewrite/examples.yml b/src/main/resources/META-INF/rewrite/examples.yml
index 3e270a92b..37207bff3 100644
--- a/src/main/resources/META-INF/rewrite/examples.yml
+++ b/src/main/resources/META-INF/rewrite/examples.yml
@@ -565,7 +565,7 @@ examples:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
- public class MyJobConfig {
+ class MyJobConfig {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@@ -584,7 +584,7 @@ examples:
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.context.annotation.Bean;
- public class MyJobConfig {
+ class MyJobConfig {
@Bean
Job myJob(Step step, JobRepository jobRepository) {
@@ -1491,22 +1491,22 @@ examples:
spring.profiles.active=production
spring.config.activate.on-profile=dev
language: properties
- - before: |2
- ---
- spring:
- profiles:
- active: dev
- ---
- spring:
- profiles: prod
- after: |2
- ---
- spring:
- profiles:
- active: dev
- ---
- spring:
- config.activate.on-profile: prod
+ - before: |
+ ---
+ spring:
+ profiles:
+ active: dev
+ ---
+ spring:
+ profiles: prod
+ after: |
+ ---
+ spring:
+ profiles:
+ active: dev
+ ---
+ spring:
+ config.activate.on-profile: prod
language: yaml
---
type: specs.openrewrite.org/v1beta/example
@@ -1856,6 +1856,39 @@ examples:
language: java
---
type: specs.openrewrite.org/v1beta/example
+recipeName: org.openrewrite.java.spring.boot3.MigrateHooksToReactorContextProperty
+examples:
+- description: ''
+ sources:
+ - before: |
+ import reactor.core.publisher.Hooks;
+ import org.springframework.boot.SpringApplication;
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+ @SpringBootApplication
+ public class MyApplication {
+ public static void main(String[] args) {
+ Hooks.enableAutomaticContextPropagation();
+ SpringApplication.run(MyApplication.class, args);
+ }
+ }
+ after: |
+ import org.springframework.boot.SpringApplication;
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+ @SpringBootApplication
+ public class MyApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(MyApplication.class, args);
+ }
+ }
+ language: java
+ - before: project
+ language: mavenProject
+ - after: spring.reactor.context-propagation=true
+ language: properties
+---
+type: specs.openrewrite.org/v1beta/example
recipeName: org.openrewrite.java.spring.boot3.MigrateMaxHttpHeaderSize
examples:
- description: ''
@@ -4648,22 +4681,13 @@ examples:
- description: ''
parameters:
- 2.7.X
- - 3.0.0-M3
+ - 3.0.0
sources:
- before: |
com.example
explicit-deps-app
0.0.1-SNAPSHOT
-
-
- spring-milestone
- https://repo.spring.io/milestone
-
- false
-
-
-
org.springframework.boot
@@ -4683,25 +4707,16 @@ examples:
com.example
explicit-deps-app
0.0.1-SNAPSHOT
-
-
- spring-milestone
- https://repo.spring.io/milestone
-
- false
-
-
-
org.springframework.boot
spring-boot-starter-web
- 3.0.0-M3
+ 3.0.0
org.springframework.boot
spring-boot-starter-test
- 3.0.0-M3
+ 3.0.0
test
diff --git a/src/main/resources/META-INF/rewrite/spring-boot-32.yml b/src/main/resources/META-INF/rewrite/spring-boot-32.yml
index a0906703f..c711334d3 100644
--- a/src/main/resources/META-INF/rewrite/spring-boot-32.yml
+++ b/src/main/resources/META-INF/rewrite/spring-boot-32.yml
@@ -83,6 +83,7 @@ recipeList:
artifactId: "*"
newVersion: 2.5.x
- org.openrewrite.hibernate.MigrateToHibernate64
+ - org.openrewrite.java.spring.boot3.MigrateHooksToReactorContextProperty
- org.openrewrite.java.spring.boot3.RelocateLauncherClasses
- org.openrewrite.java.spring.boot3.UpgradeMyBatisToSpringBoot_3_2
- org.openrewrite.java.springdoc.UpgradeSpringDoc_2_5
diff --git a/src/test/java/org/openrewrite/gradle/spring/UpdateGradleTest.java b/src/test/java/org/openrewrite/gradle/spring/UpdateGradleTest.java
index 13c4b61dd..9fd7a70a8 100644
--- a/src/test/java/org/openrewrite/gradle/spring/UpdateGradleTest.java
+++ b/src/test/java/org/openrewrite/gradle/spring/UpdateGradleTest.java
@@ -18,7 +18,6 @@
import org.junit.jupiter.api.Test;
import org.openrewrite.FileAttributes;
import org.openrewrite.Tree;
-import org.openrewrite.config.Environment;
import org.openrewrite.marker.BuildTool;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;
diff --git a/src/test/java/org/openrewrite/java/spring/boot2/SpringBoot2JUnit4to5MigrationTest.java b/src/test/java/org/openrewrite/java/spring/boot2/SpringBoot2JUnit4to5MigrationTest.java
index 50bef6ecd..f8e84a6e5 100644
--- a/src/test/java/org/openrewrite/java/spring/boot2/SpringBoot2JUnit4to5MigrationTest.java
+++ b/src/test/java/org/openrewrite/java/spring/boot2/SpringBoot2JUnit4to5MigrationTest.java
@@ -18,7 +18,6 @@
import org.junit.jupiter.api.Test;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.Issue;
-import org.openrewrite.config.Environment;
import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;
diff --git a/src/test/java/org/openrewrite/java/spring/boot2/search/EntityIdForRepositoryVisitorTest.java b/src/test/java/org/openrewrite/java/spring/boot2/search/EntityIdForRepositoryVisitorTest.java
index 53a05bdd2..32094d479 100644
--- a/src/test/java/org/openrewrite/java/spring/boot2/search/EntityIdForRepositoryVisitorTest.java
+++ b/src/test/java/org/openrewrite/java/spring/boot2/search/EntityIdForRepositoryVisitorTest.java
@@ -326,10 +326,10 @@ interface MyIntermediateRepository extend
),
java(
"""
- interface MyConcreteRepository extends MyIntermediateRepository{}
+ interface MyConcreteRepository extends MyIntermediateRepository{}
""",
"""
- interface MyConcreteRepository extends MyIntermediateRepository*/Long>{}
+ interface MyConcreteRepository extends MyIntermediateRepository*/Long>{}
"""
)
);
@@ -359,10 +359,10 @@ interface MyOtherIntermediateRepository1 extends Repository<
),
java(
"""
- interface MyOtherConcreteRepository1 extends MyOtherIntermediateRepository1{}
+ interface MyOtherConcreteRepository1 extends MyOtherIntermediateRepository1{}
""",
"""
- interface MyOtherConcreteRepository1 extends /*~~(Expected Domain Type ID is 'java.lang.String')~~>*/MyOtherIntermediateRepository1{}
+ interface MyOtherConcreteRepository1 extends /*~~(Expected Domain Type ID is 'java.lang.String')~~>*/MyOtherIntermediateRepository1{}
"""
)
);
@@ -392,10 +392,10 @@ interface MyOtherIntermediateRepository2 extends Repository{}
+ interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2{}
""",
"""
- interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2*~~(Expected Domain Type ID is 'java.lang.String')~~>*/Long>{}
+ interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2*~~(Expected Domain Type ID is 'java.lang.String')~~>*/Long>{}
"""
)
);
@@ -425,10 +425,10 @@ interface MyOtherIntermediateRepository2 extends Repositor
),
java(
"""
- interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2{}
+ interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2{}
""",
"""
- interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2*~~(Expected Domain Type ID is 'java.lang.String')~~>*/Long, String>{}
+ interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2*~~(Expected Domain Type ID is 'java.lang.String')~~>*/Long, String>{}
"""
)
@@ -468,10 +468,10 @@ interface MyOtherIntermediateRepository2 extends MyOtherIntermediateRepos
),
java(
"""
- interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2{}
+ interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2{}
""",
"""
- interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2*~~(Expected Domain Type ID is 'java.lang.String')~~>*/Long, Customer>{}
+ interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2*~~(Expected Domain Type ID is 'java.lang.String')~~>*/Long, Customer>{}
"""
)
);
@@ -501,10 +501,10 @@ interface MyOtherIntermediateRepository1 extends Repository{}
+ interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository1{}
""",
"""
- interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository1*~~(Expected Domain Type ID is 'java.lang.String')~~>*/Long, Customer>{}
+ interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository1*~~(Expected Domain Type ID is 'java.lang.String')~~>*/Long, Customer>{}
"""
)
);
@@ -534,10 +534,10 @@ interface MyOtherIntermediateRepository2 extends Repository,
),
java(
"""
- interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2{}
+ interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2{}
""",
"""
- interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2*/Long, Customer>{}
+ interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2*/Long, Customer>{}
"""
)
);
@@ -575,10 +575,10 @@ interface MyOtherIntermediateRepository2 extends MyOtherIntermediateRepositor
),
java(
"""
- interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2{}
+ interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2{}
""",
"""
- interface MyOtherConcreteRepository2 extends /*~~(Expected Domain Type ID is 'java.lang.String')~~>*/MyOtherIntermediateRepository2{}
+ interface MyOtherConcreteRepository2 extends /*~~(Expected Domain Type ID is 'java.lang.String')~~>*/MyOtherIntermediateRepository2{}
"""
)
);
diff --git a/src/test/java/org/openrewrite/java/spring/boot3/MigrateHooksToReactorContextPropertyTest.java b/src/test/java/org/openrewrite/java/spring/boot3/MigrateHooksToReactorContextPropertyTest.java
new file mode 100644
index 000000000..db76d6808
--- /dev/null
+++ b/src/test/java/org/openrewrite/java/spring/boot3/MigrateHooksToReactorContextPropertyTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Moderne Source Available License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://docs.moderne.io/licensing/moderne-source-available-license
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.spring.boot3;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.*;
+import static org.openrewrite.properties.Assertions.properties;
+
+class MigrateHooksToReactorContextPropertyTest implements RewriteTest {
+
+ @Override
+ public void defaults(RecipeSpec spec) {
+ spec.recipe(new MigrateHooksToReactorContextProperty())
+ .parser(JavaParser.fromJavaVersion()
+ //language=java
+ .dependsOn(
+ """
+ package org.springframework.boot.autoconfigure;
+ public @interface SpringBootApplication {}
+ """,
+ """
+ package org.springframework.boot;
+ public class SpringApplication {
+ public static void run(Class> cls, String[] args) {}
+ }
+ """,
+ """
+ package reactor.core.publisher;
+ public class Hooks {
+ public static void enableAutomaticContextPropagation() {}
+ }
+ """
+ )
+ );
+ }
+
+ @DocumentExample
+ @Test
+ void replaceMethodCallWithProperty() {
+ rewriteRun(
+ spec -> spec.recipe(new MigrateHooksToReactorContextProperty()),
+ mavenProject("project",
+ srcMainJava(
+ java(
+ """
+ import reactor.core.publisher.Hooks;
+ import org.springframework.boot.SpringApplication;
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+ @SpringBootApplication
+ public class MyApplication {
+ public static void main(String[] args) {
+ Hooks.enableAutomaticContextPropagation();
+ SpringApplication.run(MyApplication.class, args);
+ }
+ }
+ """,
+ """
+ import org.springframework.boot.SpringApplication;
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+ @SpringBootApplication
+ public class MyApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(MyApplication.class, args);
+ }
+ }
+ """
+ )
+ ),
+ srcTestResources(
+ properties(
+ "",
+ "spring.reactor.context-propagation=true",
+ spec -> spec.path("application.properties")
+ )
+ )
+ )
+ );
+ }
+
+ @Test
+ void shouldNotAddPropertyWhenNoHooksPresent() {
+ rewriteRun(
+ spec -> spec.recipe(new MigrateHooksToReactorContextProperty()),
+ mavenProject("project",
+ srcMainJava(
+ java(
+ """
+ import org.springframework.boot.SpringApplication;
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+ @SpringBootApplication
+ public class MyApplication {
+ public static void main(String[] args) {
+ // No Hooks.enableAutomaticContextPropagation() here
+ SpringApplication.run(MyApplication.class, args);
+ }
+ }
+ """
+ )
+ ),
+ srcTestResources(
+ properties(
+ """
+ server.port=8080
+ """,
+ spec -> spec.path("application.properties")
+ )
+ )
+ )
+ );
+ }
+}
diff --git a/src/test/java/org/openrewrite/java/spring/boot3/SpringCloudVersionUpgradeTest.java b/src/test/java/org/openrewrite/java/spring/boot3/SpringCloudVersionUpgradeTest.java
index dd257e0e3..859b3f28a 100644
--- a/src/test/java/org/openrewrite/java/spring/boot3/SpringCloudVersionUpgradeTest.java
+++ b/src/test/java/org/openrewrite/java/spring/boot3/SpringCloudVersionUpgradeTest.java
@@ -17,7 +17,6 @@
import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
-import org.openrewrite.config.Environment;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;
diff --git a/src/test/java/org/openrewrite/java/spring/boot3/UpgradeSpringBoot3ConfigurationTest.java b/src/test/java/org/openrewrite/java/spring/boot3/UpgradeSpringBoot3ConfigurationTest.java
index fc9b1556d..3c15ce405 100644
--- a/src/test/java/org/openrewrite/java/spring/boot3/UpgradeSpringBoot3ConfigurationTest.java
+++ b/src/test/java/org/openrewrite/java/spring/boot3/UpgradeSpringBoot3ConfigurationTest.java
@@ -17,7 +17,6 @@
import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
-import org.openrewrite.config.Environment;
import org.openrewrite.test.RewriteTest;
import static org.openrewrite.java.Assertions.mavenProject;
diff --git a/src/test/java/org/openrewrite/java/spring/data/MigrateJpaSortTest.java b/src/test/java/org/openrewrite/java/spring/data/MigrateJpaSortTest.java
index b207fb0e2..ca6a8423e 100644
--- a/src/test/java/org/openrewrite/java/spring/data/MigrateJpaSortTest.java
+++ b/src/test/java/org/openrewrite/java/spring/data/MigrateJpaSortTest.java
@@ -15,7 +15,6 @@
*/
package org.openrewrite.java.spring.data;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.InMemoryExecutionContext;
diff --git a/src/test/java/org/openrewrite/java/spring/http/ReplaceLiteralsTest.java b/src/test/java/org/openrewrite/java/spring/http/ReplaceLiteralsTest.java
index 79fd4aa29..3adb3b692 100644
--- a/src/test/java/org/openrewrite/java/spring/http/ReplaceLiteralsTest.java
+++ b/src/test/java/org/openrewrite/java/spring/http/ReplaceLiteralsTest.java
@@ -16,9 +16,7 @@
package org.openrewrite.java.spring.http;
import org.junit.jupiter.api.Test;
-import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.Issue;
-import org.openrewrite.config.Environment;
import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;
diff --git a/src/test/java/org/openrewrite/java/spring/security6/oauth2/client/OAuth2LoginLambdaDslTest.java b/src/test/java/org/openrewrite/java/spring/security6/oauth2/client/OAuth2LoginLambdaDslTest.java
index fab6e44d6..8470ee249 100644
--- a/src/test/java/org/openrewrite/java/spring/security6/oauth2/client/OAuth2LoginLambdaDslTest.java
+++ b/src/test/java/org/openrewrite/java/spring/security6/oauth2/client/OAuth2LoginLambdaDslTest.java
@@ -22,7 +22,6 @@
import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;
-import org.openrewrite.test.TypeValidation;
import static org.openrewrite.java.Assertions.java;