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:
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]);
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
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)
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.
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...
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
hmm... nice post. for an introduction on Dynamic Proxies, refer this blog also :
Java Rendezvous
Post a Comment