jacksonjax-rsjersey-2.0auto-value

Jersey ignores ExceptionMapper


I made an ExceptionMapper to catch and log all exceptions, like:

@Provider
public class CatchAllExceptionsMapper implements ExceptionMapper<Throwable> {
    private static final Logger LOG = LoggerFactory.getLogger(CatchAllExceptionsMapper.class);
    @Override
    public Response toResponse(Throwable exception) {
        LOG.error("Exception not catched!", exception);
        return Response.serverError().build();
    }
}

It catches the Exceptions my code throws, but if I send a Request with a JSON value that throws an IllegalStateException at my object's creation, this ExceptionMapper is ignored and I get a 400 Bad Request Response.

Funny thing is this Response is not the traditional Tomcat HTML formatted Response, its just plain text. It say just:

Cannot construct instance of `com.example.vo.AutoValue_Customer$Builder`, problem: First name is null or empty. at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 14, column: 1]

I thought this might be something short-circuiting Jersey, but my @PreMatching ContainerRequestFilter is executed beforehand, so I really have no idea why the 400 Response is not the traditional HTML one from Tomcat.

Why is this happening? What can I do to catch this and return my own Response?


Solution

  • As stated by Paul Samsotha in the comments, JacksonFeature from the jersey-media-json-jackson package define some ExceptionMappers, like JsonMappingException and JsonParseException. The solution is to create our own, register them within the ResourceConfig and register JacksonFeature last, otherwise it won't work.

    e.g.

    @Provider
    @Priority(1) // hack for overriding other implementations.
    public class JsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException> {
        @Override
        public Response toResponse(JsonMappingException exception) {        
            return Response.status(Status.BAD_REQUEST).build();
        }
    }
    
    
    @Provider
    @Priority(1) // hack for overriding other implementations.
    public class JsonParseExceptionMapper implements ExceptionMapper<JsonParseException> {
        @Override
        public Response toResponse(JsonParseException exception) {        
            return Response.status(Status.BAD_REQUEST).build();
        }
    }
    
    public class MyResourceConfig extends ResourceConfig {
        public MyResourceConfig() {
            register(CatchAllExceptionsMapper.class);
            register(JsonMappingExceptionMapper.class);
            register(JsonParseExceptionMapper.class);
            register(JacksonFeature.class);
        }
    }