Skip to content

Commit 6ed76d1

Browse files
evie-lautimtebeek
andauthored
Add recipe to replace ManagedBean with Named annotation (#753)
* Add recipe and test for replacing ManagedBean with Named annotation * Update description with EE levels * Copyright and formatting * Apply suggestions from code review Review suggestions Co-authored-by: Tim te Beek <tim@moderne.io> * Add jakarta inject jar, use precondition * Simplify getting annotation name value * Use a single annotation matcher with a wildcard * Update faces recipe lists * Add tags to recipe * Cleanup --------- Co-authored-by: Tim te Beek <tim@moderne.io>
1 parent fe32354 commit 6ed76d1

File tree

5 files changed

+180
-59
lines changed

5 files changed

+180
-59
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.jakarta;
17+
18+
import lombok.EqualsAndHashCode;
19+
import lombok.Value;
20+
import org.openrewrite.ExecutionContext;
21+
import org.openrewrite.Preconditions;
22+
import org.openrewrite.Recipe;
23+
import org.openrewrite.TreeVisitor;
24+
import org.openrewrite.java.AnnotationMatcher;
25+
import org.openrewrite.java.JavaIsoVisitor;
26+
import org.openrewrite.java.JavaParser;
27+
import org.openrewrite.java.JavaTemplate;
28+
import org.openrewrite.java.search.UsesType;
29+
import org.openrewrite.java.trait.Annotated;
30+
import org.openrewrite.java.tree.J;
31+
32+
import java.util.Arrays;
33+
import java.util.HashSet;
34+
import java.util.Optional;
35+
import java.util.Set;
36+
37+
import static org.openrewrite.java.trait.Traits.annotated;
38+
39+
@Value
40+
@EqualsAndHashCode(callSuper = false)
41+
public class UpdateManagedBeanToNamed extends Recipe {
42+
43+
@Override
44+
public String getDisplayName() {
45+
return "Update Faces `@ManagedBean` to use CDI `@Named`";
46+
}
47+
48+
@Override
49+
public String getDescription() {
50+
return "Faces ManagedBean was deprecated in JSF 2.3 (EE8) and removed in Jakarta Faces 4.0 (EE10). " +
51+
"Replace `@ManagedBean` with `@Named` for CDI-based bean management.";
52+
}
53+
54+
@Override
55+
public Set<String> getTags() {
56+
return new HashSet<>(Arrays.asList("jakarta", "faces", "jsf"));
57+
}
58+
59+
private static final AnnotationMatcher MANAGED_BEAN_MATCHER = new AnnotationMatcher("*.faces.bean.ManagedBean");
60+
61+
@Override
62+
public TreeVisitor<?, ExecutionContext> getVisitor() {
63+
return new Preconditions.Check(new UsesType<>("*.faces.bean.ManagedBean", false),
64+
new JavaIsoVisitor<ExecutionContext>() {
65+
@Override
66+
public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ctx) {
67+
Optional<Annotated> annotated = annotated(MANAGED_BEAN_MATCHER).get(getCursor());
68+
if (annotated.isPresent()) {
69+
maybeAddImport("jakarta.inject.Named");
70+
maybeRemoveImport("javax.faces.bean.ManagedBean");
71+
maybeRemoveImport("jakarta.faces.bean.ManagedBean");
72+
// Get the name from the @ManagedBean annotation
73+
String beanName = annotated
74+
.flatMap(a -> a.getDefaultAttribute("name"))
75+
.map(name -> name.getValue(String.class))
76+
.orElse(null);
77+
// Replace the @ManagedBean annotation with @Named
78+
if (beanName != null) {
79+
return JavaTemplate.builder("@Named(\"#{}\")")
80+
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "jakarta.inject-api-2.0.1"))
81+
.imports("jakarta.inject.Named")
82+
.build()
83+
.apply(getCursor(), annotation.getCoordinates().replace(), beanName);
84+
}
85+
return JavaTemplate.builder("@Named")
86+
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "jakarta.inject-api-2.0.1"))
87+
.imports("jakarta.inject.Named")
88+
.build()
89+
.apply(getCursor(), annotation.getCoordinates().replace());
90+
}
91+
return annotation;
92+
}
93+
});
94+
}
95+
}

src/main/resources/META-INF/rewrite/jakarta-faces-3.yml

Lines changed: 1 addition & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ recipeList:
3737
- org.openrewrite.java.migrate.jakarta.RemovedJakartaFacesResourceResolver
3838
- org.openrewrite.java.migrate.jakarta.RemovedStateManagerMethods
3939
- org.openrewrite.java.migrate.jakarta.RemovedUIComponentConstant
40-
- org.openrewrite.java.migrate.jakarta.FacesManagedBeansDeprecated
4140
- org.openrewrite.java.migrate.jakarta.UpgradeFaces3OpenSourceLibraries
4241
---
4342
type: specs.openrewrite.org/v1beta/recipe
@@ -367,63 +366,8 @@ recipeList:
367366
newFullyQualifiedTypeName: jakarta.el.ELException
368367
ignoreDefinition: true
369368
---
370-
# TODO: currently same as FacesManagedBeansRemoved in Faces4 migration. Should some items be split up?
371-
type: specs.openrewrite.org/v1beta/recipe
372-
name: org.openrewrite.java.migrate.jakarta.FacesManagedBeansDeprecated
373-
displayName: Substitute deprecated Faces Managed Beans
374-
description: >-
375-
This recipe substitutes Faces Managed Beans, which were deprecated in JavaServer Faces 3.0.
376-
recipeList:
377-
- org.openrewrite.java.ChangeType:
378-
oldFullyQualifiedTypeName: javax.faces.bean.ApplicationScoped
379-
newFullyQualifiedTypeName: jakarta.enterprise.context.ApplicationScoped
380-
ignoreDefinition: true
381-
- org.openrewrite.java.ChangeType:
382-
oldFullyQualifiedTypeName: jakarta.faces.bean.ApplicationScoped
383-
newFullyQualifiedTypeName: jakarta.enterprise.context.ApplicationScoped
384-
ignoreDefinition: true
385-
- org.openrewrite.java.ChangeType:
386-
oldFullyQualifiedTypeName: javax.faces.bean.ManagedProperty
387-
newFullyQualifiedTypeName: jakarta.faces.annotation.ManagedProperty
388-
ignoreDefinition: true
389-
- org.openrewrite.java.ChangeType:
390-
oldFullyQualifiedTypeName: jakarta.faces.bean.ManagedProperty
391-
newFullyQualifiedTypeName: jakarta.faces.annotation.ManagedProperty
392-
ignoreDefinition: true
393-
- org.openrewrite.java.ChangeType:
394-
oldFullyQualifiedTypeName: javax.faces.bean.NoneScoped
395-
newFullyQualifiedTypeName: jakarta.enterprise.context.Dependent
396-
ignoreDefinition: true
397-
- org.openrewrite.java.ChangeType:
398-
oldFullyQualifiedTypeName: jakarta.faces.bean.NoneScoped
399-
newFullyQualifiedTypeName: jakarta.enterprise.context.Dependent
400-
ignoreDefinition: true
401-
- org.openrewrite.java.ChangeType:
402-
oldFullyQualifiedTypeName: javax.faces.bean.RequestScoped
403-
newFullyQualifiedTypeName: jakarta.enterprise.context.RequestScoped
404-
ignoreDefinition: true
405-
- org.openrewrite.java.ChangeType:
406-
oldFullyQualifiedTypeName: jakarta.faces.bean.RequestScoped
407-
newFullyQualifiedTypeName: jakarta.enterprise.context.RequestScoped
408-
ignoreDefinition: true
409-
- org.openrewrite.java.ChangeType:
410-
oldFullyQualifiedTypeName: javax.faces.bean.SessionScoped
411-
newFullyQualifiedTypeName: jakarta.enterprise.context.SessionScoped
412-
ignoreDefinition: true
413-
- org.openrewrite.java.ChangeType:
414-
oldFullyQualifiedTypeName: jakarta.faces.bean.SessionScoped
415-
newFullyQualifiedTypeName: jakarta.enterprise.context.SessionScoped
416-
ignoreDefinition: true
417-
- org.openrewrite.java.ChangeType:
418-
oldFullyQualifiedTypeName: javax.faces.bean.ViewScoped
419-
newFullyQualifiedTypeName: jakarta.faces.view.ViewScoped
420-
ignoreDefinition: true
421-
- org.openrewrite.java.ChangeType:
422-
oldFullyQualifiedTypeName: jakarta.faces.bean.ViewScoped
423-
newFullyQualifiedTypeName: jakarta.faces.view.ViewScoped
424-
ignoreDefinition: true
425-
---
426369
# TODO: should this upgrade to earliest or latest compatible version for EE9?
370+
# TODO: maybe should pick Java8 compatible ones since EE9 recipe doesn't seem to migrate to Java11
427371
type: specs.openrewrite.org/v1beta/recipe
428372
name: org.openrewrite.java.migrate.jakarta.UpgradeFaces3OpenSourceLibraries
429373
displayName: Upgrade Faces open source libraries

src/main/resources/META-INF/rewrite/jakarta-faces-4.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,12 +199,11 @@ recipeList:
199199
elementName: web-app
200200
newValue: https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd
201201
---
202-
# TODO: currently same as FacesManagedBeansDeprecated in Faces3 migration. Can this list be pared down?
203202
type: specs.openrewrite.org/v1beta/recipe
204203
name: org.openrewrite.java.migrate.jakarta.FacesManagedBeansRemoved
205204
displayName: Substitute removed Faces Managed Beans
206205
description: >-
207-
This recipe substitutes Faces Managed Beans, which were deprecated in JavaServer Faces 3.0 and have been removed from Jakarta Faces 4.0.
206+
This recipe substitutes Faces Managed Beans, which were deprecated in JavaServer Faces 2.3 and have been removed from Jakarta Faces 4.0.
208207
recipeList:
209208
- org.openrewrite.java.ChangeType:
210209
oldFullyQualifiedTypeName: javax.faces.bean.ApplicationScoped
@@ -254,6 +253,7 @@ recipeList:
254253
oldFullyQualifiedTypeName: jakarta.faces.bean.ViewScoped
255254
newFullyQualifiedTypeName: jakarta.faces.view.ViewScoped
256255
ignoreDefinition: true
256+
- org.openrewrite.java.migrate.jakarta.UpdateManagedBeanToNamed
257257
---
258258
type: specs.openrewrite.org/v1beta/recipe
259259
name: org.openrewrite.java.migrate.jakarta.UpgradeFaces4OpenSourceLibraries
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.jakarta;
17+
18+
import org.junit.jupiter.params.ParameterizedTest;
19+
import org.junit.jupiter.params.provider.ValueSource;
20+
import org.openrewrite.InMemoryExecutionContext;
21+
import org.openrewrite.java.JavaParser;
22+
import org.openrewrite.test.RecipeSpec;
23+
import org.openrewrite.test.RewriteTest;
24+
25+
import static org.openrewrite.java.Assertions.java;
26+
27+
class UpdateManagedBeanToNamedTest implements RewriteTest {
28+
@Override
29+
public void defaults(RecipeSpec spec) {
30+
spec.parser(JavaParser.fromJavaVersion()
31+
.classpathFromResources(new InMemoryExecutionContext(), "jsf-api-2.1.29-11", "jakarta.faces-api-3.0.0"))
32+
.recipe(new UpdateManagedBeanToNamed());
33+
}
34+
35+
@ParameterizedTest
36+
@ValueSource(strings = {"javax", "jakarta"})
37+
void updateManagedBeanToNamed(String pkg) {
38+
rewriteRun(
39+
//language=java
40+
java(
41+
"""
42+
import %s.faces.bean.ManagedBean;
43+
44+
@ManagedBean
45+
public class ApplicationBean2 {
46+
}
47+
""".formatted(pkg),
48+
"""
49+
import jakarta.inject.Named;
50+
51+
@Named
52+
public class ApplicationBean2 {
53+
}
54+
"""
55+
)
56+
);
57+
}
58+
59+
@ParameterizedTest
60+
@ValueSource(strings = {"javax", "jakarta"})
61+
void updateManagedBeanToNamedWithArg(String pkg) {
62+
rewriteRun(
63+
//language=java
64+
java(
65+
"""
66+
import %s.faces.bean.ManagedBean;
67+
68+
@ManagedBean(name="myBean")
69+
public class ApplicationBean2 {
70+
}
71+
""".formatted(pkg),
72+
"""
73+
import jakarta.inject.Named;
74+
75+
@Named("myBean")
76+
public class ApplicationBean2 {
77+
}
78+
"""
79+
)
80+
);
81+
}
82+
}
10.4 KB
Binary file not shown.

0 commit comments

Comments
 (0)