/* 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.huihoo.jfox.service;

import java.lang.reflect.Proxy;

import javax.management.ObjectName;
import javax.management.MBeanServer;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServerInvocationHandler;
import javax.management.AttributeChangeNotification;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.MBeanRegistration;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationListener;

import org.huihoo.jfox.logging.Logger;
import org.huihoo.jfox.jmx.ExtendedObjectInstance;

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

public abstract class ComponentSupport extends NotificationBroadcasterSupport implements ComponentSupportMBean, MBeanRegistration, NotificationBroadcaster, NotificationListener {
  /**
   * service name of the service
   */
  protected String name = null;
  /**
   * the ObjectName of the service register into the MBeanServer
   */
  protected ObjectName objectName = null;
  /**
   * the status value of the service
   */
  protected volatile State state = State.ORIGINAL;


  /**
   * MBeanService instance
   */
  protected MBeanServer server = null;

  protected Logger logger = Logger.getLogger(getClass().getName());
  protected volatile long sequence = 0L;

  protected Object proxyInstance = null;

  public ComponentSupport() {
    setName(parseName(this.getClass().getName()));
  }

  public ComponentSupport(String name) {
    setName(name);
  }

  public String getName() {
    return name;
  }

  protected void setName(String name) {
    this.name = name;
  }

  public ObjectName getObjectName() {
    return objectName;
  }

  public State getState() {
    return state;
  }

  /**
   * get the proxy instance
   * if return itself, unregisterMBean will invalidate
   */
  public Proxy getProxyInstance() {
    if(server == null || objectName == null) return null;
    // initialzing proxyInstance;
    if(proxyInstance == null) {
      ExtendedObjectInstance oi = null;
      try {
        oi = (ExtendedObjectInstance)server.getObjectInstance(objectName);
      }
      catch(InstanceNotFoundException e){
        e.printStackTrace();
      }
      Class interfaceClass = oi.getInterfaceClass();
      proxyInstance = MBeanServerInvocationHandler.newProxyInstance(server,
                                                                    objectName,
                                                                    interfaceClass,
                                                                    true);
    }

    return (Proxy)proxyInstance;
  }

  public Logger getLogger() {
    return logger;
  }

  protected void setLogger(Logger logger){
    this.logger = logger;
  }

  public void init() throws Exception {
    if(!State.canInit(state)) {
      logger.warn(name + " can not initialize, state = " + state);
      return;
    }

//    if(logger == null) logger = Logger.getLogger(getClass().getName());
    logger.info("initializing...");
    sendNotification(new AttributeChangeNotification(this,
                                                     sequence++,
                                                     System.currentTimeMillis(),
                                                     getName()+" starting",
                                                     "State",
                                                     "java.lang.Integer",
                                                     new Integer(state.getId()),
                                                     new Integer(State.INITIALIZING.getId())));
    state = State.INITIALIZING;

    try {
      doInit();
    }
    catch (Exception e) {
      sendNotification(new AttributeChangeNotification(this,
                                                       sequence++,
                                                       System.currentTimeMillis(),
                                                       getName()+" creating failed",
                                                       "State",
                                                       "java.lang.Integer",
                                                       new Integer(state.getId()), new Integer(State.INTERRUPTED.getId())));
      state = State.INTERRUPTED;
      logger.error("initialze failed", e);
      throw e;
    }
    sendNotification(new AttributeChangeNotification(this,
                                                     sequence++,
                                                     System.currentTimeMillis(),
                                                     getName()+" created",
                                                     "State",
                                                     "java.lang.Integer",
                                                     new Integer(state.getId()),
                                                     new Integer(State.INITIALIZED.getId())));

    state = State.INITIALIZED;
    logger.info("initialized.");

  }


  public void destroy() throws Exception {
    if(!State.canDestroy(state)) {
      logger.warn(name + " can not destroy, state = " + state);
      return;
    }

    logger.info("destroying...");

    sendNotification(new AttributeChangeNotification(this,
                                                     sequence++,
                                                     System.currentTimeMillis(),
                                                     getName()+" destroying",
                                                     "State",
                                                     "java.lang.Integer",
                                                     new Integer(state.getId()),
                                                     new Integer(State.DESTROYING.getId())));
    state = State.DESTROYING;
    try {
      doDestroy();
    }
    catch (Exception e) {
      sendNotification(new AttributeChangeNotification(this,
                                                       sequence++,
                                                       System.currentTimeMillis(),
                                                       getName()+" destroy failed",
                                                       "State",
                                                       "java.lang.Integer",
                                                       new Integer(state.getId()), new Integer(State.INTERRUPTED.getId())));
      state = State.INTERRUPTED;
      logger.error("stopping failed", e);
      throw e;
    }
    sendNotification(new AttributeChangeNotification(this,
                                                     sequence++,
                                                     System.currentTimeMillis(),
                                                     getName()+" destroyed",
                                                     "State",
                                                     "java.lang.Integer",
                                                     new Integer(state.getId()),
                                                     new Integer(State.DESTROYED.getId())));
    state = State.DESTROYED;
    logger.info("destroyed");

  }

  /**
   * ObjectName must specified in the MLet, or use registerMBean specified explicitly
   */
  public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
    if(name == null) throw new IllegalArgumentException("ObjectName can not be null.");

    // get log service instance
    this.server = server;
    objectName = name;

    return name;
  }

  public void postRegister(Boolean registrationDone) {
    if(!registrationDone.booleanValue()){
      try{
        this.destroy();
      }
      catch(Exception e){
        logger.error(e.getMessage(),e);
      }
    }
    else {
      // initilize & start
      try {
        this.init();
      }
      catch(Exception e){
        logger.error(e.getMessage(),e);
      }

    }

  }

  public void preDeregister() throws Exception {

  }

  public void postDeregister() {

  }

  public void handleNotification(Notification notification, Object obj) {
    logger.info("Recieved notification, type: " + notification.getType() + ", " +
                 "message: " + notification.getMessage() + ", " +
                 "source: " + notification.getSource());

  }


  /**
   * do actually create action
   */
  protected abstract void doInit() throws Exception;
  /**
   * do actually destory action
   */
  protected abstract void doDestroy() throws Exception;

  // trip the ServiceName from it's class nameSpace
  protected static String parseName(String className){
    int lastDotIndex = className.lastIndexOf(".");
    return className.substring(lastDotIndex + 1);
  }


}
