[
https://issues.apache.org/jira/browse/CAMEL-24398?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18106473#comment-18106473
]
Deepak commented on CAMEL-24398:
--------------------------------
Hi [~atiaomar2025], Thanks a lot for your contribution. just checking if
there’s any update on the PR for the Camel bug changes. Please let me know if
you need any additional input from my side or if there’s anything I can help
with to move it forward. Thanks!
> [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)