Custom Scopes and Providers

766 views
Skip to first unread message

Ryan Asleson

unread,
Feb 12, 2014, 9:43:36 AM2/12/14
to google...@googlegroups.com
Hello,

I'm using Guice 4.0 beta 2.  I have some experience with Guice 3.0 on a previous project, but no experience with custom scopes.

In my new application I have a Provider for an ApplicationConfiguration object.  The Provider selects an ApplicationConfiguration object from the servlet context, based on information in the HttpServletRequest which is injected into the Provider.  

The binding looks like this:

bind(ApplicationConfiguration.class).toProvider(ApplicationConfigurationProvider.class).in(RequestScoped.class);

So far so good.  The Provider correctly provides the ApplicationConfiguration for each request.

Note that the ApplicationConfiguration is injected into other Provider objects.

Now, I need to do some processing outside of a servlet request that still depends on the ApplicationConfiguration object.  Specifically, I need to do some processing that is started from a servlet context listener.  The processing occurs after Guice has been initialized.  I have access to the correct ApplicationConfiguration instance and don't need the Provider to provide one.

I created a custom scope following the instructions on the "Custom Scopes" wiki page, and registered the scope and its annotation with Guice.  

Since I already have the correct ApplicationConfiguration instance and don't need a Provider to provide one, I seed the scope with the ApplicationConfiguration instance.  I then try to do something with the Injector:

SimpleScope scope = ....;

 scope.enter();
 try
 {
   scope.seed(Key.get(ApplicationConfiguration.class), appConfig);

   //ask the injector for an instance of something....
 }
 finally
 {
    scope.exit();
 }
 
It's failing when I ask the injector for something that depends on an ApplicationConfiguration.  Here's part of the error:

1) 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.

Now, I think it might be failing on another Provider that is also request scoped, and this Provider has an ApplicationConfiguration injected into it, but I'm not sure:

bind(EntityManager.class).toProvider(EntityManagerProvider.class).in(RequestScoped.class);

(The EntityManager is picked based on the ApplicationConfiguration)

So, I thought I could scope another Provider that is not in request scope, but Guice wouldn't let me do that.

Does anybody have any thoughts on what is going wrong?  Do I need more or additional bindings?  Am I not seeding the custom scope correctly?

Thank you!!!

-Ryan


Tavian Barnes

unread,
Feb 12, 2014, 11:00:35 AM2/12/14
to google...@googlegroups.com
The Guice servlet extension seems to be handling @RequestScoped instead of your custom scope.  You can create a different scope annotation and use bindScope() to have SimpleScope handle it.

Ryan Asleson

unread,
Feb 12, 2014, 12:37:42 PM2/12/14
to google...@googlegroups.com

I got a little further.  Now within my custom scope, I'm seeding both the ApplicationConfiguration and the EntityManager:

      scope.enter();
      try
      {
        scope.seed(Key.get(ApplicationConfiguration.class), appConfig);
        
        EntityManagerFactory emf = injector.getInstance(Key.get(EntityManagerFactory.class, Names.named(appConfig.getAppCode())));
        EntityManager em = emf.createEntityManager();
        
        scope.seed(Key.get(EntityManager.class), em);

        //UserDao is bound with no scope, but to a provider....
       UserDao userDao = injector.getInstance(UserDao.class);
      }
      finally
      {
        scope.exit();
      }

See the above.  UserDao is bound to a Provider that depends on the Injector and ApplicationConfiguration being injected into it.  I'm getting into UserDaoProvider, and the injector has been set, but the ApplicationConfiguration on which it depends is null.

Since I seeded an implementation of ApplicationConfiguration, shouldn't that be injected into the UserDaoProvider?




 

Ryan Asleson

unread,
Feb 12, 2014, 1:05:24 PM2/12/14
to google...@googlegroups.com

Maybe I'm going about this wrong and shouldn't be using custom scopes in the first place. 

Here is what I really need to do:

When handling user web requests, the ApplicationConfiguration needs to be looked up (it's stored in servlet context; there are many there, and the correct one needs to be picked) and injected.  That's why I use this binding:

bind(ApplicationConfiguration.class).toProvider(ApplicationConfigurationProvider.class).in(RequestScoped.class);

However, in a non user-request environment, I already have the instance of the ApplicationConfiguration that needs to be used.  How do I tell the injector to use that instance, instead of using the ApplicationConfigurationProvider?  I was thinking that I could tell Guice to use one Provider for one scope and something else for another scope, but that's apparently not how scopes work.

How can I accomplish this?

Tim Boudreau

unread,
Feb 18, 2014, 6:26:21 AM2/18/14
to google...@googlegroups.com
I had somewhat similar problems and wrote a library to solve it:  https://github.com/timboudreau/scopes (you can pick it up from the Maven repo here: http://timboudreau.com/builds/ )

Basically you should just be able to do something like

ReentrantScope myOtherScope = new ReentrantScope();
// in your binder
myOtherScope.bindTypes(binder(), ApplicationConfiguration.class);

then when you have your instance:

try (QuietAutoCloseable ac = myOtherScope.enter( theConfiguration )) {
     // do what you need to 
}

The pattern I wind up using this for (not a servlet environment) is to take a scope's contents, and sort of freeze that, throw some work on thread pool.  To make that easy, there is  ReentrantScope.wrap(ExecutorService), which gets you an ExecutorService which will freeze the scope's contents at the time a Runnable/Callable is submitted, and then reenter the scope with the same contents before the submitted code is invoked.

All that being said, if you have the objects that would be injected to run whatever needs the ApplicationConfiguration, it may be simplest to just call what you need to call - in other words, be sure that injection is buying you more than the complexity you seem to be adding to do it with injection.

-Tim

Reply all
Reply to author
Forward
0 new messages