Binding Names to Multiple Properties File

1,207 views
Skip to first unread message

Evan Ruff

unread,
Jan 29, 2011, 12:57:18 PM1/29/11
to google...@googlegroups.com
Guy guys,

Guice newbie here. I'm trying to migrate my DAO Factory Pattern to a Guice provider framework. The issue I'm having is figuring out how to create data sources that point to various databases, all in the same module. For a single database, I'm golden, everything is working great; however, I'm not sure how to proceed. 

I've got my ProductionDatabaseModule (AbstractModule) that does that does:

    protected void configure() {
        Names.bindProperties(binder(), loadProperties("database_one.properties" ));
        bind(DataSource.class).annotatedWith( @DBOne ).toProvider(MySQLDataSourceProvider.class).in(Scopes.SINGLETON);
    }

What I'd like to do, is follow it up with something like this:

Names.bindProperties(binder(), loadProperties("database_two.properties" ));
bind(DataSource.class).annotatedWith( @DBTwo ).toProvider(MySQLDataSourceProvider.class).in(Scopes.SINGLETON);

But I don't understand how to make my second binding hook up to the second properties file without overwriting the first. Any recommendation on how to handle this?

Thanks for helping out the new guy!

E

Fred Faber

unread,
Jan 29, 2011, 1:11:23 PM1/29/11
to google...@googlegroups.com
Off the cuff, you could probably do this with PrivateModules:

public class DatabaseOneModule extends PrivateModule {
  @Override protected void configure() {
     Names.bindProperties(binder(), loadProperties("database_one.properties" ));
   Key<DataSource> dataSourceKey = Key.get(Datasource.class, DbOne.class);
   bind(dataSourceKey)
        .toProvider(MySQLDataSourceProvider.class)
         .in(Singleton.class); // prefer Singleton.class over Scopes.SINGLETON
   expose(dataSourceKey);
  }
}

...and you'd have something almost identical for DatabaseTwoModule (in fact, you could probably parameterize a generic module to accept the file name and the annotation you want, but that's not the most salient point here).

Then you install DatabaseOneModule and DatabaseTwoModule and I suspect you will get what you're looking for.

-Fred

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

Evan Ruff

unread,
Jan 29, 2011, 7:01:55 PM1/29/11
to google...@googlegroups.com
Hey Fred,

Thanks for the suggestion. I'm not exactly sure if I'm missing something here, but is it possible to do something like:

    protected void configure() {
        Names.bindProperties(binder(), loadProperties("database_one.properties" ));
        bind(DataSource.class).annotatedWith( @DBOne ).toProvider(MySQLDataSourceProvider.class).in(Scopes.SINGLETON);

        Names.bindProperties(binder(), loadProperties("database_two.properties" ));
        bind(DataSource.class).annotatedWith( @DBTwo ).toProvider(MySQLDataSourceProvider.class).in(Scopes.SINGLETON);
    }

In a single module? Are modules explicitly supposed to have a 1:1 relationship between the interface/class bindings, or can you double stuff up based on the annotation as I have done above? Is it common or prefered to have a single Module for the entire configuration, or should I have a bunch of Singleton injectors with a number of modules, depending on things like property files, etc?

And now, I've really confused myself... damn... thanks for the guidance!

E

Fred Faber

unread,
Jan 29, 2011, 7:32:21 PM1/29/11
to google...@googlegroups.com
Evan,

You'r right, I should qualify:  I was suspecting that you would have identically  named properties in each of database_{one,two}.properties.  If you did, then there would be a binding clash because Guice would try to bind different values to the same property name (also here I'm presuming their values would be different).

e.g., Imagine you had:

in database_one.properties:
  db.name = "database_one"

in database_two.properties:
  db.name = "database_two"

Guice wouldn't know what to give you when you asked for "@Named("db.name") String dbName," and that's why it doesn't allow you to have duplicate bindings as such.

* * *

Typically, you do want one something roughly equivalent to one Module per package.  This convention goes a long way in:
1) allowing you to keep your implementation classes package private, or at the least, their constructors package private for public classes
2) providing a convention that makes it tractable to find where the binding exists for a given class

For (2), this is critical for a large app, lest folks be binding classes in unpredictable locations.

So you might have:
  A
 /  \
B   C             F
     / \           /  \
   D  E        G   H

As a package structure.  Depending on the  nature of the parent-child relationships involved, a typical scenario will look like:

ModuleA
  install module B, C, F

Module C:
  install D, E

Module F:
  install G, H

Then it's typically ModuleA that you will pass to Guice.createInjector().

Now this needs to be take with common sense and context of the app.  Often package structure doesn't reflect logical relationships (shit happens in the course of coding, we all know).  As such, it might not make sense for a parent package's module to install a child package's module.  Especially if two child modules represent different implementations of the same interfaces (e.g., fake file system and real file system).  At this point, ModuleA, or whatever your top-level server module is, will go underneath a package and directly install one if its child modules.


* * *

Getting back to your question - the PrivateModule skirts some of the issues of the Robot Legs problem.  This allows you to install duplicate bindings which remain entirely local to that module.  As such, other modules won't see the bindings that are not exposed through the "expose()" method.  

* * *

You bring up some fundamental questions and this is dense material!  Please continue to follow-up with clarifying questions.

regards
-Fred


E

--

Evan Ruff

unread,
Jan 31, 2011, 5:57:50 PM1/31/11
to google...@googlegroups.com
Hey Fred,

Thanks a lot for helping me get started with Guice. I believe I've gotten my DataSource injection stuff worked out. As you suggested, I created a PrivateModule that accepted an annotation and a properties filename in the constructor. I then instantiate this PrivateModule for however many separate DB connections I need, using the Singleton scope to facilitate connection pooling in the DB itself. The connections are injected into the various DAO's using the annotation for their respective data sources. I've converted over all my DAO Unit Tests and this seems to be work very well!

After reading some more documentation and getting into the question of deployment and setup in an actual system, I have quickly encountered the "robot legs" scenario that you are describing above. If I used Guice to push as far into my code base as possible, I can see it deteriorating into a really messy web. Most of my applications are web-based running in a container. Is it smart to create a static injector instance in the application container's bootstrap (servlet listener or whatever) and then use that to instantiate the objects throughout the application's base nodes (say, in the servlet itself, or wherever)

Is there a different or better approach to configuring the bindings for the application in a web context? I see a guice-servlet library in the 3.0 RC2 which looks like it uses a Servlet Listener. Wondering how that jibes with GWT?

Thanks for any help!

E

Fred Faber

unread,
Feb 1, 2011, 8:10:39 PM2/1/11
to google...@googlegroups.com
Evan,

Glad to know you're seeing some successes.  Hope that is an ongoing trend.

As far as a web of robot legs, often it might seem worse than it is, given a few mitigating factors:

1) if you're building a web app, then typically you will be able to make use of request scoped values, given that often the object graph with which you want to process a request is dependent on the type of request received.  

As a contrived example, imagine if you had a sharded database of users.  Also imagine that the user for which you were processing a request was easily identified from its URL (such as "www.yourapp.com/process-user?user-id=123").  In this example, you want to determine which database to use for each request, given each user might map to a different physical database.  You could add a Filter to perform the work of mapping user-id => shard, and then you could seed the calculated value into RequestScope.

This means that your robot legs problem is effectively solved by scoping, given you could change your "static" PrivateModule bindings to use a scoped value.  For example, you might now have:
  
public class DatabaseConnectionSupplier {
   private final Provider<String> databaseNameProvider;

  // always be keeping your ctr's package private!
  @Inject DatabaseConnection(
       Named("database_name") Provider<String> databaseNameProvider) {
     this.databaseNamedProvider = databaseNameProvider;
   }

   
   public DatbaseConnection supplyDatabaseConnection() {
         // we know we're in scope now, so we can call .get()
        String databaseName = databaseNameProvider.get();
         .....
    }
}

Of course this would be horrible inefficient, but it illustrates the scoping point, I hope.  Practically, you'd probably have a singleton pool and simply dispatch from Map<String, DatabaseConnection>.

I've added an example on the Guice wiki that shows how to do this with a Filter:


2) MapBinder

MapBinder is powerful!  It boils down to having a Map that can be populated in a plugin-style fashion.  In the context of this thread, it allows you to have multiple object graphs pre-constructed, keyed off a value that you would know at the time you need to decide which leg to use.  

In the example above, a class that needs a DatabaseConnection might itself be injected with:
  Map<String, DatabaseConnection> connectionsByName;

Here, each DatabaseConnection would have the relevant name injected into it upon construction (ala the PrivateModule solution).  The DatabaseClient class could then learn which DatabaseConnection it should use at runtime, such as:

public class DatabaseClient {

  @Inject DatabaseClient(Map<String, DatabaseConnection> cnxsByName) { ...}

  public doSomethingRequiringTheDatabase(Integer userId) {
     String databaseOfUser = shardingService.getShardForUser(userId);
     DatabaseConnection databaseConnection =
           cnxsByName.get(databaseOfUser);
     .....
   }
}

 
The difference here is ultimately a design question:  whether to hide the configuration behind a scope or to have it explicit within blogic is the choice to make.  A good rule of thumb to use is this:
  if a value is required in several packages, or at least across several classes, then scoping is often the best idea.  otherwise, passing a value directly is probably going to be more straightforward, and therefore the better choice.

In other words, use scope judiciously, as, end of the day, it really is a glorified global variable (but not in the pejorative sense), and this can make it confusing to see where a scoped value "comes from."


* * *

To answer your other question, ServletModule goes well with GWT, mainly because the two are somewhat independent.  You'll want to use Google-Gin for your client side injections, as it deals with the issues of GWT that drive us all subtly mad (i.e., those issues requiring the java to be compilable to js).  

On the server side you can easily configure your resource servlets and remote logging servlets with ServletModule, and I absolutely recommend you do that!

The ServletListener, btw, is used only to bootstrap the app within a servlet container.  If you're not running within a container, such as using an "embedded" servlet engine, you can do something programmatic, such as this.  Either way, certainly it is advantageous to use ServletModule.

-Fred



E

--
Reply all
Reply to author
Forward
0 new messages