Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a Jersey GET request return a polymorphic entity?

I've got a Resource class that attempts to return an interface type, say "Shape":

public interface Shape {...}

@XmlRootElement
public class Circle implements Shape {...}

@Path("/api/shapes")
public class ShapeResource {
    @GET
    @Path("/{shapeId}")
    public Shape get(@PathParam("shapeId") String shapeId) {
        ....
        return new Circle();
    }
}

Experimenting with the above, I see that the server is returning XML like so:

<?xml version="1.0" encoding="UTF-8"?>
<circle>
...
</circle>

So far so good. The problem is, the client doesn't know how to unmarshall this. I'm getting:

com.sun.jersey.api.client.ClientHandlerException: A message body for Java type, interface Shape, and MIME media type, application/xml, was not found

Given a WebResource, and asking for an entity type of Shape.class causes an exception.

The server seems to be doing the correct thing. I've been struggling for hours on how to get the Client to deserialize this class. I even tried wrapping the interface I'm really trying to get in a wrapper as outlined here: https://jaxb.dev.java.net/guide/Mapping_interfaces.html. That didn't work either.

My Client code looks like this:

    ClientResponse response = get(wr, ClientResponse.class); // wr == WebResource
    try {
        return response.getEntity(Shape.class); // <-- FAIL       
    } catch (Exception e) {
        e.printStackTrace();
        // com.sun.jersey.api.client.ClientHandlerException: A message body reader for Java type, interface Shape, and MIME media type, application/xml, was not found
    }
    try {
        return response.getEntity(Circle.class); // <-- WIN, but hacky and forces me to try every concrete type
    } catch (Exception e) {}
    return null;

Any insight or guidance is greatly appreciated. Thanks in advance.

like image 729
Erik Avatar asked Sep 09 '10 18:09

Erik


1 Answers

The problem probably isn't with the implementation of JAXB that you are using since the message is being marshalled correctly.

Instead the problem is with the following call:

return response.getEntity(Shape.class); 

I'm not sure how this is implemented, but I can imagine that it does something like the following, which would be invalid:

jaxbContext.createUnmarshaller.unmarshal(xmlSource, Shape.class);

Since it appears that all of your implementations of Shape are annotated with @XmlRootElement, we need a way to trigger the following call to JAXB:

jaxbContext.createUnmarshaller.unmarshal(xmlSource);

You could doe this outside of the Jersey client APIs:

URL url = new URL("http://www.example.com/api/shapes/123");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "application/xml");

JAXBContext jaxbContext = JAXBContext.newInstance(Circle.class, Square.class, Triangle.class);
Shape shape = (Shape) jaxbContext.createUnmarshaller().unmarshal(connection.getInputStream());

connection.disconnect();

So there should be a way to do it with Jersey client APIs.

like image 105
bdoughan Avatar answered Sep 21 '22 16:09

bdoughan