3

I am using jersey for REST web services.

I am handling all 404 responses by throwing NotFoundException(Package com.sun.jersey.api) whenever I don't get any object from service layer.

e.g.

@GET
@Path("/{folderID}")
@Produces(MediaType.APPLICATION_JSON)
public Response getFolder(@PathParam("folderID") int folderID) {
       .
       .
       .
       Folder folderObj = folderService.getFolder(folderID);
       if(folderObj == null){
            throw new NotFoundException("Folder with ID '"+folderID+"'  not found.");
       }
}

I have written ExceptionMapper for this exception.

@Provider
public class NotFoundExceptionMapper implements   ExceptionMapper<NotFoundException> {

public Response toResponse(NotFoundException ex) {
    ErrorMesage errorMessage = new ErrorMessage();
    errorMessage.setCode(Status.NOT_FOUND.getStatusCode());
    errorMessage.setMessage(ex.getMessage());

    return Response.status(Status.NOT_FOUND)
            .entity(errorMessage)
            .type(MediaType.APPLICATION_JSON)
            .build();
}

}

So When I give unknown folder ID as path parameter, exception is thrown but code in NotFoundExceptionMapper is not invoked. (I can see exception message in response but as 'plain text', even though in mapper I am returning response in JSON; and debug break point is also not hit).

Also, Above exception mapper is invoked when I enter incorrect resource name in URI, but not for incorrect path param.

I have also added exception mapper like below to respond to all other exceptions.

public class GenericExceptionMapper implements  ExceptionMapper<Throwable>{

public Response toResponse(Throwable ex) {
    ErrorMessage errorMessage = new ErrorMessage();
    errorMessage.setCode(Status.INTERNAL_SERVER_ERROR.getStatusCode());
    errorMessage.setMessage(ex.getMessage());


    return Response.status(errorMessage.getCode())
            .entity(errorMessage)
            .type(MediaType.APPLICATION_JSON)
            .build();   
}

Above code is called whenever any exception (other than mapped exceptions) is thrown and I get proper JSON response.

So what is wrong with NotFoundException here? I have googled about this and also looked into source of NotFoundException but didn't find anything useful, please guide me on this.

dk_123
  • 41
  • 1
  • 2
  • 1
    How do you register these exception mappers? – Jaroslaw Pawlak Nov 16 '15 at 10:04
  • Haven't tested, but might be related to [this](http://stackoverflow.com/q/29414041/2587435)... The correlation can be found in the [javavdoc](https://jersey.java.net/apidocs/1.19/jersey/index.html?com/sun/jersey/api/NotFoundException.html) for NotFoundException, where it says the String arg is the entity. – Paul Samsotha Nov 16 '15 at 10:04
  • I have tried both ways. 1. Adding @Provider annotation 2. Registering resource in Application class – dk_123 Nov 16 '15 at 10:04
  • I've never tried registering more than one exception mapper. I'm wondering whether the problem could be that wrong mapper is called. Last time I was doing this, we had only one exception mapper taking `RuntimeException` and then a couple of `instanceof` inside for cases such as 404. All other exceptions (not covered by `instanceof`) were resulting in 500. – Jaroslaw Pawlak Nov 16 '15 at 10:10
  • Is there any in built mapper in jersey for NotFoundException? constructor of NotFoundException does this super(Responses.notFound().entity(message).type("text/plain").build()); Is this the reason I am getting response in 'plain text'? – dk_123 Nov 16 '15 at 10:20
  • Most likely. You can always create your own NotFoundException that extends WebApplicationException. You can just make whatever response you want in the constructor. This way you don't need a mapper. If you still want it to go through a mapper (maybe for logging), just don't extend WebApplicationException – Paul Samsotha Nov 16 '15 at 10:30
  • Thanks! I too think I need to create my own exception. I used other constructor of NotFoundException and it worked!! But I couldn't get desired message in 'string', I can only get hold of resource URI – dk_123 Nov 16 '15 at 10:36
  • In Jersey 2.x, if you throw a WebApplicationException, with a non-null entity in the response, it will not be caught by mapper. javax.ws.rs.NotFoundException extends WebApplicationException. Make sure not to set enitty before throwing it. – Filip Nov 16 '15 at 12:25
  • @peeskillet is correct. The problem is that ExceptionMapper isn't triggered when a WebApplicationException is thrown that contains an entity. Seems like a bug – ChrisO Nov 15 '17 at 23:40

1 Answers1

3

A snippet from the jersey ServerRuntime class. It has a special logic that if the Exception is an instance of WebApplicationException and has a body, it does not go to the exception mappers at all.

private Response mapException(Throwable originalThrowable) throws Throwable {
    if (throwable instanceof WebApplicationException) {
        WebApplicationException webApplicationException = (WebApplicationException)throwable;
     }

    this.processingContext.routingContext().setMappedThrowable(throwable);
    waeResponse = webApplicationException.getResponse();
    if (waeResponse.hasEntity()) {
        LOGGER.log(java.util.logging.Level.FINE, LocalizationMessages.EXCEPTION_MAPPING_WAE_ENTITY(waeResponse.getStatus()), throwable);
        return waeResponse;
    }        

    long timestamp = this.tracingLogger.timestamp(ServerTraceEvent.EXCEPTION_MAPPING);
    ExceptionMapper mapper = this.runtime.exceptionMappers.findMapping(throwable);
}
Sunny Agarwal
  • 1,451
  • 4
  • 18
  • 36