JAX-RS exception handling on Websphere Liberty

This is the expected behavior based on Section 3.3.4 (and 4.5.1) of the JAX-RS 2.0 Spec. These sections describe how exceptions from JAX-RS resources and providers are handled - in short:

  1. If the exception is a WebApplicationException, then it will automatically mapped to a Response.
  2. If there is an ExceptionMapper registered that can handle the thrown exception, then that will be used to generate the response.
  3. Unchecked exceptions are propagated to the container (i.e. Liberty's JAX-RS implementation code).
  4. Unmapped exceptions must be handled via a container-specific exception and then appropriately propagated to the underlying container - in this case a ServletException must be passed to the web container.

The JaxRsRuntimeException is used to satisfy step 4.

In this scenario the built-in JSON provider (based on Jackson 1.X) is throwing the EOFException. Since there are no exception mappers for the EOFException (or any of it's superclasses), it is ultimately mapped to a ServletException by way of the JaxRsRuntimeException.

In order for an application to handle this scenario, there are a few different options:

  1. You can register an ExceptionMapper that is specific to this exception type (EOFException or any of it's superclasses - i.e. IOException). You should not need to register a mapper for JaxRsRuntimeException as that exception is only used internally in Liberty - and should not be mapped. If you are seeing the JaxRsRuntimeException passed to an ExceptionMapper, then you should open a support case with IBM, as this is likely a bug.

With an ExceptionMapper<EOFException> you can return a specific response whenever an EOFException is thrown from a provider or resource.

  1. You can register your own MessageBodyReader that will convert JSON to objects (using Jackson or any other JSON serialization code) but that will handle empty message bodies in the way you want - for example, converting it to null or using some kind of default object instance. Because user-registered providers take priority over built-in providers, this MBR would be used instead of Liberty's Jackson-based MBR.

This approach definitely gives you more control over how the data is deserialized as well as the exception handling.

  1. Register a ContainerRequestFilter provider that will abort when the message body is empty. Here is an example:

    @Provider
    public class EmptyBodyCheckFilter implements ContainerRequestFilter {
    
        @Override
        public void filter(ContainerRequestContext crc) throws IOException {
            if (crc.getEntityStream().available() < 1) {
                crc.abortWith(Response.status(400).entity("Invalid request - empty message body").build());
            }
        }
    }
    

I've successfully tested options 1 and 3 using the WebSphere Liberty May 2018 Beta. I haven't personally tested option 2 for this scenario, but based on using custom MBRs in the past, this should work.

One thing to keep in mind is that when Liberty GAs the jaxrs-2.1 feature, it will use JSONB as the built-in provider for serializing/deserializing JSON instead of Jackson. I tested your scenario using JAX-RS 2.1 (also in the May Beta) and instead of an EOFException, the JSONB code throws a NoSuchElementException. If you think you might move to JAX-RS 2.1, then I would suggest option 2 or 3. Option 1 would require that you create a new ExceptionMapper for JAX-RS 2.1.

Hope this helps,

Andy


Not a direct answert on "why WLP wrap the exception ..etc" but maybe add an exception interceptor as you did but on"ExceptionMapper<Exception>"and recusrsively iterate on the "causes" to check if java.io.EOFExceptionis one of those...