Skip to content

CAMEL-24406: camel-xml-jaxp: add prolog guard to XmlConverter.toDOMDocument to avoid SAXParseException for non-XML content - #25555

Open
mayurbm wants to merge 2 commits into
apache:mainfrom
mayurbm:fix/camel-xml-jaxp-prolog-guard
Open

CAMEL-24406: camel-xml-jaxp: add prolog guard to XmlConverter.toDOMDocument to avoid SAXParseException for non-XML content#25555
mayurbm wants to merge 2 commits into
apache:mainfrom
mayurbm:fix/camel-xml-jaxp-prolog-guard

Conversation

@mayurbm

@mayurbm mayurbm commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes CAMEL-24406.

XmlConverter.toDOMDocument(byte[], Exchange) and toDOMDocument(StreamCache, Exchange) passed content directly to DocumentBuilder.parse() without checking whether it could plausibly be XML. Non-XML content (empty body, JSON error response, plain-text HTTP response, BOM-only) triggered a SAXParseException deep 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 two toDOMDocument overloads. When content cannot be XML, throw TypeConversionException immediately — before DocumentBuilder.parse() is called — so:

  • No DOM allocation occurs
  • The Camel error handler fires with a clear, diagnosable message
  • The exchange fails explicitly (not silently with a null body)

Changes

  • XmlConverter.javatoDOMDocument(byte[], Exchange) and toDOMDocument(StreamCache, Exchange): add looksLikeXml guard, throw TypeConversionException for non-XML; add looksLikeXml(byte[]) static helper
  • XmlConverterPrologTest.java (new) — 16 tests: 13 unit tests for looksLikeXml + 3 integration tests asserting TypeConversionException is thrown for empty, JSON, plain-text payloads

Test Results

AI Attribution

This contribution was developed with AI assistance using Claude Code.

…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>
@mayurbm

mayurbm commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Priority Justification

This is not just a cosmetic exception-message improvement. The impact under real production load is significantly worse than it appears:

Why this deserves high priority

1. 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>
@mayurbm

mayurbm commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Update: switched from return null to throw TypeConversionException

The initial implementation used @Converter(allowNull = true) and returned null for non-XML content. This was incorrect — returning null lets the exchange continue with a null body, causing a silent NullPointerException or data loss downstream instead of a clean, diagnosable failure.

Updated approach: throw TypeConversionException immediately when the prolog check fails. This:

  • Fires the Camel error handler with a clear message
  • Avoids DOM allocation entirely (check runs before DocumentBuilder.parse() is called)
  • Produces a directly actionable log entry:
Error during type conversion from type: byte[] to the required type: org.w3c.dom.Document
Payload does not start with a valid XML prolog
(possible causes: empty body, JSON/HTML error response, wrong encoding)

Local validation after update:

  • mvn formatter:format impsort:sort — no changes needed
  • Tests run: 16, Failures: 0, Errors: 0, Skipped: 0 [JDK 21 / Maven 3.9]
    • 13 unit tests for looksLikeXml()
    • 3 integration tests asserting TypeConversionException is thrown for empty, JSON, plain-text payloads

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants