/* JFox, the OpenSource J2EE Application Server
 *
 * Distributable under GNU LGPL license by gun.org
 * more details please visit http://www.huihoo.org/jfox
 */

package org.jfox.framework.extra;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.jfox.framework.Component;
import org.jfox.framework.ComponentException;
import org.jfox.framework.ComponentName;
import org.jfox.framework.ConstrComponentFactory;
import org.jfox.framework.Parameter;

/**
 * if use MethodInterceptable Component, must use this ComponentFacotry
 *
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 */

public class DynamicProxyInstantiatingComponentFactory extends ConstrComponentFactory {

    public DynamicProxyInstantiatingComponentFactory(Class implementation, Parameter[] constrParams) throws ComponentException {
        super(implementation, constrParams);
    }

    public DynamicProxyInstantiatingComponentFactory(ComponentName name, Parameter[] constrParams) throws ComponentException {
        super(name, constrParams);
    }

    public Component makeComponent() throws ComponentException {
        Component comp = super.makeComponent();
        return (Component) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                getInterfaces(comp.getClass()),
                new DPInvocationHandler(comp));
    }

    private class DPInvocationHandler implements InvocationHandler {
        private Component comp = null;
        private boolean isInterceptable = false;

        public DPInvocationHandler(Component comp) {
            this.comp = comp;
            if(comp instanceof MethodInterceptable) {
                isInterceptable = true;
            }
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if(isInterceptable) {
                ((MethodInterceptable) comp).preInvoke(method, args);
            }
            Object result = method.invoke(comp, args);
            if(isInterceptable) {
                result = ((MethodInterceptable) comp).postInvoke(method, result);
            }
            return result;
        }
    }

    /**
     * must get the interfaces of the superclass
     *
     * @param clazz
     */
    public static Class[] getInterfaces(Class clazz) {
        Set interfaces = new HashSet();
        getInterfaces(clazz, interfaces);
        return (Class[]) interfaces.toArray(new Class[interfaces.size()]);
    }

    private static void getInterfaces(Class clazz, Set interfaces) {
        while(clazz != null) {
            Class[] implemeted = clazz.getInterfaces();
            List implementedList = Arrays.asList(implemeted);
            interfaces.addAll(implementedList);
            clazz = clazz.getSuperclass();
        }
    }
}

