114

Starting from scratch without any previous Jersey 1.x knowledge, I'm having a hard time understanding how to setup dependency injection in my Jersey 2.0 project.

I also understand that HK2 is available in Jersey 2.0, but I cannot seem to find docs that help with Jersey 2.0 integration.

@ManagedBean
@Path("myresource")
public class MyResource {

    @Inject
    MyService myService;

    /**
     * Method handling HTTP GET requests. The returned object will be sent
     * to the client as "text/plain" media type.
     *
     * @return String that will be returned as a text/plain response.
     */
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/getit")
    public String getIt() {
        return "Got it {" + myService + "}";
    }
}

@Resource
@ManagedBean
public class MyService {
    void serviceCall() {
        System.out.print("Service calls");
    }
}

pom.xml

<properties>
    <jersey.version>2.0-rc1</jersey.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey</groupId>
            <artifactId>jersey-bom</artifactId>
            <version>${jersey.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-common</artifactId>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey</groupId>
        <artifactId>jax-rs-ri</artifactId>
    </dependency>
</dependencies>

I can get the container to start and serve up my resource, but as soon as I add @Inject to MyService, the framework throws an exception:

SEVERE: Servlet.service() for servlet [com.noip.MyApplication] in context with path [/jaxrs] threw exception [A MultiException has 3 exceptions.  They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=MyService,parent=MyResource,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,1039471128)
2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.noip.MyResource errors were found
3. java.lang.IllegalStateException: Unable to perform operation: resolve on com.noip.MyResource
] with root cause
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=MyService,parent=MyResource,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,1039471128)
    at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:74)


My starter project is available at GitHub: https://github.com/donaldjarmstrong/jaxrs

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
donnie_armstrong
  • 1,353
  • 2
  • 10
  • 8

8 Answers8

111

You need to define an AbstractBinder and register it in your JAX-RS application. The binder specifies how the dependency injection should create your classes.

public class MyApplicationBinder extends AbstractBinder {
    @Override
    protected void configure() {
        bind(MyService.class).to(MyService.class);
    }
}

When @Inject is detected on a parameter or field of type MyService.class it is instantiated using the class MyService. To use this binder, it need to be registered with the JAX-RS application. In your web.xml, define a JAX-RS application like this:

<servlet>
  <servlet-name>MyApplication</servlet-name>
  <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
  <init-param>
    <param-name>javax.ws.rs.Application</param-name>
    <param-value>com.mypackage.MyApplication</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>MyApplication</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping>

Implement the MyApplication class (specified above in the init-param).

public class MyApplication extends ResourceConfig {
    public MyApplication() {
        register(new MyApplicationBinder());
        packages(true, "com.mypackage.rest");
    }
}

The binder specifying dependency injection is registered in the constructor of the class, and we also tell the application where to find the REST resources (in your case, MyResource) using the packages() method call.

mkobit
  • 43,979
  • 12
  • 156
  • 150
joscarsson
  • 4,789
  • 4
  • 35
  • 43
  • 1
    What about the EntityManager? Any hint how to bind it, so I can Inject it via @PersistenceContext ? – Johannes Staehlin Jun 18 '13 at 09:15
  • 4
    I'm not sure what `EntityManager` is, but judging by http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html it seems to be an interface. You can bind it using `bind(EntityManagerImpl.class).to(EntityManager.class)` (which will bind a class `EntityManagerImpl` implementing the interface `EntityManager`. If you need to use a factory, take a look at `bindFactory()` in the `AbstractBinder`. If you need help with this, please create a new question (I won't have room to answer it in the comments). Also, I'm not sure you should use @PersistentContext, just use @Inject for everything – joscarsson Jun 25 '13 at 15:03
  • Yeah, EntityManager is JPA (Java EE) specific. Thanks for you comment, I will open another question if I run into a specific problem! – Johannes Staehlin Jun 25 '13 at 15:09
  • Just for the record, JPA also runs on Java SE. http://www.oracle.com/technetwork/java/javaee/tech/persistence-jsp-140049.html – prefabSOFT Sep 08 '13 at 05:51
  • 2
    What does bind do? What if I have an interface and an implementation? – Dejell Jan 21 '14 at 17:06
  • For `EntityManager`, you can look at [this post](http://stackoverflow.com/q/28045019/2587435) – Paul Samsotha Feb 01 '15 at 03:18
  • For an interface and an implement just add: bind(MyServiceImpl.class).to(MyServiceIntf.class).in(RequestScoped.class) and it works well – nofomopls Mar 21 '17 at 07:10
  • What if I would like to use Dagger dependency injection? https://stackoverflow.com/q/49221066/942671 – Rinke Jan 24 '19 at 12:12
58

First just to answer a comment in the accepts answer.

"What does bind do? What if I have an interface and an implementation?"

It simply reads bind( implementation ).to( contract ). You can alternative chain .in( scope ). Default scope of PerLookup. So if you want a singleton, you can

bind( implementation ).to( contract ).in( Singleton.class );

There's also a RequestScoped available

Also, instead of bind(Class).to(Class), you can also bind(Instance).to(Class), which will be automatically be a singleton.


Adding to the accepted answer

For those trying to figure out how to register your AbstractBinder implementation in your web.xml (i.e. you're not using a ResourceConfig), it seems the binder won't be discovered through package scanning, i.e.

<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
    <param-name>jersey.config.server.provider.packages</param-name>
    <param-value>
        your.packages.to.scan
    </param-value>
</init-param>

Or this either

<init-param>
    <param-name>jersey.config.server.provider.classnames</param-name>
    <param-value>
        com.foo.YourBinderImpl
    </param-value>
</init-param>

To get it to work, I had to implement a Feature:

import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;

@Provider
public class Hk2Feature implements Feature {

    @Override
    public boolean configure(FeatureContext context) {
        context.register(new AppBinder());
        return true;
    }
}

The @Provider annotation should allow the Feature to be picked up by the package scanning. Or without package scanning, you can explicitly register the Feature in the web.xml

<servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>
            com.foo.Hk2Feature
        </param-value>
    </init-param>
    ...
    <load-on-startup>1</load-on-startup>
</servlet>

See Also:

and for general information from the Jersey documentation


UPDATE

Factories

Aside from the basic binding in the accepted answer, you also have factories, where you can have more complex creation logic, and also have access to request context information. For example

public class MyServiceFactory implements Factory<MyService> {
    @Context
    private HttpHeaders headers;

    @Override
    public MyService provide() {
        return new MyService(headers.getHeaderString("X-Header"));
    }

    @Override
    public void dispose(MyService service) { /* noop */ }
}

register(new AbstractBinder() {
    @Override
    public void configure() {
        bindFactory(MyServiceFactory.class).to(MyService.class)
                .in(RequestScoped.class);
    }
});

Then you can inject MyService into your resource class.

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • I could register my binder class just via a ResourceConfig implementation, like shown in the accepted answer. No Feature class was needed. – Patrick Koorevaar Sep 23 '16 at 08:54
  • Using `web.xml` even though the `configure()` on `Hk2Feature` is called, requesting resource throws a `NullPointerException`. @PaulSamsotha – bytesandcaffeine Jun 05 '18 at 07:03
  • Well *It simply reads* does not really make me understand, what it does. Additionally, in case of `bind(Instance).to(Class)`, where do you get the instance from? Because we are talking about `configure` in the `AbstractBinder`, right? – BairDev Dec 10 '20 at 12:29
13

The selected answer dates from a while back. It is not practical to declare every binding in a custom HK2 binder. I'm using Tomcat and I just had to add one dependency. Even though it was designed for Glassfish it fits perfectly into other containers.

   <dependency>
        <groupId>org.glassfish.jersey.containers.glassfish</groupId>
        <artifactId>jersey-gf-cdi</artifactId>
        <version>${jersey.version}</version>
    </dependency>

Make sure your container is properly configured too (see the documentation).

Olivier Tonglet
  • 3,312
  • 24
  • 40
  • The last line (Make sure your container is properly configured too) is a bit vague. Any help here? What annotations do we use where? – markthegrea Jun 27 '16 at 20:42
  • We were using Weld for dependency injection which required some special config to work with Tomcat (our application "container"). If you are using Spring it works out of the box. – Olivier Tonglet Jul 26 '16 at 09:53
5

Late but I hope this helps someone.

I have my JAX RS defined like this:

@Path("/examplepath")
@RequestScoped //this make the diference
public class ExampleResource {

Then, in my code finally I can inject:

@Inject
SomeManagedBean bean;

In my case, the SomeManagedBean is an ApplicationScoped bean.

Hope this helps to anyone.

gjijon
  • 51
  • 1
  • 3
3

Oracle recommends to add the @Path annotation to all types to be injected when combining JAX-RS with CDI: http://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm Though this is far from perfect (e.g. you will get warning from Jersey on startup), I decided to take this route, which saves me from maintaining all supported types within a binder.

Example:

@Singleton
@Path("singleton-configuration-service")
public class ConfigurationService {
  .. 
}

@Path("my-path")
class MyProvider {
  @Inject ConfigurationService _configuration;

  @GET
  public Object get() {..}
}
Benjamin Mesing
  • 4,075
  • 1
  • 18
  • 22
  • 1
    Link is dead, should point to [here](http://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm) – Hank Aug 11 '15 at 14:06
0

If you prefer to use Guice and you don't want to declare all the bindings, you can also try this adapter:

guice-bridge-jit-injector

Choi
  • 39
  • 2
0

For me it works without the AbstractBinder if I include the following dependencies in my web application (running on Tomcat 8.5, Jersey 2.27):

<dependency>
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.1</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>${jersey-version}</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.ext.cdi</groupId>
    <artifactId>jersey-cdi1x</artifactId>
    <version>${jersey-version}</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.inject</groupId>
    <artifactId>jersey-hk2</artifactId>
    <version>${jersey-version}</version>
</dependency>

It works with CDI 1.2 / CDI 2.0 for me (using Weld 2 / 3 respectively).

jansohn
  • 2,246
  • 2
  • 28
  • 40
0

Dependency required for jersey restful service and Tomcat is the server. where ${jersey.version} is 2.29.1

    <dependency>
        <groupId>javax.enterprise</groupId>
        <artifactId>cdi-api</artifactId>
        <version>2.0.SP1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-server</artifactId>
        <version>${jersey.version}</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet</artifactId>
        <version>${jersey.version}</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.inject</groupId>
        <artifactId>jersey-hk2</artifactId>
        <version>${jersey.version}</version>
    </dependency>

The basic code will be as follows:

@RequestScoped
@Path("test")
public class RESTEndpoint {

   @GET
   public String getMessage() {
alokj
  • 759
  • 1
  • 6
  • 13