UnMarshalling Interceptor for Mule

This code snippet can be used for Mule application workflows that require a generic un-marshaling component to convert a JSON or XML serialized object into an instantiated Java object. The serialized format is chosen by the execution component that is invoking the Mule application workflow. Typically this is an http request utilizing a RESTful interface that has been exposed by the Mule application workflow.

The serialized object is POSTed to the Mule application in the request body and a Content-Type header is included in the request to indicate the serialized format is either “application/json” or “application/xml”.

The Mule application un-marshals the serialized object using either JaxB (for XML format) or Jackson (for JSON format). The specific object to un-marshal is specified as a Java class name in either the Mule un-marshal configuration or in the Content-Type header provided by the invoking component. This latter function is meant to support RESTful API’s that utilize a mime attribute extension to define the specific object type being transmitted. For instance, the following header would attempt to instantiate the serialized JSON data as an InvoiceItem:

Content-Type: application/json;class=org.example.InvoiceItem

Specifying the object type in the Mule un-marshal configuration disables the Content-Type specification of object type.

Usage

The following xml is added to the Mule flow to un-marshal the serialized object based entirely on the Content-Type specified by the request:

<custom-interceptor class="org.mule.interceptor.UnMarshalObjectInterceptor" />

Alternatively, the following xml inserted into the Mule flow will force the un-marshalling to use the InvoiceItem object type:

<custom-interceptor class="org.mule.interceptor.UnMarshalObjectInterceptor" />
<spring:property name=”resultClass” value="org.example.InvoiceItem" />
</custom-interceptor>

Cut and paste the Java code below into the Anypoint (or Mule) studio as a new Java class file (in the src directory).

Code


package org.mule.interceptor;

import java.io.StringReader;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.mule.api.MuleEvent;
import org.mule.api.MuleException;
import org.mule.api.MuleMessage;
import org.mule.api.interceptor.Interceptor;
import org.mule.api.transformer.TransformerException;
import org.mule.config.i18n.MessageFactory;
import org.mule.processor.AbstractInterceptingMessageProcessor;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 
 * Uses Content-Type inbound property to define both the mime type and the
 * output class. For instance:
 * 
 * 
 * Content-Type: application/json;class=org.obj.MyObject
 * 
 *                    or
 * 
 * Content-Type: application/xml;class=org.obj.MyObject
 * 
 * 
 * The Mule interceptor is configured with the custom-interceptor tag:
 * 
 * 
 * <custom-interceptor class="org.mule.interceptor.UnMarshalObjectInterceptor"/>
 * 
 * 
 * The resultClass configuration property can be specified in the interceptor
 * configuration, which will be used to override the class specified in the
 * Content-Type inbound property.
 * 
 * A default content type can be specified with the defaultContentType
 * configuration property.
 * 
 * Note that the unmarshalled objects use Jackson or JaxB for performing the
 * transformation. Therefore, the result class should be annotated with
 * appropriate @Json and @Xml verbs, for example:
 * 
 * @XmlRootElement, @XmlAccessorType(XmlAccessType.FIELD),
 * @XmlElement(required=false name="alternalteName"), @JsonProperty,
 * etc. will be used in the transformation.
 * 
 * 
 * 
 * @author peterdunworth
 * 
 */
public class UnMarshalObjectInterceptor extends
		AbstractInterceptingMessageProcessor implements Interceptor {

	public static Logger log = Logger
			.getLogger(UnMarshalObjectInterceptor.class);

	private String resultClass = null;
	private String defaultContentType = "application/json";

	/**
	 * Can be set with a
	 * 
	 * 
	 * 	<spring:property name="resultClass" value="org.mystuff.MyClass"/>
	 * 
	 * 
	 * If specified, the Content-Type attribute class is ignored.
	 * 
	 * @return the class to build from the serialized string in the message
	 *         package.
	 */
	public String getResultClass() {
		return resultClass;
	}

	public void setResultClass(String resultClass) {
		this.resultClass = resultClass;
	}

	/**
	 * Can be set with a
	 * 
	 * 
	 * <spring:property name="defaultContentType" value="application/json"/>
	 * 
	 * 
	 * Default value is application/json
	 * 
	 * @return the default content type
	 */
	public String getDefaultContentType() {
		return defaultContentType;
	}

	public void setDefaultContentType(String defaultContentType) {
		this.defaultContentType = defaultContentType;
	}

	/**
	 * Transforms the message payload to the indicated output Class using the
	 * serialized message payload. The serialized format is specified in the
	 * Content-Type property.
	 * 
	 * The default content type can be specified with the default content type
	 * property.
	 * 
	 * 
	 */
	@Override
	public MuleEvent process(MuleEvent event) throws MuleException {
		MuleMessage message = event.getMessage();
		log.trace("message.getInboundProperty.Content-Type:"
				+ message.getInboundProperty("Content-Type"));

		String contentType = null;
		String outputClassName = null;

		if (!((String) message.getInboundProperty("http.method"))
				.equalsIgnoreCase("get")) {
			// allow flow to override the content type
			contentType = message.getInboundProperty("Content-Type");
		}
		if (contentType == null) {
			contentType = defaultContentType;
		}

		/**
		 * Specified property overrides the content-type specification
		 */
		if (resultClass != null) {
			outputClassName = resultClass;

		} else {
			int className = contentType.lastIndexOf(";class=");
			if (className < 5) {
				throw new TransformerException(
						MessageFactory
								.createStaticMessage("Missing ';class=' at the end of the Content-Type"));
			}
			outputClassName = contentType.substring(className + 7);
			int endOfClassName = outputClassName.indexOf(";");
			if (endOfClassName > 0) {
				outputClassName = outputClassName.substring(0, endOfClassName);
			}
			outputClassName = outputClassName.trim();
		}

		try {
			if (contentType.contains("application/json")) {
				message.setPayload(transformJSON(message, outputClassName));
				return processNext(event);
			} else if (contentType.contains("application/xml")) {
				message.setPayload(transformXML(message, outputClassName));
				return processNext(event);
			}
			return processNext(event);
		} catch (Exception e) {
			e.printStackTrace();
			message.setOutboundProperty("UnMarshalObjectErrorMsg", e.toString());
			throw new TransformerException(
					MessageFactory.createStaticMessage("UnMarshal Exception: "
							+ e.toString()));
		}
	}

	private Object transformJSON(MuleMessage message, String outputClassName)
			throws Exception {
		Class outputClass = Class.forName(outputClassName);
		Object obj = doTransformJSON(outputClass, message.getPayloadAsString());
		return obj;
	}

	public static Object doTransformJSON(Class iClass, String payload)
			throws Exception {
		log.trace("transformJSON");
		StringReader reader = null;
		try {
			ObjectMapper mapper = new ObjectMapper();
			reader = new StringReader(payload);
			@SuppressWarnings("unchecked")
			Object obj = mapper.readValue(reader, iClass);
			return obj;
		} catch (Exception e) {
			throw e;
		} finally {
			if (reader != null) {
				IOUtils.closeQuietly(reader);
			}
		}
	}

	private Object transformXML(MuleMessage message, String outputClassName)
			throws Exception {
		Class outputClass = Class.forName(outputClassName);
		Object obj = doTransformXML(outputClass, message.getPayloadAsString());
		return obj;
	}

	public static Object doTransformXML(Class iClass, String payload)
			throws JAXBException {
		log.trace("transformXML");

		StringReader reader = null;
		try {
			JAXBContext context = JAXBContext.newInstance(iClass);
			Unmarshaller unmarshaller = context.createUnmarshaller();
			reader = new StringReader(payload);
			Object obj = unmarshaller.unmarshal(reader);
			return obj;
		} catch (JAXBException e) {
			if (e.getCause() == null) {
				throw e;
			}
			throw new JAXBException(e.getCause().toString());
		} finally {
			if (reader != null) {
				IOUtils.closeQuietly(reader);
			}
		}
	}
}

Eclipse Maven Dependencies

If this code is built in Eclipse instead of Anypoint (Mule) Studio, the following maven dependencies are required. Once built, the jar file can be dropped into the lib folder of the Mule project directory (and the jar file added to the build path).


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.t35</groupId>
    <artifactId>mule-marshalling-interceptor</artifactId>
    <version>0.0.1</version>
    <name>Marshalling Interceptor for Mule ESB</name>
    <description>Marshalling Interceptor for Mule ESB</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <mule.version>3.4.1</mule.version>
        <jdkName>JavaSE-1.6</jdkName>
        <jdk.version>1.6</jdk.version>
        <junit.version>4.9</junit.version>
    </properties>

    <repositories>
        <repository>
            <id>Central</id>
            <name>Central</name>
            <url>http://repo1.maven.org/maven2/</url>
            <layout>default</layout>
        </repository>
        <repository>
            <id>mulesoft-releases</id>
            <name>MuleSoft Releases Repository</name>
            <url>http://repository.mulesoft.org/releases/</url>
            <layout>default</layout>
        </repository>
        <repository>
            <id>mulesoft-snapshots</id>
            <name>MuleSoft Snapshots Repository</name>
            <url>http://repository.mulesoft.org/snapshots/</url>
            <layout>default</layout>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>mulesoft-release</id>
            <name>mulesoft release repository</name>
            <layout>default</layout>
            <url>http://repository.mulesoft.org/releases/</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>${jdk.version}</source>
                    <target>${jdk.version}</target>
                    <encoding>ISO-8859-1</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <executions>
                    <execution>
                        <id>attach-sources</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <executions>
                    <execution>
                        <id>attach-javadocs</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.2.1</version>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>project</descriptorRef>
                    </descriptorRefs>
                    <finalName>mule-marshalling-interceptors</finalName>
                </configuration>
            </plugin>
            <!-- plugin> <groupId>org.jsonschema2pojo</groupId> <artifactId>jsonschema2pojo-maven-plugin</artifactId> 
                <version>0.4.2</version> <configuration> <sourceDirectory>${basedir}/src/main/api/schemas</sourceDirectory> 
                <targetPackage>com.example.types</targetPackage> </configuration> <executions> 
                <execution> <goals> <goal>generate</goal> </goals> </execution> </executions> 
                </plugin -->
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.mule</groupId>
            <artifactId>mule-core</artifactId>
            <version>${mule.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.1.1</version>
        </dependency>
    </dependencies>
</project>
Leave a comment

Comments