David B.
David B.

Reputation: 5880

Jersey-Guice doesn't process bound resources if injector is a child?

I'm using Jersey-Guice to configure a Jersey app, following this template. Everything works fine if the Injector returned by the GuiceServletContextListener.getInjector() method is created by Guice.createInjector(). If that injector is instead the child of another injector, then the bound resources (e.g., MyResource in the code below) are never added to the Jersey ResourceConfig and Jersey crashes with a complaint about missing root resources. I don't think the bound resources are even scanned, because the usual "INFO: Registering my.example.MyResource as a root resource class" doesn't appear in the log.

Any ideas why this might be happening? Both versions are show below.

As an additional question: I'm trying to use the child injector because I want to configure my application data service object in my Main() class. More than just the Jersey resources need access to it. I still need it injected into the Jersey resources.

If there's a better way to share the application singleton between the application Injector and servlet injector (better than my current approach of the servlet injector being a child of the application injector), please let me know.

This version works.

public class MyConfig extends GuiceServletContextListener {
    @Override
    protected Injector getInjector() {
        return Guice.createInjector(new ServletModule() {
            @Override
            protected void configureServlets() {
                bind(MyResource.class);
                serve("*").with(GuiceContainer.class);
            }
        });
    }
}

The following code does not work though.

 public class MyConfig extends GuiceServletContextListener {
     final Injector parentInjector;

     public MyConfig(Injector injector) {
         this.parentInjector = injector;
     }

     @Override
     protected Injector getInjector() {
         return parentInjector.getChildInjector(new ServletModule() {
             @Override
             protected void configureServlets() {
                 bind(MyResource.class);
                 serve("*").with(GuiceContainer.class);
             }
         });
     }
 }

Upvotes: 3

Views: 1407

Answers (1)

David B.
David B.

Reputation: 5880

I figured it out after some fun with the debugger.

The resources are discovered by iterating over the bindings of the injector, checking for those that are resources or providers. The injector used is injected into the GuiceContainer via a constructor like this: public GuiceContainer(@Inject injector). With no explicit binding for GuiceContainer.class specified in the child injector, the parent (i.e., root) injector is used to create the instance (just-in-time binding, I guess) and consequently the parent (not the child) injector is injected into to the GuiceContainer instance.

The fix is simple:

Explicitly bind GuiceContainer.class in the child injector. The following code works

 public class MyConfig extends GuiceServletContextListener {
     final Injector parentInjector;

     public MyConfig(Injector injector) {
         this.parentInjector = injector;
     }

     @Override
     protected Injector getInjector() {
         return parentInjector.getChildInjector(new ServletModule() {
             @Override
             protected void configureServlets() {
                 /* Explicitly bind GuiceContainer so that
                  * the child, not root, injector is injected 
                  * into its constructor. */
                 bind(GuiceContainer.class);
                 bind(MyResource.class);
                 serve("*").with(GuiceContainer.class);
             }
         });
     }
 }

Upvotes: 5

Related Questions