/*
 * JBoss, the OpenSource EJB server
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.util;

import java.util.Date;

import javax.management.NotificationBroadcasterSupport;
import javax.management.AttributeChangeNotification;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.ObjectName;

import org.apache.log4j.NDC;
import org.jboss.logging.Logger;

/** An abstract base class JBoss services can subclass to implement a
service that conforms to the ServiceMBean interface. Subclasses must
override {@link #getName() getName} method and should override 
{@link #initService() initService}, {@link #startService() startService},
{@link #stopService() stopService}, {@link #destroyService() destroyService}
as approriate.

@see org.jboss.util.ServiceMBean

@author Rickard Öberg (rickard.oberg@telkel.com)
@author Scott.Stark@jboss.org
@version $Revision: 1.13.4.5 $
*/
public abstract class ServiceMBeanSupport
   extends NotificationBroadcasterSupport
   implements ServiceMBean, MBeanRegistration
{
   // Attributes ----------------------------------------------------
   private int state;
   protected MBeanServer server;
   private int id = 0;
   protected Logger log;

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

   // Constructors --------------------------------------------------
   public ServiceMBeanSupport()
   {
      log = Logger.getLogger(getClass());
   }


   // Public --------------------------------------------------------
   public abstract String getName();

   public MBeanServer getServer()
   {
       return server;
   }

   public int getState()
   {
      return state;
   }
   
   public String getStateString()
   {
      return states[state];
   }

    public void init()
            throws Exception
    {
        log.info("Initializing");
        NDC.push(getName());
        try
        {   
           initService();
        } catch (Exception e)
        {
           log.error("Initialization failed", e);
           throw e;
        } finally
        {
           NDC.pop();
        }
        log.info("Initialized");
    }

   public void start()
      throws Exception
   {
      if (getState() != STOPPED)
      	return;
			
      state = STARTING;
      //AS It seems that the first attribute is not needed anymore and use a long instead of a Date
      sendNotification(new AttributeChangeNotification(this, id++, new Date().getTime(), getName()+" starting",
         "State", "java.lang.Integer", new Integer(STOPPED), new Integer(STARTING)));
      log.info("Starting");
      NDC.push(getName());
      try
      {
         startService();
      }
      catch (Exception e)
      {
         state = STOPPED;
         //AS It seems that the first attribute is not needed anymore and use a long instead of a Date
         sendNotification(new AttributeChangeNotification(this, id++, new Date().getTime(), getName()+" stopped",
            "State", "java.lang.Integer", new Integer(STARTING), new Integer(STOPPED)));
         log.error("Stopped", e);
         throw e;
      }
      finally
      {
         NDC.pop();
      }
      state = STARTED;
      //AS It seems that the first attribute is not needed anymore and use a long instead of a Date
      sendNotification(new AttributeChangeNotification(this, id++, new Date().getTime(), getName()+" started",
         "State", "java.lang.Integer", new Integer(STARTING), new Integer(STARTED)));
      log.info("Started");
   }

    public void stop()
    {
        if (getState() != STARTED)
                return;
	
      state = STOPPING;
      //AS It seems that the first attribute is not needed anymore and use a long instead of a Date
      sendNotification(new AttributeChangeNotification(this, id++, new Date().getTime(), getName()+" stopping", "State", "java.lang.Integer", new Integer(STARTED), new Integer(STOPPING)));
      log.info("Stopping");
      NDC.push(getName());
      
      try
      {
         stopService();
      } catch (Throwable e)
      {
         log.error(e);
      }
      
      state = STOPPED;
      //AS It seems that the first attribute is not needed anymore and use a long instead of a Date
      sendNotification(new AttributeChangeNotification(this, id++, new Date().getTime(), getName()+" stopped",
         "State", "java.lang.Integer", new Integer(STOPPING), new Integer(STOPPED)));
      NDC.pop();
      log.info("Stopped");
   }

   public void destroy()
   {
        if (getState() != STOPPED)
                stop();
	
   	log.info("Destroying");
        NDC.push(getName());
   	try
   	{
   	   destroyService();
   	} catch (Exception e)
   	{
   	   log.error(e);
   	}
   	
   	NDC.pop();
   	log.info("Destroyed");
   }
	
   public ObjectName preRegister(MBeanServer server, ObjectName name)
      throws java.lang.Exception
   {
        name = getObjectName(server, name);
        this.server = server;
        return name;
   }

   public void postRegister(java.lang.Boolean registrationDone)
   {
      if (!registrationDone.booleanValue()) {
         log.info( "Registration is not done -> destroy" );
         destroy();
      }
   }
   
   public void preDeregister()
      throws java.lang.Exception
   {
   }
   
   public void postDeregister()
   {
       destroy();
   }
   
   // Protected -----------------------------------------------------
   protected ObjectName getObjectName(MBeanServer server, ObjectName name)
      throws javax.management.MalformedObjectNameException
   {
      return name;
   }
   
   protected void initService()
      throws Exception
   {
   }
	
   protected void startService()
      throws Exception
   {
   }
   
   protected void stopService()
      throws Exception
   {
   }
	
   protected void destroyService()
      throws Exception
   {
   }

   protected int nextSequenceNumber()
   {
      return id++;
   }
}
