Modular services with OpenJDK Jigsaw and Guice

Paul Sandoz paul.sandoz at oracle.com
Wed Jul 11 01:49:09 PDT 2012


Hi Tim,

Just to be clear on the approach i was experimenting with.

I was not trying to shoe horn service discovery into Guice. 

Rather i was using Guice for explicit binding of service interfaces and using the ServiceLoader mechanism to bootstrap access to Injector instances provided by modules. 

The bindings of the Injector instances are introspected to find all keys for the type literal that is the service interface. From those keys service instances are obtained from the corresponding Injector.

Using Guice this way provides a richer way to bind and scope service interfaces than is capable with ServiceLoader (in classpath or module mode) at the expense of having to explicitly declare the binding (which is currently also required in module mode for services), and the resolver is blind to what service interfaces are bound.

I anticipated there would be one Guice Module/Injector per Java module for such Guice-based service lookup.

I am sure it is possible to improve on this with tighter integration with the module system, especially the resolver if there were richer ways to signal the binding of service interfaces.

Paul.

On Jul 10, 2012, at 7:54 PM, Tim Boudreau wrote:

> On Tue, Jul 10, 2012 at 4:38 AM, Paul Sandoz <paul.sandoz at oracle.com> wrote:
> Sisu provides extensions to Guice to avoid the need to explicitly bind:
> 
>   http://www.eclipse.org/sisu/
> 
> but i dunno if that can do what you want.
> 
> FWIW i have a preference for explicit bindings, it seems to fit well with the idea of a module declaration expressing it's interface to consumers.
> 
> Yeah, I've done the classpath scanning thing too.  It's a bit brute-force and ugly, though.  It does give you an additional sanity check on startup - if a class couldn't actually resolve its dependencies or threw an exception in a static initializer, you find that out immediately on startup.  When I did this, I would just, say, look up all non-abstract subtypes of some interface which did not have my @Invisible annotation.
> 
> But I don't think it's worth it - I'd rather use a mechanism like Lookup, or if I need more metadata, write an annotation processor that writes a file into the classpath, and then provide a module that finds and reads all such files, loads classes and binds them.  Forcing loading every class the VM can see is overkill - and it should be a clear indication to anybody doing it that they're trying to fit a square peg in a round hole.
> 
> The thing we're really touching on here is that Service Discovery != Dependency Injection, and sometimes you need both.  Trying to shoehorn generic service discovery into Guice doesn't seem like a fabulous idea - it's simply not for that.  If you've gotta, gotta, gotta do that, an annotation processor and some mechanism like META-INF/services files will work (ideally with richer metainfo than just a class name).  
> 
> But Guice injection is entirely based around Key[1] - which really just wraps up a type, generic RTTI and optional annotations;  when it tries to inject a constructor parameter, it is constructing a Key for that parameter and then comparing it with the keys that the injector knows about.  So injection is not polymorphic unless you tell Guice it is - I can call Lookup.lookup(Object.class) and I'll get something or other;  if I @Inject Object, it's an error unless something told Guice to bind Object somehow - it will not guess for me (I had to learn this the hard way a few years ago, since I was used to Lookup [2]).
> 
> One of my favorite things about Guice (as opposed to, say, Spring) is that it is tightly focused on exactly one very specific problem it's there to solve.  It isn't a service discovery library, it's a separate-your-configuration-from-your-business-logic library.  Probably things that try to abuse Guice to be a service-discovery/auto-wiring platform should not be encouraged, and some simple, clean mechanism like Lookup should be fulfilling that role - with an easy way to wrap a Guice Provider around that mechanism *for specific types*.
> 
> Auto-wiring is useful and good (but commonly you need a way to override or turn pieces of it off in a test environment, and that aspect is usually implemented as an afterthought or hack when somebody tries to write a test and something explodes  - Jarda, how many subclasses are there of Repository in older unit tests for NetBeans that don't directly use FileSystem?).  It's good to have the mechanism be configurable and optionally able to be initialized by running code on startup (to solve the test environment problem).  But it's really a different problem than Guice solves - it's just that the shape of the solution is deceptively similar.
> 
> Orthagonal to all of this is the desire not to load classes on startup.  That's an optimization.  One that's critical for desktop applications, and uninteresting or even harmful for server-side applications (if the process is going to run for months, I care much more about it failing quickly if it's broken than I do about startup time).  So probably any service discovery mechanism should be *able* to be completely declarative, but that should not be the only way to initialize things.
> 
> -Tim
> 
> [1] http://google-guice.googlecode.com/git/javadoc/com/google/inject/Key.html
> [2] https://groups.google.com/forum/?fromgroups#!topic/google-guice/wyA1ZijITZU
> 




More information about the jigsaw-dev mailing list