Platinum Partner
netbeans,web services,spring,spring framework,rest,restful web services

Spring Framework Support in REST 0.7

REST 0.7 (i.e., Jersey, the reference implementation for RESTful web services) is out and now includes support for the Spring framework. Here is a step-by-step scenario based on a nice example written by my Sun colleague Peter Liu. Peter's sample uses NetBeans IDE 6.1 to create a web application that contains a Spring-aware servlet, a singleton resource, and a request resource. His example is derived from a blog post by Paul Sandoz showing how to manually integrate Spring and Jersey (and predating REST 0.7).

Note: In REST 0.8, Spring support will be enhanced so that the user does not have to create a Spring-aware servlet.

This is the example. It requires the NetBeans IDE Web and Java EE distribution 6.1 and REST plugin 0.7.

  1. Create a Web Application project and name it SpringRestWebApp. On the Frameworks page of the project creation wizard, select Spring Web MVC 2.5.

    Adding Spring framework in New Project wizard

  2. Right-click the SpringRestWebApp node and choose New > RESTful Web Services from Patterns. Select the Singleton pattern and name the resource Singleton (class name will then be SingletonResource). Create the test.servlet package to contain the resource.

    RESTful service from patterns wizard, showing resource and package names

  3. Replace the code in SingletonResource with the following.

    @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());
    }
    }

  4. Right-click in the code and select Fix Imports. If the imports cannot be fixed, check that the Swing and Jersey libraries are in the project.

    Context menu inside code showing Fix Imports option

  5. Create another RESTful Web Service using the Singleton pattern. Name the resource PerRequest and create it in the same test.servlet package.

  6. Replace the code in PerRequestResource with the following. Right-click in the code and select Fix Imports when you are done.

    @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());
    }
    }

  7. Right-click the SpringRestWebApp node and choose New > Servlet. Name the servlet SpringServlet and create it in the test.servlet package. You have now added all resources to the project, which should have the following structure:

    Complete test.servlet package structure

  8. Replace the code in SpringServlet with the following. Right-click in the code and select Fix Imports when done.

    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 Object getInjectableInstance(Object instance) {
    return instance;
    }

    public void inject(Object instance) {
    }
    };

    @Override
    protected void initiate(ResourceConfig rc, WebApplication wa) {
    ApplicationContext springContext = WebApplicationContextUtils.
    getRequiredWebApplicationContext(getServletContext());

    wa.initiate(rc, new SpringComponentProvider(springContext));
    }
    }

    Paul Sandoz writes "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)."


  9. Expand the project's Web Pages > WEB-INF node. Open the project's web.xml file. Replace com.sun.ws.rest.impl.container.servlet.ServletAdaptor with test.servlet.SpringServlet.

  10. Add the following to the project's applicationContext.xml file, to initialize the resources:
    <bean id="bean1" scope="singleton" class="test.servlet.SingletonResource">
    <property name="name" value="Mr. Singleton Bean"/>
    </bean>
    <bean id="bean2" scope="prototype" class="test.servlet.PerRequestResource">
    <property name="name" value="Mr. PerRequest Bean"/>
    </bean>

  11. Right-click the project node and select Test RESTful Web Services. The IDE deploys the project and launches the RESTful service tester in a browser window.

    RESTful service tester in browser window, showing test of SingletonResource
When you test the Singleton path, the service returns the Mr. Singleton Bean property and the Uses value increments for every request. When you test the PerRequest path, the service returns the Mr. PerRequest Bean property and the Uses value does not increment.
{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}