Squarespace / jersey2-guice

Jersey 2.0 w/ Guice

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Calling modules() in JerseyGuiceServletContextListener constructor is too early

stickfigure opened this issue · comments

I just noticed that the constructor for JerseyGuiceServletContextListener itself calls modules(). This presents a problem because it is impossible to pass information through to the module constructors. For example, I have a Guice module that keeps track of the command line program arguments. The arguments themselves need to be passed into the module (this is simplified):

@RequiredArgsConstructor
public class ArgsModule extends AbstractModule {
    private final String[] args;

    @Override
    protected void configure() {
        bind(Args.class).toInstance(new Args(args));
    }
}

The idea is that I would pass in the args to MyJerseyGuiceServletContextListener, and MyJerseyGuiceServletContextListener would create the ArgsModule. However, because JerseyGuiceServletContextListener calls modules() right away, the args field in MyJerseyGuiceServletContextListener hasn't been initialized yet.

Is it possible to create the Injector slightly later, perhaps in contextInitialized()?

contextInitialized gets called too late to plug Guice into HK2. You have a couple of options.

  1. Don't use JerseyGuiceServletContextListener. You say you have command line arguments. This implies you have a main() method?! You can do something like...
public static void main(String[] args) {
  List<Module> modules = new ArrayList<>();
  // ...

  ServiceLocator locator = BootstrapUtils.newServiceLocator();
  Injector injector = BootstrapUtils.newInjector(locator, stage, modules);
  BootstrapUtils.install(locator);

  // ...
}
  1. Of if you want to use it.
public static void main(String[] args) {
   MyListener.ARGUMENTS.set(args);

   // ...
}

class MyListener extends JerseyGuiceServletContextListener {

  public static final AtomicReference<String[]> ARGUMENTS = new AtomicReference<>();

  @Override
  public List<Module> modules() {
    String[] arguments = ARGUMENTS.getAndSet(null);
    // ...
  }
}

Sorry that it's taken me so long to get back to this - I had to run forward with Jersey1 to meet a deadline, now I'd like to get Jersey2 sorted.

This code...

    public static void main(String[] args) throws Exception {
        ServiceLocator locator = BootstrapUtils.newServiceLocator();
        Injector injector = BootstrapUtils.newInjector(locator, Arrays.asList(new AppModule()));

        BootstrapUtils.install(locator);
    }

...produces this exception:

Exception in thread "main" com.google.inject.CreationException: Unable to create injector, see the following errors:

1) No scope is bound to com.google.inject.servlet.RequestScoped.
  at com.squarespace.jersey2.guice.InternalJerseyModule.configure(InternalJerseyModule.java:63) (via modules: com.squarespace.jersey2.guice.BootstrapModule -> com.squarespace.jersey2.guice.InternalJerseyModule)

2) No scope is bound to com.google.inject.servlet.RequestScoped.
  at com.squarespace.jersey2.guice.InternalJerseyModule.configure(InternalJerseyModule.java:67) (via modules: com.squarespace.jersey2.guice.BootstrapModule -> com.squarespace.jersey2.guice.InternalJerseyModule)

3) No scope is bound to com.google.inject.servlet.RequestScoped.
  at com.squarespace.jersey2.guice.InternalJerseyModule.configure(InternalJerseyModule.java:71) (via modules: com.squarespace.jersey2.guice.BootstrapModule -> com.squarespace.jersey2.guice.InternalJerseyModule)

4) No scope is bound to com.google.inject.servlet.RequestScoped.
  at com.squarespace.jersey2.guice.InternalJerseyModule.configure(InternalJerseyModule.java:75) (via modules: com.squarespace.jersey2.guice.BootstrapModule -> com.squarespace.jersey2.guice.InternalJerseyModule)

4 errors
    at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:448)
    at com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:155)
    at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:107)
    at com.google.inject.Guice.createInjector(Guice.java:96)
    at com.google.inject.Guice.createInjector(Guice.java:73)
    at com.squarespace.jersey2.guice.BootstrapUtils.createInjector(BootstrapUtils.java:270)
    at com.squarespace.jersey2.guice.BootstrapUtils.newInjector(BootstrapUtils.java:190)
    at com.squarespace.jersey2.guice.BootstrapUtils.newInjector(BootstrapUtils.java:171)
    at App2.main(App2.java:11)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

Is there some additional setup missing?

BTW, thanks for working on this project, it is sorely needed!

This can be fixed by installing at least one ServletModule, which registers the request scope. Perhaps the JerseyModule should extend ServletModule instead?