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

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


The following commit(s) were added to refs/heads/camel-4.10.x by this push:
     new df12381957f CAMEL-21755: Adjust attachments API on Message to avoid 
issue during routing (#17243)
df12381957f is described below

commit df12381957fa00d0f80a2c3c19d0e5431a08665b
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Feb 24 12:05:29 2025 +0000

    CAMEL-21755: Adjust attachments API on Message to avoid issue during 
routing (#17243)
    
    * CAMEL-21755: Adjust attachments API on Message to avoid issue during 
routing
    
    * CAMEL-21755: Adjust attachments API on Message to avoid issue during 
routing
---
 .../camel/attachment/AttachmentConverter.java      |   2 -
 .../attachment/AttachmentExpressionBuilder.java    | 115 ++++++++++-----------
 .../org/apache/camel/attachment/AttachmentMap.java |  31 ------
 .../apache/camel/attachment/AttachmentMessage.java |  21 ++--
 .../camel/attachment/CSimpleAttachmentHelper.java  |  44 ++++----
 .../camel/attachment/DefaultAttachmentMessage.java | 114 +++++++++-----------
 .../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 +-
 .../java/org/apache/camel/ExchangeExtension.java   |   5 -
 .../src/main/java/org/apache/camel/Message.java    |  10 ++
 .../apache/camel/trait/message/MessageTrait.java   |   9 +-
 .../org/apache/camel/support/AbstractExchange.java |  12 ++-
 .../org/apache/camel/support/DefaultMessage.java   |   2 +
 .../org/apache/camel/support/ExchangeHelper.java   |  16 +--
 .../camel/support/ExtendedExchangeExtension.java   |   5 -
 .../org/apache/camel/support/MessageSupport.java   |  19 +++-
 .../apache/camel/support/MessageHelperTest.java    |  15 +--
 21 files changed, 196 insertions(+), 252 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/AttachmentMap.java
 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentMap.java
deleted file mode 100644
index bf4eb24c2cc..00000000000
--- 
a/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentMap.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.attachment;
-
-import java.util.LinkedHashMap;
-
-import org.apache.camel.SafeCopyProperty;
-
-public final class AttachmentMap extends LinkedHashMap<String, Attachment> 
implements SafeCopyProperty {
-
-    @Override
-    public SafeCopyProperty safeCopy() {
-        AttachmentMap clone = new AttachmentMap();
-        clone.putAll(this);
-        return clone;
-    }
-}
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 10e53c88b78..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;
@@ -30,23 +31,24 @@ import org.apache.camel.trait.message.MessageTrait;
 
 public final class DefaultAttachmentMessage implements AttachmentMessage {
 
-    /*
-     * Attachments are stores as a property on the {@link Exchange} which 
ensures they are propagated
-     * during routing and we dont have to pollute the generic {@link Message} 
with attachment APIs
-     */
-    private static final String ATTACHMENT_OBJECTS = "CamelAttachmentObjects";
-
     private final Message delegate;
-    private final Exchange exchange;
 
     public DefaultAttachmentMessage(Message delegate) {
         this.delegate = delegate;
-        this.exchange = delegate.getExchange();
+    }
+
+    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
-    public Message getDelegateMessage() {
-        return delegate;
+    public Message newInstance() {
+        return new DefaultAttachmentMessage(delegate);
     }
 
     @Override
@@ -176,7 +178,7 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
 
     @Override
     public Message copy() {
-        return delegate.copy();
+        return new DefaultAttachmentMessage(delegate.copy());
     }
 
     @Override
@@ -190,106 +192,77 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public DataHandler getAttachment(String id) {
-        AttachmentMap map = 
exchange.getExchangeExtension().getSafeCopyProperty(ATTACHMENT_OBJECTS, 
AttachmentMap.class);
-        if (map != null) {
-            Attachment att = map.get(id);
-            if (att != null) {
-                return att.getDataHandler();
-            }
+        Attachment att = (Attachment) getAttachmentsMap().get(id);
+        if (att != null) {
+            return att.getDataHandler();
         }
         return null;
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public Attachment getAttachmentObject(String id) {
-        AttachmentMap map = 
exchange.getExchangeExtension().getSafeCopyProperty(ATTACHMENT_OBJECTS, 
AttachmentMap.class);
-        if (map != null) {
-            return map.get(id);
-        }
-        return null;
+        return (Attachment) getAttachmentsMap().get(id);
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public Set<String> getAttachmentNames() {
-        AttachmentMap map = 
exchange.getExchangeExtension().getSafeCopyProperty(ATTACHMENT_OBJECTS, 
AttachmentMap.class);
-        if (map != null) {
-            return map.keySet();
-        }
-        return null;
+        return getAttachmentsMap().keySet();
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public void removeAttachment(String id) {
-        AttachmentMap map = 
exchange.getExchangeExtension().getSafeCopyProperty(ATTACHMENT_OBJECTS, 
AttachmentMap.class);
-        if (map != null) {
-            map.remove(id);
-        }
+        getAttachmentsMap().remove(id);
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public void addAttachment(String id, DataHandler content) {
-        AttachmentMap map = 
exchange.getExchangeExtension().getSafeCopyProperty(ATTACHMENT_OBJECTS, 
AttachmentMap.class);
-        if (map == null) {
-            map = new AttachmentMap();
-            
exchange.getExchangeExtension().setSafeCopyProperty(ATTACHMENT_OBJECTS, map);
-        }
-        map.put(id, new DefaultAttachment(content));
+        getAttachmentsMap().put(id, new DefaultAttachment(content));
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public void addAttachmentObject(String id, Attachment content) {
-        AttachmentMap map = 
exchange.getExchangeExtension().getSafeCopyProperty(ATTACHMENT_OBJECTS, 
AttachmentMap.class);
-        if (map == null) {
-            map = new AttachmentMap();
-            
exchange.getExchangeExtension().setSafeCopyProperty(ATTACHMENT_OBJECTS, map);
-        }
-        map.put(id, content);
+        getAttachmentsMap().put(id, content);
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public Map<String, DataHandler> getAttachments() {
-        Map<String, Attachment> map = 
exchange.getExchangeExtension().getSafeCopyProperty(ATTACHMENT_OBJECTS, 
Map.class);
-        if (map != null) {
-            Map<String, DataHandler> answer = new LinkedHashMap<>();
-            map.forEach((id, att) -> answer.put(id, att.getDataHandler()));
-            return answer;
-        }
-        return null;
+        Map<String, DataHandler> answer = new LinkedHashMap<>();
+        getAttachmentsMap().forEach((id, att) -> {
+            Attachment a = (Attachment) att;
+            answer.put(id, a.getDataHandler());
+        });
+        return Collections.unmodifiableMap(answer);
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public Map<String, Attachment> getAttachmentObjects() {
-        return 
exchange.getExchangeExtension().getSafeCopyProperty(ATTACHMENT_OBJECTS, 
Map.class);
+        Map<String, Attachment> answer = new LinkedHashMap<>();
+        getAttachmentsMap().forEach((id, att) -> {
+            Attachment a = (Attachment) att;
+            answer.put(id, a);
+        });
+        return Collections.unmodifiableMap(answer);
     }
 
     @Override
     public void setAttachments(Map<String, DataHandler> attachments) {
-        AttachmentMap map = new AttachmentMap();
-        attachments.forEach((id, dh) -> map.put(id, new 
DefaultAttachment(dh)));
-        
exchange.getExchangeExtension().setSafeCopyProperty(ATTACHMENT_OBJECTS, map);
+        attachments.forEach((id, dh) -> getAttachmentsMap().put(id, new 
DefaultAttachment(dh)));
     }
 
     @Override
     public void setAttachmentObjects(Map<String, Attachment> attachments) {
-        AttachmentMap map = new AttachmentMap();
-        map.putAll(attachments);
-        
exchange.getExchangeExtension().setSafeCopyProperty(ATTACHMENT_OBJECTS, map);
+        getAttachmentsMap().putAll(attachments);
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public boolean hasAttachments() {
-        AttachmentMap map = 
exchange.getExchangeExtension().getSafeCopyProperty(ATTACHMENT_OBJECTS, 
AttachmentMap.class);
-        return map != null && !map.isEmpty();
+        return delegate.hasTrait(MessageTrait.ATTACHMENTS);
+    }
+
+    @Override
+    public void clearAttachments() {
+        delegate.removeTrait(MessageTrait.ATTACHMENTS);
     }
 
     @Override
@@ -306,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 294d72f33d0..1cc465b6b50 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/ExchangeExtension.java 
b/core/camel-api/src/main/java/org/apache/camel/ExchangeExtension.java
index 9d13bcd16b1..ded6208da5b 100644
--- a/core/camel-api/src/main/java/org/apache/camel/ExchangeExtension.java
+++ b/core/camel-api/src/main/java/org/apache/camel/ExchangeExtension.java
@@ -38,11 +38,6 @@ public interface ExchangeExtension {
      */
     <T> T getInOrNull(Class<T> type);
 
-    /**
-     * Prepares the exchange by setting IN as OUT
-     */
-    void prepareInToOut();
-
     /**
      * Sets the endpoint which originated this message exchange. This method 
should typically only be called by
      * {@link Endpoint} implementations
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 a056aeb2dcb..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
@@ -33,6 +33,11 @@ import org.apache.camel.trait.message.MessageTrait;
  */
 public interface Message {
 
+    /**
+     * Returns a new instance of this type.
+     */
+    Message newInstance();
+
     /**
      * Clears the message from user data, so the message can be reused.
      * <p/>
@@ -355,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 98d1cabd2cb..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,10 +516,13 @@ abstract class AbstractExchange implements Exchange {
     }
 
     private Message newOutMessage() {
-        if (in instanceof MessageSupport messageSupport) {
-            return messageSupport.newInstance();
+        if (in != null) {
+            Message answer = in.newInstance();
+            CamelContextAware.trySetCamelContext(answer, getContext());
+            return answer;
+        } else {
+            return new DefaultMessage(getContext());
         }
-        return new DefaultMessage(getContext());
     }
 
     @SuppressWarnings("deprecated")
@@ -655,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 fdec471fc0f..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
@@ -24,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}
@@ -55,6 +56,7 @@ public class DefaultMessage extends MessageSupport {
         if (headers != null) {
             headers.clear();
         }
+        removeTrait(MessageTrait.ATTACHMENTS);
     }
 
     @Override
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/ExchangeHelper.java 
b/core/camel-support/src/main/java/org/apache/camel/support/ExchangeHelper.java
index 054b2d904b5..1d09ab4315c 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/ExchangeHelper.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/ExchangeHelper.java
@@ -398,13 +398,8 @@ public final class ExchangeHelper {
         // we just need to ensure MEP is as expected (eg copy result to OUT if 
out capable)
         // and the result is not failed
         if (result.getPattern().isOutCapable() && !result.hasOut() && 
!result.isFailed()) {
-            // prepare IN as OUT as we expect a OUT response
-            if (result == source) {
-                // optimized
-                result.getExchangeExtension().prepareInToOut();
-            } else {
-                result.getOut().copyFrom(source.getIn());
-            }
+            // copy IN to OUT as we expect a OUT response
+            result.getOut().copyFrom(source.getIn());
         }
     }
 
@@ -415,12 +410,7 @@ public final class ExchangeHelper {
         // so lets assume the last IN is the OUT
         if (!preserverPattern && result.getPattern().isOutCapable()) {
             // only set OUT if its OUT capable
-            if (result == source) {
-                // optimized
-                result.getExchangeExtension().prepareInToOut();
-            } else {
-                result.getOut().copyFrom(source.getIn());
-            }
+            result.getOut().copyFrom(source.getIn());
         } else {
             // if not replace IN instead to keep the MEP
             result.getIn().copyFrom(source.getIn());
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/ExtendedExchangeExtension.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/ExtendedExchangeExtension.java
index 8b383af7163..bb41cac492f 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/ExtendedExchangeExtension.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/ExtendedExchangeExtension.java
@@ -271,11 +271,6 @@ public class ExtendedExchangeExtension implements 
ExchangeExtension {
         return this.exchange.getInOrNull(type);
     }
 
-    @Override
-    public void prepareInToOut() {
-        this.exchange.out = this.exchange.in;
-    }
-
     @Override
     public AsyncCallback getDefaultConsumerCallback() {
         return this.defaultConsumerCallback;
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 f1813d0b32b..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
@@ -226,6 +229,12 @@ public abstract class MessageSupport implements Message, 
CamelContextAware, Data
                 getHeaders().putAll(that.getHeaders());
             }
         }
+
+        // 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) {
@@ -252,11 +261,6 @@ public abstract class MessageSupport implements Message, 
CamelContextAware, Data
         this.typeConverter = camelContext.getTypeConverter();
     }
 
-    /**
-     * Returns a new instance
-     */
-    public abstract Message newInstance();
-
     /**
      * A factory method to allow a provider to lazily create the message body 
for inbound messages from other sources
      *
@@ -319,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 6ba63e877b9..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
@@ -92,8 +92,12 @@ class MessageHelperTest {
         }
 
         @Override
-        public void reset() {
+        public Message newInstance() {
+            return null;
+        }
 
+        @Override
+        public void reset() {
         }
 
         @Override
@@ -108,7 +112,6 @@ class MessageHelperTest {
 
         @Override
         public void setMessageId(String messageId) {
-
         }
 
         @Override
@@ -153,7 +156,6 @@ class MessageHelperTest {
 
         @Override
         public void setHeader(String name, Object value) {
-
         }
 
         @Override
@@ -178,7 +180,6 @@ class MessageHelperTest {
 
         @Override
         public void setHeaders(Map<String, Object> headers) {
-
         }
 
         @Override
@@ -213,7 +214,6 @@ class MessageHelperTest {
 
         @Override
         public <T> void setBody(Object body, Class<T> type) {
-
         }
 
         @Override
@@ -223,12 +223,10 @@ class MessageHelperTest {
 
         @Override
         public void copyFrom(Message message) {
-
         }
 
         @Override
         public void copyFromWithNewBody(Message message, Object newBody) {
-
         }
 
         @Override
@@ -243,7 +241,10 @@ class MessageHelperTest {
 
         @Override
         public void setPayloadForTrait(MessageTrait trait, Object object) {
+        }
 
+        @Override
+        public void removeTrait(MessageTrait trait) {
         }
     }
 }


Reply via email to