CAMEL-24406: camel-xml-jaxp: add prolog guard to XmlConverter.toDOMDocument to avoid SAXParseException for non-XML content - #25555
Conversation
…id SAXParseException for non-XML content
When a ByteArrayInputStreamCache (or byte[]) carrying a non-XML response
body (empty, JSON, plain-text HTTP error, BOM-only) is passed to
toDOMDocument, the JDK XML parser throws:
SAXParseException: Content is not allowed in prolog.
Seen in practice via:
XmlConverter.toDOMDocument(StreamCache, Exchange)
<- CxfPayloadConverter.convertTo()
<- DefaultCxfBinding.getBodyFromCamel()
Fix: add a cheap static looksLikeXml(byte[]) helper that checks only the
first few bytes (handles UTF-8/UTF-16 BOMs and leading whitespace) and
returns null from toDOMDocument(byte[], Exchange) and
toDOMDocument(StreamCache, Exchange) when content cannot be XML.
The @converter(allowNull=true) annotation tells the Camel type-converter
framework that null is a valid non-match result, so it falls through to
the next converter gracefully instead of logging an error.
Add XmlConverterPrologTest with 13 tests covering null, empty, JSON,
plain-text, BOM-only, UTF-8/UTF-16 BOM variants, and valid XML cases.
Co-authored-by: Claude <claude@anthropic.com>
Priority JustificationThis is not just a cosmetic exception-message improvement. The impact under real production load is significantly worse than it appears: Why this deserves high priority1. Expensive DOM construction before failure Invalid / non-XML content does not just produce a clean exception — it forces expensive DOM construction first. The JDK XML parser allocates internal buffers, parser state, and partial DOM nodes before it reaches the prolog and throws. Every failed conversion has already consumed heap memory proportional to the payload size. 2. Retries multiply the cost Retries multiply the cost because the same stream objects are rebuilt on every attempt. A single bad payload hitting a retry-enabled route or a CXF fault path can trigger 3–5 full parse attempts, each allocating and immediately abandoning the same DOM scaffolding. 3. Heap and GC pressure under load Under load or with large bad payloads this becomes a significant heap-memory and GC problem, even though the final outcome is always a parse failure. In high-throughput CXF/SOAP services receiving occasional non-XML error responses (upstream 5xx HTML pages, JSON fault bodies), this pattern can saturate the old-gen heap and trigger full GC pauses or OutOfMemoryError — all for payloads that were never going to parse successfully. in our one production landscape 5 million such errors seen with different tenants. Mitigation (until this fix is merged)Perform a cheap prefix + size check on the raw / before any type conversion or CXF binding runs. This rejects the payload early and avoids the DOM allocation entirely: The fix in this PR moves exactly this check into itself, making it automatic for all routes without requiring any application-level workaround. |
…ll for non-XML prolog Replace the earlier null-return approach (allowNull=true) with an explicit TypeConversionException when the prolog guard detects content that cannot be XML. Returning null lets the exchange continue with a null body, causing a silent NullPointerException or data loss downstream. Throwing TypeConversionException fires the Camel error handler immediately with a clear, diagnosable message. Also adds TypeConversionException import and three integration tests that assert the exception is thrown (not null returned) for empty body, JSON and plain-text HTTP payloads. Local validation: - mvn formatter:format impsort:sort -- no changes needed - Tests run: 16, Failures: 0, Errors: 0, Skipped: 0 [JDK 21 / Maven 3.9] Co-authored-by: Claude <claude@anthropic.com>
Update: switched from
|
Summary
Fixes CAMEL-24406.
XmlConverter.toDOMDocument(byte[], Exchange)andtoDOMDocument(StreamCache, Exchange)passed content directly toDocumentBuilder.parse()without checking whether it could plausibly be XML. Non-XML content (empty body, JSON error response, plain-text HTTP response, BOM-only) triggered aSAXParseExceptiondeep inside the JDK parser with expensive DOM allocation first.JIRA
CAMEL-24406
Fix
Add a cheap static
looksLikeXml(byte[])helper (handles UTF-8/UTF-16 BOMs and leading whitespace) and guard the twotoDOMDocumentoverloads. When content cannot be XML, throwTypeConversionExceptionimmediately — beforeDocumentBuilder.parse()is called — so:Changes
XmlConverter.java—toDOMDocument(byte[], Exchange)andtoDOMDocument(StreamCache, Exchange): addlooksLikeXmlguard, throwTypeConversionExceptionfor non-XML; addlooksLikeXml(byte[])static helperXmlConverterPrologTest.java(new) — 16 tests: 13 unit tests forlooksLikeXml+ 3 integration tests assertingTypeConversionExceptionis thrown for empty, JSON, plain-text payloadsTest Results
AI Attribution
This contribution was developed with AI assistance using Claude Code.