/* 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.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.rmi.RemoteException;
import java.net.InetAddress;

import javax.ejb.EJBMetaData;
import javax.ejb.EJBObject;
import javax.ejb.EJBHome;
import javax.ejb.HomeHandle;
import javax.ejb.EJBException;
import javax.ejb.EnterpriseBean;

import org.huihoo.jfox.system.ComponentSupport;
import org.huihoo.jfox.logging.Logger;
import org.jfox.ejb.deploy.EJBClassLoader;
import org.jfox.ejb.meta.EJBDescriptor;

/**
 * 提供 bulk 的公共方法
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 */

public abstract class BulkSupport extends ComponentSupport implements Bulk {
//  protected Logger logger = Logger.getLogger(getClass().getName());
  protected BulkMetaData bulkMeta = null;
//  protected Container container = null;

  // homeClassName 将作为ObjectId 的前缀
  protected String homeClassName = null;
  /**
   * EJBObject 接口中定义的方法
   * methodHashCode => Method Object
   */
  protected Map beanMethods = new HashMap();
  protected Map homeMethods = new HashMap();

  protected OIDGenerator oidGenerator = null;

  protected List interceptors = new ArrayList();

  private static String ipAddress = "127.0.0.1";
  static {
    try {
      ipAddress = InetAddress.getLocalHost().getHostAddress();
    }
    catch(Exception e){
//      e.printStackTrace();
    }

  };

  public BulkSupport(BulkMetaData meta) {
    this.bulkMeta = meta;
    logger = Logger.getLogger(bulkMeta.getBeanClass().getName());
    oidGenerator = OIDGenerator.newInstance(ipAddress,
                                              meta.getEJBMetaData().getHomeInterfaceClass().getName(),
                                              meta.getEJBMetaData().getRemoteInterfaceClass().getName());

    this.homeClassName = meta.getEJBMetaData().getHomeInterfaceClass().getName();
    homeMethods = introspectorInterfMethods(meta.getEJBMetaData().getHomeInterfaceClass());
    beanMethods = introspectorBeanMethods(meta.getBeanClass(),meta.getEJBMetaData().getRemoteInterfaceClass());
    beanMethods.putAll(introspectorInterfMethods(EJBObject.class));
    // setEJBHome , 以便 Container 能够将其绑定到 jndi
    ((EJBMetaDataImpl)meta.getEJBMetaData()).setEJBHome(createEJBHome());

  }

  public EJBClassLoader getEJBClassLoader() {
    return (EJBClassLoader)bulkMeta.getBeanClass().getClassLoader();
  }

  public EJBMetaData getEJBMetaData() throws RemoteException {
    return bulkMeta.getEJBMetaData();
  }

  public EJBHome getEJBHome() throws RemoteException {
    return bulkMeta.getEJBMetaData().getEJBHome();
  }

  public HomeHandle getHomeHandle() throws RemoteException {
    return new HomeHandleImpl(bulkMeta.getEJBDescriptor().getJndiName());
  }

  // call back the ejbCreate method
  public void ejbCreate(EnterpriseBean bean, String createMethod, Object[] args) throws RemoteException {
    char c = createMethod.charAt(0);
    String ejbCreateMethodName = "ejb" + Character.toUpperCase(c) + createMethod.substring(1);

    logger.debug(bean + " " + ejbCreateMethodName);
    Class[] signatures = null;
    if(args !=null && args.length !=0 ){
      signatures = new Class[args.length];
      for(int i=0;i<args.length;i++){
        signatures[i] = args[i].getClass();
      }
    }

    try {
      bean.getClass().getMethod(ejbCreateMethodName,signatures).invoke(bean,args);
    }
    catch(Exception e){
      throw new RemoteException(e.getMessage(),e);
    }
  }

  /**
   * 生成 EJBHome ,使用 DelegateInvocationHandler, 由客户来决定使用什么协议
   * @return
   */
  private EJBHome createEJBHome(){
    EJBHome home = (EJBHome)Proxy.newProxyInstance(bulkMeta.getEJBMetaData().getHomeInterfaceClass().getClassLoader(),
            new Class[]{bulkMeta.getEJBMetaData().getHomeInterfaceClass(), ExtendedEJBHome.class},
            new DelegateInvocationHandler(oidGenerator.getHomeObjectId(),
                    bulkMeta.getEJBDescriptor().getRemoteProtocol(),
                    bulkMeta.getEJBDescriptor().getLocalProtocol())
    );
    logger.debug("createEJBHome "+ homeClassName +" Successful");
    return home;
  }

  /**
   * 缓存在 EJBHome,EJBObject 接口中定义的对象
   * @param classObj
   * @return
   */
  protected Map introspectorInterfMethods(Class classObj) {
    Map homeMethods = new HashMap();
    Method[] methods = classObj.getMethods();
    for(int i=0; i<methods.length; i++){
        long hashCode = MethodHasher.getMethodHash(methods[i]);
        homeMethods.put(String.valueOf(hashCode), methods[i]);
        logger.debug("Cache method: " + hashCode + " => " + methods[i].getName());
    }
    return homeMethods;
  }

  /**
   * 缓存在 EnterpriseBean 类中定义的对象
   * 这些方法应该都是已经在 EJBObject 接口中已经定义的
   * @param beanClass
   * @return
   */
  protected Map introspectorBeanMethods(Class beanClass,Class remoteInterface) {
    Map methods = new HashMap();
    Method[] remoteMethods = remoteInterface.getDeclaredMethods();
    for(int i=0; i<remoteMethods.length; i++){
      Method _method = remoteMethods[i];
      try {
        Method method = beanClass.getMethod(_method.getName(),_method.getParameterTypes());
        long hashCode = MethodHasher.getMethodHash(method);
        methods.put(String.valueOf(hashCode), method);
        logger.debug("Cache business method: " +  hashCode + " => " + method.getName());
      }
      catch(Exception e){
        throw new EJBException(e);
      }
    }
    return methods;
  }


  public Method getHomeMethod(Invocation invocation) throws NoSuchMethodException {
    if(!homeMethods.containsKey(invocation.getMethodHash())) {
      throw new NoSuchMethodException("no such EJBHome method with method hash is " + invocation.getMethodHash());
    }
    return (Method)homeMethods.get(invocation.getMethodHash());
  }

  public Method getBeanMethod(Invocation invocation) throws NoSuchMethodException {
    if(!beanMethods.containsKey(invocation.getMethodHash())) {
      throw new NoSuchMethodException("no such EnterpriseBean method with method hash is " + invocation.getMethodHash());
    }
    return (Method)beanMethods.get(invocation.getMethodHash());
  }

  public EJBDescriptor getEJBDescriptor(){
    return bulkMeta.getEJBDescriptor();
  }

  protected void doInit() throws Exception {
    ContainerImpl.INITIAL_CONTEXT.bind(bulkMeta.getEJBDescriptor().getJndiName(),bulkMeta.getEJBMetaData().getEJBHome());
  }

  protected void doDestroy() throws Exception {
    ContainerImpl.INITIAL_CONTEXT.unbind(bulkMeta.getEJBDescriptor().getJndiName());
  }
}