Questions about services that require configuration and validation

106 views
Skip to first unread message

Stéphane Claret

unread,
Aug 22, 2014, 6:32:07 AM8/22/14
to google...@googlegroups.com
Hello,
I am a beginner with Guice, I am not sure how I should configure my services. Unfortunately, most examples found on the net are trivial and solved with a few @Named properties without any kind of validation other than Null and type-safety.

I would need to initialize a service class that requires some non-trivial configuration. During initialization, it needs to scan a given directory on disk for config files, read these files, validate their content and only then it can build and return a working service object.
So basically, creating the service requires some disk IO, configuration validation and has possible side effects (=> throws exception) that should be handled and properly reported. In a non guice app I would typically do such work on application loading and terminate the process in case of problem.

I thought I could do the big initialization job in my module, create the service instance and bind it to some interface so I could inject a ready to use object later during the app lifecycle. However I have learnt in the documentation that this approach is discouraged :

So according to the example in the above doc, a possible way would be to configure the module, gets an instance from the injector and call some init() method on it. Ok it lets me call my init method manually, let me handle exceptions, but... It doesn't seem very natural to me because :

1) The service is constructed and left in a non-usable state until some method is called on it, I can live with that but only if I really have too.
2) I don't think a service should know how to extract its own configuration from disk. I would rather create an another class that builds it and returns an instance. 

My other idea would be to use a (Throwing?)Provider that can create my service instance. But even so it seems doubtful because

1) The provider will create the service once when get() is called, and I guess I will have to cache it somewhere since it's a relatively heavy task. My provider then looks like a lazy singleton.
2) I am not sure how I can control where the first call to get() will occur, so I don't have a clear place to catch initialization errors

Another way I can think of would be to inject a service provider rather than bind the service class to the provider in the module, manually retrieve the Provider instance from the injector right after its creation and call the first get() myself, surrounding everything with a try-catch but it seems a bit hacky and forces me to inject a provider rather than a service just because I need to check that everything is OK once, at creation time.

I would like to know how other people are handling this problem. Any advice? Are there other solutions?
Thanks in advance

Tim Boudreau

unread,
Aug 30, 2014, 12:10:16 AM8/30/14
to google...@googlegroups.com
The approach I've used successfully in many projects is to have a generic mechanism for "settings loading" that comes before creating any modules, but contains a mechanism to bind the contents of properties files (layering them up from a variety of standard locations in the filesystem, or any source you care to provide - i.e. layer up /etc/foo.properties + ~/foo.properties + ./foo.properties - or URLs or wherever you want).  I wound up creating a pretty minimal library for that - https://github.com/timboudreau/giulius and there's an example of a totally non-filesystem configuration source - https://github.com/timboudreau/giulius-settings-etcd )

Making this stuff lazy is an optimization, and trades time during startup for indeterminacy in runtime performance (you don't know what will trigger initialization), so it's worth being really sure that doing the loading upfront is prohibitively expensive.

For my part, with regard to exceptions thrown during that process, I've settled on doing something somewhat naughty - http://timboudreau.com/blog/Unchecked_checked_exceptions/read - if the configuration is hosed during initialization, I want to the process to exit, not stagger around as a zombie.

IMO, if something configuration-related can be catastrophically broken, it's preferable to pay in startup time to know that as early as possible (you could always keep your lazy singleton providers, but in production mode have a class bound as an eager singleton that requests all of them in its constructor).

-Tim

Tim Boudreau

unread,
Aug 30, 2014, 12:38:02 AM8/30/14
to google...@googlegroups.com
I thought I could do the big initialization job in my module, create the service instance and bind it to some interface so I could inject a ready to use object later during the app lifecycle. However I have learnt in the documentation that this approach is discouraged :

BTW, I generally agree with the advice there, but the reality is that sometimes you need a "shutdown" lifecycle piece.  Giulius, which I linked in the previous message, has something for that:
which can be used to trigger logical shutdown (i.e. close connections, etc.) either on VM shutdown or when Dependencies.shutdown() is called (Dependencies is just a wrapper for the injector that provides a module to bind @Named stuff).

Logical shutdown very useful in unit tests (I wrote a JUnit extension - https://github.com/timboudreau/giulius-tests - which lets you have test methods with injected arguments) where you want to completely clean up the environment after a test run;  I've even used it to allow an application to completely unload and reload itself on a unix signal, without shutting down the JVM.

The bottom line for me is that the usefulness of being able to inject, say, a JDBC connection, and not pollute application code with the setup of it, is too valuable not to have.

-Tim

Stéphane Claret

unread,
Sep 2, 2014, 5:10:26 AM9/2/14
to google...@googlegroups.com
Hello Tim,
Thanks for your answers, I really appreciate the time and effort. 
Just as you say, I'd rather have badly configured services blowing up immediately on application startup, eager error detection being an important criteria for good software imho. Honestly I admit I still haven't found a satisfying approach and almost got back to my hand-made, factory-based dependency injection. For my project, I could not afford to spend too much time figuring out how to combine guice with my non-trivial-non-named-properties-based initialization needs. I am thinking about using providers with unchecked exceptions but need to experiment first.

Kind regards

Martin Grajcar

unread,
Sep 2, 2014, 10:35:51 AM9/2/14
to google...@googlegroups.com
1) The provider will create the service once when get() is called, and I guess I will have to cache it somewhere since it's a relatively heavy task. My provider then looks like a lazy singleton.

You can combine Provider and @Singleton, so Guice takes care of the caching (I'm unsure of where the annotation should go).

2) I am not sure how I can control where the first call to get() will occur, so I don't have a clear place to catch initialization errors

Just directly after the createInjector call? Or place it anywhere you want and use an unchecked exception as you wan't forget to catch it, will you?

Just as you say, I'd rather have badly configured services blowing up immediately on application startup,

That's what Stage can be used for (pass it to createInjector as the first argument). Or use EagerSingleton.

Stéphane Claret

unread,
Sep 4, 2014, 2:49:41 AM9/4/14
to google...@googlegroups.com
Thanks,
It seems that using a Provider throwing unchecked exceptions and manually calling injector.getInstance(...) right after createInjector on my top-level services allows me to initialize and validate my configuration on Startup. 
Problem solved, thanks for your help!
Best

Tim Boudreau

unread,
Sep 24, 2014, 11:20:07 PM9/24/14
to google...@googlegroups.com
Thanks,
It seems that using a Provider throwing unchecked exceptions and manually calling injector.getInstance(...) right after createInjector on my top-level services allows me to initialize and validate my configuration on Startup. 

FYI, a slightly nicer pattern for that is something like

bind(SanityChecker.class).asEagerSingleton();

static class SanityChecker {
   @Inject
    SanityChecker(Service1 one, Service2 two) {}
}

That way, the sanity check is part of your module, and if someone reuses it, everybody who reuses it doesn't have to remember to ask the injector for anything that might break.

-Tim
 
Reply all
Reply to author
Forward
0 new messages