“EasyMock” style continuations in Java …   Leave a comment

Wouldn’t it be great if you could do this:
useResultFor(bar.foo(), new Continuation());

… when “bar” was a proxy object that wouldn’t return any-time soon?

Actually … it’s  possible, as long as you don’t mind doing it in a purely OO approach.
(Sorry about the lack of indentation … I can’t seem to get around it)


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedList;

/**
* I called the class "French" because I was thinking of EasyMock (which I think
* is French)
*
* @author Peter LaValle
*/
public class French {

/**
* A {@link Boo} interface just has a {@link Boo.foo} method.
*
* @author Peter LaValle
*
*/
public static interface Boo {
Integer foo();
}

/**
* A {@link Continuation} will be run with some value
*
* @author Peter LaValle
*
* @param
*/
public static interface Continuation {
public void run(T value);
}

/**
* This would be {@link ThreadLocal} in real life ...
*/
public static final LinkedList CALLED_METHODS = new LinkedList();

/**
* While we don't *need* all of these in this demo, they would be required
* in a real implementation.
*/
public static final Class[] PRIMITIVE_BOXES = new Class[] {
Byte.class, Character.class, Short.class, Integer.class,
Long.class, Float.class, Boolean.class, Double.class };

/**
* Let's go ...
*
* @param args
*/
public static void main(String[] args) {

// First we're going to implement the class {@link Boo} as a proxy that
// just catches invocations.
// This is being done as a proxy because ... well ... that's how I
// wanted to use this technique in my project.
final Boo bar = (Boo) Proxy.newProxyInstance(
French.class.getClassLoader(), new Class[] { Boo.class },
new InvocationHandler() {

@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {

// we check that CALLED_METHODS is empty ..
assert CALLED_METHODS.isEmpty();

CALLED_METHODS.add(method);

return null;
}
});

// Now that we have this neat little proxy object, we use the {@link }
// to call our dynamic continuation.
useResultFor(bar.foo(), new Continuation() {

@Override
public void run(Integer value) {
System.out.println("Integer = " + value);
}
});

// just for fun ...
useResultFor(bar.toString(), new Continuation() {

@Override
public void run(String value) {
System.out.println("String = " + value);
}
});
}

public static void useResultFor(T willBeNull, Continuation logic) {

assert null == willBeNull;

final Method method = CALLED_METHODS.removeLast();

// in real life, we'd run our remote query to get the result of this
// value before passing it to logic below
if (method.getName().equals("foo")) {
logic.run((T) new Integer(7));
} else {
logic.run((T) method.getReturnType().cast("Squirell!"));
}
}
}


So the proxy is just putting the method (or whatever) onto a ThreadLocal stack for later use, and the “useResultFor” method picks it up and knows what to do with it.

Advertisements

Posted October 2, 2010 by grigoriipechorin in weaving

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: