Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[releases/6.3.z] WINDUPRULE-1041 jakarta cdi to quarkus rules (#1040) #1055

Merged
merged 1 commit into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rules/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@
<include>eap6/api-jars/*.jar</include>
<include>eap7/api-jars/*.jar</include>
<include>openjdk11/api-jars/*.jar</include>
<include>quarkus/api-jars/*.jar</include>
<include>**/*.xsl</include>
<include>**/rewrite.yml</include>
</includes>
Expand Down
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ ruleSet("cdi-to-quarkus-groovy")

void perform(GraphRewrite event, EvaluationContext context, JavaAnnotationTypeReferenceModel payload) {
final String annotatedClass = payload.getAnnotatedType().getResolvedSourceSnippit()
System.out.println("ANNOTATED TYPE: " + annotatedClass)
final boolean injectedClassHasScopeAnnotations =
JavaClass.references(annotatedClass)
.at(TypeReferenceLocation.TYPE)
Expand All @@ -87,8 +88,8 @@ ruleSet("cdi-to-quarkus-groovy")
if (!injectedClassHasScopeAnnotations && !injectedClassHasSingletonAnnotations) {
// first of all select only the file belonging to the same root project as the payload
// to reduce (i.e. optimize) the number of files found from the second query
if (Query.fromType(FileModel.class).withProperty(FileModel.FILE_PATH, QueryPropertyComparisonType.CONTAINS_TOKEN, payload.getFile().getProjectModel().getRootFileModel().getPrettyPath() + "/").as(FROM_FILES_IN_PROJECT).evaluate(event, context)
&& JavaClass.from(FROM_FILES_IN_PROJECT).references(annotatedClass).at(TypeReferenceLocation.TYPE).as(INJECT_CLASS_DECLARATION).evaluate(event, context)) {
Query.fromType(FileModel.class).withProperty(FileModel.FILE_PATH, QueryPropertyComparisonType.CONTAINS_TOKEN, payload.getFile().getProjectModel().getRootFileModel().getPrettyPath() + "/").as(FROM_FILES_IN_PROJECT).evaluate(event, context)
JavaClass.from(FROM_FILES_IN_PROJECT).references(annotatedClass).at(TypeReferenceLocation.TYPE).as(INJECT_CLASS_DECLARATION).evaluate(event, context)
Iteration.over(INJECT_CLASS_DECLARATION)
.perform(
((Hint) Hint.titled("Injected class is missing scope annotation")
Expand All @@ -99,18 +100,17 @@ ruleSet("cdi-to-quarkus-groovy")
.withIssueCategory(potentialIssueCategory)
.with(guideLink)
.with(cdiSpecLink)
.withEffort(1)
)
.withEffort(1))
)
.endIteration()
}

}
}
}
)
.endIteration()
)
.withId("cdi-to-quarkus-groovy-00010")
.withId("cdi-to-quarkus-groovy-00010")
// suggest to replace cdi-api TRANSITIVE dependency if no Quarkus dependency has been already added and 'javax.enterprise.{packages}.{*}' package is used somewhere in the code
.addRule()
.when(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package quarkus.javaee

import org.jboss.windup.ast.java.data.TypeReferenceLocation
import org.jboss.windup.config.GraphRewrite
import org.jboss.windup.config.Variables
import org.jboss.windup.config.metadata.TechnologyReference
import org.jboss.windup.config.operation.Iteration
import org.jboss.windup.config.operation.iteration.AbstractIterationOperation
import org.jboss.windup.config.query.Query
import org.jboss.windup.config.query.QueryPropertyComparisonType
import org.jboss.windup.graph.model.FileLocationModel
import org.jboss.windup.graph.model.FileReferenceModel
import org.jboss.windup.graph.model.ProjectModel
import org.jboss.windup.graph.model.WindupVertexFrame
import org.jboss.windup.graph.model.resource.FileModel
import org.jboss.windup.reporting.category.IssueCategory
import org.jboss.windup.reporting.category.IssueCategoryRegistry
import org.jboss.windup.reporting.config.Hint
import org.jboss.windup.reporting.config.Link
import org.jboss.windup.rules.apps.java.condition.JavaClass
import org.jboss.windup.rules.apps.java.condition.annotation.AnnotationTypeCondition
import org.jboss.windup.rules.apps.java.scan.ast.annotations.JavaAnnotationTypeReferenceModel
import org.jboss.windup.rules.apps.xml.condition.XmlFile
import org.ocpsoft.rewrite.config.And
import org.ocpsoft.rewrite.config.Or
import org.ocpsoft.rewrite.context.EvaluationContext

import java.util.stream.Collectors
import java.util.stream.StreamSupport

final IssueCategory potentialIssueCategory = new IssueCategoryRegistry().getByID(IssueCategoryRegistry.POTENTIAL)
final Link guideLink = Link.to("Quarkus - Guides", "https://quarkus.io/guides/cdi-reference")
final Link cdiSpecLink = Link.to("CDI 2.0 - Scopes: Default scope", "https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#default_scope")

static boolean matchesProject(GraphRewrite event, FileLocationModel payload) {
final Iterable<? extends WindupVertexFrame> previouslyFound = Optional.ofNullable(Variables.instance(event).findVariable("discard")).orElse(Collections.emptySet())
final Set<ProjectModel> projectModels = StreamSupport.stream(previouslyFound.spliterator(), false)
.map {
if (it instanceof FileReferenceModel) return ((FileReferenceModel) it).getFile().getProjectModel()
else if (it instanceof FileModel) return ((FileModel) it).getProjectModel()
else return null
}
.collect (Collectors.toSet())
final boolean matchesProject = projectModels.isEmpty() || projectModels.stream().anyMatch{payload.getFile().belongsToProject(it)}
return matchesProject
}

ruleSet("jakarta-cdi-to-quarkus-groovy")
.addSourceTechnology(new TechnologyReference("java-ee", null))
.addTargetTechnology(new TechnologyReference("quarkus", null))
// this rule si required for Windup to know about storing data related to the classes involved in the
// `when` condition because useful later on in the `perform` step of the next rule
.addRule()
.when(
Or.any(
JavaClass.references("jakarta.enterprise.context.{scope}").at(TypeReferenceLocation.ANNOTATION).as("placeholder1"),
JavaClass.references("jakarta.inject.Singleton").at(TypeReferenceLocation.ANNOTATION).as("placeholder2"),
)
)
.where("scope").matches("(ApplicationScoped|ConversationScoped|Dependent|RequestScoped|SessionScoped)")
.withId("jakarta-cdi-to-quarkus-groovy-00000")
.addRule()
.when(
JavaClass.references("jakarta.inject.Inject").at(TypeReferenceLocation.ANNOTATION).as("main")
)
.perform(
Iteration.over("main")
.perform(
new AbstractIterationOperation<JavaAnnotationTypeReferenceModel>() {
public static final String FROM_FILES_IN_PROJECT = "filesInProject"
public static final String INJECT_CLASS_DECLARATION = "injectClassDeclaration"

void perform(GraphRewrite event, EvaluationContext context, JavaAnnotationTypeReferenceModel payload) {
final String annotatedClass = payload.getAnnotatedType().getResolvedSourceSnippit()
final boolean injectedClassHasScopeAnnotations =
JavaClass.references(annotatedClass)
.at(TypeReferenceLocation.TYPE)
.annotationMatches(new AnnotationTypeCondition("jakarta.enterprise.context.(ApplicationScoped|ConversationScoped|Dependent|RequestScoped|SessionScoped)"))
.as("discard")
.evaluate(event, context)
final boolean injectedClassHasSingletonAnnotations =
JavaClass.references(annotatedClass)
.at(TypeReferenceLocation.TYPE)
.annotationMatches(new AnnotationTypeCondition("jakarta.inject.Singleton"))
.as("discardAsWell")
.evaluate(event, context)
if (!injectedClassHasScopeAnnotations && !injectedClassHasSingletonAnnotations) {
// first of all select only the file belonging to the same root project as the payload
// to reduce (i.e. optimize) the number of files found from the second query
final FileModel fileModel = payload.getFile()
final String filePath = fileModel.getProjectModel().getRootFileModel().getPrettyPath() + "/"
Query.fromType(FileModel.class).withProperty(FileModel.FILE_PATH, QueryPropertyComparisonType.CONTAINS_TOKEN, filePath).as(FROM_FILES_IN_PROJECT).evaluate(event, context)
//Query.fromType(FileModel.class).withProperty(JavaClass.from
JavaClass.from(FROM_FILES_IN_PROJECT).references(annotatedClass).at(TypeReferenceLocation.TYPE).as(INJECT_CLASS_DECLARATION).evaluate(event, context)
Iteration.over(INJECT_CLASS_DECLARATION)
.perform(
((Hint) Hint.titled("Injected class is missing scope annotation")
.withText("""
A class injected but missing an annotation to define its scope type is not going to be discovered from Quarkus.
Consider adding the `@Dependent` scope which is the default scope for a bean which does not explicitly declare a scope type (ref. [CDI 2.0 - Scopes: Default scope](https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#default_scope))
""")
.withIssueCategory(potentialIssueCategory)
.with(guideLink)
.with(cdiSpecLink)
.withEffort(1))
)
.endIteration()
}
}
}
)
.endIteration()
)
.withId("jakarta-cdi-to-quarkus-groovy-00010")
// suggest to replace cdi-api TRANSITIVE dependency if no Quarkus dependency has been already added and 'javax.enterprise.{packages}.{*}' package is used somewhere in the code
.addRule()
.when(
And.all(
JavaClass.references("jakarta.enterprise.{packages}.{*}").at(TypeReferenceLocation.ANNOTATION).as("discard"),
XmlFile.matchesXpath("/m:project/m:dependencies[count(m:dependency/m:artifactId[contains(., 'cdi-api')]) = 0 and count(m:dependency/m:artifactId[contains(., 'quarkus-')]) = 0]")
.inFile("pom.xml")
.namespace("m", "http://maven.apache.org/POM/4.0.0")
.as("dependencies-section")
)
)
.perform(
Iteration.over("dependencies-section").perform(
new AbstractIterationOperation<FileLocationModel>() {
void perform(GraphRewrite event, EvaluationContext context, FileLocationModel payload) {
if (matchesProject(event, payload)) {
((Hint) Hint.titled("Remove jakarta.enterprise:cdi-api transitive dependency")
.withText("""
Transitive dependency `jakarta.enterprise:cdi-api` should be removed and the `io.quarkus:quarkus-arc` dependency added.
""")
.withIssueCategory(potentialIssueCategory)
.with(guideLink)
.withEffort(1)
).performParameterized(event, context, payload)
}
}
}
)
.endIteration()
)
.where("packages").matches("(context|event|inject|util)")
.withId("jakarta-cdi-to-quarkus-groovy-00020")
// suggest to replace javax.inject TRANSITIVE dependency if no Quarkus dependency has been already added and 'javax.inject' package is used somewhere in the code
.addRule()
.when(
And.all(
JavaClass.references("jakarta.inject.{*}").at(TypeReferenceLocation.ANNOTATION).as("discard"),
XmlFile.matchesXpath("/m:project/m:dependencies[count(m:dependency/m:artifactId[contains(., 'jakarta.inject')]) = 0 and count(m:dependency/m:artifactId[contains(., 'quarkus-')]) = 0]")
.inFile("pom.xml")
.namespace("m", "http://maven.apache.org/POM/4.0.0")
.as("dependencies-section")
)
)
.perform(
Iteration.over("dependencies-section").perform(
new AbstractIterationOperation<FileLocationModel>() {
void perform(GraphRewrite event, EvaluationContext context, FileLocationModel payload) {
if (matchesProject(event, payload)) {
((Hint) Hint.titled("Remove jakarta.inject:jakarta.inject transitive dependency")
.withText("""
The application has a transitive `javax.inject:javax.inject` dependency because at least one Java class that imports from the `javax.inject` has been found.
The direct dependency injecting `javax.inject:javax.inject` should be identified and replaced with the `io.quarkus:quarkus-arc` dependency.
""")
.withIssueCategory(potentialIssueCategory)
.with(guideLink)
.withEffort(1)
).performParameterized(event, context, payload)
}
}
}
)
.endIteration()
)
.withId("jakarta-cdi-to-quarkus-groovy-00030")
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?xml version="1.0"?>
<ruleset xmlns="http://windup.jboss.org/schema/jboss-ruleset" id="jakarta-cdi-to-quarkus"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://windup.jboss.org/schema/jboss-ruleset http://windup.jboss.org/schema/jboss-ruleset/windup-jboss-ruleset.xsd">
<metadata>
<description>
This ruleset gives hints to migrate CDI usage to Quarkus
</description>
<dependencies>
<addon id="org.jboss.windup.rules,windup-rules-javaee,3.0.0.Final" />
<addon id="org.jboss.windup.rules,windup-rules-java,3.0.0.Final" />
<addon id="org.jboss.windup.rules,windup-rules-xml,3.0.0.Final" />
</dependencies>
<sourceTechnology id="java-ee" />
<targetTechnology id="quarkus" />
</metadata>
<rules>
<rule id="jakarta-cdi-to-quarkus-00000">
<when>
<xmlfile in="pom.xml" matches="/m:project/m:dependencies/m:dependency[m:artifactId/text() = 'jakarta.enterprise.cdi-api' and m:groupId/text() = 'jakarta.enterprise' and (count(../m:dependency/m:groupId[contains(., 'io.quarkus')]) = 0)]" >
<namespace prefix="m" uri="http://maven.apache.org/POM/4.0.0" />
</xmlfile>
</when>
<perform>
<hint title="Replace jakarta.enterprise:jakarta.enterprise.cdi-api dependency" effort="1" category-id="mandatory">
<message>
Dependency `jakarta.enterprise:jakarta.enterprise.cdi-api` has to be replaced with `io.quarkus:quarkus-arc` artifact.
</message>
<link title="Quarkus - Guide" href="https://quarkus.io/guides/cdi-reference" />
</hint>
</perform>
</rule>
<!-- suggest to replace jakarta.inject dependency if no Quarkus dependency has been already added -->
<rule id="jakarta-cdi-to-quarkus-00020">
<when>
<xmlfile in="pom.xml" matches="/m:project/m:dependencies/m:dependency[m:artifactId/text() = 'jakarta.inject-api' and m:groupId/text() = 'jakarta.inject' and (count(../m:dependency/m:groupId[contains(., 'io.quarkus')]) = 0)]" >
<namespace prefix="m" uri="http://maven.apache.org/POM/4.0.0" />
</xmlfile>
</when>
<perform>
<hint title="Replace jakarta.inject:jakarta.inject-api dependency" effort="1" category-id="mandatory">
<message>
Dependency `jakarta.inject:jakarta.inject-api` has to be replaced with `io.quarkus:quarkus-arc` artifact.
</message>
<link title="Quarkus - Guide" href="https://quarkus.io/guides/cdi-reference" />
</hint>
</perform>
</rule>
<!-- explain beans.xml descriptor content is ignored -->
<rule id="jakarta-cdi-to-quarkus-00030">
<when>
<xmlfile in="beans.xml" matches="/b:beans" as="root">
<namespace prefix="b" uri="https://jakarta.ee/xml/ns/jakartaee" />
</xmlfile>
</when>
<perform>
<iteration over="root">
<hint title="`beans.xml` descriptor content is ignored" effort="3" category-id="potential">
<message>
The `beans.xml` descriptor content is ignored and it could be removed from the application.
Refer to the guide referenced below to check the supported CDI feature in Quarkus.
</message>
<link title="Quarkus - Guide" href="https://quarkus.io/guides/cdi-reference#limitations" />
</hint>
</iteration>
</perform>
</rule>
<rule id="jakarta-cdi-to-quarkus-00040">
<when>
<javaclass references="jakarta.enterprise.inject.Produces">
<location>ANNOTATION</location>
</javaclass>
</when>
<perform>
<hint title="Producer annotation no longer required" effort="1" category-id="potential">
<message>In Quarkus you can skip the @Produces annotation completely if the producer method is annotated with a scope annotation, a stereotype or a qualifier..
This field could be accessed using a `@Named` getter method instead.
</message>
<link title="Quarkus Simplified Producer Method Declaration" href="https://quarkus.io/guides/cdi-reference#simplified-producer-method-declaration"/>
</hint>
</perform>
</rule>
<rule id="jakarta-cdi-to-quarkus-00050">
<when>
<javaclass references="jakarta.ejb.Stateless">
<location>ANNOTATION</location>
</javaclass>
</when>
<perform>
<hint title="Stateless annotation can be replaced with scope" effort="1" category-id="potential">
<message>The Stateless EJBs can be converted to a cdi bean by replacing the `@Stateless` annotation with a scope eg `@ApplicationScoped`
</message>
<link title="Quarkus CDI reference" href="https://quarkus.io/guides/cdi-reference"/>
</hint>
</perform>
</rule>
</rules>
</ruleset>
Loading