Future Guice: Providers that throw unchecked exceptions
As
previously mentioned, I'm cataloging Guice's changes since 1.0.
Suppose you have a provider that throws an unchecked exception:
class PhaseOfTheMoonProvider implements Provider<PhaseOfTheMoon> {
@Inject Observatory observatory;
public PhaseOfTheMoon get() {
if (observatory.isNighttime()) {
return observatory.getCurrentPhase();
}
throw new IllegalStateException("Can't see the moon unless it's nighttime.");
}
}
For the most part, unchecked exceptions are useful for validating pre- and postconditions. But a problem arises when code attempts to catch the specific exception thrown by a provider. Suppose this is our calling code:
@Inject Provider<PhaseOfTheMoon> phaseOfTheMoonProvider;
public boolean isFullMoon() {
try {
PhaseOfTheMoon phaseOfTheMoon = phaseOfTheMoonProvider.get();
return phaseOfTheMoon == PhaseOfTheMoon.FULL;
} catch (IllegalStateException e) {
return false;
}
}
This code works, but it's prone to regress as the provider code is maintained. For example, if the provider code is changed to throw
NoMoonlightException
, our application breaks without any notice from the compiler. And should other unchecked exceptions be thrown, the only way to reliably handle them is to suffix all provider access with an ugly
catch (RuntimeException e)
block.
Enter ProvisionException
To simplify this situation, recent snapshots of Guice make
ProvisionException
public, and guarantee that that will be the only exception type thrown by injected providers. Client code only needs to catch
ProvisionException
in order to recover from a failed provider.
To implement this, Guice wraps user-supplied providers and performs exception-chaining to rethrow arbitrary exceptions as
ProvisionException
. It embeds contextual information in the provision exception, which simplifies diagnosing problems with indirectly-injected values.
The new client code can catch
ProvisionException
instead. This code will continue to work, even if the
Provider<PhaseOfTheMoonProvider>
changes its thrown types:
public boolean isFullMoon() {
try {
PhaseOfTheMoon phaseOfTheMoon = phaseOfTheMoonProvider.get();
return phaseOfTheMoon == PhaseOfTheMoon.FULL;
} catch (ProvisionException e) {
return false;
}
}
This is the Guice change that I'm most anxious about. It's makes a nontrivial change in Guice's behaviour in an area that is least likely to have test coverage - the exceptional case. How good are your unit tests?
PS - for checked exceptions, look at the throwing providers extension.
# posted by Jesse Wilson
on Tuesday, April 29, 2008
0 comments
post a comment
Glazed Lists talk at JavaOne: Friday at 2:50pm
Details on this year's
Glazed Lists tech session:
Simply Sweet Applications with Glazed Lists
Ken Orr, The MathWorks
Session TS-6047
Friday May 9 at 14:50, Hall E 135
From
Ken's Abstract,
Glazed Lists fosters data-centric design, which promotes decoupling of components and increases the testability of your code. This lets developers envision their data infrastructure as a data fabric, which is a fantastic abstraction. List pipelining is where the real power of Glazed Lists shines.
I've committed to hang around Google's booth on Tuesday. I'll be there from 11:30 to 12:30, plus 1:30 to 2:30 if you'd like to drop by and say hi.
# posted by Jesse Wilson
on Tuesday, April 29, 2008
3 comments
post a comment
Guice Multibinder API proposal
Multibindings is an idea to allow multiple independent modules to contribute elements to a collection. There's
lots of proposed APIs to add multibindings to Guice.
Unlike almost all of the existing proposals, this strategy can be used to add multibindings as an extension to Guice. Therefore we don't increase the complexity of the existing Binder API in the much more common non-multibinding case. The API:
public class Multibinder<T> {
/**
* Returns a new multibinder that collects instances of {@code type} in a list.
*/
static <T> Multibinder<T> newListBinder(Binder binder, Class<T> type);
/**
* Returns a new multibinder that collects instances of {@code type} in a set.
*/
static <T> Multibinder<T> newSetBinder(Binder binder, Class<T> type);
/**
* Build a new element in the bound collection.
*/
AnnotatedBindingBuilder<T> addBinding();
}
... and here's what the client code would look like:
public static void testMultibinder() {
AbstractModule stanCartmanKennyModule = new AbstractModule() {
protected void configure() {
Multibinder<String> stringBinder
= Multibinder.newSetBinder(binder(), String.class);
stringBinder.addBinding().toInstance("Stan");
stringBinder.addBinding().annotatedWith(Names.named("Fatass")).toInstance("Cartman");
stringBinder.addBinding().toProvider(new Provider<String>() {
public String get() {
return "Kenny";
}
});
}
};
AbstractModule kyleModule = new AbstractModule() {
protected void configure() {
Multibinder.newSetBinder(binder(), String.class)
.addBinding().toInstance("Kyle");
}
};
Injector injector = Guice.createInjector(stanCartmanKennyModule, kyleModule);
Set<String> boys = injector.getInstance(Key.get(new TypeLiteral<Set<String>>() {}));
assertEquals(Sets.newHashSet("Stan", "Cartman", "Kenny", "Kyle"), boys);
assertEquals("Cartman", Key.get(String.class, Names.named("Fatass"));
}
Note that I've simplified the code above by omitting some details (annotating the collections, binding collections of parameterized types).
I think this API is both compact and convenient. What's your favourite
multibinding API?
# posted by Jesse Wilson
on Tuesday, April 29, 2008
2 comments
post a comment
Future Guice: injecting inner classes
As
previously mentioned, I'm cataloging the differences in Guice since 1.0.
This test passes in Guice 1.0, but how it does so is surprising:
public class InjectTest extends TestCase {
public void testFoo() {
Foo foo = Guice.createInjector().getInstance(InnerFoo.class)
}
class InnerFoo implements Foo {
@Inject InnerFoo() {}
}
}
The problem is that the
InnerFoo
inner class is not marked
static
. Therefore each instance of
InnerFoo
has an implicit, invisible reference to its containing class,
InjectTest
. When it creates an instance of
InnerFoo
, Guice 1.0 will also build a new
InjectTest
, using its default constructor.
This is weird! The outer-class instance is only reachable via the implicit reference, which is awkward and unexpected.
The latest snapshots of Guice refuse to construct non-static inner classes. This will help to find bugs! And for the rare situation where non-static is desired, Providers can be used. Plus they make the choice of containing instance explicit.
# posted by Jesse Wilson
on Tuesday, April 29, 2008
1 comments
post a comment
Future Guice: More aggressive error detection
For the past month, I've been cataloging the various changes since Bob & Kevin released version 1.0 back in March 2007. For a 1.0, it's held up remarkably well. We've been using it on my team without many problems and we've quite enjoyed it.
Good News!
Guice has improved since 1.0. There's new features, bugfixes, performance improvements and tools for Guice users to enjoy.
Bad News
The latest snapshosts of Guice are not 100% backwards-compatible with version 1.0. For example, Guice had a bug where it quietly ignored some bindings that it couldn't fulfill. This test case gives a false-positive result: it succeeds even though there's no binding for interface
A
:
public void testBindProvidersOrder() {
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bind(String.class).toProvider(new Provider<String>() {
@Inject A a;
public String get() {
return "hello world";
}
});
}
});
assertEquals("hello world", injector.getInstance(String.class));
}
Today's Guice is better - when this test runs it prints an error at Injector-creation time:
com.google.inject.CreationException: Guice configuration errors:
1) Error at com.google.inject.BinderOrderTest$1$1.a(BinderOrderTest.java:31):
Binding to com.google.inject.BinderOrderTest$A not found. No bindings to that type were found.
1 error[s]
at com.google.inject.InjectorBuilder$ConfigurationErrorHandler.blowUpIfErrorsExist(InjectorBuilder.java:281)
at com.google.inject.InjectorBuilder.validate(InjectorBuilder.java:181)
at com.google.inject.Guice.createInjector(Guice.java:59)
at com.google.inject.BinderOrderTest.testBindProvidersOrder(BinderOrderTest.java:26)
This particular problem is easy to recognize and fix. It's a net win, even though it breaks backwards-compatibility. There's more similar changes, and I intend to post about them in the coming weeks...
# posted by Jesse Wilson
on Friday, April 25, 2008
0 comments
post a comment
Quine Programs in Java
Michael Kölling
blogged about
Quines - programs that print themselves. Here's my best effort in Java (prettyprinted for readability):
class A {
static {
String a="class A{static{String a=%s%s%1$s;System.out.printf(a,'%1$s',a);}}";
System.out.printf(a,'"',a);
}
}
Can you beat 122 characters?
# posted by Jesse Wilson
on Wednesday, April 16, 2008
6 comments
post a comment
Amazon MP3 works good
I just bought the new Panic At The Disco album on
Amazon MP3. It was easy! And I don't have the DRM-guilt that I usually get whenever I buy stuff from iTunes. The price is right, the quality is high, and I don't have to do the
Deregister Computer bullshit if I change my laptop. Hooray!
I don't think I'll be going back to the iTunes store for music.
# posted by Jesse Wilson
on Sunday, April 06, 2008
1 comments
post a comment
Don't do this: Share names
The following program is valid Java, even though the
Runnable
on line 1 is a completely different symbol than
Runnable
on line 3:
public class Refrigerator implements Runnable {
public void run() {
new Runnable().freeze();
}
public class Runnable {
void freeze() {
System.out.println("cold and refreshing");
}
}
public static void main(String[] args) {
new Thread(new Refrigerator()).start();
}
}
I'm not quite sure if this is
shadowing,
obscuring or
hiding, but it's certainly not good. Writing a compiler must be pretty difficult!
# posted by Jesse Wilson
on Friday, April 04, 2008
2 comments
post a comment