users@jersey.java.net

[Jersey] Fwd: more elegant exception mapping

From: Ryan Stewart <zzantozz_at_gmail.com>
Date: Sun, 16 Jan 2011 11:10:23 -0600

I just noticed that when I'm replying to messages, they're going directly to
a user rather than to the list. I'm going to forward my replies from the
past few days to the list. I apologize for the double replies to the
posters. Shouldn't there be a reply-to on list messages so that replies go
back to the list?

---------- Forwarded message ----------
From: Ryan Stewart <zzantozz_at_gmail.com>
Date: Thu, Jan 13, 2011 at 10:17 AM
Subject: Re: [Jersey] more elegant exception mapping
To: Christopher Piggott <cpiggott_at_gmail.com>


Hey Chris, I messed around with the exception stuff a lot myself. I ended up
with a solution that works pretty well. I made a class called
StandardErrorEntity, like:
@XmlRootElement(name = "error")
class StandardErrorEntity {
  private Class type;
  private String message;

  public StandardErrorEntity(Exception e) {
    this.type = exception.getClass();
    this.message = ...; // builds up a message from the
                        // exception and its nested exceptions
  ...
}

Most ExceptionMappers just do something like:
  return Response.status(4xx).entity(new
StandardErrorEntity(exception)).build();

This has a few benefits. First, you get a nice, clean response body. An XML
response looks like:
<error>
  <type>com.foo.SomeException</type>
  <message>You did something boneheaded</message>
</error>

You also have choices about how to handle it on the client. If the class is
available, you can reconstruct and rethrow the same exception. If not, you
can use the type to determine an action to take. The clients that use the
services where I've used the StandardErrorEntity share a common parent class
that tries to get a StandardErrorEntity out of the response when an error
occurs. If it succeeds, it reconstructs the exception and wraps it in an
unchecked ClientException and throws it. If it can't get a
StandardErrorEntity out of the response, it still throws a ClientException,
but it wraps an InternalException instead. This gives some uniformity to the
clients, as a client call will always either succeed or throw a
ClientException, and by checking the cause of the ClientException, you can
learn what happened. I don't have the code in front of me, but it goes like:

class ErrorHandlingJerseyClient {
  private Client jerseyClient = ...;

  protected <T> T doRestCall(Callback<T> callback) {
    try {
      return callback.execute(jerseyClient);
    } catch (UniformInterfaceException e) {
      ClientException clientException =
buildClientException(e.getResponse());
      throw clientException;
    }
  }

  private ClientException buildClientException(ClientResponse response) {
    try {
      StandardErrorEntity entity =
response.getEntity(StandardErrorEntity.class);
      return new ClientException(reconstructException(entity));
    } catch (Exception e) {
      return new ClientException(new InternalException(...));
    }
  }
}

interface Callback<T> {
  T execute(Client client);

}

On Wed, Jan 12, 2011 at 6:55 PM, Christopher Piggott <cpiggott_at_gmail.com>wrote:

> Hi,
>
> I have been trying to find a more elegant way to deal with some
> exceptions I have created.
>
> public class AppBaseException extends Exception { }
> public class SpecificException extends AppBaseException { }
>
> My resource class normally returns a bean, but it might throw
> SpecificException if something goes wrong.
>
> The way I handle this now is :
>
> public class SpecificAppExceptionMapper extends
> ExceptionMapper<SpecificAppException> { }
>
> which, on map, throws another object I created:
>
> public class SpecificExceptionWrapper { }
>
> which does NOT extend Exception.
>
> On the client end, I've got something like:
>
> try {
> Something = response.getEntity(Something.class)
> } catch (UniformInterfaceException ex) {
> if( status == statusImInterestedIn ) {
> SpecificExceptionWrapper w =
> ex.getResponse().getEntity(SpecificExceptionWrapper.class);
> /* turn this into a SpecificException then throw it */
> }
> }
>
>
> If there's a bunch of representations for various statuses this gets
> pretty messy.
>
>
> What I would LIKE to do is to have the exception mapper just throw the
> exception and have one generic ExceptionMapper<AppBaseException> on
> the server side to serialize the exception directly, and take
> advantage of the fact that jaxb supports polymorphism via the root
> entity's name (provided I set up a JAXBContext to accomodate this).
>
> I can't do that, because anything that extends Exception can't be
> easily serialized by jaxb. I looked into tricks to do it, but they're
> all messy. (The real guts of the problem is that @XmlAccessorType
> doesn't override the default in java.lang.Exception -- meaning that
> jaxb always tries to serialize stackTrace and blows up. I even tried
> overriding getStackTrace() to no avail.)
>
>
> Any thoughts on how to better handle this sort of thing?
>
> --Chris
>