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 2ab7ecb72732c..469905347bd0e 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 void setOutputs(List> outputs) { public void addOutput(ProcessorDefinition output) { this.outputs.add(output); } + + @Override + public void setExpression(ExpressionDefinition expression) { + // Detect when an expression element (e.g. ) 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 (processor) instead of (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 instead of . " + + "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 150e89434e8d2..882ec709678be 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 @@ -57,6 +57,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -449,6 +450,54 @@ public void testParseError() throws Exception { assertTrue(e.getMessage().startsWith("Unexpected attribute '{}ref'")); } + @Test + public void testMethodAfterStepsInWhenClauseShouldFail() throws Exception { + String routesXml = """ + + + + + + ${header.foo} == 'bar' + + + + + + + + + + """; + Exception e = assertThrows(Exception.class, () -> { + new ModelParser(new StringReader(routesXml)).parseRoutesDefinition(); + }); + assertThat(e).hasStackTraceContaining("already has a predicate"); + } + + @Test + public void testMethodAsPredicateInWhenClauseShouldWork() throws Exception { + String routesXml = """ + + + + + + + + + + + + + + + """; + 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";