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 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 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*/Long>{} + interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2*/Long>{} """ ) ); @@ -425,10 +425,10 @@ interface MyOtherIntermediateRepository2 extends Repositor ), java( """ - interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2{} + interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2{} """, """ - interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2*/Long, String>{} + interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2*/Long, String>{} """ ) @@ -468,10 +468,10 @@ interface MyOtherIntermediateRepository2 extends MyOtherIntermediateRepos ), java( """ - interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2{} + interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2{} """, """ - interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2*/Long, Customer>{} + interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository2*/Long, Customer>{} """ ) ); @@ -501,10 +501,10 @@ interface MyOtherIntermediateRepository1 extends Repository{} + interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository1{} """, """ - interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository1*/Long, Customer>{} + interface MyOtherConcreteRepository2 extends MyOtherIntermediateRepository1*/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;