Marshalling Interceptor for Mule

This code snippet can be used for Mule application workflows that require a generic marshaling component to serialize a Java object into either a JSON or XML format. 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 Accept header is used to specify whether JSON or XML is to be produced.

The serialized object replaces the Mule message payload and a Content-Type header is included in the outbound properties to indicate the serialized format is either “application/json” or “application/xml”.

The Mule application marshals the serialized object using either JaxB (for XML format) or Jackson (for JSON format). The specific object to marshal is the message payload and is expected to be a Java object that is annotated for serialization. As mentioned earlier, the Accept inbound property indicates what serialization format to use.  For instance, request XML serialized data, use this Accept header:

Accept: application/xml

The Mule marshal configuration can also ‘hardcode’ the content type, by using the configuration property “contentType”.  Specifying the content type in the Mule marshal configuration disables the Content-Type specification and forces the work flow to alway produce the indicated serialization format.

Usage

The following xml is added to the Mule flow to marshal the object into a serialized format based entirely on the Accept header specified by the request:

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

Alternatively, the following xml inserted into the Mule flow will force the marshalling to always produce an XML serialized form:

<custom-interceptor class="org.mule.interceptor.MarshalObjectInterceptor" />
<spring:property name=”contentType” value="application/xml" />
</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.StringWriter;
import java.util.StringTokenizer;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

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;

/**
 * 
 * Converts the message payload object into a serialized string (json or xml)
 * based on the Accept inbound property in the request. For example:
 * 
 * 
 * Accept: application/json
 *           or
 * Accept: application/xml
 * 
 * 
 * The Mule interceptor is configured with the custom-interceptor tag:
 * 
 * 
 * <custom-interceptor class="org.mule.interceptor.MarshalObjectInterceptor"/>
 * 
 * 
 * The content type can be overridden by interceptor configuration by specifying
 * a contentType property in the interceptor configuration. Default value for
 * the returned content type is "application/json" if neither an Accept inbound
 * property or the contentType property is specified.
 * 
 * 
 * 
 * @author peterdunworth
 * 
 */
public class MarshalObjectInterceptor extends
		AbstractInterceptingMessageProcessor implements Interceptor {

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

	private String contentType = null;

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

	public void setContentType(String contentType) {
		this.contentType = contentType;
	}

	public MuleEvent process(MuleEvent event) throws MuleException {
		MuleMessage message = event.getMessage();
		log.trace("message.getInboundProperty.Accept:"
				+ message.getInboundProperty("Accept"));

		// allow flow to override what is generated
		String useContentType = null;
		if (contentType != null) {
			useContentType = contentType;
		} else {
			useContentType = message.getInboundProperty("Accept");
		}
		String outputEncoding = "UTF-8";

		try {
			if (useContentType == null) {
				useContentType = "application/json";
			}
			message.setPayload(firstRecognizedType(message, outputEncoding,
					useContentType));
			message.setOutboundProperty("Content-Type", useContentType);
			return processNext(event);
		} catch (Exception e) {
			e.printStackTrace();
			message.setOutboundProperty("MarshalObjectErrorMsg", e.toString());
			throw new TransformerException(
					MessageFactory.createStaticMessage("Marshal Exception: "
							+ e.toString()));
		}
	}

	private Object firstRecognizedType(MuleMessage message,
			String outputEncoding, String mimeTypes) throws Exception {
		StringTokenizer tok = new StringTokenizer(mimeTypes, ", ");
		while (tok.hasMoreElements()) {
			String mtype = tok.nextToken();
			if (mtype.equalsIgnoreCase("application/json")) {
				return transformJSON(message, outputEncoding);
			} else if (mtype.equalsIgnoreCase("application/xml")) {
				return transformXML(message, outputEncoding);
			}
		}
		return transformJSON(message, outputEncoding);
	}

	private Object transformJSON(MuleMessage message, String outputEncoding)
			throws Exception {
		Object obj = message.getPayload();
		return doTransformJSON(obj.getClass(), obj);
	}

	public static Object doTransformJSON(Class iClass, Object payload)
			throws Exception {
		log.trace("transformJSON");
		ObjectMapper mapper = new ObjectMapper();
		// mapper.enable(SerializationFeature.INDENT_OUTPUT);
		return mapper.writeValueAsString(payload);
	}

	private Object transformXML(MuleMessage message, String outputEncoding)
			throws Exception {
		Object obj = message.getPayload();
		return doTransformXML(obj.getClass(), obj);
	}

	public static Object doTransformXML(Class iClass, Object payload)
			throws Exception {
		log.trace("transformXML");
		StringWriter writer = new StringWriter();
		try {
			JAXBContext context = JAXBContext.newInstance(iClass);
			Marshaller marshaller = context.createMarshaller();
			// marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
			// Boolean.TRUE);
			marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
			marshaller.marshal(payload, writer);
			return writer.toString();
		} finally {
			if (writer != null) {
				IOUtils.closeQuietly(writer);
			}
		}
	}

}

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>
Advertisements
Leave a comment

Comments

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: