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 }