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    }