/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.management.j2ee;

import org.jboss.invocation.InvocationStatistics;
import org.jboss.logging.Logger;
import org.jboss.management.j2ee.statistics.CountStatisticImpl;
import org.jboss.management.j2ee.statistics.EJBStatsImpl;
import org.jboss.management.j2ee.statistics.TimeStatisticImpl;

import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.j2ee.statistics.Stats;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

/**
 * Root class of the JBoss JSR-77.3.10 EJB model
 *
 * @author  <a href="mailto:andreas@jboss.org">Andreas Schaefer</a>
 * @author  <a href="mailto:scott.stark@jboss.org">Scott Stark</a>
 * @author  <a href="mailto:thomas.diesler@jboss.org">Thomas Diesler</a>
 * @version $Revision$
 */
public abstract class EJB extends J2EEManagedObject
   implements EJBMBean
{
   // Constants -----------------------------------------------------
   public static final int ENTITY_BEAN = 0;
   public static final int STATEFUL_SESSION_BEAN = 1;
   public static final int STATELESS_SESSION_BEAN = 2;
   public static final int MESSAGE_DRIVEN_BEAN = 3;

   // Attributes ----------------------------------------------------
   
   /** The logger */
   private static Logger log = Logger.getLogger(EJB.class);

   /**
    * The ObjectName of the ejb container MBean
    */
   protected ObjectName ejbContainerName;
   
   protected String jndiName;
   protected String localJndiName;

   // Static --------------------------------------------------------

   /**
    * Create a JSR77 EJB submodel.
    *
    * @param mbeanServer      the MBeanServer to use for mbean creation
    * @param ejbModuleName    the name of the JSR77 EJBModule mbean
    * @param ejbContainerName the name of the JBoss ejb container mbean
    * @param ejbType          an EJB.XXX_BEAN type constant value
    * @param ejbName          the bean ejb-name
    * @param jndiName         the jndi name of the remote home binding if one exists, or null
    * @param localJndiName    the jndi name of the local home binding if one exists, or null
    * @return the ObjectName of the JSR77 EJB mbean
    */
   public static ObjectName create(MBeanServer mbeanServer, ObjectName ejbModuleName,
      ObjectName ejbContainerName, int ejbType, String ejbName,
      String jndiName, String localJndiName)
   {
      try
      {
         // Now create the EJB mbean
         EJB ejb = null;
         switch (ejbType)
         {
            case ENTITY_BEAN:
               ejb = new EntityBean(ejbName, ejbModuleName, ejbContainerName,
                 jndiName, localJndiName);
               break;
            case STATEFUL_SESSION_BEAN:
               ejb = new StatefulSessionBean(ejbName, ejbModuleName,
                  ejbContainerName, jndiName, localJndiName);
               break;
            case STATELESS_SESSION_BEAN:
               ejb = new StatelessSessionBean(ejbName, ejbModuleName,
                  ejbContainerName, jndiName, localJndiName);
               break;
            case MESSAGE_DRIVEN_BEAN:
               ejb = new MessageDrivenBean(ejbName, ejbModuleName,
                  ejbContainerName, localJndiName);
               break;
         }

         ObjectName jsr77Name = ejb.getObjectName();
         mbeanServer.registerMBean(ejb, jsr77Name);
         log.debug("Created JSR-77 EJB: " + jsr77Name);
         return jsr77Name;
      }
      catch (Exception e)
      {
         log.debug("Could not create JSR-77 EJB: " + ejbName, e);
         return null;
      }
   }

   public static void destroy(MBeanServer mbeanServer, ObjectName jsr77Name)
   {
      try
      {
         // Now remove the EJB
         mbeanServer.unregisterMBean(jsr77Name);
         log.debug("Destroyed JSR-77 EJB: " + jsr77Name);
      }
      catch (javax.management.InstanceNotFoundException ignore)
      {
      }
      catch (Exception e)
      {
         log.debug("Could not destroy JSR-77 EJB: " + jsr77Name, e);
      }
   }

   // Constructors --------------------------------------------------

   /**
    * Create a EJB model
    *
    * @param ejbType          the EJB.EJB_TYPES string
    * @param ejbName          the ejb-name from the deployment
    * @param ejbModuleName    the JSR-77 EJBModule name for this bean
    * @param ejbContainerName the JMX name of the JBoss ejb container MBean
    * @throws MalformedObjectNameException
    * @throws InvalidParentException
    */
   public EJB(String ejbType, String ejbName, ObjectName ejbModuleName,
              ObjectName ejbContainerName)
           throws MalformedObjectNameException,
           InvalidParentException
   {
      this(ejbType, ejbName, ejbModuleName, ejbContainerName, null, null);
   }
   
   /**
    * Create a EJB model
    *
    * @param ejbType          the EJB.EJB_TYPES string
    * @param ejbName          the ejb-name from the deployment
    * @param ejbModuleName    the JSR-77 EJBModule name for this bean
    * @param ejbContainerName the JMX name of the JBoss ejb container MBean
    * @param jndiName the jndi name of the remote home binding is one exists,
    *    null if there is no remote home.
    * @param localJndiName the jndi name of the local home binding is one exists,
    *    null if there is no local home.
    * @throws MalformedObjectNameException
    * @throws InvalidParentException
    */
   public EJB(String ejbType, String ejbName, ObjectName ejbModuleName,
              ObjectName ejbContainerName, String jndiName, String localJndiName)
           throws MalformedObjectNameException,
           InvalidParentException
   {
      super(ejbType, ejbName, ejbModuleName);
      this.ejbContainerName = ejbContainerName;
      this.jndiName = jndiName;
      this.localJndiName = localJndiName;
   }

   // Begin StatisticsProvider interface methods

   /**
    * Obtain the Stats from the StatisticsProvider.
    *
    * @return An EJBStats subclass
    * @jmx:managed-attribute
    */
   public abstract Stats getstats();

   /**
    * Reset all statistics in the StatisticsProvider
    *
    * @jmx:managed-operation
    */
   public abstract void resetStats();
   // End StatisticsProvider interface methods

   public String getJndiName()
   {
      return this.jndiName;
   }
   
   public String getLocalJndiName()
   {
      return this.localJndiName;
   }

   // java.lang.Object overrides --------------------------------------

   public String toString()
   {
      return "EJB { " + super.toString() + " } []";
   }
   // Package protected ---------------------------------------------

   // Protected -----------------------------------------------------

   /**
    * Obtain the Stats from the StatisticsProvider. This method simply
    * updates the statistics common to all EJBs:
    * CreateCount
    * RemoveCount
    * InvocationTimes
    * <p/>
    * It should be invoked to update these common statistics.
    */
   protected void updateCommonStats(EJBStatsImpl stats)
   {
      try
      {
         ObjectName containerName = getContainerName();
         CountStatisticImpl createCount = (CountStatisticImpl) stats.getCreateCount();
         Long creates = (Long) server.getAttribute(containerName, "CreateCount");
         createCount.set(creates.longValue());
         CountStatisticImpl removeCount = (CountStatisticImpl) stats.getRemoveCount();
         Long removes = (Long) server.getAttribute(containerName, "RemoveCount");
         removeCount.set(removes.longValue());

         // Now build a TimeStatistics for every
         InvocationStatistics times = (InvocationStatistics) server.getAttribute(containerName, "InvokeStats");
         HashMap timesMap = new HashMap(times.getStats());
         Iterator iter = timesMap.entrySet().iterator();
         while (iter.hasNext())
         {
            Map.Entry entry = (Map.Entry) iter.next();
            Method m = (Method) entry.getKey();
            InvocationStatistics.TimeStatistic stat = (InvocationStatistics.TimeStatistic) entry.getValue();
            TimeStatisticImpl tstat = new TimeStatisticImpl(m.getName(), StatisticsConstants.MILLISECOND,
                    "The timing information for the given method");
            tstat.set(stat.count, stat.minTime, stat.maxTime, stat.totalTime);
            stats.addStatistic(m.getName(), tstat);
         }
      }
      catch (Exception e)
      {
         log.debug("Failed to retrieve stats", e);
      }
   }

   /**
    * @return the JMX name of the EJB container
    */
   protected ObjectName getContainerName()
   {
      return this.ejbContainerName;
   }

   /**
    * @return the JMX name of the EJB container cache
    */
   protected ObjectName getContainerCacheName()
   {
      ObjectName cacheName = null;
      try
      {
         Hashtable props = ejbContainerName.getKeyPropertyList();
         props.put("plugin", "cache");
         cacheName = new ObjectName(ejbContainerName.getDomain(), props);
      }
      catch (MalformedObjectNameException e)
      {
      }
      return cacheName;
   }

   /**
    * @return the JMX name of the EJB container pool
    */
   protected ObjectName getContainerPoolName()
   {
      ObjectName poolName = null;
      try
      {
         Hashtable props = ejbContainerName.getKeyPropertyList();
         props.put("plugin", "pool");
         poolName = new ObjectName(ejbContainerName.getDomain(), props);
      }
      catch (MalformedObjectNameException e)
      {
      }
      return poolName;
   }

   /**
    * @return A hashtable with the EJB-Module, J2EE-Application and J2EE-Server as parent
    */
   protected Hashtable getParentKeys(ObjectName pParent)
   {
      Hashtable lReturn = new Hashtable();
      Hashtable lProperties = pParent.getKeyPropertyList();
      lReturn.put(J2EETypeConstants.EJBModule, lProperties.get("name"));
      // J2EE-Application and J2EE-Server is already parent of J2EE-Application therefore lookup
      // the name by the J2EE-Server type
      lReturn.put(J2EETypeConstants.J2EEApplication, lProperties.get(J2EETypeConstants.J2EEApplication));
      lReturn.put(J2EETypeConstants.J2EEServer, lProperties.get(J2EETypeConstants.J2EEServer));
      return lReturn;
   }

   // Private -------------------------------------------------------

   // Inner classes -------------------------------------------------

}
