6

If I am using Jersey 1.12, and I have multiple resource classes, and they all need to access some shared context, what's the best way to inject a dependency, whether it be in the constructor for the resource class, or into the handler method? Do I need to use an external DI library, or does Jersey have something built-in?

i.e. maybe the resource for Foos looks like this:

package com.example.resource;

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

@Path("/some/api/path/foo")
public class FooResource
{
    @GET
    @Produces("text/html")
    public String getFoo(@QueryParam("id") String id)
    {
        Foo foo = /* get a Foo from some shared context based on id */
        /* Process foo into a String */
    }
}

and for Bars:

package com.example.resource;

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

@Path("/some/api/path/bar")
public class BarResource
{
    @GET
    @Produces("text/html")
    public String getBar(@QueryParam("id") String id)
    {
        Bar bar = /* get a Bar from some shared context based on id */
        /* Process bar into a String */
    }
}
Shaggy Frog
  • 27,575
  • 16
  • 91
  • 128

4 Answers4

12

I ended up using Google Guice, which is a lightweight DI framework that integrates well with Jersey. Here's what I had to do:

First, I added dependencies in the pom.xml:

    <dependency>
        <groupId>com.google.inject</groupId>
        <artifactId>guice</artifactId>
        <version>3.0</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>com.sun.jersey.contribs</groupId>
        <artifactId>jersey-guice</artifactId>
        <version>1.12</version>
        <scope>compile</scope>
    </dependency>

I wanted a DAO implemented as a singleton with an interface:

public interface MySingletonDao
{
    // ... methods go here ...
}

and a concrete implementation:

@Singleton
public class ConcreteMySingletonDao implements MySingletonDao
{
    // ... methods go here ...
}

Decorated the resource classes like so:

@Path("/some/path")
@RequestScoped
public class MyResource
{
    private final MySingletonDao mySingletonDao;

    @Inject
    public MyResource(MySingletonDao mySingletonDao)
    {
        this.mySingletonDao = mySingletonDao;
    }

    @POST
    @Produces("application/json")
    public String post() throws Exception
    {
            // ... implementation goes here ...
    }
}

Created a class that will do the bindings:

public class GuiceConfig extends GuiceServletContextListener
{
    @Override
    protected Injector getInjector()
    {
        return Guice.createInjector(new JerseyServletModule()
        {
            @Override
            protected void configureServlets()
            {
                bind(MyResource.class);
                bind(AnotherResource.class);
                bind(MySingletonDao.class).to(ConcreteMySingletonDao.class);
                serve("/*").with(GuiceContainer.class);
            }
        });
    }
}

I used Jetty instead of Glassfish to actually act as the server. In my functional test, that looks something like:

private void startServer() throws Exception
{
    this.server = new Server(8080);
    ServletContextHandler root =
        new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);

    root.addEventListener(new GuiceConfig());
    root.addFilter(GuiceFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
    root.addServlet(EmptyServlet.class, "/*");

    this.server.start();
}

The EmptyServlet comes from Sunny Gleason's sample code given out as an answer at: https://stackoverflow.com/a/3296467 -- I originally had

root.addServlet(new ServletHolder(new ServletContainer(new PackagesResourceConfig("com.example.resource"))), "/*");

instead of the line

root.addServlet(EmptyServlet.class, "/*");

But that caused Jersey to try and perform the dependency injection instead of Guice, which caused runtime errors.

Community
  • 1
  • 1
Shaggy Frog
  • 27,575
  • 16
  • 91
  • 128
  • This more Inversion of Control than Dependency Injection. This sound fussy but this distinction has opened a world to me. – maborg Jun 18 '18 at 18:03
3

you can use SingletonTypeInjectableProvider: http://jersey.java.net/nonav/apidocs/1.12/jersey/com/sun/jersey/spi/inject/SingletonTypeInjectableProvider.html

sample:

ResourceConfig resourceConfig = new DefaultResourceConfig();
resourceConfig.getSingletons().add(
        new SingletonTypeInjectableProvider<Context, SingletonType>(
               SingletonType.class, new SingletonType()) {});{code}

or you can create SingletonTypeInjectableProvider descendant, annotate it with @Provider add it as a class. The you can inject provided instance wherever you need and where standard Jersey injection kicks in.

Pavel Bucek
  • 5,304
  • 27
  • 44
1

There is a jersey-spring project that supports Spring dependency injection. Replace your jersey ServletContainer with a SpringServlet, add a ContextLoaderListener to your web.xml and you can inject beans into your components. Here's a pretty decent walkthrough of the setup

http://www.mkyong.com/webservices/jax-rs/jersey-spring-integration-example/

EDIT

Here's an idea that doesn't require adding any dependencies. Create your own ServletContextListener that adds your objects to the ServletContext. Then inject the ServletContext into your Resources

public class MyContextListener implements ServletContextListener
{

    @Override
    public void contextDestroyed(ServletContextEvent event)
    {
    }

    @Override
    public void contextInitialized(ServletContextEvent event)
    {
        ServletContext context = event.getServletContext();
        context.setAttribute(Foo.class.getName(), new FooImpl());
    }

}

Then in your Resource

@Path("blah")
public class MyResource 
{
   private Foo foo;

   public MyResource(@Context ServletContext context)
   {
      foo = (Foo) context.getAttribute(Foo.class.getName());
   } 
}
jeff
  • 4,325
  • 16
  • 27
1

You do not have to use an external library unless you want to. It's well documented that getting CDI to work correctly with Jersey is currently a pain. However I can speak from experience that it can be done having done it myself. It's been awhile since I jumped through those hoops but I seem to recall we had to make our Resources Stateless EJB's to get it to work. There could have been other steps I took but I don't recall those now.

When Jersey 2.0 comes out this should get a whole lot easier as they'll be switching over to using the Core CDI implementation instead of their own. See this bug for more info:

http://java.net/jira/browse/JERSEY-517

nwallman
  • 502
  • 7
  • 14