You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by or...@apache.org on 2023/05/02 05:24:00 UTC

[camel] branch main updated: CAMEL-19058: rework the Message to avoid hitting the type-check scalability issue

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

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


The following commit(s) were added to refs/heads/main by this push:
     new d0888629fcb CAMEL-19058: rework the Message to avoid hitting the type-check scalability issue
d0888629fcb is described below

commit d0888629fcbcffb8839c6019aa2346b04cb40cb2
Author: Otavio Rodolfo Piske <an...@gmail.com>
AuthorDate: Fri Feb 10 09:44:05 2023 +0100

    CAMEL-19058: rework the Message to avoid hitting the type-check scalability issue
    
    This introduces a trait that works similarly to the internal properties one in the Exchange so that we carry certain details about the message without relying on type specifications.
    
    Among other things this:
    
    - avoids unnecessarily recreating the DefaultMessage multiple times
    - moved the data type aware checks out of the hot path
    - reworks the isTransactedRedelivered to avoid costlier checks for specific message types
    
    Signed-off-by: Otavio R. Piske <an...@gmail.com>
---
 .../camel/attachment/DefaultAttachmentMessage.java | 16 +++++++-
 .../org/apache/camel/component/jms/JmsMessage.java | 13 ++-----
 .../camel/component/jms/JmsMessageHelper.java      | 24 ++++++++++++
 .../apache/camel/component/sjms/SjmsMessage.java   | 15 +++-----
 .../camel/component/sjms/jms/JmsMessageHelper.java | 24 ++++++++++++
 .../src/main/java/org/apache/camel/Message.java    | 25 ++++++++++++
 .../apache/camel/trait/message/MessageTrait.java   | 34 +++++++++++++++++
 .../trait/message/RedeliveryTraitPayload.java      | 38 +++++++++++++++++++
 .../org/apache/camel/support/AbstractExchange.java | 23 +++++------
 .../org/apache/camel/support/DefaultMessage.java   | 15 --------
 .../camel/support/DefaultPooledExchange.java       |  1 -
 .../org/apache/camel/support/MessageHelper.java    | 15 ++++----
 .../org/apache/camel/support/MessageSupport.java   | 44 ++++++++++++++++------
 .../apache/camel/support/MessageHelperTest.java    | 16 ++++++++
 14 files changed, 234 insertions(+), 69 deletions(-)

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 665b3d0bacb..71ab662adb0 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
@@ -26,9 +26,9 @@ import jakarta.activation.DataHandler;
 import org.apache.camel.Exchange;
 import org.apache.camel.InvalidPayloadException;
 import org.apache.camel.Message;
+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
@@ -286,4 +286,18 @@ public final class DefaultAttachmentMessage implements AttachmentMessage {
         return map != null && !map.isEmpty();
     }
 
+    @Override
+    public boolean hasTrait(MessageTrait trait) {
+        return delegate.hasTrait(trait);
+    }
+
+    @Override
+    public Object getPayloadForTrait(MessageTrait trait) {
+        return delegate.getPayloadForTrait(trait);
+    }
+
+    @Override
+    public void setPayloadForTrait(MessageTrait trait, Object object) {
+        delegate.setPayloadForTrait(trait, object);
+    }
 }
diff --git a/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsMessage.java b/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsMessage.java
index 86adc7ff5b8..add5a3b28e1 100644
--- a/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsMessage.java
+++ b/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsMessage.java
@@ -30,6 +30,7 @@ import org.apache.camel.Exchange;
 import org.apache.camel.RuntimeExchangeException;
 import org.apache.camel.support.DefaultMessage;
 import org.apache.camel.support.ExchangeHelper;
+import org.apache.camel.trait.message.MessageTrait;
 import org.apache.camel.util.ObjectHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -41,6 +42,7 @@ import static org.apache.camel.support.MessageHelper.copyBody;
  */
 public class JmsMessage extends DefaultMessage {
     private static final Logger LOG = LoggerFactory.getLogger(JmsMessage.class);
+
     private Message jmsMessage;
     private Session jmsSession;
     private JmsBinding binding;
@@ -148,6 +150,7 @@ public class JmsMessage extends DefaultMessage {
             }
         }
         this.jmsMessage = jmsMessage;
+        setPayloadForTrait(MessageTrait.REDELIVERY, JmsMessageHelper.evalRedeliveryMessageTrait(jmsMessage));
     }
 
     /**
@@ -269,15 +272,6 @@ public class JmsMessage extends DefaultMessage {
         }
     }
 
-    @Override
-    protected Boolean isTransactedRedelivered() {
-        if (jmsMessage != null) {
-            return JmsMessageHelper.getJMSRedelivered(jmsMessage);
-        } else {
-            return null;
-        }
-    }
-
     private String getDestinationAsString(Destination destination) throws JMSException {
         String result = null;
         if (destination == null) {
@@ -293,5 +287,4 @@ public class JmsMessage extends DefaultMessage {
     private String getSanitizedString(Object value) {
         return value != null ? value.toString().replaceAll("[^a-zA-Z0-9\\.\\_\\-]", "_") : "";
     }
-
 }
diff --git a/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsMessageHelper.java b/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsMessageHelper.java
index e18b19cb5d1..3f10d4767dd 100644
--- a/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsMessageHelper.java
+++ b/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsMessageHelper.java
@@ -28,6 +28,8 @@ import jakarta.jms.Message;
 
 import org.apache.camel.Exchange;
 import org.apache.camel.support.ExchangeHelper;
+import org.apache.camel.trait.message.MessageTrait;
+import org.apache.camel.trait.message.RedeliveryTraitPayload;
 import org.apache.camel.util.ObjectHelper;
 
 import static org.apache.camel.component.jms.JmsConfiguration.QUEUE_PREFIX;
@@ -349,6 +351,28 @@ public final class JmsMessageHelper {
         return null;
     }
 
+    /**
+     * For a given message, evaluates what is the redelivery state for it and gives the appropriate {@link MessageTrait}
+     * for that redelivery state
+     *
+     * @param  message the message to evalute
+     * @return         The appropriate MessageTrait for the redelivery state (one of MessageTrait.UNDEFINED_REDELIVERY,
+     *                 MessageTrait.IS_REDELIVERY or MessageTrait.NON_REDELIVERY).
+     */
+    public static RedeliveryTraitPayload evalRedeliveryMessageTrait(Message message) {
+        final Boolean redelivered = JmsMessageHelper.getJMSRedelivered(message);
+
+        if (redelivered == null) {
+            return RedeliveryTraitPayload.UNDEFINED_REDELIVERY;
+        }
+
+        if (Boolean.TRUE.equals(redelivered)) {
+            return RedeliveryTraitPayload.IS_REDELIVERY;
+        }
+
+        return RedeliveryTraitPayload.NON_REDELIVERY;
+    }
+
     /**
      * Gets the JMSMessageID from the message.
      *
diff --git a/components/camel-sjms/src/main/java/org/apache/camel/component/sjms/SjmsMessage.java b/components/camel-sjms/src/main/java/org/apache/camel/component/sjms/SjmsMessage.java
index a99ac0d0574..69c2b815345 100644
--- a/components/camel-sjms/src/main/java/org/apache/camel/component/sjms/SjmsMessage.java
+++ b/components/camel-sjms/src/main/java/org/apache/camel/component/sjms/SjmsMessage.java
@@ -32,6 +32,7 @@ import org.apache.camel.component.sjms.jms.JmsBinding;
 import org.apache.camel.component.sjms.jms.JmsMessageHelper;
 import org.apache.camel.support.DefaultMessage;
 import org.apache.camel.support.ExchangeHelper;
+import org.apache.camel.trait.message.MessageTrait;
 import org.apache.camel.util.ObjectHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,6 +44,7 @@ import static org.apache.camel.support.MessageHelper.copyBody;
  */
 public class SjmsMessage extends DefaultMessage {
     private static final Logger LOG = LoggerFactory.getLogger(SjmsMessage.class);
+
     private Message jmsMessage;
     private Session jmsSession;
     private JmsBinding binding;
@@ -52,6 +54,8 @@ public class SjmsMessage extends DefaultMessage {
         setJmsMessage(jmsMessage);
         setJmsSession(jmsSession);
         setBinding(binding);
+
+        setPayloadForTrait(MessageTrait.REDELIVERY, JmsMessageHelper.evalRedeliveryMessageTrait(jmsMessage));
     }
 
     public void init(Exchange exchange, Message jmsMessage, Session jmsSession, JmsBinding binding) {
@@ -61,6 +65,8 @@ public class SjmsMessage extends DefaultMessage {
         setBinding(binding);
         // need to populate initial headers when we use pooled exchanges
         populateInitialHeaders(getHeaders());
+
+        setPayloadForTrait(MessageTrait.REDELIVERY, JmsMessageHelper.evalRedeliveryMessageTrait(jmsMessage));
     }
 
     @Override
@@ -287,15 +293,6 @@ public class SjmsMessage extends DefaultMessage {
         }
     }
 
-    @Override
-    protected Boolean isTransactedRedelivered() {
-        if (jmsMessage != null) {
-            return JmsMessageHelper.getJMSRedelivered(jmsMessage);
-        } else {
-            return null;
-        }
-    }
-
     private String getDestinationAsString(Destination destination) throws JMSException {
         String result = null;
         if (destination == null) {
diff --git a/components/camel-sjms/src/main/java/org/apache/camel/component/sjms/jms/JmsMessageHelper.java b/components/camel-sjms/src/main/java/org/apache/camel/component/sjms/jms/JmsMessageHelper.java
index 43e8a741dbb..342a6e3d9d2 100644
--- a/components/camel-sjms/src/main/java/org/apache/camel/component/sjms/jms/JmsMessageHelper.java
+++ b/components/camel-sjms/src/main/java/org/apache/camel/component/sjms/jms/JmsMessageHelper.java
@@ -27,6 +27,8 @@ import jakarta.jms.Message;
 
 import org.apache.camel.Exchange;
 import org.apache.camel.support.ExchangeHelper;
+import org.apache.camel.trait.message.MessageTrait;
+import org.apache.camel.trait.message.RedeliveryTraitPayload;
 import org.apache.camel.util.ObjectHelper;
 
 import static org.apache.camel.util.StringHelper.removeStartingCharacters;
@@ -328,6 +330,28 @@ public final class JmsMessageHelper {
         return null;
     }
 
+    /**
+     * For a given message, evaluates what is the redelivery state for it and gives the appropriate {@link MessageTrait}
+     * for that redelivery state
+     *
+     * @param  message the message to evalute
+     * @return         The appropriate MessageTrait for the redelivery state (one of MessageTrait.UNDEFINED_REDELIVERY,
+     *                 MessageTrait.IS_REDELIVERY or MessageTrait.NON_REDELIVERY).
+     */
+    public static RedeliveryTraitPayload evalRedeliveryMessageTrait(Message message) {
+        final Boolean redelivered = JmsMessageHelper.getJMSRedelivered(message);
+
+        if (redelivered == null) {
+            return RedeliveryTraitPayload.UNDEFINED_REDELIVERY;
+        }
+
+        if (Boolean.TRUE.equals(redelivered)) {
+            return RedeliveryTraitPayload.IS_REDELIVERY;
+        }
+
+        return RedeliveryTraitPayload.NON_REDELIVERY;
+    }
+
     /**
      * Gets the JMSMessageID from the message.
      *
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 36e1a1538ef..b4d4da50eb9 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
@@ -20,6 +20,7 @@ import java.util.Map;
 import java.util.function.Supplier;
 
 import org.apache.camel.spi.HeadersMapFactory;
+import org.apache.camel.trait.message.MessageTrait;
 
 /**
  * Implements the <a href="http://camel.apache.org/message.html">Message</a> pattern and represents an inbound or
@@ -319,4 +320,28 @@ public interface Message {
      */
     void copyFromWithNewBody(Message message, Object newBody);
 
+    /**
+     * Checks whether the message has a given {@link MessageTrait}
+     *
+     * @param  trait the {@link MessageTrait} to check
+     * @return       true if the message instance has the trait or false otherwise
+     */
+    boolean hasTrait(MessageTrait trait);
+
+    /**
+     * Gets the payload for the {@link MessageTrait}
+     *
+     * @param  trait the {@link MessageTrait} to obtain the payload
+     * @return       The trait payload or null if not available
+     */
+    Object getPayloadForTrait(MessageTrait trait);
+
+    /**
+     * Sets the payload for the {@link MessageTrait}
+     *
+     * @param trait  the {@link MessageTrait} to set the payload
+     * @param object the payload
+     */
+    void setPayloadForTrait(MessageTrait trait, Object object);
+
 }
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
new file mode 100644
index 00000000000..8d25bba5a0b
--- /dev/null
+++ b/core/camel-api/src/main/java/org/apache/camel/trait/message/MessageTrait.java
@@ -0,0 +1,34 @@
+/*
+ * 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.trait.message;
+
+/**
+ * Message traits are runtime traits that can be associated with a message (for instance, the redelivery state, a data
+ * 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;
+}
diff --git a/core/camel-api/src/main/java/org/apache/camel/trait/message/RedeliveryTraitPayload.java b/core/camel-api/src/main/java/org/apache/camel/trait/message/RedeliveryTraitPayload.java
new file mode 100644
index 00000000000..71865b22acf
--- /dev/null
+++ b/core/camel-api/src/main/java/org/apache/camel/trait/message/RedeliveryTraitPayload.java
@@ -0,0 +1,38 @@
+/*
+ * 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.trait.message;
+
+/**
+ * Some messages can carry redelivery details which might affect routing (i.e; JMS messages). This trait allows
+ * implementations to assign a payload that determines the redelivery state for the message.
+ */
+public enum RedeliveryTraitPayload {
+    /**
+     * The default redelivery payload, as most messages don't support redeliveries
+     **/
+    UNDEFINED_REDELIVERY,
+    /**
+     * When a message supports redelivery, this indicates that this message is in a non-redelivery state
+     */
+    NON_REDELIVERY,
+
+    /**
+     * When a message supports redelivery, this indicates that this message is in a redelivery state
+     */
+    IS_REDELIVERY,
+}
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 26162146be4..2533c79bc6d 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
@@ -35,6 +35,8 @@ import org.apache.camel.MessageHistory;
 import org.apache.camel.SafeCopyProperty;
 import org.apache.camel.spi.HeadersMapFactory;
 import org.apache.camel.spi.UnitOfWork;
+import org.apache.camel.trait.message.MessageTrait;
+import org.apache.camel.trait.message.RedeliveryTraitPayload;
 import org.apache.camel.util.ObjectHelper;
 
 import static org.apache.camel.support.MessageHelper.copyBody;
@@ -64,12 +66,12 @@ class AbstractExchange implements Exchange {
     protected Exception exception;
     protected String exchangeId;
     protected ExchangePattern pattern;
-    protected Boolean externalRedelivered;
     protected boolean routeStop;
     protected boolean rollbackOnly;
     protected boolean rollbackOnlyLast;
     protected Map<String, SafeCopyProperty> safeCopyProperties;
     private final ExtendedExchangeExtension privateExtension;
+    private RedeliveryTraitPayload externalRedelivered = RedeliveryTraitPayload.UNDEFINED_REDELIVERY;
 
     public AbstractExchange(CamelContext context) {
         this(context, ExchangePattern.InOnly);
@@ -635,20 +637,13 @@ class AbstractExchange implements Exchange {
 
     @Override
     public boolean isExternalRedelivered() {
-        if (externalRedelivered == null) {
-            // lets avoid adding methods to the Message API, so we use the
-            // DefaultMessage to allow component specific messages to extend
-            // and implement the isExternalRedelivered method.
-            Message msg = getIn();
-            if (msg instanceof DefaultMessage) {
-                externalRedelivered = ((DefaultMessage) msg).isTransactedRedelivered();
-            }
-            // not from a transactional resource so mark it as false by default
-            if (externalRedelivered == null) {
-                externalRedelivered = false;
-            }
+        if (externalRedelivered == RedeliveryTraitPayload.UNDEFINED_REDELIVERY) {
+            Message message = getIn();
+
+            externalRedelivered = (RedeliveryTraitPayload) message.getPayloadForTrait(MessageTrait.REDELIVERY);
         }
-        return externalRedelivered;
+
+        return externalRedelivered == RedeliveryTraitPayload.IS_REDELIVERY;
     }
 
     @Override
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 1716ba0dc52..2336629c1df 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
@@ -340,25 +340,10 @@ public class DefaultMessage extends MessageSupport {
         // do nothing by default
     }
 
-    /**
-     * A strategy for component specific messages to determine whether the message is redelivered or not.
-     * <p/>
-     * <b>Important: </b> It is not always possible to determine if the transacted is a redelivery or not, and therefore
-     * <tt>null</tt> is returned. Such an example would be a JDBC message. However JMS brokers provides details if a
-     * transacted message is redelivered.
-     *
-     * @return <tt>true</tt> if redelivered, <tt>false</tt> if not, <tt>null</tt> if not able to determine
-     */
-    protected Boolean isTransactedRedelivered() {
-        // return null by default
-        return null;
-    }
-
     /**
      * Returns true if the headers have been mutated in some way
      */
     protected boolean hasPopulatedHeaders() {
         return headers != null;
     }
-
 }
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/DefaultPooledExchange.java b/core/camel-support/src/main/java/org/apache/camel/support/DefaultPooledExchange.java
index 958b6b92179..33e65434f07 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/DefaultPooledExchange.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/DefaultPooledExchange.java
@@ -103,7 +103,6 @@ public final class DefaultPooledExchange extends AbstractExchange implements Poo
             // reset pattern to original
             this.pattern = originalPattern;
             // do not reset endpoint/fromRouteId as it would be the same consumer/endpoint again
-            this.externalRedelivered = null;
             this.routeStop = false;
             this.rollbackOnly = false;
             this.rollbackOnlyLast = false;
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
index 62a10bf16ad..b45106f8eb6 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
@@ -38,6 +38,7 @@ import org.apache.camel.WrappedFile;
 import org.apache.camel.spi.DataTypeAware;
 import org.apache.camel.spi.ExchangeFormatter;
 import org.apache.camel.spi.HeaderFilterStrategy;
+import org.apache.camel.trait.message.MessageTrait;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.StopWatch;
 import org.apache.camel.util.StringHelper;
@@ -620,14 +621,14 @@ public final class MessageHelper {
      */
     public static void copyBody(Message source, Message target) {
         // Preserve the DataType if both messages are DataTypeAware
-        if (source instanceof DataTypeAware && target instanceof DataTypeAware) {
-            final DataTypeAware dataTypeAwareSource = (DataTypeAware) source;
-            if (dataTypeAwareSource.hasDataType()) {
-                final DataTypeAware dataTypeAwareTarget = (DataTypeAware) target;
-                dataTypeAwareTarget.setBody(source.getBody(), dataTypeAwareSource.getDataType());
-                return;
-            }
+        if (source.hasTrait(MessageTrait.DATA_AWARE)) {
+            target.setBody(source.getBody());
+            target.setPayloadForTrait(MessageTrait.DATA_AWARE,
+                    source.getPayloadForTrait(MessageTrait.DATA_AWARE));
+
+            return;
         }
+
         target.setBody(source.getBody());
     }
 
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 87f30bac445..78cf5a979c8 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
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.support;
 
+import java.util.Arrays;
+
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.Exchange;
@@ -24,6 +26,7 @@ import org.apache.camel.Message;
 import org.apache.camel.TypeConverter;
 import org.apache.camel.spi.DataType;
 import org.apache.camel.spi.DataTypeAware;
+import org.apache.camel.trait.message.MessageTrait;
 
 /**
  * A base class for implementation inheritance providing the core {@link Message} body handling features but letting the
@@ -33,19 +36,22 @@ import org.apache.camel.spi.DataTypeAware;
  * from {@link DefaultMessage}
  */
 public abstract class MessageSupport implements Message, CamelContextAware, DataTypeAware {
+    private static final int NUM_TRAITS = MessageTrait.values().length;
+
     CamelContext camelContext;
     TypeConverter typeConverter;
     private Exchange exchange;
     private Object body;
     private String messageId;
     private long messageTimestamp;
-    private DataType dataType;
+
+    private final Object[] traits = new Object[NUM_TRAITS];
 
     @Override
     public void reset() {
         body = null;
         messageId = null;
-        dataType = null;
+        Arrays.fill(traits, null);
     }
 
     @Override
@@ -133,7 +139,7 @@ public abstract class MessageSupport implements Message, CamelContextAware, Data
         this.body = body;
         // set data type if in use
         if (body != null && camelContext != null && camelContext.isUseDataType()) {
-            this.dataType = new DataType(body.getClass());
+            setPayloadForTrait(MessageTrait.DATA_AWARE, new DataType(body.getClass()));
         }
     }
 
@@ -152,22 +158,23 @@ public abstract class MessageSupport implements Message, CamelContextAware, Data
     @Override
     public void setBody(Object body, DataType type) {
         this.body = body;
-        this.dataType = type;
+        setPayloadForTrait(MessageTrait.DATA_AWARE, type);
     }
 
     @Override
     public DataType getDataType() {
-        return this.dataType;
+        Object payload = getPayloadForTrait(MessageTrait.DATA_AWARE);
+        return (DataType) payload;
     }
 
     @Override
     public void setDataType(DataType type) {
-        this.dataType = type;
+        setPayloadForTrait(MessageTrait.DATA_AWARE, type);
     }
 
     @Override
     public boolean hasDataType() {
-        return dataType != null;
+        return hasTrait(MessageTrait.DATA_AWARE);
     }
 
     @Override
@@ -187,11 +194,8 @@ public abstract class MessageSupport implements Message, CamelContextAware, Data
 
         copyFromWithNewBody(that, that.getBody());
         // Preserve the DataType
-        if (that instanceof DataTypeAware) {
-            final DataTypeAware dataTypeAware = (DataTypeAware) that;
-            if (dataTypeAware.hasDataType()) {
-                setDataType(dataTypeAware.getDataType());
-            }
+        if (that.hasTrait(MessageTrait.DATA_AWARE)) {
+            setPayloadForTrait(MessageTrait.DATA_AWARE, that.getPayloadForTrait(MessageTrait.DATA_AWARE));
         }
     }
 
@@ -306,4 +310,20 @@ public abstract class MessageSupport implements Message, CamelContextAware, Data
         }
     }
 
+    @Override
+    public boolean hasTrait(MessageTrait trait) {
+        Object payload = traits[trait.ordinal()];
+
+        return payload != null;
+    }
+
+    @Override
+    public Object getPayloadForTrait(MessageTrait trait) {
+        return traits[trait.ordinal()];
+    }
+
+    @Override
+    public void setPayloadForTrait(MessageTrait trait, Object object) {
+        traits[trait.ordinal()] = object;
+    }
 }
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 d19dd43eb5e..661f778dd7f 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
@@ -24,6 +24,7 @@ import org.apache.camel.Exchange;
 import org.apache.camel.InvalidPayloadException;
 import org.apache.camel.Message;
 import org.apache.camel.spi.DataType;
+import org.apache.camel.trait.message.MessageTrait;
 import org.junit.jupiter.api.Test;
 
 import static org.apache.camel.support.MessageHelper.copyBody;
@@ -231,5 +232,20 @@ class MessageHelperTest {
         public void copyFromWithNewBody(Message message, Object newBody) {
 
         }
+
+        @Override
+        public boolean hasTrait(MessageTrait trait) {
+            return false;
+        }
+
+        @Override
+        public Object getPayloadForTrait(MessageTrait trait) {
+            return null;
+        }
+
+        @Override
+        public void setPayloadForTrait(MessageTrait trait, Object object) {
+
+        }
     }
 }