JAXRS 1 and unmarshalling of response.getEntity()


JAXRS 2 provides a simple way to unmarshal using JAXRS providers a Response entity (got from response#getEntity() method): readEntity(…).

However if you still have to stick to JAXRS 1 you’ll often get an InputStream as entity…not that fun.

The worse is when you use an interface with the JAXRS metadatas to generate a client. To avoid to need to play with stream there is a simple solution.

If you are using CXF you just need to add as provider ResponseReader (uses CXF internal MessageContext) and set as parameter the expected unmarshalling type.

If you don’t use CXF you can write your own. it basically will look like:

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.core.Response;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Providers;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.List;

// use Response.readEntity in JAXRS 2
public class ResponseReader implements MessageBodyReader<Response> {
    @Context // portable
    private Providers providers;

    private final Class<?> entityCls;

    public ResponseReader(final Class<?> entityCls) {
        this.entityCls = entityCls;
    }

    public boolean isReadable(final Class<?> cls, final Type genericType, final Annotation[] anns, final MediaType mt) {
        return cls.isAssignableFrom(Response.class);
    }

    @Override
    public Response readFrom(final Class<Response> cls, final Type genericType,
                             final Annotation[] anns, final MediaType mt,
                             final MultivaluedMap<String, String> headers, final InputStream is)
        throws IOException, WebApplicationException {

        // you can pass it as constructor parameter too,
        // the hypothesis is: if you go here all went fine so return OK, CREATED...
        final Response.ResponseBuilder rb = Response.status(Response.Status.CREATED);

        for (final String header : headers.keySet()) {
            List<String> values = headers.get(header);
            for (String value : values) {
                rb.header(header, value);
            }
        }

        if (entityCls != null) {
            rb.entity(readFrom(entityCls, anns, mt, headers, is));
        }

        return rb.build();
    }

    private <T> T readFrom(final Class<T> type,
                           final Annotation anns[], MediaType mt,
                           final MultivaluedMap<String, String> headers,
                           final InputStream is) throws IOException, WebApplicationException {

        final MessageBodyReader<T> reader = providers.getMessageBodyReader(type, type, anns, mt);
        if (reader == null) {
            throw new IllegalArgumentException("No reader for Response entity " + entityCls.getName());
        }

        return reader.readFrom(type, type, anns, mt, headers, is);
    }
}

Then simply add it to the list of providers.

For instance to create a CXF client based on the UserResource interface just do:

List<MessageBodyReader<?>> providers = Arrays.asList(new ResponseReader(Data.class), new JacksonJsonProvider());
MyResource client = JAXRSClientFactory.create(baseAddress, MyResource.class, providers);
// ...
Response resp = client.workOn(...);
Data outData = resp.getEntity(); // no InputStream 😉

Want more content ? Keep up-to-date on my new blog

Or stay in touch on twitter @rmannibucau

 

1 thought on “JAXRS 1 and unmarshalling of response.getEntity()

  1. Anonymous Coder

    Thanks for this excellent post, which really got me out of a jam dealing with Response objects with JAX-RS 1 and CXF 2.2.9!

    Reply

Leave a reply to Anonymous Coder Cancel reply