001    /*
002    @license.text@
003     */
004    package biz.hammurapi.util;
005    
006    import java.lang.reflect.InvocationHandler;
007    import java.lang.reflect.Method;
008    import java.lang.reflect.Proxy;
009    
010    /**
011     * Creates a proxy which combines several classes and sequentially searches for 
012     * matching method to invoke. This allows to implement lazy instantiation for
013     * situations where you have part of object data in cheaply accessible storage (cache or DB)
014     * and the other part in expensively accessible storage (e.g. XML file shall be
015     * parsed to access all object data).
016     * @author Pavel Vlasov
017     * @version $Revision: 1.1 $
018     */
019    public class CompositeProxyFactory {
020            
021            /**
022             * @author Pavel Vlasov
023             * @version $Revision: 1.1 $
024             */
025            public interface TargetFactory {
026                    
027                    /**
028                     * @return target class for the proxy to know which methods it implements.
029                     * You might want to return only one of class interfaces or base classes
030                     * to limit number of exposed methods.
031                     */
032                    Class getTargetClass();
033                    
034                    /**
035                     * @return target itself to perform invocation.
036                     */
037                    Object getTarget();
038            }
039            
040            private ClassLoader classLoader;
041    
042            /**
043             * Creates factory which uses default classloader. 
044             */
045            public CompositeProxyFactory() {
046                    super();
047                    classLoader=getClass().getClassLoader();
048            }
049            
050            /**
051             * Creates factory whith specified classloader which will be
052             * passed to createProxy(ClassLoader, Class[], TargetFactory[]) 
053             */
054            public CompositeProxyFactory(ClassLoader classLoader) {
055                    super();
056                    this.classLoader=classLoader;
057            }
058            
059            public static Object createProxy(ClassLoader classLoader, Class[] interfaces, final TargetFactory[] targetFactories) {
060                    return Proxy.newProxyInstance(
061                                    classLoader, 
062                                    interfaces, 
063                                    new InvocationHandler() {
064                                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
065                                                    for (int i=0; i<targetFactories.length; i++) {
066                                                            try {
067                                                                    return targetFactories[i]
068                                                                            .getTargetClass()
069                                                                            .getMethod(method.getName(), method.getParameterTypes())
070                                                                            .invoke(targetFactories[i].getTarget(), args);
071                                                            } catch (NoSuchMethodException e) {
072                                                                    // Ignore it.
073                                                            }
074                                                    }
075                                                    
076                                                    throw new NoSuchMethodException("Method not found in targets: "+method);
077                                            }                                       
078                                    });
079                    
080            }
081            
082            public Object createProxy(Class[] interfaces, TargetFactory[] targetFactories) {
083                    return createProxy(classLoader, interfaces, targetFactories);           
084            }       
085    }