/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.ejb3;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.management.ObjectName;
import javax.security.jacc.PolicyConfiguration;
import org.jboss.deployment.DeploymentInfo;
import org.jboss.ejb3.security.JaccHelper;
import org.jboss.ejb3.service.ServiceManager;
import org.jboss.logging.Logger;
import org.jboss.mx.util.MBeanProxy;
import org.jboss.mx.util.MBeanProxyCreationException;
import org.jboss.mx.util.MBeanProxyExt;
import org.jboss.remoting.transport.ConnectorMBean;
import org.jboss.system.ServiceControllerMBean;
import org.jboss.system.ServiceMBean;

/**
 * Comment
 *
 * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
 * @version $Revision: 1.3.2.3 $
 */
public class Ejb3JmxDeployment extends Ejb3Deployment
{
   private ServiceControllerMBean serviceController;
   private DeploymentInfo deploymentInfo;

   private static final Logger log = Logger.getLogger(Ejb3JmxDeployment.class);

   private static class JmxDeploymentUnit implements DeploymentUnit
   {
      private DeploymentInfo deploymentInfo;

      URL extractDescriptorUrl(String resource)
      {
         String urlStr = deploymentInfo.url.getFile();
         // However the jar must also contain at least one ejb-jar.xml
         try
         {
            URL dd = deploymentInfo.localCl.findResource(resource);
            if (dd == null)
            {
               return null;
            }

            // If the DD url is not a subset of the urlStr then this is coming
            // from a jar referenced by the deployment jar manifest and the
            // this deployment jar it should not be treated as an ejb-jar
            if( deploymentInfo.localUrl != null )
            {
               urlStr = deploymentInfo.localUrl.toString();
            }

            String ddStr = dd.toString();
            if ( ddStr.indexOf(urlStr) >= 0 )
            {
               return dd;
            }
         }
         catch( Exception ignore )
         {
         }
         return null;
      }

      public URL getPersistenceXml()
      {
         return extractDescriptorUrl("META-INF/persistence.xml");
      }

      public URL getEjbJarXml()
      {
         return extractDescriptorUrl("META-INF/ejb-jar.xml");
      }

      public URL getJbossXml()
      {
         return extractDescriptorUrl("META-INF/jboss.xml");
      }

/*
      public URL getPersistenceXml()
      {
         return getResourceLoader().getResource("META-INF/persistence.xml");
      }

      public URL getEjbJarXml()
      {
         return getResourceLoader().getResource("META-INF/ejb-jar.xml");
      }

      public URL getJbossXml()
      {
         return getResourceLoader().getResource("META-INF/jboss.xml");
      }
*/
      public List<Class> getClasses()
      {
         return null;
      }

      public JmxDeploymentUnit(DeploymentInfo deploymentInfo)
      {
         this.deploymentInfo = deploymentInfo;
      }

      public ClassLoader getClassLoader()
      {
         return deploymentInfo.ucl;
      }

      public ClassLoader getResourceLoader()
      {
         return deploymentInfo.localCl;
      }

      public String getShortName()
      {
         return deploymentInfo.shortName;
      }

      public URL getUrl()
      {
         return deploymentInfo.url;
      }

      public String getDefaultEntityManagerName()
      {
         String url = getUrl().toString();
         String name = url.substring(url.lastIndexOf('/') + 1, url.lastIndexOf('.'));
         return name;
      }

      public Map getDefaultPersistenceProperties()
      {
         try
         {
            EJB3DeployerMBean deployer = (EJB3DeployerMBean) MBeanProxy.get(EJB3DeployerMBean.class, EJB3DeployerMBean.OBJECT_NAME,
                                                                            deploymentInfo.getServer());

            return deployer.getDefaultProperties();
         }
         catch (MBeanProxyCreationException e)
         {
            throw new RuntimeException(e);
         }
      }


      public Hashtable getJndiProperties()
      {
         return null;
      }

   }


   public Ejb3JmxDeployment(DeploymentInfo di)
   {
      super(new JmxDeploymentUnit(di));
      serviceController = (ServiceControllerMBean) MBeanProxyExt.create(ServiceControllerMBean.class, ServiceControllerMBean.OBJECT_NAME,
                                                                        di.getServer());
      this.deploymentInfo = di;
   }

   protected PolicyConfiguration createPolicyConfiguration() throws Exception
   {
      return JaccHelper.initialiseJacc(getJaccContextId());

   }

   protected void initializeJacc(PolicyConfiguration pc, DeploymentUnit unit)
   {
      try
      {
         JaccHelper.putJaccInService(pc, deploymentInfo);
      }
      catch (Exception e)
      {
         throw new RuntimeException(e);
      }

   }

   @Override
   public String getDefaultClientBinding()
   {
      try
      {
         ObjectName on = new ObjectName("jboss.remoting:type=Connector,name=DefaultEjb3Connector,handler=ejb3");
         ConnectorMBean connector = (ConnectorMBean) MBeanProxyExt.create(ConnectorMBean.class, on, deploymentInfo.getServer());
         return connector.getInvokerLocator();
      }
      catch (Exception e)
      {
         throw new RuntimeException(e);
      }
   }

   @Override
   protected void registerContainer(Container container, Collection depends) throws Exception
   {
      super.registerContainer(container, depends);

      ObjectName on = container.getObjectName();
      deploymentInfo.getServer().registerMBean(container, on);
      deploymentInfo.mbeans.add(on);
      serviceController.create(on, depends);
   }

   @Override
   public void create() throws Exception
   {
      ClassLoader old = Thread.currentThread().getContextClassLoader();
      try
      {
         Thread.currentThread().setContextClassLoader(deploymentInfo.ucl);
         super.create();
      }
      finally
      {
         Thread.currentThread().setContextClassLoader(old);
      }
   }

   @Override
   public void start() throws Exception
   {
      ClassLoader old = Thread.currentThread().getContextClassLoader();
      try
      {
         Thread.currentThread().setContextClassLoader(deploymentInfo.ucl);
         super.start();
         ArrayList<ObjectName> serviceBeans = new ArrayList<ObjectName>();
         Iterator it = containers.keySet().iterator();
         while (it.hasNext())
         {
            ObjectName on = (ObjectName) it.next();
            Container con = (Container) containers.get(on);
            if (con instanceof ServiceManager)
            {
               serviceBeans.add(on);
            }
            else
            {
               serviceController.start(on);
               if (log.isDebugEnabled())
                  log.debug("startService, starting container: " + con.getEjbName());
            }
         }

         for (ObjectName on : serviceBeans)
         {
            serviceController.start(on);
         }
      }
      finally
      {
         Thread.currentThread().setContextClassLoader(old);
      }
   }


   @Override
   public void stop() throws Exception
   {
      ClassLoader old = Thread.currentThread().getContextClassLoader();
      try
      {
         Thread.currentThread().setContextClassLoader(deploymentInfo.ucl);
         Iterator it = containers.keySet().iterator();
         while (it.hasNext())
         {
            ObjectName on = (ObjectName) it.next();
            serviceController.stop(on);
            if (log.isDebugEnabled())
            {
               Container con = (Container) containers.get(on);
               log.debug("stopService, stopping container: " + con.getEjbName());
            }
         }
         super.stop();
      }
      finally
      {
         Thread.currentThread().setContextClassLoader(old);
      }
   }

   @Override
   public void destroy() throws Exception
   {
      ClassLoader old = Thread.currentThread().getContextClassLoader();
      try
      {
         Thread.currentThread().setContextClassLoader(deploymentInfo.ucl);
         super.destroy();

         Iterator it = containers.keySet().iterator();
         while (it.hasNext())
         {
            ObjectName on = (ObjectName) it.next();
            Container con = (Container) containers.get(on);
            serviceController.destroy(on);
            if (log.isDebugEnabled())
            {
               log.debug("destroyService, destroying container: "
                         + con.getEjbName());
            }
            int conState = ((ServiceMBean) con).getState();
            // If the container was registered with the mbeanserver, remove it
            if (conState != ServiceMBean.UNREGISTERED)
            {
               try
               {
                  serviceController.remove(on);
               }
               catch (Throwable e)
               {
                  log.error("unexpected exception removing Container: " + on, e);
               } // end of try-catch
            }
         }
      }
      finally
      {
         Thread.currentThread().setContextClassLoader(old);
      }
   }

}
