users@jersey.java.net

[Jersey] How to configure Jersey to use an existing spring child (non-root) application context

From: <davide.cavestro_at_sempla.it>
Date: Fri, 3 Aug 2012 09:32:07 +0000 (GMT)

I need to include jersey into my web application. I use spring ioc and
spring mvc and already have some dispatcher servlet instances. Jersey
resources should be automatically injected with spring beans from the
child (non-root) application context associated with one of the
dispatcher servlet.
I tried mapping Jersey [i]SpringServlet[/i] as a servlet into
web.xml... but it works well only when you want to use the root
application context or create a child new one. That's not my case... I
was not able to configure a to workaround the problem I had to subclass
[i]SpringServlet[/i] in order to expose it as a controller from my
dispatcher servlet.

Here you are the controller I made
[code]
package com.acme.my.app;

import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections.iterators.IteratorEnumeration;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.ServletConfigAware;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.servlet.ModelAndView;

import com.sun.jersey.api.core.PackagesResourceConfig;
import com.sun.jersey.spi.spring.container.servlet.SpringServlet;

/**
 *
 *
 * @author Davide Cavestro
 *
 */
@Controller
@RequestMapping ("/resources")
public class SuiteServlet extends SpringServlet implements
ApplicationContextAware, ServletContextAware, InitializingBean,
ServletConfigAware, ApplicationListener<ContextRefreshedEvent> {

    /**
     * serial version uid
     */
    private static final long serialVersionUID = 1L;
    private ApplicationContext applicationContext;
    private ServletConfig servletConfig;
    private Map<String, String> initParameters = new HashMap<String,
String> ();

    /**
     *
     */
    public SuiteServlet () {//simulate servlet init params (we don't
have a web.xml mapping to se them)
        initParameters.put (PackagesResourceConfig.PROPERTY_PACKAGES,
"com.acme.my.app");
        initParameters.put
("com.sun.jersey.api.json.POJOMappingFeature", "true");
    }

    @Override
    protected ConfigurableApplicationContext getContext () {
        return (ConfigurableApplicationContext) applicationContext;
    }

    @Override
    public void setApplicationContext (final ApplicationContext
applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet () throws Exception {
    }

    @Override
    public void setServletContext (final ServletContext servletContext)
{
        // no-op
    }

    @RequestMapping ("/**")
    public ModelAndView main (final HttpServletRequest request, final
HttpServletResponse response) throws ServletException, IOException {
        service (request, response);
        return null;
    }

    @Override
    public void setServletConfig (final ServletConfig servletConfig) {
        this.servletConfig = servletConfig;
    }

    @Override
    public ServletConfig getServletConfig () {
        // wrap the original servlet config in order to expose servlet
init params
        final ServletConfig config = new ServletConfig () {

            @Override
            public String getServletName () {
                return servletConfig.getServletName ();
            }

            @Override
            public ServletContext getServletContext () {
                return servletConfig.getServletContext ();
            }

            @Override
            public String getInitParameter (String name) {
                return initParameters.get (name);
            }

            @Override
            public Enumeration getInitParameterNames () {
                return new IteratorEnumeration (initParameters.keySet
().iterator ());
            }};
        return config;
    }

    @Override
    public void onApplicationEvent (final ContextRefreshedEvent event)
{
        try {
            init (getServletConfig ());
        } catch (final ServletException e) {
            e.printStackTrace (System.err);
        }
    }
}
[/code]

This workaround has at least one drawback:
* if the SpringServlet is mapped to [i]"/resources"[/i], the REST
resources still have to declare [i]"resources"[/i] in their mapping
(i.e. @Path("/resources/myresource/"): the two mappings have to match,
otherwise the controller or the resource does not participate to the
request processing

Maybe starting from the SpringServlet a new application context having
my dispatcherservlet's existing application context as parent would
give better results.
[b]Is there any other (cleaner) way to achieve jersey-spring child
application context integration? [/b]