Search code examples
restjerseyglassfishjava-ee-6provider

MessageBodyWriter for custom media type in Jersey


I've been writing RESTful Web service. The technologies that I use:

  • Glassfish 3 (based on Java 6)
  • JDK v7
  • Eclipse EE Kepler
  • Jersey (part of Glassfish)

I've created custom POJO for custom MediaType:

public final class SimpleEntity{

private int value = 0;
private String stValue = "";

public SimpleEntity(){}

public SimpleEntity(int value, String stValue) {
    super();
    this.value = value;
    this.stValue = stValue;
}
... getters, setters

My resource method:

@POST
@Produces("application/entity")
@Path("/getSimpleEntityNeedsProvider")
public Response getSimpleEntityNeedsProvider(){
    SimpleEntity entity = new SimpleEntity(47, "String input value");

    return Response.ok().entity(entity).build();
}

My message body writer:

 @Provider
@Produces("application/entity")
public final class SimpleEntityBodyWriterProvider implements MessageBodyWriter<SimpleEntity>{

    private static Logger log = Logger.getLogger(Class.class);

    @Override
public long getSize(SimpleEntity arg0, Class arg1, Type arg2, Annotation[] arg3,
        MediaType arg4) {

    return -1;
}

@Override
public boolean isWriteable(Class arg0, Type arg1, Annotation[] arg2,
        MediaType arg3) {
    if(arg0.equals(SimpleEntity.class)){            
        return true;
    }
    return false;
}

@Override
public void writeTo(SimpleEntity entity, Class arg1, Type arg2, Annotation[] arg3,
        MediaType media, MultivaluedMap arg5, OutputStream out)
        throws IOException, WebApplicationException {

    log.log(Level.INFO, "Input to SimpleEntityBodyWriterProvider: "+entity.getValue());

    out.write(entity.getValue());   
    out.write(entity.getStValue().getBytes());
}

}

My service client:

private void getSimpleEntityNeedsProvider(String strUrl, String method){
    HttpURLConnection connect = null;
    try {
        URL url = new URL(strUrl);
        connect = (HttpURLConnection)url.openConnection();

        connect.setRequestProperty("Accept", "application/entity");// Accept from server
        connect.setRequestMethod(method);   
        connect.connect();

        InputStream input = connect.getInputStream();

        int size = input.available();
        byte[] b = new byte[size];
        input.read(b, 0, size);

        System.out.println("From resource: "+new String(b));

        System.out.println("Status: "+connect.getResponseCode()+" : "+connect.getResponseMessage());

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

The root path:

    @ApplicationPath("/rest/v6")
public class AppV6 extends Application {
    private static Logger log = Logger.getLogger(Class.class.getName());
    public AppV6(){}

    @Override


public Set<Class<?>> getClasses(){
        Set<Class<?>> cls = new HashSet<>();
    cls.add(SimpleEntity.class);
    cls.add(SimpleEntityBodyWriterProvider.class);

    return cls;
}

}

When I run the application I get the following output from my service client:

From resource: /String input value
Status: 200 : OK

I want to use custom media type and MessageBodyWriter in order to have better understanding of RESTful/Jersey service. The questions that I have:

  • Is this is the correct way of involving/coding the custom media type?
  • The data that I receive at service client is not totally correct. I.e., in stead of "/" the value needs to be a digit 47. So, why do I get the a character instead of digit?
  • As you have seen at resource method I input two values to custom POJO: digit and string. At service client I've got InputStream. I get byte array of overall data. How can I read data as chunks of separate data, which is encoded as fields in my POJO? I could read byte by byte, but is it a correct way of doing it?

Solution

  • The solution, that I figured out, is simple. I just needed to use wrapper class for InputStream, i.e.,

    InputStream data = connect.getInputStream();
            DataInputStream input = new DataInputStream(data);
    
            System.out.println("From resource: "+input.readInt()+" : "+input.readUTF());
    

    The same wrapper needs to be applied at MessageBodyWriter implementation class. I added implementation for MessageBodyReader (the same rules apply as above) for reading custom POJO/MediaType at resource method. It works smoothly.