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

import org.jfox.ejb.Container;
import org.jfox.ejb.ContainerImpl;
import org.jfox.ejb.EJBMetaDataImpl;
import org.jfox.ejb.BulkMetaData;
import org.jfox.ejb.BulkMetaDataImpl;
import org.jfox.ejb.meta.EJBDescriptionException;
import org.jfox.ejb.meta.EJBDescriptor;
import org.jfox.ejb.meta.EJBMeta;
import org.jfox.ejb.meta.EJBBundle;
import org.jfox.ejb.meta.SessionBeanDescriptor;

import javax.ejb.EJBHome;
import javax.ejb.EJBObject;
import javax.ejb.EnterpriseBean;
import javax.ejb.EJBMetaData;
import java.net.URL;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;

/**
 *
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 */

// 原则:这个尽量多的做事情,这样可以让容器(Container) 少做事情
public class EJBDeployer extends DeployerSupport {
  private Container container = null;

  /**
   *
   * @param url ejb jar file url
   * @return
   * @throws java.io.IOException
   */
  protected synchronized EJBDescriptor[] doDeploy(URL url) throws IOException,EJBDescriptionException {
    EJBBundle enterpriseMeta = getEnterpriseMetaInfo(url);
    EJBDescriptor[] ejbMetas = (EJBDescriptor[])enterpriseMeta.getEjbs().toArray(new EJBDescriptor[0]);
    List metaInfos = new ArrayList(); // 保存成功部署的 EJBDescriptor
    EJBClassLoader loader = new EJBClassLoader(url,Thread.currentThread().getContextClassLoader());
    for(int i=0; i<ejbMetas.length; i++){
      EJBDescriptor metaInfo = deploy(loader,ejbMetas[i]);
      if(metaInfo != null){
        metaInfos.add(metaInfo);
      }
    }
    return (EJBDescriptor[])metaInfos.toArray(new EJBDescriptor[0]);
  }

  protected void doUndeploy(EJBDescriptor[] deployments) throws IOException {
    for(int i=0; i<deployments.length; i++){
      try {
        container.dropBulk(deployments[i].getHomeClassName());
        logger.debug("undeploy " + deployments[i].getEjbClassName() + " successful.");
      }
      catch(Exception e){
        logger.warn("undeploy " + deployments[i].getEjbClassName() + " failed.",e);
      }
    }
  }

  protected void doInit() throws Exception {
    container = ContainerImpl.getInstance();
  }

  protected void doDestroy() throws Exception {
    container = null;
  }

  /**
   * 成功部署,返回 EJBDescriptor,else return null;
   *
   * @param loader
   * @param ejbDescriptor
   */
  private EJBDescriptor deploy(ClassLoader loader, EJBDescriptor ejbDescriptor){
    try {

/*
      Class metaClass = loader.loadClass(metaClassName);
      if(!testMetaClass(metaClass)){
        logger.warn("Meta Class: " + metaClass + " not extends " + EJBMeta.class.getName());
        return null;
      }
*/

//      EJBMeta meta = (EJBMeta)metaClass.newInstance();
//      EJBDescriptor metaInfo = meta.getEJBDescriptor();

      Class homeClass = loader.loadClass(ejbDescriptor.getHomeClassName());
      if(!testHomeClass(homeClass)){
        logger.warn("Home Class: " + homeClass + " not extends " + EJBHome.class.getName());
        return null;
      }

      Class remoteClass = loader.loadClass(ejbDescriptor.getRemoteClassName());
      if(!testRemoteClass(remoteClass)){
        logger.warn("Bean Interface Class: " + remoteClass + " not extends " + EJBObject.class.getName());
        return null;
      }

      Class beanClass = loader.loadClass(ejbDescriptor.getEjbClassName());
      if(!testBeanClass(beanClass)){
        logger.warn("Bean Class: " + beanClass + " not extends " + EnterpriseBean.class.getName());
        return null;
      }

      try {
        Class metaClass = loader.loadClass(ejbDescriptor.getRemoteClassName()+"Meta");
        if(testMetaClass(metaClass)){
          EJBMeta ejbMeta = (EJBMeta)metaClass.newInstance();
          ejbDescriptor.setJndiName(ejbMeta.getJndiName());
          ejbDescriptor.setRemoteProtocol(ejbMeta.getRemoteProtocol());
          ejbDescriptor.setLocalProtocol(ejbMeta.getLocalProtocol());
          logger.warn("EJBMeta " + ejbMeta.getRemoteProtocol());
        }
        else {
          logger.warn("Meta Interface Class: " + metaClass + " not extends " + EJBMeta.class.getName());
        }
      }
      catch(ClassNotFoundException ignore){ }

      EJBMetaData ejbMetaData = null;
      if(ejbDescriptor.getType().equals(EJBDescriptor.SESSION_BEAN)) { // is SessionBean
        ejbMetaData = new EJBMetaDataImpl(homeClass,remoteClass,true, ((SessionBeanDescriptor)ejbDescriptor).isStateless());
      }
      else { // Maybe EntityBean, but not support now
        ejbMetaData = new EJBMetaDataImpl(homeClass,remoteClass,true, false);
      }

      // will create a ejb bulk and bind it's home to jndi
      BulkMetaData bulkMeta = new BulkMetaDataImpl(ejbMetaData, ejbDescriptor, beanClass);
      container.createBulk(bulkMeta);
      logger.info("deploy bean [ " + beanClass.getName() + " ] successful");
      return ejbDescriptor;
    }
    catch(Exception e){
      logger.error("deploy " + ejbDescriptor.getEjbClassName() + " failed! ",e);
      return null;
    }
  }

  private boolean testHomeClass(Class homeClass){
    return EJBHome.class.isAssignableFrom(homeClass);
  }

  private boolean testRemoteClass(Class remoteClass){
    return EJBObject.class.isAssignableFrom(remoteClass);
  }

  private boolean testBeanClass(Class beanClass) {
    return EnterpriseBean.class.isAssignableFrom(beanClass);
  }

  private boolean testMetaClass(Class metaClass) {
    return EJBMeta.class.isAssignableFrom(metaClass);
  }
  
  private EJBBundle getEnterpriseMetaInfo(URL url) throws EJBDescriptionException {
    return new EJBBundle(url);
  }

}