Skip to content

Commit a457348

Browse files
committed
Keep jakarta.annotation-api dependency when moving to Jakarta with Spring Boot project and null annotations are used
1 parent 8a107ee commit a457348

File tree

3 files changed

+236
-0
lines changed

3 files changed

+236
-0
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package org.openrewrite.java.migrate.jakarta;
2+
3+
import org.jspecify.annotations.Nullable;
4+
import org.openrewrite.ExecutionContext;
5+
import org.openrewrite.ScanningRecipe;
6+
import org.openrewrite.Tree;
7+
import org.openrewrite.TreeVisitor;
8+
import org.openrewrite.java.JavaIsoVisitor;
9+
import org.openrewrite.java.search.FindAnnotations;
10+
import org.openrewrite.java.tree.J;
11+
import org.openrewrite.marker.SearchResult;
12+
13+
import java.util.concurrent.atomic.AtomicBoolean;
14+
15+
public class HasNoJakartaNullAnnotations extends ScanningRecipe<AtomicBoolean> {
16+
@Override
17+
public String getDisplayName() {
18+
return "Project has no Jakarta null annotations";
19+
}
20+
21+
@Override
22+
public String getDescription() {
23+
return "Search for @Nonnull and @Nullable annotations, mark all source as found if no annotations are found.";
24+
}
25+
26+
@Override
27+
public AtomicBoolean getInitialValue(ExecutionContext ctx) {
28+
return new AtomicBoolean();
29+
}
30+
31+
@Override
32+
public TreeVisitor<?, ExecutionContext> getScanner(AtomicBoolean acc) {
33+
return new JavaIsoVisitor<ExecutionContext>() {
34+
@Override
35+
public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) {
36+
J.CompilationUnit c = super.visitCompilationUnit(cu, ctx);
37+
if (!acc.get()) {
38+
if ((!FindAnnotations.find(c, "@jakarta.annotation.Nonnull", true).isEmpty()) ||
39+
(!FindAnnotations.find(c, "@jakarta.annotation.Nullable", true).isEmpty())) {
40+
acc.set(true);
41+
}
42+
}
43+
return cu;
44+
}
45+
};
46+
}
47+
48+
@Override
49+
public TreeVisitor<?, ExecutionContext> getVisitor(AtomicBoolean acc) {
50+
return new TreeVisitor<Tree, ExecutionContext>() {
51+
@Override
52+
public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
53+
assert tree != null;
54+
if (!acc.get()) {
55+
return SearchResult.found(tree, "Project has no Jakarta null annotations");
56+
}
57+
return tree;
58+
}
59+
};
60+
}
61+
}

src/main/resources/META-INF/rewrite/jakarta-ee-9.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,7 @@ name: org.openrewrite.java.migrate.jakarta.RemoveJakartaAnnotationDependency
10831083
displayName: Remove `jakarta.annotation-api` dependency when managed by Spring Boot
10841084
description: Counteract the `jakarta.annotation-api` added by `org.openrewrite.java.migrate.javax.AddCommonAnnotationsDependencies` for Spring Boot applications.
10851085
preconditions:
1086+
- org.openrewrite.java.migrate.jakarta.HasNoJakartaNullAnnotations
10861087
- org.openrewrite.java.dependencies.DependencyInsight:
10871088
groupIdPattern: org.springframework.boot
10881089
artifactIdPattern: spring-boot-starter

src/test/java/org/openrewrite/java/migrate/jakarta/JavaxToJakartaTest.java

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ public void foo() {}
6060
}
6161
""";
6262

63+
@Language("java")
64+
private static final String jakartaAnnotation =
65+
"""
66+
package jakarta.annotation;
67+
public @interface Nonnull {
68+
}
69+
public @interface Nullable {
70+
}
71+
""";
72+
6373
@Override
6474
public void defaults(RecipeSpec spec) {
6575
spec.recipe(
@@ -574,6 +584,170 @@ public class TestApplication {
574584
);
575585
}
576586

587+
@Test
588+
void projectWithSpringBoot3StarterWebShouldNotRemoveJakartaDependencyWhenUsingNonnullAnnotation() {
589+
rewriteRun(
590+
spec -> spec.parser(JavaParser.fromJavaVersion().dependsOn(javaxServlet, jakartaAnnotation)),
591+
mavenProject(
592+
"Sample",
593+
//language=xml
594+
pomXml(
595+
"""
596+
<?xml version="1.0" encoding="UTF-8"?>
597+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
598+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
599+
<modelVersion>4.0.0</modelVersion>
600+
<parent>
601+
<groupId>org.springframework.boot</groupId>
602+
<artifactId>spring-boot-starter-parent</artifactId>
603+
<version>3.2.5</version>
604+
<relativePath/> <!-- lookup parent from repository -->
605+
</parent>
606+
<groupId>com.example</groupId>
607+
<artifactId>demo</artifactId>
608+
<version>0.0.1-SNAPSHOT</version>
609+
<dependencies>
610+
<dependency>
611+
<groupId>jakarta.annotation</groupId>
612+
<artifactId>jakarta.annotation-api</artifactId>
613+
<version>1.3.5</version>
614+
</dependency>
615+
<dependency>
616+
<groupId>org.springframework.boot</groupId>
617+
<artifactId>spring-boot-starter-web</artifactId>
618+
</dependency>
619+
</dependencies>
620+
</project>
621+
""",
622+
"""
623+
<?xml version="1.0" encoding="UTF-8"?>
624+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
625+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
626+
<modelVersion>4.0.0</modelVersion>
627+
<parent>
628+
<groupId>org.springframework.boot</groupId>
629+
<artifactId>spring-boot-starter-parent</artifactId>
630+
<version>3.2.5</version>
631+
<relativePath/> <!-- lookup parent from repository -->
632+
</parent>
633+
<groupId>com.example</groupId>
634+
<artifactId>demo</artifactId>
635+
<version>0.0.1-SNAPSHOT</version>
636+
<dependencies>
637+
<dependency>
638+
<groupId>jakarta.annotation</groupId>
639+
<artifactId>jakarta.annotation-api</artifactId>
640+
<version>2.0.0</version>
641+
</dependency>
642+
<dependency>
643+
<groupId>org.springframework.boot</groupId>
644+
<artifactId>spring-boot-starter-web</artifactId>
645+
</dependency>
646+
</dependencies>
647+
</project>
648+
"""
649+
),
650+
srcMainJava(
651+
//language=java
652+
java(
653+
"""
654+
import jakarta.annotation.Nonnull;
655+
656+
public class TestApplication {
657+
@Nonnull
658+
public String upperCase(@Nonnull String input) {
659+
return input.toUpperCase();
660+
}
661+
}
662+
"""
663+
)
664+
)
665+
)
666+
);
667+
}
668+
669+
@Test
670+
void projectWithSpringBoot3StarterWebShouldNotRemoveJakartaDependencyWhenUsingNullableAnnotation() {
671+
rewriteRun(
672+
spec -> spec.parser(JavaParser.fromJavaVersion().dependsOn(javaxServlet, jakartaAnnotation)),
673+
mavenProject(
674+
"Sample",
675+
//language=xml
676+
pomXml(
677+
"""
678+
<?xml version="1.0" encoding="UTF-8"?>
679+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
680+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
681+
<modelVersion>4.0.0</modelVersion>
682+
<parent>
683+
<groupId>org.springframework.boot</groupId>
684+
<artifactId>spring-boot-starter-parent</artifactId>
685+
<version>3.2.5</version>
686+
<relativePath/> <!-- lookup parent from repository -->
687+
</parent>
688+
<groupId>com.example</groupId>
689+
<artifactId>demo</artifactId>
690+
<version>0.0.1-SNAPSHOT</version>
691+
<dependencies>
692+
<dependency>
693+
<groupId>jakarta.annotation</groupId>
694+
<artifactId>jakarta.annotation-api</artifactId>
695+
<version>1.3.5</version>
696+
</dependency>
697+
<dependency>
698+
<groupId>org.springframework.boot</groupId>
699+
<artifactId>spring-boot-starter-web</artifactId>
700+
</dependency>
701+
</dependencies>
702+
</project>
703+
""",
704+
"""
705+
<?xml version="1.0" encoding="UTF-8"?>
706+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
707+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
708+
<modelVersion>4.0.0</modelVersion>
709+
<parent>
710+
<groupId>org.springframework.boot</groupId>
711+
<artifactId>spring-boot-starter-parent</artifactId>
712+
<version>3.2.5</version>
713+
<relativePath/> <!-- lookup parent from repository -->
714+
</parent>
715+
<groupId>com.example</groupId>
716+
<artifactId>demo</artifactId>
717+
<version>0.0.1-SNAPSHOT</version>
718+
<dependencies>
719+
<dependency>
720+
<groupId>jakarta.annotation</groupId>
721+
<artifactId>jakarta.annotation-api</artifactId>
722+
<version>2.0.0</version>
723+
</dependency>
724+
<dependency>
725+
<groupId>org.springframework.boot</groupId>
726+
<artifactId>spring-boot-starter-web</artifactId>
727+
</dependency>
728+
</dependencies>
729+
</project>
730+
"""
731+
),
732+
srcMainJava(
733+
//language=java
734+
java(
735+
"""
736+
import jakarta.annotation.Nullable;
737+
738+
public class TestApplication {
739+
@Nullable
740+
public String safeUpperCase(@Nullable String input) {
741+
return input == null ? null : input.toUpperCase();
742+
}
743+
}
744+
"""
745+
)
746+
)
747+
)
748+
);
749+
}
750+
577751
@Test
578752
void upgradeAnnotationApiFromV1ToV2() {
579753
rewriteRun(

0 commit comments

Comments
 (0)