Sunday, September 16, 2007

Java Concurrency in Practice news

I'm told that Java Concurrency in Practice is going into its fifth printing!

Custom Matcher and MethodInterceptor for DWR-Guice

I wanted a way to intercept calls to public methods of a remoted interface so I could replace my use of bindFilter with bindInterceptor, as described in an earlier posting.

At first I thought I could just use the static methods in Matchers:

bindInterceptor(
only(MyService.class),
any(),
myInterceptor
);

This doesn't intercept anything, because MyService is an interface. What I needed was to intercept calls to a method of a subclass of MyService for which there exists a method of the same name and argument types in MyService.

bindInterceptor(
subclassesOf(MyService.class),
declaredBy(MyService.class),
myInterceptor
);

The subclassesOf matcher is already provided in com.google.inject.matcher.Matchers, but I had to roll my own declaredBy method matcher. Here's the heart of the implementation (cls is a final Class<?> field, initialized from the argument to declaredBy):

public boolean matches(Method method) {
try {
// Matches if the method is from a subclass
// of the given class (or the class itself)
// and the given class declares a method
// with the same name and parameter types.
if (cls.isAssignableFrom(
method.getDeclaringClass())) {
// Return value of getDeclaredMethod
// is ignored. It throws an exception
// if the method is not found.
cls.getDeclaredMethod(
method.getName(),
method.getParameterTypes());
return true;
}
// fall through
} catch (NoSuchMethodException e) {
// fall through
} catch (SecurityException e) {
// fall through
}
return false;
}

This still didn't work quite right; the implementation of one method called another public method of the interface, and that call was intercepted even though it wasn't a remote call. To mimic AjaxFilter, I want to able to intercept only the outermost call. I wrote a MethodInterceptor-decorator:

public class OutermostCallInterceptor
implements MethodInterceptor {
/**
* Decorates a MethodInterceptor so that only the
* outermost invocation using that interceptor will
* be intercepted and nested invocations willbe
* ignored.
*/
public static MethodInterceptor outermostCall(
MethodInterceptor interceptor) {
return new OutermostCallInterceptor(interceptor);
}

/** Ensure underlying interceptor is injected. */
@Inject void injectInterceptor(Injector injector) {
injector.injectMembers(interceptor);
}

public Object invoke(MethodInvocation invocation)
throws Throwable {
int savedCount = count.get();
count.set(savedCount + 1);
try {
if (count.get() > 1)
return invocation.proceed();
else
return interceptor.invoke(invocation);
} finally {
count.set(savedCount);
}
}

private OutermostCallInterceptor(
MethodInterceptor interceptor) {
this.interceptor = interceptor;
}

private final MethodInterceptor interceptor;

private final ThreadLocal count =
new ThreadLocal() {
@Override protected Integer initialValue() {
return 0;
}
};
}

So now my binding looks like this:

bindInterceptor(
subclassesOf(MyService.class),
declaredBy(MyService.class),
outermostCall(myInterceptor)
);

and myInterceptor is injected. It's not as compact as using bindFilter, but I can apply multiple interceptors without having to resort to FluentConfigurator. The use of subclassesOf is probably redundant, since declaredBy checks that the method is from a subclass of MyService, but I think it helps to clarify what's going on.

Saturday, September 15, 2007

Replacing AjaxFilter with MethodInterceptor

I wrote the previous posting without referring to DWR-Guice integration but in practice I extended org.directwebremoting.guice.AbstractDwrModule, not com.google.inject.AbstractModule.

For DWR-Guice users, the nice thing about injectable method interceptors is that you can use them to replace most uses of AjaxFilter, which is a good thing because the AjaxFilter support in the DWR-Guice stuff is weak (mea culpa).

I had to write some support utilities to get the right effect, but now I can do something like this:

bindInterceptor(
subclassesOf(VulnerableService.class), // class matcher
declaredBy(VulnerableService.class), // method matcher
// prevent nested interception
outermostCall(new AuthenticationInterceptor()),
outermostCall(new AuthorizationInterceptor())
);
and AuthenticationInterceptor is injected, so that it can look like
this:
class AuthenticationInterceptor 
implements MethodInterceptor {
@Inject Provider<RequestParameters> reqParmsProvider;
@Inject Provider<AuthenticationService> authSvcProvider;
public Object invoke(MethodInvocation invocation)
throws Throwable {
RequestParameters reqParms = reqParmsProvider.get();
AuthenticationService authSvc = authSvcProvider.get();
if (authSvc.authenticate(reqParms))
return invocation.proceed();
else
throw new AuthenticationException(reqParms);
}
}
The subclassesOf matcher is part of Guice. The declaredBy matcher and the outermostCall interceptor decorator are custom implementations that I'll describe in a later posting.

Friday, September 14, 2007

Injecting method interceptors in Guice 1.0

Guice 1.1 will probably have injected method interceptors, but until then those of us who need this kind of thing will have to roll our own.

[Update 2009-1-3] Guice 2 has (or will have, depending on how you view its release status) a requestInjection facility.

The way I've been doing it is similar to the way Stuart McCulloch suggested, but more automatic: I extend AbstractModule by
  1. adding a registerForInjection method that takes a varargs array of objects that should be injected and
  2. overriding bindInterceptor to call registerForInjection(interceptors) before calling the superclass implementation.
The implementation of registerForInjection adds the elements of the array argument to a set of objects to be injected. There is a private method, injectRegisteredObjects, marked with @Inject; all it does is call Injector.injectMembers on every element of this set.

To ensure that injectRegisteredObjects is called, I bind the module class to the module instance. Since I want to be able to do this for every instance of the module, I bind with a unique annotation instance each time. The private method ensureSelfInjection does this binding once only for each module instance, so I can safely call it in registerForInjection.

Here's the code:

/**
 * An extension of AbstractModule that provides
* support for member injection of instances
* constructed at bind-time; in particular,
* itself and MethodInterceptors.
*/
public abstract class ExtendedModule extends AbstractModule {

/**
* Overridden version of bindInterceptor that,
* in addition to the standard behavior,
* arranges for field and method injection of
* each MethodInterceptor in {@code interceptors}.
*/
@Override public void bindInterceptor(Matcher<? super Class<?>> classMatcher,
Matcher<? super Method> methodMatcher,
MethodInterceptor... interceptors) {
registerForInjection(interceptors);
super.bindInterceptor(classMatcher, methodMatcher, interceptors);
}

/**
* Arranges for this module and each of the given
* objects (if any) to be field and method injected
* when the Injector is created. It is safe to call
* this method more than once, and it is safe
* to call it more than once on the same object(s).
*/
protected <T> void registerForInjection(T... objects) {
ensureSelfInjection();
if (objects != null) {
for (T object : objects) {
if (object != null) toBeInjected.add(object);
}
}
}

@Inject private void injectRegisteredObjects(Injector injector) {
for (Object injectee : toBeInjected) {
injector.injectMembers(injectee);
}
}

private void ensureSelfInjection() {
if (!selfInjectionInitialized) {
bind(ExtendedModule.class)
.annotatedWith(getUniqueAnnotation())
.toInstance(this);
selfInjectionInitialized = true;
}
}

private final Set<Object> toBeInjected = new HashSet<Object>();

private boolean selfInjectionInitialized = false;

/**
* Hack to ensure unique Keys for binding different
* instances of ExtendedModule. The prefix is chosen
* to reduce the chances of a conflict with some other
* use of @Named. A better solution would be to invent
* an Annotation for just this purpose.
*/
private static Annotation getUniqueAnnotation() {
return named("ExtendedModule-" + count.incrementAndGet());
}

private static final AtomicInteger count = new AtomicInteger();
}

There's a follow-up posting on DWR-Guice applications.