diff --git a/recipes/pom.xml b/recipes/pom.xml
index e25283232b..40e22839ab 100644
--- a/recipes/pom.xml
+++ b/recipes/pom.xml
@@ -22,6 +22,15 @@
4.46.0
5.40.6
+
+ 1.16.0
+
+ 5.4.0
+ 3.1.0
+
+ 4.5.14
1.18.28
@@ -74,6 +83,12 @@
rewrite-java-17
provided
+
+ org.openrewrite.recipe
+ rewrite-migrate-java
+ ${rewrite-migrate-java.version}
+ provided
+
@@ -103,6 +118,13 @@
provided
+
+
+ org.openrewrite
+ rewrite-maven
+ provided
+
+
org.projectlombok
@@ -117,6 +139,18 @@
rewrite-test
test
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.version}
+ test
+
@@ -187,6 +221,40 @@
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 11
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 11
+
+
+
+ maven-surefire-plugin
+ ${surefire.plugin.version}
+
+
+ org.apache.maven.surefire
+ surefire-api
+ ${surefire.plugin.version}
+
+
+
+
+ ${settings.localRepository}
+ ${project.build.directory}
+
+
+
+
-
+
\ No newline at end of file
diff --git a/recipes/src/main/java/org/apache/camel/quarkus/update/AbstractCamelQuarkusJavaVisitor.java b/recipes/src/main/java/org/apache/camel/quarkus/update/AbstractCamelQuarkusJavaVisitor.java
new file mode 100644
index 0000000000..07bcbb9585
--- /dev/null
+++ b/recipes/src/main/java/org/apache/camel/quarkus/update/AbstractCamelQuarkusJavaVisitor.java
@@ -0,0 +1,155 @@
+package org.apache.camel.quarkus.update;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.MethodMatcher;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+/**
+ * Parent of Camel visitors, skips visit methods in case that there is no camel package imported.
+ *
+ * Every method visit* is marked as final and methods doVisit* are used instead.
+ *
+ *
+ * Simple cache for methodMatchers is implemented here. Usage: call MethodMatcher getMethodMatcher(String signature).
+ *
+ */
+public abstract class AbstractCamelQuarkusJavaVisitor extends JavaIsoVisitor {
+ private static final Logger LOGGER = LoggerFactory.getLogger(AbstractCamelQuarkusJavaVisitor.class);
+ //flag that camel package is imported to the file
+ private boolean camel = false;
+
+ private LinkedList implementsList = new LinkedList<>();
+
+ //There is no need to initialize all patterns at the class start.
+ //Map is a cache for created patterns
+ private static Map methodMatchers = new HashMap();
+
+ @Override
+ public final J.Import visitImport(J.Import _import, ExecutionContext context) {
+ //if there is at least one import of camel class, the camel recipe should be executed
+ if(_import.getTypeName().contains("org.apache.camel")) {
+ camel = true;
+ }
+
+ if(!camel) {
+ //skip recipe if file does not contain camel
+ return _import;
+ }
+
+ return executeVisitWithCatch(() -> doVisitImport(_import, context), _import, context);
+ }
+
+ @Override
+ public final J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext context) {
+ if (classDecl.getImplements() != null && !classDecl.getImplements().isEmpty()) {
+ implementsList.addAll(classDecl.getImplements().stream().map(i -> i.getType()).collect(Collectors.toList()));
+ }
+ return executeVisitWithCatch(() -> doVisitClassDeclaration(classDecl, context), classDecl, context);
+ }
+
+ @Override
+ public final J.FieldAccess visitFieldAccess(J.FieldAccess fieldAccess, ExecutionContext context) {
+ if(!camel) {
+ //skip recipe if file does not contain camel
+ return fieldAccess;
+ }
+
+ return executeVisitWithCatch(() -> doVisitFieldAccess(fieldAccess, context), fieldAccess, context);
+ }
+
+ @Override
+ public final J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext context) {
+ if(!camel) {
+ //skip recipe if file does not contain camel
+ return method;
+ }
+
+ return executeVisitWithCatch(() -> doVisitMethodDeclaration(method, context), method, context);
+ }
+
+ @Override
+ public final J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext context) {
+ if(!camel) {
+ //skip recipe if file does not contain camel
+ return method;
+ }
+
+ return executeVisitWithCatch(() -> doVisitMethodInvocation(method, context), method, context);
+ }
+
+ @Override
+ public final J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext context) {
+ if(!camel) {
+ //skip recipe if file does not contain camel
+ return annotation;
+ }
+
+ return executeVisitWithCatch(() -> doVisitAnnotation(annotation, context), annotation, context);
+ }
+
+
+ //-------------------------------- internal methods used by children---------------------------------
+
+ protected J.Import doVisitImport(J.Import _import, ExecutionContext context) {
+ return super.visitImport(_import, context);
+ }
+
+ protected J.ClassDeclaration doVisitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext context) {
+ return super.visitClassDeclaration(classDecl, context);
+ }
+
+ protected J.FieldAccess doVisitFieldAccess(J.FieldAccess fieldAccess, ExecutionContext context) {
+ return super.visitFieldAccess(fieldAccess, context);
+ }
+
+ protected J.MethodDeclaration doVisitMethodDeclaration(J.MethodDeclaration method, ExecutionContext context) {
+ return super.visitMethodDeclaration(method, context);
+ }
+
+ protected J.MethodInvocation doVisitMethodInvocation(J.MethodInvocation method, ExecutionContext context) {
+ return super.visitMethodInvocation(method, context);
+ }
+
+ protected J.Annotation doVisitAnnotation(J.Annotation annotation, ExecutionContext context) {
+ return super.visitAnnotation(annotation, context);
+ }
+
+ // ------------------------------------------ helper methods -------------------------------------------
+
+ protected LinkedList getImplementsList() {
+ return implementsList;
+ }
+
+ // If the migration fails - do not fail whole migration process, only this one recipe
+ protected T executeVisitWithCatch(Supplier visitMethod, T origValue, ExecutionContext context) {
+ try {
+ return visitMethod.get();
+ } catch (Exception e) {
+ LOGGER.warn(String.format("Internal error detected in %s, recipe is skipped.",context.getMessage("org.openrewrite.currentRecipe").getClass().getSimpleName()), e);
+ return origValue;
+ }
+ }
+
+ protected MethodMatcher getMethodMatcher(String signature) {
+ synchronized (methodMatchers) {
+ MethodMatcher matcher = methodMatchers.get(signature);
+
+ if (matcher == null) {
+ matcher = new MethodMatcher(signature);
+ methodMatchers.put(signature, matcher);
+ }
+
+ return matcher;
+ }
+ }
+}
diff --git a/recipes/src/main/java/org/apache/camel/quarkus/update/RecipesUtil.java b/recipes/src/main/java/org/apache/camel/quarkus/update/RecipesUtil.java
new file mode 100644
index 0000000000..7bdd9d4a9b
--- /dev/null
+++ b/recipes/src/main/java/org/apache/camel/quarkus/update/RecipesUtil.java
@@ -0,0 +1,245 @@
+package org.apache.camel.quarkus.update;
+
+import org.openrewrite.Cursor;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Tree;
+import org.openrewrite.java.tree.Comment;
+import org.openrewrite.java.tree.Expression;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JContainer;
+import org.openrewrite.java.tree.JRightPadded;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.Space;
+import org.openrewrite.java.tree.TextComment;
+import org.openrewrite.marker.Markers;
+import org.openrewrite.xml.tree.Xml;
+import org.openrewrite.yaml.tree.Yaml;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.function.Function;
+
+import static org.openrewrite.Tree.randomId;
+
+public class RecipesUtil {
+
+ private static String CAMEL_PRESENT_KEY = RecipesUtil.class.getSimpleName();
+
+ //---------------- annotations helpers
+
+ public static J.Annotation createAnnotation(J.Annotation annotation, String name, Function argMatcher, String args) {
+
+ LinkedList originalArguments = annotation.getArguments() == null ? new LinkedList<>() : new LinkedList(annotation.getArguments());
+
+ String newArgName = args.replaceAll("=.*", "").trim();
+
+ //remove argument with the same name as the new one
+ if(argMatcher == null) {
+ originalArguments.add(new J.Empty(randomId(), Space.format(args), Markers.EMPTY));
+ } else {
+ for (ListIterator iter = originalArguments.listIterator(); iter.hasNext(); ) {
+ Expression expr = iter.next();
+ if (argMatcher.apply(expr.toString().replaceAll("\\s", ""))) {
+ iter.set(new J.Empty(randomId(), Space.format(args), Markers.EMPTY));
+ }
+ }
+ }
+
+ //construct arguments for the new annotation
+ List> newArgs = new LinkedList<>();
+ for(Expression e: originalArguments) {
+ newArgs.add(new JRightPadded(e, Space.EMPTY, Markers.EMPTY));
+ }
+
+ J.Identifier newAnnotationIdentifier = new J.Identifier(randomId(), annotation.getPrefix(), Markers.EMPTY, name,
+ JavaType.ShallowClass.build("java.lang.Object"), null);
+ JContainer arguments = JContainer.build(
+ Space.EMPTY,
+ newArgs,
+ Markers.EMPTY);
+ return new J.Annotation(UUID.randomUUID(), annotation.getPrefix(), Markers.EMPTY,
+ newAnnotationIdentifier, arguments);
+ }
+
+ public static Optional getValueOfArgs(List expressions, String parameter) {
+ if(expressions == null || expressions.isEmpty()) {
+ return Optional.empty();
+ }
+ return expressions.stream()
+ .filter(e -> e.toString().replaceAll("\\s", "").startsWith(parameter + "="))
+ .map(e -> e.toString().replaceAll("\\s", "").replaceFirst(parameter + "=", ""))
+ .findFirst();
+ }
+
+ //-------------- methods helping with comments ----
+
+ public static Comment createMultinlineComment(String text) {
+ return new TextComment(true, text, null, Markers.EMPTY);
+ }
+ public static Comment createComment(String text) {
+ return new TextComment(false, text, null, Markers.EMPTY);
+ }
+ public static Xml.Comment createXmlComment(String text) {
+ return new Xml.Comment(UUID.randomUUID(), null, Markers.EMPTY, text);
+ }
+
+ //--------------- typeCast helper --------------------------------
+
+ public static J createTypeCast(Object type, Expression arg) {
+ return new J.TypeCast(
+ Tree.randomId(),
+ Space.EMPTY,
+ Markers.EMPTY,
+ RecipesUtil.createParentheses(type),
+ arg);
+ }
+
+ // -------------------- other helper methods
+
+ public static J.ControlParentheses createParentheses(T t) {
+ return new J.ControlParentheses(
+ Tree.randomId(),
+ Space.EMPTY,
+ Markers.EMPTY,
+ padRight(t));
+ }
+
+ public static J.Identifier createIdentifier(Space prefix, String name, String type) {
+ return new J.Identifier(randomId(), prefix, Markers.EMPTY, name,
+ JavaType.ShallowClass.build(type), null);
+ }
+
+ private static JRightPadded padRight(T tree) {
+ return new JRightPadded<>(tree, Space.EMPTY, Markers.EMPTY);
+ }
+
+ public static String getProperty(Cursor cursor) {
+ StringBuilder asProperty = new StringBuilder();
+ Iterator