[
https://issues.apache.org/jira/browse/CAMEL-24398?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Omar Atie reassigned CAMEL-24398:
---------------------------------
Assignee: Omar Atie
> [BUG] DefaultAttachmentMessage.hasAttachments() returns true for messages
> with no attachments in Camel 4.x
> ----------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-24398
> URL: https://issues.apache.org/jira/browse/CAMEL-24398
> Project: Camel
> Issue Type: Bug
> Components: camel-attachments
> Affects Versions: 4.14.2
> Reporter: Deepak
> Assignee: Omar Atie
> Priority: Critical
>
> *Summary*
> hasAttachments() incorrectly returns true on a plain (non-multipart) message
> in Camel 4.x. This is a regression from
> Camel 3.x introduced during the MessageTrait refactor. It causes
> MimeMultipartDataFormat.marshal() to take the
> multipart code path for plain XML messages, corrupting the message body.
> *Component*
> camel-attachments — org.apache.camel.attachment.DefaultAttachmentMessage
> ---
> *Steps to Reproduce*
> 1. Configure an HTTP Sender adapter with a MimeMultipartDataFormat marshal
> step downstream.
> 2. POST a plain XML payload (no multipart, no attachments) to the endpoint.
> 3. Observe that MimeMultipartDataFormat.marshal() takes the multipart path
> instead of writing the body as-is.
> ---
> *Root Cause*
> The MessageTrait refactor in Camel 4.x introduced a lazy-init pattern in
> getAttachmentsMap() that eagerly registers
> the ATTACHMENTS trait with an empty LinkedHashMap the first time any
> attachment-inspection method is called (e.g.
> getAttachments(), getAttachmentNames()). Once registered, hasAttachments()
> returns true because it only checks for
> trait presence — not whether the map is empty.
> *Call chain for a plain XML POST:*
> DefaultHttpBinding.readRequest()
> → readBody()
> → populateAttachments() // called unconditionally for every
> request
> → getMessage(AttachmentMessage.class)
> → AttachmentConverter.toAttachmentMessage()
> → new DefaultAttachmentMessage(message)
> → getAttachmentsMap()
> → setPayloadForTrait(ATTACHMENTS, emptyLinkedHashMap) // ←
> trait registered with empty map
> ... later ...
> MimeMultipartDataFormat.marshal()
> → exchange.getIn(AttachmentMessage.class).hasAttachments()
> → delegate.hasTrait(ATTACHMENTS) // ← returns true! empty map was
> registered above
> → takes multipart path // ← wrong for plain XML
> ---
> *Code Comparison*
> Camel 3.14.7 — DefaultAttachmentMessage.hasAttachments() ✅ Correct
> @Override
> public boolean hasAttachments() {
> Map<String, Attachment> map =
> getExchange().getProperty(ATTACHMENT_OBJECTS, Map.class);
> return map != null && !map.isEmpty(); // checks both null AND emptiness
> }
> Attachments were stored as an Exchange property. Reading the property when
> nothing was added returns null, so
> hasAttachments() correctly returns false.
> ---
> Camel 4.14.2 — DefaultAttachmentMessage.hasAttachments() ❌ Broken
> @Override
> public boolean hasAttachments() {
> return delegate.hasTrait(MessageTrait.ATTACHMENTS); // only checks
> trait key presence
> }
> Combined with the lazy-init in getAttachmentsMap():
> private Map<String, Object> getAttachmentsMap() {
> var m = (Map<String, Object>)
> delegate.getPayloadForTrait(MessageTrait.ATTACHMENTS);
> if (m == null) {
> m = new LinkedHashMap<>();
> delegate.setPayloadForTrait(MessageTrait.ATTACHMENTS, m); //
> registers trait even when empty
> }
> return m;
> }
> Any read-only call to getAttachments(), getAttachmentNames(), or
> getAttachmentObjects() silently registers the
> ATTACHMENTS trait with an empty map, causing all subsequent
> hasAttachments() calls to return true.
> Observed vs Expected
> {*}Expected{*}: hasAttachments() returns false when no attachments have been
> added
> {*}Actual{*}: hasAttachments() returns true after any read-only attachment
> inspection, even with zero attachments
> --------------------
> *Debugger Evidence*
> Exchange traits map at MimeMultipartDataFormat.marshal() breakpoint:
> traits = \{EnumMap size=1}
> MessageTrait.ATTACHMENTS → \{LinkedHashMap size=0} ← trait present, but
> map is EMPTY
--
This message was sent by Atlassian Jira
(v8.20.10#820010)