Tuesday Apr 14, 2009

Jersey and Spring enterprise tech tip

An new enterprise tech tip explaining how to get started with Jersey and Spring is available.

Additionally, see here for JavaDoc and here for a Spring sample.


Monday Aug 18, 2008

Integrating Jersey and Spring: Take 3

Just in case it got hidden in the message of the 0.8 release Jersey now supplies Spring support via the spring maven module that was contributed by Martin.

So, rather than copying some code from my blog, you can depend on this module and reference the Spring servlet:

com.sun.jersey.spi.spring.container.servlet.SpringServlet 

in the web.xml. That is it. Martin describes this in more detail here. Note that after this was written some changes were made to the package names as Martin describes here.

I am quite happy with it as we managed to solve a knotty issue of referencing Spring beans in constructor/method parameters that Jersey is responsible for invoking, thus JAX-RS/Jersey-based annotated parameters can be intermixed with Spring-based annotated or referenced parameters. Having said that i think we may be able to make the integration even smoother in two areas when using Spring-based annotation configuration: 1) inferring the life-cycle Jersey requires; and 2) reusing @Autowired for constructor/method parameters.

The mechanisms by which Jersey integrates with Spring can equally apply to Guice or WebBeans or a more specialized integration.

Friday Feb 01, 2008

Integrating Jersey and Spring: Take 2

Marc previously described how to integrate Spring with Jersey 0.4 for the instantiation of root resources. This was an great first step but it fell short in a couple of areas:

  • there was some initialization code that could not be performed at initialization stage;
  • it was necessary to annotate (or specify the default provider for) all resources with a Spring specific life-cycle annotation, thus it was not possible to write 'vanilla' resources for use with Spring; and
  • this was only applicable to root resource classes. Jersey has other components, such as instances of MessageBodyReader/Writer, and it would be useful if those components could also be Spring-enabled.

I have spent this week unifing the instantiation of components (in addition to removing the requirement of META-INF/services for registration, it is all dynamic like for root resource classes). Instantiation of any component managed in Jersey is deferred to a ComponentProvider. By default Jersey provides a basic implementation but it is possible to  provide an application-specific implementation for say Spring. Jersey will then adapt that implementation so that Spring-registered and non-Spring-registered components can be instantiated.

(All code referenced below only works with the latest build of Jersey.)

I am still pondering the best way to declare an application-specific ComponentProvider but for now i am experimenting specifically with a Spring aware Servlet. Jersey ships with a servlet that can be extended to configure the WebApplication, which makes it very easy to extend for Spring support. Below is the code for the SpringServlet:

public class SpringServlet extends ServletContainer {
   
    private static class SpringComponentProvider implements ComponentProvider {
        private ApplicationContext springContext;

        SpringComponentProvider(ApplicationContext springContext) {
            this.springContext = springContext;
        }
       
        private String getBeanName(Class c) {
            String names[] = springContext.getBeanNamesForType(c);
            if (names.length == 0) {
                return null;
            } else if (names.length > 1) {
                throw new RuntimeException("Multiple configured beans for "
                        + c.getName());
            }
            return names[0];           
        }
       
        public Object getInstance(Scope scope, Class c)
                throws InstantiationException, IllegalAccessException {           
            String beanName = getBeanName(c);
            if (beanName == null) return null;
           
            if (scope == Scope.WebApplication &&
                    springContext.isSingleton(beanName)) {
                return springContext.getBean(beanName, c);
            } else if (scope == Scope.ApplicationDefined &&
                    springContext.isPrototype(beanName) &&
                    !springContext.isSingleton(beanName)) {
                return springContext.getBean(beanName, c);
            } else {
                return null;
            }
        }

        public Object getInstance(Scope scope, Constructor contructor,
                Object[] parameters)
                throws InstantiationException, IllegalArgumentException,
                IllegalAccessException, InvocationTargetException {
            return null;
        }

        public void inject(Object instance) {
        }       
    };
   
    @Override
    protected void initiate(ResourceConfig rc, WebApplication wa) {
        ApplicationContext springContext = WebApplicationContextUtils.
                getRequiredWebApplicationContext(getServletContext());
       
        wa.initiate(rc, new SpringComponentProvider(springContext));
    }       
}

Notice that SpringServlet extends ServletContainer and the initiate method is overridden. This method creates an ApplicationContext and then initiates the WebApplication by passing in an instance of the static inner class SpringComponentProvider. This class implements ComponentProvider and the getInstance method will attempt to obtain a Spring bean that is present and matches the requested scope, if so then the bean instance is returned otherwise null is returned. (Note that the getInstance method with a Constructor type parameter is not implemented, this is because we have not determined how to support constructors with Spring beans).

The SpringServlet can be used in a web.xml as follows:

<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
    <listener>
        <description>Spring listener that initializes the ApplicationContext in ServletContext</description>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>   
    <servlet>
        <servlet-name>Jersey Spring</servlet-name>
        <servlet-class>test.spring.SpringServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey Spring</servlet-name>
        <url-pattern>/\*</url-pattern>
    </servlet-mapping>
</web-app>

Then i can create two root resource classes. The first, SingletonResource, uses the Jersey supplied singleton life-cycle:

@Path("singleton")
@Singleton
public class SingletonResource {
   
    private String name;
   
    private int uses = 0;
   
    private synchronized int getCount() {
        return ++uses;
    }
   
    public SingletonResource() {
        name = "unset";
    }

    public String getName() {
        return name;
    }
   
    public void setName(String name) {
        this.name = name;
    }
   
    @GET
    @ProduceMime("text/plain")
    public String getDescription() {
        return "Name: " + getName() + ", Uses: " + Integer.toString(getCount());
    }
}

and the second, PerRequestResource, uses the per-request life-cycle:

@Path("request")
public class PerRequestResource {
   
    private String name;
   
    private int uses = 0;
   
    private synchronized int getCount() {
        return ++uses;
    }
   
    public PerRequestResource() {
        name = "unset";
    }

    public String getName() {
        return name;
    }
   
    public void setName(String name) {
        this.name = name;
    }
   
    @GET
    @ProduceMime("text/plain")
    public String getDescription() {
        return "Name: " + getName() + ", Uses: " + Integer.toString(getCount());
    }
}

The root resources are almost identical except that the former is annotated with @Singleton, but they should return different output (as shown later).

Then i can create the Spring applicationContext.xml:

<beans xmlns="http://www.springframework.org/schema/beans">
    <bean id="bean1" scope="singleton" class="test.spring.SingletonResource">
        <property name="name" value="Mr. Singleton Bean"/>
    </bean>
    <bean id="bean2" scope="prototype" class="test.spring.PerRequestResource">
        <property name="name" value="Mr. PerRequest Bean"/>
    </bean>
</beans>

Where the root resource classes are registered (with the appropriate scope) and a property is specified for each.

Then i can deploy and test using curl (the '>' character is my shell prompt): 

> curl http://localhost:8080/SpringResourceWebApp/singleton ; echo
Name: Mr. Singleton Bean, Uses: 1
> curl http://localhost:8080/SpringResourceWebApp/request ; echo
Name: Mr. PerRequest Bean, Uses: 1
>
> curl http://localhost:8080/SpringResourceWebApp/singleton ; echo
Name: Mr. Singleton Bean, Uses: 2
> curl http://localhost:8080/SpringResourceWebApp/request ; echo
Name: Mr. PerRequest Bean, Uses: 1
>
> curl http://localhost:8080/SpringResourceWebApp/singleton ; echo
Name: Mr. Singleton Bean, Uses: 3
> curl http://localhost:8080/SpringResourceWebApp/request ; echo
Name: Mr. PerRequest Bean, Uses: 1

Notice that the response to the URI containing the 'singleton' path segment returns the 'Mr. Singleton Bean' property as specified in the applicationContext.xml, and similarly the URI containing the 'request' path segment returns the 'Mr. PerRequest Bean' property. Also notice that the Uses value for the singleton-based URI increments for each request where as the one for the request-based URI does not. It works!

Having to edit  applicationContext.xml is something i would prefer to avoid and instead utilize some Spring specific annotations. The ComponentProvider interface could probably do with some tweaks to make things more efficient for per-request life-cycle. But in general it seems to work reasonably well and i am sure the same concepts for Spring integration would apply to Guice.

Friday Sep 14, 2007

A Spring in Jersey's integration step

Last week Marc talked about a resource provider SPI for Jersey and this week he presents how to use this SPI to integrate resource classes with Spring.
About

sandoz

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today