Bootstrapping Guice ...

258 views
Skip to first unread message

Jeff

unread,
Oct 27, 2011, 1:14:59 AM10/27/11
to google...@googlegroups.com
I want to do something like:
@Inject @Named("conf")
Properties config;
 
@Inject @Named("lang")
Properties language;
Where these objects are singletons initialized once at runtime with environment-specific information.
 
I've tried various things and none result in my Provider or @Provides methods getting called.  Here is a trimmed down version of my code using a Provider class:
 
MyModule.java:
public class SeleniumInjectionModule extends AbstractModule {
  @Override
  protected void configure() {
    bind(Properties.class).annotatedWith(Names.named("conf")).toProvider(ConfigProvider.class);
    bind(Properties.class).annotatedWith(Names.named("lang")).toProvider(LanguageProvider.class);
  }
}
ConfigProvider.java:
@Singleton
public class ConfigProvider implements Provider<Properties> {
  private final Properties config = new Properties();
 
  public ConfigProvider() {
    //Load properties file based on 'config' system property value set in Maven profile
    ...
    config.load(...);
    ...
  }
  @Override
  public Properties get() {
    return config;
  }
}
 
LanguageProvider.java:
@Singleton
public class LanguageProvider implements Provider<Properties> {
  private final Properties lang = new Properties();
 
  public LanguageProvider() {
    //Load properties file based on 'language' system property value set in Maven profile
    ...
    lang.load(...);
    ...
  }
  @Override
  public Properties get() {
    return lang;
  }
}
 In my Global Init code:
    Guice.createInjector(new MyModule());
 
What am I missing?
 
--
Jeff Vincent
See my LinkedIn profile at:
I ♥ DropBox !! 
 

Stuart McCulloch

unread,
Oct 27, 2011, 7:40:23 AM10/27/11
to google...@googlegroups.com
Where's the class containing the injected config/language fields mentioned in the start of this email?

Assuming that class is called Foo then you need to use injector.getInstance(Foo.class) - or injector.injectMembers(myFoo) if you already have an instance of Foo - to start the injection process and inject those fields. Note that you don't need to do this for everything, just the class at the beginning of the injection graph to kick things off.

--
Jeff Vincent
See my LinkedIn profile at:
I ♥ DropBox !! 
 

--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To post to this group, send email to google...@googlegroups.com.
To unsubscribe from this group, send email to google-guice...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-guice?hl=en.

Jeff

unread,
Oct 27, 2011, 4:12:01 PM10/27/11
to google...@googlegroups.com
The classes containing the injected fields are TestNG test classes.  I am creating the Injector using the ServiceLoader mechanism within a class that implements org.testng.ISuiteListener.  TestNG automatically picks up and runs the onStart() method before doing anything else and I call createInjector() there.
 
Though, I think I just realized my problem (besides trying to learn/use Guice from within another framework I'm also just learning).
 
The key bit I had not grasped previously was that for Guice/@Inject to work, object instantiation must be done directly by Guice (Injector.getInstance()), at least for the root node of the object graph (I think).
 
Since TestNG instantiates classes without Guice by default, it was just creating normal instances and the @Inject annotations weren't getting processed.
 
TestNG does support Guice via @Test(guiceModule = MyModule.class) annotation, but I wanted to avoid the need to put this on EVERY test class and thought I could insert my Modules/Providers and enable my custom bindings to work globally.
 
I'm still playing with my code, but I feel like I'm on the right track.  Can anyone confirm or help me understand if there is a different way?
 
Thanks!!

Mingfai

unread,
Oct 28, 2011, 3:21:30 AM10/28/11
to google...@googlegroups.com
hi,

your question is a bit long and i'm not sure if i fully understand the problem, but let me share some thoughts:

  1. I wouldn't create 2 providers just for the two properties. Technically you can but it makes the design more complicated.

    you could either just load the properties in the configure() method that i suppose is the default method. The code is something like:
    Properties prop1 = /** load it */
    bind(Properties.class).annotatedWith(Names.named("conf")).toInstance(prop1);

    Another way I prefer is to use provider annotation in the Module class, the code is something like:
    public class PropertiesModule extends AbstractModule{
      protected void configure() {} //you could just leave it blank
      @Provides @Named("conf") Properties getConf(@Named("base") File basePath){ // basePath is just an example for you. you could just use no parameter
      return null;
     }
    }

  2. Guice is an DI framework so it is designed to create and manage the instance for you, but technically, there are ways that allow you to create instance elsewhere and pass in Guice. e.g. you could add member variables to your Module class, e.g.

    public class PropertiesModule extends AbstractModule{
     final Properties lang; //final is optional
     final Properties conf;
     public PropertiesModule(Properties lang, Properties conf){
       this.lang=lang; this.conf = conf;
     }
    protected void configure(){
      bind(Properties.class).annotatedWith(Names.named("conf")).toInstance(conf); //will throw NPE if conf is null
      //TODO: do the same for lang
     }
    }

  3. i don't know TestNG but use JUnit intensively. The concept should be similar. I wouldn't use ServiceLocator to load all module in unit test. The purpose of unit test is to isolate units for testing and the beauty of Guice is that it allows us to programmatically prepare the minimum number of modules for testing. Another practical reason is it probably will take too long to load all modules for running every unit test case.

    an example of junit test class that load a single module is as follows:

    public class PropertiesTest{
      static Injector g = Guice.createInjector( new PropertiesModule() );

    @Test public void testSomething(){
        Properties conf = g.getInstance( Key.get( Properties.class, Names.named("conf"))
      assertNotNull(conf);
     }
    }
disclaimer: all code above are written in email client but not IDE and they may not work. they are just for illustrating the concept.

just my 2 cents. hope they help.

regards,
mingfai

Stuart McCulloch

unread,
Oct 28, 2011, 8:32:00 AM10/28/11
to google...@googlegroups.com
On 27 Oct 2011, at 21:12, Jeff wrote:

The classes containing the injected fields are TestNG test classes.  I am creating the Injector using the ServiceLoader mechanism within a class that implements org.testng.ISuiteListener.  TestNG automatically picks up and runs the onStart() method before doing anything else and I call createInjector() there.
 
Though, I think I just realized my problem (besides trying to learn/use Guice from within another framework I'm also just learning).
 
The key bit I had not grasped previously was that for Guice/@Inject to work, object instantiation must be done directly by Guice (Injector.getInstance()), at least for the root node of the object graph (I think).
 
Since TestNG instantiates classes without Guice by default, it was just creating normal instances and the @Inject annotations weren't getting processed.
 
TestNG does support Guice via @Test(guiceModule = MyModule.class) annotation, but I wanted to avoid the need to put this on EVERY test class and thought I could insert my Modules/Providers and enable my custom bindings to work globally.
 
I'm still playing with my code, but I feel like I'm on the right track.  Can anyone confirm or help me understand if there is a different way? 

Yes, either the root of the object graph needs to be instantiated by Guice or you need to use injectMembers(...) to inject an existing instance (in which case you can't use constructor injection in that particular class). Of course one benefit of putting @Test(guiceModule=...) on each test class (or @Guice in the latest version: http://testng.org/doc/documentation-main.html#guice-dependency-injection) is that it documents that you're using dependency injection for that test, and with modern IDEs it's not too hard to add this automatically or use a template. Also note the latest release of TestNG supports module factories so you could use the same factory across all your tests: http://testng.org/javadocs/org/testng/IModuleFactory.html

Jeff

unread,
Oct 28, 2011, 1:43:54 PM10/28/11
to google...@googlegroups.com
Thanks for the great info and helping me work things out.  The fog is clearing.
 
Since my language provider depends on the environment, is it kosher to do something like:
  @Provides @Named("ENV") @Singleton 
  public Properties EnvironmentProvider() {
    //setup env
    return env;
  }
   
  @Provides @Named("LANG") @Singleton @Inject
  public Properties LanguageProvider(@Named("ENV") Properties env) {
    //load language files based on lang setting in env
    return lang;
  }
And if I mark these as @Singleton, am I the one that needs to store and manage the instance or does Guice cache the object returned for lang and env and never call this method directly again?

Stuart McCulloch

unread,
Oct 28, 2011, 1:51:03 PM10/28/11
to google...@googlegroups.com
On 28 Oct 2011, at 18:43, Jeff wrote:

Thanks for the great info and helping me work things out.  The fog is clearing.
 
Since my language provider depends on the environment, is it kosher to do something like:
  @Provides @Named("ENV") @Singleton 
  public Properties EnvironmentProvider() {
    //setup env
    return env;
  }
   
  @Provides @Named("LANG") @Singleton @Inject
^ you shouldn't need @Inject here since every parameter in a @Provides method will always be injected

  public Properties LanguageProvider(@Named("ENV") Properties env) {
    //load language files based on lang setting in env
    return lang;
  }
And if I mark these as @Singleton, am I the one that needs to store and manage the instance or does Guice cache the object returned for lang and env and never call this method directly again?

Guice will apply the scoping around the @Provides method, so you don't need cache the result yourself

Kartik Kumar

unread,
Oct 29, 2011, 4:05:51 AM10/29/11
to google...@googlegroups.com
It depends on what you want. If you want constructor injection, then guiceModule might be a way to go. If method or field injection is fine with you then you can implement TestNG's IObjectFactory.

public class MyObjectFactory implements IObjectFactory {
   
public final Object newInstance(@SuppressWarnings("unchecked") final Constructor constructor, final Object... args)  {
ObjectFactoryImpl impl = new ObjectFactoryImpl();
Object testInstance = impl.newInstance(constructor, args);

Injector injector = Guice.createInjector(this.overrideAbstractMethodToReturnListOfModules());
injector.injectMembers(testInstance);
}

Jeff

unread,
Nov 2, 2011, 6:01:52 PM11/2/11
to google...@googlegroups.com
Is the @Singleton scope based on the instance of the Injector being used?
 
I have two classes that aren't getting injected.   One is a static factory class (ThreadResourceFactory).  The other is a class that the factory instantiates for me (ThreadResource).
 
In my TestNG @BeforeClass method, I call ThreadResourceFactory.get() to retrieve an instance of ThreadResource for the current TestNG test thread.
 
Since I couldn't figure out a way to retrieve the injector previously created by TestNG, I did the following in the ThreadResource constructor:
 
  Guice.createInjector(new MyModule()).injectMembers(this);
 
This works sort of, but because I create another injector, it seems to be managing another set of @Singletons for that new Injector instance. 
 
I'm sure there is a better way to do this, I am just unsure how.  Any suggestions?

Stuart McCulloch

unread,
Nov 2, 2011, 6:49:11 PM11/2/11
to google...@googlegroups.com
On 2 Nov 2011, at 22:01, Jeff wrote:

Is the @Singleton scope based on the instance of the Injector being used?
 
I have two classes that aren't getting injected.   One is a static factory class (ThreadResourceFactory).  The other is a class that the factory instantiates for me (ThreadResource).
 
In my TestNG @BeforeClass method, I call ThreadResourceFactory.get() to retrieve an instance of ThreadResource for the current TestNG test thread.
 
Since I couldn't figure out a way to retrieve the injector previously created by TestNG, I did the following in the ThreadResource constructor:
 
  Guice.createInjector(new MyModule()).injectMembers(this);
 
This works sort of, but because I create another injector, it seems to be managing another set of @Singletons for that new Injector instance. 

Correct, @Singleton's are per-binding, per-injector - if you bind A.class to Singleton scope in two different injectors, you'll get two different instances

I'm sure there is a better way to do this, I am just unsure how.  Any suggestions? 

You could always request static injection for the ThreadResourceFactory class in your module:

    @Override public void configure() {
        requestStaticInjection(ThreadResourceFactory.class);
        // ...
    }

You could then have a setter method in ThreadResourceFactory (marked with @Inject) that accepts Injector - this wouldn't be thread-safe because of the static-ness, but that might not be an issue if you're running tests sequentially. Alternatively you could @Inject the Injector or the ThreadResourceFactory in a base test class that you then extend for all your tests.

Reply all
Reply to author
Forward
0 new messages