8
votes

Per title, exceptions thrown from a ParamConverter are NOT handled the way I expect.

With an ExceptionMapper:

@Provider
public class MyExceptionMapper implements ExceptionMapper<MyException> {
    @Override
    public Response toResponse(MyException exception) {
        return Response.serverError().entity( "It triggered" ).build();
    }
}

and ParamConverter:

@Provider 
(boilerplate junk)
    @Override
    public DateTime fromString(String value) {
        throw new MyException("convert");
    }

It does NOT return the "It triggered" text in a 500 error, but rather a 404.

Anticipated question : Are both providers registered?

Yes - If I throw "MyException" from a resource (within 'regular' code) it works as expected. I can also convert see the stacktrace with the "convert" message.

Is there any way to make exceptions from ParamConverters be handled by the ExceptionMapper?

I am using jersey 2.3.1, along with spring-jersey, launched in a jetty container 9.1.0.RC0

2

2 Answers

5
votes

Seem from reading this, the JAX-RS spec says the implementor should wrap unhandled exceptions in a NotFoundException (404) for @QueryParam and @PathParam, and from what I tested a 400, (I'm guessing BadRequestException) for @FormParam.

"if the field or property is annotated with @MatrixParam, @QueryParam or @PathParam then an implementation MUST generate an instance of NotFoundException (404 status) that wraps the thrown exception and no entity"

A couple ways I can see handling the exception, is to

  1. Just handle it in the ParamConverter, e.g.

    return new ParamConverter<T>() {
    
        @Override
        public T fromString(String string) {
            try {
                return (T)new MyObject().setValue(string);
            } catch (MyException ex) {
                Response response = Response.serverError().entity("Boo").build()
                throw new WebApplicationException(response);
            }
        }
    
        @Override
        public String toString(T t) {
            return t.toString();
        }  
    };
    
  2. Or just have your exception extend WebApplicationException, and return the Response there. e.g.

    public class MyException extends WebApplicationException {
    
        public MyException(String message) {
            super(Response.serverError().entity(message).build());
        }
    }
    
1
votes

I experienced the same behavior in Jersey 2.26.

Any Exception that extends RuntimeException gets mapped to a ParamException, which is itself a sublcass of WebApplicationException. Assuming your MyException extends RuntimeException, it's not getting caught because your ExceptionMapper only handles MyException.

Regarding the Jersey docs saying to throw a NotFoundException: I would argue a 404 does not apply when a queryParam can't be converted. A BadRequestException seems more appropriate. And also, I can't see anything unique in the Jersey frame work when a NotFoundException is thrown besides setting the response code

To get exceptions thrown from a ParamConverter end up in an ExceptionMapper, you'll have to have your ExceptionMapper catching a more global exception, like Throwable.

Another answer suggests returning a WebApplicationException. This should be a fine solution but will NOT work if the Response object has an entity. See here: https://github.com/jersey/jersey/issues/3716