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:
- If the exception is a
WebApplicationException
, then it will automatically mapped to aResponse
. - If there is an
ExceptionMapper
registered that can handle the thrown exception, then that will be used to generate the response. - Unchecked exceptions are propagated to the container (i.e. Liberty's JAX-RS implementation code).
- 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:
- 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 forJaxRsRuntimeException
as that exception is only used internally in Liberty - and should not be mapped. If you are seeing the JaxRsRuntimeException passed to anExceptionMapper
, 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.
- 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 tonull
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.
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.EOFException
is one of those...