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

/*
* 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();
}


}

Comments: