You are currently browsing the Philipp Meier's weblog posts tagged: observer


Refactor-Safe passing of method names for callbacks

Reflection is a very helpful tool for decoupling and avoiding marker interfaces. A common pattern for registering callbacks at the obersever pattern makes use of passing the callback method name as a String to a Observable:

[source:java]
class Observer {
Observer() {
observable.register(this, “onEvent”);
}

public void onEvent(String message) { … }
}
[/source]

The Observable class uses reflection to extract the method “onEvent” at runtime and will invoke it to notify Observable that an event occurred. This approach shows several advantages over the use of marker interfaces like java.util.Observer. First, no marker interface is needed, which decouples the code and depending on the effort put into the observer, the callback method must not be defined as strictly. E.g. the observer could even handle a signature like “void onEvent()” and simple do not pass any argument to the method.

Drawback of this approach is that the method name is passed to the Observable as String which makes refactoring your code harder and leads to nasty runtime errors. There is not check if the methods signature matches the signature which Observable expects. These errors won’t occur until the Observable will try to make a callback to the Observer.

Inspired by EasyMock 2.0 I found a way to extract a method name at runtime without invoking the method. Create a proxy with the Cglib bytecode manipulation library which records the method called on the proxy. Later on, the method can be used to register an Observer. I made the following, experimental code with Proxytoys and Cglib.

[source:java]
public class MethodRecordingInvoker implements Invoker {
private final ObjectReference<Method> methodNameReference;

public MethodRecordingInvoker(ObjectReference<Method> methodNameReference) {
this.methodNameReference = methodNameReference;
}

public Object invoke(Object object, Method method, Object[] objects) throws Throwable {
methodNameReference.set(method);
return methodNameReference;
}

static <T> T createMethodRecordingInstance(T tc, ObjectReference<Method> methodNameReference) {
return (T) createMethodRecordingInstanceFromClass(tc.getClass(), methodNameReference);
}

static <T> T createMethodRecordingInstanceFromClass(Class< ? extends T> tc,
ObjectReference<Method> methodNameReference) {
return (T) new CglibProxyFactory().createProxy(new Class[]{tc},
new MethodRecordingInvoker(methodNameReference));
}
}
[/source]

All small test shows how it should work:

[source:java]
public class MethodRecordingInvokerTest extends TestCase {

public void testRecording() throws Throwable {
ObjectReference<Method> ref = new ObjectReference<method>();
MethodRecordingInvoker invoker = new MethodRecordingInvoker(ref);
Method m = Object.class.getMethods()[0];
invoker.invoke(null, m, null);
assertSame(“Method not recorder”, m, ref.get());
}

public void testWithProxy() {
class Test {
public Test() {
}

public void dummy() {
}
}

Test testInstance = new Test();

ObjectReference<Method> methodReference = new ObjectReference<Method>();
Test testStub = MethodRecordingInvoker.createMethodRecordingInstance(testInstance, methodReference);

// This invokation will be intercepted by the invoker which records the method in methodReference.
testStub.dummy();
assertEquals(“Method recorded”, “dummy”, methodReference.get().getName());
}
}
[/source]

As you can see, this one is refactor-safe because the name of the method isn’t used anymore.

The full power of this with the oberserver pattern:

[source:java]
public class TestObserverAdvaced extends TestCase {
class Observer {
public Observer() {
}

private int events;

public void onEvent(String event) {
events++;
}

public int getEvents() {
return events;
}
}

interface CallBackRecorder<T> {
void onCallback(T target);
}

class Observed {
private Map<Object , Method> observers = new HashMap<Object , Method>();

public void addObserver(Object observer, Method method) {
observers.put(observer, method);
}

public void addObserver(Object observer, CallBackRecorder callMock) {
ObjectReference<Method> ref = new ObjectReference<Method>();
Object instance = MethodRecordingInvoker.createMethodRecordingInstance(observer, ref);
callMock.onCallback(instance);
addObserver(observer, ref.get());
}

public void notifyObservers(String event) throws IllegalAccessException, InvocationTargetException {
for(Map.Entry<Object , Method> entry: observers.entrySet()) {
Object observer = entry.getKey();
Method method = entry.getValue();
method.invoke(observer, event);
}
}
}

public void testOberserved() throws Exception{
Observed o = new Observed();
Observer observer = new Observer();

o.addObserver(observer, new CallBackRecorder() {
public void onCallback(Observer target) {
target.onEvent(“dummy”); // parameter value not used.
}
});

assertEquals(“Observer not initialized correclty”, 0, observer.getEvents());
o.notifyObservers(“arbitrary value passed”);
assertEquals(“Observer must be notified”, 1, observer.getEvents());
o.notifyObservers(“arbitrary value passed”);
assertEquals(“Observer must be notified”, 2, observer.getEvents());
}
}
[/source]

I must admit that there is another solution to the problem of callback interfaces using anonymous inner classes, too:

[source:java]
public class TestObserverWithInnerClass extends TestCase {
class ObserableFoo {
private List<FooObserver>observers = new LinkedList<FooObserver>();
void registerObserver(FooObserver observer) {
observers.add(observer);
}
void notifyObservers(String foo) {
for(FooObserver observer: observers) {
observer.onEvent(foo);
}
}
}

interface FooObserver {
void onEvent(String message);
}

public class BarWithInnerObservable {
private String message;

public BarWithInnerObservable(ObserableFoo observableFoo) {
observableFoo.registerObserver(new FooObserver() {
public void onEvent(String message) {
onEventImpl(message);
}
});
}
public void onEventImpl(String message) {
this.message = message;
}

public String getMessage() {
return message;
}
}

public void testWithCallBack() {
ObserableFoo foo = new ObserableFoo();
BarWithInnerObservable bar = new BarWithInnerObservable(foo);
foo.notifyObservers(“demo”);
assertEquals(“demo”, bar.getMessage());
}
}
[/source]

This implementation which abstains of making Bar implement any Observer interface, suffers from the famous inner-class-breaks-security-problem (see entry at c2 wiki). The reflection based solution above uses an inner class only the extract the method name.

More stuff to read:

[updated and fixed broken comment form]

help
conditions
Bear
copyright