This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch att
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 349043f69d0a2070d1651baa430a845aaef5d009
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Feb 24 11:53:46 2025 +0100

    CAMEL-21755: Adjust attachments API on Message to avoid issue during routing
---
 .../camel/attachment/AttachmentConverter.java      |   2 -
 .../attachment/AttachmentExpressionBuilder.java    | 115 ++++++++++-----------
 .../apache/camel/attachment/AttachmentMessage.java |  21 ++--
 .../camel/attachment/CSimpleAttachmentHelper.java  |  44 ++++----
 .../camel/attachment/DefaultAttachmentMessage.java |  28 +++--
 .../component/jetty12/AttachmentHttpBinding.java   |   7 +-
 .../apache/camel/component/mail/MailConsumer.java  |   8 --
 .../component/mail/SplitAttachmentsExpression.java |   2 +-
 .../component/mail/MailAttachmentNamesTest.java    |   5 +-
 .../component/servlet/AttachmentHttpBinding.java   |   3 +-
 .../undertow/DefaultUndertowHttpBinding.java       |   3 +-
 .../src/main/java/org/apache/camel/Message.java    |   9 +-
 .../apache/camel/trait/message/MessageTrait.java   |   9 +-
 .../org/apache/camel/support/AbstractExchange.java |  11 +-
 .../org/apache/camel/support/DefaultMessage.java   |  21 +---
 .../org/apache/camel/support/MessageSupport.java   |  27 +++--
 .../apache/camel/support/MessageHelperTest.java    |  14 +--
 .../ROOT/pages/camel-4x-upgrade-guide-4_10.adoc    |  20 ++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_11.adoc    |  20 ++++
 19 files changed, 196 insertions(+), 173 deletions(-)

diff --git 
a/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentConverter.java
 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentConverter.java
index 9fcf132a725..bfe3b0e81d4 100644
--- 
a/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentConverter.java
+++ 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentConverter.java
@@ -37,8 +37,6 @@ public final class AttachmentConverter {
             answer = am;
         } else {
             answer = new DefaultAttachmentMessage(message);
-            // need to wrap exchange message as attachment capable
-            message.getExchange().setMessage(answer);
         }
         return answer;
     }
diff --git 
a/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java
 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java
index d86f06e6664..fbdda092088 100644
--- 
a/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java
+++ 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentExpressionBuilder.java
@@ -30,14 +30,21 @@ import static 
org.apache.camel.support.builder.ExpressionBuilder.simpleExpressio
 
 public class AttachmentExpressionBuilder {
 
+    private static AttachmentMessage toAttachmentMessage(Exchange exchange) {
+        AttachmentMessage answer;
+        if (exchange.getMessage() instanceof AttachmentMessage am) {
+            answer = am;
+        } else {
+            answer = new DefaultAttachmentMessage(exchange.getMessage());
+        }
+        return answer;
+    }
+
     public static Expression attachments() {
         return new ExpressionAdapter() {
             @Override
             public Object evaluate(Exchange exchange) {
-                if (exchange.getMessage() instanceof AttachmentMessage am) {
-                    return am.getAttachments();
-                }
-                return null;
+                return toAttachmentMessage(exchange).getAttachments();
             }
         };
     }
@@ -46,10 +53,7 @@ public class AttachmentExpressionBuilder {
         return new ExpressionAdapter() {
             @Override
             public Object evaluate(Exchange exchange) {
-                if (exchange.getMessage() instanceof AttachmentMessage am) {
-                    return am.getAttachments().size();
-                }
-                return 0;
+                return toAttachmentMessage(exchange).getAttachments().size();
             }
         };
     }
@@ -60,20 +64,18 @@ public class AttachmentExpressionBuilder {
 
             @Override
             public Object evaluate(Exchange exchange) {
-                Object answer = null;
-                if (exchange.getMessage() instanceof AttachmentMessage am) {
-                    var dh = am.getAttachment(key);
+                Object answer;
+                var dh = toAttachmentMessage(exchange).getAttachment(key);
+                try {
+                    answer = dh.getContent();
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+                if (answer != null && clazz != null) {
                     try {
-                        answer = dh.getContent();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                    if (answer != null && clazz != null) {
-                        try {
-                            answer = 
exchange.getContext().getTypeConverter().mandatoryConvertTo(clazz, answer);
-                        } catch (NoTypeConversionAvailableException e) {
-                            throw 
CamelExecutionException.wrapCamelExecutionException(exchange, e);
-                        }
+                        answer = 
exchange.getContext().getTypeConverter().mandatoryConvertTo(clazz, answer);
+                    } catch (NoTypeConversionAvailableException e) {
+                        throw 
CamelExecutionException.wrapCamelExecutionException(exchange, e);
                     }
                 }
                 return answer;
@@ -99,16 +101,14 @@ public class AttachmentExpressionBuilder {
             @Override
             public Object evaluate(Exchange exchange) {
                 Object answer = null;
-                if (exchange.getMessage() instanceof AttachmentMessage am) {
-                    var ao = am.getAttachmentObject(key);
-                    if (ao != null) {
-                        answer = ao.getHeader(name);
-                        if (answer != null && clazz != null) {
-                            try {
-                                answer = 
exchange.getContext().getTypeConverter().mandatoryConvertTo(clazz, answer);
-                            } catch (NoTypeConversionAvailableException e) {
-                                throw 
CamelExecutionException.wrapCamelExecutionException(exchange, e);
-                            }
+                var ao = 
toAttachmentMessage(exchange).getAttachmentObject(key);
+                if (ao != null) {
+                    answer = ao.getHeader(name);
+                    if (answer != null && clazz != null) {
+                        try {
+                            answer = 
exchange.getContext().getTypeConverter().mandatoryConvertTo(clazz, answer);
+                        } catch (NoTypeConversionAvailableException e) {
+                            throw 
CamelExecutionException.wrapCamelExecutionException(exchange, e);
                         }
                     }
                 }
@@ -132,11 +132,9 @@ public class AttachmentExpressionBuilder {
         return new ExpressionAdapter() {
             @Override
             public Object evaluate(Exchange exchange) {
-                if (exchange.getMessage() instanceof AttachmentMessage am) {
-                    var dh = am.getAttachment(key);
-                    if (dh != null) {
-                        return dh.getContentType();
-                    }
+                var dh = toAttachmentMessage(exchange).getAttachment(key);
+                if (dh != null) {
+                    return dh.getContentType();
                 }
                 return null;
             }
@@ -164,15 +162,12 @@ public class AttachmentExpressionBuilder {
         return new ExpressionAdapter() {
             @Override
             public Object evaluate(Exchange exchange) {
-                if (exchange.getMessage() instanceof AttachmentMessage am) {
-                    String key = attachmentName.evaluate(exchange, 
String.class);
-                    Object answer = am.getAttachment(key);
-                    if (mandatory && answer == null) {
-                        throw 
RuntimeCamelException.wrapRuntimeCamelException(new 
NoSuchAttachmentException(exchange, key));
-                    }
-                    return answer;
+                String key = attachmentName.evaluate(exchange, String.class);
+                Object answer = 
toAttachmentMessage(exchange).getAttachment(key);
+                if (mandatory && answer == null) {
+                    throw RuntimeCamelException.wrapRuntimeCamelException(new 
NoSuchAttachmentException(exchange, key));
                 }
-                return null;
+                return answer;
             }
 
             @Override
@@ -198,27 +193,25 @@ public class AttachmentExpressionBuilder {
         return new SimpleExpressionBuilder.KeyedOgnlExpressionAdapter(
                 ognl, "attachmentOgnl(" + ognl + ")",
                 (exchange, exp) -> {
-                    if (exchange.getMessage() instanceof AttachmentMessage am) 
{
-                        String text = exp.evaluate(exchange, String.class);
-                        var dh = am.getAttachment(text);
-                        if (dh == null && ObjectHelper.isNumber(text)) {
-                            try {
-                                // fallback to lookup by numeric index
-                                int idx = Integer.parseInt(text);
-                                if (idx < am.getAttachments().size()) {
-                                    var it = 
am.getAttachments().values().iterator();
-                                    for (int i = 0; i < idx; i++) {
-                                        it.next();
-                                    }
-                                    dh = it.next();
+                    String text = exp.evaluate(exchange, String.class);
+                    var am = toAttachmentMessage(exchange);
+                    var dh = am.getAttachment(text);
+                    if (dh == null && ObjectHelper.isNumber(text)) {
+                        try {
+                            // fallback to lookup by numeric index
+                            int idx = Integer.parseInt(text);
+                            if (idx < am.getAttachments().size()) {
+                                var it = 
am.getAttachments().values().iterator();
+                                for (int i = 0; i < idx; i++) {
+                                    it.next();
                                 }
-                            } catch (NumberFormatException e) {
-                                // ignore
+                                dh = it.next();
                             }
+                        } catch (NumberFormatException e) {
+                            // ignore
                         }
-                        return dh;
                     }
-                    return null;
+                    return dh;
                 });
     }
 
diff --git 
a/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentMessage.java
 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentMessage.java
index 44240d265e7..4a910993ddf 100644
--- 
a/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentMessage.java
+++ 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentMessage.java
@@ -28,12 +28,6 @@ import org.apache.camel.Message;
  */
 public interface AttachmentMessage extends Message {
 
-    /**
-     * The {@link AttachmentMessage} will wrap the previous {@link Message} 
and this method gives access to the previous
-     * message instance.
-     */
-    Message getDelegateMessage();
-
     /**
      * Returns the attachment specified by the id
      *
@@ -81,16 +75,20 @@ public interface AttachmentMessage extends Message {
     void addAttachmentObject(String id, Attachment content);
 
     /**
-     * Returns all attachments of the message
+     * Returns all attachments of the message.
+     * <p/>
+     * To add or remove attachments then use the APIs from this message, as 
the returned map is a read-only instance.
      *
-     * @return the attachments in a map or <tt>null</tt>
+     * @return the attachments in a read-only map
      */
     Map<String, DataHandler> getAttachments();
 
     /**
      * Returns all attachments of the message
+     * <p/>
+     * To add or remove attachments then use the APIs from this message, as 
the returned map is a read-only instance.
      *
-     * @return the attachments in a map or <tt>null</tt>
+     * @return the attachments in a read-only map
      */
     Map<String, Attachment> getAttachmentObjects();
 
@@ -115,4 +113,9 @@ public interface AttachmentMessage extends Message {
      */
     boolean hasAttachments();
 
+    /**
+     * Clears all the attachments.
+     */
+    void clearAttachments();
+
 }
diff --git 
a/components/camel-attachments/src/main/java/org/apache/camel/attachment/CSimpleAttachmentHelper.java
 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/CSimpleAttachmentHelper.java
index 1252657eadd..b6caaefb121 100644
--- 
a/components/camel-attachments/src/main/java/org/apache/camel/attachment/CSimpleAttachmentHelper.java
+++ 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/CSimpleAttachmentHelper.java
@@ -24,31 +24,33 @@ import org.apache.camel.Exchange;
 
 public class CSimpleAttachmentHelper {
 
-    public static Map<String, DataHandler> attachments(Exchange exchange) {
+    private static AttachmentMessage toAttachmentMessage(Exchange exchange) {
+        AttachmentMessage answer;
         if (exchange.getMessage() instanceof AttachmentMessage am) {
-            return am.getAttachments();
+            answer = am;
+        } else {
+            answer = new DefaultAttachmentMessage(exchange.getMessage());
         }
-        return null;
+        return answer;
+    }
+
+    public static Map<String, DataHandler> attachments(Exchange exchange) {
+        return toAttachmentMessage(exchange).getAttachments();
     }
 
     public static int attachmentsSize(Exchange exchange) {
-        if (exchange.getMessage() instanceof AttachmentMessage am) {
-            return am.getAttachments().size();
-        }
-        return 0;
+        return toAttachmentMessage(exchange).getAttachments().size();
     }
 
     public static Object attachmentContent(Exchange exchange, String key) 
throws Exception {
-        if (exchange.getMessage() instanceof AttachmentMessage am) {
-            var dh = am.getAttachments().get(key);
-            if (dh != null) {
-                return dh.getContent();
-            }
+        var dh = toAttachmentMessage(exchange).getAttachments().get(key);
+        if (dh != null) {
+            return dh.getContent();
         }
         return null;
     }
 
-    public static Object attachmentContentAsText(Exchange exchange, String 
key) throws Exception {
+    public static String attachmentContentAsText(Exchange exchange, String 
key) throws Exception {
         Object data = attachmentContent(exchange, key);
         if (data != null) {
             return 
exchange.getContext().getTypeConverter().convertTo(String.class, exchange, 
data);
@@ -65,21 +67,17 @@ public class CSimpleAttachmentHelper {
     }
 
     public static String attachmentContentType(Exchange exchange, String key) {
-        if (exchange.getMessage() instanceof AttachmentMessage am) {
-            var dh = am.getAttachments().get(key);
-            if (dh != null) {
-                return dh.getContentType();
-            }
+        var dh = toAttachmentMessage(exchange).getAttachments().get(key);
+        if (dh != null) {
+            return dh.getContentType();
         }
         return null;
     }
 
     public static String attachmentHeader(Exchange exchange, String key, 
String name) {
-        if (exchange.getMessage() instanceof AttachmentMessage am) {
-            var ao = am.getAttachmentObjects().get(key);
-            if (ao != null) {
-                return ao.getHeader(name);
-            }
+        var ao = toAttachmentMessage(exchange).getAttachmentObjects().get(key);
+        if (ao != null) {
+            return ao.getHeader(name);
         }
         return null;
     }
diff --git 
a/components/camel-attachments/src/main/java/org/apache/camel/attachment/DefaultAttachmentMessage.java
 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/DefaultAttachmentMessage.java
index 49fdb908c73..fb31dfb2837 100644
--- 
a/components/camel-attachments/src/main/java/org/apache/camel/attachment/DefaultAttachmentMessage.java
+++ 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/DefaultAttachmentMessage.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.attachment;
 
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
@@ -36,9 +37,13 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
         this.delegate = delegate;
     }
 
-    @Override
-    public Message getDelegateMessage() {
-        return delegate;
+    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);
+        }
+        return m;
     }
 
     @Override
@@ -178,7 +183,7 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
 
     @Override
     public void copyFrom(Message message) {
-
+        delegate.copyFrom(message);
     }
 
     @Override
@@ -227,7 +232,7 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
             Attachment a = (Attachment) att;
             answer.put(id, a.getDataHandler());
         });
-        return answer;
+        return Collections.unmodifiableMap(answer);
     }
 
     @Override
@@ -237,7 +242,7 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
             Attachment a = (Attachment) att;
             answer.put(id, a);
         });
-        return answer;
+        return Collections.unmodifiableMap(answer);
     }
 
     @Override
@@ -252,12 +257,12 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
 
     @Override
     public boolean hasAttachments() {
-        return delegate.hasAttachments();
+        return delegate.hasTrait(MessageTrait.ATTACHMENTS);
     }
 
     @Override
-    public Map<String, Object> getAttachmentsMap() {
-        return delegate.getAttachmentsMap();
+    public void clearAttachments() {
+        delegate.removeTrait(MessageTrait.ATTACHMENTS);
     }
 
     @Override
@@ -274,4 +279,9 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
     public void setPayloadForTrait(MessageTrait trait, Object object) {
         delegate.setPayloadForTrait(trait, object);
     }
+
+    @Override
+    public void removeTrait(MessageTrait trait) {
+        delegate.removeTrait(trait);
+    }
 }
diff --git 
a/components/camel-jetty/src/main/java/org/apache/camel/component/jetty12/AttachmentHttpBinding.java
 
b/components/camel-jetty/src/main/java/org/apache/camel/component/jetty12/AttachmentHttpBinding.java
index c22ea5c1222..6e7597e2320 100644
--- 
a/components/camel-jetty/src/main/java/org/apache/camel/component/jetty12/AttachmentHttpBinding.java
+++ 
b/components/camel-jetty/src/main/java/org/apache/camel/component/jetty12/AttachmentHttpBinding.java
@@ -33,6 +33,7 @@ import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.attachment.Attachment;
 import org.apache.camel.attachment.AttachmentMessage;
 import org.apache.camel.attachment.DefaultAttachment;
+import org.apache.camel.attachment.DefaultAttachmentMessage;
 import org.apache.camel.component.jetty.MultiPartFilter;
 import org.apache.camel.http.common.DefaultHttpBinding;
 import org.apache.camel.http.common.HttpHelper;
@@ -65,7 +66,7 @@ final class AttachmentHttpBinding extends DefaultHttpBinding {
                             attachment.addHeader(headerName, headerValue);
                         }
                     }
-                    AttachmentMessage am = 
message.getExchange().getMessage(AttachmentMessage.class);
+                    AttachmentMessage am = new 
DefaultAttachmentMessage(message);
                     am.addAttachmentObject(part.getName(), attachment);
                     String name = part.getSubmittedFileName();
                     Object value = am.getAttachment(name);
@@ -101,12 +102,12 @@ final class AttachmentHttpBinding extends 
DefaultHttpBinding {
         //        }
 
         // attachment is optional
-        AttachmentMessage am = 
message.getExchange().getMessage(AttachmentMessage.class);
+        AttachmentMessage am = new DefaultAttachmentMessage(message);
 
         Enumeration<?> names = request.getParameterNames();
         while (names.hasMoreElements()) {
             String name = (String) names.nextElement();
-            if (am != null && am.getAttachment(name) != null) {
+            if (am.getAttachment(name) != null) {
                 DataHandler dh = am.getAttachment(name);
                 Object value = dh;
                 if (dh.getContentType() == null || 
dh.getContentType().startsWith("text/plain")) {
diff --git 
a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java
 
b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java
index d770c7daf2c..82c0887bb4e 100644
--- 
a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java
+++ 
b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java
@@ -235,10 +235,6 @@ public class MailConsumer extends 
ScheduledBatchPollingConsumer {
 
             // must use the original message in case we need to work around a 
charset issue when extracting mail content
             var msg = exchange.getIn();
-            if (msg instanceof AttachmentMessage am) {
-                // unwrap from attachment message
-                msg = am.getDelegateMessage();
-            }
             final Message mail = ((MailMessage) msg).getOriginalMessage();
 
             // add on completion to handle after work when the exchange is done
@@ -456,10 +452,6 @@ public class MailConsumer extends 
ScheduledBatchPollingConsumer {
     protected void processExchange(Exchange exchange) throws Exception {
         if (LOG.isDebugEnabled()) {
             var msg = exchange.getIn();
-            if (msg instanceof AttachmentMessage am) {
-                // unwrap from attachment message
-                msg = am.getDelegateMessage();
-            }
             if (msg instanceof MailMessage mm) {
                 LOG.debug("Processing message: {}", 
MailUtils.dumpMessage(mm.getMessage()));
             }
diff --git 
a/components/camel-mail/src/main/java/org/apache/camel/component/mail/SplitAttachmentsExpression.java
 
b/components/camel-mail/src/main/java/org/apache/camel/component/mail/SplitAttachmentsExpression.java
index d12ab09c643..3ec673dbd94 100644
--- 
a/components/camel-mail/src/main/java/org/apache/camel/component/mail/SplitAttachmentsExpression.java
+++ 
b/components/camel-mail/src/main/java/org/apache/camel/component/mail/SplitAttachmentsExpression.java
@@ -72,7 +72,7 @@ public class SplitAttachmentsExpression extends 
ExpressionAdapter {
             }
 
             // clear attachments on original message after we have split them
-            inMessage.getAttachmentObjects().clear();
+            inMessage.clearAttachments();
 
             return answer;
         } catch (Exception e) {
diff --git 
a/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailAttachmentNamesTest.java
 
b/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailAttachmentNamesTest.java
index b2c740b4499..28daf0c5858 100644
--- 
a/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailAttachmentNamesTest.java
+++ 
b/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailAttachmentNamesTest.java
@@ -42,7 +42,6 @@ import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class MailAttachmentNamesTest extends CamelTestSupport {
@@ -170,7 +169,7 @@ public class MailAttachmentNamesTest extends 
CamelTestSupport {
         resultDefaultEndpoint.assertIsSatisfied();
         Exchange exchange = 
resultDefaultEndpoint.getReceivedExchanges().get(0);
         assertNotNull(exchange.getIn(AttachmentMessage.class));
-        
assertNull(exchange.getIn(AttachmentMessage.class).getAttachmentObjects());
+        assertEquals(0, 
exchange.getIn(AttachmentMessage.class).getAttachmentObjects().size());
     }
 
     /**
@@ -185,7 +184,7 @@ public class MailAttachmentNamesTest extends 
CamelTestSupport {
         resultDefaultEndpoint.assertIsSatisfied();
         Exchange exchange = 
resultDefaultEndpoint.getReceivedExchanges().get(0);
         assertNotNull(exchange.getIn(AttachmentMessage.class));
-        
assertNull(exchange.getIn(AttachmentMessage.class).getAttachmentObjects());
+        assertEquals(0, 
exchange.getIn(AttachmentMessage.class).getAttachmentObjects().size());
     }
 
     @Test
diff --git 
a/components/camel-servlet/src/main/java/org/apache/camel/component/servlet/AttachmentHttpBinding.java
 
b/components/camel-servlet/src/main/java/org/apache/camel/component/servlet/AttachmentHttpBinding.java
index 1b360d5c0ec..0495a2cb9ab 100644
--- 
a/components/camel-servlet/src/main/java/org/apache/camel/component/servlet/AttachmentHttpBinding.java
+++ 
b/components/camel-servlet/src/main/java/org/apache/camel/component/servlet/AttachmentHttpBinding.java
@@ -31,6 +31,7 @@ import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.attachment.Attachment;
 import org.apache.camel.attachment.AttachmentMessage;
 import org.apache.camel.attachment.DefaultAttachment;
+import org.apache.camel.attachment.DefaultAttachmentMessage;
 import org.apache.camel.http.common.DefaultHttpBinding;
 import org.apache.camel.util.FileUtil;
 import org.slf4j.Logger;
@@ -75,7 +76,7 @@ public final class AttachmentHttpBinding extends 
DefaultHttpBinding {
                             attachment.addHeader(headerName, headerValue);
                         }
                     }
-                    AttachmentMessage am = 
message.getExchange().getMessage(AttachmentMessage.class);
+                    AttachmentMessage am = new 
DefaultAttachmentMessage(message);
                     am.addAttachmentObject(part.getName(), attachment);
                 } else {
                     LOG.debug(
diff --git 
a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/DefaultUndertowHttpBinding.java
 
b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/DefaultUndertowHttpBinding.java
index 114a9e95be6..a669de857ba 100644
--- 
a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/DefaultUndertowHttpBinding.java
+++ 
b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/DefaultUndertowHttpBinding.java
@@ -47,6 +47,7 @@ import org.apache.camel.Message;
 import org.apache.camel.TypeConverter;
 import org.apache.camel.attachment.AttachmentMessage;
 import org.apache.camel.attachment.DefaultAttachment;
+import org.apache.camel.attachment.DefaultAttachmentMessage;
 import org.apache.camel.spi.HeaderFilterStrategy;
 import org.apache.camel.support.DefaultMessage;
 import org.apache.camel.support.ExceptionHelper;
@@ -136,7 +137,7 @@ public class DefaultUndertowHttpBinding implements 
UndertowHttpBinding {
                 formData.get(key).forEach(value -> {
                     if (value.isFile()) {
                         DefaultAttachment attachment = new 
DefaultAttachment(new FilePartDataSource(value));
-                        AttachmentMessage am = 
result.getExchange().getMessage(AttachmentMessage.class);
+                        AttachmentMessage am = new 
DefaultAttachmentMessage(result);
                         am.addAttachmentObject(key, attachment);
                         body.put(key, attachment.getDataHandler());
                     } else if (headerFilterStrategy != null
diff --git a/core/camel-api/src/main/java/org/apache/camel/Message.java 
b/core/camel-api/src/main/java/org/apache/camel/Message.java
index 1211354c25b..9bdd1bb1e75 100644
--- a/core/camel-api/src/main/java/org/apache/camel/Message.java
+++ b/core/camel-api/src/main/java/org/apache/camel/Message.java
@@ -220,10 +220,6 @@ public interface Message {
      */
     boolean hasHeaders();
 
-    boolean hasAttachments();
-
-    Map<String, Object> getAttachmentsMap();
-
     /**
      * Returns the body of the message as a POJO
      * <p/>
@@ -364,4 +360,9 @@ public interface Message {
      */
     void setPayloadForTrait(MessageTrait trait, Object object);
 
+    /**
+     * Removes the trait
+     */
+    void removeTrait(MessageTrait trait);
+
 }
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/trait/message/MessageTrait.java 
b/core/camel-api/src/main/java/org/apache/camel/trait/message/MessageTrait.java
index 604c7fb1f1c..3b7067eaae2 100644
--- 
a/core/camel-api/src/main/java/org/apache/camel/trait/message/MessageTrait.java
+++ 
b/core/camel-api/src/main/java/org/apache/camel/trait/message/MessageTrait.java
@@ -22,13 +22,20 @@ package org.apache.camel.trait.message;
  * type, etc). This is specifically for internal usage of Camel and not a 
public API.
  */
 public enum MessageTrait {
+
     /**
      * The redelivery trait for the message. See {@link 
RedeliveryTraitPayload}.
      */
     REDELIVERY,
+
     /**
      * Whether the message can store a data type. This carries the payload 
associated with the API specified in
      * {@link org.apache.camel.spi.DataTypeAware}.
      */
-    DATA_AWARE
+    DATA_AWARE,
+
+    /**
+     * Trait for storing attachments on the message.
+     */
+    ATTACHMENTS
 }
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java
index f158bd54f2f..46d948f1b1e 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java
@@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
 import org.apache.camel.CamelExecutionException;
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
@@ -515,7 +516,13 @@ abstract class AbstractExchange implements Exchange {
     }
 
     private Message newOutMessage() {
-        return in.newInstance();
+        if (in != null) {
+            Message answer = in.newInstance();
+            CamelContextAware.trySetCamelContext(answer, getContext());
+            return answer;
+        } else {
+            return new DefaultMessage(getContext());
+        }
     }
 
     @SuppressWarnings("deprecated")
@@ -652,10 +659,8 @@ abstract class AbstractExchange implements Exchange {
     public boolean isExternalRedelivered() {
         if (externalRedelivered == 
RedeliveryTraitPayload.UNDEFINED_REDELIVERY) {
             Message message = getIn();
-
             externalRedelivered = (RedeliveryTraitPayload) 
message.getPayloadForTrait(MessageTrait.REDELIVERY);
         }
-
         return externalRedelivered == RedeliveryTraitPayload.IS_REDELIVERY;
     }
 
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/DefaultMessage.java 
b/core/camel-support/src/main/java/org/apache/camel/support/DefaultMessage.java
index 7af576a13c4..728486f44dd 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/DefaultMessage.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/DefaultMessage.java
@@ -17,7 +17,6 @@
 package org.apache.camel.support;
 
 import java.util.HashMap;
-import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Supplier;
@@ -25,6 +24,7 @@ import java.util.function.Supplier;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
 import org.apache.camel.spi.HeadersMapFactory;
+import org.apache.camel.trait.message.MessageTrait;
 
 /**
  * The default implementation of {@link org.apache.camel.Message}
@@ -37,7 +37,6 @@ import org.apache.camel.spi.HeadersMapFactory;
  */
 public class DefaultMessage extends MessageSupport {
     private Map<String, Object> headers;
-    private Map<String, Object> attachments;
 
     public DefaultMessage(Exchange exchange) {
         setExchange(exchange);
@@ -57,9 +56,7 @@ public class DefaultMessage extends MessageSupport {
         if (headers != null) {
             headers.clear();
         }
-        if (attachments != null) {
-            attachments.clear();
-        }
+        removeTrait(MessageTrait.ATTACHMENTS);
     }
 
     @Override
@@ -297,20 +294,6 @@ public class DefaultMessage extends MessageSupport {
         return !headers.isEmpty();
     }
 
-    @Override
-    public boolean hasAttachments() {
-        return attachments != null && !attachments.isEmpty();
-    }
-
-    @Override
-    public Map<String, Object> getAttachmentsMap() {
-        if (attachments == null) {
-            // force creating attachments
-            attachments = new LinkedHashMap<>();
-        }
-        return attachments;
-    }
-
     @Override
     public DefaultMessage newInstance() {
         return new DefaultMessage(camelContext);
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/MessageSupport.java 
b/core/camel-support/src/main/java/org/apache/camel/support/MessageSupport.java
index baf81854880..cb241291c1d 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/MessageSupport.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/MessageSupport.java
@@ -17,6 +17,8 @@
 package org.apache.camel.support;
 
 import java.util.EnumMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
@@ -198,6 +200,7 @@ public abstract class MessageSupport implements Message, 
CamelContextAware, Data
     }
 
     @Override
+    @SuppressWarnings("raw")
     public void copyFromWithNewBody(Message that, Object newBody) {
         if (that == this) {
             // it's the same instance, so do not need to copy
@@ -227,28 +230,17 @@ public abstract class MessageSupport implements Message, 
CamelContextAware, Data
             }
         }
 
-        // the attachments may be the same instance if the end user has made 
some mistake
-        // and set the OUT message with the same attachment instance of the IN 
message etc
-        if (!sameAttachments(that)) {
-            if (hasAttachments()) {
-                // okay its safe to clear the attachments
-                getAttachmentsMap().clear();
-            }
-            if (that.hasAttachments()) {
-                getAttachmentsMap().putAll(that.getAttachmentsMap());
-            }
+        // copy attachments
+        Map<String, Object> attachments = (Map<String, Object>) 
that.getPayloadForTrait(MessageTrait.ATTACHMENTS);
+        if (attachments != null) {
+            setPayloadForTrait(MessageTrait.ATTACHMENTS, new 
LinkedHashMap<>(attachments));
         }
-
     }
 
     private boolean sameHeaders(Message that) {
         return hasHeaders() && that.hasHeaders() && getHeaders() == 
that.getHeaders();
     }
 
-    private boolean sameAttachments(Message that) {
-        return hasAttachments() && that.hasAttachments() && 
getAttachmentsMap() == that.getAttachmentsMap();
-    }
-
     @Override
     public Exchange getExchange() {
         return exchange;
@@ -331,4 +323,9 @@ public abstract class MessageSupport implements Message, 
CamelContextAware, Data
     public void setPayloadForTrait(MessageTrait trait, Object object) {
         traits.put(trait, object);
     }
+
+    @Override
+    public void removeTrait(MessageTrait trait) {
+        traits.remove(trait);
+    }
 }
diff --git 
a/core/camel-support/src/test/java/org/apache/camel/support/MessageHelperTest.java
 
b/core/camel-support/src/test/java/org/apache/camel/support/MessageHelperTest.java
index 32f251c710a..55d9a099154 100644
--- 
a/core/camel-support/src/test/java/org/apache/camel/support/MessageHelperTest.java
+++ 
b/core/camel-support/src/test/java/org/apache/camel/support/MessageHelperTest.java
@@ -187,16 +187,6 @@ class MessageHelperTest {
             return false;
         }
 
-        @Override
-        public boolean hasAttachments() {
-            return false;
-        }
-
-        @Override
-        public Map<String, Object> getAttachmentsMap() {
-            return null;
-        }
-
         @Override
         public Object getBody() {
             return body;
@@ -252,5 +242,9 @@ class MessageHelperTest {
         @Override
         public void setPayloadForTrait(MessageTrait trait, Object object) {
         }
+
+        @Override
+        public void removeTrait(MessageTrait trait) {
+        }
     }
 }
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_10.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_10.adoc
index 18f1f24f789..d03880b0ad1 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_10.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_10.adoc
@@ -6,6 +6,26 @@ from both 4.0 to 4.1 and 4.1 to 4.2.
 
 == Upgrading from 4.10.0 to 4.10.1
 
+=== camel-api
+
+Added `removeTraits` method to `org.apache.camel.Message`.
+
+=== camel-attachments
+
+The attachments have been refactored to be stored internally as a _message 
trait_,
+and the `org.apache.camel.attachment.AttachmentMessage` is only a facade to 
provide
+end user access to the fine-grained Attachment APIs. The underlying message 
implementation
+such as `DefaultMessage` in the `Exchange` is un-affected when converting from 
`Message` to `AttachmentMessage` via:
+
+[source,java]
+----
+AttachmentMessage am = exchange.getMessage(AttachmentMessage.class);
+am.addAttachment("message1.xml", new DataHandler(new FileDataSource(new 
File("src/test/data/message1.xml"))));
+----
+
+The class `org.apache.camel.attachment.AttachmentMap` has been removed.
+Removed `getDelegateMessage` method from 
`org.apache.camel.attachment.AttachmentMessage`.
+
 === camel-ftp
 
 The file name header `Exchange.FILE_NAME` now includes the relative path such 
as `subdir/hello.txt`
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_11.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_11.adoc
index 1b49530f034..83e0a725569 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_11.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_11.adoc
@@ -6,6 +6,26 @@ from both 4.0 to 4.1 and 4.1 to 4.2.
 
 == Upgrading Camel 4.10 to 4.11
 
+=== camel-api
+
+Added `removeTraits` method to `org.apache.camel.Message`.
+
+=== camel-attachments
+
+The attachments have been refactored to be stored internally as a _message 
trait_,
+and the `org.apache.camel.attachment.AttachmentMessage` is only a facade to 
provide
+end user access to the fine-grained Attachment APIs. The underlying message 
implementation
+such as `DefaultMessage` in the `Exchange` is un-affected when converting from 
`Message` to `AttachmentMessage` via:
+
+[source,java]
+----
+AttachmentMessage am = exchange.getMessage(AttachmentMessage.class);
+am.addAttachment("message1.xml", new DataHandler(new FileDataSource(new 
File("src/test/data/message1.xml"))));
+----
+
+The class `org.apache.camel.attachment.AttachmentMap` has been removed.
+Removed `getDelegateMessage` method from 
`org.apache.camel.attachment.AttachmentMessage`.
+
 === camel-main
 
 Remove the deprecated `camel.main.lightweight` option that was not in use.


Reply via email to