Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAX-RS exception mapper: What about wrapped exceptions and default case?

A typical way to deal with exceptions in REST service is to define custom exception types (typically from RuntimeException) and then implement a mapper class to produce HTTP codes, for example:

public class MyExceptionMapper implements ExceptionMapper<MyException> {
  @Override
  public Response toResponse(MyExceptionex) {
    return Response.status(400).entity("bad request")
      .type(MediaType.APPLICATION_JSON).build();
  }
}

Now, I have 2 questions:

  • How would I implement a mapper for the "default case", meaning for every exception not mapped here or in another mapper? When for example implementing one for Throwable in order to produce an HTTP 500, wouldn't it catch my own exceptions again? Or can an order be defined in which mappers work?
  • When calling managed components like EJBs from REST service, wouldn't an exception thrown there result in an EJBException or some Transaction...Exception wrapping my own one?
like image 200
Alexander Rühl Avatar asked Sep 17 '25 16:09

Alexander Rühl


1 Answers

mapper for the "default case"...

Just as Paul Samsotha writes in the comment, the JAX-RS server runtime is supposed to pick the most specific exception mapper. Or to quote the JAX-RS specs (for JEE7/version 2.0):

3.3.4 Exceptions

[...]

  1. If an exception mapping provider (see Section 4.4) is available for the exception or one of its superclasses, an implementation MUST use the provider whose generic type is the nearest superclass of the exception to create a Response instance that is then processed according to Section 3.3.3.[...]

So I guess you can use an exception mapper for Throwable - its signature verifies it anyway:

public interface ExceptionMapper<E extends Throwable> {...}

When calling managed components like EJBs from REST service...

The EJB container will wrap the exception, if it needs to be wrapped. Not all exceptions thrown by EJBs are required to be wrapped. The EJB spec (v3.1) makes the distinction between application exceptions (annotated with javax.ejb.ApplicationException) and "all other exceptions" (see section 14.3). So make the exception @ApplicationException and provide a mapper for it. But if you still want to respond based on the wrapped exception:

Respond based on the wrapped exception

You cannot directly select a mapper based on the wrapped exception. But you can create an exception mapper for the wrapper exception that unwraps it and selects an appropriate mapper for the wrapped exception based on the Providers context (see JAX-RS 2.0 section 9.2.6 and the javax.ws.rs.ext.Providers Javadocs). An example, untested code for a hypothetical MyWrapperException would be:

@Provider
public class MyWrapperExceptionMapper implements ExceptionMapper<MyWrapperException> {
    @Context
    private Providers providers;

    public Response toResponse(MyWrapperException e) {
        Throwable t = e.getCause();
        ExceptionMapper mapper = providers.getExceptionMapper(t.getClass());
        if( mapper != null ) {
            return mapper.toResponse(t);
        }
        else {
            // custom handling...
        }
    }
}
like image 171
Nikos Paraskevopoulos Avatar answered Sep 19 '25 08:09

Nikos Paraskevopoulos