1
votes

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?

1
There is probably another exception mapper handling this. Only one mapper will be called per request. The one with the closest exception type to the type of the exception will be used. If you are using the Jackson provider, it is likely its mapper is being used. It is known to spit out the exception message as the response. - Paul Samsotha
I only made one ExceptionMapper, how can I confirm its Jackson's one? - Michel Feinstein
Are you using jersey-media-json-jackson? - Paul Samsotha
Can I disable it? - Michel Feinstein
Yes, I am using jersey-media-json-jackson 2.27 - Michel Feinstein

1 Answers

0
votes

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);
    }
}