/***************************************
 *                                     *
 *  JBoss: The OpenSource J2EE WebOS   *
 *                                     *
 *  Distributable under LGPL license.  *
 *  See terms of license at gnu.org.   *
 *                                     *
 ***************************************/
 
package org.jboss.monitor.alarm;

import java.util.Map;
import java.util.HashMap;

import javax.management.Notification;
import javax.management.AttributeChangeNotification;

/**
 * AlarmManager
 *
 * @author  <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
 *
 * @version $Revision: 1.1.4.1 $
**/
public class AlarmManager
{
   // Private/Protected Data ----------------------------------------
   
   // mediates the related MBean
   protected MBeanImplAccess mbeanImpl;
   
   // holds the alarm type-to-AlarmInfo mappings
   private Map typeMap;
   
   // Constructors --------------------------------------------------
   
   /**
    * CTOR
   **/
   public AlarmManager(MBeanImplAccess mbeanImpl)
   {
      this.mbeanImpl = mbeanImpl;
      this.typeMap = new HashMap();
   }
   
   // High-level part of the interface used to support Statefull Alarms.
   // The sending of the actual AlarmNotifications is done through the
   // sendAlarmNotification() method.

   /**
    * Sets the severity of an Alarm, keyed by its type, without producing
    * an AlarmNotification
   **/
   public void setSeverity(String type, int severity)
   {
      synchronized (this) {
         AlarmInfo ai = (AlarmInfo)this.typeMap.get(type);
         
         if (ai == null)
            this.typeMap.put(type, new AlarmInfo(severity));
         else
            ai.setSeverity(severity);
      }
   }

   /**
    * Gets the severity of an alarm, keyed by its type.
   **/
   public int getSeverity(String type)
   {
      AlarmInfo ai;
      
      synchronized (this) {
         ai = (AlarmInfo)this.typeMap.get(type);
      
         // if alarm did not exist, added with a default severity
         if (ai == null) {
            ai = new AlarmInfo(Alarm.SEVERITY_NORMAL);
            this.typeMap.put(type, ai);
         }
      }
      return ai.getSeverity();
   }
   
   /**
    * Gets the severity of an alarm, keyed by its type, as String
   **/
   public String getSeverityAsString(String type)
   {
      return Alarm.SEVERITY_STRINGS[getSeverity(type)];      
   }

   /**
    * Sets the alarm, keyed by its type, to a particular state.
    * If severity has changed an AlarmNotification will be thrown.
    * The alarmState of the AlarmNotification will be either
    * Alarm.STATE_CREATED, Alarm.STATE_CHANGED or Alarm.STATE_CLEARED.
   **/   
   public void setAlarm(String type, int severity, String message, Object userData)
   {
      AlarmInfo ai;
      
      synchronized (this) {
         ai = (AlarmInfo)this.typeMap.get(type);
         
         // if alarm did not exist, added with a default severity
         if (ai == null) {
            ai = new AlarmInfo(Alarm.SEVERITY_NORMAL);
            this.typeMap.put(type, ai);
         }
      }
      
      // There must be a small race condition here if 2 threads
      // set the same severity, thus producing duplicate notifications
      // Not a big deal...
      int oldSeverity = ai.getSeverity();
      
      // if the severity has changed, send an AlarmNotification
      if (severity != oldSeverity) {
         
         // store the new severity
         ai.setSeverity(severity);
         
         if (severity == Alarm.SEVERITY_NORMAL)
            sendAlarmNotification(
               type, Alarm.STATE_CLEARED, severity, message, userData);
         else if (oldSeverity == Alarm.SEVERITY_NORMAL)
            sendAlarmNotification(
               type, Alarm.STATE_CREATED, severity, message, userData);
         else
            sendAlarmNotification(
               type, Alarm.STATE_CHANGED, severity, message, userData);
      }
   }
   
   /**
    * See set Alarm above
    *
    * Essentially a helper method that will populate the userData field
    * of the Notification with a HashMap, containing a single key/value pair.
    *
    * Note, that an AlarmNotification will not be thrown if there is no
    * severity change.
   **/
   public void setAlarm(
      String type, int severity, String message, String key, Object value)
   {
      HashMap map = new HashMap();
      map.put(key, value);
      this.setAlarm(type, severity, message, map);      
   }
      
   // Low-level part of the interface used to generate and send
   // various types of notifications, including AlarmNotifications
   // corresponding to Stateless Alarms.
   
   /**
    * Generates and sends an AlarmNotification
    *
    * Essentially a helper method that will populate the userData field
    * of the Notification with a HashMap, containing a single key/value pair.
   **/
   public void sendAlarmNotification(
      String type, int alarmState, int severity,
      String message,
      String key, Object value)
   {
      HashMap map = new HashMap();
      map.put(key, value);
      this.sendAlarmNotification(type, alarmState, severity, message, map);
   }
   
   /**
    * Generates and sends an AlarmNotification.
    *
    * An alarmState of Alarm.STATE_CLEARED forces severity to SEVERITY_NORMAL
    *
    * source, sequenceNumber, timeStamp
    * will be automatically filled.
   **/   
   public void sendAlarmNotification(
      String type, int alarmState, int severity,
      String message, Object userData)
   {
      Notification n = new AlarmNotification(
         type,
         this.mbeanImpl.getMBeanName(), // source
         this.mbeanImpl.getSequenceNumber(),
         System.currentTimeMillis(),
         message,
         alarmState,
         severity         
      );
      n.setUserData(userData);
      
      // send it away
      this.mbeanImpl.emitNotification(n);
   }

   /**
    * Generates and sends an AttributeChangeNotification.
    *
    * source, sequenceNumber, timeStamp
    * will be automatically filled in.
   **/   
   public void sendAttributeChangeNotification(
      String type, String message, Object userData,
      String attributeName, String attributeType,
      Object oldValue, Object newValue)
   {
      Notification n = new AttributeChangeNotification(
         this.mbeanImpl.getMBeanName(), // source
         this.mbeanImpl.getSequenceNumber(),
         System.currentTimeMillis(),
         message,
         attributeName,
         attributeType,
         oldValue,
         newValue
      );
      n.setUserData(userData);
      
      // send it away
      this.mbeanImpl.emitNotification(n);
   }

   /**
    * Generates and sends a simple Notification.
    *
    * source, sequenceNumber, timeStamp
    * will be automatically filled in.
   **/   
   public void sendNotification(String type, String message, Object userData)
   {
      Notification n = new Notification(
         type,
         this.mbeanImpl.getMBeanName(), // source
         this.mbeanImpl.getSequenceNumber(),
         System.currentTimeMillis(),
         message
      );
      n.setUserData(userData);
      
      // send it away
      this.mbeanImpl.emitNotification(n);
   }

   // Inner Class ---------------------------------------------------
   
   /**
    * Simple Data Holder
   **/
   private static class AlarmInfo
   {
      private int severity;
      
      public AlarmInfo(int severity)
      {
         this.severity = severity;
      }
   
      public void setSeverity(int severity)
      {
         this.severity = severity;
      }
      
      public int getSeverity()
      {
         return this.severity;
      }
   }      
}