3

The Guice guidance on side-effects in modules is basically "don't do it." Same with doing I/O in Providers, which I assume includes modules. They then give a vacant example of using a Service interface that starts their services in the proper order.

Injector injector = Guice.createInjector(
    new DatabaseModule(),
    new WebserverModule(),
    ...
);

Service databaseConnectionPool = injector.getInstance(
    Key.get(Service.class, DatabaseService.class));
databaseConnectionPool.start();
addShutdownHook(databaseConnectionPool);

Service webserver = injector.getInstance(
    Key.get(Service.class, WebserverService.class));
webserver.start();
addShutdownHook(webserver);

But in the real world, there the webserver transitively depends on the database. What's more, the only way to get a DataSource is to connect to the database. And in between the webserver and the database are several layers of abstraction, all wanting their dependencies injected AND having their services start.

I've considered two different options to handle this situation. First is to go with guice's services, but inject Provider<DataSource>s or equivalents. But this would leak guice all over my code because everything now uses Provider<LayerBelow> instead of the actual LayerBelow

The second approach I tried was to chain creating injectors where I build a child injector per layer, start the layer, then use that to create a child injector for the next. It also seems kludgy. Like this:

// I/O in the static method call; returns a module with
// the environment-based objects
EnvironmentModule environment = EnvironmentModule.configureEnvironment(args);

Injector injector = Guice.createInjector(
     environment,
     // the following connects to the database, and returns a module
     // providing the DataSource
     SQLModule.connectToDatabase());

injector = injector.createChildInjector(new BusinessLayerModule());
startTimers(injector);

injector = injector.createChildInjector(
    new ServicesModule(),
    new WebServerModule());

startServer(injector);

Is there a better way to do I/O and side-effects during startup? I'm using Guice 4

Related questions:

Community
  • 1
  • 1
Michael Deardeuff
  • 10,386
  • 5
  • 51
  • 74

2 Answers2

0

My approach is similar to your second approach, except for the child injector.

At first, a module SQLModule dedicated to initialization is created :

Injector initInjector = Guice.createInjector(Stage.PRODUCTION, new SQLModule());
SQLService service = initInjector.getInstance(SQLService.class);
Properties initProps = service.doInit(); // for example

And then the main module is created :

Injector mainInjector = Guice.createInjector(Stage.PRODUCTION, 
                 new SQLModule(), 
                 new MainModule(initProps));
// main program here using the mainInjector
Minh-Triet LÊ
  • 1,374
  • 9
  • 17
0

For example I had two Modules, main to whole application "fat" injected quite-early, and BasicConfigurationService injected very-very-early ;) , Basic works without SQL connection etc. Goal is to resolve SQL connection and most general path to application data ONLY.

Module definitions are set in this way, when main activates, the basic is already active/injected .

Sorry if my English isn't too clear.

Jacek Cz
  • 1,872
  • 1
  • 15
  • 22