001 /*
002 @license.text@
003 */
004 package biz.hammurapi.wrap;
005
006 import java.lang.reflect.InvocationHandler;
007 import java.lang.reflect.Method;
008 import java.lang.reflect.Proxy;
009 import java.util.ArrayList;
010 import java.util.Arrays;
011 import java.util.Collection;
012 import java.util.HashMap;
013 import java.util.HashSet;
014 import java.util.Iterator;
015 import java.util.Map;
016 import java.util.Set;
017
018 /**
019 * Wraps Jsel object such as the underlying object becomes garbage collection
020 * eligible and will be transparently restored from the database and source files
021 * on demand
022 * @author Pavel Vlasov
023 * @version $Revision: 1.2 $
024 */
025 public abstract class WrapperHandler implements InvocationHandler {
026 private Object proxy;
027
028 /**
029 * Subclasses can add more classes to wrap.
030 */
031 protected Collection classesToWrap=new ArrayList();
032
033 private static Map interfaceMap=new HashMap();
034
035 public WrapperHandler(Object master) {
036 proxy=Proxy.newProxyInstance(master.getClass().getClassLoader(), getClassInterfaces(master.getClass()), this);
037
038 classesToWrap.add(Collection.class);
039 classesToWrap.add(Map.class);
040 }
041
042 public Object getProxy() {
043 return proxy;
044 }
045
046 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
047 Object[] unwrappedArgs;
048 if (args!=null && args.length!=0) {
049 unwrappedArgs=new Object[args.length];
050 for (int i=0; i<unwrappedArgs.length; i++) {
051 unwrappedArgs[i]=args[i];
052 if (Proxy.isProxyClass(args[i].getClass())) {
053 InvocationHandler handler=Proxy.getInvocationHandler(proxy);
054 if (handler instanceof WrapperHandler) {
055 unwrappedArgs[i]=((WrapperHandler) handler).getMaster();
056 if (unwrappedArgs[i]==null) {
057 throw new IllegalStateException("Unwrapped object is null for parameter "+i+" in method "+method);
058 }
059 }
060 }
061 }
062 } else {
063 unwrappedArgs=args;
064 }
065
066 Object master = getMaster();
067 if (master==null) {
068 throw new NullPointerException("Master object is null");
069 }
070
071 Object ret=method.invoke(master, unwrappedArgs);
072
073 return wrap(ret, classesToWrap);
074 }
075
076 /**
077 * Convenience method.
078 * @param toWrap Object to wrap
079 * @param classesToWrap collection of classes that shall be wrapped. Wrappable is always wrapped. Can be null.
080 * @return Proxy object for toWrap if toWrap is either Wrappable or instance of one of classes from classesToWrap,
081 * toWrap otherwise
082 * @throws Throwable
083 */
084 public static Object wrap(Object toWrap, Collection classesToWrap) throws Throwable {
085 if (toWrap instanceof Wrappable) {
086 return ((Wrappable) toWrap).getProxy();
087 }
088
089 if (classesToWrap!=null) {
090 Iterator it=classesToWrap.iterator();
091 while (it.hasNext()) {
092 if (((Class) it.next()).isInstance(toWrap)) {
093 return new StrongWrapperHandler(toWrap).getProxy();
094 }
095 }
096 }
097
098 return toWrap;
099 }
100
101 public static Object wrap(Object toWrap) throws Throwable {
102 return wrap(toWrap, null);
103 }
104
105 protected abstract Object getMaster() throws Throwable;
106
107 /**
108 * @param sourceClass
109 * @return all interfaces implemented by this class
110 */
111 public static Class[] getClassInterfaces(Class sourceClass) {
112 synchronized (interfaceMap) {
113 Class[] ret=(Class[]) interfaceMap.get(sourceClass);
114 if (ret==null) {
115 Set set=new HashSet();
116 getClassInterfaces(sourceClass, set);
117 ret = (Class[]) new ArrayList(set).toArray(new Class[set.size()]);
118 interfaceMap.put(sourceClass, ret);
119 }
120 return ret;
121 }
122 }
123
124 /**
125 * @param sourceClass
126 */
127 private static void getClassInterfaces(Class sourceClass, Collection set) {
128 if (sourceClass!=null) {
129 Class[] interfaces = sourceClass.getInterfaces();
130 for (int i=0; interfaces!=null && i<interfaces.length; i++) {
131 getClassInterfaces(interfaces[i]);
132 }
133 getClassInterfaces(sourceClass.getSuperclass(), set);
134 set.addAll(Arrays.asList(interfaces));
135 }
136 }
137 }