Showing posts with label DWR. Show all posts
Showing posts with label DWR. Show all posts

Sunday, September 16, 2007

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.

Thursday, April 05, 2007

Guice support in DWR

Guice is the hot new dependency injection framework from Bob Lee and others at Google. DWR is a system for remoting Java server-side objects as JavaScript client-side objects in a way that is natural on both the client and server sides.

I'd been using DWR with Spring to wire together all the components of the Seat Yourself on-line ticket sales web application for almost a year and I was pretty happy with the combination. Then a few weeks ago, just as I was considering how to make the webapp more easily configurable, to support rapid administration and self-configuration of many customers, Josh Bloch suggested I take a look at Guice, and I suddenly realized just how fragile and unmaintainable was all that XML I had written for DWR and Spring.

Of course I could use Guice without integrating it with DWR, but one of the things that had always bothered me about DWR (and many other tools, not just DWR) was that everything -- remoted objects, data transfer objects, extension classes -- has to be concrete and public, with a public parameterless constructor, and everything works through setter injection, which makes things difficult. I want to work in terms of interfaces, with implementation classes that have final fields for injected state.

Guice looked like a way to have it all: easy remoting with type-safe (pure Java) configuration. I wrote a Guice integration package for DWR, and Joe Walker signed me on as a DWR contributor so I can help maintain it as part of the DWR distribution. The package Javadoc describes how to use it along with a simple example.

I'm still in the early stages of applying it to the Seat Yourself web sales application, but already the power of Guice has made things simpler. Each customer (where a customer is ticket-selling entity, like a theater) is associated with a domain where all their data -- seat charts, performance information, pricing information, patrons and their reservations, and holds on seats -- is maintained. When a user visits the web store to buy tickets, the link contains a domain name, e.g., "t3" for Theatre Three. Before Guice support, I had devised a complicated system using reflection and proxying to route the user request to the appropriate service object. It was so involved that I had trouble explaining it to my partners, both smart people. Not being able to explain something is a bad code smell.

With Guice-injected remoted objects, I can define a new scope, domain scope, where the details of providing the appropriate domain-specific service object are neatly encapsulated and managed by Guice. All I have to do is write something like this in my binding code:

bindRemotedAs("Tix", WebstoreService.class) // interface
.to(WebstoreServiceImpl.class) // impl
.in(DomainScopes.DOMAIN); // scope

where the interface looks like this:

public interface WebstoreService {
PerformanceAvailability getPerformanceAvailability();
...
}


Only the methods of WebstoreService are exposed to the world, even though the implementation class might have other public methods. With the Guice support, I don't need to specify which methods to expose. As usual with DWR, on the JavaScript side I can write things like:

Tix.getPerformanceAvailability({
callback : function(availability) {
notifyPerformanceAvailabilityReceived(availability)
},
exceptionHandler : ...
});


Because Guice has Spring integration, I can migrate my existing Spring-based code smoothly. Because it has JNDI integration, I can rewrite the code that manages the domain-specific configuration to use whatever LDAP back-end I like, instead of having to maintain an assortment of XML files and registry settings. We've already had problems with syntax errors in the XML configuration files, so this will save us a lot of heartache.

Guice also has support for AOP Alliance method interceptors, which means that on most of the occasions when I would feel most tempted to use AspectJ, I can stick to pure Java. Not that I have anything against AspectJ, but it's a different language and one more thing to worry about. I want to save it for the occasions when I really need it (as was the case for another submodule of our project).