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; + } + } +}
