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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 2b84cf0534a8 CAMEL-26589: Fix ObjectHelper.loadClass() failing on JVM 
internal array binary names like [B (#29168)
2b84cf0534a8 is described below

commit 2b84cf0534a880d2959e4d726294f7f0ac68893b
Author: Guillaume Nodet <[email protected]>
AuthorDate: Mon Nov 9 10:33:03 2026 +0100

    CAMEL-26589: Fix ObjectHelper.loadClass() failing on JVM internal array 
binary names like [B (#29168)
    
    * CAMEL-26589: Fix ObjectHelper.loadClass() failing on JVM internal arr…
    * CAMEL-26589: Simplify array binary name handling via StringHelper.rea…
---
 .../transformer/TransformerContractTest.java       | 63 ++++++++++++++++++++++
 .../java/org/apache/camel/util/ObjectHelper.java   |  3 ++
 .../camel/util/ObjectHelperLoadClassTest.java      | 54 ++++++++++++++++++-
 3 files changed, 119 insertions(+), 1 deletion(-)

diff --git 
a/core/camel-core/src/test/java/org/apache/camel/processor/transformer/TransformerContractTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/processor/transformer/TransformerContractTest.java
index 0954e1f3b894..faa44365c5d8 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/processor/transformer/TransformerContractTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/processor/transformer/TransformerContractTest.java
@@ -36,6 +36,7 @@ import org.apache.camel.support.DefaultDataFormat;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
 
 public class TransformerContractTest extends ContextTestSupport {
 
@@ -44,6 +45,51 @@ public class TransformerContractTest extends 
ContextTestSupport {
         return false;
     }
 
+    /**
+     * CAMEL-26589: When a byte[] body flows through a route with a Java type 
contract, ContractAdvice calls
+     * TypeConverterTransformer which calls 
classResolver.resolveMandatoryClass("[B"). Before the fix,
+     * ObjectHelper.loadClass("[B") throws ClassNotFoundException because 
ClassLoader.loadClass cannot resolve JVM
+     * internal binary names.
+     * <p>
+     * This test uses inputType(String.class) with a byte[] body: the body is 
tagged java:[B by setBody(), and
+     * ContractAdvice must resolve "[B" to convert it to String. That path hit 
the bug on main.
+     */
+    @Test
+    void testByteArrayBodyConvertedToStringInputType() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() {
+                
from("direct:bytesIn").inputType(String.class).to("mock:bytesIn");
+            }
+        });
+        context.start();
+
+        MockEndpoint mock = context.getEndpoint("mock:bytesIn", 
MockEndpoint.class);
+        mock.expectedBodiesReceived("hello");
+        template.sendBody("direct:bytesIn", "hello".getBytes());
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    /**
+     * CAMEL-26589: A route with outputType declared as a non-Java scheme and 
a byte[] body also hits the bug.
+     * ContractAdvice calls convertIfRequired(message, from=java:[B) which 
triggers
+     * classResolver.resolveMandatoryClass("[B").
+     */
+    @Test
+    void testByteArrayBodyWithNonJavaOutputType() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() {
+                
transformer().scheme("application-octet-stream").withDataFormat(new 
BytesDataFormatDefinition());
+                
from("direct:bytes").outputType("application-octet-stream").to("mock:bytes");
+            }
+        });
+        context.start();
+
+        Exchange out = template.send("direct:bytes", e -> 
e.getMessage().setBody("hello".getBytes()));
+        assertNull(out.getException());
+    }
+
     @Test
     public void testInputTypeOnly() throws Exception {
         context.getTypeConverterRegistry().addTypeConverters(new 
MyTypeConverters());
@@ -127,6 +173,23 @@ public class TransformerContractTest extends 
ContextTestSupport {
         }
     }
 
+    public static class BytesDataFormatDefinition extends DataFormatDefinition 
{
+        public BytesDataFormatDefinition() {
+            super(new DefaultDataFormat() {
+                @Override
+                public void marshal(Exchange exchange, Object graph, 
OutputStream stream) throws Exception {
+                    byte[] bytes = 
exchange.getContext().getTypeConverter().mandatoryConvertTo(byte[].class, 
graph);
+                    stream.write(bytes);
+                }
+
+                @Override
+                public Object unmarshal(Exchange exchange, InputStream stream) 
throws Exception {
+                    return stream.readAllBytes();
+                }
+            });
+        }
+    }
+
     public static class MyDataFormatDefinition extends DataFormatDefinition {
 
         public MyDataFormatDefinition() {
diff --git 
a/core/camel-util/src/main/java/org/apache/camel/util/ObjectHelper.java 
b/core/camel-util/src/main/java/org/apache/camel/util/ObjectHelper.java
index 51744bd5c5a0..9cc98abd6488 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/ObjectHelper.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/ObjectHelper.java
@@ -439,6 +439,9 @@ public final class ObjectHelper {
         if (ObjectHelper.isEmpty(name)) {
             return null;
         }
+        // Convert JVM internal binary names (e.g. [B, [[I, 
[Ljava.lang.String;) to human-readable form
+        // (byte[], int[][], java.lang.String[]) so the existing 
loadSimpleType + dimension-stripping logic handles them.
+        name = StringHelper.readableClassName(name);
 
         int dimensions = 0;
 
diff --git 
a/core/camel-util/src/test/java/org/apache/camel/util/ObjectHelperLoadClassTest.java
 
b/core/camel-util/src/test/java/org/apache/camel/util/ObjectHelperLoadClassTest.java
index 2aac5bd77452..fc46e0e31554 100644
--- 
a/core/camel-util/src/test/java/org/apache/camel/util/ObjectHelperLoadClassTest.java
+++ 
b/core/camel-util/src/test/java/org/apache/camel/util/ObjectHelperLoadClassTest.java
@@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test;
 public class ObjectHelperLoadClassTest {
 
     @Test
-    public void testLoadClass() {
+    void testLoadClass() {
         Class<?> clazz = ObjectHelper.loadClass("int");
         Assertions.assertEquals("int", clazz.getName());
 
@@ -40,4 +40,56 @@ public class ObjectHelperLoadClassTest {
         Assertions.assertTrue(clazz.isArray());
     }
 
+    /**
+     * CAMEL-26589: ObjectHelper.loadClass must resolve JVM internal binary 
names for array types such as {@code [B}
+     * (byte[]), {@code [I} (int[]), and {@code [Ljava.lang.String;} 
(String[]). These names are produced by
+     * {@link Class#getName()} on array types and appear in DataType when a 
byte[] body is tagged with
+     * {@code new DataType(body.getClass())}.
+     */
+    @Test
+    void testLoadClassArrayBinaryNames() {
+        // Primitive array binary names
+        Class<?> clazz = ObjectHelper.loadClass("[B");
+        Assertions.assertNotNull(clazz, "[B should resolve to byte[]");
+        Assertions.assertEquals(byte[].class, clazz);
+
+        clazz = ObjectHelper.loadClass("[S");
+        Assertions.assertEquals(short[].class, clazz);
+
+        clazz = ObjectHelper.loadClass("[I");
+        Assertions.assertEquals(int[].class, clazz);
+
+        clazz = ObjectHelper.loadClass("[J");
+        Assertions.assertEquals(long[].class, clazz);
+
+        clazz = ObjectHelper.loadClass("[F");
+        Assertions.assertEquals(float[].class, clazz);
+
+        clazz = ObjectHelper.loadClass("[D");
+        Assertions.assertEquals(double[].class, clazz);
+
+        clazz = ObjectHelper.loadClass("[Z");
+        Assertions.assertEquals(boolean[].class, clazz);
+
+        clazz = ObjectHelper.loadClass("[C");
+        Assertions.assertEquals(char[].class, clazz);
+
+        // Object array binary name: [Ljava.lang.String;
+        // readableClassName() converts it to "java.lang.String[]" after 
normalizeClassName strips ";".
+        clazz = ObjectHelper.loadClass("[Ljava.lang.String;");
+        Assertions.assertNotNull(clazz, "[Ljava.lang.String; should resolve to 
String[]");
+        Assertions.assertEquals(String[].class, clazz);
+
+        // POJO array binary name: [Lorg.apache.camel.util.MyPojo;
+        clazz = ObjectHelper.loadClass("[Lorg.apache.camel.util.MyPojo;");
+        Assertions.assertNotNull(clazz, "[Lorg.apache.camel.util.MyPojo; 
should resolve to MyPojo[]");
+        Assertions.assertTrue(clazz.isArray());
+        Assertions.assertEquals(MyPojo.class, clazz.getComponentType());
+
+        // Multi-dimensional array: [[B (byte[][])
+        clazz = ObjectHelper.loadClass("[[B");
+        Assertions.assertNotNull(clazz, "[[B should resolve to byte[][]");
+        Assertions.assertEquals(byte[][].class, clazz);
+    }
+
 }

Reply via email to