Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jersey;: Registering Injected ExceptionMapper Instances via SpringServlet

Tags:

spring

jersey

Jersey: 1.12 Spring: 3.11 JDK: 1.6.0_35 Tomcat: 6..0.33

I am trying to register ExceptionMapper instances for a Jersey servlet that are instantiated by a Spring IoC container in the context of a SpringServlet, and am running into ye olde "The scope of the component class X must be a singleton" exception during the Jersey servlet initialization. I'm not in a position to post the actual code at this time, so I'm going to give the essentials of the implementation to see if something obvious jumps out at someone, or if I can be pointed at some documentation that I have missed.

The ExceptionMapper instance is essentially as follows:

// ... package definitions omitted ...

@Provider
@Singleton
public class MyExceptionMapper
    implements ExceptionMapper<MyException>
{
    // ... injected attributes omitted ...

    @Override
    public Response toResponse(MyException exception)
    {
        // ... specific handling logic omitted ...

       return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
    }
}

The bean definition in applicationContext.xml is as follows, which defaults to singleton scope (and adding an explicit scope specifier doesn't seem to change the behavior):

<bean id="myExceptionMapper" class="my.package.MyExceptionMapper" />

The Spring context loader listener and SpringServlet configuration in web.xml are essentially boilerplate, and the servlet will load, initialize and operate properly with other inject attributes when the bean definition for MyExceptionMapper is commented out of the applicationContext.xml. But when the bean definition is present in applicationContext.xml, I get log messages to the effect of:

SpringProviderFactory - Registering Spring bean, myExpectionMapper, of type my.package.MyExceptionMapper as a provider class
SpringProviderFactory - Registering Spring bean, myServiceController, of type my.package.MyServiceController as a root resource class
... other root resource classes loading ...
SpringServlet - Exception occurred when initialization
java.lang.RuntimeException: The scope of the component class my.package.MyExceptionMapper must be a singleton
  at som.sun.jersey.core.spi.component.ioc.IoCProviderFactory.wrap(...)

I have tried placing MyExceptionMapper in the scanned packages hierarchy to be picked up by the SpringServlet during initialization, and in a separate package hierarchy, and the results do not change.

Any assistance or guidance on this would be greatly appreciated.

like image 908
J. Gregory Wright Avatar asked Nov 30 '12 08:11

J. Gregory Wright


3 Answers

My ExceptionMappers are under the structure where I'm doing component scan, and mine are simply annotated with @Component for Spring registry and @Provider for Jersey.

My component scan just looks like:

<context:component-scan base-package="com.company.web"/>

and the ExceptionMapper looks pretty much like yours:

package com.mycompany.web.provider;

import com.mycompany.exception.SpecialException;
import com.mycompany.exception.Error;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
@Component
public class SpecialExceptionMapper implements ExceptionMapper<SpecialException> {

    @Autowired
    private MessageSource messageSource;

    @Override
    public Response toResponse(SpecialException exception) {

        Error error = new Error();


        error.errorMessage = messageSource.getMessage(exception.getResourceBundleKey(), exception.getArgs());

        return Response.status(Response.Status.BAD_REQUEST).
                entity(error).
                type(MediaType.APPLICATION_JSON).
                build();
    }
}
like image 62
Patrick Avatar answered Nov 15 '22 06:11

Patrick


Adding the scope explicitly in the spring config did the trick for me:

<bean id="exceptionMapper" class="my.pkg.MyExceptionMapper" scope="singleton" />
like image 39
Tim Van Laer Avatar answered Nov 15 '22 08:11

Tim Van Laer


Stepped on same problem with Spring 3.2.3 and Jersey 2.3.1 integration.

What worked for me was combination of two:

  1. Annotating jersey @Provider class with @Service or making it explicitly singleton in app-context xml

    @Service //implies a singleton-scope
    @Provider
    public class UnhandledExceptionMapper implements ExceptionMapper<Throwable> {
    
        @Override
        public Response toResponse(Throwable exception) { //...
    
  2. Adding package that contains provider class to jersey packages, I prefer to do this in MyApplication class:

    public class MyApplication extends ResourceConfig {
    public MyApplication() {
        packages("com.mycompany.api.controller", "com.mycompany.api.jersey");
                //....
        }
    }
    
like image 38
mSolujic Avatar answered Nov 15 '22 07:11

mSolujic