40

Jersey normally uses HK2 dependency injection, but I would like to use Jersey with Dagger 2. Both Dagger and HK2 implement JSR 330, which I have taken as evidence that this should be possible without too much effort. I found ways to make Jersey work with CDI (e.g. Weld), Spring DI and Guice, but I can't find anything on Dagger.

To provide some context: I'm running a Grizzly–Jersey server in an SE environment, not in an EE container. My Maven project has com.google.dagger:dagger and org.glassfish.jersey.containers:jersey-container-grizzly2-http as dependencies, but not org.glassfish.jersey.inject:jersey-hk2, since I want to replace HK2 with Dagger.

The resource classes look like this:

@Path("/example")
public final class ExampleResource {

    private final Dependency dependency;

    @Inject
    public ExampleResource(final Dependency dependency) {
        this.dependency = Objects.requireNonNull(dependency);
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Example getExample() {
        return this.dependency.giveExample();
    }

}

And the Dagger component could e.g. be defined as follows:

@Component
public interface Application {

    public ExampleResource exampleEndpoint();
    public XyzResource xyzEndpoint();
    // etc.

}

So that the main method would look similar to:

public final class Main {

    public static void main(final String[] args) {
        final Application application = DaggerApplication.create();
        final URI baseUri = UriBuilder.fromUri("http://0.0.0.0/").port(80).build();
        final ResourceConfig resourceConfig = new ResourceConfig();
        // how to initialize `resourceConfig` using `application`?
        final HttpServer httpServer = GrizzlyHttpServerFactory
                .createHttpServer(baseUri, resourceConfig, false);
        try {
            httpServer.start();
        } catch (final IOException ex) {
            ...
        }
    }

}

Running the application immediately results in an exception: IllegalStateException: InjectionManagerFactory not found. It seems that a Dagger implementation of this factory is needed.

My question is: how to integrate Dagger with Jersey?

Rinke
  • 6,095
  • 4
  • 38
  • 55
  • 1
    Did you try https://github.com/johnlcox/dagger-servlet ? – Sasha Shpota Mar 18 '18 at 10:54
  • @OleksandrShpota No, but I’m not using servlets. I’m running Jersey from a main method. No EE. – Rinke Mar 18 '18 at 10:59
  • 1
    @Rinke that's fine. The dagger-servlet project that was linked above should be a good starting point. Inside that project, there's a module named dagger-jersey that contains classes that hook dagger up to Jersey's IOC interfaces. In particular, the DaggerComponentProviderFactory class has most of the logic necessary. – Dogs Mar 22 '18 at 13:59
  • @OleksandrShpota and @Dogs According to [this comment](https://stackoverflow.com/q/31277675/942671#comment67823487_31277675) `dagger-servlet` is only for Dagger 1 and _not_ compatible with Dagger 2... unfortunately. – Rinke Mar 30 '18 at 12:17
  • You could have a look at the answer to https://stackoverflow.com/questions/31277675/is-it-possible-to-use-dagger2-with-jersey-the-same-way-it-is-possible-with-guice – Rob Bygrave Apr 20 '18 at 04:29
  • Side point: If you are looking to make this a docker service then you might consider using http://0.0.0.0 rather than http://localhost for the baseUri (such that the service processes all requests on that port) – Rob Bygrave Apr 20 '18 at 04:31
  • @RobBygrave I had seen that question. Thanks though. It seems that `dagger-servlet` is Dagger 1 only. (See my earlier comment.) As for your side point: good one. I’ll update my question. – Rinke Apr 24 '18 at 11:30

2 Answers2

1

You shouldn't think of it as "how to integrate dagger with jersey". Figure out how to setup jersey, then once you have that figured out, then you can worry about using dagger.

Here's (very roughly) how I would do it.

Create your own implementation of the ResourceConfig class.

@ApplicationPath("/service")
public class MyResourceConfig extends ResourceConfig {

    @Inject
    public MyResourceConfig(
            @Nonnull final ExampleResource exampleResource) {
        this.register(exampleResource);
    }

}

Then create a module that sets up everything you need to create an HttpServer

@Module
public class MyServiceModule {

    @Provides
    @Singleton
    @Named("applicationPort")
    public Integer applicationPort() {
        return 80;
    }

    @Provides
    @Singleton
    @Named("applicationBaseUri")
    public URI baseUri(
            @Named("applicationPort") @Nonnull final Integer applicationPort) {
        return UriBuilder.fromUri("http://0.0.0.0/").port(applicationPort).build();
    };

    @Provides
    @Singleton
    public HttpServer httpServer(
            @Named("applicationBaseUri") @Nonnull final URI applicationBaseUri,
            @Nonnull final MyResourceConfig myResourceConfig) {
        return GrizzlyHttpServerFactory
                .createHttpServer(applicationBaseUri, myResourceConfig, false);
    }

}

Then create your component that exposes the HttpServer. I typically like to make components that expose as little as possible. In this case, all you need to expose is the HttpServer.

@Singleton
@Component(modules = { MyServiceModule.class })
protected interface ServiceComponent {

    HttpServer httpServer();

    @Component.Builder
    interface Builder {

        // Bind any parameters here...

        ServiceComponent build();

    }

}

Then just go ahead and build your component, and start your HttpServer

public static void main(String[] args) {
    final ServiceComponent component = DaggerServiceComponent.builder().build()
    try {
        component.httpServer().start();
    } catch (Exception ex) {
        // handle exception...
    }
}

One more thing to note. I personally do not ever use the @Named("") annotation. I prefer to use a Qualifier. So you create a Qualifier annotation with a unique value. Then you can inject things like

@Provides
@Singleton
@MyUniqueQualifier
public String myUniqueQualifierProviderValue() {
    return "something";
}

Then when injecting it

@Inject
public SomeClass(@MyUniqueQualifier @Nonnull final String myUniqueQualifiedValue) 

If you use the @Named annotation you don't get compile time checks for conflicts or missing values. You would find out at run time that a value was not injected or then name conflicts with something else. It gets messy quick.

Callan
  • 475
  • 3
  • 11
  • 1
    Thanks for your answer Callan! However, the point of question was how to use Dagger instead of HK2, so that you don’t have to configure a `ResourceConfig` class manually. – Rinke Oct 05 '21 at 06:59
  • @Rinke dagger is just dependency injection. It doesn't know anything about the ResourceConfig. You need to both provide the objects you want to inject, and also have a place where they get inject. And based on how jersey is setup in this case, it makes the most sense to inject them into your own ResourceConfig class. What is the problem you're trying to work around? Is there a technical reason you can't create your own ResourceConfig class? – Callan Oct 05 '21 at 18:12
  • You may be missing the point of my question. Have you worked with the Jersey-HK2 combination? It’s very convenient. My question is about how to get the same experience with Dagger instead of HK2. I want Jersey to use Dagger to instantiate the resource classes. – Rinke Oct 06 '21 at 08:47
  • Dagger and HK2 are two different frameworks. The question I answered was how would one go about setting up Jersey with Dagger dependency injection. But it sounds like what you're asking is, how can I make dagger work like HK2? That I couldn't tell you, but it's a different framework with different usage, so it's probably not going to do the exact same things for you. – Callan Oct 08 '21 at 00:19
  • Jersey can use various DI frameworks to instantiate the resource classes, like HK2, Spring DI, Weld and others. The question is how to configure Jersey so that it uses Dagger. I thank you for taking the effort to try and answer my question. – Rinke Oct 08 '21 at 05:51
0

You need to implement an InjectionManagerFactory that will return an InjectionManager delegating to Dagger and have it registered as a service by putting an entry in META-INF/services, similar to the hk2 one here: https://github.com/jersey/jersey/blob/master/inject/hk2/src/main/resources/META-INF/services/org.glassfish.jersey.internal.inject.InjectionManagerFactory but referencing your own implementation.

  • Unfortunately I don't really understand your answer. I already understood that I need a `DaggerInjectionManagerFactory`. However, it's not trivial how to implement the `DaggerInjectionManager` that it should produce. – Rinke Jan 18 '19 at 14:55
  • Can you elaborate on the META-INF entry? I don't know how to translate it to my situation. – Rinke Jan 18 '19 at 15:00
  • Regarding the META-INF i'm not sure what you're asking - look at the link in my answer, yours would look exactly the same, just pointing to your `InjectionManagerFactory`. A simple approach to implementing the actual `InjectionManager` (at least for the `getInstance` methods) could be to create an instance of your `MyComponent` in it's constructor, loop over it's methods putting the results in a map of each methods return class to object returned by the method invocation, then in the `getInstance` method (at least the one taking a class) return that instance from the map. – user7505251 Jan 21 '19 at 12:39
  • This answer is the smarter approach and I recommend to elaborate it more: he is basically suggesting how to configure your how DI mechanism in Jersey, by following this approach you can remove the dependency to org.glassfish.jersey.inject:jersey-hk2 as you'll replace it with your custom implementation where you can delegate to a Dagger Component, to a Spring Application or a Guice Injector. You have to implement your version of InjectionManagerFactory and returns your version of InjectionManager: this approach seems to need a lot of boilerplate code but it's worth to try it IMO – Cristiano Costantini Aug 01 '22 at 22:29