The Source for Java Technology Collaboration
User: Password:



Marc Hadley's Blog

September 2007 Archives


Integrating Jersey and Spring

Posted by mhadley on September 14, 2007 at 09:18 AM | Permalink | Comments (10)

To further test the new resource provider SPI I described earlier I thought I'd try building a new resource provider that defers to Spring for resource creation. Note that I've never used Spring before so if there are better ways to accomplish what I did please let me know. I also ran into a problem that limited the integration more than I'd prefer (details at the end of this post). The Jersey dev list would be a great place to post any suggestions on how to improve the integration.

Implementation

The first task was to write a ResourceProvider implementation that uses Spring for resource creation. This is pretty straightforward and the getInstance method turned out to require only a few lines of code:

public Object getInstance(ResourceProviderContext context) {
  try {
    initSpringContext(context);
    initBeanName();
    Object resource = springContext.getBean(beanName, resourceClass);
    context.injectDependencies(resource);
    return resource;
  } catch (Exception ex) {
    throw new ContainerException("Unable to create resource", ex);
  }
}

The ResourceProvider interface only supplies a ResourceProviderContext to the getInstance method so I had to defer some initialization code to that method that would have fit better in the init method. I'll check if it makes sense to add a ResourceProviderContext to the init method but for this experiment I stuck with the SPI as is. The initSpringContext method uses the ResourceProviderContext to get the ServletContext which is then used to get the Spring ApplicationContext which has been initialized in the suggested way.

protected static synchronized void initSpringContext(
      ResourceProviderContext context) {
  if (springContext==null) {
    DummyResource r = new DummyResource();
    context.injectDependencies(r);
    springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(
        r.servletConfig.getServletContext());
  }
}

DummyResource is a simple annotated static inner class that is used as an injection target:

public static class DummyResource {
  @Resource
  public ServletConfig servletConfig;
}

Jersey is based on annotated classes whereas Spring is based on named beans. The Spring ResourceProvider needs to map a class name to a Spring bean name which is accomplished with the initBeanName method:

protected synchronized void initBeanName() {
  if (beanName==null) {
    String names[] = springContext.getBeanNamesForType(resourceClass);
    if (names.length==0)
      throw new RuntimeException("No configured bean for "+resourceClass.getName());
    else if (names.length>1)
      throw new RuntimeException("Multiple configured beans for "+resourceClass.getName());
    beanName=names[0];
  }
}

I couldn't see any straightforward way to determine which bean to use for a given resource class if there is more than one defined at the top level so the provider requires a single top-level bean for each resource class.

The final task was to define a new annotation that instructs the Jersey runtime to use the Spring resource provider:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ResourceFactory(SpringProvider.class)
public @interface SpringFactory {}

Example

With the above implemented you can now write a resource class whose instantiation is controlled by Spring:

@UriTemplate("{id}")
@SpringFactory
public class SpringResource {
    
  private String name;
    
  public SpringResource() {
    name="unset";
  }

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

With the following applicationContext.xml in the WEB-INF directory the resource will report its name as "Mr. Bean" thus demonstrating Spring-provided resource injection.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
  <bean id="bean1" scope="prototype" class="com.sun.ws.rest.spring.resources.SpringResource">
    <property name="name" value="Mr. Bean"/>
  </bean>
</beans>

The complete code is available here. No libraries are included in the ZIP file so you'll have to patch up the references to the required Jersey and Spring libraries.

Integration Shortcoming

I couldn't see any way to add support for Jersey-defined resources as constructor parameters. In an earlier post I described the new support for non-empty constructors. When using the Spring resource provider, the constructor can only include resources defined within the Spring configuration, not values supplied by Jersey. Is there a Spring API I could use to programatically add support for Jersey-defined constructor parameters ?



NetBeans Jersey Tooling

Posted by mhadley on September 10, 2007 at 12:37 PM | Permalink | Comments (0)

A couple of video's demonstrating NetBeans 6 support for creating Jersey-based Web services:

This is an up-to-date version of the demo we showed in our JavaOne session this year.



New Jersey Features

Posted by mhadley on September 07, 2007 at 09:05 AM | Permalink | Comments (0)

I recently merged the experimental "resourcefactory" branch back into the main Jersey trunk. The new code includes the following features and changes:

  • A change to the default resource object lifecyle from singleton to per-request
  • Support for non-empty resource class constructors
  • A new SPI for plugging-in new resource providers

Details on these changes below

Lifecycle Changes

Prior to the merge the Jersey runtime would create a single instance of a resource class and use that for all requests. Resource classes had to be re-entrant and thread safe. Following the merge the default scope for a resource object is per-request, i.e. the Jersey runtime creates a new instance of a resource class for each request. This change brings Jersey back in line with the current JSR 311 specification which requires a default per-request life-cycle. The old behavior can be recovered by annotating a resource class with the Singleton annotation from the com.sun.ws.rest.spi.resource package. If you want to be explicit about a resource classes life-cycle rather than rely on the default you can also use the PerRequest annotation from the same package. More on how these annotations work in the description of the new SPI below.

Non-empty Constructors

Prior to the merge a resource class was required to have a zero argument constructor that the Jersey runtime would use when creating the singleton instance. Following the merge this requirement has been relaxed such that constructors can now have arguments. This in conjunction with the change to a default per-request lifecycle allows a more natural use of constructors to perform instance initialization, e.g.:

@UriTemplate("entries/{id}")
public class Entry {
  
  EntryEntity entity;

  public Entry(@UriParam("id") String id) {
    entity = findEntryEntity(id);
    ...
  }

  @HttpMethod
  @ProduceMime({"application/json", "application/xml"})
  public EntryEntity getEntity() {
    return entity;
  }
}

The arguments allowed in a resource class constructor depends on the resource provider used to create an instance of the resource class (see the description of the new SPI below). For default per-request resource classes you can use any combination of parameters annotated with UriParam, UriParam, QueryParam, MatrixParam, HeaderParam or HttpContext. Singleton resource classes must still have a zero argument constructor.

Resource Provider SPI

The new resource provider SPI allows plugging-in of custom resource class factories and is used internally to support the per-request and singleton life-cycles. If you're not planning to develop a new resource provider you can safely stop reading here unless of course you are curious as to how the above features are implemented.

A resource provider is responsible for creating instances of resource classes for the Jersey runtime to use. A resource provider implements ResourceProvider and must have a zero-argument constructor. The Jersey runtime will create a new instance of a resource provider for each resource class that uses that provider so there is no need for a provider to maintain a list or map of resource classes and instances thereof.

The Jersey runtime identifies the resource provider to use for a particular resource class by examining the annotations on that resource class. It examines each class-level annotation in turn looking for a ResourceFactory meta-annotation and then uses the value of that annotation or the default per-request provider if none is found as the resource provider. This approach allows you to define new annotations that point to a particular resource provider, e.g. Jersey defines the Singleton annotation as follows:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ResourceFactory(SingletonProvider.class)
public @interface Singleton {}

Note the ResourceFactory meta-annotation which points to SingletonProvider, the Jersey-supplied resource provider for singleton resource classes.

The getInstance method of ResourceProvider supplies a ResourceProviderContext that a resource provider can use to obtain values for the resource class constructor's arguments and perform dependency injection on a newly created resource class instance, PerRequestProvider shows an example of its use. A resource provider is expected to supply values for constructor arguments unknown to Jersey (anything not annotated with UriParam, UriParam, QueryParam, MatrixParam, HeaderParam or HttpContext) and inject additional dependencies as required.

Its hoped that this SPI will support integration of Jersey with a variety of frameworks. Let us know if anything is unclear or needs improvement.





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds