Another way to pull nontrivial initialization logic out of regular object
graph creation is to use ConcurrentSingleton scope for heavyweight
singletons, something I wrote
about<http://tembrel.blogspot.com/2009/11/concurrently-initialized-singletons-in.html>a
while ago. It's similar in some ways to the idea of using Services to
perform initialization.

--tim

On Sat, Jul 21, 2012 at 11:24 AM, Fred Faber <[email protected]> wrote:

> The notion of avoiding heavyweight work in constructors is largely borne
> out separating the creation of an object graph from executing nontrivial
> logic in an app.  The object graph creation is essentially a side-effect of
> injector creation, and we don't have a good reach into this activity.  This
> prevents us from inclulding some elementary behavior such as error
> handling.  More optimistically in the no-error case, we want an app to have
> control as soon as possible instead of blocking on injector creation, and
> hence want to keep constructors simple and quick.
>
> Lazy initialization is part of the wider topic of object lifecycle.
>  There's nothing intrinsic within Guice that directly addresses this (minus
> to some extent type listeners).   However the 
> Service<http://code.google.com/p/guava-libraries/wiki/ServiceExplained>interface
>  within Guava is a convenient solution.  Specifically:  1) define
> initialization logic as a Service (most often extending
> AbstractIdleService<http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/AbstractIdleService.html>);
> 2) bind your services within a multibinder (Set<Service>); and, 3) after
> your injector is created, iterate over this set and start each service.
>
> For (3), creating a wrapper to do the iteration is convenient so that you
> can more easily parallelize the initialization.  There's a candidate for
> this to be released within Guava but I don't know what a timeline is / how
> concrete it is.
>
> Fred
>
> On Fri, Jul 20, 2012 at 6:37 PM, Leigh Klotz, Jr. 
> <[email protected]>wrote:
>
>> I'd like to get some guidance on correct, clear, and concise
>> initialization of singleton, read-only access objects
>> wrapping cached run-once computations, with either lazy or eager
>> initialization.
>>
>> I brought this up peripherally in
>>
>> http://groups.google.com/forum/?fromgroups#!topic/google-guice/u0V97-FZBTQ
>> but I wanted to avoid taking my attempted threadjacking any further and
>> am starting a new topic.
>>
>> Here's a sample use case:
>>
>> A Database provides a table of String keys and values.  I'd like to
>> provide a read-only get(String)->String access
>> object, and I'd like to initialize it safely, so that the resulting
>> object is thread-safe.  I'd like to express this
>> concisely, in a way that is clear to code readers so they will be
>> incented to copy the pattern.
>>
>> Lazy init can be done in Java using the static hack, as descrbied by Bob
>> here
>>   http://blog.crazybob.org/2007/01/lazy-loading-singletons.html
>>   and in Wikipedia here
>>   http://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom
>>
>> but I'd like to do it using Guice, for all the usual reasons, such as so
>> I can integrate these providers in with normal injection.
>>
>> I hope my problem statement is clear. I'm looking for guidance on the
>> best solution.
>>
>> Here are three unsatisfactory ideas and one maybe OK one.
>>
>> Proposal A:
>> Proposal A does the work of reading the database in the constructor.
>>
>> This code is clear, and it's certainly concise, and as near as I can
>> understand, it's thread-safe.
>>
>> Unfortunately, I belive it isn't correct in Guice because of the problems
>> associated with doing work in constructors
>> (proxy objects, for example).
>>
>>     @LazySingleton
>>     class CachedThings {
>>       private final Map<String,String> cache;
>>
>>       @Inject CachedThings(DB db) {
>>         this.cache = readCache(db);
>>       }
>>
>>       public String get(String x) {
>>         return cache.get(x);
>>       }
>>
>>       private Map<String,String> readCache(DB db) {
>>         Map<String,String> result = new HashMap<String,String>();
>>         for (Row row : db) { map.add(row.a, row.b); }
>>         return result;
>>       }
>>
>>     }
>>
>> Proposal B:
>>
>> Proposal B splits the data access object from the database reader
>> operation, moving the database read into its own
>> Provider, where it can operate safely.  This appears to be just as
>> thread-safe as the previous version, but is
>> considerably less concise.  It also exposes a @Named TypeLiteral that is
>> not only ugly, but also opens the
>> possibility of someone directly injecting that Map and causing unwanted
>> expensive database reads.
>>
>>     @LazySingleton
>>     class CachedThings {
>>       private final Map<String,String> cache;
>>
>>       @Inject CachedThings(@Named("hack") Map<String,String> cache) {
>>         this.cache = cache;
>>       }
>>
>>       public String get(String x) {
>>         return cache.get(x);
>>       }
>>
>>       static class MyModule extends AbstractModule {
>>          protected void configure() {
>>            bind(new
>> TypeLiteral<Map<String,String>>(){/**/}.annotatedWith(Names.named("hack")).toProvider(ThingProvider.class).in(LazySingleton.class);
>>
>>          }
>>       }
>>
>>       @LazySingleton
>>       static class ThingProvider implements Provider<Map<String,String>> {
>>         private final DB db;
>>
>>         @Inject ThingProvider(DB db) {
>>           this.db = db;
>>         }
>>
>>         private Map<String,String> get() {
>>           Map<String,String> result = new HashMap<String,String>();
>>           for (Row row : db) { map.add(row.a, row.b); }
>>           return result;
>>         }
>>     }
>>
>>
>> Proposal C:
>> Proposal C uses @Provides methods so avoid to the problematic verbosity
>> of TypeLiteral, and indeed the code is cleaner, but we still have the
>> @Named hack and
>> internal data exposure.  (I've written the MyModule as a static class of
>> CachedThings, which is questionable, so we might need to deduct a few points
>> for the additional verbosity needed to move the module out.)
>>
>>     @LazySingleton
>>     class CachedThings {
>>       private final Map<String,String> cache;
>>
>>       @Inject CachedThings(@Named("hack") Provider<Map<String,String>>
>> thingProvider) {
>>         this.cache = thingProvider.get();
>>       }
>>
>>       public String get(String x) {
>>         return cache.get(x);
>>       }
>>
>>       static class MyModule extends AbstractModule {
>>          protected void configure() { }
>>
>>          @LazySingleton @Provides @Named("hack")
>> Provider<Map<String,String>> getThing(DB db) {
>>            Map<String,String> result = new HashMap<String,String>();
>>            for (Row row : db) { map.add(row.a, row.b); }
>>            return result;
>>         }
>>       }
>>     }
>>
>> Proposal D:
>> I don't know much about injected methods other than that they run after
>> constructors and the results can't be final.
>> Is this correct with regard to Guice initialization sequence?  Is it safe
>> for multi-threaded readonly access of the resulting HashMap?
>>
>> Maybe this is the right solution is to use an @Inject setCache(DB) method?
>>
>>     @LazySingleton
>>     class CachedThings {
>>       private Map<String,String> cache;
>>
>>       @Inject CachedThings(...) {
>>         ... other stuff here if necessary ...
>>       }
>>
>>       @Inject void setCache(DB db) {
>>          Map<String,String> result = new HashMap<String,String>();
>>          for (Row row : db) { map.add(row.a, row.b); }
>>          cache = result;
>>       }
>>
>>       public String get(String x) {
>>         return cache.get(x);
>>       }
>>     }
>>
>> Thank you,
>> Leigh.
>>
>>  --
>> You received this message because you are subscribed to the Google Groups
>> "google-guice" group.
>> To view this discussion on the web visit
>> https://groups.google.com/d/msg/google-guice/-/HUKKPENRVqcJ.
>> To post to this group, send email to [email protected].
>> To unsubscribe from this group, send email to
>> [email protected].
>> For more options, visit this group at
>> http://groups.google.com/group/google-guice?hl=en.
>>
>
>  --
> You received this message because you are subscribed to the Google Groups
> "google-guice" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to
> [email protected].
> For more options, visit this group at
> http://groups.google.com/group/google-guice?hl=en.
>

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

Reply via email to