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 82e48cc5fb6462eb900a4a60738a4cb3e8f88bb9 Author: Claus Ibsen <[email protected]> AuthorDate: Wed Sep 23 21:51:05 2026 +0200 CAMEL-24976: type converter - fix smaller bugs in core converters - String to TimeUnit supports MICROSECONDS - String to ByteBuffer uses the charset from the exchange (header or property) like the other converters, instead of only the property - converting an array to a Set no longer returns a List - enum conversion prefers an exact match over a case-insensitive match - converting a Future restores the interrupted flag when interrupted Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../apache/camel/converter/DateTimeConverter.java | 1 + .../org/apache/camel/converter/NIOConverter.java | 23 ++-------------------- .../camel/impl/converter/ArrayTypeConverter.java | 8 +++++--- .../camel/impl/converter/EnumTypeConverter.java | 7 +++++++ .../camel/impl/converter/FutureTypeConverter.java | 3 +++ .../camel/converter/DateTimeConverterTest.java | 1 + .../apache/camel/converter/EnumConverterTest.java | 10 ++++++++++ .../camel/converter/FutureConverterTest.java | 19 ++++++++++++++++++ .../apache/camel/converter/NIOConverterTest.java | 10 ++++++++++ .../converter/CoreTypeConverterRegistryTest.java | 13 ++++++++++++ 10 files changed, 71 insertions(+), 24 deletions(-) diff --git a/core/camel-base/src/main/java/org/apache/camel/converter/DateTimeConverter.java b/core/camel-base/src/main/java/org/apache/camel/converter/DateTimeConverter.java index b1714a9d17a5..e81c133000c4 100644 --- a/core/camel-base/src/main/java/org/apache/camel/converter/DateTimeConverter.java +++ b/core/camel-base/src/main/java/org/apache/camel/converter/DateTimeConverter.java @@ -59,6 +59,7 @@ public final class DateTimeConverter { case "MINUTES" -> TimeUnit.MINUTES; case "SECONDS" -> TimeUnit.SECONDS; case "MILLISECONDS" -> TimeUnit.MILLISECONDS; + case "MICROSECONDS" -> TimeUnit.MICROSECONDS; case "NANOSECONDS" -> TimeUnit.NANOSECONDS; default -> throw new IllegalStateException("Unexpected value: " + unit); }; diff --git a/core/camel-base/src/main/java/org/apache/camel/converter/NIOConverter.java b/core/camel-base/src/main/java/org/apache/camel/converter/NIOConverter.java index 55eb58eef253..5dd054764ecc 100644 --- a/core/camel-base/src/main/java/org/apache/camel/converter/NIOConverter.java +++ b/core/camel-base/src/main/java/org/apache/camel/converter/NIOConverter.java @@ -20,16 +20,13 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Path; import org.apache.camel.Converter; import org.apache.camel.Exchange; -import org.apache.camel.ExchangePropertyKey; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.camel.support.ExchangeHelper; import static org.apache.camel.util.BufferCaster.cast; @@ -39,8 +36,6 @@ import static org.apache.camel.util.BufferCaster.cast; @Converter(generateBulkLoader = true) public final class NIOConverter { - private static final Logger LOG = LoggerFactory.getLogger(NIOConverter.class); - /** * Utility classes should not have a public constructor. */ @@ -88,21 +83,7 @@ public final class NIOConverter { @Converter(order = 7) public static ByteBuffer toByteBuffer(String value, Exchange exchange) { - byte[] bytes = null; - if (exchange != null) { - String charsetName = exchange.getProperty(ExchangePropertyKey.CHARSET_NAME, String.class); - if (charsetName != null) { - try { - bytes = value.getBytes(charsetName); - } catch (UnsupportedEncodingException e) { - LOG.warn("Cannot convert the byte to String with the charset {}", charsetName, e); - } - } - } - if (bytes == null) { - bytes = value.getBytes(); - } - return ByteBuffer.wrap(bytes); + return ByteBuffer.wrap(value.getBytes(ExchangeHelper.getCharset(exchange))); } @Converter(order = 8) diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/converter/ArrayTypeConverter.java b/core/camel-base/src/main/java/org/apache/camel/impl/converter/ArrayTypeConverter.java index 4716d0c62e3d..b053bed4ff52 100644 --- a/core/camel-base/src/main/java/org/apache/camel/impl/converter/ArrayTypeConverter.java +++ b/core/camel-base/src/main/java/org/apache/camel/impl/converter/ArrayTypeConverter.java @@ -54,10 +54,12 @@ public class ArrayTypeConverter extends TypeConverterSupport { } return (T) answer; } - } else if (Collection.class.isAssignableFrom(type)) { + } else if (Collection.class.isAssignableFrom(type) && type.isAssignableFrom(ArrayList.class)) { + // only for collection types a list can be assigned to (such as not a Set) if (value != null) { - if (value instanceof Object[]) { - return (T) Arrays.asList((Object[]) value); + if (value instanceof Object[] arr) { + List<Object> list = Arrays.asList(arr); + return (T) (type.isInstance(list) ? list : new ArrayList<>(list)); } else if (value.getClass().isArray()) { int size = Array.getLength(value); List<Object> answer = new ArrayList<>(size); diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/converter/EnumTypeConverter.java b/core/camel-base/src/main/java/org/apache/camel/impl/converter/EnumTypeConverter.java index 0be4f5ef0727..432f7f920fb0 100644 --- a/core/camel-base/src/main/java/org/apache/camel/impl/converter/EnumTypeConverter.java +++ b/core/camel-base/src/main/java/org/apache/camel/impl/converter/EnumTypeConverter.java @@ -72,6 +72,13 @@ public class EnumTypeConverter extends TypeConverterSupport implements CamelCont String text = value.toString().trim(); Class<Enum<?>> enumClass = (Class<Enum<?>>) type; + // prefer an exact match + for (Enum<?> enumValue : enumClass.getEnumConstants()) { + if (enumValue.name().equals(text)) { + return type.cast(enumValue); + } + } + // we want to match case insensitive for enums for (Enum<?> enumValue : enumClass.getEnumConstants()) { if (enumValue.name().equalsIgnoreCase(text)) { diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/converter/FutureTypeConverter.java b/core/camel-base/src/main/java/org/apache/camel/impl/converter/FutureTypeConverter.java index 9da2fc178c4b..26ca7c8ee0be 100644 --- a/core/camel-base/src/main/java/org/apache/camel/impl/converter/FutureTypeConverter.java +++ b/core/camel-base/src/main/java/org/apache/camel/impl/converter/FutureTypeConverter.java @@ -44,6 +44,9 @@ public final class FutureTypeConverter extends TypeConverterSupport { public <T> T convertTo(Class<T> type, Exchange exchange, Object value) { try { return doConvertTo(type, exchange, value); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new TypeConversionException(value, type, e); } catch (Exception e) { throw new TypeConversionException(value, type, e); } diff --git a/core/camel-core/src/test/java/org/apache/camel/converter/DateTimeConverterTest.java b/core/camel-core/src/test/java/org/apache/camel/converter/DateTimeConverterTest.java index acb2d1e784d2..8c6204d3836b 100644 --- a/core/camel-core/src/test/java/org/apache/camel/converter/DateTimeConverterTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/converter/DateTimeConverterTest.java @@ -59,5 +59,6 @@ public class DateTimeConverterTest extends ContextTestSupport { public void testToTimeUnit() { assertEquals(TimeUnit.DAYS, context.getTypeConverter().convertTo(TimeUnit.class, "DAYS")); assertEquals(TimeUnit.MILLISECONDS, context.getTypeConverter().convertTo(TimeUnit.class, "MILLISECONDS")); + assertEquals(TimeUnit.MICROSECONDS, context.getTypeConverter().convertTo(TimeUnit.class, "MICROSECONDS")); } } diff --git a/core/camel-core/src/test/java/org/apache/camel/converter/EnumConverterTest.java b/core/camel-core/src/test/java/org/apache/camel/converter/EnumConverterTest.java index 9981c70f0f18..ccfaf995daeb 100644 --- a/core/camel-core/src/test/java/org/apache/camel/converter/EnumConverterTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/converter/EnumConverterTest.java @@ -139,4 +139,14 @@ public class EnumConverterTest extends ContextTestSupport { GET_USERS_BY_TOPIC } + @Test + public void testEnumPreferExactMatch() { + assertSame(CaseEnum.a, context.getTypeConverter().convertTo(CaseEnum.class, "a")); + assertSame(CaseEnum.A, context.getTypeConverter().convertTo(CaseEnum.class, "A")); + } + + public enum CaseEnum { + a, + A + } } diff --git a/core/camel-core/src/test/java/org/apache/camel/converter/FutureConverterTest.java b/core/camel-core/src/test/java/org/apache/camel/converter/FutureConverterTest.java index d93a04e19023..a059ea5cbdcc 100644 --- a/core/camel-core/src/test/java/org/apache/camel/converter/FutureConverterTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/converter/FutureConverterTest.java @@ -17,11 +17,13 @@ package org.apache.camel.converter; import java.sql.Timestamp; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import org.apache.camel.ContextTestSupport; import org.apache.camel.Exchange; import org.apache.camel.NoTypeConversionAvailableException; +import org.apache.camel.TypeConversionException; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.support.DefaultExchange; import org.junit.jupiter.api.Test; @@ -30,6 +32,7 @@ import org.junit.jupiter.api.parallel.Isolated; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; @Isolated public class FutureConverterTest extends ContextTestSupport { @@ -113,4 +116,20 @@ public class FutureConverterTest extends ContextTestSupport { } }; } + + @Test + public void testConvertInterruptedFuture() { + Future<String> future = new CompletableFuture<>() { + @Override + public String get() throws InterruptedException { + throw new InterruptedException("Interrupted"); + } + }; + Exchange exchange = new DefaultExchange(context); + + assertThrows(TypeConversionException.class, + () -> context.getTypeConverter().convertTo(String.class, exchange, future)); + // the interrupted flag should be restored (and interrupted() clears it again) + assertTrue(Thread.interrupted()); + } } diff --git a/core/camel-core/src/test/java/org/apache/camel/converter/NIOConverterTest.java b/core/camel-core/src/test/java/org/apache/camel/converter/NIOConverterTest.java index a8bf1349b9de..2246d0e9a181 100644 --- a/core/camel-core/src/test/java/org/apache/camel/converter/NIOConverterTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/converter/NIOConverterTest.java @@ -223,4 +223,14 @@ class NIOConverterTest extends ContextTestSupport { assertEquals("Hello", IOConverter.toString(is, null)); } + @Test + public void testToByteBufferWithCharsetHeader() { + Exchange exchange = context.getEndpoint("direct:start").createExchange(); + exchange.getIn().setHeader(Exchange.CHARSET_NAME, "UTF-16BE"); + + ByteBuffer bb = NIOConverter.toByteBuffer("A", exchange); + assertEquals(2, bb.remaining()); + assertEquals(0, bb.get()); + assertEquals('A', bb.get()); + } } 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 index 35d4144a4b6d..b8e2906c90c0 100644 --- 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 @@ -21,6 +21,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.camel.ContextTestSupport; import org.apache.camel.Exchange; @@ -244,4 +245,16 @@ public class CoreTypeConverterRegistryTest extends ContextTestSupport { return type.cast(new Foo(name)); } } + + @Test + public void testArrayToCollectionTypes() { + TypeConverter tc = context.getTypeConverter(); + + assertInstanceOf(List.class, tc.convertTo(List.class, new String[] { "a", "b" })); + assertInstanceOf(ArrayList.class, tc.convertTo(ArrayList.class, new String[] { "a", "b" })); + assertInstanceOf(List.class, tc.convertTo(List.class, new int[] { 1, 2 })); + // a set is not a list + assertInstanceOf(Set.class, tc.convertTo(Set.class, new String[] { "a", "b" })); + assertNull(tc.convertTo(Set.class, new int[] { 1, 2 })); + } }
