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