I decided to start with something simple, like a service that simply returns a java POJO object.
So with that in mind, I created an EntryPoint that mapped to the root path of /.
Simple Service
@Path("/") public class EntryPoint { @GET @Produces(MediaType.APPLICATION_XML) public Data getData() { final Data data = new Data(); data.add("key1", "value1"); data.add("key2", "value2"); return data; } }
And I tried to access the web application with my Safari, deployed to tomcat automatically with eclipse.
A message body writer for Java type, class me.kentlai.jaxrs.services.Data, and MIME media type, application/xml, was not found.
Hmm.. I wonder what are all the message body writers registered in the system. Other than the com.sun.jersey.server.impl.template.ViewableMessageBodyWriter in jersey-server, the following were found in jersey-core.
com.sun.jersey.core.impl.provider.entity.StringProvider com.sun.jersey.core.impl.provider.entity.ByteArrayProvider com.sun.jersey.core.impl.provider.entity.FileProvider com.sun.jersey.core.impl.provider.entity.InputStreamProvider com.sun.jersey.core.impl.provider.entity.DataSourceProvider com.sun.jersey.core.impl.provider.entity.RenderedImageProvider com.sun.jersey.core.impl.provider.entity.MimeMultipartProvider com.sun.jersey.core.impl.provider.entity.FormProvider com.sun.jersey.core.impl.provider.entity.FormMultivaluedMapProvider com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$App com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$Text com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$App com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$Text com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$App com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$Text com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General com.sun.jersey.core.impl.provider.entity.ReaderProvider com.sun.jersey.core.impl.provider.entity.StreamingOutputProvider com.sun.jersey.core.impl.provider.entity.SourceProvider$SourceWriter
Ok to be fair, there are various ways to serialize xml. And various xml libraries. There are a few in-built xml providers, but they take in JAXB elements of DOM elements (as far as I can see. I am not an xml guru).
It is a good chance to experiment with my own provider. An xstream writer provider.
Custom XStream Message Body Writer
This is not an entry about how to use xstream. There are much better articles out there for xstream, so I will skim over the details.
I annotated my POJO with xstream annotations, to make the xml output prettier.
The xstream writer provider was rather simple to write. All it had to do was match the xml media type, and write the object out with an xstream instance. I am not using any xstream annotation here yet. Note also that I annotated it with javax.ws.rs.ext.Provider and com.sun.jersey.spi.resource.Singleton. The first allows the class to be picked up when Jersey scans the package for providers, and the second requests jersey to create only a single instance of the writer for the entire web application. This is a better option as readers/writers should be stateless and can be reused.
@Provider @Singleton public class XStreamMessageBodyWriter implements MessageBodyWriter<Object> { public long getSize(final Object t, final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { return -1; } public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { return mediaType.isCompatible(MediaType.APPLICATION_XML_TYPE) || mediaType.isCompatible(MediaType.TEXT_XML_TYPE); } public void writeTo(final Object t, final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException, WebApplicationException { xstream.processAnnotations(type); xstream.toXML(t, entityStream); } private final XStream xstream = new XStream(); }
And accessing the redeployed page gives me the following:
<data> <entry key="service1"> <value>path1</value> </entry> <entry key="service2"> <value>path2</value> </entry> </data>
Cool! Now how about json?
Custom JSON Message Body Writer
Now change the javax.ws.rs.Produces to produce MediaType.APPLICATION_JSON instead.
A message body writer for Java type, class me.kentlai.jaxrs.services.Data, and MIME media type, application/json, was not found
That was expected. Now, to handle Json specifically with a JSON message body writer using json-lib.
@Provider @Singleton public class JSONMessageBodyWriter implements MessageBodyWriter<Object> { public long getSize(final Object t, final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { return -1; } public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { return mediaType.isCompatible(MediaType.APPLICATION_JSON_TYPE); } public void writeTo(final Object t, final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException, WebApplicationException { entityStream.write(serializer.toJSON(t).toString().getBytes()); } private final JSONSerializer serializer = new JSONSerializer(); }
Accessing the page gives me the following:
{"data":[{"key":"key1","value":"value1"},{"key":"key2","value":"value2"}]}
End of this post
This is a simple post, with nothing overly hard to try nor implement.
Next up I will probably try to trace the flow of request through the components up to the response.
Update: My bad, I did not notice that if I annotated my POJO with a JAXB @XmlRootEntity/Type, it will trigger the JAXB writer.