This is an automated email from the ASF dual-hosted git repository.
Croway 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 1012de47aabf CAMEL-24553: camel-support: OOM guard in getMandatoryBody
for huge body-to-String conversion (#25871)
1012de47aabf is described below
commit 1012de47aabf76f9bdfc161b7a68b5ee42142513
Author: mayurbm <[email protected]>
AuthorDate: Fri Aug 28 21:49:24 2026 +0530
CAMEL-24553: camel-support: OOM guard in getMandatoryBody for huge
body-to-String conversion (#25871)
* CAMEL-24553: camel-support: OOM guard in getMandatoryBody for huge
body-to-String conversion
MessageSupport.getMandatoryBody(Class) now refuses to convert bodies whose
known length exceeds the in-memory conversion limit (default 256 MiB) to
bulk
types (String, byte[], CharSequence). This prevents the JVM dying with
OutOfMemoryError when the Splitter/Bean path calls
getMandatoryBody(String.class)
on a multi-GB payload.
The check uses the already-known body length (CharSequence.length(),
byte[].length, StreamCache.length()) with no allocation, so there is zero
overhead for normal-sized payloads. When triggered, a clear
InvalidPayloadException with a diagnostic message is thrown instead.
The cap is overridable via system property camel.message.max-in-memory-body
(value in bytes, default 256 * 1024 * 1024).
Add MessageSupportOversizedBodyTest with 6 tests covering: below cap passes,
at cap passes, String over cap refused, byte[] over cap refused, non-bulk
type
not guarded, system property override works.
Co-authored-by: Claude Sonnet 4.5 <[email protected]>
* CAMEL-24553: camel-api/camel-support: OOM guard v2 — safe
TypeConversionException + wouldMaterializeHugeBulk
Address @Croway review: stop TypeConversionException.createMessage() from
calling toString() on the value (which can OOM on huge payloads). Use
safeValueDescription() that shows type name + identity for unknown types,
truncated preview for CharSequence, and safe descriptions for byte[],
File, Path, ByteBuffer.
Rework MessageSupport.getMandatoryBody() per v2 design:
- wouldMaterializeHugeBulk() only fires when conversion would actually
allocate (same-type bodies pass through unchanged — heap already paid)
- Richer length detection: CharSequence, byte[], ByteBuffer, StreamCache,
File, Path, CamelFileLength/Content-Length headers, GenericFile duck-type
- Cap hierarchy: exchange property CamelConvertMaxBytes → context global
option → system property camel.convert.max-bytes → 16 MiB default
Add 12 tests covering: same-type pass-through, cross-type guard, cap
hierarchy (exchange property, context global option), size in message,
and TypeConversionException toString() regression tests.
Co-authored-by: Claude Sonnet 4.5 <[email protected]>
* CAMEL-24553: camel-api: stop TypeConversionException from calling
toString() on body
TypeConversionException.createMessage() previously concatenated the value
via string concatenation (+ value), which calls toString() on unknown types.
For huge message bodies this causes OutOfMemoryError before any exception
is even thrown.
Remove the value from the message entirely. The value remains accessible
via getValue() for callers that need it. Change the message format from:
"...with value <value> due to..."
to:
"...due to..."
Add createMessage_doesNotCallToStringOnValue regression test to
TypeConversionExceptionMessageTest: the body's toString() throws
AssertionError if called, proving the fix holds.
Update DefaultComponentTest to match the new message format (no "with
value").
Remove MessageSupportOversizedBodyTest (MessageSupport guard removed per
Croway review — scope limited to TypeConversionException fix only).
Co-authored-by: Claude Sonnet 4.5 <[email protected]>
---------
Co-authored-by: mayurmohan <[email protected]>
Co-authored-by: Claude Sonnet 4.5 <[email protected]>
---
.../org/apache/camel/TypeConversionException.java | 8 +++++---
.../camel/TypeConversionExceptionMessageTest.java | 22 +++++++++++++++++++++-
.../apache/camel/impl/DefaultComponentTest.java | 2 +-
3 files changed, 27 insertions(+), 5 deletions(-)
diff --git
a/core/camel-api/src/main/java/org/apache/camel/TypeConversionException.java
b/core/camel-api/src/main/java/org/apache/camel/TypeConversionException.java
index 1ada45cecf43..a72960c7a6d6 100644
--- a/core/camel-api/src/main/java/org/apache/camel/TypeConversionException.java
+++ b/core/camel-api/src/main/java/org/apache/camel/TypeConversionException.java
@@ -70,14 +70,16 @@ public class TypeConversionException extends
RuntimeCamelException {
}
/**
- * Returns an error message for type conversion failed.
+ * Returns an error message for type conversion failed. The value itself
is intentionally omitted from the message
+ * to avoid calling {@code toString()} on potentially huge or sensitive
message bodies; the value remains accessible
+ * via {@link #getValue()}.
*/
public static String createMessage(@Nullable Object value, Class<?> type,
Throwable cause) {
Objects.requireNonNull(type, "type");
Objects.requireNonNull(cause, "cause");
return "Error during type conversion from type: " + typeName(value !=
null ? value.getClass() : null)
- + " to the required type: " + typeName(type) + " with value " +
value + " due to "
- + cause.getClass().getName() + ": " + cause.getMessage();
+ + " to the required type: " + typeName(type)
+ + " due to " + cause.getClass().getName() + ": " +
cause.getMessage();
}
/**
diff --git
a/core/camel-core/src/test/java/org/apache/camel/TypeConversionExceptionMessageTest.java
b/core/camel-core/src/test/java/org/apache/camel/TypeConversionExceptionMessageTest.java
index 13d70ecf3f43..d1a3f1db2ee9 100644
---
a/core/camel-core/src/test/java/org/apache/camel/TypeConversionExceptionMessageTest.java
+++
b/core/camel-core/src/test/java/org/apache/camel/TypeConversionExceptionMessageTest.java
@@ -22,7 +22,8 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* Verifies {@link TypeConversionException#createMessage} handles anonymous
and local classes whose
- * {@link Class#getCanonicalName()} returns {@code null}.
+ * {@link Class#getCanonicalName()} returns {@code null}, and that the message
never calls {@code toString()} on the
+ * value (which could OOM for huge payloads).
*
* <p>
* Real-world trigger: SFTP stream body is an anonymous inner class ({@code
ChannelSftp$2}); its canonical name is
@@ -66,4 +67,23 @@ class TypeConversionExceptionMessageTest {
.contains("from type: null")
.contains("to the required type: java.lang.String");
}
+
+ @Test
+ void createMessage_doesNotCallToStringOnValue() {
+ // Fails immediately if exception construction invokes body.toString()
+ Object body = new Object() {
+ @Override
+ public String toString() {
+ throw new AssertionError("TypeConversionException must not
call toString() on the body value");
+ }
+ };
+
+ TypeConversionException exception = new TypeConversionException(body,
String.class, new RuntimeException("cause"));
+
+ assertThat(exception.getValue()).isSameAs(body);
+ assertThat(exception.getMessage())
+ .contains(body.getClass().getName())
+ .contains("cause")
+ .doesNotContain("with value");
+ }
}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/impl/DefaultComponentTest.java
b/core/camel-core/src/test/java/org/apache/camel/impl/DefaultComponentTest.java
index 54ace772ad9d..33c097196123 100644
---
a/core/camel-core/src/test/java/org/apache/camel/impl/DefaultComponentTest.java
+++
b/core/camel-core/src/test/java/org/apache/camel/impl/DefaultComponentTest.java
@@ -129,7 +129,7 @@ public class DefaultComponentTest extends
ContextTestSupport {
assertEquals(
"Error during type conversion from type: java.lang.String " +
"to the required type: java.lang.Integer "
- + "with value abc due to java.lang.NumberFormatException:
For input string: \"abc\"",
+ + "due to java.lang.NumberFormatException: For input
string: \"abc\"",
ex.getMessage());
}