org.mule.DefaultMuleMessage.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.DefaultMuleMessage.java

Source

/*
 * $Id$
 * --------------------------------------------------------------------------------------
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 *
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */

package org.mule;

import org.mule.api.ExceptionPayload;
import org.mule.api.MuleContext;
import org.mule.api.MuleEvent;
import org.mule.api.MuleException;
import org.mule.api.MuleMessage;
import org.mule.api.MuleRuntimeException;
import org.mule.api.ThreadSafeAccess;
import org.mule.api.config.MuleProperties;
import org.mule.api.transformer.Converter;
import org.mule.api.transformer.DataType;
import org.mule.api.transformer.MessageTransformer;
import org.mule.api.transformer.Transformer;
import org.mule.api.transformer.TransformerException;
import org.mule.api.transformer.TransformerMessagingException;
import org.mule.api.transport.OutputHandler;
import org.mule.api.transport.PropertyScope;
import org.mule.config.MuleManifest;
import org.mule.config.i18n.CoreMessages;
import org.mule.transformer.TransformerUtils;
import org.mule.transformer.types.DataTypeFactory;
import org.mule.transformer.types.MimeTypes;
import org.mule.transport.NullPayload;
import org.mule.util.ClassUtils;
import org.mule.util.ObjectUtils;
import org.mule.util.StringMessageUtils;
import org.mule.util.StringUtils;
import org.mule.util.UUID;
import org.mule.util.store.DeserializationPostInitialisable;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Reader;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * <code>DefaultMuleMessage</code> is a wrapper that contains a payload and properties
 * associated with the payload.
 */
public class DefaultMuleMessage implements MuleMessage, ThreadSafeAccess, DeserializationPostInitialisable {
    protected static final String NOT_SET = "<not set>";

    private static final long serialVersionUID = 1541720810851984845L;
    private static final Log logger = LogFactory.getLog(DefaultMuleMessage.class);
    private static final List<Class<?>> consumableClasses = new ArrayList<Class<?>>();

    /**
     * The default UUID for the message. If the underlying transport has the notion of a
     * message id, this uuid will be ignored
     */
    private String id = UUID.getUUID();
    private String rootId = id;

    private transient Object payload;
    private transient Object originalPayload;

    /**
     * If an exception occurs while processing this message an exception payload
     * will be attached here
     */
    private ExceptionPayload exceptionPayload;

    /**
     * Scoped properties for this message
     */
    private MessagePropertiesContext properties = new MessagePropertiesContext();

    /**
     * Collection of attachments that were attached to the incoming message
     */
    private transient Map<String, DataHandler> inboundAttachments = new ConcurrentHashMap<String, DataHandler>();

    /**
     * Collection of attachments that will be sent out with this message
     */
    private transient Map<String, DataHandler> outboundAttachments = new ConcurrentHashMap<String, DataHandler>();

    private transient byte[] cache;
    protected transient MuleContext muleContext;

    // these are transient because serialisation generates a new instance
    // so we allow mutation again (and we can't serialize threads anyway)
    private transient AtomicReference<Thread> ownerThread = null;
    private transient AtomicBoolean mutable = null;

    private DataType<?> dataType;

    static {
        addToConsumableClasses("javax.xml.stream.XMLStreamReader");
        addToConsumableClasses("javax.xml.transform.stream.StreamSource");
        consumableClasses.add(OutputHandler.class);
        consumableClasses.add(InputStream.class);
        consumableClasses.add(Reader.class);
    }

    private static void addToConsumableClasses(String className) {
        try {
            consumableClasses.add(ClassUtils.loadClass(className, DefaultMuleMessage.class));
        } catch (ClassNotFoundException e) {
            // ignore
        }
    }

    public DefaultMuleMessage(Object message, MuleContext muleContext) {
        this(message, (Map<String, Object>) null, muleContext);
    }

    public DefaultMuleMessage(Object message, Map<String, Object> outboundProperties, MuleContext muleContext) {
        this(message, outboundProperties, null, muleContext);
    }

    public DefaultMuleMessage(Object message, Map<String, Object> outboundProperties,
            Map<String, DataHandler> attachments, MuleContext muleContext) {
        this(message, null, outboundProperties, attachments, muleContext);
    }

    public DefaultMuleMessage(Object message, Map<String, Object> inboundProperties,
            Map<String, Object> outboundProperties, Map<String, DataHandler> attachments, MuleContext muleContext) {
        setMuleContext(muleContext);

        if (message instanceof MuleMessage) {
            MuleMessage muleMessage = (MuleMessage) message;
            setPayload(muleMessage.getPayload());
            copyMessageProperties(muleMessage);
        } else {
            setPayload(message);
            originalPayload = message;
        }
        addProperties(inboundProperties, PropertyScope.INBOUND);
        addProperties(outboundProperties);

        //Add inbound attachments
        if (attachments != null) {
            inboundAttachments = attachments;
        }

        resetAccessControl();
    }

    public DefaultMuleMessage(Object message, MuleMessage previous, MuleContext muleContext) {
        id = previous.getUniqueId();
        rootId = previous.getMessageRootId();
        setMuleContext(muleContext);
        setEncoding(previous.getEncoding());

        if (message instanceof MuleMessage) {
            MuleMessage payloadMessage = (MuleMessage) message;
            setPayload(payloadMessage.getPayload());
            copyMessageProperties(payloadMessage);
        } else {
            setPayload(message);
            copyMessageProperties(previous);
        }
        originalPayload = previous.getPayload();

        if (previous.getExceptionPayload() != null) {
            setExceptionPayload(previous.getExceptionPayload());
        }

        if (previous instanceof DefaultMuleMessage) {
            setInvocationProperties(((DefaultMuleMessage) previous).properties.invocationMap);
            setSessionProperties(((DefaultMuleMessage) previous).properties.sessionMap);
        }

        copyAttachments(previous);

        resetAccessControl();
    }

    protected void copyMessageProperties(MuleMessage muleMessage) {
        // explicitly copy INBOUND message properties over. This cannot be done in the loop below
        Map<String, Object> inboundProperties = ((DefaultMuleMessage) muleMessage).properties
                .getScopedProperties(PropertyScope.INBOUND);
        addInboundProperties(inboundProperties);

        for (PropertyScope scope : new PropertyScope[] { PropertyScope.INBOUND, PropertyScope.OUTBOUND }) {
            try {
                for (String name : muleMessage.getPropertyNames(scope)) {
                    Object value = muleMessage.getProperty(name, scope);
                    if (value != null) {
                        setProperty(name, value, scope);
                    }
                }
            } catch (IllegalArgumentException iae) {
                // ignore non-registered property scope
            }
        }
    }

    private void copyAttachments(MuleMessage previous) {
        if (previous.getInboundAttachmentNames().size() > 0) {
            for (String name : previous.getInboundAttachmentNames()) {
                try {
                    inboundAttachments.put(name, previous.getInboundAttachment(name));
                } catch (Exception e) {
                    throw new MuleRuntimeException(CoreMessages.failedToReadAttachment(name), e);
                }
            }
        }

        if (previous.getOutboundAttachmentNames().size() > 0) {
            for (String name : previous.getOutboundAttachmentNames()) {
                try {
                    addOutboundAttachment(name, previous.getOutboundAttachment(name));
                } catch (Exception e) {
                    throw new MuleRuntimeException(CoreMessages.failedToReadAttachment(name), e);
                }
            }
        }
    }

    public DefaultMuleMessage(MuleMessage message) {
        this(message.getPayload(), message, message.getMuleContext());
    }

    private void setMuleContext(MuleContext context) {
        if (context == null) {
            throw new IllegalArgumentException(CoreMessages.objectIsNull("muleContext").getMessage());
        }
        muleContext = context;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> T getPayload(Class<T> outputType) throws TransformerException {
        return getPayload(DataTypeFactory.create(outputType), getEncoding());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> T getPayload(DataType<T> outputType) throws TransformerException {
        return getPayload(outputType, getEncoding());
    }

    @Override
    public MuleContext getMuleContext() {
        return muleContext;
    }

    /**
     * Will attempt to obtain the payload of this message with the desired Class type. This will
     * try and resolve a transformer that can do this transformation. If a transformer cannot be
     * found an exception is thrown. Any transformers added to the registry will be checked for
     * compatability.
     *
     * @param resultType the desired return type
     * @param encoding   the encoding to use if required
     * @return The converted payload of this message. Note that this method will not alter the
     *         payload of this message <b>unless</b> the payload is an {@link InputStream} in which
     *         case the stream will be read and the payload will become the fully read stream.
     * @throws TransformerException if a transformer cannot be found or there is an error during
     *                              transformation of the payload.
     * @since 3.0
     */
    @SuppressWarnings("unchecked")
    protected <T> T getPayload(DataType<T> resultType, String encoding) throws TransformerException {
        // Handle null by ignoring the request
        if (resultType == null) {
            throw new IllegalArgumentException(CoreMessages.objectIsNull("resultType").getMessage());
        }

        DataType source = DataTypeFactory.createFromObject(this);

        // If no conversion is necessary, just return the payload as-is
        if (resultType.isCompatibleWith(source)) {
            return (T) getPayload();
        }

        // The transformer to execute on this message
        Transformer transformer = muleContext.getRegistry().lookupTransformer(source, resultType);
        if (transformer == null) {
            throw new TransformerException(CoreMessages.noTransformerFoundForMessage(source, resultType));
        }

        // Pass in the message itself
        Object result = transformer.transform(this, encoding);

        // Unless we disallow Object.class as a valid return type we need to do this extra check
        if (!resultType.getType().isAssignableFrom(result.getClass())) {
            throw new TransformerException(CoreMessages.transformOnObjectNotOfSpecifiedType(resultType, result));
        }

        // If the payload is a stream and we've consumed it, then we should set the payload on the
        // message. This is the only time this method will alter the payload on the message
        if (isPayloadConsumed(source.getType())) {
            setPayload(result);
        }

        return (T) result;
    }

    /**
     * Checks if the payload has been consumed for this message. This only applies to Streaming payload types
     * since once the stream has been read, the payload of the message should be updated to represent the data read
     * from the stream
     *
     * @param inputCls the input type of the message payload
     * @return true if the payload message type was stream-based, false otherwise
     */
    protected boolean isPayloadConsumed(Class<?> inputCls) {
        return InputStream.class.isAssignableFrom(inputCls) || isConsumedFromAdditional(inputCls);
    }

    private boolean isConsumedFromAdditional(Class<?> inputCls) {
        if (consumableClasses.isEmpty()) {
            return false;
        }

        for (Class<?> c : consumableClasses) {
            if (c.isAssignableFrom(inputCls)) {
                return true;
            }
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object getOriginalPayload() {
        return originalPayload;
    }

    public void setInboundProperty(String key, Object value) {
        setProperty(key, value, PropertyScope.INBOUND);
    }

    @Override
    public void setInvocationProperty(String key, Object value) {
        setProperty(key, value, PropertyScope.INVOCATION);
    }

    @Override
    public void setOutboundProperty(String key, Object value) {
        setProperty(key, value, PropertyScope.OUTBOUND);
    }

    @Override
    public void setSessionProperty(String key, Object value) {
        setProperty(key, value, PropertyScope.SESSION);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setProperty(String key, Object value, PropertyScope scope) {
        assertAccess(WRITE);
        if (key != null) {
            if (value != null) {
                properties.setProperty(key, value, scope);
            } else {
                logger.warn(
                        "setProperty(key, value) called with null value; removing key: " + key
                                + "; please report the following stack trace to " + MuleManifest.getDevListEmail(),
                        new Throwable());
                properties.removeProperty(key);
            }
        } else {
            logger.warn(
                    "setProperty(key, value) ignored because of null key for object: " + value
                            + "; please report the following stack trace to " + MuleManifest.getDevListEmail(),
                    new Throwable());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public Object getProperty(String key) {
        assertAccess(READ);
        return properties.getProperty(key, PropertyScope.OUTBOUND);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object removeProperty(String key) {
        //TODO
        //logger.warn("MuleMessage.removeProperty() method is deprecated, use MuleMessage.removeProperty(String, PropertyScope) instead.  This method will be removed in the next point release");
        //return removeProperty(key, PropertyScope.OUTBOUND);
        assertAccess(WRITE);
        return properties.removeProperty(key);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object removeProperty(String key, PropertyScope scope) {
        assertAccess(WRITE);
        return properties.removeProperty(key, scope);
    }

    /**
     * Set a property on the message. This method will now set a value on the outbound scope only.
     *
     * @param key   the key on which to associate the value
     * @param value the property value
     * @see #setInboundProperty(String, Object)
     * @see #setInvocationProperty(String, Object)
     * @see #setOutboundProperty(String, Object)
     * @see #setSessionProperty(String, Object)
     * @deprecated use {@link #setProperty(String, Object, org.mule.api.transport.PropertyScope)} or
     *             preferrably any of the scope-specific set methods.
     */
    @Override
    @Deprecated
    public void setProperty(String key, Object value) {
        assertAccess(WRITE);
        if (key != null) {
            if (value != null) {
                properties.setProperty(key, value, PropertyScope.OUTBOUND);
            } else {
                logger.warn(
                        "setProperty(key, value) called with null value; removing key: " + key
                                + "; please report the following stack trace to " + MuleManifest.getDevListEmail(),
                        new Throwable());
                properties.removeProperty(key);
            }
        } else {
            logger.warn(
                    "setProperty(key, value) ignored because of null key for object: " + value
                            + "; please report the following stack trace to " + MuleManifest.getDevListEmail(),
                    new Throwable());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final String getPayloadAsString() throws Exception {
        assertAccess(READ);
        return getPayloadAsString(getEncoding());
    }

    /**
    * {@inheritDoc}
    */
    @Override
    public String getPayloadForLogging(String encoding) {
        try {
            return getPayloadAsString(encoding);
        } catch (Exception e) {
            return "[Message could not be converted to string]";
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getPayloadForLogging() {
        try {
            return getPayloadAsString();
        } catch (Exception e) {
            return "[Message could not be converted to string]";
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public byte[] getPayloadAsBytes() throws Exception {
        assertAccess(READ);
        if (cache != null) {
            return cache;
        }
        byte[] result = getPayload(DataType.BYTE_ARRAY_DATA_TYPE);
        if (muleContext.getConfiguration().isCacheMessageAsBytes()) {
            cache = result;
        }
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getPayloadAsString(String encoding) throws Exception {
        assertAccess(READ);
        if (cache != null) {
            return new String(cache, encoding);
        }
        String result = getPayload(DataType.STRING_DATA_TYPE, encoding);
        if (muleContext.getConfiguration().isCacheMessageAsBytes()) {
            cache = result.getBytes(encoding);
        }
        return result;
    }

    /**
     * {@inheritDoc}
     *
     * @deprecated use {@link #getPropertyNames(org.mule.api.transport.PropertyScope)}
     */
    @Override
    @Deprecated
    public Set<String> getPropertyNames() {
        //TODO logger.warn("MuleMessage.getPropertyNames() method is deprecated, use MuleMessage.getOutboundPropertyNames() instead.  This method will be removed in the next point release");
        //return getOutboundPropertyNames();
        assertAccess(READ);
        return properties.getPropertyNames();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<String> getPropertyNames(PropertyScope scope) {
        assertAccess(READ);
        return properties.getScopedProperties(scope).keySet();
    }

    @Override
    public Set<String> getInvocationPropertyNames() {
        return getPropertyNames(PropertyScope.INVOCATION);
    }

    @Override
    public Set<String> getInboundPropertyNames() {
        return getPropertyNames(PropertyScope.INBOUND);
    }

    @Override
    public Set<String> getOutboundPropertyNames() {
        return getPropertyNames(PropertyScope.OUTBOUND);
    }

    @Override
    public Set<String> getSessionPropertyNames() {
        return getPropertyNames(PropertyScope.SESSION);
    }

    //** {@inheritDoc} */

    /**
     * {@inheritDoc}
     */
    @Override
    public String getUniqueId() {
        assertAccess(READ);
        return id;
    }

    public void setUniqueId(String uid) {
        assertAccess(WRITE);
        id = uid;
    }

    @Override
    public String getMessageRootId() {
        assertAccess(READ);
        return rootId;
    }

    @Override
    public void setMessageRootId(String rid) {
        assertAccess(WRITE);
        rootId = rid;
    }

    @Override
    public void propagateRootId(MuleMessage parent) {
        assertAccess(WRITE);
        if (parent != null) {
            rootId = parent.getMessageRootId();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object getProperty(String name, Object defaultValue) {
        //TODO logger.warn("MuleMessage.getProperty() method is deprecated, use MuleMessage.getOutboundProperty() instead.  This method will be removed in the next point release");
        //return getOutboundProperty(name, defaultValue);
        assertAccess(READ);
        return properties.getProperty(name, defaultValue);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProperty(String name, PropertyScope scope) {
        assertAccess(READ);
        return (T) properties.getProperty(name, scope);
    }

    @Override
    public <T> T getInboundProperty(String name, T defaultValue) {
        return getProperty(name, PropertyScope.INBOUND, defaultValue);
    }

    @Override
    public <T> T getInboundProperty(String name) {
        return getProperty(name, PropertyScope.INBOUND, (T) null);
    }

    @Override
    public <T> T getInvocationProperty(String name, T defaultValue) {
        return getProperty(name, PropertyScope.INVOCATION, defaultValue);
    }

    @Override
    public <T> T getInvocationProperty(String name) {
        return getInvocationProperty(name, (T) null);
    }

    @Override
    public <T> T getOutboundProperty(String name, T defaultValue) {
        return getProperty(name, PropertyScope.OUTBOUND, defaultValue);
    }

    @Override
    public <T> T getOutboundProperty(String name) {
        return getOutboundProperty(name, (T) null);
    }

    @Override
    public <T> T getSessionProperty(String name, T defaultValue) {
        return getProperty(name, PropertyScope.SESSION, defaultValue);
    }

    @Override
    public <T> T getSessionProperty(String name) {
        return getSessionProperty(name, (T) null);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProperty(String name, PropertyScope scope, T defaultValue) {
        assertAccess(READ);
        T result;

        //Note that we need to keep the (redundant) casts in here because the compiler compiler complains
        //about primitive types being cast to a generic type
        if (defaultValue instanceof Boolean) {
            result = (T) (Boolean) ObjectUtils.getBoolean(getProperty(name, scope), (Boolean) defaultValue);
        } else if (defaultValue instanceof Byte) {
            result = (T) (Byte) ObjectUtils.getByte(getProperty(name, scope), (Byte) defaultValue);
        } else if (defaultValue instanceof Integer) {
            result = (T) (Integer) ObjectUtils.getInt(getProperty(name, scope), (Integer) defaultValue);
        } else if (defaultValue instanceof Short) {
            result = (T) (Short) ObjectUtils.getShort(getProperty(name, scope), (Short) defaultValue);
        } else if (defaultValue instanceof Long) {
            result = (T) (Long) ObjectUtils.getLong(getProperty(name, scope), (Long) defaultValue);
        } else if (defaultValue instanceof Float) {
            result = (T) (Float) ObjectUtils.getFloat(getProperty(name, scope), (Float) defaultValue);
        } else if (defaultValue instanceof Double) {
            result = (T) (Double) ObjectUtils.getDouble(getProperty(name, scope), (Double) defaultValue);
        } else if (defaultValue instanceof String) {
            result = (T) ObjectUtils.getString(getProperty(name, scope), (String) defaultValue);
        } else {
            Object temp = getProperty(name, scope);
            if (temp == null) {
                return defaultValue;
            } else if (defaultValue == null) {
                return (T) temp;
            }
            //If defaultValue is set and the result is not null, then validate that they are assignable
            else if (defaultValue.getClass().isAssignableFrom(temp.getClass())) {
                result = (T) temp;
            } else {
                throw new IllegalArgumentException(
                        CoreMessages.objectNotOfCorrectType(temp.getClass(), defaultValue.getClass()).getMessage());
            }
        }
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setCorrelationId(String id) {
        assertAccess(WRITE);
        if (StringUtils.isNotBlank(id)) {
            setProperty(MuleProperties.MULE_CORRELATION_ID_PROPERTY, id, PropertyScope.OUTBOUND);
        } else {
            removeProperty(MuleProperties.MULE_CORRELATION_ID_PROPERTY);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getCorrelationId() {
        assertAccess(READ);
        String correlationId = getOutboundProperty(MuleProperties.MULE_CORRELATION_ID_PROPERTY);
        if (correlationId == null) {
            correlationId = getInboundProperty(MuleProperties.MULE_CORRELATION_ID_PROPERTY);
        }

        return correlationId;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setReplyTo(Object replyTo) {
        assertAccess(WRITE);
        if (replyTo != null) {
            setProperty(MuleProperties.MULE_REPLY_TO_PROPERTY, replyTo, PropertyScope.OUTBOUND);
        } else {
            removeProperty(MuleProperties.MULE_REPLY_TO_PROPERTY);
            removeProperty(MuleProperties.MULE_REPLY_TO_PROPERTY, PropertyScope.INBOUND);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object getReplyTo() {
        assertAccess(READ);
        Object replyTo = getProperty(MuleProperties.MULE_REPLY_TO_PROPERTY, PropertyScope.OUTBOUND);
        if (replyTo == null) {
            // fallback to inbound, use the requestor's setting if the invocation didn't set any
            replyTo = getProperty(MuleProperties.MULE_REPLY_TO_PROPERTY, PropertyScope.INBOUND);
        }
        return replyTo;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getCorrelationSequence() {
        assertAccess(READ);
        // need to wrap with another getInt() as some transports operate on it as a String
        Object correlationSequence = findPropertyInSpecifiedScopes(
                MuleProperties.MULE_CORRELATION_SEQUENCE_PROPERTY, PropertyScope.OUTBOUND, PropertyScope.INBOUND);
        return ObjectUtils.getInt(correlationSequence, -1);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setCorrelationSequence(int sequence) {
        assertAccess(WRITE);
        setOutboundProperty(MuleProperties.MULE_CORRELATION_SEQUENCE_PROPERTY, sequence);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getCorrelationGroupSize() {
        assertAccess(READ);
        // need to wrap with another getInt() as some transports operate on it as a String
        Object correlationGroupSize = findPropertyInSpecifiedScopes(
                MuleProperties.MULE_CORRELATION_GROUP_SIZE_PROPERTY, PropertyScope.OUTBOUND, PropertyScope.INBOUND);
        return ObjectUtils.getInt(correlationGroupSize, -1);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setCorrelationGroupSize(int size) {
        assertAccess(WRITE);
        setOutboundProperty(MuleProperties.MULE_CORRELATION_GROUP_SIZE_PROPERTY, size);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ExceptionPayload getExceptionPayload() {
        assertAccess(READ);
        return exceptionPayload;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setExceptionPayload(ExceptionPayload exceptionPayload) {
        assertAccess(WRITE);
        this.exceptionPayload = exceptionPayload;
    }

    @Override
    public String toString() {
        assertAccess(READ);
        StringBuffer buf = new StringBuffer(120);
        final String nl = System.getProperty("line.separator");

        // format message for multi-line output, single-line is not readable
        buf.append(nl);
        buf.append(getClass().getName());
        buf.append(nl);
        buf.append("{");
        buf.append(nl);
        buf.append("  id=").append(getUniqueId());
        buf.append(nl);
        buf.append("  payload=").append(getPayload().getClass().getName());
        buf.append(nl);
        buf.append("  correlationId=").append(StringUtils.defaultString(getCorrelationId(), NOT_SET));
        buf.append(nl);
        buf.append("  correlationGroup=").append(getCorrelationGroupSize());
        buf.append(nl);
        buf.append("  correlationSeq=").append(getCorrelationSequence());
        buf.append(nl);
        buf.append("  encoding=").append(getEncoding());
        buf.append(nl);
        buf.append("  exceptionPayload=").append(ObjectUtils.defaultIfNull(exceptionPayload, NOT_SET));
        buf.append(nl);
        buf.append(StringMessageUtils.headersToString(this));
        // no new line here, as headersToString() adds one
        buf.append('}');
        return buf.toString();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public void addAttachment(String name, DataHandler dataHandler) throws Exception {
        logger.warn(
                "MuleMessage.addAttachment() method is deprecated, use MuleMessage.addOutboundAttachment() instead.  This method will be removed in the next point release");
        addOutboundAttachment(name, dataHandler);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public void removeAttachment(String name) throws Exception {
        logger.warn(
                "MuleMessage.removeAttachment() method is deprecated, use MuleMessage.removeOutboundAttachment() instead.  This method will be removed in the next point release");
        removeOutboundAttachment(name);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public DataHandler getAttachment(String name) {
        logger.warn(
                "MuleMessage.getAttachment() method is deprecated, use MuleMessage.getInboundAttachment() instead.  This method will be removed in the next point release");
        return getInboundAttachment(name);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public Set<String> getAttachmentNames() {
        logger.warn(
                "MuleMessage.getAttachmentNames() method is deprecated, use MuleMessage.getInboundAttachmentNames() instead.  This method will be removed in the next point release");
        return getInboundAttachmentNames();
    }

    @Override
    public void addOutboundAttachment(String name, DataHandler dataHandler) throws Exception {
        assertAccess(WRITE);
        outboundAttachments.put(name, dataHandler);
    }

    ///TODO this should not be here, but needed so that a message factory can add attachments
    //This is not part of the API

    public void addInboundAttachment(String name, DataHandler dataHandler) throws Exception {
        assertAccess(WRITE);
        inboundAttachments.put(name, dataHandler);
    }

    @Override
    public void addOutboundAttachment(String name, Object object, String contentType) throws Exception {
        assertAccess(WRITE);
        DataHandler dh;
        if (object instanceof File) {
            if (contentType != null) {
                dh = new DataHandler(new FileInputStream((File) object), contentType);

            } else {
                dh = new DataHandler(new FileDataSource((File) object));
            }
        } else if (object instanceof URL) {
            if (contentType != null) {
                dh = new DataHandler(((URL) object).openStream(), contentType);
            } else {
                dh = new DataHandler((URL) object);
            }
        } else {
            dh = new DataHandler(object, contentType);
        }
        outboundAttachments.put(name, dh);
    }

    @Override
    public void removeOutboundAttachment(String name) throws Exception {
        assertAccess(WRITE);
        outboundAttachments.remove(name);
    }

    @Override
    public DataHandler getInboundAttachment(String name) {
        assertAccess(READ);
        return inboundAttachments.get(name);
    }

    @Override
    public DataHandler getOutboundAttachment(String name) {
        assertAccess(READ);
        return outboundAttachments.get(name);
    }

    @Override
    public Set<String> getInboundAttachmentNames() {
        assertAccess(READ);
        return Collections.unmodifiableSet(inboundAttachments.keySet());
    }

    @Override
    public Set<String> getOutboundAttachmentNames() {
        assertAccess(READ);
        return Collections.unmodifiableSet(outboundAttachments.keySet());
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T findPropertyInAnyScope(String name, T defaultValue) {
        Object value = findPropertyInSpecifiedScopes(name, PropertyScope.OUTBOUND, PropertyScope.INVOCATION,
                PropertyScope.SESSION, PropertyScope.INBOUND);
        if (value == null) {
            return defaultValue;
        }
        return (T) value;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getEncoding() {
        assertAccess(READ);
        String encoding = null;
        if (dataType != null) {
            encoding = dataType.getEncoding();
        }
        if (encoding != null) {
            return encoding;
        }
        encoding = getOutboundProperty(MuleProperties.MULE_ENCODING_PROPERTY);
        if (encoding != null) {
            return encoding;
        } else {
            return System.getProperty(MuleProperties.MULE_ENCODING_SYSTEM_PROPERTY);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setEncoding(String encoding) {
        assertAccess(WRITE);
        if (encoding != null) {
            setOutboundProperty(MuleProperties.MULE_ENCODING_PROPERTY, encoding);
        }
    }

    /**
     * @param mimeType
     * @since 3.0
     */
    public void setMimeType(String mimeType) {
        assertAccess(WRITE);
        if (mimeType != null && !mimeType.equals(MimeTypes.ANY)) {
            String encoding = getEncoding();
            if (encoding != null) {
                mimeType = mimeType + ";charset=" + encoding;
            }
            setOutboundProperty(MuleProperties.CONTENT_TYPE_PROPERTY, mimeType);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addProperties(Map<String, Object> props) {
        addProperties(props, properties.getDefaultScope());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addProperties(Map<String, Object> props, PropertyScope scope) {
        assertAccess(WRITE);
        if (props != null) {
            synchronized (props) {
                for (Map.Entry<String, Object> entry : props.entrySet()) {
                    setProperty(entry.getKey(), entry.getValue(), scope);
                }
            }
        }
    }

    public void addInboundProperties(Map<String, Object> props) {
        properties.addInboundProperties(props);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void clearProperties() {
        assertAccess(WRITE);
        //Inbound scope is read-only
        properties.clearProperties(PropertyScope.INVOCATION);
        properties.clearProperties(PropertyScope.OUTBOUND);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void clearProperties(PropertyScope scope) {
        assertAccess(WRITE);
        properties.clearProperties(scope);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object getPayload() {
        return payload;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized void setPayload(Object payload) {
        if (payload == null) {
            this.payload = NullPayload.getInstance();
        } else {
            this.payload = payload;
        }
        cache = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void release() {
        cache = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void applyTransformers(MuleEvent event, List<? extends Transformer> transformers) throws MuleException {
        applyTransformers(event, transformers, null);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void applyTransformers(MuleEvent event, Transformer... transformers) throws MuleException {
        applyTransformers(event, Arrays.asList(transformers), null);
    }

    @Override
    public void applyTransformers(MuleEvent event, List<? extends Transformer> transformers, Class<?> outputType)
            throws MuleException {
        if (!transformers.isEmpty()) {
            applyAllTransformers(event, transformers);
        }

        if (null != outputType && !getPayload().getClass().isAssignableFrom(outputType)) {
            setPayload(getPayload(DataTypeFactory.create(outputType)));
        }
    }

    protected void applyAllTransformers(MuleEvent event, List<? extends Transformer> transformers)
            throws MuleException {
        if (!transformers.isEmpty()) {
            for (int index = 0; index < transformers.size(); index++) {
                Transformer transformer = transformers.get(index);

                Class<?> srcCls = getPayload().getClass();
                DataType<?> originalSourceType = DataTypeFactory.create(srcCls);

                if (transformer.isSourceDataTypeSupported(originalSourceType)) {
                    transformMessage(event, transformer);
                } else {
                    if (logger.isDebugEnabled()) {
                        logger.debug(
                                "Transformer " + transformer + " doesn't support the source payload: " + srcCls);
                    }

                    if (useExtendedTransformations()) {
                        if (canSkipTransformer(transformers, index)) {
                            continue;
                        }

                        // Resolves implicit conversion if possible
                        Transformer implicitTransformer = muleContext.getDataTypeConverterResolver()
                                .resolve(originalSourceType, transformer.getSourceDataTypes());

                        if (implicitTransformer != null) {
                            transformMessage(event, implicitTransformer);
                            transformMessage(event, transformer);
                        } else {
                            throw new IllegalArgumentException(
                                    "Cannot apply transformer " + transformer + " on source payload: " + srcCls);
                        }
                    } else if (!transformer.isIgnoreBadInput()) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Exiting from transformer chain (ignoreBadInput = false)");
                        }
                        break;
                    }
                }
            }
        }
    }

    private boolean canSkipTransformer(List<? extends Transformer> transformers, int index) {
        Transformer transformer = transformers.get(index);

        boolean skipConverter = false;

        if (transformer instanceof Converter) {
            if (index == transformers.size() - 1) {
                try {
                    TransformerUtils.checkTransformerReturnClass(transformer, payload);
                    skipConverter = true;
                } catch (TransformerException e) {
                    // Converter cannot be skipped
                }
            } else {
                skipConverter = true;
            }
        }

        if (skipConverter) {
            logger.debug("Skipping converter: " + transformer);
        }

        return skipConverter;
    }

    private boolean useExtendedTransformations() {
        boolean result = true;
        if (muleContext != null && muleContext.getConfiguration() != null) {
            result = muleContext.getConfiguration().useExtendedTransformations();
        }

        return result;
    }

    private void transformMessage(MuleEvent event, Transformer transformer)
            throws TransformerMessagingException, TransformerException {
        Object result;
        if (transformer instanceof MessageTransformer) {
            result = ((MessageTransformer) transformer).transform(this, event);
        } else {
            result = transformer.transform(this);
        }
        // Update the RequestContext with the result of the transformation.
        RequestContext.internalRewriteEvent(this, false);

        if (originalPayload == null && muleContext.getConfiguration().isCacheMessageOriginalPayload()) {
            originalPayload = payload;
        }

        if (result instanceof MuleMessage) {
            if (!result.equals(this)) {
                // Only copy the payload and properties of mule message
                // transformer result if the message is a different
                // instance
                synchronized (this) {
                    MuleMessage resultMessage = (MuleMessage) result;
                    setPayload(resultMessage.getPayload());
                    originalPayload = resultMessage.getOriginalPayload();
                    copyMessageProperties(resultMessage);
                    copyAttachments(resultMessage);
                }
            }
        } else {
            setPayload(result);
        }
        setDataType(transformer.getReturnDataType());
    }

    protected void setDataType(DataType<?> dt) {
        dataType = dt;
        setEncoding(dt == null ? null : dt.getEncoding());
        setMimeType(dt == null ? null : dt.getMimeType());
    }

    //////////////////////////////// ThreadSafeAccess Impl ///////////////////////////////

    /**
     * {@inheritDoc}
     */
    @Override
    public ThreadSafeAccess newThreadCopy() {
        return new DefaultMuleMessage(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void resetAccessControl() {
        // just reset the internal state here as this method is explicitly intended not to
        // be used from the outside
        if (ownerThread != null) {
            ownerThread.set(null);
        }
        if (mutable != null) {
            mutable.set(true);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void assertAccess(boolean write) {
        if (AccessControl.isAssertMessageAccess()) {
            initAccessControl();
            setOwner();
            checkMutable(write);
        }
    }

    private synchronized void initAccessControl() {
        if (null == ownerThread) {
            ownerThread = new AtomicReference<Thread>();
        }
        if (null == mutable) {
            mutable = new AtomicBoolean(true);
        }
    }

    private void setOwner() {
        if (null == ownerThread.get()) {
            ownerThread.compareAndSet(null, Thread.currentThread());
        }
    }

    private void checkMutable(boolean write) {

        // IF YOU SEE AN EXCEPTION THAT IS RAISED FROM WITHIN THIS CODE
        // ============================================================
        //
        // First, understand that the exception here is not the "real" problem.  These exceptions
        // give early warning of a much more serious issue that results in unreliable and unpredictable
        // code - more than one thread is attempting to change the contents of a message.
        //
        // Having said that, you can disable these exceptions by defining
        // MuleProperties.MULE_THREAD_UNSAFE_MESSAGES_PROPERTY (mule.disable.threadsafemessages)
        // (i.e., by adding -Dmule.disable.threadsafemessages=true to the java command line).
        //
        // To remove the underlying cause, however, you probably need to do one of:
        //
        // - make sure that the message you are using correctly implements the ThreadSafeAccess
        //   interface
        //
        // - make sure that dispatcher and receiver classes copy ThreadSafeAccess instances when
        //   they are passed between threads

        Thread currentThread = Thread.currentThread();
        if (currentThread.equals(ownerThread.get())) {
            if (write && !mutable.get()) {
                if (isDisabled()) {
                    logger.warn("Writing to immutable message (exception disabled)");
                } else {
                    throw newException("Cannot write to immutable message");
                }
            }
        } else {
            if (write) {
                if (isDisabled()) {
                    logger.warn("Non-owner writing to message (exception disabled)");
                } else {
                    throw newException("Only owner thread can write to message: " + ownerThread.get() + "/"
                            + Thread.currentThread());
                }
            }
        }
    }

    protected boolean isDisabled() {
        return !AccessControl.isFailOnMessageScribbling();
    }

    protected IllegalStateException newException(String message) {
        IllegalStateException exception = new IllegalStateException(message);
        logger.warn("Message access violation", exception);
        return exception;
    }

    /**
     * Determines if the payload of this message is consumable i.e. it can't be read
     * more than once. This is here temporarily without adding to MuleMessage
     * interface until MULE-4256 is implemented.
     */
    public boolean isConsumable() {
        return isConsumedFromAdditional(this.getPayload().getClass());
    }

    public static class SerializedDataHandler implements Serializable {
        private static final long serialVersionUID = 1L;

        private DataHandler handler;
        private String contentType;
        private Object contents;

        public SerializedDataHandler(String name, DataHandler handler, MuleContext muleContext) throws IOException {
            if (handler != null && !(handler instanceof Serializable)) {
                contentType = handler.getContentType();
                Object theContent = handler.getContent();
                if (theContent instanceof Serializable) {
                    contents = theContent;
                } else {
                    try {
                        DataType source = DataTypeFactory.createFromObject(theContent);
                        Transformer transformer = muleContext.getRegistry().lookupTransformer(source,
                                DataType.BYTE_ARRAY_DATA_TYPE);
                        if (transformer == null) {
                            throw new TransformerException(CoreMessages.noTransformerFoundForMessage(source,
                                    DataType.BYTE_ARRAY_DATA_TYPE));
                        }
                        contents = transformer.transform(theContent);
                    } catch (TransformerException ex) {
                        String message = String.format(
                                "Unable to serialize the attachment %s, which is of type %s with contents of type %s",
                                name, handler.getClass(), theContent.getClass());
                        logger.error(message);
                        throw new IOException(message);
                    }
                }
            } else {
                this.handler = handler;
            }
        }

        public DataHandler getHandler() {
            return contents != null ? new DataHandler(contents, contentType) : handler;
        }
    }

    private void writeObject(ObjectOutputStream out) throws Exception {
        out.defaultWriteObject();
        if (payload instanceof Serializable) {
            out.writeBoolean(true);
            out.writeObject(payload);
        } else {
            out.writeBoolean(false);
            byte[] serializablePayload = getPayloadAsBytes();
            out.writeInt(serializablePayload.length);
            out.write(serializablePayload);
        }
        out.writeObject(serializeAttachments(inboundAttachments));
        out.writeObject(serializeAttachments(outboundAttachments));

        // TODO: we don't serialize the originalPayload for now
    }

    private Map<String, SerializedDataHandler> serializeAttachments(Map<String, DataHandler> attachments)
            throws IOException {
        Map<String, SerializedDataHandler> toWrite;
        if (attachments == null) {
            toWrite = null;
        } else {
            toWrite = new HashMap<String, SerializedDataHandler>(attachments.size());
            for (Map.Entry<String, DataHandler> entry : attachments.entrySet()) {
                String name = entry.getKey();
                toWrite.put(name, new SerializedDataHandler(name, entry.getValue(), muleContext));
            }
        }

        return toWrite;
    }

    private Map<String, DataHandler> deserializeAttachments(Map<String, SerializedDataHandler> attachments)
            throws IOException {
        Map<String, DataHandler> toReturn;
        if (attachments == null) {
            toReturn = null;
        } else {
            toReturn = new HashMap<String, DataHandler>(attachments.size());
            for (Map.Entry<String, SerializedDataHandler> entry : attachments.entrySet()) {
                toReturn.put(entry.getKey(), entry.getValue().getHandler());
            }
        }

        return toReturn;
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();

        boolean payloadWasSerialized = in.readBoolean();
        if (payloadWasSerialized) {
            payload = in.readObject();
        } else {
            int payloadSize = in.readInt();
            byte[] serializedPayload = new byte[payloadSize];
            in.read(serializedPayload);
            payload = serializedPayload;
        }
        inboundAttachments = deserializeAttachments((Map<String, SerializedDataHandler>) in.readObject());
        outboundAttachments = deserializeAttachments((Map<String, SerializedDataHandler>) in.readObject());
    }

    /**
     * Invoked after deserialization. This is called when the marker interface
     * {@link org.mule.util.store.DeserializationPostInitialisable} is used. This will get invoked
     * after the object has been deserialized passing in the current mulecontext when using either
     * {@link org.mule.transformer.wire.SerializationWireFormat},
     * {@link org.mule.transformer.wire.SerializedMuleMessageWireFormat} or the
     * {@link org.mule.transformer.simple.ByteArrayToSerializable} transformer.
     *
     * @param context the current muleContext instance
     * @throws MuleException if there is an error initializing
     */
    public void initAfterDeserialisation(MuleContext context) throws MuleException {
        this.muleContext = context;
    }

    @Override
    public DataType<?> getDataType() {
        return dataType;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public int getIntProperty(String name, int defaultValue) {
        assertAccess(READ);
        logger.warn(
                "MuleMessage.getIntProperty() method is deprecated, use MuleMessage.getInboundProperty() instead.  This method will be removed in the next point release");
        return getInboundProperty(name, defaultValue);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public long getLongProperty(String name, long defaultValue) {
        assertAccess(READ);
        logger.warn(
                "MuleMessage.getLongProperty() method is deprecated, use MuleMessage.getInboundProperty() instead.  This method will be removed in the next point release");
        return getInboundProperty(name, defaultValue);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public double getDoubleProperty(String name, double defaultValue) {
        assertAccess(READ);
        logger.warn(
                "MuleMessage.getDoubleProperty() method is deprecated, use MuleMessage.getInboundProperty() instead.  This method will be removed in the next point release");
        return getInboundProperty(name, defaultValue);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public boolean getBooleanProperty(String name, boolean defaultValue) {
        assertAccess(READ);
        logger.warn(
                "MuleMessage.getBooleanProperty() method is deprecated, use MuleMessage.getInboundProperty() instead.  This method will be removed in the next point release");
        return getInboundProperty(name, defaultValue);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public void setBooleanProperty(String name, boolean value) {
        assertAccess(WRITE);
        logger.warn(
                "MuleMessage.setBooleanProperty() method is deprecated, use MuleMessage.setOutboundProperty() instead.  This method will be removed in the next point release");
        setOutboundProperty(name, value);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public void setIntProperty(String name, int value) {
        assertAccess(WRITE);
        logger.warn(
                "MuleMessage.setIntProperty() method is deprecated, use MuleMessage.setOutboundProperty() instead.  This method will be removed in the next point release");
        setOutboundProperty(name, value);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public void setLongProperty(String name, long value) {
        assertAccess(WRITE);
        logger.warn(
                "MuleMessage.setLongProperty() method is deprecated, use MuleMessage.setOutboundProperty() instead.  This method will be removed in the next point release");
        setOutboundProperty(name, value);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public void setDoubleProperty(String name, double value) {
        assertAccess(WRITE);
        logger.warn(
                "MuleMessage.setDoubleProperty() method is deprecated, use MuleMessage.setOutboundProperty() instead.  This method will be removed in the next point release");
        setOutboundProperty(name, value);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public String getStringProperty(String name, String defaultValue) {
        assertAccess(READ);
        logger.warn(
                "MuleMessage.getStringProperty() method is deprecated, use MuleMessage.getInboundProperty() instead.  This method will be removed in the next point release");
        return getInboundProperty(name, defaultValue);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public void setStringProperty(String name, String value) {
        assertAccess(WRITE);
        logger.warn(
                "MuleMessage.setStringProperty() method is deprecated, use MuleMessage.setOutboundProperty() instead.  This method will be removed in the next point release");
        setOutboundProperty(name, value);
    }

    /**
     * Find property in one of the specified scopes, in order
     */
    @SuppressWarnings("unchecked")
    public <T> T findPropertyInSpecifiedScopes(String name, PropertyScope... scopesToSearch) {
        for (PropertyScope scope : scopesToSearch) {
            Object result = getProperty(name, scope);
            if (result != null) {
                return (T) result;
            }
        }
        return null;
    }

    @Override
    public MuleMessage createInboundMessage() throws Exception {
        DefaultMuleMessage newMessage = new DefaultMuleMessage(getPayload(), this, getMuleContext());
        copyToInbound(newMessage);
        return newMessage;
    }

    /**
     * copy outbound artifacts to inbound artifacts in the new message
     */
    protected void copyToInbound(DefaultMuleMessage newMessage) throws Exception {
        // Copy message, but put all outbound properties and attachments on inbound scope.
        // We ignore inbound and invocation scopes since the VM receiver needs to behave the
        // same way as any other receiver in Mule and would only receive inbound headers
        // and attachments
        Map<String, DataHandler> attachments = new HashMap<String, DataHandler>(3);
        for (String name : getOutboundAttachmentNames()) {
            attachments.put(name, getOutboundAttachment(name));
        }

        Map<String, Object> newInboundProperties = new HashMap<String, Object>(3);
        for (String name : getOutboundPropertyNames()) {
            newInboundProperties.put(name, getOutboundProperty(name));
        }

        newMessage.clearProperties(PropertyScope.INBOUND);
        newMessage.clearProperties(PropertyScope.INVOCATION);
        newMessage.clearProperties(PropertyScope.OUTBOUND);

        for (Map.Entry<String, Object> s : newInboundProperties.entrySet()) {
            newMessage.setInboundProperty(s.getKey(), s.getValue());
        }

        newMessage.inboundAttachments.clear();
        newMessage.outboundAttachments.clear();

        for (Map.Entry<String, DataHandler> s : attachments.entrySet()) {
            newMessage.addInboundAttachment(s.getKey(), s.getValue());
        }

        newMessage.setCorrelationId(getCorrelationId());
        newMessage.setCorrelationGroupSize(getCorrelationGroupSize());
        newMessage.setCorrelationSequence(getCorrelationSequence());
        newMessage.setReplyTo(getReplyTo());
        newMessage.setEncoding(getEncoding());
    }

    void setSessionProperties(Map<String, Object> sessionProperties) {
        properties.sessionMap = sessionProperties;
    }

    void setInvocationProperties(Map<String, Object> invocationProperties) {
        properties.invocationMap = invocationProperties;
    }

}