This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch CAMEL-26041-object-method-body-hint in repository https://gitbox.apache.org/repos/asf/camel.git
commit a04524399569f0886f380c3d544498bf3311ca73 Author: Claus Ibsen <[email protected]> AuthorDate: Thu Oct 22 22:49:26 2026 +0200 CAMEL-26041: camel-bean - a call of equals on the body says how to compare, not to unmarshal the body Camel does not call equals, hashCode and the other methods of java.lang.Object on a bean, so ${body.equals('x')} fails for any body. Since CAMEL-25749 the error added the body hint for a body that is still the payload, and so told to unmarshal a String body to read its fields. The error now says that Camel does not call the method, and for equals to compare with an operator, such as ${body} == 'value' in simple. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../org/apache/camel/component/bean/BeanInfo.java | 22 +++++++++++++++---- .../apache/camel/language/bean/BeanExpression.java | 25 ++++++++++++++++++++++ .../language/simple/SimpleSyntaxHintsTest.java | 23 ++++++++++++++++++++ 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/components/camel-bean/src/main/java/org/apache/camel/component/bean/BeanInfo.java b/components/camel-bean/src/main/java/org/apache/camel/component/bean/BeanInfo.java index 13a0ae2a22e7..3bb635ab6d69 100644 --- a/components/camel-bean/src/main/java/org/apache/camel/component/bean/BeanInfo.java +++ b/components/camel-bean/src/main/java/org/apache/camel/component/bean/BeanInfo.java @@ -983,6 +983,22 @@ public class BeanInfo { return null; } + /** + * Whether a method of this name is never a candidate for Camel Bean Binding, such as equals and the other methods + * of java.lang.Object. getClass is one too, but an OGNL call of it is invoked on its own. + * + * @param name the name of the method + * @return true if the name is excluded from Camel Bean Binding + */ + public static boolean isExcludedMethodName(String name) { + for (String s : EXCLUDED_METHOD_NAMES) { + if (s.equals(name)) { + return true; + } + } + return false; + } + /** * Validates whether the given method is a valid candidate for Camel Bean Binding. * @@ -993,10 +1009,8 @@ public class BeanInfo { protected boolean isValidMethod(Class<?> clazz, Method method) { // method name must not be in the excluded list String name = method.getName(); - for (String s : EXCLUDED_METHOD_NAMES) { - if (name.equals(s)) { - return false; - } + if (isExcludedMethodName(name)) { + return false; } // special for Object where clone is not allowed to be called directly diff --git a/components/camel-bean/src/main/java/org/apache/camel/language/bean/BeanExpression.java b/components/camel-bean/src/main/java/org/apache/camel/language/bean/BeanExpression.java index 1a5e368d6f26..c81504b037c9 100644 --- a/components/camel-bean/src/main/java/org/apache/camel/language/bean/BeanExpression.java +++ b/components/camel-bean/src/main/java/org/apache/camel/language/bean/BeanExpression.java @@ -415,6 +415,24 @@ public class BeanExpression implements Expression, Predicate { return methodName; } + /** + * Why ${body.equals('x')}, ${body.hashCode()} and the other methods Camel does not call on a bean fail: the body is + * not the reason, so the hint says what to write instead of what the body is (CAMEL-26041). Null for another + * method. + */ + private static String excludedMethodHint(String methodName) { + String name = methodName != null ? StringHelper.before(methodName, "(", methodName).trim() : null; + // getClass is excluded from the bean binding, but BeanInfo invokes it on its own + if (name == null || "getClass".equals(name) || !BeanInfo.isExcludedMethodName(name)) { + return null; + } + if ("equals".equals(name)) { + return " (Camel does not call equals on a bean: compare with an operator instead, such as ${body} == 'value'" + + " in simple)"; + } + return " (Camel does not call " + name + " on a bean)"; + } + /** * The bean name, or for an OGNL on the message body (${body.foo()}) what the body is, so the message does not say * "on null". @@ -543,6 +561,13 @@ public class BeanExpression implements Expression, Predicate { return value; } String method = methodHint(methodName, resultExchange.getException()); + String excluded = excludedMethodHint(methodName); + if (excluded != null) { + // ${body.equals('x')}: no bean binding calls it, whatever the body is (CAMEL-26041) + throw new RuntimeBeanExpressionException( + exchange, describeBean(holder, beanName, exchange), method + excluded, + resultExchange.getException()); + } Object bean = beanOf(holder, exchange); String body = bodyHint(exchange, bean); String list = body == null ? listHint(bean, methodName) : null; diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleSyntaxHintsTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleSyntaxHintsTest.java index fcc8ac4567aa..9e9946b61c4e 100644 --- a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleSyntaxHintsTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleSyntaxHintsTest.java @@ -220,6 +220,29 @@ public class SimpleSyntaxHintsTest extends ExchangeTestSupport { .contains("it has no size and can be read once: split it, or have the producer give a List"); } + @Test + public void testEqualsOnTheBodySaysHowToCompare() { + // CAMEL-26041: Camel does not call equals on a bean, so the text of the body is not why it fails + exchange.getIn().setBody("test-value-2"); + Exception e = assertThrows(Exception.class, + () -> context.resolveLanguage("simple").createExpression("${body.equals('test-value-2')}") + .evaluate(exchange, String.class)); + assertThat(e.getMessage()).contains("equals('test-value-2') (Camel does not call equals on a bean: compare with an" + + " operator instead, such as ${body} == 'value' in simple)") + .doesNotContain("unmarshal"); + } + + @Test + public void testHashCodeOnAListIsNotAMissingElement() { + // CAMEL-26041: nor is the body a List without such a method or key + exchange.getIn().setBody(List.of("A1", "A2")); + Exception e = assertThrows(Exception.class, + () -> context.resolveLanguage("simple").createExpression("${body.hashCode()}").evaluate(exchange, + String.class)); + assertThat(e.getMessage()).contains("hashCode() (Camel does not call hashCode on a bean)") + .doesNotContain("a List has no method"); + } + @Test public void testKeyOnTheFileAsReadSaysToUnmarshal() { // CAMEL-25749: ${body[status]} on the file consumer's body, before any unmarshal
