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

gnodet pushed a commit to branch camel-22884-when-method-validation
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 5d1bc14daa89e6a12c59c11c9ced2ce6ced67101
Author: Guillaume Nodet <[email protected]>
AuthorDate: Fri Mar 13 08:36:13 2026 +0100

    CAMEL-22884: Validate method attribute placement in when clause
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../camel/model/BasicOutputExpressionNode.java     | 28 +++++++++++++
 .../org/apache/camel/xml/in/ModelParserTest.java   | 46 ++++++++++++++++++++++
 2 files changed, 74 insertions(+)

diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/BasicOutputExpressionNode.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/BasicOutputExpressionNode.java
index 2ab7ecb72732..469905347bd0 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/BasicOutputExpressionNode.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/BasicOutputExpressionNode.java
@@ -75,4 +75,32 @@ public abstract class BasicOutputExpressionNode extends 
BasicExpressionNode<Basi
     public void addOutput(ProcessorDefinition<?> output) {
         this.outputs.add(output);
     }
+
+    @Override
+    public void setExpression(ExpressionDefinition expression) {
+        // Detect when an expression element (e.g. <method>) appears after 
processing steps
+        // inside a when/filter clause in XML/YAML DSL. This is almost 
certainly a user mistake
+        // where they intended to use <bean> (processor) instead of <method> 
(expression/predicate).
+        // We skip this check when the existing expression wraps an 
ExpressionClause (Java DSL),
+        // because preCreateProcessor() legitimately re-sets the expression 
after resolving it.
+        if (expression != null && getExpression() != null && 
!outputs.isEmpty()) {
+            ExpressionDefinition existing = getExpression();
+            boolean isExpressionClause
+                    = existing.getExpressionValue() instanceof 
org.apache.camel.builder.ExpressionClause
+                            || existing.getPredicate() instanceof 
org.apache.camel.builder.ExpressionClause;
+            if (!isExpressionClause) {
+                String lang = expression.getLanguage() != null
+                        ? expression.getLanguage() : 
expression.getClass().getSimpleName();
+                throw new IllegalArgumentException(
+                        "The " + getShortName() + " already has a predicate (" 
+ existing
+                                                   + ") and " + outputs.size() 
+ " output(s). "
+                                                   + "The expression '" + lang
+                                                   + "' is being parsed as an 
expression/predicate but appears after processing steps. "
+                                                   + "If you intended to call 
a bean method as a processing step, use <bean> instead of <method>. "
+                                                   + "An expression element 
must be the first child of <" + getShortName()
+                                                   + ">.");
+            }
+        }
+        super.setExpression(expression);
+    }
 }
diff --git 
a/core/camel-xml-io/src/test/java/org/apache/camel/xml/in/ModelParserTest.java 
b/core/camel-xml-io/src/test/java/org/apache/camel/xml/in/ModelParserTest.java
index 150e89434e8d..679edbff0924 100644
--- 
a/core/camel-xml-io/src/test/java/org/apache/camel/xml/in/ModelParserTest.java
+++ 
b/core/camel-xml-io/src/test/java/org/apache/camel/xml/in/ModelParserTest.java
@@ -449,6 +449,52 @@ public class ModelParserTest {
         assertTrue(e.getMessage().startsWith("Unexpected attribute '{}ref'"));
     }
 
+    @Test
+    public void testMethodAfterStepsInWhenClauseShouldFail() throws Exception {
+        String routesXml = "<routes 
xmlns=\"http://camel.apache.org/schema/xml-io\";>"
+                           + "  <route>"
+                           + "    <from uri=\"direct:start\"/>"
+                           + "    <choice>"
+                           + "      <when>"
+                           + "        <simple>${header.foo} == 'bar'</simple>"
+                           + "        <log message=\"Condition met\"/>"
+                           + "        <method ref=\"someBean\" 
method=\"processFooBar\"/>"
+                           + "      </when>"
+                           + "      <otherwise>"
+                           + "        <to uri=\"mock:other\"/>"
+                           + "      </otherwise>"
+                           + "    </choice>"
+                           + "  </route>"
+                           + "</routes>";
+        Exception e = assertThrows(Exception.class, () -> {
+            new ModelParser(new 
StringReader(routesXml)).parseRoutesDefinition();
+        });
+        assertTrue(e.getMessage().contains("already has a predicate")
+                || e.getCause().getMessage().contains("already has a 
predicate"),
+                "Error message should indicate that the when clause already 
has a predicate: " + e.getMessage());
+    }
+
+    @Test
+    public void testMethodAsPredicateInWhenClauseShouldWork() throws Exception 
{
+        String routesXml = "<routes 
xmlns=\"http://camel.apache.org/schema/xml-io\";>"
+                           + "  <route>"
+                           + "    <from uri=\"direct:start\"/>"
+                           + "    <choice>"
+                           + "      <when>"
+                           + "        <method ref=\"someBean\" 
method=\"isFoo\"/>"
+                           + "        <to uri=\"mock:foo\"/>"
+                           + "      </when>"
+                           + "      <otherwise>"
+                           + "        <to uri=\"mock:other\"/>"
+                           + "      </otherwise>"
+                           + "    </choice>"
+                           + "  </route>"
+                           + "</routes>";
+        RoutesDefinition routes = new ModelParser(new 
StringReader(routesXml)).parseRoutesDefinition().orElse(null);
+        assertNotNull(routes);
+        assertEquals(1, routes.getRoutes().size());
+    }
+
     private Path getResourceFolder() {
         final URL resource = 
getClass().getClassLoader().getResource("barInterceptorRoute.xml");
         assert resource != null : "Cannot find barInterceptorRoute.xml";

Reply via email to