Wanted: javax.interceptor extension for Guice
I'm feverishly preparing Guice for the 2.0 release later this summer, and tonight I scanned through our issues list. There's a whole bunch of good features that I won't get to before our release. So I'm looking for Guice users to help out with development!Introducing javax.interceptor
javax.interceptor is a fairly-simple method interception package for modern Enterprise Java stacks. These examples show how it works. Create a class with an
@Interceptors
annotation:@Interceptors(AuditInterceptor.class)
public class AccountBean implements Account {
private int balance = 0;
public void deposit(int amount) {
balance += amount;
}
public void withdraw(int amount) {
balance -= amount;
}
}
Then create the interceptor:public class AuditInterceptor {
@AroundInvoke
public Object audit(InvocationContext invocationContext) throws Exception {
System.out.println("Invoking method: " + invocationContext.getMethod());
return invocationContext.proceed();
}
}
If everything works as intended, the interceptor's
audit()
method will intercept all calls to deposit()
and withdraw()
.Guice's MethodInterceptor
Guice has another API for this, MethodInterceptor. Guice uses Matchers to support arbitrary selection of interceptable methods.
Wanted: an Extension for javax.interceptor
I believe this feature could be implemented as an extension to Guice. The extension would certainly require some clever tricks, but it should not require changes to the Guice internals. Here's what I guess the implementation could look like:
public class JavaxInterceptorModule extends AbstractModule {
public void configure() {
JavaxInterceptor interceptor = new JavaxInterceptor();
injectMembers(interceptor);
bindInterceptor(Matchers,any(), new InterceptMethodsMatcher(), interceptor);
}
static class JavaxInterceptor {
/**
* Loop over all the injector's bindings, looking for @Interceptor.
* Then verify that the interceptor classes are either injectable (ie.
* they have bindings), or they have a no-arg constructor. This
* isn't strictly necessary, but it allows us to fail faster, which is
* always a Guicy thing to do.
*/
@Inject void initialize(Injector injector) { ... }
/**
* Validate that the target method is intercepted. We need to
* consider just-in-time bindings that might not have been
* checked during initialize(). We also need to check for the
* ExcludeClassInterceptors etc. annotations.
*
* <p>Instantiate all of the injectors for this method, then
* run 'em. We'll need our own implementation of InvocationContext
* to pass to the interceptors.
*/
Object invoke(MethodInvocation invocation) { ... }
}
}
Of course it's not this simple. The implementation needs be tested to be consistent with the Java EE implementations. It needs to have reasonable error handing. And there's nuances related to inheritance etc. It also needs thorough unit tests.
Recruiting Contributors
Does writing this code sound fun to you? If it does, we'd love your help. Post your interest on the bug! You'll need to checkout the Guice code, write the code and tests, and upload a patch. I'll code review the patch (a fairly involved process) and we'll iterate until it's perfect.
In return, you'll get to see your
@author
line in the Guice source code. You'll probably learn a lot about AOP, Guice and reflection. It's resume padding. And of course, coding is its own reward.