com.adaptris.core.jms.AutoConvertMessageTranslator.java Source code

Java tutorial

Introduction

Here is the source code for com.adaptris.core.jms.AutoConvertMessageTranslator.java

Source

/*
 * Copyright 2015 Adaptris Ltd.
 * 
 * Licensed 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 com.adaptris.core.jms;

import static com.adaptris.core.jms.MetadataHandler.isReserved;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.Set;

import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.TextMessage;

import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;

import com.adaptris.annotation.AutoPopulated;
import com.adaptris.annotation.DisplayOrder;
import com.adaptris.annotation.InputFieldDefault;
import com.adaptris.core.AdaptrisMessage;
import com.adaptris.core.MetadataElement;
import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
 * Perform a best guess routine on the JMSMessage when translating to an AdaptrisMessage.
 * <p>
 * This handles the types {@linkplain TextMessage}, {@linkplain BytesMessage}, {@linkplain ObjectMessage} by delegating to the
 * correct {@linkplain MessageTypeTranslator} implementation. The mapping from {@linkplain MapMessage} to AdaptrisMessage is
 * simplistic; the name value pairs (assumed to be String (or convertable to String)) are set as AdaptrisMessage metadata, the
 * resulting payload is empty. The mapping from AdaptrisMessage to {@linkplain MapMessage} results in all metadata being mapped as
 * name value pairs in the MapMessage; the payload is ignored.
 * </p>
 * <p>
 * If you have used this translator for your consumer we add a new meta-data item to the {@linkplain AdaptrisMessage}} named "adpmessagetype".
 * The value of this item will be one of "Text", "Bytes", "Map" or "Object".
 * This allows you to set the following item; "convert-back-to-consumed-type" on the AutoConvertMessageTranslator for your JMS producer to "true" (default is false).
 * In this case this translator will attempt to translate the produced message to the same message type we consumed.
 * </p>
 * <p>
 * By default the metadata item "adpmessagetype" is removed before we convert the Adaptris message to a JMS message, so that the JMS message
 * does not contain the key.  You can override the removal of the key should you have multiple producers, by setting remove-original-message-type-key to "true".
 * </p>
 * <p>
 * If this converter cannot find an appropriate translator then a very basic translation will be applied. This will NOT include any
 * payload translation. You will also see a warning in the logs to notify you a basic translation has been applied.
 * </p>
 * <p>
 * It is primarily a fallback translator to mitigate configuration errors.
 * </p>
 * 
 * @config auto-convert-message-translator
 * 
 * @author lchan
 */
@XStreamAlias("auto-convert-message-translator")
@DisplayOrder(order = { "metadataFilter", "moveMetadata", "moveJmsHeaders", "reportAllErrors" })
public class AutoConvertMessageTranslator extends MessageTypeTranslatorImp {

    private static final String CONSUMED_MESSAGE_TYPE_KEY = "adpmessagetype";

    private static final boolean DEFAULT_CONVERT_BACK_TO_ORIGINAL_TYPE = false;

    /**
     * javax.jms.Message types that are supported by this translator.
     *
     *
     */
    public static enum SupportedMessageType {
        /** Represents {@linkplain javax.jms.TextMessage} */
        Text {
            @Override
            MessageTypeTranslatorImp create(MessageTypeTranslatorImp parent) throws JMSException {
                return copyConfiguration(parent, new TextMessageTranslator());
            }

            @Override
            boolean isSupported(Message m) {
                return m instanceof TextMessage;
            }
        },
        /** Represents {@linkplain javax.jms.BytesMessage} */
        Bytes {
            @Override
            MessageTypeTranslatorImp create(MessageTypeTranslatorImp parent) throws JMSException {
                return copyConfiguration(parent, new BytesMessageTranslator());
            }

            @Override
            boolean isSupported(Message m) {
                return m instanceof BytesMessage;
            }
        },
        /** Represents {@linkplain javax.jms.ObjectMessage} */
        Object {
            @Override
            MessageTypeTranslatorImp create(MessageTypeTranslatorImp parent) throws JMSException {
                return copyConfiguration(parent, new ObjectMessageTranslator());
            }

            @Override
            boolean isSupported(Message m) {
                return m instanceof ObjectMessage;
            }
        },
        /** Represents {@linkplain javax.jms.MapMessage} */
        Map {
            @Override
            MessageTypeTranslatorImp create(MessageTypeTranslatorImp parent) throws JMSException {
                return copyConfiguration(parent, new NaiveMapMessageTranslator());
            }

            @Override
            boolean isSupported(Message m) {
                return m instanceof MapMessage;
            }

        };

        abstract MessageTypeTranslatorImp create(MessageTypeTranslatorImp parent) throws JMSException;

        abstract boolean isSupported(Message m) throws JMSException;

        public static MessageTypeTranslatorImp createFallback(MessageTypeTranslatorImp parent) throws JMSException {
            return copyConfiguration(parent, new BasicJavaxJmsMessageTranslator());
        }
    }

    private String jmsOutputType;

    @AutoPopulated
    @InputFieldDefault(value = "false")
    private Boolean convertBackToConsumedType;
    @AutoPopulated
    @InputFieldDefault(value = "true")
    private Boolean removeOriginalMessageTypeKey;

    /**
     * Default constructor.
     * <p>
     * <ul>
     * <li>jms-output-type is "Text"</li>
     * </ul>
     */
    public AutoConvertMessageTranslator() {
        super();
        setJmsOutputType(SupportedMessageType.Text.name());
    }

    public Message translate(AdaptrisMessage msg) throws JMSException {
        Message result = null;
        MessageTypeTranslator mt = null;

        boolean convertingBackToOriginal = false;
        try {
            if (convertBackToConsumedType()
                    && (!StringUtils.isEmpty(msg.getMetadataValue(CONSUMED_MESSAGE_TYPE_KEY)))) {
                SupportedMessageType messageType = null;
                try {
                    messageType = SupportedMessageType.valueOf(msg.getMetadataValue(CONSUMED_MESSAGE_TYPE_KEY));
                    mt = messageType.create(this);
                    convertingBackToOriginal = true;
                } catch (IllegalArgumentException ex) {
                    log.warn("Cannot convert to type: {}", msg.getMetadataValue(CONSUMED_MESSAGE_TYPE_KEY));
                    for (SupportedMessageType mti : SupportedMessageType.values()) {
                        if (mti.name().equalsIgnoreCase(getJmsOutputType())) {
                            mt = mti.create(this);
                            break;
                        }
                    }
                }
            } else {
                for (SupportedMessageType mti : SupportedMessageType.values()) {
                    if (mti.name().equalsIgnoreCase(getJmsOutputType())) {
                        mt = mti.create(this);
                        break;
                    }
                }
            }
            if (mt != null) {
                if ((convertingBackToOriginal) && (removeOriginalMessageTypeKey()))
                    msg.removeMessageHeader(CONSUMED_MESSAGE_TYPE_KEY);
                start(mt);
                result = mt.translate(msg);
            } else {
                mt = SupportedMessageType.createFallback(this);
                start(mt);
                result = mt.translate(msg);
            }
        } finally {
            stop(mt);
        }
        return result;
    }

    public AdaptrisMessage translate(Message msg) throws JMSException {
        AdaptrisMessage result = null;
        MessageTypeTranslator mt = null;
        String messageType = null;
        try {
            for (SupportedMessageType mti : SupportedMessageType.values()) {
                if (mti.isSupported(msg)) {
                    mt = mti.create(this);
                    messageType = mti.name();
                    break;
                }
            }
            if (mt != null) {
                start(mt);
                log.trace("Converting [{}] using [{}]", msg.getClass().getSimpleName(),
                        mt.getClass().getSimpleName());
                result = mt.translate(msg);
                result.addMessageHeader(CONSUMED_MESSAGE_TYPE_KEY, messageType);
            }
        } finally {
            stop(mt);
        }
        if (result == null) {
            mt = SupportedMessageType.createFallback(this);
            start(mt);
            result = mt.translate(msg);
        }
        return result;
    }

    public String getJmsOutputType() {
        return jmsOutputType;
    }

    /**
     * Set the javax.jms.Message implementation that this MessageTranslator
     * creates when converting from AdaptrisMessage.
     *
     * @param outputType the output type, one of Object, Text, Bytes, Map
     * @see SupportedMessageType
     */
    public void setJmsOutputType(String outputType) {
        jmsOutputType = outputType;
    }

    public Boolean getConvertBackToConsumedType() {
        return convertBackToConsumedType;
    }

    boolean convertBackToConsumedType() {
        return BooleanUtils.toBooleanDefaultIfNull(getConvertBackToConsumedType(),
                DEFAULT_CONVERT_BACK_TO_ORIGINAL_TYPE);
    }

    public void setConvertBackToConsumedType(Boolean convertBackToConsumedType) {
        this.convertBackToConsumedType = convertBackToConsumedType;
    }

    public Boolean getRemoveOriginalMessageTypeKey() {
        return removeOriginalMessageTypeKey;
    }

    public void setRemoveOriginalMessageTypeKey(Boolean removeOriginalMessageTypeKey) {
        this.removeOriginalMessageTypeKey = removeOriginalMessageTypeKey;
    }

    boolean removeOriginalMessageTypeKey() {
        return BooleanUtils.toBooleanDefaultIfNull(getRemoveOriginalMessageTypeKey(), true);
    }

    private static class NaiveMapMessageTranslator extends MessageTypeTranslatorImp {

        public Message translate(AdaptrisMessage msg) throws JMSException {
            MapMessage jmsMsg = session.createMapMessage();
            Set metadata = msg.getMetadata();
            for (Iterator i = metadata.iterator(); i.hasNext();) {
                MetadataElement element = (MetadataElement) i.next();
                if (!isReserved(element.getKey())) {
                    jmsMsg.setString(element.getKey(), element.getValue());
                }
            }
            return helper.moveMetadata(msg, jmsMsg);

        }

        public AdaptrisMessage translate(Message msg) throws JMSException {
            AdaptrisMessage result = currentMessageFactory().newMessage();
            MapMessage jmsMsg = (MapMessage) msg;
            for (Enumeration e = jmsMsg.getMapNames(); e.hasMoreElements();) {
                String mapName = (String) e.nextElement();
                result.addMetadata(mapName, jmsMsg.getString(mapName));
            }
            return helper.moveMetadata(msg, result);
        }
    }

}