Auto-Generate Example of JSON or XML objects

This code snippet can be used for Mule application workflows that are required to produce ‘mocked’ JSON or XML serialized objects for use in an agile development process.  The interceptor requires a result class and a serialized format specification, from which an example can be produced. These two items can be specified in a variety of ways to the interceptor.  At the most basic level, the interceptor can use an Http request url’s relative path to designate the class and format.  An example of using the url to specify the generation of a JSON object is:

http://localhost:8081/example/org.myclasses.MyClass.json

The serialized object replaces the Mule message payload.  The Accept request header can be used instead of the .json or .xml suffix in the request url.

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.

Finally, the Mule configuration can ‘hardcode’ the result class name so that the workflow always produces the same object regardless of what was specified in the request’s relative url.

Usage

The following xml is added to the Mule flow to marshal the object into a serialized format based entirely on the request’s relative url and optionally the Accept header:

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

Alternatively, the following xml inserted into the Mule flow will force the example to always produce specific class:

<custom-interceptor class="org.mule.interceptor.MarshalObjectInterceptor" />
<spring:property name=”resultClass” value="org.myclasses.MyObj" />
</custom-interceptor>

The following Mule configuration entry will force the example to always produce specific class and serialized format:

<custom-interceptor class="org.mule.interceptor.MarshalObjectInterceptor" />
<spring:property name=”resultClass” value="org.myclasses.MyObj" />
<spring:property name=”contentType” value="application/json" />
</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.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

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;

/**
 * An object name can be specified in the url with either a .json or .xml
 * extension. This will generate an example of the object into the specified
 * serialized format. For example,
 * http://localhost:8081/example/org.myobjs.MyObj.xml will create an example of
 * the org.myobjs.MyObj in serialized xml format. The interceptor can also be
 * configured to 'hardcode' the object type and/or serialized form using the
 * interceptor configuration properties.
 * 
 * To 'hardcode' the object name, use the resultClass property:
 * 
 * <spring:property name="resultClass" value="org.mystuff.MyClass"/>
 * 
 * The content type can be specified as a configuration property as well. In the
 * case where both resultClass and contentType are specified, the resultClass
 * property will override any class specified in the contentType property. Here
 * is an example of specifying
 * 
 * <spring:property name="contentType"
 * value="application/xml;class=org.mystuff.MyClass"/>
 * 
 * @return the class to build from the serialized string in the message package.
 */
public class ExampleGenerator extends AbstractInterceptingMessageProcessor
		implements Interceptor {

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

	private String resultClass = null;
	private String contentType = null;

	public String getResultClass() {
		return resultClass;
	}

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

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

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

	public ExampleGenerator() {
	}

	/**
	 * Set the message payload to the serialized form of the configured or
	 * specified object.
	 */
	public MuleEvent process(MuleEvent event) throws MuleException {
		MuleMessage message = event.getMessage();
		String outputClassName = null;
		String useContentType = null;

		useContentType = contentType;
		outputClassName = resultClass;

		/**
		 * Specified property overrides the url request
		 */
		if (resultClass != null) {
			outputClassName = resultClass;
		} else if (contentType != null) {
			int className = contentType.lastIndexOf(";class=");
			if (className > 4) {
				outputClassName = contentType.substring(className + 7);
				int endOfClassName = outputClassName.indexOf(";");
				if (endOfClassName > 0) {
					outputClassName = outputClassName.substring(0,
							endOfClassName);
				}
				outputClassName = outputClassName.trim();
			}
		}

		if (outputClassName == null) {
			outputClassName = message.getInboundProperty("http.relative.path");
		}

		if (outputClassName == null || outputClassName.trim().length() <= 0) {
			throw new TransformerException(
					MessageFactory
							.createStaticMessage("No class name was specified in the request URL or in the resultClass property"));
		}

		if (outputClassName.toLowerCase().endsWith(".json")) {
			outputClassName = outputClassName.substring(0,
					outputClassName.length() - 5);
			if (useContentType == null) {
				useContentType = "application/json";
			}
		} else if (outputClassName.toLowerCase().endsWith(".xml")) {
			outputClassName = outputClassName.substring(0,
					outputClassName.length() - 4);
			if (useContentType == null) {
				useContentType = "application/xml";
			}
		}

		if (useContentType == null) {
			/* last option is to use what was specified in the request */
			useContentType = message.getInboundProperty("Accept");
		}

		if (useContentType == null
				|| (!useContentType.contains("application/json") && !useContentType
						.contains("application/xml"))) {
			useContentType = "application/json";
		}
		message.setOutboundProperty("Content-Type", useContentType);

		try {
			message.setPayload(examples(outputClassName, useContentType));
			return processNext(event);
		} catch (Exception e) {
			e.printStackTrace();
			message.setOutboundProperty("ExampleGeneratorErrorMsg",
					e.toString());
			throw new TransformerException(
					MessageFactory
							.createStaticMessage("Example Generator Exception: "
									+ e.toString()));
		}
	}

	private static void assignFieldValue(Object inputObject,
			String inputFieldClassName, Field field) throws Exception {

		ArrayList arrayList = null;
		Object obj = inputObject;
		String fieldClassName = inputFieldClassName;

		if (inputFieldClassName.startsWith("java.util.List<")) {
			fieldClassName = inputFieldClassName.substring(
					"java.util.List<".length(),
					inputFieldClassName.length() - 1);
			arrayList = new ArrayList();
			field.set(obj, arrayList);
		} else if (inputFieldClassName.startsWith("class ")) {
			fieldClassName = inputFieldClassName.substring("class ".length(),
					inputFieldClassName.length());
		}

		if (fieldClassName.equals("java.lang.String")) {
			StringBuilder sb = new StringBuilder();
			sb.append("_").append(field.getName()).append("_");
			if (arrayList != null) {
				arrayList.add(sb.toString());
				arrayList.add(sb.toString());
				arrayList = null;
			} else {
				field.set(obj, sb.toString());
			}
		} else if (fieldClassName.equals("java.util.Date")) {
			Calendar calendar = Calendar.getInstance(TimeZone
					.getTimeZone("UTC"));
			Date date = calendar.getTime();

			if (arrayList != null) {
				arrayList.add(date);
				arrayList.add(date);
				arrayList = null;
			} else {
				field.set(obj, date);
			}
		} else if (fieldClassName.equals("java.lang.Boolean")) {
			if (arrayList != null) {
				arrayList.add(Boolean.valueOf(true));
				arrayList.add(Boolean.valueOf(true));
				arrayList = null;
			} else {
				field.set(obj, Boolean.valueOf(true));
			}
		} else if (fieldClassName.equals("java.lang.Short")) {
			if (arrayList != null) {
				arrayList.add(Short.valueOf((short) 7));
				arrayList.add(Short.valueOf((short) 7));
				arrayList = null;
			} else {
				field.set(obj, Short.valueOf((short) 7));
			}
		} else if (fieldClassName.equals("java.lang.Byte")) {
			if (arrayList != null) {
				arrayList.add(Byte.valueOf((byte) 66));
				arrayList.add(Byte.valueOf((byte) 66));
				arrayList = null;
			} else {
				field.set(obj, Byte.valueOf((byte) 66));
			}
		} else if (fieldClassName.equals("java.lang.Integer")) {
			if (arrayList != null) {
				arrayList.add(Integer.valueOf(10));
				arrayList.add(Integer.valueOf(10));
				arrayList = null;
			} else {
				field.set(obj, Integer.valueOf(10));
			}
		} else if (fieldClassName.equals("java.lang.Long")) {
			if (arrayList != null) {
				arrayList.add(Long.valueOf(999999L));
				arrayList.add(Long.valueOf(999999L));
				arrayList = null;
			} else {
				field.set(obj, Long.valueOf(999999L));
			}
		} else if (fieldClassName.equals("java.lang.Float")) {
			if (arrayList != null) {
				arrayList.add(Float.valueOf(34.8f));
				arrayList.add(Float.valueOf(34.8f));
				arrayList = null;
			} else {
				field.set(obj, Float.valueOf(34.8f));
			}
		} else if (fieldClassName.equals("java.lang.Double")) {
			if (arrayList != null) {
				arrayList.add(Double.valueOf(8888888.6d));
				arrayList.add(Double.valueOf(8888888.6d));
				arrayList = null;
			} else {
				field.set(obj, Double.valueOf(8888888.6d));
			}
		} else if (fieldClassName.equals("boolean")) {
			if (arrayList != null) {
				arrayList.add(true);
				arrayList.add(true);
				arrayList = null;
			} else {
				field.set(obj, true);
			}
		} else if (fieldClassName.equals("short")) {
			if (arrayList != null) {
				arrayList.add(7);
				arrayList.add(7);
				arrayList = null;
			} else {
				field.set(obj, 7);
			}
		} else if (fieldClassName.equals("char")) {
			if (arrayList != null) {
				arrayList.add('B');
				arrayList.add('B');
				arrayList = null;
			} else {
				field.set(obj, 'B');
			}
		} else if (fieldClassName.equals("byte")) {
			if (arrayList != null) {
				arrayList.add((byte) 66);
				arrayList.add((byte) 66);
				arrayList = null;
			} else {
				field.set(obj, (byte) 66);
			}
		} else if (fieldClassName.equals("int")) {
			if (arrayList != null) {
				arrayList.add(10);
				arrayList.add(10);
				arrayList = null;
			} else {
				field.set(obj, 10);
			}
		} else if (fieldClassName.equals("long")) {
			if (arrayList != null) {
				arrayList.add(999999L);
				arrayList.add(999999L);
				arrayList = null;
			} else {
				field.set(obj, 999999L);
			}
		} else if (fieldClassName.equals("float")) {
			if (arrayList != null) {
				arrayList.add(34.8f);
				arrayList.add(34.8f);
				arrayList = null;
			} else {
				field.set(obj, 34.8f);
			}
		} else if (fieldClassName.equals("double")) {
			if (arrayList != null) {
				arrayList.add(8888888.6d);
				arrayList.add(8888888.6d);
				arrayList = null;
			} else {
				field.set(obj, 8888888.6d);
			}
		} else {
			if (arrayList != null) {
				arrayList.add(examples(fieldClassName));
				arrayList.add(examples(fieldClassName));
				arrayList = null;
			} else {
				field.set(obj, examples(fieldClassName));
			}

		}

	}

	public static Object examples(String outputClassName) throws Exception {
		Class outputClass = Class.forName(outputClassName);
		Object exampleObj = outputClass.newInstance();
		Field[] fields = outputClass.getDeclaredFields();
		for (Field field : fields) {
			String fieldClassName = field.getGenericType().toString();
			field.setAccessible(true);
			assignFieldValue(exampleObj, fieldClassName, field);
		}
		return exampleObj;
	}

	public static String examples(String outputClassName, String contentType)
			throws Exception {
		Class outputClass = Class.forName(outputClassName);
		Object exampleObj = examples(outputClassName);
		if (contentType.contains("application/xml")) {
			return (String) MarshalObjectInterceptor.doTransformXML(
					outputClass, exampleObj);
		} else {
			return (String) MarshalObjectInterceptor.doTransformJSON(
					outputClass, exampleObj);
		}
	}

}

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

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>

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>