/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * This software is the proprietary information of Sun Microsystems, Inc.
 * Use is subject to license terms.
 *
 * Copyright 2004 Sun Microsystems, Inc.  Tous droits réservés.
 * Ce logiciel est propriété de Sun Microsystems, Inc.
 * Distribué par des licences qui en restreignent l'utilisation.
 *
 * ident        "@(#)CIBServicePerfStatsImpl.java 1.22     04/10/14 SMI"
 *
 */

package com.sun.mfwk.cib.sdk.statistics;

// jdk
import java.util.Map;
import java.util.Hashtable;
import java.util.logging.Logger;

// cib
import com.sun.mfwk.cib.statistics.CIBResourcePerfStats;
import com.sun.mfwk.cib.statistics.CIBValueStatistic;
import com.sun.mfwk.cib.statistics.CIBTimeStatistic;
import com.sun.mfwk.cib.statistics.CIBStatistic;
import com.sun.mfwk.cib.statistics.CIBStats;
import com.sun.mfwk.cib.CIBException;
import com.sun.mfwk.cib.CIBIOException;
import com.sun.mfwk.cib.CIBAttributeNotFoundException;

// Java ES-MF sdk
import com.sun.mfwk.util.log.MfLogService;
import com.sun.mfwk.cib.sdk.conn.CIBConnection;
import com.sun.mfwk.cib.sdk.util.CIBObjectNameMapFactory;

// jmx
import javax.management.Attribute;
import javax.management.MBeanServerConnection;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenType;
import javax.management.openmbean.CompositeType;

// j2ee
import javax.management.j2ee.statistics.Statistic;
import javax.management.*;
import com.sun.mfwk.cib.statistics.CIBServicePerfStats;
import com.sun.mfwk.cib.statistics.CIBCountStatistic;
import com.sun.mfwk.cib.statistics.CIBRangeStatistic;

/**
 * Provides a default implementation for a CIBServicePerfStats provider
 * This implementation is fully based on the Java-ES MF Instrumentation SDK.
 */
public class CIBServicePerfStatsImpl
    extends CIBPerfStatsImpl
    implements CIBServicePerfStats {

  // log file
  static final Logger logger = MfLogService.getLogger("cib-sdk");
  static final String className = "CIBServicePerfStatsImpl";

  // Communication
  private CIBConnection myConnection = null;

  // Table of statistics
  private Hashtable myTable = null;

  // internal provider state
  private boolean isInitialized = false;
  private boolean isStarted = false;

  // Time when the measurement has begun
  private long myStartTime = -1;

  /**
   * Builds a CIBServicePerfstats provider which uses the MBeanServerConnection to connect
   * the CP and the name as the key value of the name's key of the ObjectName to get
   * attributes from the CP's JMX MBeanServer.
   * @param connection MBeanServerConnection
   * @param name String
   * @throws MalformedObjectNameException
   */
  public CIBServicePerfStatsImpl(
      MBeanServerConnection connection,
      String name) throws MalformedObjectNameException {

    logger.entering(className, "Constructor");
    myConnection = new CIBConnection(connection,
                                     CIBObjectNameMapFactory.
                                     getCIBServicePerfStatsMap(name));
    myTable = new Hashtable();

    // Create the hashTable for the CIBExternalServicePerfStats
    myTable.put("ResidentTime",
                new CIBTimeStatisticImpl(
        "ResidentTime",
        "Resident Time of a CIBServicePerfStats"));

    myTable.put("ServiceTime",
                new CIBTimeStatisticImpl(
        "ServiceTime",
        "Service Time of a CIBServicePerfStats"));

    myTable.put("InRequests",
                new CIBCountStatisticImpl("InRequests",
                                          "Number of received Requests of a CIBServicePerfStats"));
    myTable.put("OutRequests",
                new CIBCountStatisticImpl("OutRequests",
                                          "Number of Processed Requests of a CIBServicePerfStats"));
    myTable.put("FailedRequests",
                new CIBCountStatisticImpl("FailedRequests",
                                          "Number of Failed Requests of a CIBServicePerfStats"));
    myTable.put("AbortedRequests",
                new CIBCountStatisticImpl("AbortedRequests",
                                          "Number of Aborted Requests of a CIBServicePerfStats"));
    myTable.put("OutstandingRequests",
                new CIBRangeStatisticImpl("OutstandingRequests",
                                          "Outstanding Requests of a CIBServicePerfStats"));

    super.statisticsTable = myTable;

    logger.exiting(className, "constructor", myTable);

  }

  /**
   * Builds a CIBServicePerfstats provider which uses the MBeanServerConnection to connect
   * the CP and the name as the ObjectName to get attributes from the CP's JMX MBeanServer.
   * @param connection MBeanServerConnection
   * @param name ObjectName
   * @throws MalformedObjectNameException
   */
  public CIBServicePerfStatsImpl(
      MBeanServerConnection connection,
      ObjectName name) throws MalformedObjectNameException {

    logger.entering(className, "Constructor");
    myConnection = new CIBConnection(connection,
                                     CIBObjectNameMapFactory.
                                     getCIBServicePerfStatsMap(name));

    myTable = new Hashtable();

    // Create the hashTable for the CIBExternalServicePerfStats
    myTable.put("ResidentTime",
                new CIBTimeStatisticImpl(
        "ResidentTime",
        "Resident Time of a CIBServicePerfStats"));

    myTable.put("ServiceTime",
                new CIBTimeStatisticImpl(
        "ServiceTime",
        "Service Time of a CIBServicePerfStats"));

    myTable.put("InRequests",
                new CIBCountStatisticImpl("InRequests",
                                          "Number of received Requests of a CIBServicePerfStats"));
    myTable.put("OutRequests",
                new CIBCountStatisticImpl("OutRequests",
                                          "Number of Processed Requests of a CIBServicePerfStats"));
    myTable.put("FailedRequests",
                new CIBCountStatisticImpl("FailedRequests",
                                          "Number of Failed Requests of a CIBServicePerfStats"));
    myTable.put("AbortedRequests",
                new CIBCountStatisticImpl("AbortedRequests",
                                          "Number of Aborted Requests of a CIBServicePerfStats"));
    myTable.put("OutstandingRequests",
                new CIBRangeStatisticImpl("OutstandingRequests",
                                          "Outstanding Requests of a CIBServicePerfStats"));

    super.statisticsTable = myTable;

    logger.exiting(className, "constructor", myTable);

  }

  /**
   * Builds a CIBServicePerfstats provider which uses the MBeanServerConnection to connect
   * the CP and a map providing the mapping between the Java-ES Framework and the CP meaning
   * the mapping between the CIB attributes and the CP's ObjectNames.
   * @param connection MBeanServerConnection
   * @param map Map
   */
  public CIBServicePerfStatsImpl(
      MBeanServerConnection connection,
      Map map) {
    logger.entering(className, "Constructor");
    myConnection = new CIBConnection(connection, map);
    myTable = new Hashtable();

    // Create the hashTable for the CIBExternalServicePerfStats
    myTable.put("ResidentTime",
                new CIBTimeStatisticImpl(
        "ResidentTime",
        "Resident Time of a CIBServicePerfStats"));

    myTable.put("ServiceTime",
                new CIBTimeStatisticImpl(
        "ServiceTime",
        "Service Time of a CIBServicePerfStats"));

    myTable.put("InRequests",
                new CIBCountStatisticImpl("InRequests",
                                          "Number of received Requests of a CIBServicePerfStats"));
    myTable.put("OutRequests",
                new CIBCountStatisticImpl("OutRequests",
                                          "Number of Processed Requests of a CIBServicePerfStats"));
    myTable.put("FailedRequests",
                new CIBCountStatisticImpl("FailedRequests",
                                          "Number of Failed Requests of a CIBServicePerfStats"));
    myTable.put("AbortedRequests",
                new CIBCountStatisticImpl("AbortedRequests",
                                          "Number of Aborted Requests of a CIBServicePerfStats"));
    myTable.put("OutstandingRequests",
                new CIBRangeStatisticImpl("OutstandingRequests",
                                          "Outstanding Requests of a CIBServicePerfStats"));

    super.statisticsTable = myTable;

    logger.exiting(className, "constructor", myTable);

  }

  /**
   * Returns a CIBTimeStatistic object representing the Resident time.
   * @throws com.sun.mfwk.cib.CIBIOException
   * @throws com.sun.mfwk.cib.CIBAttributeNotFoundException
   * @return com.sun.mfwk.cib.statistics.CIBTimeStatistic
   */
  public CIBTimeStatistic getResidentTime() throws CIBIOException,
      CIBAttributeNotFoundException {

    String attributeError;
    String errorAttr;
    String methodName = "getResidentTime";

    logger.entering(className, methodName);

    if (isStarted == true) {
      logger.finest(className + " Stats Provider started");
      Attribute attribute = null;
      Attribute attribute2 = null;
      long value = -1;
      long value2 = -1;

      CIBTimeStatisticImpl statistic = (CIBTimeStatisticImpl) myTable.get(
          "ResidentTime");

      logger.finest(className + " getAttribute MinResponseTime");
      logger.finest(className + " getAttribute MinResponseTime");
      attribute = myConnection.getAttribute("MinResponseTime");
      value = (new Long(attribute.getValue().toString())).longValue();
      statistic.setMinTime(value);

      logger.finest(className + " getAttribute MaxResponseTime");
      attribute = myConnection.getAttribute("MaxResponseTime");
      value = (new Long(attribute.getValue().toString())).longValue();
      statistic.setMaxTime(value);

      logger.finest(className + " getAttribute AccumulatedResponseTime");
      attribute = myConnection.getAttribute("AccumulatedResponseTime");
      value = (new Long(attribute.getValue().toString())).longValue();
      statistic.setTotalTime(value);

      // Count = NbOutRequests + NbFailedRequests
      logger.finest(className + " getAttribute NbFailedRequests");
      attribute2 = myConnection.getAttribute("NbFailedRequests");
      value2 = (new Long(attribute2.getValue().toString())).longValue();

      logger.finest(className + " getAttribute NbOutRequests");
      attribute = myConnection.getAttribute("NbOutRequests");
      value = (new Long(attribute.getValue().toString())).longValue();
      statistic.setCount(value+value2);

      logger.finest(className + " getAttribute AccumulatedSqResponseTime");
      attribute = myConnection.getAttribute("AccumulatedSqResponseTime");
      value = (new Long(attribute.getValue().toString())).longValue();
      statistic.setTotalSquaredTime(value);

      statistic.setLastSampleTime(System.currentTimeMillis());
      statistic.setStartTime(myStartTime);
      logger.exiting(className, " getResidentTime", statistic.toString());
      return statistic;
    }
    else {
      logger.finest(className + " Stats Provider not Started");
      logger.exiting(className, "getResidentTime");
      return null;
    }
  }

  /**
   * Returns a CIBTimeStatistic object representing the Service Time
   * @throws com.sun.mfwk.cib.CIBIOException
   * @throws com.sun.mfwk.cib.CIBAttributeNotFoundException
   * @return com.sun.mfwk.cib.statistics.CIBTimeStatistic
   */
  public CIBTimeStatistic getServiceTime() throws CIBIOException,
      CIBAttributeNotFoundException {
    logger.entering(className, "getServiceTime");
    if (isStarted == true) {

      logger.finest(className + " Stats Provider Started");
      Attribute attribute = null;
      Attribute attribute2 = null;
      long value = -1;
      long value2 = -1;

      CIBTimeStatisticImpl statistic = (CIBTimeStatisticImpl) myTable.get(
          "ServiceTime");

      logger.finest(className + " getAttribute MinServiceTime");
      attribute = myConnection.getAttribute("MinServiceTime");
      value = (new Long(attribute.getValue().toString())).longValue();
      statistic.setMinTime(value);

      logger.finest(className + " getAttribute MaxServiceTime");
      attribute = myConnection.getAttribute("MaxServiceTime");
      value = (new Long(attribute.getValue().toString())).longValue();
      statistic.setMaxTime(value);

      logger.finest(className + " getAttribute AccumulatedServiceTime");
      attribute = myConnection.getAttribute("AccumulatedServiceTime");
      value = (new Long(attribute.getValue().toString())).longValue();
      statistic.setTotalTime(value);

      // Count = NbOutRequests + NbFailedRequests
      logger.finest(className + " getAttribute NbFailedRequests");
      attribute2 = myConnection.getAttribute("NbFailedRequests");
      value2 = (new Long(attribute2.getValue().toString())).longValue();

      logger.finest(className + " getAttribute NbOutRequests");
      attribute = myConnection.getAttribute("NbOutRequests");
      value = (new Long(attribute.getValue().toString())).longValue();
      statistic.setCount(value+value2);

      logger.finest(className + " getAttribute AccumulatedSqServiceTime");
      attribute = myConnection.getAttribute("AccumulatedSqServiceTime");
      value = (new Long(attribute.getValue().toString())).longValue();
      statistic.setTotalSquaredTime(value);

      statistic.setLastSampleTime(System.currentTimeMillis());
      statistic.setStartTime(myStartTime);
      logger.exiting(className, "getServiceTime", statistic.toString());
      return statistic;

    }
    else {
      logger.finest(className + " Stats Provider not started");
      logger.exiting(className, "getServiceTime");
      return null;
    }
  }

  /**
   * Returns a CIBValueStatistic representing the number of failed requests
   * @throws com.sun.mfwk.cib.CIBIOException
   * @throws com.sun.mfwk.cib.CIBAttributeNotFoundException
   * @return com.sun.mfwk.cib.statistics.CIBCountStatistic
   */
  public CIBCountStatistic getFailedRequests() throws CIBIOException,
      CIBAttributeNotFoundException {
    logger.entering(className, "getFailedRequests");
    if (isStarted == true) {

      logger.finest(className + " Stats Provider started");
      Attribute attribute = null;
      long value;

      CIBCountStatisticImpl statistic = (CIBCountStatisticImpl) myTable.get(
          "FailedRequests");

      //Min Time
      logger.finest(className + " getAttribute NbFailedRequests");
      attribute = myConnection.getAttribute("NbFailedRequests");
      value = new Long(attribute.getValue().toString()).longValue();
      statistic.setCount(value);
      statistic.setLastSampleTime(System.currentTimeMillis());
      statistic.setStartTime(myStartTime);
      logger.exiting(className, "getFailedRequests", statistic.toString());
      return statistic;
    }
    else {
      logger.finest(className + " Stats Provider not started");
      logger.exiting(className, "getFailedRequests");
      return null;
    }
  }

  /**
   * Returns a CIBRangeStatistic representing the Outstanding Requests statistics
   * The current value is (NbInRequests-(NbOutRequests+NbFailedRequests+NbAbortedRequests))
   * The processing does not take into account the number of Abort requests
   * @throws com.sun.mfwk.cib.CIBIOException
   * @throws com.sun.mfwk.cib.CIBAttributeNotFoundException
   * @return com.sun.mfwk.cib.statistics.CIBRangeStatistic
   */
  public CIBRangeStatistic getOutstandingRequests() throws CIBIOException,
      CIBAttributeNotFoundException {
    logger.entering(className, "getOutstandingRequests");
    if (isStarted == true) {
      logger.finest(className + " Stats Provider started");
      CIBCountStatistic countIn = this.getInRequests();
      CIBCountStatistic countOut = this.getOutRequests();
      CIBCountStatistic countFailed = this.getFailedRequests();
      CIBCountStatistic countAborted = this.getAbortedRequests();

      CIBRangeStatisticImpl statistic = (CIBRangeStatisticImpl) myTable.get(
          "OutstandingRequests");
      long current = countIn.getCount() -
          (countOut.getCount() + countFailed.getCount() + countAborted.getCount());

      statistic.setCurrent(current);
      if (statistic.getHighWaterMark() < 0) {
        statistic.setHighWaterMark(current);
      }
      else if (current > statistic.getHighWaterMark()) {
        statistic.setHighWaterMark(current);
      }
      if (statistic.getLowWaterMark() < 0) {
        statistic.setLowWaterMark(current);
      }
      else if (current < statistic.getLowWaterMark()) {
        statistic.setLowWaterMark(current);
      }
      statistic.setLastSampleTime(System.currentTimeMillis());
      statistic.setStartTime(myStartTime);
      logger.exiting(className, "getOutstandingRequests", statistic.toString());
      return statistic;
    }
    else {
      logger.finest(className + " Stats Provider not started");
      logger.exiting(className, "getOutstandingRequests");
      return null;
    }
  }

  /**
   * Returns a CIBCountStatistic representing the number of In Requests
   * @throws com.sun.mfwk.cib.CIBIOException
   * @throws com.sun.mfwk.cib.CIBAttributeNotFoundException
   * @return com.sun.mfwk.cib.statistics.CIBCountStatistic
   */
  public CIBCountStatistic getInRequests() throws CIBIOException,
      CIBAttributeNotFoundException {
    logger.entering(className, "getInRequests");
    if (isStarted == true) {
      logger.finest(className + " Stats Provider started");
      Attribute attribute = null;
      long value = -1;

      CIBCountStatisticImpl statistic = (CIBCountStatisticImpl) myTable.get(
          "InRequests");

      //Min Time
      logger.finest(className + " getAttribute NbInRequests");
      attribute = myConnection.getAttribute("NbInRequests");
      value = new Long(attribute.getValue().toString()).longValue();
      statistic.setCount(value);
      statistic.setLastSampleTime(System.currentTimeMillis());
      statistic.setStartTime(myStartTime);
      logger.exiting(className, "getInRequests", statistic.toString());
      return statistic;
    }
    else {
      logger.finest(className + " Stats Provider not started");
      logger.exiting(className, "getInRequests");
      return null;
    }
  }

  /** Returns a CIBCountStatistic representing the number of Out Requests
   * @throws com.sun.mfwk.cib.CIBIOException
   * @throws com.sun.mfwk.cib.CIBAttributeNotFoundException
   * @return com.sun.mfwk.cib.statistics.CIBCountStatistic
   */
  public CIBCountStatistic getOutRequests() throws CIBIOException,
      CIBAttributeNotFoundException {
    logger.entering(className, "getOutRequests");
    if (isStarted == true) {
      logger.finest(className + " Stats Provider started");
      Attribute attribute = null;
      long value;

      CIBCountStatisticImpl statistic = (CIBCountStatisticImpl) myTable.get(
          "OutRequests");

      //Min Time
      logger.finest(className + " getAttribute NbOutRequests");
      attribute = myConnection.getAttribute("NbOutRequests");
      value = new Long(attribute.getValue().toString()).longValue();
      statistic.setCount(value);
      statistic.setLastSampleTime(System.currentTimeMillis());
      statistic.setStartTime(myStartTime);
      logger.exiting(className, "getOutRequests", statistic.toString());
      return statistic;
    }
    else {
      logger.finest(className + " Stats Provider not started");
      logger.exiting(className, "getOutRequests");
      return null;
    }
  }

  /** Returns a CIBCountStatistic representing the number of Aborted Requests
   * @throws com.sun.mfwk.cib.CIBIOException
   * @throws com.sun.mfwk.cib.CIBAttributeNotFoundException
   * @return com.sun.mfwk.cib.statistics.CIBCountStatistic
   */
  public CIBCountStatistic getAbortedRequests() throws CIBIOException,
      CIBAttributeNotFoundException {
    logger.entering(className, "getAbortedRequests");
    if (isStarted == true) {
      logger.finest(className + " Stats Provider started");
      Attribute attribute = null;
      long value;

      CIBCountStatisticImpl statistic = (CIBCountStatisticImpl) myTable.get(
          "AbortedRequests");

      //Min Time
      logger.finest(className + " getAttribute NbOutRequests");
      attribute = myConnection.getAttribute("NbAbortedRequests");
      value = new Long(attribute.getValue().toString()).longValue();
      statistic.setCount(value);
      statistic.setLastSampleTime(System.currentTimeMillis());
      statistic.setStartTime(myStartTime);
      logger.exiting(className, "getAbortedRequests", statistic.toString());
      return statistic;
    }
    else {
      logger.finest(className + " Stats Provider not started");
      logger.exiting(className, "getAbortedRequests");
      return null;
    }
  }

  /**
   * Initializes the statistics mechanism. In particular, initialize the
   * communication to the instrumentation layer.
   * @throws com.sun.mfwk.cib.CIBIOException
   * @throws com.sun.mfwk.cib.CIBException
   */
  public void init() throws CIBIOException, CIBException {
    logger.entering(className, "init");
    if (isInitialized == false) {
      logger.finest(className + " Initialize the provider");
      super.init();
      this.stop();
      this.start();
      logger.finest(className + " Stats Provider initialized");
      isInitialized = true;
      isStarted = true;
    }
    else {
      logger.finest(className + " Stats Provider already initialized");
    }
    logger.exiting(className, "init");
  }

  /**
   * Destroy the statistics and clean the statistics objects/values
   * when needed, close all the communications.
   * @throws com.sun.mfwk.cib.CIBIOException
   * @throws com.sun.mfwk.cib.CIBException
   */
  public void destroy() throws CIBIOException, CIBException {
    logger.entering(className, "destroy");
    if (isInitialized == true) {
      logger.finest(className + " Stats Provider initialized");
      super.destroy();
      isInitialized = false;
      stop();
      logger.finest(className + " Stats Provider destroyed");
    }
    else {
      logger.finest(className + " Stats Provider already destroy");
    }
    logger.exiting(className, "destroy");
  }

  /**
   * Start the statistics collections and clean the statistics objects/values
   * when needed, close all the communications.
   * @throws com.sun.mfwk.cib.CIBIOException
   * @throws com.sun.mfwk.cib.CIBException
   */
  public void start() throws CIBIOException, CIBException {
    logger.entering(className, "start");
    if (isStarted == false) {
      logger.finest(className + " Enable Monitoring for the provider");
      myConnection.invoke("enableMonitoring");
      logger.finest(className + " Stats provider started");
      isStarted = true;
      myStartTime = System.currentTimeMillis();
    }
    else {
      logger.finest(className + " Stats provider already started");
    }
    logger.exiting(className, "start");
  }

  /**
   * Stop the statistics collections and clean the statistics objects/values
   * when needed, close all the communications.
   * @throws com.sun.mfwk.cib.CIBIOException
   * @throws com.sun.mfwk.cib.CIBException
   */
  public void stop() throws CIBIOException, CIBException {
    logger.entering(className, "stop");
    if (isStarted == true) {
      logger.finest(className + " Disable Monitoring for the provider");
      myConnection.invoke("disableMonitoring");
      isStarted = false;
      myStartTime = -1;
      logger.finest(className + " Stats provider stopped");
    }
    logger.exiting(className, "stop");
  }

  /**
   * toCompositeData
   * @throws OpenDataException
   * @return CompositeData
   */
  public CompositeData toCompositeData() throws OpenDataException {

    logger.entering(className, "toCompositeData");

    // Build the itemNames
    // Build the descrition;
    Statistic[] statistics = getStatistics();

    // CompositeData Type for statistics
    OpenType[] itemTypes = new OpenType[statistics.length];
    String[] itemNames = new String[statistics.length];
    String[] itemDescriptions = new String[statistics.length];

    CompositeData[] statisticsData = new CompositeDataSupport[statistics.length];

    // Build the compositeData
    for (int i = 0; i < statistics.length; i++) {
      statisticsData[i] = ( (CIBStatistic) statistics[i]).toCompositeData();
      itemTypes[i] = ( (CIBStatistic) statistics[i]).toCompositeData().
          getCompositeType();
      itemNames[i] = statistics[i].getName();
      itemDescriptions[i] = statistics[i].getDescription();
    }

    String statsName = "CIBServicePerfStats";
    String statsDescription = "CIBService Performance Statistics";
    CompositeType statsTypes = null;
    CompositeDataSupport data = null;

    try {
      statsTypes = new CompositeType(statsName, statsDescription, itemNames,
                                     itemDescriptions, itemTypes);

      data = new CompositeDataSupport(statsTypes, itemNames, statisticsData);
    }
    catch (OpenDataException e) {
      logger.throwing(className, "toCompositeData", e);
      logger.exiting(className, "toCompositeData");
      throw e;
    }
    logger.exiting(className, "toCompositeData");
    return data;
  }

  /**
   * fromCompositeData
   * @param data CompositeData
   * @return com.sun.mfwk.cib.statistics.CIBStats
   */
  public CIBStats fromCompositeData(CompositeData data) {
    logger.entering(className, "fromCompositeData");
    logger.exiting(className, "fromCompositeData");
    return null;
  }

}
