MultiException when custom jersey param throws exception

Srikanth picture Srikanth · Apr 29, 2015 · Viewed 8.4k times · Source

NOTE: All code to reproduce this problem is available at https://gist.github.com/SrikanthRao/c9fc35e6fe22a74ab40c

http://localhost:8080/date/bean?date=2014-13-23 (using BeanParam) produces "{"code":500,"message":"There was an error processing your request. It has been logged (ID 48be9aa43bd49547)."}" without adding MultiExceptionMapper to jersey.

If I add MultiExceptionMapper to jersey, the above url results in

"Date is either not in YYYY-MM-DD format or is invalid"

http://localhost:8080/date?date=2014-13-23 (direct @QueryParam parameter) produces "Date is either not in YYYY-MM-DD format or is invalid"

Couple of questions:

  1. Is this the right way to handle input validation in a cleaner way?
  2. I expected this to work without needing to add my own MultiExceptionMapper. Doesn't Jersey support custom *Params inside a POJO that is injected as @BeanParam in resource methods?

Here is the stack trace produced when requesting (without adding MultiExceptionMapper to jersey). Ofcourse removed long traces. Let me know if you need complete stack trace.

    ERROR [2015-05-04 18:48:33,366] io.dropwizard.jersey.errors.LoggingExceptionMapper: Error handling a request: 0f23e4de758653d6
    ! javax.ws.rs.WebApplicationException: HTTP 400 Bad Request
    ! at io.dropwizard.jersey.params.AbstractParam.<init>(AbstractParam.java:28) ~[dropwizard-jersey-0.8.1.jar:0.8.1]
    ! at com.fun.myapp.LocalDateTimeParam.<init>(LocalDateTimeParam.java:20) ~[classes/:na]
    ! at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_45]
...
...
Causing: org.glassfish.hk2.api.MultiException: A MultiException has 3 exceptions.  They are:
! 1. javax.ws.rs.WebApplicationException: HTTP 400 Bad Request
! 2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.fun.myapp.PaginationFilters errors were found
! 3. java.lang.IllegalStateException: Unable to perform operation: resolve on com.fun.myapp.PaginationFilters
! 
! at org.jvnet.hk2.internal.Collector.throwIfErrors(Collector.java:88) ~[hk2-locator-2.4.0-b10.jar:na]
! at org.jvnet.hk2.internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:252) ~[hk2-locator-2.4.0-b10.jar:na]
! at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:360) ~[hk2-locator-2.4.0-b10.jar:na]
! at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:471) ~[hk2-locator-2.4.0-b10.jar:na]
....
....
WARN  [2015-05-04 18:48:33,401] org.glassfish.jersey.internal.Errors: The following warnings have been detected: WARNING: Unknown HK2 failure detected:
MultiException stack 1 of 3
javax.ws.rs.WebApplicationException: HTTP 400 Bad Request
    at io.dropwizard.jersey.params.AbstractParam.<init>(AbstractParam.java:28)
    at com.fun.myapp.LocalDateTimeParam.<init>(LocalDateTimeParam.java:20)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
...
...
MultiException stack 2 of 3
java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.fun.myapp.PaginationFilters errors were found
    at org.jvnet.hk2.internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:249)
    at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:360)
    at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:471)
...
...
MultiException stack 3 of 3
java.lang.IllegalStateException: Unable to perform operation: resolve on com.fun.myapp.PaginationFilters
    at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:389)
    at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:471)
    at org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:162)

I've asked this question on dropwizard-user google group - https://groups.google.com/forum/#!topic/dropwizard-user/yW-RXSSlspY

Answer

Guy Bouallet picture Guy Bouallet · May 8, 2015

Question 1

According to dropwizard's core documentation, I see two possible implementations for input validation:

  • through validation annotation

you can add validation annotations to fields of your representation classes and validate them...

This doesn't seem to be suitable for your case. In fact, there is no available annotation for LocalDateTime and creating one leads to parsing the LocalDateTime twice: for validation and for setting bean field.

  • Through error handling:

If you want more control, you can also declare JerseyProviders in your Environment to map Exceptions to certain responses by calling JerseyEnvironment#register(Object) with an implementation of javax.ws.rs.ext.ExceptionMapper...

To answer your first question, I would say that using an exception mapper is perfectly fine in your case.

Question 2 Debugging both isValidDate methods shows that the @BeanParam version uses ClazzCreator.java while the @QueryParam doesn't. This class is responsible for creating the bean class instance. If you check line 226 of the class, you will see that it collects multiple exceptions while parsing input with multiple errors. This should allow reporting several errors related to different bean fields, including some subsequent exceptions.

The answer is that Jeysey supports *Params inside a POJO. However, the related exception in encapsulated into a MultiException in the context of @BeanParam. Consequently, if you are planning to add other fields to PaginationFilters, you should consider reporting multiple errors in your mapped exception message.