javakaffee

Just another weblog about (web) development technologies like spring, tapestry, jersey, jsf, hibernate and more

April 21, 2008

jersey-spring integration mostly complete

Filed under: development, webdev, REST — martin.grotzke @ 1:48 am

Hi,

after jersey 0.7 was released on friday, I just finished writing tests for the spring-integration stuff.
The jersey-spring-integration is based on the SpringComponentProvider blogged by Paul Sandoz and adds some nice things like annotation based injection of IoC-container managed beans (useful for all IoC-frameworks) and annotation based autowiring support even for spring-2.0 users.
Some extensions were made to jersey itself, which are useful for integration of IoC-containers in general (like the mentioned annotation based injection of beans).

The jersey-spring-integration is developed in a separate branch and is going to be part of jersey release 0.8. If you want to use it before jersey 0.8 is there, you can checkout the branch, build jersey, then spring and use the built jars (and configure your web.xml accordingly as described below).

So what does the jersey-spring-integration provide?

  • Integration of spring-2.0 or spring-2.5 with jersey (what else 8-))
  • jersey resources can be managed by spring (pulled from spring’s bean-factory)
  • jersey resources can use spring managed beans via the @Inject annotation that is now provided by jersey (also useful for integration of other IoC-containers)
  • jersey resources can be marked with the @Autowire annotation (that is provided by the jersey-spring-integration), such resources then are created by spring and dependencies injected accordingly
  • jersey resources can use annotation based configuration that comes with spring-2.5
  • jersey now provides an AnnotationInjectable, that can be provided for some annotation, so that the injectable is asked for an instance of a class if a field of some resource is annotated with the specified annotation
  • jersey now provides a ResourceContext, that can be used to get an instance of some subresource from the IoC-container and therfore can use features like e.g. AOP provided by the IoC-container also for subresources

Integration of spring with jersey

After you built both jersey and jersey-spring from the spring-integration branch and configured them as dependencies for your project, you only have to register the SpringServlet in your web.xml (I’m asuming that you have spring already configured therein):

<servlet>
    <servlet-name>Jersey Spring</servlet-name>
    <servlet-class>com.sun.ws.rest.spring.SpringServlet</servlet-class>
    <init-param>
        <param-name>com.sun.ws.rest.config.property.resourceConfigClass</param-name>
        <param-value>com.sun.ws.rest.api.core.PackagesResourceConfig</param-value>
    </init-param>
    <init-param>
        <param-name>com.sun.ws.rest.config.property.packages</param-name>
        <param-value>your.resource.package</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>Jersey Spring</servlet-name>
    <url-pattern>/api/*</url-pattern>
</servlet-mapping>

Use resources managed by spring

If you want to get your resource created by spring you simply have to define it in your applicationContext.xml. Only bear in mind, that the scope in your applicationContext.xml matches the scope of your resource class (jersey-Singleton == spring-singleton, jersey-PerRequest == spring-prototype).


For example you might have this resource:

@Path( "springifiedsingleton" )
@Singleton
public class SpringifiedSingletonRootResource {

    private String _value;

    public SpringifiedSingletonRootResource String value ) {
        _value = value;
    }
    
    @GET
    @ProduceMime("text/plain")
    public String getValue() {
        return _value;
    }
    
}

Define the resource in your applicationContext.xml:

<bean id="singletonRootResource" class="spring.examples.SpringifiedSingletonRootResource"
    scope="singleton">
    <constructor-arg value="foo" />
</bean>

That’s it, now your resources will be provided by spring.


If you don’t want to define each new resource in your applicationContext, but simply want to use your existing spring managed beans/services, you can use the following approach using the @Inject annotation.

@Inject spring managed beans/services into your resources

The @Inject annotation is now provided by jersey and is useful for integration of several IoC-containers, not only for spring. A field that is annotated with @Inject is initialized with an instance (of the fields type), that is pulled from the ComponentProvider, and in this case from spring.


Just to have an example:

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.ProduceMime;

import com.sun.ws.rest.spi.resource.Inject;
import com.sun.ws.rest.spi.resource.Singleton;

@Path( "injectingsingleton" )
@Singleton
public class InjectingSingletonRootResource {

    @Inject
    private SomeService _someService;
    
    @GET
    @ProduceMime("text/plain")
    public String getValue() {
        return _someService.getSomething();
    }
    
}

Your applicationContext.xml now only has to contain the bean definition for your SomeService


The instance of SomeService is retrieved from spring by its type, so if there are more than one bean definitions for SomeService the injection is not going to work. If this would be a requirement we should use another annotation that allows to set the bean-name (like e.g. @Resource).

Use @Autowire to autowire your resources with spring-2.0

If you want to get your resource class autowired, you can annotate it with @Autowire, which is provided by the jersey-spring-integration package. Resource classes annotated with @Autowire are created by spring (also spring BeanPostProcessors are applied). The @Autowire also allows to specify the AutowireMode (one of AUTODETECT, BY_NAME, BY_TYPE, CONSTRUCTOR, see AutowireCapableBeanFactory) and whether to perform a dependency check for objects (which is not applicable to autowiring a constructor, thus ignored there).


The example is really simple:

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.ProduceMime;

import com.sun.ws.rest.spi.resource.Inject;
import com.sun.ws.rest.spi.resource.Singleton;
import com.sun.ws.rest.spring.annotations.Autowire;

@Path( "injectingsingleton" )
@Singleton
@Autowire
public class AutowiringSingletonRootResource {

    @Inject
    private SomeService _someService;
    
    @GET
    @ProduceMime("text/plain")
    public String getValue() {
        return _someService.getSomething();
    }
    
}

Your applicationContext.xml still has to contain the bean definition for your SomeService. As this AutowiringSingletonRootResource is created by spring it can make use of all spring features, but you don’t have to define each resource in your applicationContext.xml.

Retrieve your subresources from the ResourceContext

If you have your root resource classes created by spring, you’re perhaps happy that you can use features provided by spring like AOP, transaction-handling or caching. But what to do when your root resource must return some sub-resource (sub-resource locator), but this sub-resource shall also be created by spring? Until now the sub-resource was created using new() and therefore it could not be created by spring and therefore was not proxied - no AOP, transaction-handling or caching…


This can now be achieved using the ResourceContext, that can be used to retrieve an instance of some sub-resource:

import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;

import spring.examples.UserService.User;

import com.sun.ws.rest.api.NotFoundException;
import com.sun.ws.rest.api.core.ResourceContext;
import com.sun.ws.rest.spi.resource.Inject;
import com.sun.ws.rest.spi.resource.Singleton;

@Path( "users" )
@Singleton
public class UsersResource {

    private final UserService _userService;
    
    @Context
    private ResourceContext _resourceContext;
    
    public UsersResource( UserService userService ) {
        _userService = userService;
    }
    
    @Path( "{username}" )
    public UserResource getValue( @PathParam( "username" ) String username ) {
        final User user = _userService.findByUsername( username );
        if ( user == null ) {
            throw new NotFoundException( "User " + username + " not found!" );
        }
        final UserResource result = _resourceContext.getResource( UserResource.class );
        result.setUser( user );
        return result;
    }
    
}

In this example, I asume that both the UsersResource and the UserResource are defined in your applicationContext.xml (and the UserService of course also) and therefore created by spring, the ResourceContext is injected by jersey.

Using spring-2.5 features

Just to mention, it’s also possible to use annotation driven bean configuration as it came with spring-2.5. This is an example for a resource class, also notice that both scopes must match:

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.ProduceMime;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.sun.ws.rest.spi.resource.PerRequest;

@Path( "injectingsingleton" )
@PerRequest
@Component
@Scope( "prototype" )
public class AnnotatedPerRequestResource {

    @Autowired
    private SomeService _someService;
    
    @GET
    @ProduceMime("text/plain")
    public String getValue() {
        return _someService.getSomething();
    }
    
}

That spring discovers this resource class, the applicationContext.xml must contain the , as it’s described in the spring documentation.

5 Comments »

  1. Many thanks! The main reason for me to keep using the CXF jax-rs implementation instead
    of the one of Jersey was its integration with Spring.
    Thanks for making it possible for me to move to Jersey.

    Comment by Arnold Reuser — April 22, 2008 @ 2:44 pm

  2. Great :)

    Cheers,
    Martin

    Comment by martin.grotzke — April 22, 2008 @ 4:41 pm

  3. Just to add this as a comment here: if you use jersey-spring from the jersey trunk, remember to change the dependency in your pom.xml, configuration stuff in the web.xml and imported jersey/jersey-spring classes according to the changes that were made today.

    Cheers,
    Martin

    Comment by martin.grotzke — April 29, 2008 @ 12:03 am

  4. […] 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. […]

    Pingback by outaTiME at refinn dot com » Blog Archive » Integrating Jersey and Spring: Take 3 — August 19, 2008 @ 2:52 am

  5. For me is very helpful article

    thanks a lot

    Comment by kfzversicherungsvergleich — March 7, 2011 @ 11:43 pm

RSS feed for comments on this post. TrackBack URI

Leave a comment

*
To prove you're a person (not a spam script), type the security word shown in the picture.
Anti-Spam Image

Powered by WordPress