Atom Feed SITE FEED   ADD TO GOOGLE READER

Inside Guice 2: binder sources

To celebrate the release of Guice 2.0, I'd like to showcase my favourite parts of the new code. In this N-part series, I'll go deep into the implementation details to explain the clever and interesting code within the project. I'll focus on general techniques that you can use in your own applications.

Background: Getting sources


When you invoke methods on a Binder (or invoke them indirectly, via AbstractModule), Guice captures the caller's source. This is used to create javac-like error messages in the event of a configuration problem. For example, the following binding is illegal because Executor is an interface without an implementation:
class BadModule extends AbstractModule {
protected void configure() {
bind(Executor.class);
}
}
When this faulty module is used to create an injector, Guice reports the problem with the source line where it occurred:
Exception in thread "main" com.google.inject.CreationException: Guice creation errors:

1) No implementation for java.util.concurrent.Executor was bound.
at com.publicobject.blog.Scratch$1.configure(Scratch.java:18)

1 error

To obtain the offending source line, Guice grabs a stacktrace using new Throwable().getStackTrace() and scans through its frames to find the logical source. It has to ignore some classes: AbstractModule.java, RecordingBinder.java, etc.

The stacktrace grabbing trick is extremely cool, but it's not sufficient. Guice can't always know the full set of classes that it should skip. Extensions like Multibinder create bindings on their user's behalf and need to omit additional classes from the trace (RealMultibinder.java, Multibinder.java). And Provider Methods specify their source as a method name instead of relying on a stack trace.

We need an API to specify which classes to skip in stacktraces, and another API to specify a source object directly.

Binder Sources


The difficulty in finding a nice API for configuring sources is that it's naturally temporal. We want to specify a source, use it for a binding or two, and then reset it back:
  public void configure(Binder binder) {
Method method = ...
Object previous = binder.getSource();
binder.setSource(method);

try {
binder.bind(method.getGenericReturnType())
.toProvider(new ProviderMethod(method));
} finally {
binder.setSource(previous);
}
}

Yuck.

Taking inspiration from List.subList(), our approach is to instead return a new binder that works with the provided source:
  public void configure(Binder binder) {
Method method = ...
Binder sourcedBinder = binder.withSource(method);
sourcedBinder.bind(method.getGenericReturnType())
.toProvider(new ProviderMethod(method));
}

Every binding, injection or configuration applied to sourcedBinder will use the user-specified source. But the passed-in binder is untouched. With some inlining and the binder's existing embedded DSL, the whole thing can be a single statement:
  public void configure(Binder binder) {
Method method = ...
binder.withSource(method)
.bind(method.getGenericReturnType())
.toProvider(new ProviderMethod(method));
}

This new API is cute and fits nicely with the existing code.

Part 2.
Hi Jesse,

Grateful for the tutorial. But I think it would be more useful if you could somehow put it as a TOC and mark it clearly. I used your Google Collections tutorial to learn them but sometimes the lessons are difficult to search for on Google. If I want to find the 10th lesson in your series, I find that it is difficult to load it on your first try.