/* 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.ejb;

import java.lang.reflect.Method;
import javax.ejb.EJBException;

import org.jfox.ejb.connector.ClientContainerInvoker;
import org.jfox.ejb.connector.ConnectorHelper;
import org.jfox.ejb.connector.ContainerRemote;
import org.jfox.ejb.connector.local.LOCALContainerInvoker;
import org.jfox.ejb.connector.local.LOCALContainerService;
import org.jfox.ejb.meta.Protocol;
import org.jfox.framework.Registry;

/**
 * DelegateInvocationHandler 是一个与协议无关的InvocationHandler，最初和 EJBHome 绑定到 jndi 上，
 * 在 ejbHome.create 的时候，将调用 setProtocolInvocationHandler 设置一个具体协议的 InvocationHandler
 *
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 */

public class DelegateInvocationHandler implements ClientContainerInvoker {

    // remote object identity
    private EJBObjectId objectId;
    private Protocol defualtRemoteProtocol;
    private Protocol defualtLocalProtocol;
    private ClientContainerInvoker protocolInvocationHandler = null;

    public DelegateInvocationHandler(EJBObjectId objectId, Protocol remoteProtocol, Protocol localProtocol) {
        this.objectId = objectId;
        this.defualtRemoteProtocol = remoteProtocol;
        this.defualtLocalProtocol = localProtocol;
    }

    public EJBObjectId getObjectId() {
        return objectId;
    }

    public ClientContainerInvoker getProtocolInvocationHandler() {
        return protocolInvocationHandler;
    }

    public void setProtocolInvocationHandler(ClientContainerInvoker protocolInvocationHandler) {
        this.protocolInvocationHandler = protocolInvocationHandler;
    }

    /**
     * 可以实现通用的 Object 方法，比如：toString, hashCode 等
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /*
         * 选择一种协议的 InvocationHandler，由 useProtocol 指定
         */
        if(method.equals(ExtendedEJBHomeMethod.UseProtocol)) {
            Protocol protocol = (Protocol) args[0];
            try {
                protocolInvocationHandler = getInvocationHandlerByProtocol(protocol);
            }
            catch(EJBException e) {
                throw e;
            }
            catch(Exception e) {
                throw new EJBException("protocol " + protocol + " not supported!", e);
            }
            return null; // 调用的是　useProtocol 方法，在这里即返回
        }

        // 还没有设置 protocolInvocationHandler，使用默认的 InvocationHandler
        if(protocolInvocationHandler == null) {
            synchronized(this) {
                protocolInvocationHandler = getInvocationHandlerByDefault();
            }
        }
        // delegate invocation to 具体协议的 InvocationHandler
        return protocolInvocationHandler.invoke(proxy, method, args);
    }

    private ClientContainerInvoker getInvocationHandlerByProtocol(Protocol protocol) throws Exception {
        if(protocol.equals(Protocol.LOCAL)) {
            // 使用　Local 协议的时候，需要特殊对待，首先确定Bean和Container在同一JVM内
/*      if(!ObjectId.CONTAINER_IDENTITY.equals(objectId.getIdentity())){
        throw new EJBException("can not use local protocol, because the bean is not in same JVM as Container");
      }*/

            if(!objectId.isCurrentVM()) {
                throw new EJBException("can not use local protocol, because the bean is not in same JVM as Container");
            }
            else {
//        return new LOCALContainerInvoker(objectId,LOCALContainerService.getInstance());
                return new LOCALContainerInvoker(objectId, (ContainerRemote) (Registry.getInstance().getFirstComponentMetabyImplemetation(LOCALContainerService.class).getComponentInstance()));
            }
        }
        else {
            // will use jndi to lookup default JRMPContainerService
            ContainerRemote invoker = ConnectorHelper.lookupContainerInvoker(protocol);
            return ConnectorHelper.createClientInvocationHandler(protocol, objectId, invoker);
        }
    }

    private ClientContainerInvoker getInvocationHandlerByDefault() throws Exception {
        // CONTAINER_IDENTITY 和 要调用的 ejb 的 ObjectId 的 Identity 一致，表明该 ejb 就在此容器中
//    if(ObjectId.CONTAINER_IDENTITY.equals(objectId.getIdentity())){
        if(objectId.isCurrentVM()) {
            // 对于 EJB <-> EJB 的访问，默认使用　local 协议
            return getInvocationHandlerByProtocol(defualtLocalProtocol);
        }
        else { // 对于远程调用，默认使用 jrmp 协议
            return getInvocationHandlerByProtocol(defualtRemoteProtocol);
        }
    }

}