Wednesday, September 07, 2005
some real code..
All of the code in the previous post was typed on the fly, and I would not expect it to compile. Before I get any further into refactoring it, here are complete working versions:
MyInvocationHandler.java
MethodSig.java
HashKey.java
MyInvocationHandler.java
/*
* MyInvocationHandler.java
*
* Created on September 7, 2005, 1:35 PM
*/
import java.lang.reflect.*;
import java.util.*;
/**
* Test code for my blog postings about InvocationHandlers.
*
* @author jRobertson
*/
public class MyInvocationHandler implements InvocationHandler {
/**
* The interface for the class that we are proxying/decorating/delegating-to.
*/
public static interface SomeInterface {
int foo(int x);
void bar(String s);
boolean baz();
void quux();
}
/**
* An implementation of this interface.
*/
public static class SomeImpl implements SomeInterface {
public int foo(int x) {
System.out.println("SomeImpl.foo");
return x+1;
}
public void bar(String s) {
System.out.println("SomeImpl.bar");
}
public boolean baz() {
System.out.println("SomeImpl.baz");
return true;
}
public void quux() {
System.out.println("SomeImpl.quux");
}
}
private Object realObject;
private Map methodsMap = new HashMap();
private void addMethod(Method m) {
methodsMap.put( new MethodSig(m), m);
}
public MyInvocationHandler(Object realObject) {
this.realObject = realObject;
Method[] methods = getClass().getMethods();
for(int i =0; i<methods.length; i++) {
addMethod( methods[i]);
}
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Method myMethod = (Method) methodsMap.get( new MethodSig(method));
if( myMethod != null ) {
// if it's in the map, run the one from the map
return myMethod.invoke(this,args);
} else {
// otherwise just run the method on the original object
return method.invoke(realObject,args);
}
}
/**
* Replacement method that overrides the matching foo() method
*/
public int foo(int x) {
System.out.println("MyInvocationHandler.foo");
return ((SomeInterface) realObject).foo(x);
}
/**
* Replacement method that overrides the matching bar() method
*/
public void bar(String s) {
// do nothing! hahaha
}
/**
* Replacement method that overrides the matching baz() method
*/
public boolean baz() {
throw new RuntimeException("you can't call baz()!");
}
// notice that we have no replacement method for quux()
/**
* Main method to serve as a basic test.
*/
public static void main(String args[]) throws Throwable {
SomeInterface si = (SomeInterface)
Proxy.newProxyInstance(MyInvocationHandler.class.getClassLoader(), new Class[]{SomeInterface.class}, new MyInvocationHandler(new SomeImpl()));
// should print:
// MyInvocationHandler.foo
// SomeImpl.foo
si.foo(1);
// should print NOTHING, since the proxy should suppress the call to the object
si.bar("test");
// should print "SomeImpl.quux" because we don't declare a replacement method
si.quux();
// should throw!
try {
si.baz();
} catch (Throwable t) {
System.out.println(t);
}
}
}
MethodSig.java
/*
* MethodSig.java
*
* Created on September 7, 2005, 1:36 PM
*/
import java.lang.reflect.Method;
/**
* Hash key for a map of methods based on method name and parameters.
*/
public class MethodSig extends HashKey {
public MethodSig(Method m) {
super( m.getName(),
java.util.Arrays.asList( m.getParameterTypes()) );
}
}
HashKey.java
/*
* HashKey.java
*
* Created on February 8, 2005, 9:54 AM
*/
import java.util.*;
/**
* A convenience class for easily making hashmap
* keys that involve multiple objects. Essentially
* just a read-only collection of Objects, except for
* the 9 overloaded constructors that allow you to
* write code like this:
* <pre>
*
* map.put( new HashKey("foo","bar"), someValue);
*
* Object v = map.get( new HashKey("foo","bar"));
*
* </pre>
*
* Can be useful as a base class for local hash key classes:
*
* <pre>
*
* public class MyHashKey extends HashKey {
* public MyHashKey(SomeClass thing) {
* super( thing.getFoo(), thing.getBar());
* }
* }
*
* </pre>
*
* @author jRobertson
*/
public class HashKey extends AbstractCollection implements Collection, java.io.Serializable {
private Collection keys;
/** Creates a new instance of HashKey */
public HashKey(Object key1) {
this( new Object[]{ key1 });
}
/** Creates a new instance of HashKey */
public HashKey(Object key1, Object key2) {
this( new Object[]{ key1, key2});
}
/** Creates a new instance of HashKey */
public HashKey(Object key1, Object key2, Object key3) {
this( new Object[]{ key1,key2,key3});
}
/** Creates a new instance of HashKey */
public HashKey(Object key1, Object key2, Object key3, Object key4) {
this( new Object[]{ key1,key2,key3,key4});
}
/** Creates a new instance of HashKey */
public HashKey(Object key1, Object key2, Object key3, Object key4, Object key5) {
this( new Object[]{ key1,key2,key3,key4,key5});
}
/** Creates a new instance of HashKey */
public HashKey(Object key1, Object key2, Object key3, Object key4, Object key5, Object key6) {
this( new Object[]{ key1,key2,key3,key4,key5,key6});
}
/** Creates a new instance of HashKey */
public HashKey(Object key1, Object key2, Object key3, Object key4, Object key5, Object key6, Object key7) {
this( new Object[]{ key1,key2,key3,key4,key5,key6,key7});
}
/** Creates a new instance of HashKey */
public HashKey(Object key1, Object key2, Object key3, Object key4, Object key5, Object key6, Object key7, Object key8) {
this( new Object[]{ key1,key2,key3,key4,key5,key6,key7,key8});
}
/** Creates a new instance of HashKey */
public HashKey(Object key1, Object key2, Object key3, Object key4, Object key5, Object key6, Object key7, Object key8, Object key9) {
this( new Object[]{ key1,key2,key3,key4,key5,key6,key7,key8,key9});
}
/** Creates a new instance of HashKey */
public HashKey(Object[] keys) {
this( Arrays.asList(keys));
}
/** Creates a new instance of HashKey */
public HashKey(Collection keysToAdd) {
keys = keysToAdd;
}
public boolean equals(Object obj) {
if( !(obj instanceof HashKey) )
return false;
HashKey that = (HashKey) obj;
return keys.equals( that.keys);
}
public String toString() {
return keys.toString();
}
public int hashCode() {
return keys.hashCode();
}
public int size() {
return keys.size();
}
public Iterator iterator() {
return keys.iterator();
}
}