3

I have a working Wicket [v6] application with Guice [v3] - I have used dependency injection for repository operations right now and I want to expend it into using services that are session scoped (one per user's session). I have read through official documentation, various blog posts and questions here, but I am not sure if I am using the correct approach.

I have two questions: 1. Do I use the correct way? 2. Do I need anything special to run TestNG tests on classes that rely on SessionScoped injections?

My setup: web.xml:

<filter>
    <filter-name>guiceFilter</filter-name>
    <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>    
<filter-mapping>
    <filter-name>guiceFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
    <listener-class>com.xxx.CustomServletConfig</listener-class>

MyApplication init:

@Override
protected void init()
{
    super.init();
    getResourceSettings().setResourcePollFrequency(null);
    getMarkupSettings().setStripWicketTags(true);
    getDebugSettings().setDevelopmentUtilitiesEnabled(true);
    GuiceComponentInjector injector = new GuiceComponentInjector(this, new WebModule(), new GuiceModule());;
}

CustomServletConfig:

public class CustomServletConfig  extends GuiceServletContextListener {

    @Override
    protected Injector getInjector() {
        return Guice.createInjector(new GuiceModule(), new WebModule());
    }

WebModule:

public static class WebModule extends ServletModule {

    @Override
    protected void configureServlets() {
        bind(WebApplication.class).toProvider(WicketGuiceAppProvider.class).asEagerSingleton();  

        bind(IUserService.class).to(UserService.class).in(ServletScopes.SESSION);

        Map<String, String> params = new HashMap<String, String>();    
        params.put(WicketFilter.FILTER_MAPPING_PARAM, "/*");  

        filter("/*").through(WicketGuiceFilter.class, params);  
    }
}

In an example page I have:

@Inject
IUserService userService

...

userService.doSomething

At userService.doSomething during unit test I am getting Guice OutOfScopeException, pointing to my bindings in ServletModule: Error in custom provider, com.google.inject.OutOfScopeException?: Cannot access scoped object. Either we are not currently inside an HTTP Servlet request, or you may have forgotten to apply com.google.inject.servlet.GuiceFilter? as a servlet filter for this request.

Is my configuration ok and I need to run unit tests differently (I am simply launching my application with WicketTester), or is my design faulty?

aioobe
  • 413,195
  • 112
  • 811
  • 826
Paul
  • 389
  • 3
  • 11

1 Answers1

3

This is very common fault.

All entities in ServletScopes or RequestScopes should be passed in as Providers.

So your code should be:

@Inject
Provider<IUserService> userServiceProvider

public IUserService getUserService() {
  userServiceProvider.get(); 
}

Why so?! Everything fine as long as you use it in Stage.DEVELOPMENT and the parent class is not created eagerly. If you bind you parent class as asEagerSingleton or switch to Stage.PRODUCTION your classes start to be created eagerly at startup time. Otherwise they are created in lazy way only when they are accessed (very likely during first request).

And there your problem comes to scene. Your WebApplication is initialized eagerly at startup time. Then guice tries to inject all child dependencies and found IUserService which is field injection in SessionScope. The problem is you are currently not inside GuiceFilter and there is no request, so guice cannot determine current session or create new one. So these scopes cannot be reached. You are currently in your ContextListener and your application is instantiated eagerly. Everything could be fine if you would use just Singleton instead of asEagerSingleton because of lazy loading.

Anyway, passing Session and Request scoped objects as Providers is the best practice. You can learn more about Providers here and Scopes here (there is also nice table with eager vs. lazy loading comparsion)

Milan Baran
  • 4,133
  • 2
  • 32
  • 49