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

Reply via email to