Structural linking with Jersey + Moxy – an AspectJ solution

The requirement

In a REST resource representation, structural links can be embedded in the resource to allow the client to navigate to subresources. This is in contrast to so-called transitional links, which represent state transitions.

    <parent id="100">
        <!-- I am a transitional link -->
        <link href="http://myserver/parents/100" rel="delete">  

        <!-- We are child resources with structural links to our full representation -->
        <child id="1" href="http://myserver/children/1"/>       
        <child id="2" href="http://myserver/children/2"/>
    </parent>

The JAX-RS project has a good discussion of the differences, as well as some explanation of why transitional links are so much easier to support than structural links.

I’ve been struggling to find a good way to implement structural links, especially as I want to add a couple more constraints:

  1. The domain model must not be touched. Even when you do have access to the source code, I simply don’t think it’s kosher to pollute a domain model with cross-cutting concerns like serialization to a particular format.
  2. I won’t put up with death-by-boilerplate, such as creating a DTO for each domain model class.

I posted this problem as a Stack Overflow question along with some candidate approaches. This blog post is about the first candidate, the AspectJ solution.

The solution sketch

This solution uses Jersey’s very handy declarative linking capability. You can add a field to a model class and Jersey will populate it for you. Even more impressively, you can simply point Jersey at a method in your REST API and Jersey will derive your link from the @Path annotations on that method. Too easy. Just too bad this violates my #1 constraint, that the model not be touched.

Enter AspectJ. We can use an intertype declaration to add the field, and everything just works.

The downside of this solution is that the structural link configuration is not truly externalized (with respect to the model). Yes, we’ve avoided altering the model source code – which, IMHO, is no small win, as preserving the expressive power of the model source code is a pretty high priority for me. However, at the byte code level, we have most definitely altered the model, at least within this compilation unit. The new fields will be visible to any reflection-driven framework and may pop up in other representations. If we can live with that, this is a pretty clean solution.

Example code

This code is also available on BitBucket.

Model classes

package testing;

import java.util.ArrayList;
import java.util.List;

public class Planet {

    private int id = 1;
    private String name = "test";
    private double radius = 3.0;

    private List<Moon> moons = new ArrayList<Moon>(0);
    
    public void addMoon(Moon moon) {
    	moons.add(moon);
    }
}
package testing;

public class Moon {
	
	private String name;
	
	// No-arg constructor is a requirement of JAXB
	public Moon() {
	}
	
	public Moon(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}

}

Resource class

For the proof-of-concept, we’re just serving an freshly created instance of the model.

package testing;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/services")
@Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
public class Services {
	
	private Planet initPlanet() {
		Planet p = new Planet();
		p.addMoon(new Moon("moon1"));
		p.addMoon(new Moon("moon2"));
		return p;
	}

	@GET
	public Planet planet () {
		return initPlanet();
	}
	
	@GET @Path("/moons/{moonid}")
	public Moon moon (@PathParam("moonid") String name) {
		return new Moon(name);
	}
	
}

Jersey configuration

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>test</display-name>
  
  <servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
	<init-param>
	    <param-name>javax.ws.rs.Application</param-name>
	    <param-value>testing.MyApplication</param-value>
	</init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
  
</web-app>

Application class

This is the class pointed to by the web.xml, and it really just exists so we can register the declarative linking feature.

package testing;

import org.glassfish.jersey.linking.DeclarativeLinkingFeature;
import org.glassfish.jersey.server.ResourceConfig;

public class MyApplication extends ResourceConfig {
	
    public MyApplication() {
        packages("testing");
        register(DeclarativeLinkingFeature.class);
    }

}

Moxy configuration

Moxy’s ability to configure JAXB mappings entirely from an external file is a cornerstone of my approach to serving up unmodified domain objects.

jaxb.properties

This is how we let Jersey know we want to use Moxy as the JAXB provider.

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

The moxy mapping file, planets.oxm.xml

Note that the href elements mentioned in the mapping file do not exist in the model source code.

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="testing"
    xml-mapping-metadata-complete="true"
    xml-accessor-type="NONE">
    <java-types>
        <java-type name="Planet">
            <xml-root-element/>
            <java-attributes>
	           	<xml-attribute java-attribute="href"/>
                <xml-element java-attribute="name"/>
                <xml-element java-attribute="radius"/>
 				<xml-element java-attribute="moons" name="moon">
 					<xml-element-wrapper name="moons"/>
 				</xml-element> 	
            </java-attributes>
        </java-type>
        <java-type name="Moon">
            <xml-root-element/>
        	<java-attributes>
	           	<xml-attribute java-attribute="href"/>
        		<xml-element java-attribute="name"/>
        	</java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

Moxy context resolver

This is how we supply Moxy with the correct mapping file. The @Provider annotation lets Jersey automatically find our context resolver.

package testing;

import java.util.HashMap;
import java.util.Map;

import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import org.eclipse.persistence.jaxb.JAXBContextProperties;

@Provider
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class MyMoxyContextResolver implements ContextResolver<JAXBContext> {
    private JAXBContext context = null;
 
    public JAXBContext getContext(Class<?> type) {
	System.out.println("Invoking MyMoxyContextResolver.getContext");
    	if (context == null || context != null) {
	        try {
	        	System.out.println("MyMoxyContextResolver - Creating new JAXBContext");
	            Map<String, Object> properties = new HashMap<String, Object>(1);
	            properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "testing/planets.oxm.xml");
	            context = JAXBContext.newInstance(new Class[] {Planet.class}, properties);
	            
	        } catch(JAXBException e) {
	            throw new RuntimeException(e);
	        }
    	}
       return context;
    }

}

Custom MessageBodyWriter

As per this StackOverflow question, we need to add a custom MessageBodyWriter to convince Jersey to marshall a completely unannotated model. Again, Jersey picks this up automatically.

package testing;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.Providers;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;


@Provider
@Produces("application/xml")
public class MyMessageBodyWriter implements MessageBodyWriter<Object> {

	@Context
	protected Providers providers;

	@Override
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return true;
    }
 
    @Override
    public long getSize(Object les, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return -1;
    }
 
    @Override
    public void writeTo(Object les, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
        try {
        	ContextResolver<JAXBContext> resolver = providers.getContextResolver(JAXBContext.class, mediaType);
        	JAXBContext context = resolver.getContext(type);
        	
        	Marshaller m = context.createMarshaller();
			m.marshal(les, entityStream);

        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
    
}

The aspect

This is what makes it all work. As this project is laid out all in one package, all you need to do is compile with the AspectJ compiler and this aspect will be woven into the model classes. In a more realistic project, you’d have the model classes on the inpath and this aspect on the aspectpath.

package testing;

import org.glassfish.jersey.linking.InjectLink;
import org.glassfish.jersey.linking.Binding;

public aspect HrefInjector {
	
	private String Planet.href;
	declare @field : * Planet.href : @InjectLink(
										resource=Services.class, 
										style=InjectLink.Style.ABSOLUTE
									) ;

	private String Moon.href;
	declare @field : * Moon.href : @InjectLink(
										resource=Services.class,
										method="moon",
										bindings={@Binding(
												name="moonid", value="${instance.name}"
												)},
										style=InjectLink.Style.ABSOLUTE
									) ;

}

Project layout

Screenshot from 2014-06-27 16:40:30

(click to enlarge)

Dependencies

As they appear in the gradle build file:

dependencies {
   compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.9'
   compile 'org.glassfish.jersey.media:jersey-media-moxy:2.9'
   compile 'org.glassfish.jersey.ext:jersey-declarative-linking:2.9'
   
   providedCompile 'javax.servlet:servlet-api:2.5'
}

As always, you can run

./gradlew dependencies

to see the full list of transitive dependencies.

Sample output

XML

	<planet href="http://localhost:8080/reststructlinks/rest/services">
		<name>test</name>
		<radius>3.0</radius>
		<moons>
			<moon href="http://localhost:8080/reststructlinks/rest/services/moons/moon1">
				<name>moon1</name>
			</moon>
			<moon href="http://localhost:8080/reststructlinks/rest/services/moons/moon2">
				<name>moon2</name>
			</moon>
		</moons>
	</planet>

JSON

{
  "href": "http://localhost:8080/reststructlinks/rest/services",
  "name": "test",
  "radius": 3,
  "moons": {
    "moon": [
      {
        "href": "http://localhost:8080/reststructlinks/rest/services/moons/moon1",
        "name": "moon1"
      },
      {
        "href": "http://localhost:8080/reststructlinks/rest/services/moons/moon2",
        "name": "moon2"
      }
    ]
  }
}

Conclusion

A few concluding comments:

  1. There are only two source files concerned with structural linking – the mapping file, and the aspect. The rest of the system operates exactly as per usual. This satisifies the requirement for structural linking to be separated out as a crosscutting concern.
  2. It’s entirely appropriate that the mapping file is involved, as it means you can control when and where you want the structural links by modifying the mapping file.
  3. The aspect requires no special configuration, as it is using bog-standard built-in Jersey functionality.
  4. Jersey’s built-in UriBuilder approach to transitional linking co-exists happily with this aspect-driven approach.
  5. Of course, some people/projects are allergic to AspectJ for whatever reason, so it would still be good to see another candidate solution emerge.

Hope this helps!

Using Moxy with Jersey for XML Part 3

See part 1 for my initial confused ramblings, and part 2 for a workaround. This third and final part will hopefully bring some clarity to both the initial problem and the solution.

Firstly, a brief statement of how I went astray:

The use case

The requirement is to create a REST API for manipulating an existing domain model. While I do have access to the domain model source code, I’m reluctant to alter it for this specific scenario, so anything requiring annotating the domain model is out. Similarly, while it’s possible to create an intermediate model (e.g. a set of DTO’s) and annotate that, that approach has always seemed to me like excessive double handling. To my mind, XML is supposed to be the dumb DTO format. I know there are use cases which require all these layers, but I also know that most of the time YAGNI.

The solution sketch

  1. JAX-RS seems to be a nicely concise, annotation-driven way to create REST services on a servlet container
  2. Jersey is a JAX-RS implementation with some traction, and some useful integrations with various containers and format providers
  3. By using JAXB with Jersey, we remove the need to write the XML marshalling/unmarshalling layer
  4. JAXB by default is completely annotation-driven, but Moxy is a JAXB implementation that can be configured via an external mapping document. Happily, Jersey ships with Moxy integration.
  5. Finally, Moxy gives us JSON capability for free.

So that was the plan. Implement that stack of frameworks, create a mapping file, and serve up both XML and JSON based on the one unannotated domain model. There are lots of examples around to help get started:

plus many more linked from the above. There’s no example I could find that reproduces all the elements of my solution sketch – Jersey + JAXB + Moxy + external mapping file + unannotated file – but surely there’s enough to get started.

What the documentation doesn’t tell you

Spot the flaw in my thinking:

  1. Jersey integrates with Moxy out of the box – TRUE
  2. Moxy supports the use of an external mapping file – TRUE
  3. When you use Moxy with the external mapping, you don’t need any annotations on the model – TRUE
  4. The Jersey docs even show you how to configure Moxy with a mapping file – TRUE
  5. Therefore, you can use Jersey + Moxy without annotations on the model – FALSE

Stumbled at the last hurdle. To add to the bafflement, this all works flawlessly for JSON marshalling, but fails for XML. I’ll go into more detail below as to why this is so, but for now, here’s what you need to know:

Jersey’s Moxy integration does not expose all of Moxy’s functionality for the XML case. There is still some of the standard annotation-driven JAXB code in the mix, and to get past that your model must be annotated at least with @XmlRootElement

Once you’ve satisified Jersey’s default JAXB provider by adding that one annotation, Jersey will happily hand you over to Moxy and all the external mapping goodness works just fine.

Is there a better way?

But wait, I started off by saying I didn’t want to annotate my model. If you really don’t want to or can’t annotate the model, there is another way. You need to register your own provider. This example gives the basic idea, although I needed to tweak it a bit to get it to work (more on that below). This is in fact what Jersey’s Moxy code does for the JSON case, which is why that works and the XML case doesn’t.

Example code

Most of this example code is identical to all the other examples out there, but I’ll collect it here as the full description of what finally worked for me. This is using Jersey 2.9 and EclipseLink Moxy 2.5 on Java 7. This code can also be found on bitbucket.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>test</display-name>
  
  <servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>jersey.config.server.provider.packages</param-name>
      <param-value>testing</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
  
</web-app>

JAX-RS service definition

Without the custom provider described further down, this code works for the JSON case (/json) and the two endpoints that use the annotated model (/json2 and /xml2), but fails for the unannotated XML case (/xml).

package testing;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/services")
public class Services {

	@GET @Path("/json")
	@Produces(MediaType.APPLICATION_JSON)
	public Planets serviceListJSON () {
		return new Planets();
	}
	
	@GET @Path("/xml")
	@Produces(MediaType.APPLICATION_XML)
	public Planets  serviceListXML () {
		return new Planets();
	}
	
	@GET @Path("/json2")
	@Produces(MediaType.APPLICATION_JSON)
	public PlanetsAnnotated serviceListJSON2 () {
		return new PlanetsAnnotated();
	}
	
	@GET @Path("/xml2")
	@Produces(MediaType.APPLICATION_XML)
	public PlanetsAnnotated  serviceListXML2 () {
		return new PlanetsAnnotated();
	}
	
}

Model classes

package testing;

public class Planets {

    private int id = 1;
    private String name = "test";
    private double radius = 3.0;


}
package testing;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class PlanetsAnnotated {

    private int id = 1;
    private String name = "test";
    private double radius = 3.0;


}

Mapping file

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="testing"
    xml-mapping-metadata-complete="true"
    xml-accessor-type="NONE">
    <java-types>
        <java-type name="Planets">
            <xml-root-element/>
            <java-attributes>
                <xml-element java-attribute="id"/>
                <xml-element java-attribute="name"/>
                <xml-element java-attribute="radius"/>
            </java-attributes>
        </java-type>
         <java-type name="PlanetsAnnotated">
            <xml-root-element/>
            <java-attributes>
                <xml-element java-attribute="id"/>
                <xml-element java-attribute="name"/>
                <xml-element java-attribute="radius"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

jaxb.properties

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

Context resolver

This is one of several ways to give Moxy the mapping file.

package testing;

import java.util.HashMap;
import java.util.Map;

import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;

import org.eclipse.persistence.jaxb.JAXBContextProperties;

@Provider
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class MyMoxyContextResolver implements ContextResolver<JAXBContext> {
    private JAXBContext context = null;
 
    public JAXBContext getContext(Class<?> type) {
	System.out.println("Invoking MyMoxyContextResolver.getContext");
    	if (context == null) {
	        try {
	            Map<String, Object> properties = new HashMap<String, Object>(1);
	            properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "testing/planets.oxm.xml");
	            context = JAXBContext.newInstance(new Class[] {Planets.class}, properties);
	        } catch(JAXBException e) {
	            throw new RuntimeException(e);
	        }
    	}
       return context;
    }

}

Custom provider

This is the part that makes the XML case finally work without annotations. For a full solution this would implement MessageBodyReader as well.

package testing;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.Providers;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;

@Provider
@Produces("application/xml")
public class MyMessageBodyWriter implements MessageBodyWriter<Object> {

	@Context
	protected Providers providers;

	@Override
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return true;
    }
 
    @Override
    public long getSize(Object les, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return -1;
    }
 
    @Override
    public void writeTo(Object les, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
        try {
        	ContextResolver<JAXBContext> resolver = providers.getContextResolver(JAXBContext.class, mediaType);
        	JAXBContext context = resolver.getContext(type);
        	
            context.createMarshaller().marshal(les, entityStream);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
    
}

Source code layout

This is my example structure for a gradle project.

src/
   main/
      java/
           jaxb.properties
           MyMessageBodyWriter.java
           MyMoxyContextResolver.java
           Planets.java
           planets.oxm.xml
           PlanetsAnnotated.java
           Services.java
   webapp/
      WEB-INF/
           web.xml 

Help!

This is a super-simple proof-of-concept. No doubt it has all sorts of sub-optimal choices, and for all I know breaks all sorts of other use cases while solving mine. However, as far as I can tell there is no other example out there for this specific scenario. It would be great if this could be evolved into something more robust, so please let me know if you have any suggestions.

The gory details

This is a sketch of what’s happening in the innards of Jersey when using JAXB to marshal the results of a service call. I’m including this as background to any future discussion on what a supported solution might look like. For example, maybe MoxyXmlFeature should register a provider that relaxes the requirement for @XmlRootAnnotation.

Jersey

At startup time:

  • Jersey registers built-in providers
  • Features (discovered on the classpath) register providers and context resolvers
  • The jaxb.properties file defines the JAXBContextFactory to be used

At request processing time:

  • The annotated JAX-RS service method returns a Java object
  • Jersey’s interceptor fires (triggered by the @Produces annotation)
  • Jersey searches its list of providers based on the class of the returned Java object and on the media type required
  • The chosen provider searches for a context resolver that is configured to provide a context for the Java class
  • The chosen context resolver obtains and configures a JAXBContext and returns it to the provider
  • The JAXBContext returns a marshaller
  • The provider invokes the marshaller, which emits the marshalled representation of the Java object

Each provider has a list of rules defining what it will handle. For example, there’s a built-in XmlRootElementProvider, which will only handle classes annotated with @XmlRootElement for media type application/xml. If no matching provider can be found, an exception is thrown.

So, on to Moxy. The jersey-media-moxy module, which ships with Jersey and provides Moxy integration, registers two features – MoxyJsonFeature and MoxyXmlFeature.

MoxyJsonFeature registers a provider, ConfigurableMoxyJsonProvider, which will handle marshalling any object where the media type is application/json. You can then use either the MoxyJsonConfig method or register your own ContextResolver to configure a mapping file if so desired.

MoxyXmlFeature, on the other hand, does not register a provider. For annotated models, one of Jersey’s built-in providers will work just fine, but for non-annotated models that means that no provider will be found and an exception will be thrown. MoxyXmlFeature does register a context resolver, thus providing a mechanism for configuring a mapping file, but without any annotations to get you past the Jersey provider that context resolver will never be invoked. Hence the need to create our own provider.

Using Moxy with Jersey for XML Part 2

In part 1 I described the ways in which the documented approach doesn’t work. This post is about my first workaround, but for a comprehensive debrief, see part 3.

The approach that works is:

  1. Build with the jersey-media-moxy module – so
  2. Put the jaxb.properties file in the right place – so
  3. Create a mapping file – so
  4. Create a ContextResolver – example. This will be used for JSON but not for XML.
  5. Create a MessageBodyWriter – example. This will be used for XML.
  6. Most importantly, write and let me know where on earth I went wrong to need to jump through all these hoops 🙂
  7. Check out this little demo project to see how it all fits together.

Using Moxy with Jersey for XML

This post is about my initial attempts to get Jersey and Moxy working the way I want. See part 3 for the full analysis of why I was having trouble and my eventual solution.

Jersey is an implementation of JAX-RS, the Java standard for REST web services. To this you can add JAXB, which allows your REST services to use declarative metadata to map domain objects to a wire format, typically either JSON or XML.

The downside of the JAXB reference implementation is that the metadata is expressed as annotations on the domain model. Personally, I think that this kind of mapping is definitely a cross-cutting concern and is to be kept out of the domain model source code at all costs. Moxy comes to the rescue! You can plug Moxy in as your JAXB provider, replacing the reference implementation entirely, and Moxy supports externalizing the mapping metadata to an XML file. So, how to get it working?

The Jersey documentation is very simple and to the point. To use Moxy for JSON binding, just place the appropriate jar on the classpath – see instructions. You can then use a custom JAXB context resolver to set Moxy up with a mapping file, and away you go.

The documentation for using Moxy for XML binding is even simpler. The simplest way is:

… use the standard JAXB mechanisms to define the JAXBContextFactory from which a JAXBContext instance would be obtained (for more on this topic, read JavaDoc on JAXBContext)…

That boils down to putting a jaxb.properties file alongside your model, as documented here.

The alternative mechanism is to manually configure Jersey, registering and configuring the MoxyXmlFeature.

However, neither mechanism actually works. It appears that, while Jerseys Moxy JSON feature registers a MessageBodyWriter to handle JSON writes, the XML feature does not do anything of the sort, instead falling back onto Jersey’s default XML MessageBodyWriter. Unfortunately, the default XML writer appears to completely ignore any registered ContextResolvers you’ve registered. Thus, while you can marshall a completely unannotated domain object to JSON, if you try to marshall the same object to XML you’ll get an exception.

See https://bitbucket.org/jmetcher/resttest for a small project showing Jersey setup strictly according to the documentation. Check out the master branch for the basic setup.

The solution is to register your own custom MessageBodyWriter, as described here. Sadly, as far as I can tell this is 100% missing from the documentation. Check out the MBW branch of the above project to see what it looks like with the recommended MessageBodyWriter approach. Unfortunately, this doesn’t work either. It appears that, contrary to about a dozen examples dotted around the web, injection of the ContextResolver does not work, as you’ll see by the NPE if you run that code.

Finally, have a look at the MBW_Better branch of the project. As this one actually works, I’ll pull it out into a separate post.