Creating a simple JAX-RS MessageBodyWriter
JAX-RS is really cool and with the help of JAXB a lot of response data types can be converted for you simply by adding annotating the data objects with JAXB annotations. I am fairly new at JAXB but some simple cut/paste of annotations will take you a long way.
There maybe some types of data that you can’t or won’t annotate for the purposes of returning that data type from a JAX-RS resource method. One simple example is returning either a boolean (primitive) or the wrapper Boolean class. I read a question on StackOverflow where someone asked if they could return a boolean from a resource method and since I didn’t know the answer, I decided to try it! My version only returns XML, not JSON but you should get the idea.
I started with the Jersey User’s Guide HelloWorld example and starting modifying from there. I used the pom.xml and the only change was to uncomment a block to allow using JSON.
Main class
This the main class from the Hello World example without any changes.
package com.example; import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; import org.glassfish.jersey.server.ResourceConfig; import java.io.IOException; import java.net.URI; /** * Main class. * */ public class Main { // Base URI the Grizzly HTTP server will listen on public static final String BASE_URI = "http://localhost:8080/myapp/"; /** * Starts Grizzly HTTP server exposing JAX-RS resources defined in this application. * @return Grizzly HTTP server. */ public static HttpServer startServer() { // create a resource config that scans for JAX-RS resources and providers // in com.example package final ResourceConfig rc = new ResourceConfig().packages("com.example"); // create and start a new instance of grizzly http server // exposing the Jersey application at BASE_URI return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc); } /** * Main method. * @param args * @throws IOException */ public static void main(String[] args) throws IOException { final HttpServer server = startServer(); System.out.println(String.format("Jersey app started with WADL available at " + "%sapplication.wadl\nHit enter to stop it...", BASE_URI)); System.in.read(); server.stop(); } }
Resource class
I created a resource class that included a GET method to return a boolean and another GET method to return the wrapper Boolean class. Notice the getBool() and getBoolean() methods return XML as the first option.
package com.example; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; /** * Root resource (exposed at "myresource" path) */ @Path("myresource") public class MyResource { /** * Method handling HTTP GET requests. The returned object will be sent * to the client as "text/plain" media type. * * @return String that will be returned as a text/plain response. */ @GET @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN}) public String getIt() { return "Got it!"; } @GET @Path("/bool") @Produces({MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN}) public boolean getBool() { return false; } @GET @Path("/Boolean") @Produces({MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN}) public Boolean getBoolean() { return Boolean.TRUE; } }
BooleanMessageBodyWriter class
Here’s the interesting part, creating the MessageBodyWriter class to allow the resource method to return XML for the boolean or Boolean.
package com.example; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; import javax.ws.rs.WebApplicationException; import java.io.IOException; import java.io.InputStream; import java.io.DataOutputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.reflect.Type; @Provider @Produces("application/xml") public class BooleanMessageBodyWriter implements MessageBodyWriter{ @Override public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { System.out.println("isWriteable called..."); return type == Boolean.class; } @Override public long getSize(Boolean myBool, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { // deprecated by JAX-RS 2.0 and ignored by Jersey runtime return 0; } @Override public void writeTo(Boolean myBool, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { StringBuilder sb = new StringBuilder(); sb.append(" "); DataOutputStream dos = new DataOutputStream(entityStream); dos.writeUTF(sb.toString()); } } ").append(myBool.toString()).append("
I haven’t used Maven before but the following targets are all you need to compile and run the project, after installing maven (of course!).
- mvn compile – compiles the code
- mvn exec:java – starts the Grizzly HttpServer and deploys the restful service.
Hope this helps!
Mr. Mike Miller!
Thank you very much!
Yours example really help me!
With best wishes from Russia, Moscow,
Leonid O
How can I send the JSON response with writeto