Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAX-RS Custom ExceptionMapper not intercept RuntimeException

Tags:

jersey

jax-rs

I want to wrap underlaying RuntimeExceptions to a custom json format , making the servlet container won't dump the stacktrace to client.

I follow this question : JAX-RS (Jersey) custom exception with XML or JSON . When calling :

try {
  doSomething(parameters);
}
catch(RuntimeException e) {
  throw new MyCustomException(500 , e.getMessage() , Status.INTERNAL_SERVER_ERROR);
}

When I intentionally feed wrong parameters (and trigger RuntimeException thrown by doSomething() ) , I didn't see MyCustomExceptionMapper working. Instead , the servlet container dumps :

The RuntimeException could not be mapped to a response, re-throwing to the HTTP container
api.MyCustomException: (underlaying msgs)

The MyCustomExceptionMapper is indeed registered in the javax.ws.rs.core.Application :

  @Override
  public Set<Class<?>> getClasses()
  {
    Set<Class<?>> set = new HashSet<Class<?>>();
    set.add(other classes);
    set.add(MyCustomExceptionMapper.class);
    return set;
  }

What did I miss ?

Thanks a lot !

Environment : JAX-RS , jersey-server 1.5

classes spec :

class MyCustomException extends RuntimeException 
@Provider
class MyCustomExceptionMapper implements ExceptionMapper<MyCustomException>

updated :

I suspect that Application.getClasses() is never called , so I add some println messages :

  @Override
  public Set<Class<?>> getClasses()
  {
    System.out.println("\n\n\n\n ApiConfig getClasses");
  }

And in deed , it's never shown !

I am sure this ApiConfig is in the web.xml :

  <context-param>
    <param-name>javax.ws.rs.core.Application</param-name>
    <param-value>destiny.web.api.ApiConfig</param-value>
  </context-param>

But why it seems Jersey never calls it ?

like image 546
smallufo Avatar asked Apr 25 '11 04:04

smallufo


People also ask

How to handle Exception in JAX-RS?

Thrown exceptions are handled by the JAX-RS runtime if you have registered an exception mapper. Exception mappers can convert an exception to an HTTP response. If the thrown exception is not handled by a mapper, it is propagated and handled by the container (i.e., servlet) JAX-RS is running within.

What is an exception Mapper?

ExceptionMappers are custom, application provided, components that can catch thrown application exceptions and write specific HTTP responses. The are classes annotated with @Provider and that implement this interface.


2 Answers

I found the solution.

All I have to do is annotate MyCustomExceptionMapper with Spring's @Repository.

And remove the section in web.xml (not needed)

  <context-param>
    <param-name>javax.ws.rs.core.Application</param-name>
    <param-value>destiny.web.api.ApiConfig</param-value>
  </context-param>

Because Spring will lookup all @Repository and find a @Provider , and Jersey will make use of it.

like image 81
smallufo Avatar answered Oct 28 '22 14:10

smallufo


I think (on the basis of my experiments) that exception providers are looked up by exact class match, rather than by inheritance match, so an exception provider that handles RuntimeException will only fire if the app throws a raw RuntimeException; that's not the case with the class you've showed us. I have some theories about how to fix this (e.g., with a custom filter handler, or possibly some use of AOP) but nothing final yet.

In relation to the second half of your question, I just don't know. What I do know is that Apache CXF (the JAX-RS implementation I've worked with) has/had some failings in this area, and that I thus stick to registering all my @Providers by hand in the app's Spring config. I offer that as experience…

like image 27
Donal Fellows Avatar answered Oct 28 '22 14:10

Donal Fellows