[ 
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)

Reply via email to