Thursday, August 16, 2007

Make use of Java dynamic proxy

I didn't know about Java dynamic proxy before but finally got a chance to learn and found a good use for it. The test I was writing had the smell of repeating code:

private void testPutWithoutSynch(params...) throws Exception {
...

map.put("k1", "v1");

assertions...
}

private void testPutWithSynch(params...) throws Exception {
...

synchronized(map) {
map.put("k1", "v1");
}

assertions...
}


The test pattern repeated with other operations that you can do with Map like puttAll, remove, etc... Do_set_up() is actually some pre setup steps that I need to perform according to parameters list. As you can see, I have to repeat the test method twice for each Map operation I want to test, with and without the synchronize block.

So there is this code smell and I didn't know get rid of it. Then Tim Eck, my coworker, showed me how to use Java proxy. The idea is to wrap my map in a proxy, and use reflection to invoke methods of the map. By using a invocation handler, I can have a variable to control whether or not I want to use synchronize for that particular operation.

class Handler implements InvocationHandler {
private final Map map;
private boolean useSynch = false;

public Handler(Map map) {
this.map = map;
}

public void setUseSynch(boolean flag) {
this.useSynch = flag;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (useSynch) {
return invokeWithSynch(method, args);
} else {
return method.invoke(map, args);
}
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}

private Object invokeWithSynch(Method method, Object[] args) throws Throwable {
synchronized (map) {
return method.invoke(map, args);
}
}
}


Then the proxy can be created like this:

Map map = new HashMap();
Handler handler = new Handler(map);
Map mapProxy = (Map)Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { Map.class }, handler);


With that my test methods can be reduced to:

private void testPut(params..., boolean useSynch) throws Exception {
do_set_up(params)

handler.setUseSynch(useSynch);
mapProxy.put("k1", "v1");

assertions depends on useSynch
}


Then I just need to call this method twice and accomplish the same thing.

testPut(params..., false);
testPut(params..., true);


Pretty neat huh? Java proxy is nice tool to know.

8 comments:

Taylor said...
This comment has been removed by the author.
Taylor said...

If your InvocationHandler had a static method called createProxy, you could push the Proxy creation into the Handler and then remove it from your application code.

Something like:

Map mapProxy = InvocationHandler.createProxy(map, [true | false]);

Jonas said...

Thanks for your post. DPs are powerful, allows AOPish style of development.

Don't mean to self promote, but this is a pretty cool usage sf DPs :-)

http://jonasboner.com/2007/07/27/the-power-of-sockets-and-dynamic-proxies/

/Jonas

Hung Huynh said...

Nice tip Taylor. Thanks. Thought I need to be able to flip the bit useSynch on the fly in my test (since I'm testing the same map)

Jim d said...

This is a nitpick, but aren't boolean method parms a code smell also? That aside, I've been meaning to learn about dynamic proxies, so thanks for the article.

Jason said...

cool article on dynamic proxies... but there's a much simpler way to solve your problem:

Map map = ...

if (testUsingSync)
map = Collections.synchronizedMap(map);

do_set_up(params);
map.put("k1","v1");

assertions...

NABH said...

I really appreciate your post and you explain each and every point very well.Thanks for sharing this information.And I’ll love to read your next post too.
Regards:
NABH

Manish kumar said...

hmm... nice post. for an introduction on Dynamic Proxies, refer this blog also :
Java Rendezvous