/* JFox, the OpenSource J2EE Application Server
 *
 * Copyright (C) 2002 huihoo.com
 * Distributable under GNU LGPL license
 * See the GNU Lesser General Public License for more details.
 */

package org.jfox.ejb;

import java.lang.reflect.Method;

import org.jfox.ejb.invoker.local.LOCALContainerInvoker;
import org.jfox.ejb.invoker.local.LOCALContainerService;
import org.jfox.ejb.invoker.ContainerRemote;
import org.jfox.ejb.invoker.ContainerInvokerHelper;
import org.jfox.ejb.invoker.ClientContainerInvoker;
import org.jfox.ejb.meta.Protocol;

import javax.ejb.EJBException;

/**
 * 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 ObjectId objectId;
  private String defualtRemoteProtocol;
  private String defualtLocalProtocol;
  private ClientContainerInvoker protocolInvocationHandler = null;

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

  public ObjectId 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)) {
      String protocol = (String)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(String protocol) throws Exception{
    if(protocol.equalsIgnoreCase(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");
      }
      else {
        return new LOCALContainerInvoker(objectId,LOCALContainerService.getInstance());
      }
    }
    else {
      // will use jndi to lookup default JRMPContainerService
      ContainerRemote invoker = ContainerInvokerHelper.lookupContainerInvoker(protocol);
      return ContainerInvokerHelper.createClientInvocationHandler(protocol,objectId,invoker);
    }
  }

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

}