This is an automated email from the ASF dual-hosted git repository.

Croway pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-upgrade-recipes.git


The following commit(s) were added to refs/heads/main by this push:
     new e23bbde  Gate the remaining XML DSL recipes on a Camel document (#127)
e23bbde is described below

commit e23bbded8ca17e2480ef9daa6d26f9cc9a1cf944
Author: Federico Mariani <[email protected]>
AuthorDate: Tue Aug 25 15:11:33 2026 +0200

    Gate the remaining XML DSL recipes on a Camel document (#127)
    
    * Gate the remaining XML DSL recipes on a Camel document
    
    Fixes #126.
    
    The Java recipes are gated by UsesType and the YAML recipes by
    camelYamlDslPrecondition, and #125 added camelXmlDslPrecondition and
    applied it to two XML recipes. The other ten visited every XML document
    in a project, so a matcher that happens to fit an unrelated file rewrote
    it. XmlDsl418_1SagaRecipe did not merely rename in such a file, it
    removed a child element and folded it into an attribute.
    
    Wraps every remaining XML visitor in the precondition, so the whole set
    is now uniform and a new XML recipe inherits the same guard.
    
    CameXmlDslRecipeTest.circuitBreakerFull used a synthetic <differentContext>
    root, which is not a Camel document. Its sibling circuitBreaker test
    already covers the <route> context, so the root becomes <camelContext>,
    which is where a top level resilience4jConfiguration actually lives, and
    the option coverage the test exists for is unchanged.
    
    Adds CamelXmlDslPreconditionTest, one negative test per gated recipe,
    each feeding a document carrying exactly the shape that recipe matches
    under a plainly non-Camel root. Kept in one class rather than spread over
    the existing test classes because the property is uniform across the
    recipes, so a new XML recipe is one line away from being covered.
    Verified the tests are not vacuous: with the gating reverted, 8 of the 10
    fail, the other 2 being the recipes already gated by #125.
    
    camel40.xml.XmlDslRecipe has no negative test on purpose, its matchers
    are anchored at /routes/route so the document root has to be routes,
    which is a Camel root.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
    
    * Address review: accept Spring layouts, gate the last recipes
    
    * Accept a Camel document wherever the Camel marker sits. isCamelXmlDsl
      rejected any root declaring the Spring beans namespace before looking
      further, so the classic <beans><camelContext xmlns="...camel..."> layout
      was skipped by every gated recipe, silently under-migrating it. A Camel
      namespace or a camelContext/routeContext element is now conclusive at
      any depth, and the Spring namespace only rules a document out when no
      Camel marker is present.
    
    * Add the missing roots. <camel> is the most common root in Camel's own
      XML test resources, and routeConfigurations, routeTemplates and
      templatedRoutes are the plural wrappers. Namespace-less files with
      those roots were excluded.
    
    * Gate camel43.CamelResequenceEIPXmlRecipe, which was missed because it
      extends XmlIsoVisitor directly rather than AbstractCamelXmlVisitor.
    
    * Add FindCamelXmlDsl, a search recipe form of the precondition, and use
      it from the declarative XML recipes in 4.12.yaml and 4.13.yaml, which
      ran org.openrewrite.xml.ChangeTagName against any document. furyDsl is
      split so that gating the XML half does not gate the Java DSL change.
    
    * Only convert Camel beans in XmlDsl46Recipe. Accepting <camel> roots
      makes files such as camel-kamelet-main's spring-camel1.xml eligible,
      and those carry Spring bean definitions next to the routes. A Spring
      bean declares its class with class= where a Camel bean uses type=, so
      rewriting <property> into <properties> there would have broken them.
      Found by testing the widened precondition rather than by review.
    
    Adds positive tests for the Spring layout, the <camel> root and a plural
    wrapper root, so the gate cannot silently narrow again.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
 .../java/org/apache/camel/upgrade/RecipesUtil.java |  34 +-
 .../camel40/xml/CircuitBreakerXmlDslRecipe.java    |   6 +-
 .../camel/upgrade/camel40/xml/XmlDslRecipe.java    |   6 +-
 .../apache/camel/upgrade/camel41/XmlDslRecipe.java |   6 +-
 .../camel/upgrade/camel410/XmlDsl410Recipe.java    |   6 +-
 .../upgrade/camel418_1/XmlDsl418_1SagaRecipe.java  |   6 +-
 .../camel418_3/RenameHeaderPrefixInXmlDsl.java     |   5 +-
 .../camel43/CamelResequenceEIPXmlRecipe.java       |   6 +-
 .../camel/upgrade/camel46/XmlDsl46Recipe.java      |  16 +-
 .../camel/upgrade/camel47/XmlDsl47Recipe.java      |   6 +-
 .../upgrade/customRecipes/FindCamelXmlDsl.java     |  47 +++
 .../ReplacePropertyInComponentXml.java             |   5 +-
 .../ReplacePropertyInDataFormatXml.java            |   6 +-
 .../internal/ChangeXmlComponentUriRecipe.java      |   5 +-
 .../src/main/resources/META-INF/rewrite/4.12.yaml  |   2 +
 .../src/main/resources/META-INF/rewrite/4.13.yaml  |  17 +-
 .../upgrade/camel40/CameXmlDslRecipeTest.java      |   8 +-
 .../upgrade/xml/CamelXmlDslPreconditionTest.java   | 361 +++++++++++++++++++++
 18 files changed, 509 insertions(+), 39 deletions(-)

diff --git 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/RecipesUtil.java 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/RecipesUtil.java
index e6b769b..56d0df1 100644
--- 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/RecipesUtil.java
+++ 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/RecipesUtil.java
@@ -87,8 +87,17 @@ public class RecipesUtil {
 
     // ---------------- Camel XML DSL precondition
     private static final Set<String> CAMEL_XML_DSL_ROOT_TAGS = Set.of(
-            "routes", "route", "routeConfiguration", "routeTemplate", 
"templatedRoute",
-            "rests", "rest", "restConfiguration", "camelContext", 
"routeContext", "bean", "beans");
+            "camel", "routes", "route",
+            "routeConfigurations", "routeConfiguration",
+            "routeTemplates", "routeTemplate",
+            "templatedRoutes", "templatedRoute",
+            "rests", "rest", "restConfiguration",
+            "camelContext", "routeContext", "bean", "beans");
+
+    // Elements that only ever appear in a Camel document, wherever they 
happen to be nested
+    private static final Set<String> CAMEL_CONTEXT_TAGS = 
Set.of("camelContext", "routeContext");
+
+    private static final String CAMEL_NAMESPACE = "camel.apache.org";
 
     private static final String SPRING_BEANS_NAMESPACE = 
"springframework.org/schema/beans";
 
@@ -113,18 +122,23 @@ public class RecipesUtil {
             return false;
         }
 
-        // <bean> and <beans> are shared with Spring, whose namespace rules 
the document out
-        if (declaresNamespace(root, SPRING_BEANS_NAMESPACE)) {
-            return false;
+        // A Camel namespace or a Camel context element is conclusive wherever 
it sits in the document.
+        // This is what makes the classic Spring and Blueprint layouts work, 
where a <camelContext> is
+        // nested in a <beans> or <blueprint> root that carries a foreign 
namespace.
+        if (hasCamelMarker(root)) {
+            return true;
         }
 
-        // A Camel namespace is conclusive, whatever the root element is
-        if (declaresNamespace(root, "camel.apache.org")) {
+        // Otherwise fall back on the root element. Camel XML is frequently 
written without any
+        // namespace, but <bean> and <beans> are shared with Spring, whose 
namespace rules them out.
+        return !declaresNamespace(root, SPRING_BEANS_NAMESPACE) && 
CAMEL_XML_DSL_ROOT_TAGS.contains(root.getName());
+    }
+
+    private static boolean hasCamelMarker(Xml.Tag tag) {
+        if (declaresNamespace(tag, CAMEL_NAMESPACE) || 
CAMEL_CONTEXT_TAGS.contains(tag.getName())) {
             return true;
         }
-
-        // Camel XML DSL files are commonly written without any namespace, so 
fall back on the root element
-        return CAMEL_XML_DSL_ROOT_TAGS.contains(root.getName());
+        return 
tag.getChildren().stream().anyMatch(RecipesUtil::hasCamelMarker);
     }
 
     private static boolean declaresNamespace(Xml.Tag root, String 
namespaceFragment) {
diff --git 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel40/xml/CircuitBreakerXmlDslRecipe.java
 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel40/xml/CircuitBreakerXmlDslRecipe.java
index 2fdabbf..67a71f4 100644
--- 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel40/xml/CircuitBreakerXmlDslRecipe.java
+++ 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel40/xml/CircuitBreakerXmlDslRecipe.java
@@ -16,8 +16,10 @@
  */
 package org.apache.camel.upgrade.camel40.xml;
 
+import org.apache.camel.upgrade.RecipesUtil;
 import org.apache.camel.upgrade.AbstractCamelXmlVisitor;
 import org.jspecify.annotations.Nullable;
+import org.openrewrite.Preconditions;
 import org.openrewrite.ExecutionContext;
 import org.openrewrite.Recipe;
 import org.openrewrite.Tree;
@@ -101,7 +103,7 @@ public class CircuitBreakerXmlDslRecipe extends Recipe {
 
     @Override
     public TreeVisitor<?, ExecutionContext> getVisitor() {
-        return new AbstractCamelXmlVisitor() {
+        return Preconditions.check(RecipesUtil.camelXmlDslPrecondition(), new 
AbstractCamelXmlVisitor() {
 
             @Override
             public  Xml.@Nullable Tag doVisitTag(final Xml.Tag tag, final 
ExecutionContext ctx) {
@@ -142,6 +144,6 @@ public class CircuitBreakerXmlDslRecipe extends Recipe {
 
                 return t;
             }
-        };
+        });
     }
 }
diff --git 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel40/xml/XmlDslRecipe.java
 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel40/xml/XmlDslRecipe.java
index 5d47bae..80e5a1e 100644
--- 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel40/xml/XmlDslRecipe.java
+++ 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel40/xml/XmlDslRecipe.java
@@ -16,8 +16,10 @@
  */
 package org.apache.camel.upgrade.camel40.xml;
 
+import org.apache.camel.upgrade.RecipesUtil;
 import org.apache.camel.upgrade.AbstractCamelXmlVisitor;
 import org.jspecify.annotations.Nullable;
+import org.openrewrite.Preconditions;
 import org.openrewrite.ExecutionContext;
 import org.openrewrite.Recipe;
 import org.openrewrite.Tree;
@@ -69,7 +71,7 @@ public class XmlDslRecipe extends Recipe {
 
     @Override
     public TreeVisitor<?, ExecutionContext> getVisitor() {
-        return new AbstractCamelXmlVisitor() {
+        return Preconditions.check(RecipesUtil.camelXmlDslPrecondition(), new 
AbstractCamelXmlVisitor() {
 
             @Override
             public  Xml.@Nullable Tag doVisitTag(final Xml.Tag tag, final 
ExecutionContext ctx) {
@@ -100,6 +102,6 @@ public class XmlDslRecipe extends Recipe {
 
                 return t;
             }
-        };
+        });
     }
 }
diff --git 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel41/XmlDslRecipe.java
 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel41/XmlDslRecipe.java
index 0bfa9c1..1b32245 100644
--- 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel41/XmlDslRecipe.java
+++ 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel41/XmlDslRecipe.java
@@ -16,7 +16,9 @@
  */
 package org.apache.camel.upgrade.camel41;
 
+import org.apache.camel.upgrade.RecipesUtil;
 import org.apache.camel.upgrade.AbstractCamelXmlVisitor;
+import org.openrewrite.Preconditions;
 import org.openrewrite.ExecutionContext;
 import org.openrewrite.Recipe;
 import org.openrewrite.Tree;
@@ -70,7 +72,7 @@ public class XmlDslRecipe extends Recipe {
 
     @Override
     public TreeVisitor<?, ExecutionContext> getVisitor() {
-        return new AbstractCamelXmlVisitor() {
+        return Preconditions.check(RecipesUtil.camelXmlDslPrecondition(), new 
AbstractCamelXmlVisitor() {
 
             @Override
             public Xml.Tag doVisitTag(final Xml.Tag tag, final 
ExecutionContext ctx) {
@@ -106,6 +108,6 @@ public class XmlDslRecipe extends Recipe {
 
                 return t;
             }
-        };
+        });
     }
 }
diff --git 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel410/XmlDsl410Recipe.java
 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel410/XmlDsl410Recipe.java
index 49f13ca..8c2f3f2 100644
--- 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel410/XmlDsl410Recipe.java
+++ 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel410/XmlDsl410Recipe.java
@@ -16,7 +16,9 @@
  */
 package org.apache.camel.upgrade.camel410;
 
+import org.apache.camel.upgrade.RecipesUtil;
 import org.apache.camel.upgrade.AbstractCamelXmlVisitor;
+import org.openrewrite.Preconditions;
 import org.openrewrite.ExecutionContext;
 import org.openrewrite.Recipe;
 import org.openrewrite.TreeVisitor;
@@ -50,7 +52,7 @@ public class XmlDsl410Recipe extends Recipe {
 
     @Override
     public TreeVisitor<?, ExecutionContext> getVisitor() {
-        return new AbstractCamelXmlVisitor() {
+        return Preconditions.check(RecipesUtil.camelXmlDslPrecondition(), new 
AbstractCamelXmlVisitor() {
 
             @Override
             public Xml.Tag doVisitTag(final Xml.Tag tag, final 
ExecutionContext ctx) {
@@ -64,6 +66,6 @@ public class XmlDsl410Recipe extends Recipe {
                         .findAny()
                         .orElse(t);
             }
-        };
+        });
     }
 }
diff --git 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel418_1/XmlDsl418_1SagaRecipe.java
 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel418_1/XmlDsl418_1SagaRecipe.java
index 34ed9d8..070b11a 100644
--- 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel418_1/XmlDsl418_1SagaRecipe.java
+++ 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel418_1/XmlDsl418_1SagaRecipe.java
@@ -16,7 +16,9 @@
  */
 package org.apache.camel.upgrade.camel418_1;
 
+import org.apache.camel.upgrade.RecipesUtil;
 import org.apache.camel.upgrade.AbstractCamelXmlVisitor;
+import org.openrewrite.Preconditions;
 import org.openrewrite.ExecutionContext;
 import org.openrewrite.Recipe;
 import org.openrewrite.TreeVisitor;
@@ -49,7 +51,7 @@ public class XmlDsl418_1SagaRecipe extends Recipe {
 
     @Override
     public TreeVisitor<?, ExecutionContext> getVisitor() {
-        return new AbstractCamelXmlVisitor() {
+        return Preconditions.check(RecipesUtil.camelXmlDslPrecondition(), new 
AbstractCamelXmlVisitor() {
 
             @Override
             public Xml.Tag doVisitTag(final Xml.Tag tag, final 
ExecutionContext ctx) {
@@ -111,6 +113,6 @@ public class XmlDsl418_1SagaRecipe extends Recipe {
                         )
                 )));
             }
-        };
+        });
     }
 }
diff --git 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel418_3/RenameHeaderPrefixInXmlDsl.java
 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel418_3/RenameHeaderPrefixInXmlDsl.java
index ee54903..f5ce0e8 100644
--- 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel418_3/RenameHeaderPrefixInXmlDsl.java
+++ 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel418_3/RenameHeaderPrefixInXmlDsl.java
@@ -16,7 +16,9 @@
  */
 package org.apache.camel.upgrade.camel418_3;
 
+import org.apache.camel.upgrade.RecipesUtil;
 import org.apache.camel.upgrade.AbstractCamelXmlVisitor;
+import org.openrewrite.Preconditions;
 import org.openrewrite.ExecutionContext;
 import org.openrewrite.Option;
 import org.openrewrite.Recipe;
@@ -69,7 +71,8 @@ public class RenameHeaderPrefixInXmlDsl extends Recipe {
 
     @Override
     public TreeVisitor<?, ExecutionContext> getVisitor() {
-        return new XmlHeaderPrefixVisitor(oldPrefix, newPrefix);
+        return Preconditions.check(RecipesUtil.camelXmlDslPrecondition(),
+                                   new XmlHeaderPrefixVisitor(oldPrefix, 
newPrefix));
     }
 
     private static class XmlHeaderPrefixVisitor extends 
AbstractCamelXmlVisitor {
diff --git 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel43/CamelResequenceEIPXmlRecipe.java
 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel43/CamelResequenceEIPXmlRecipe.java
index 44a7663..4a6f167 100644
--- 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel43/CamelResequenceEIPXmlRecipe.java
+++ 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel43/CamelResequenceEIPXmlRecipe.java
@@ -16,7 +16,9 @@
  */
 package org.apache.camel.upgrade.camel43;
 
+import org.apache.camel.upgrade.RecipesUtil;
 import org.openrewrite.ExecutionContext;
+import org.openrewrite.Preconditions;
 import org.openrewrite.Recipe;
 import org.openrewrite.TreeVisitor;
 import org.openrewrite.xml.XPathMatcher;
@@ -75,7 +77,7 @@ public class CamelResequenceEIPXmlRecipe extends Recipe {
 
     @Override
     public TreeVisitor<?, ExecutionContext> getVisitor() {
-        return new XmlIsoVisitor<>() {
+        return Preconditions.check(RecipesUtil.camelXmlDslPrecondition(), new 
XmlIsoVisitor<ExecutionContext>() {
 
             @Override
             public Xml.Tag visitTag(final Xml.Tag tag, final ExecutionContext 
ctx) {
@@ -89,6 +91,6 @@ public class CamelResequenceEIPXmlRecipe extends Recipe {
 
                 return t;
             }
-        };
+        });
     }
 }
diff --git 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel46/XmlDsl46Recipe.java
 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel46/XmlDsl46Recipe.java
index 4040fa9..408e72f 100644
--- 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel46/XmlDsl46Recipe.java
+++ 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel46/XmlDsl46Recipe.java
@@ -19,6 +19,7 @@ package org.apache.camel.upgrade.camel46;
 import org.apache.camel.upgrade.AbstractCamelXmlVisitor;
 import org.apache.camel.upgrade.RecipesUtil;
 import org.jspecify.annotations.Nullable;
+import org.openrewrite.Cursor;
 import org.openrewrite.ExecutionContext;
 import org.openrewrite.Preconditions;
 import org.openrewrite.Recipe;
@@ -61,7 +62,7 @@ public class XmlDsl46Recipe extends Recipe {
 
                 //save all properties into a list placed to the bean tag
                 //and the first property rename to properties and strip content
-                if (BEAN_PROPERTY_XPATH_MATCHER.matches(getCursor())) {
+                if (BEAN_PROPERTY_XPATH_MATCHER.matches(getCursor()) && 
isCamelBean(getCursor().getParent())) {
 
                     List<Xml.Tag> sb = 
getCursor().getParent().getMessage("properties");
                     if(sb == null) {
@@ -90,6 +91,19 @@ public class XmlDsl46Recipe extends Recipe {
                 return t;
             }
 
+            /**
+             * A Camel bean declares its class with {@code type}, while a 
Spring bean definition uses
+             * {@code class}. Camel XML files legitimately carry Spring bean 
definitions alongside routes,
+             * and rewriting those into {@code <properties>} would break them.
+             */
+            private boolean isCamelBean(@Nullable Cursor beanCursor) {
+                if (beanCursor == null || !(beanCursor.getValue() instanceof 
Xml.Tag)) {
+                    return false;
+                }
+                Xml.Tag bean = (Xml.Tag) beanCursor.getValue();
+                return bean.getAttributes().stream().anyMatch(a -> 
"type".equals(a.getKeyAsString()));
+            }
+
         });
     }
 }
diff --git 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel47/XmlDsl47Recipe.java
 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel47/XmlDsl47Recipe.java
index 37eca9b..82c0c8e 100644
--- 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel47/XmlDsl47Recipe.java
+++ 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/camel47/XmlDsl47Recipe.java
@@ -16,7 +16,9 @@
  */
 package org.apache.camel.upgrade.camel47;
 
+import org.apache.camel.upgrade.RecipesUtil;
 import org.apache.camel.upgrade.AbstractCamelXmlVisitor;
+import org.openrewrite.Preconditions;
 import org.openrewrite.ExecutionContext;
 import org.openrewrite.Recipe;
 import org.openrewrite.TreeVisitor;
@@ -54,7 +56,7 @@ public class XmlDsl47Recipe extends Recipe {
 
     @Override
     public TreeVisitor<?, ExecutionContext> getVisitor() {
-        return new AbstractCamelXmlVisitor() {
+        return Preconditions.check(RecipesUtil.camelXmlDslPrecondition(), new 
AbstractCamelXmlVisitor() {
 
             @Override
             public Xml.Tag doVisitTag(final Xml.Tag tag, final 
ExecutionContext ctx) {
@@ -68,6 +70,6 @@ public class XmlDsl47Recipe extends Recipe {
                         .findAny()
                         .orElse(t);
             }
-        };
+        });
     }
 }
diff --git 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/FindCamelXmlDsl.java
 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/FindCamelXmlDsl.java
new file mode 100644
index 0000000..59ddc51
--- /dev/null
+++ 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/FindCamelXmlDsl.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.apache.camel.upgrade.customRecipes;
+
+import org.apache.camel.upgrade.RecipesUtil;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+
+/**
+ * Search recipe form of {@link RecipesUtil#camelXmlDslPrecondition()}, so 
that declarative recipes can
+ * reference it from a {@code preconditions:} block the way the Java recipes 
use it in code.
+ */
+public class FindCamelXmlDsl extends Recipe {
+
+    @Override
+    public String getDisplayName() {
+        return "Find Camel XML DSL documents";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Marks XML documents that belong to the Camel XML DSL, so that 
a recipe operating on XML " +
+               "is not applied to unrelated documents such as Spring bean 
definitions. A Camel namespace " +
+               "or a Camel context element anywhere in the document is 
conclusive; otherwise the root " +
+               "element decides.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return RecipesUtil.camelXmlDslPrecondition();
+    }
+}
diff --git 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/ReplacePropertyInComponentXml.java
 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/ReplacePropertyInComponentXml.java
index ebf85fd..e820154 100644
--- 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/ReplacePropertyInComponentXml.java
+++ 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/ReplacePropertyInComponentXml.java
@@ -18,6 +18,7 @@ package org.apache.camel.upgrade.customRecipes;
 
 import org.apache.camel.upgrade.AbstractCamelXmlVisitor;
 import org.apache.camel.upgrade.RecipesUtil;
+import org.openrewrite.Preconditions;
 import org.openrewrite.ExecutionContext;
 import org.openrewrite.Option;
 import org.openrewrite.Recipe;
@@ -89,7 +90,7 @@ public class ReplacePropertyInComponentXml extends Recipe {
 
     @Override
     public TreeVisitor<?, ExecutionContext> getVisitor() {
-        return new AbstractCamelXmlVisitor() {
+        return Preconditions.check(RecipesUtil.camelXmlDslPrecondition(), new 
AbstractCamelXmlVisitor() {
 
             @Override
             public Xml.Tag doVisitTag(final Xml.Tag tag, final 
ExecutionContext ctx) {
@@ -104,7 +105,7 @@ public class ReplacePropertyInComponentXml extends Recipe {
 
                 return t;
             }
-        };
+        });
     }
 
     private Xml.Tag replacePropertyIfPossible(final Xml.Tag tag) {
diff --git 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/ReplacePropertyInDataFormatXml.java
 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/ReplacePropertyInDataFormatXml.java
index 94237f3..5d51591 100644
--- 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/ReplacePropertyInDataFormatXml.java
+++ 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/ReplacePropertyInDataFormatXml.java
@@ -16,7 +16,9 @@
  */
 package org.apache.camel.upgrade.customRecipes;
 
+import org.apache.camel.upgrade.RecipesUtil;
 import org.apache.camel.upgrade.AbstractCamelXmlVisitor;
+import org.openrewrite.Preconditions;
 import org.openrewrite.ExecutionContext;
 import org.openrewrite.Option;
 import org.openrewrite.Recipe;
@@ -73,7 +75,7 @@ public class ReplacePropertyInDataFormatXml extends Recipe {
 
     @Override
     public TreeVisitor<?, ExecutionContext> getVisitor() {
-        return new AbstractCamelXmlVisitor() {
+        return Preconditions.check(RecipesUtil.camelXmlDslPrecondition(), new 
AbstractCamelXmlVisitor() {
 
             @Override
             public Xml.Tag doVisitTag(final Xml.Tag tag, final 
ExecutionContext ctx) {
@@ -84,7 +86,7 @@ public class ReplacePropertyInDataFormatXml extends Recipe {
                 }
                 return t;
             }
-        };
+        });
     }
 
     private Xml.Tag replacePropertyIfPossible(final Xml.Tag tag) {
diff --git 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/internal/ChangeXmlComponentUriRecipe.java
 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/internal/ChangeXmlComponentUriRecipe.java
index 88b0ac4..2c97a5b 100644
--- 
a/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/internal/ChangeXmlComponentUriRecipe.java
+++ 
b/camel-upgrade-recipes/src/main/java/org/apache/camel/upgrade/customRecipes/internal/ChangeXmlComponentUriRecipe.java
@@ -18,6 +18,7 @@ package org.apache.camel.upgrade.customRecipes.internal;
 
 import org.apache.camel.upgrade.AbstractCamelXmlVisitor;
 import org.apache.camel.upgrade.RecipesUtil;
+import org.openrewrite.Preconditions;
 import org.openrewrite.ExecutionContext;
 import org.openrewrite.Option;
 import org.openrewrite.Recipe;
@@ -97,7 +98,7 @@ public class ChangeXmlComponentUriRecipe extends Recipe {
     public TreeVisitor<?, ExecutionContext> getVisitor() {
         Pattern pattern = Pattern.compile(uriPattern);
 
-        return new AbstractCamelXmlVisitor() {
+        return Preconditions.check(RecipesUtil.camelXmlDslPrecondition(), new 
AbstractCamelXmlVisitor() {
             @Override
             public Xml.Tag doVisitTag(Xml.Tag tag, ExecutionContext ctx) {
                 Xml.Tag t = super.doVisitTag(tag, ctx);
@@ -109,7 +110,7 @@ public class ChangeXmlComponentUriRecipe extends Recipe {
 
                 return t;
             }
-        };
+        });
     }
 
     private static Xml.Tag transformXmlUri(Xml.Tag tag, Pattern pattern, 
String replacement) {
diff --git 
a/camel-upgrade-recipes/src/main/resources/META-INF/rewrite/4.12.yaml 
b/camel-upgrade-recipes/src/main/resources/META-INF/rewrite/4.12.yaml
index 7d48e04..2689147 100644
--- a/camel-upgrade-recipes/src/main/resources/META-INF/rewrite/4.12.yaml
+++ b/camel-upgrade-recipes/src/main/resources/META-INF/rewrite/4.12.yaml
@@ -99,6 +99,8 @@ type: specs.openrewrite.org/v1beta/recipe
 name: org.apache.camel.upgrade.camel412.xmlDslBearer
 displayName: XML DSL bearer from rest/securityDefinitions is renamed to 
bearerToken
 description: Rest definition in XML DSL with security constraints, then bearer 
in YAML and XML DSL has been renamed to bearerToken to be aligned with Java DSL.
+preconditions:
+  - org.apache.camel.upgrade.customRecipes.FindCamelXmlDsl
 recipeList:
   - org.openrewrite.xml.ChangeTagName:
       elementName: //rest/securityDefinitions/bearer
diff --git 
a/camel-upgrade-recipes/src/main/resources/META-INF/rewrite/4.13.yaml 
b/camel-upgrade-recipes/src/main/resources/META-INF/rewrite/4.13.yaml
index b09d268..7abccb3 100644
--- a/camel-upgrade-recipes/src/main/resources/META-INF/rewrite/4.13.yaml
+++ b/camel-upgrade-recipes/src/main/resources/META-INF/rewrite/4.13.yaml
@@ -70,6 +70,20 @@ recipeList:
 type: specs.openrewrite.org/v1beta/recipe
 name: org.apache.camel.upgrade.camel413.furyDsl
 displayName: Change dataformat fury to fury in xml/java dsl.
+recipeList:
+  - org.apache.camel.upgrade.camel413.furyXmlDsl
+  - org.openrewrite.java.ChangeMethodName:
+      methodPattern: org.apache.camel.builder.DataFormatClause fury(..)
+      newMethodName: fory
+---
+#https://camel.apache.org/manual/camel-4x-upgrade-guide-4_13.html#_camel_fury
+# Split from furyDsl so that the Camel XML DSL precondition does not gate the 
Java DSL change too
+type: specs.openrewrite.org/v1beta/recipe
+name: org.apache.camel.upgrade.camel413.furyXmlDsl
+displayName: Change dataformat fury to fory in xml dsl.
+description: Change dataformat fury to fory in the Camel XML DSL.
+preconditions:
+  - org.apache.camel.upgrade.customRecipes.FindCamelXmlDsl
 recipeList:
   - org.openrewrite.xml.ChangeTagName:
       elementName: dataFormats/fury
@@ -80,8 +94,5 @@ recipeList:
   - org.openrewrite.xml.ChangeTagName:
       elementName: marshal/fury
       newName: fory
-  - org.openrewrite.java.ChangeMethodName:
-      methodPattern: org.apache.camel.builder.DataFormatClause fury(..)
-      newMethodName: fory
 
 
diff --git 
a/camel-upgrade-recipes/src/test/java/org/apache/camel/upgrade/camel40/CameXmlDslRecipeTest.java
 
b/camel-upgrade-recipes/src/test/java/org/apache/camel/upgrade/camel40/CameXmlDslRecipeTest.java
index 7615142..7e3e39c 100644
--- 
a/camel-upgrade-recipes/src/test/java/org/apache/camel/upgrade/camel40/CameXmlDslRecipeTest.java
+++ 
b/camel-upgrade-recipes/src/test/java/org/apache/camel/upgrade/camel40/CameXmlDslRecipeTest.java
@@ -86,7 +86,7 @@ public class CameXmlDslRecipeTest implements RewriteTest {
         //language=xml
         rewriteRun(xml(
           """
-            <differentContext>
+            <camelContext>
                 <circuitBreaker>
                     <resilience4jConfiguration>
                         <bulkheadEnabled>5643</bulkheadEnabled>
@@ -98,15 +98,15 @@ public class CameXmlDslRecipeTest implements RewriteTest {
                         
<timeoutCancelRunningFuture></timeoutCancelRunningFuture>
                     </resilience4jConfiguration>
                 </circuitBreaker>
-            </differentContext>
+            </camelContext>
             """,
           """
-            <differentContext>
+            <camelContext>
                 <circuitBreaker>
                     <resilience4jConfiguration bulkheadEnabled="5643" 
bulkheadMaxConcurrentCalls="aaaa" bulkheadMaxWaitDuration="1" 
timeoutEnabled="true" timeoutExecutorService="1" timeoutDuration="1">
                     </resilience4jConfiguration>
                 </circuitBreaker>
-            </differentContext>
+            </camelContext>
             """));
     }
 
diff --git 
a/camel-upgrade-recipes/src/test/java/org/apache/camel/upgrade/xml/CamelXmlDslPreconditionTest.java
 
b/camel-upgrade-recipes/src/test/java/org/apache/camel/upgrade/xml/CamelXmlDslPreconditionTest.java
new file mode 100644
index 0000000..ef0794a
--- /dev/null
+++ 
b/camel-upgrade-recipes/src/test/java/org/apache/camel/upgrade/xml/CamelXmlDslPreconditionTest.java
@@ -0,0 +1,361 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.apache.camel.upgrade.xml;
+
+import org.apache.camel.upgrade.CamelTestUtil;
+import org.apache.camel.upgrade.camel410.XmlDsl410Recipe;
+import org.apache.camel.upgrade.camel418_1.XmlDsl418_1SagaRecipe;
+import org.apache.camel.upgrade.camel43.CamelResequenceEIPXmlRecipe;
+import org.apache.camel.upgrade.camel418_3.RenameHeaderInXmlDsl;
+import org.apache.camel.upgrade.camel418_3.RenameHeaderPrefixInXmlDsl;
+import org.apache.camel.upgrade.camel46.XmlDsl46Recipe;
+import org.apache.camel.upgrade.camel47.XmlDsl47Recipe;
+import org.apache.camel.upgrade.customRecipes.ReplacePropertyInComponentXml;
+import org.apache.camel.upgrade.customRecipes.ReplacePropertyInDataFormatXml;
+import 
org.apache.camel.upgrade.customRecipes.internal.ChangeXmlComponentUriRecipe;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.test.RewriteTest;
+import org.openrewrite.test.TypeValidation;
+
+import static org.openrewrite.xml.Assertions.xml;
+
+/**
+ * Every XML DSL recipe is gated on {@code 
RecipesUtil.camelXmlDslPrecondition()}, so none of them may
+ * touch a document that is not Camel XML. Each test feeds a document carrying 
exactly the shape the
+ * recipe matches, wrapped in a root element that is plainly not Camel, and 
expects no change.
+ * <p>
+ * {@code camel40.xml.XmlDslRecipe} has no test here on purpose: its matchers 
are anchored at
+ * {@code /routes/route}, so the document root has to be {@code routes}, which 
is a Camel root.
+ */
+class CamelXmlDslPreconditionTest implements RewriteTest {
+
+    @DocumentExample
+    @Test
+    void springBeanDefinitionIsNotADataFormatOrHeaderCarrier() {
+        //language=xml
+        rewriteRun(
+            spec -> spec.recipe(new XmlDsl46Recipe()),
+            xml(
+                """
+                <beans xmlns="http://www.springframework.org/schema/beans";>
+                    <bean id="example" class="com.example.Example">
+                        <property name="topic" value="orders"/>
+                    </bean>
+                </beans>
+                """
+            )
+        );
+    }
+
+    @Test
+    void headerRenameLeavesNonCamelXmlAlone() {
+        //language=xml
+        rewriteRun(
+            spec -> spec.recipe(new RenameHeaderInXmlDsl("kafka.TOPIC", 
"CamelKafkaTopic")),
+            xml(
+                """
+                <configuration xmlns="http://example.com/schema/http";>
+                    <endpoint url="https://example.com/publish";>
+                        <header name="kafka.TOPIC" value="orders"/>
+                        <template>${header.kafka.TOPIC}</template>
+                    </endpoint>
+                </configuration>
+                """
+            )
+        );
+    }
+
+    @Test
+    void headerPrefixRenameLeavesNonCamelXmlAlone() {
+        //language=xml
+        rewriteRun(
+            spec -> spec.recipe(new RenameHeaderPrefixInXmlDsl("SolrField.", 
"CamelSolrField.")),
+            xml(
+                """
+                <configuration xmlns="http://example.com/schema/http";>
+                    <endpoint url="https://example.com/search";>
+                        <header name="SolrField.id" value="doc123"/>
+                    </endpoint>
+                </configuration>
+                """
+            )
+        );
+    }
+
+    @Test
+    void sagaLeavesNonCamelXmlAlone() {
+        //language=xml
+        rewriteRun(
+            spec -> spec.recipe(new XmlDsl418_1SagaRecipe()),
+            xml(
+                """
+                <workflow xmlns="http://example.com/schema/workflow";>
+                    <saga>
+                        <compensation uri="http://example.com/undo"/>
+                        <completion uri="http://example.com/done"/>
+                    </saga>
+                </workflow>
+                """
+            )
+        );
+    }
+
+    @Test
+    void circuitBreakerLeavesNonCamelXmlAlone() {
+        // driven through the whole 4.0 migration, the way 
CameXmlDslRecipeTest drives it
+        //language=xml
+        rewriteRun(
+            spec -> CamelTestUtil.recipe(spec, CamelTestUtil.CamelVersion.v4_0)
+                    .typeValidationOptions(TypeValidation.none()),
+            xml(
+                """
+                <application>
+                    <circuitBreaker>
+                        <resilience4jConfiguration>
+                            <timeoutEnabled>true</timeoutEnabled>
+                            <timeoutDuration>1</timeoutDuration>
+                        </resilience4jConfiguration>
+                    </circuitBreaker>
+                </application>
+                """
+            )
+        );
+    }
+
+    @Test
+    void interceptLeavesNonCamelXmlAlone() {
+        //language=xml
+        rewriteRun(
+            spec -> spec.recipe(new XmlDsl410Recipe()),
+            xml(
+                """
+                <pipeline xmlns="http://example.com/schema/pipeline";>
+                    <intercept>
+                        <when>
+                            <simple>true</simple>
+                        </when>
+                    </intercept>
+                </pipeline>
+                """
+            )
+        );
+    }
+
+    @Test
+    void loadBalanceLeavesNonCamelXmlAlone() {
+        //language=xml
+        rewriteRun(
+            spec -> spec.recipe(new XmlDsl47Recipe()),
+            xml(
+                """
+                <cluster xmlns="http://example.com/schema/cluster";>
+                    <loadBalance>
+                        <roundRobin/>
+                    </loadBalance>
+                </cluster>
+                """
+            )
+        );
+    }
+
+    @Test
+    void componentPropertyLeavesNonCamelXmlAlone() {
+        //language=xml
+        rewriteRun(
+            spec -> spec.recipe(new 
ReplacePropertyInComponentXml("netty-http", "keyStoreFile", "keyStoreResource", 
"file:")),
+            xml(
+                """
+                <deployment xmlns="http://example.com/schema/deployment";>
+                    <route>
+                        <from 
uri="netty-http:https://0.0.0.0:8443/service?keyStoreFile=/opt/keystore.jks"/>
+                    </route>
+                </deployment>
+                """
+            )
+        );
+    }
+
+    @Test
+    void dataFormatPropertyLeavesNonCamelXmlAlone() {
+        //language=xml
+        rewriteRun(
+            spec -> spec.recipe(new ReplacePropertyInDataFormatXml("csv", 
"formatRef", "format")),
+            xml(
+                """
+                <deployment xmlns="http://example.com/schema/deployment";>
+                    <marshal>
+                        <csv formatRef="myCsvFormat"/>
+                    </marshal>
+                </deployment>
+                """
+            )
+        );
+    }
+
+    @Test
+    void componentUriLeavesNonCamelXmlAlone() {
+        //language=xml
+        rewriteRun(
+            spec -> spec.recipe(new 
ChangeXmlComponentUriRecipe("^langchain4j-tools:(.*)$", "ai-tool:${1}", true)),
+            xml(
+                """
+                <deployment xmlns="http://example.com/schema/deployment";>
+                    <route>
+                        <from uri="langchain4j-tools:weather?tags=weather"/>
+                    </route>
+                </deployment>
+                """
+            )
+        );
+    }
+
+    @Test
+    void resequenceLeavesNonCamelXmlAlone() {
+        //language=xml
+        rewriteRun(
+            spec -> spec.recipe(new CamelResequenceEIPXmlRecipe()),
+            xml(
+                """
+                <pipeline xmlns="http://example.com/schema/pipeline";>
+                    <resequence>
+                        <stream-config timeout="1000"/>
+                    </resequence>
+                </pipeline>
+                """
+            )
+        );
+    }
+
+    // ---- documents that must still be recognised as Camel ----
+
+    @Test
+    void springXmlLayoutIsMigrated() {
+        //language=xml
+        rewriteRun(
+            spec -> spec.recipe(new RenameHeaderInXmlDsl("kafka.TOPIC", 
"CamelKafkaTopic")),
+            xml(
+                """
+                <beans xmlns="http://www.springframework.org/schema/beans";>
+                    <camelContext 
xmlns="http://camel.apache.org/schema/spring";>
+                        <route>
+                            <from uri="direct:start"/>
+                            <setHeader name="kafka.TOPIC">
+                                <constant>orders</constant>
+                            </setHeader>
+                        </route>
+                    </camelContext>
+                </beans>
+                """,
+                """
+                <beans xmlns="http://www.springframework.org/schema/beans";>
+                    <camelContext 
xmlns="http://camel.apache.org/schema/spring";>
+                        <route>
+                            <from uri="direct:start"/>
+                            <setHeader name="CamelKafkaTopic">
+                                <constant>orders</constant>
+                            </setHeader>
+                        </route>
+                    </camelContext>
+                </beans>
+                """
+            )
+        );
+    }
+
+    @Test
+    void camelRootIsMigrated() {
+        //language=xml
+        rewriteRun(
+            spec -> spec.recipe(new RenameHeaderInXmlDsl("kafka.TOPIC", 
"CamelKafkaTopic")),
+            xml(
+                """
+                <camel>
+                    <route>
+                        <from uri="direct:start"/>
+                        <setHeader name="kafka.TOPIC">
+                            <constant>orders</constant>
+                        </setHeader>
+                    </route>
+                </camel>
+                """,
+                """
+                <camel>
+                    <route>
+                        <from uri="direct:start"/>
+                        <setHeader name="CamelKafkaTopic">
+                            <constant>orders</constant>
+                        </setHeader>
+                    </route>
+                </camel>
+                """
+            )
+        );
+    }
+
+    @Test
+    void pluralWrapperRootIsMigrated() {
+        //language=xml
+        rewriteRun(
+            spec -> spec.recipe(new RenameHeaderInXmlDsl("kafka.TOPIC", 
"CamelKafkaTopic")),
+            xml(
+                """
+                <routeConfigurations>
+                    <routeConfiguration>
+                        <onException>
+                            <setHeader name="kafka.TOPIC">
+                                <constant>dlq</constant>
+                            </setHeader>
+                        </onException>
+                    </routeConfiguration>
+                </routeConfigurations>
+                """,
+                """
+                <routeConfigurations>
+                    <routeConfiguration>
+                        <onException>
+                            <setHeader name="CamelKafkaTopic">
+                                <constant>dlq</constant>
+                            </setHeader>
+                        </onException>
+                    </routeConfiguration>
+                </routeConfigurations>
+                """
+            )
+        );
+    }
+
+    @Test
+    void springStyleBeansInACamelDocumentAreLeftAlone() {
+        // a Camel XML file legitimately carries Spring bean definitions next 
to its routes, and a Spring
+        // bean declares its class with class= where a Camel bean uses type=
+        //language=xml
+        rewriteRun(
+            spec -> spec.recipe(new XmlDsl46Recipe()),
+            xml(
+                """
+                <camel>
+                    <bean id="bean1" class="org.apache.camel.main.app.Bean1">
+                        <property name="bean" ref="bean2"/>
+                    </bean>
+                </camel>
+                """
+            )
+        );
+    }
+
+}

Reply via email to