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

davsclaus pushed a commit to branch fix/CAMEL-24976
in repository https://gitbox.apache.org/repos/asf/camel.git

commit b5fbc66c410215aae688d0aa3e4091c7c86050b4
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Sep 23 21:44:25 2026 +0200

    CAMEL-24976: type converter - converting to a primitive returns the 
matching wrapper type
    
    The fast path returned an Integer or Long value as-is for any primitive
    target, so convertTo(int.class, 5L) returned a Long and callers failed
    with a ClassCastException (or a bean method with an int parameter failed
    with argument type mismatch). tryConvertTo(boolean.class, ...) no longer
    throws when the value cannot be converted, and the last-resort Object
    converter lookup uses the wrapper type for primitive targets.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../impl/converter/CoreTypeConverterRegistry.java  | 22 +++---
 .../converter/CoreTypeConverterRegistryTest.java   | 87 ++++++++++++++++++++++
 2 files changed, 98 insertions(+), 11 deletions(-)

diff --git 
a/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java
 
b/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java
index 736bca82c758..d175c828a593 100644
--- 
a/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java
+++ 
b/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java
@@ -123,9 +123,8 @@ public abstract class CoreTypeConverterRegistry extends 
ServiceSupport implement
                 return (T) parsedBoolean;
             }
         } else if (type.isPrimitive()) {
-            // okay its a wrapper -> primitive then return as-is for some 
common types
-            Class<?> cls = value.getClass();
-            if (cls == Integer.class || cls == Long.class) {
+            // okay its a wrapper -> primitive then return as-is when the 
wrapper matches the primitive type
+            if (isWrapperOfPrimitive(type, value)) {
                 return (T) value;
             }
         } else if (type == String.class) {
@@ -223,10 +222,8 @@ public abstract class CoreTypeConverterRegistry extends 
ServiceSupport implement
                 return (T) value;
             }
             if (type == boolean.class) {
-                // primitive boolean which must return a value so throw 
exception if not possible
-                Object answer = ObjectConverter.toBoolean(value);
-                requireNonNullBoolean(type, value, answer);
-                return (T) answer;
+                // primitive boolean, but as we are only trying then return 
null if not possible
+                return (T) ObjectConverter.toBoolean(value);
             } else if (type == Boolean.class && value instanceof String str) {
                 // String -> Boolean
                 Boolean parsedBoolean = customParseBoolean(str);
@@ -234,9 +231,8 @@ public abstract class CoreTypeConverterRegistry extends 
ServiceSupport implement
                     return (T) parsedBoolean;
                 }
             } else if (type.isPrimitive()) {
-                // okay its a wrapper -> primitive then return as-is for some 
common types
-                Class<?> cls = value.getClass();
-                if (cls == Integer.class || cls == Long.class) {
+                // okay its a wrapper -> primitive then return as-is when the 
wrapper matches the primitive type
+                if (isWrapperOfPrimitive(type, value)) {
                     return (T) value;
                 }
             } else if (type == String.class) {
@@ -268,6 +264,10 @@ public abstract class CoreTypeConverterRegistry extends 
ServiceSupport implement
         return (T) answer;
     }
 
+    private static boolean isWrapperOfPrimitive(Class<?> type, Object value) {
+        return value.getClass() == 
ObjectHelper.convertPrimitiveTypeToWrapperType(type);
+    }
+
     private static <T> void requireNonNullBoolean(Class<T> type, Object value, 
Object answer) {
         if (answer == null) {
             throw new TypeConversionException(
@@ -405,7 +405,7 @@ public abstract class CoreTypeConverterRegistry extends 
ServiceSupport implement
         }
 
         // This is the last resort: if nothing else works, try to find 
something that converts from an Object to the target type
-        final TypeConverter objConverter = converters.get(new 
TypeConvertible<>(Object.class, type));
+        final TypeConverter objConverter = converters.get(new 
TypeConvertible<>(Object.class, aClass));
         if (objConverter != null) {
             converters.put(typeConvertible, objConverter);
             return objConverter.convertTo(type, exchange, value);
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/impl/converter/CoreTypeConverterRegistryTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/impl/converter/CoreTypeConverterRegistryTest.java
new file mode 100644
index 000000000000..cbcdd7cc8d09
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/impl/converter/CoreTypeConverterRegistryTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.impl.converter;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.TypeConverter;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class CoreTypeConverterRegistryTest extends ContextTestSupport {
+
+    @Test
+    public void testConvertWrapperToOtherPrimitive() throws Exception {
+        TypeConverter tc = context.getTypeConverter();
+
+        assertInstanceOf(Integer.class, tc.convertTo(int.class, 5L));
+        assertInstanceOf(Long.class, tc.convertTo(long.class, 5));
+        assertInstanceOf(Double.class, tc.convertTo(double.class, 5));
+        assertInstanceOf(Integer.class, tc.mandatoryConvertTo(int.class, 5L));
+        assertInstanceOf(Long.class, tc.mandatoryConvertTo(long.class, 7));
+        assertInstanceOf(Double.class, tc.tryConvertTo(double.class, 7));
+        assertInstanceOf(Integer.class, tc.tryConvertTo(int.class, 7L));
+
+        int i = tc.convertTo(int.class, 5L);
+        assertEquals(5, i);
+        long l = tc.convertTo(long.class, 5);
+        assertEquals(5L, l);
+
+        // same wrapper type is returned as-is
+        assertInstanceOf(Integer.class, tc.convertTo(int.class, 5));
+        assertInstanceOf(Long.class, tc.convertTo(long.class, 5L));
+    }
+
+    @Test
+    public void testTryConvertToPrimitiveBoolean() {
+        TypeConverter tc = context.getTypeConverter();
+
+        assertNull(tc.tryConvertTo(boolean.class, "abc"));
+        assertNull(tc.tryConvertTo(boolean.class, new Object()));
+        assertEquals(Boolean.TRUE, tc.tryConvertTo(boolean.class, "true"));
+    }
+
+    @Test
+    public void testBeanWithIntParameterAndLongBody() {
+        assertEquals("int:5", template.requestBody("direct:int", 5L));
+        assertEquals("long:5", template.requestBody("direct:long", 5));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:int").bean(MyNumberBean.class, "intArg");
+                from("direct:long").bean(MyNumberBean.class, "longArg");
+            }
+        };
+    }
+
+    public static class MyNumberBean {
+        public String intArg(int x) {
+            return "int:" + x;
+        }
+
+        public String longArg(long x) {
+            return "long:" + x;
+        }
+    }
+}

Reply via email to