Tuesday, September 1, 2009

Mule: Using JiBX in Mule

Below I'd like to share my attempt to use JiBX inside of Mule to transform incoming XML message into a domain object and back again on the way out. In short, I created a request and response transformer that extended AbstractTransformer.

To start, I had the following domain objects:

   class Person {
String firstName
String lastName
Address addresss
}

class Address {
String city
String state
}

And a sample XML message where a Person has an Address:

  <person>
<firstname>George</firstname>
<lastname>Costanza</lastname>
<address>
<city>Queens</city>
<state>NY</state>
</address>
</person>

The binding file I used to test out my transformers:

  <?xml version="1.0" encoding="UTF-8"?>
<binding>
<mapping name="person" class="Person">
<value name="firstname" field="firstName"/>
<value name="lastname" field="lastName"/>
<structure name="address" field="address" class="Address">
<value name="city" field="city" />
<value name="state" field="state" />
</structure>
</mapping>
</binding>

Ok, now to something more interesting with that out of the way... the transformer to turn an incoming XML message to a Person object to use in my service component. Below is one representation of the transformer in Groovy with the types releated to JiBX declared:

class JibxXmlToObject extends AbstractJibxTransformer {
protected Object doTransform(Object src, String encoding) throws TransformerException {
def transformedObject
try {
Reader reader = new StringReader((String) src)
IBindingFactory factory = BindingDirectory.getFactory(Class.forName(getTargetClassName()))
IUnmarshallingContext context = factory.createUnmarshallingContext()
transformedObject = context.unmarshalDocument(reader)
}
catch(e) {
throw new TransformerException(this, e)
}

return transformedObject
}
}

To test out my transformer originally, I originally hard-coded the target class name:

  IBindingFactory factory = BindingDirectory.getFactory(Class.forName("org.prystasj.Person"))

In my Mule configuration, I will be declaring it however. If the transformer was a Java class, I might have to have getters and setters to allow for the target class name to be injected. Below is my declaration of the transformer as a custom transformer:

  <custom-transformer name="XmlToPerson" class="org.prystasj.transformers.JibxXmlToObject">
<spring:property name="targetClass" value="org.prystasj.Person"/>
</custom-transformer>
  <file:endpoint name="XmlInboundEndpoint"
  path="/tmp/input"
  pollingFrequency="3000"
  transformer-refs="XmlToPerson"/>

And here are the outgoing side of things, transforming a Person back to XML:
public class JibxObjectToXml extends AbstractJibxTransformer {
protected Object doTransform(Object src, String encoding) throws TransformerException {
String xml
try {
def writer = new StringWriter()
IBindingFactory factory = BindingDirectory.getFactory(Class.forName(getTargetClassName()))
IMarshallingContext context = factory.createMarshallingContext()
context.marshalDocument(src, encoding, null, writer)
xml = writer.toString()
} catch(e) {
throw new TransformerException(this, e)
}

return xml
}
}

And the corresponding Mule configuration:

  <custom-transformer name="PersonToXml" class="org.prystasj.transformers.JibxObjectToXml">
<spring:property name="targetClass" value="org.prystasj.Person"/>
</custom-transformer>
  <file:endpoint name="XmlOutboundEndpoint"
path="/tmp/output"
pollingFrequency="3000"
responseTransformer-refs="PersonToXml"/>

Putting the pieces together with a service definition, this example would take a Person in XML and echo it back out in XML after performing both transforms:

  <model name="XmlPersonModel">
<service name="XmlInputService">
<inbound>
<file:inbound-endpoint ref="XmlInboundEndpoint"/>
</inbound>
<echo-component/>
<outbound>
<pass-through-router>
<file:outbound-endpoint ref="XmlOutboundEndpoint"/>
</pass-through-router>
</outbound>
</service>
</model>

A more useful service component could be used to manipulate the incoming person in some way.

No comments:

Post a Comment