org.mule.transformer.AbstractTransformer.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.transformer.AbstractTransformer.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.transformer;

import org.mule.DefaultMessageCollection;
import org.mule.DefaultMuleEvent;
import org.mule.DefaultMuleMessage;
import org.mule.api.AnnotatedObject;
import org.mule.api.MuleContext;
import org.mule.api.MuleEvent;
import org.mule.api.MuleException;
import org.mule.api.MuleMessage;
import org.mule.api.config.MuleProperties;
import org.mule.api.endpoint.ImmutableEndpoint;
import org.mule.api.lifecycle.InitialisationException;
import org.mule.api.transformer.DataType;
import org.mule.api.transformer.Transformer;
import org.mule.api.transformer.TransformerException;
import org.mule.api.transformer.TransformerMessagingException;
import org.mule.config.i18n.CoreMessages;
import org.mule.config.i18n.Message;
import org.mule.transformer.types.DataTypeFactory;
import org.mule.transformer.types.SimpleDataType;
import org.mule.transport.NullPayload;
import org.mule.util.ClassUtils;
import org.mule.util.StringMessageUtils;
import org.mule.util.StringUtils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.activation.MimeType;
import javax.activation.MimeTypeParseException;
import javax.xml.namespace.QName;
import javax.xml.transform.stream.StreamSource;

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

/**
 * <code>AbstractTransformer</code> is a base class for all transformers.
 * Transformations transform one object into another.
 */

public abstract class AbstractTransformer implements Transformer, AnnotatedObject {
    public static final DataType<MuleMessage> MULE_MESSAGE_DATA_TYPE = new SimpleDataType<MuleMessage>(
            MuleMessage.class);

    protected MuleContext muleContext;

    protected final Log logger = LogFactory.getLog(getClass());

    /**
     * The return type that will be returned by the {@link #transform} method is
     * called
     */
    protected DataType<?> returnType = new SimpleDataType<Object>(Object.class);

    /**
     * The name that identifies this transformer. If none is set the class name of
     * the transformer is used
     */
    protected String name = null;

    /**
     * The endpoint that this transformer instance is configured on
     */
    protected ImmutableEndpoint endpoint = null;

    /**
     * A list of supported Class types that the source payload passed into this
     * transformer
     */
    @SuppressWarnings("unchecked")
    protected final List<DataType<?>> sourceTypes = new CopyOnWriteArrayList/*<DataType>*/();

    /**
     * Determines whether the transformer will throw an exception if the message
     * passed is is not supported
     */
    private boolean ignoreBadInput = false;

    /**
     * Allows a transformer to return a null result
     */
    private boolean allowNullReturn = false;

    /*
     *  Mime type and encoding for transformer output
     */
    protected String mimeType;
    protected String encoding;
    private final Map<QName, Object> annotations = new ConcurrentHashMap<QName, Object>();

    /**
     * default constructor required for discovery
     */
    public AbstractTransformer() {
        super();
    }

    public MuleEvent process(MuleEvent event) throws MuleException {
        if (event != null && event.getMessage() != null) {
            try {
                MuleMessage message = event.getMessage();
                message.applyTransformers(event, this);
                if (message instanceof DefaultMessageCollection) {
                    if (((DefaultMessageCollection) message).isInvalidatedPayload()) {
                        if (logger.isDebugEnabled()) {
                            logger.debug(
                                    "Transformed message is an invalidated message collection. Creating new message with payload: "
                                            + event.getMessage().getPayload());
                        }
                        MuleMessage newMessage = new DefaultMuleMessage(message.getPayload(), message,
                                message.getMuleContext());
                        event = new DefaultMuleEvent(newMessage, event);
                    }
                }
            } catch (Exception e) {
                throw new TransformerMessagingException(event, this, e);
            }
        }
        return event;
    }

    /**
     * Register a supported data type with this transformer.  The will allow objects that match this data type to be
     * transformed by this transformer.
     *
     * @param aClass the source type to allow
     * @deprecated use registerSourceType(DataType)
     */
    @Deprecated
    protected void registerSourceType(Class<?> aClass) {
        registerSourceType(new SimpleDataType<Object>(aClass));
    }

    /**
     * Unregister a supported source type from this transformer
     *
     * @param aClass the type to remove
     * @deprecated use unregisterSourceType(DataType)
     */
    @Deprecated
    protected void unregisterSourceType(Class<?> aClass) {
        unregisterSourceType(new SimpleDataType<Object>(aClass));
    }

    /**
     * Register a supported data type with this transformer.  The will allow objects that match this data type to be
     * transformed by this transformer.
     *
     * @param dataType the source type to allow
     */
    protected void registerSourceType(DataType<?> dataType) {
        if (!sourceTypes.contains(dataType)) {
            sourceTypes.add(dataType);

            if (dataType.getType().equals(Object.class)) {
                logger.debug(
                        "java.lang.Object has been added as source type for this transformer, there will be no source type checking performed");
            }
        }
    }

    /**
     * Unregister a supported source type from this transformer
     *
     * @param dataType the type to remove
     */
    protected void unregisterSourceType(DataType<?> dataType) {
        sourceTypes.remove(dataType);
    }

    /**
     * @return transformer name
     */
    public String getName() {
        if (name == null) {
            name = this.generateTransformerName();
        }
        return name;
    }

    /**
     * @param string
     */
    public void setName(String string) {
        if (string == null) {
            string = ClassUtils.getSimpleName(this.getClass());
        }

        logger.debug("Setting transformer name to: " + string);
        name = string;
    }

    @Deprecated
    public Class<?> getReturnClass() {
        return returnType.getType();
    }

    public void setReturnDataType(DataType<?> type) {
        this.returnType = type.cloneDataType();
        this.encoding = type.getEncoding();
        this.mimeType = type.getMimeType();
    }

    public DataType<?> getReturnDataType() {
        return returnType;
    }

    @Deprecated
    public void setReturnClass(Class<?> newClass) {
        DataType<?> tempReturnType = new SimpleDataType<Object>(newClass);
        tempReturnType.setMimeType(mimeType);
        tempReturnType.setEncoding(encoding);
        setReturnDataType(tempReturnType);
    }

    public void setMimeType(String mimeType) throws MimeTypeParseException {
        if (mimeType == null) {
            this.mimeType = null;
        } else {
            MimeType mt = new MimeType(mimeType);
            this.mimeType = mt.getPrimaryType() + "/" + mt.getSubType();
        }
        if (returnType != null) {
            returnType.setMimeType(mimeType);
        }
    }

    public String getMimeType() {
        return mimeType;
    }

    public String getEncoding() {
        return encoding;
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
        if (returnType != null) {
            returnType.setEncoding(encoding);
        }
    }

    public boolean isAllowNullReturn() {
        return allowNullReturn;
    }

    public void setAllowNullReturn(boolean allowNullReturn) {
        this.allowNullReturn = allowNullReturn;
    }

    @Deprecated
    public boolean isSourceTypeSupported(Class<?> aClass) {
        return isSourceDataTypeSupported(DataTypeFactory.create(aClass), false);
    }

    public boolean isSourceDataTypeSupported(DataType<?> dataType) {
        return isSourceDataTypeSupported(dataType, false);
    }

    /**
     * Determines whether that data type passed in is supported by this transformer
     *
     * @param aClass     the type to check against
     * @param exactMatch if the source type on this transformer is open (can be anything) it will return true unless an
     *                   exact match is requested using this flag
     * @return true if the source type is supported by this transformer, false otherwise
     * @deprecated use {@link #isSourceDataTypeSupported(org.mule.api.transformer.DataType, boolean)}
     */
    @Deprecated
    public boolean isSourceTypeSupported(Class<MuleMessage> aClass, boolean exactMatch) {
        return isSourceDataTypeSupported(new SimpleDataType<MuleMessage>(aClass), exactMatch);
    }

    /**
     * Determines whether that data type passed in is supported by this transformer
     *
     * @param dataType   the type to check against
     * @param exactMatch if set to true, this method will look for an exact match to the data type, if false it will look
     *                   for a compatible data type.
     * @return true if the source type is supported by this transformer, false otherwise
     */
    public boolean isSourceDataTypeSupported(DataType<?> dataType, boolean exactMatch) {
        int numTypes = sourceTypes.size();

        if (numTypes == 0) {
            return !exactMatch;
        }

        for (DataType<?> sourceType : sourceTypes) {
            if (exactMatch) {
                if (sourceType.equals(dataType)) {
                    return true;
                }
            } else {
                if (sourceType.isCompatibleWith(dataType)) {
                    return true;
                }
            }
        }
        return false;
    }

    public final Object transform(Object src) throws TransformerException {
        return transform(src, getEncoding(src));
    }

    public Object transform(Object src, String enc) throws TransformerException {
        Object payload = src;
        if (src instanceof MuleMessage) {
            MuleMessage message = (MuleMessage) src;
            if ((!isSourceDataTypeSupported(MULE_MESSAGE_DATA_TYPE, true)
                    && !(this instanceof AbstractMessageTransformer))) {
                src = ((MuleMessage) src).getPayload();
                payload = message.getPayload();
            }
        }

        DataType<?> sourceType = DataTypeFactory.create(payload.getClass());
        //Once we support mime types, it should be possible to do this since we'll be able to discern the difference
        //between objects with the same type
        //        if(getReturnDataType().isCompatibleWith(sourceType))
        //        {
        //            logger.debug("Object is already of type: " + getReturnDataType() + " No transform to perform");
        //            return payload;
        //        }

        if (!isSourceDataTypeSupported(sourceType)) {
            if (ignoreBadInput && !useExtendedTransformations()) {
                logger.debug(
                        "Source type is incompatible with this transformer and property 'ignoreBadInput' is set to true, so the transformer chain will continue.");
                return payload;
            } else {
                Message msg = CoreMessages.transformOnObjectUnsupportedTypeOfEndpoint(getName(), payload.getClass(),
                        endpoint);
                /// FIXME
                throw new TransformerException(msg, this);
            }
        }

        if (logger.isDebugEnabled()) {
            logger.debug(String.format("Applying transformer %s (%s)", getName(), getClass().getName()));
            logger.debug(String.format("Object before transform: %s", StringMessageUtils.toString(payload)));
        }

        Object result = doTransform(payload, enc);

        if (result == null) {
            result = NullPayload.getInstance();
        }

        if (logger.isDebugEnabled()) {
            logger.debug(String.format("Object after transform: %s", StringMessageUtils.toString(result)));
        }

        TransformerUtils.checkTransformerReturnClass(this, result);

        return result;
    }

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

        return result;
    }

    protected String getEncoding(Object src) {
        String enc = null;
        if (src instanceof MuleMessage) {
            enc = ((MuleMessage) src).getEncoding();
        }

        if (enc == null && endpoint != null) {
            enc = endpoint.getEncoding();
        } else if (enc == null) {
            enc = System.getProperty(MuleProperties.MULE_ENCODING_SYSTEM_PROPERTY);
        }
        return enc;
    }

    protected boolean isConsumed(Class<?> srcCls) {
        return InputStream.class.isAssignableFrom(srcCls) || StreamSource.class.isAssignableFrom(srcCls);
    }

    public ImmutableEndpoint getEndpoint() {
        return endpoint;
    }

    public void setEndpoint(ImmutableEndpoint endpoint) {
        this.endpoint = endpoint;
    }

    protected abstract Object doTransform(Object src, String enc) throws TransformerException;

    /**
     * Template method where deriving classes can do any initialisation after the
     * properties have been set on this transformer
     *
     * @throws InitialisationException
     */
    public void initialise() throws InitialisationException {
        // do nothing, subclasses may override
    }

    /**
     * Template method where deriving classes can do any clean up any resources or state
     * before the object is disposed.
     */
    public void dispose() {
        // do nothing, subclasses may override
    }

    protected String generateTransformerName() {
        String transformerName = ClassUtils.getSimpleName(this.getClass());
        int i = transformerName.indexOf("To");
        if (i > 0 && returnType != null) {
            String target = ClassUtils.getSimpleName(returnType.getType());
            if (target.equals("byte[]")) {
                target = "byteArray";
            }
            transformerName = transformerName.substring(0, i + 2) + StringUtils.capitalize(target);
        }
        return transformerName;
    }

    @Deprecated
    public List<Class<?>> getSourceTypes() {
        //A work around to support the legacy API
        List<Class<?>> sourceClasses = new ArrayList<Class<?>>();
        for (DataType<?> sourceType : sourceTypes) {
            sourceClasses.add(sourceType.getType());
        }
        return Collections.unmodifiableList(sourceClasses);
    }

    public List<DataType<?>> getSourceDataTypes() {
        return Collections.unmodifiableList(sourceTypes);
    }

    public boolean isIgnoreBadInput() {
        return ignoreBadInput;
    }

    public void setIgnoreBadInput(boolean ignoreBadInput) {
        this.ignoreBadInput = ignoreBadInput;
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer(80);
        sb.append(ClassUtils.getSimpleName(this.getClass()));
        sb.append("{this=").append(Integer.toHexString(System.identityHashCode(this)));
        sb.append(", name='").append(name).append('\'');
        sb.append(", ignoreBadInput=").append(ignoreBadInput);
        sb.append(", returnClass=").append(returnType);
        sb.append(", sourceTypes=").append(sourceTypes);
        sb.append('}');
        return sb.toString();
    }

    public boolean isAcceptNull() {
        return false;
    }

    public void setMuleContext(MuleContext context) {
        this.muleContext = context;
    }

    public final Object getAnnotation(QName name) {
        return annotations.get(name);
    }

    public final Map<QName, Object> getAnnotations() {
        return Collections.unmodifiableMap(annotations);
    }

    public synchronized final void setAnnotations(Map<QName, Object> newAnnotations) {
        annotations.clear();
        annotations.putAll(newAnnotations);
    }
}