Multiple return type in Jersey Client request
Asked Answered
S

3

5

I'm using Jersey Client API in the following way :-

User user = webRsrc.accept(MediaType.APPLICATION_XML).post(User.class, usr);

So I'm expecting the response in object of User class which is a JAXB annotated class. However, at times I might also get an error xml and for that I've created a JAXB class ErrorResponse.

Now the problem is that if my request returns an object of ErrorResponse instead of User how can I handle that ?

I tried like this -

ClientResponse response=null;
try {

        response = webRsrc.accept(MediaType.APPLICATION_XML).post(ClientResponse.class,usr);
        User usr = response.getEntity(User.class);    
    }catch(Exception exp)
    {
       ErrorResponse err = response.getEntity(ErrorResponse.class);    
    }

But when I try to use getEntity() in catch block, it throws following exception

[org.xml.sax.SAXParseException: Premature end of file.]
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.readFrom(AbstractRootElementProvider.java:107)
at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:532)
at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:491) .....

Seems like after calling getEntity() once, the inputstream is exhausted.

Sandarac answered 8/1, 2010 at 6:59 Comment(0)
O
10

I think you missed a point in the whole "REST way of thinking".
Short answer: yes, you can only call getEntity once. You need to check the returned HTTP status to know what entity you should get.

On the server side:

  1. When designing a REST API, one should always use appropriate status codes regarding the HTTP RFC.
  2. For that matter, please consider using the ExceptionMapper interface (here's an example with a "NotFoundException"

So, now your server returns either "HTTP status OK - 200" with a User object, or an error status with an error object.

On the client side:

You need to check the return status and adapt your behavior according to the API spec. here's a quick and dirty code sample:

ClientResponse response=null;

response = webRsrc.accept(MediaType.APPLICATION_XML).post(ClientResponse.class,usr);

int status = response.getStatus();

if (Response.Status.OK.getStatusCode() == status) {

  // normal case, you receive your User object
  User usr = response.getEntity(User.class);

} else {

  ErrorResponse err = response.getEntity(ErrorResponse.class);
}

NB: depending on the status code returned, this error could be very different (thus needing very different behavior):

  • client error 40X: your client request is wrong
  • server error 500: an unexpected error occured on the server side
Osher answered 4/3, 2010 at 18:49 Comment(1)
Yes you are right Brian m8, thanks :) Unfortunately the guys who designed the services that I'm accessing, seem to have not gone by these principles. But your info is gonna come in very handy whenever I'm gonna be designing my own services. Thanks again :)Sandarac
R
2

This kind of code could be used to manage Error message or business message in the response :

protected <T> T call(String uri, Class<T> c) throws  BusinessException {


    WebResource res = new Client().create().resource(url);
    ClientResponse cresp = res.get(ClientResponse.class);
    InputStream respIS = cresp.getEntityInputStream();


    try {
        // Managing business or error response
        JAXBContext jCtx = JAXBContext.newInstance(c, BeanError.class);
        Object entity = jCtx.createUnmarshaller().unmarshal(respIS);

        // If the response is an error, throw an exception
        if(entity instanceof  BeanError) {
            BeanError error = (BeanError) entity;
            throw new  BusinessException(error);

        // If this not an error, this is the business response
        } else {
            return (T) entity;
        }

    } catch (JAXBException e) {

        throw(new BusinessException(e));
    }



}
Roadster answered 8/12, 2010 at 10:27 Comment(0)
V
0

If you can't change the server code, you can use a ReaderInterceptor. This does have some limitations but you will at least be able to get the contents of the error response object.


I'm using JSON but the same principles apply:

public class MyException extends RuntimeException {

    private final ErrorResponse content;

    public MyException(ErrorResponse content) {
        this.content = content;
    }

    public ErrorResponse getContent() {
        return content;
    }
}

public class ErrorResultInterceptor implements ReaderInterceptor {

    private static final ObjectReader JSON = new ObjectMapper().readerFor(ErrorResponse.class);

    @Override
    public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
    byte[] buffer = context.getInputStream().readAllBytes();
    context.setInputStream(new ByteArrayInputStream(buffer));
        try {
            return context.proceed();
        } catch (UnrecognizedPropertyException ex) {
            try {
                throw new MyException(JSON.readValue(buffer));
            } catch (IOException errorProcessingEx) {
                // Log errorProcessingEx using your preferred framework
                throw ex;
            }
        }
    }
}

Then use this as part of the invocation:

client.register(ErrorResultInterceptor.class);
// Oher client setup
try {
    // Make the client call
    // Handle OK case
} catch (ResponseProcessingException ex) {
    if (ex.getCause() instanceof MyException) {
        ErrorResponse respone = ((MyException)ex.getCause()).getContent();
        // Handle error response
    } else {
        throw ex;
    }
}

Limitations are:

  • The response has to be buffered which could be a memory overhead
  • The client call to get hold of the error result is very clunky
Virgy answered 7/2, 2020 at 13:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.