Hate JAXB annotations: MOXy and JAXBIntroductions comes to the rescue

JAXB is the defacto OXM binding framework for the Java platform which performs marshalling (serializing Java to XML) and unmarshalling (deserializing XML to Java) using the standard Java APIs. I must admit that JAXB is a smart addition to the Java Web services technologies stack. It reminds me of days where I used to write custom serializers/deserializers using the first generation Web services stack Axis. It used to scare away developers using complex types in their applications because of its complexity. I do agree data binding frameworks has its limitations, but no technology is a “silver-bullet”. When it comes to programming with Web services (whether JAXWS or JAXRS), JAXB is a first-class citizen in every Web services frameworks (Axis2, CXF, Jersey, Metro, RESTEasy, Wink) out there for Java developers.

IDEs have become very smart these days and they provide nice tooling around these technologies, which makes development of applications based on web services, a child’s play. XML schemas have become the natural choice for data modeling, due to its wide adoption in enterprise application integration. While, JAXB fits the bill nicely when programming in Java, it sometimes can be verbose primarily due to the heavy use of its annotations and requiring changes to source code. There may be cases where users would not have access to sources and annotating them is not an option for them. For others, it does not make sense to annotate just for the sake of mapping objects from one format to other. There is good news for these users that do want to use JAXB with out any intrusion to their code.

JAXB framework provides customization options that makes it possible to use it seamlessly without modifying source code and abstracting out those metadata in configuration files.

I am going to discuss about two popular options that helps achieve this goal.
1. MOXy from EclipseLink.
2. JAXBIntroductions from JBoss ESB.

While both these projects specifically address the concern of using annotations on your application model, EclipseLink MOXy provides advanced customizations on marshalling your domain objects. Let us build a simple Jersey application using these options for our vanilla domain model. You guessed it right, its time to dive into our Maven POM.

[sourcecode language=’xml’]

4.0.0
MOXy/JAXBIntro
jersey-moxy-jaxbintro
jersey-moxy-jaxbintro
1.0



org.eclipse.persistence
eclipselink
2.1.0


jboss.jaxbintros
jboss-jaxb-intros
1.0.2.GA


com.sun.jersey
jersey-server
1.3


com.sun.grizzly
grizzly-servlet-webserver
1.9.18-i

org.apache.maven.plugins
maven-compiler-plugin
2.3.1
1.6
1.6



EclipseLink Repo
http://www.eclipse.org/downloads/download.php?r=1&nf=1&file=/rt/eclipselink/maven.repo


maven2-repository.dev.java.net
Java.net Repository for Maven
http://download.java.net/maven/2/
default


jboss-public-repository-group
JBoss Public Maven Repository Group
https://repository.jboss.org/nexus/content/groups/public-jboss/
default

true
never


true
never


[/sourcecode]

To keep this example simple, I would like to reuse the entities such as Customer, Address and PhoneNumber used in MOXy examples. They are simple POJOs and do not carry any metadata on them. Here is the Customer POJO (others are omitted for brevity):

[sourcecode language=’java’]
public class Customer {

private String name;
private Address address;
private List phoneNumber;

public Customer() {
phoneNumber = new ArrayList();
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Address getAddress() {
return address;
}

public void setAddress(Address address) {
this.address = address;
}

public List getPhoneNumber() {
return phoneNumber;
}

public void setPhoneNumber(List phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
[/sourcecode]

The MOXy configuration file(eclipselink-oxm.xml) which defines the metadata for these entities is shown below.

[sourcecode language=’xml’]





















[/sourcecode]

You may want to reference how these metadata translates to Java annotations as detailed in this wiki example. As you may notice, this configuration is fairly simple to understand and provides more flexibility in terms of how your marshalled XML data may look like.

So far, we have seen this from the MOXy perspective and now let us see how this would be used in a Jersey application. For this, we need to implement a ContextResolver which can be used to instantiate and inject a custom JAXBContext onto the message body writer for our domain model. Here is our implementation of a ContextResolver that uses EclipseLink API JAXBContextFactory to create a JAXBContext which will be used for our marshalling business in Jersey. The configuration file (eclipselink-oxm.xml) dictates how the XML is generated by this marshaller.

[sourcecode language=’java’]
import com.mycorp.model.Customer;
import org.eclipse.persistence.jaxb.JAXBContextFactory;

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import java.util.*;

@Provider
public final class JAXBContextResolverForMOXy implements ContextResolver {

private final JAXBContext context;
private final Set types;
private final Class[] cTypes = {Customer.class};

public JAXBContextResolverForMOXy() throws Exception {
this.types = new HashSet(Arrays.asList(cTypes));
Map metadataSourceMap = new HashMap();
metadataSourceMap.put(“com.mycorp.model”, new StreamSource(this.getClass().getResourceAsStream(“/eclipselink-oxm.xml”)));
Map properties = new HashMap();
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, metadataSourceMap);
this.context = JAXBContextFactory.createContext(cTypes, properties);
}

public JAXBContext getContext(Class objectType) {
return (types.contains(objectType)) ? context : null;
}
}
[/sourcecode]

Now, we have the JAXBContextResolver that can be injected to our MessageBodyWriter to perform marshalling. The Customer MessageBodyWriter is the key which wires Customer objects to an externalized JAXB configuration using the custom marshaller injected by Jersey runtime. Here is the trivial implementation of our Customer MBW.

[sourcecode language=’java’]
@Provider
@Produces(“application/xml”)
public class CustomerMessageBodyWriter implements MessageBodyWriter {

@Context
ContextResolver contextResolver;

@Override
public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return Customer.class.isAssignableFrom(type);
}

@Override
public long getSize(Customer customer, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return -1;
}

@Override
public void writeTo(Customer customer, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
JAXBContext context = contextResolver.getContext(type);
try {
context.createMarshaller().marshal(customer, entityStream);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
[/sourcecode]

Now, it is time to wire all these parts together to see the magic. Here is our simple CustomerResource that just returns a Customer representation.

[sourcecode language=’java’]
@Path(“customers”)
@Produces(“application/xml”)
public class CustomerResource {

@GET
public Customer getCustomer() {
Customer customer = new Customer();
customer.setName(“Jane Doe”);

Address address = new Address();
address.setStreet(“123 Any Street”);
address.setCity(“My Town”);
customer.setAddress(address);

PhoneNumber workPhoneNumber = new PhoneNumber();
workPhoneNumber.setType(“work”);
workPhoneNumber.setValue(“613-555-1111”);
customer.getPhoneNumber().add(workPhoneNumber);

PhoneNumber cellPhoneNumber = new PhoneNumber();
cellPhoneNumber.setType(“cell”);
cellPhoneNumber.setValue(“613-555-2222”);
customer.getPhoneNumber().add(cellPhoneNumber);

return customer;
}
}
[/sourcecode]

Accessing the URL, http://localhost:9998/jaxb/customers, produces the following XML:

[sourcecode language=’xml’]

Jane Doe

My Town
123 Any Street
613-555-1111 613-555-2222

[/sourcecode]

Let us do a similar exercise using JAXBIntroductions which follows a similar pattern. The configuration file(intro-config.xml) of JAXBIntros is shown below.

[sourcecode language=’xml’]
















[/sourcecode]

We have to write a new ContextResolver that uses JAXBIntroductions APIs to introduce the JAXB annotations for a given JAXBContext via a RuntimeInlineAnnotationReader implementation.

[sourcecode language=’java’]
import com.mycorp.model.Customer;
import com.sun.xml.bind.api.JAXBRIContext;
import org.jboss.jaxb.intros.IntroductionsAnnotationReader;
import org.jboss.jaxb.intros.IntroductionsConfigParser;
import org.jboss.jaxb.intros.configmodel.JaxbIntros;

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
import java.util.*;

@Provider
public class JAXBContextResolverForJAXBIntroductions implements ContextResolver {

private final JAXBContext context;
private final Set types;
private final Class[] cTypes = {Customer.class};

public JAXBContextResolverForJAXBIntroductions() throws Exception {
this.types = new HashSet(Arrays.asList(cTypes));
JaxbIntros config = IntroductionsConfigParser.parseConfig(this.getClass().getResourceAsStream(“/intro-config.xml”));
IntroductionsAnnotationReader reader = new IntroductionsAnnotationReader(config);
Map jaxbConfig = new HashMap();
jaxbConfig.put(JAXBRIContext.ANNOTATION_READER, reader);
this.context = JAXBContext.newInstance(cTypes, jaxbConfig);
}

public JAXBContext getContext(Class objectType) {
return (types.contains(objectType)) ? context : null;
}
}
[/sourcecode]

Accessing the URL, http://localhost:9998/jaxb/customers, produces the following XML:

[sourcecode language=’xml’]


Jane Doe

My Town
123 Any Street
work
613-555-1111
cell
613-555-2222

[/sourcecode]

Hopefully, these examples demonstrate the benefits of using OXM tools to simplify generating XML using custom JAXB marshallers in Jersey or any other JAX-RS provider, without requiring JAXB annotations on their domain model. While, both these options are easier to implement, you may want to choose the one which fits your needs. For most cases, JAXBIntroductions should suffice as it is lightweight. If you are looking for converting JPA entities to/from XML (via JAXB), then I would prefer MOXy.

Download the project sources here. Feedback/suggestions are always welcome 🙂

Update (8/17) : Fixed the output produced from JAXBIntroductions, as it was a copy/paste error in the original post. Now, you can notice the difference between the marshalled XML in both cases. Thanks Blaise for pointing this out and providing reference to your blog post which explains how MOXy uses XPath mapping extension to add in the “personal-info” and “contact-info” grouping elements to our marshalled XML.