smallufo
smallufo

Reputation: 11796

JAX-RS Custom ExceptionMapper not intercept RuntimeException

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 ?

Upvotes: 7

Views: 16335

Answers (5)

amu
amu

Reputation: 21

I faced the same problem and the change in web.xml, in particular in tag solved the issue. Just make sure that the for the includes your package for the exception and exception mapper classes not only the packages containing the models and resources jersey.config.server.provider.packages basepackage

ex. if package for models/entities is pkg.entity and for exception is pkg.exception param-value (basepackage) will be pkg. If basepackage is set pkg.entity it doesn't work...that is how i solved the issue.

Upvotes: 0

jbuhacoff
jbuhacoff

Reputation: 1338

Your web.xml had an incorrect param-name in web.xml so that setting was being ignored.

The correct param name is javax.ws.rs.Application (not javax.ws.rs.core.Application which is the class you're extending).

See for example: docs.oracle.com/cd/E24329_01/web.1211/e24983/configure.htm#RESTF179

Upvotes: 1

pedrocgsousa
pedrocgsousa

Reputation: 417

You just need to configure the servlet in web.xml. You don't need to add @Repository to your ExceptionMapper.

<servlet>
    <servlet-name>rest-servlet</servlet-name>
    <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    <init-param>
        <param-name>com.sun.jersey.config.property.packages</param-name>
        <param-value>your.base.package.to.rest</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>rest-servlet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

When the application is deploy you can see the following lines in log file:

INFO: Scanning for root resource and provider classes in the packages:
your.base.package.to.rest

INFO: Root resource classes found:
class your.base.package.to.rest.resources.FooResource

INFO: Provider classes found:
class your.base.package.to.rest.providers.NotFoundMapper

Tested with:

  • Jersey v1.11 12/09/2011 10:27 AM
  • Spring v3.1.1
  • GlassFish Server Open Source Edition 3.1.2 (build 23)

Upvotes: 0

smallufo
smallufo

Reputation: 11796

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.

Upvotes: 2

Donal Fellows
Donal Fellows

Reputation: 137567

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…

Upvotes: 1

Related Questions