mx4j/src/core/mx4j/monitor MX4JCounterMonitor.java,NONE,1.1 MX4JCounterMonitorMBean.java,NONE,1.1 MX4JGaugeMonitor.java,NONE,1.1 MX4JGaugeMonitorMBean.java,NONE,1.1 MX4JMonitor.java,NONE,1.1 MX4JMonitorMBean.java,NONE,1.1 MX4JMonitorNotification.java,NONE,1.1 MX4JStringMonitor.java,NONE,1.1 MX4JStringMonitorMBean.java,NONE,1.1

Simone Bordet <[email protected]> Sat, 04 Sep 2004 13:55:36 +0000
Newsgroups gmane.comp.java.mx4j.cvs
Message-ID <[email protected]>
Update of /cvsroot/mx4j/mx4j/src/core/mx4j/monitor
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8483/src/core/mx4j/monitor

Added Files:
	MX4JCounterMonitor.java MX4JCounterMonitorMBean.java 
	MX4JGaugeMonitor.java MX4JGaugeMonitorMBean.java 
	MX4JMonitor.java MX4JMonitorMBean.java 
	MX4JMonitorNotification.java MX4JStringMonitor.java 
	MX4JStringMonitorMBean.java 
Log Message:
Rewritten the monitor package and its tests

--- NEW FILE: MX4JStringMonitor.java ---
/*
 * Copyright (C) The MX4J Contributors.
 * All rights reserved.
 *
 * This software is distributed under the terms of the MX4J License version 1.0.
 * See the terms of the MX4J License in the documentation provided with this software.
 */

package mx4j.monitor;

import javax.management.MBeanNotificationInfo;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.management.monitor.MonitorNotification;

import mx4j.log.Logger;

/**
 * @author <a href="mailto:[email protected]">Simone Bordet</a>
 * @version $Revision: 1.1 $
 */
public class MX4JStringMonitor extends MX4JMonitor implements MX4JStringMonitorMBean
{
   private static final String EMPTY = "";

   private String stringToCompare = EMPTY;
   private boolean notifyMatch;
   private boolean notifyDiffer;

   public MX4JStringMonitor() throws NotCompliantMBeanException
   {
      super(MX4JStringMonitorMBean.class);
   }

   public MX4JStringMonitor(Class management) throws NotCompliantMBeanException
   {
      super(management);
   }

   public MBeanNotificationInfo[] getNotificationInfo()
   {
      // TODO
      return new MBeanNotificationInfo[0];
   }

   public synchronized String getStringToCompare()
   {
      return stringToCompare;
   }

   public synchronized void setStringToCompare(String value) throws IllegalArgumentException
   {
      if (value == null) throw new IllegalArgumentException("String to compare cannot be null");
      this.stringToCompare = value;
   }

   public synchronized boolean getNotifyMatch()
   {
      return notifyMatch;
   }

   public synchronized void setNotifyMatch(boolean notifyMatch)
   {
      this.notifyMatch = notifyMatch;
   }

   public synchronized boolean getNotifyDiffer()
   {
      return notifyDiffer;
   }

   public synchronized void setNotifyDiffer(boolean notifyDiffer)
   {
      this.notifyDiffer = notifyDiffer;
   }

   public String getDerivedGauge(ObjectName objectName)
   {
      StringMonitorInfo info = (StringMonitorInfo)getMonitorInfo(objectName);
      return info.getGauge();
   }

   public long getDerivedGaugeTimeStamp(ObjectName objectName)
   {
      StringMonitorInfo info = (StringMonitorInfo)getMonitorInfo(objectName);
      return info.getTimestamp();
   }

   protected MonitorInfo createMonitorInfo()
   {
      return new StringMonitorInfo();
   }

   protected int compare(String left, String right)
   {
      return left == null ? right == null ? 0 : -1 : right == null ? 1 : left.compareTo(right);
   }

   protected void monitor(ObjectName name, String attribute, Object value, MonitorInfo monitorInfo)
   {
      if (!(value instanceof String))
      {
         sendErrorNotification(monitorInfo, MonitorNotification.OBSERVED_ATTRIBUTE_TYPE_ERROR, "Attribute type must be a String, not " + value.getClass(), name, attribute);
         return;
      }

      String gauge = (String)value;

      String reference = null;
      synchronized (this)
      {
         reference = getStringToCompare();
      }

      Logger logger = getLogger();

      StringMonitorInfo info = (StringMonitorInfo)monitorInfo;
      if (logger.isEnabledFor(Logger.DEBUG))
      {
         logger.debug("Computing gauge, previous values are: " + info);
         logger.debug("Current values are: gauge=" + gauge + ", stringToCompare=" + reference);
      }

      compareAndSendNotification(gauge, reference, info, name, attribute);

      info.setGauge(gauge);
      info.setTimestamp(System.currentTimeMillis());
   }

   private void compareAndSendNotification(String gauge, String reference, StringMonitorInfo info, ObjectName name, String attribute)
   {
      Logger logger = getLogger();

      boolean equals = compare(gauge, reference) == 0;

      if (info.isDifferNotified() && !equals)
      {
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Difference already notified, gauge=" + gauge + ", string-to-compare=" + reference);
         return;
      }
      if (info.isMatchNotified() && equals)
      {
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Match already notified, gauge=" + gauge + ", string-to-compare=" + reference);
         return;
      }

      if (equals)
      {
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Gauge matches, gauge=" + gauge + ", string-to-compare=" + reference);
         info.setDifferNotified(false);
         if (getNotifyMatch())
         {
            if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Sending string match notification");
            info.setMatchNotified(true);
            sendNotification(MonitorNotification.STRING_TO_COMPARE_VALUE_MATCHED, "Gauge " + gauge + " matched " + reference, name, attribute, gauge, reference);
         }
         else
         {
            info.setMatchNotified(false);
            if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("StringMonitor is configured in non-match-notification mode");
         }
      }
      else
      {
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Gauge differs, gauge=" + gauge + ", string-to-compare=" + reference);
         info.setMatchNotified(false);
         if (getNotifyDiffer())
         {
            if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Sending string differ notification");
            info.setDifferNotified(true);
            sendNotification(MonitorNotification.STRING_TO_COMPARE_VALUE_DIFFERED, "Gauge " + gauge + " differs from " + reference, name, attribute, gauge, reference);
         }
         else
         {
            info.setDifferNotified(false);
            if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("StringMonitor is configured in non-differ-notification mode");
         }
      }
   }

   protected class StringMonitorInfo extends MonitorInfo
   {
      private String gauge;
      private long timestamp;
      private boolean matchNotified;
      private boolean differNotified;

      public String getGauge()
      {
         return gauge;
      }

      public void setGauge(String gauge)
      {
         this.gauge = gauge;
      }

      public long getTimestamp()
      {
         return timestamp;
      }

      public void setTimestamp(long timestamp)
      {
         this.timestamp = timestamp;
      }

      public boolean isMatchNotified()
      {
         return matchNotified;
      }

      public void setMatchNotified(boolean matchNotified)
      {
         this.matchNotified = matchNotified;
      }

      public boolean isDifferNotified()
      {
         return differNotified;
      }

      public void setDifferNotified(boolean differNotified)
      {
         this.differNotified = differNotified;
      }

      public String toString()
      {
         StringBuffer buffer = new StringBuffer(super.toString());
         buffer.append(", gauge=").append(getGauge());
         buffer.append(", matchNotified=").append(isMatchNotified());
         buffer.append(", differNotified=").append(isDifferNotified());
         return buffer.toString();
      }
   }
}

--- NEW FILE: MX4JGaugeMonitorMBean.java ---
/*
 * Copyright (C) The MX4J contributors.
 * All rights reserved.
 *
 * This software is distributed under the terms of the MX4J License version 1.0.
 * See the terms of the MX4J License in the documentation provided with this software.
 */

package mx4j.monitor;

import javax.management.ObjectName;

/**
 * @author <a href="mailto:[email protected]">Carlos Quiroz</a>
 * @version $Revision: 1.1 $
 */
public interface MX4JGaugeMonitorMBean extends MX4JMonitorMBean
{
   public Number getDerivedGauge(ObjectName objectName);

   public long getDerivedGaugeTimeStamp(ObjectName objectName);

   public Number getHighThreshold();

   public Number getLowThreshold();

   public void setThresholds(Number highValue, Number lowValue) throws IllegalArgumentException;

   public boolean getNotifyHigh();

   public void setNotifyHigh(boolean value);

   public boolean getNotifyLow();

   public void setNotifyLow(boolean value);

   public boolean getDifferenceMode();

   public void setDifferenceMode(boolean value);
}

--- NEW FILE: MX4JCounterMonitor.java ---
/*
 * Copyright (C) The MX4J contributors.
 * All rights reserved.
 *
 * This software is distributed under the terms of the MX4J License version 1.0.
 * See the terms of the MX4J License in the documentation provided with this software.
 */

package mx4j.monitor;

import java.math.BigInteger;
import javax.management.MBeanNotificationInfo;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.management.monitor.MonitorNotification;

import mx4j.log.Logger;

/**
 * @author <a href="mailto:[email protected]">Simone Bordet</a>
 * @version $Revision: 1.1 $
 */
public class MX4JCounterMonitor extends MX4JMonitor implements MX4JCounterMonitorMBean
{
   private static Integer ZERO = new Integer(0);

   private Number threshold = ZERO;
   private Number offset = ZERO;
   private Number modulus = ZERO;
   private boolean notify;
   private boolean differenceMode;

   public MX4JCounterMonitor() throws NotCompliantMBeanException
   {
      super(MX4JCounterMonitorMBean.class);
   }

   protected MX4JCounterMonitor(Class management) throws NotCompliantMBeanException
   {
      super(management);
   }

   public MBeanNotificationInfo[] getNotificationInfo()
   {
      // TODO
      return new MBeanNotificationInfo[0];
   }

   public synchronized Number getInitThreshold()
   {
      return threshold;
   }

   public void setInitThreshold(Number threshold) throws IllegalArgumentException
   {
      if (threshold == null || compare(threshold, ZERO) < 0) throw new IllegalArgumentException("Threshold cannot be " + threshold);
      this.threshold = threshold;
   }

   public synchronized Number getOffset()
   {
      return offset;
   }

   public void setOffset(Number offset) throws IllegalArgumentException
   {
      if (offset == null || compare(offset, ZERO) < 0) throw new IllegalArgumentException("Offset cannot be " + offset);
      this.offset = offset;
   }

   public Number getModulus()
   {
      return modulus;
   }

   public void setModulus(Number modulus) throws IllegalArgumentException
   {
      if (modulus == null || compare(modulus, ZERO) < 0) throw new IllegalArgumentException("Modulus cannot be " + modulus);
      this.modulus = modulus;
   }

   public boolean getNotify()
   {
      return notify;
   }

   public void setNotify(boolean notify)
   {
      this.notify = notify;
   }

   public boolean getDifferenceMode()
   {
      return differenceMode;
   }

   public void setDifferenceMode(boolean mode)
   {
      this.differenceMode = mode;
   }

   public Number getDerivedGauge(ObjectName name)
   {
      CounterMonitorInfo info = (CounterMonitorInfo)getMonitorInfo(name);
      return info.getGauge();
   }

   public long getDerivedGaugeTimeStamp(ObjectName name)
   {
      CounterMonitorInfo info = (CounterMonitorInfo)getMonitorInfo(name);
      return info.getTimestamp();
   }

   public Number getThreshold(ObjectName name)
   {
      CounterMonitorInfo info = (CounterMonitorInfo)getMonitorInfo(name);
      return info.getThreshold();
   }

   protected int compare(Number left, Number right)
   {
      if (left instanceof BigInteger && right instanceof BigInteger) return ((BigInteger)left).compareTo((BigInteger)right);
      if (left.longValue() == right.longValue()) return 0;
      return left.longValue() > right.longValue() ? 1 : -1;
   }

   protected Number sum(Number left, Number right)
   {
      if (left instanceof BigInteger && right instanceof BigInteger) return ((BigInteger)left).add((BigInteger)right);
      if (left instanceof BigInteger) return ((BigInteger)left).add(BigInteger.valueOf(right.longValue()));
      if (right instanceof BigInteger) return ((BigInteger)right).add(BigInteger.valueOf(left.longValue()));
      if (left instanceof Long || right instanceof Long) return new Long(left.longValue() + right.longValue());
      if (left instanceof Integer || right instanceof Integer) return new Integer(left.intValue() + right.intValue());
      if (left instanceof Short || right instanceof Short) return new Short((short)(left.shortValue() + right.shortValue()));
      if (left instanceof Byte || right instanceof Byte) return new Byte((byte)(left.byteValue() + right.byteValue()));
      return null;
   }

   protected Number sub(Number left, Number right)
   {
      if (left instanceof BigInteger && right instanceof BigInteger) return ((BigInteger)left).subtract((BigInteger)right);
      if (left instanceof BigInteger) return ((BigInteger)left).subtract(BigInteger.valueOf(right.longValue()));
      if (left instanceof Long || right instanceof Long) return new Long(left.longValue() - right.longValue());
      if (left instanceof Integer || right instanceof Integer) return new Integer(left.intValue() - right.intValue());
      if (left instanceof Short || right instanceof Short) return new Short((short)(left.shortValue() - right.shortValue()));
      if (left instanceof Byte || right instanceof Byte) return new Byte((byte)(left.byteValue() - right.byteValue()));
      return null;
   }

   protected void monitor(ObjectName name, String attribute, Object value, MonitorInfo monitorInfo)
   {
      if (!(value instanceof Number))
      {
         sendErrorNotification(monitorInfo, MonitorNotification.OBSERVED_ATTRIBUTE_TYPE_ERROR, "Attribute type must be a Number, not " + value.getClass(), name, attribute);
         return;
      }

      Number gauge = (Number)value;

      if (compare(gauge, ZERO) < 0)
      {
         // Spec requires counter monitor values not be negative
         sendErrorNotification(monitorInfo, MonitorNotification.THRESHOLD_ERROR, "Attribute value cannot be negative " + gauge, name, attribute);
         return;
      }

      // Spec requires that types of gauge, threshold, offset and modulus be affine
      Number threshold = null;
      Number offset = null;
      Number modulus = null;
      synchronized (this)
      {
         threshold = getThreshold(name);
         offset = getOffset();
         modulus = getModulus();
      }
      Class gaugeClass = gauge.getClass();
      if (threshold != ZERO && threshold.getClass() != gaugeClass)
      {
         sendErrorNotification(monitorInfo, MonitorNotification.THRESHOLD_ERROR, "Threshold type " + threshold.getClass() + " must be of same type of the attribute " + gaugeClass, name, attribute);
         return;
      }
      if (offset != ZERO && offset.getClass() != gaugeClass)
      {
         sendErrorNotification(monitorInfo, MonitorNotification.THRESHOLD_ERROR, "Offset type " + offset.getClass() + " must be of same type of the attribute " + gaugeClass, name, attribute);
         return;
      }
      if (modulus != ZERO && modulus.getClass() != gaugeClass)
      {
         sendErrorNotification(monitorInfo, MonitorNotification.THRESHOLD_ERROR, "Modulus type " + modulus.getClass() + " must be of same type of the attribute " + gaugeClass, name, attribute);
         return;
      }

      Logger logger = getLogger();
      // Contains previous gauge and threshold
      CounterMonitorInfo info = (CounterMonitorInfo)monitorInfo;
      if (logger.isEnabledFor(Logger.DEBUG))
      {
         logger.debug("Computing gauge, previous values are: " + info);
         logger.debug("Current values are: threshold=" + threshold + ", offset=" + offset + ", modulus=" + modulus);
      }

      boolean updateThreshold = false;
      if (getDifferenceMode())
      {
         Number diffGauge = sub(gauge, info.getGauge());
         if (compare(diffGauge, ZERO) < 0) diffGauge = sum(diffGauge, getModulus());
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("CounterMonitor in difference mode, difference gauge=" + diffGauge);
         updateThreshold = compareAndSendNotification(diffGauge, threshold, info, name, attribute);
      }
      else
      {
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("CounterMonitor in absolute mode, gauge=" + gauge);
         updateThreshold = compareAndSendNotification(gauge, threshold, info, name, attribute);
      }

      if (updateThreshold)
      {
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Updating threshold, old value = " + threshold);

         // Offset the threshold until exceeds the gauge
         if (compare(offset, ZERO) != 0)
            while (compare(threshold, gauge) <= 0) threshold = sum(threshold, offset);

         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Threshold has been offset, new value = " + threshold);

         // Check for rollover
         if (getModulus() != ZERO && compare(threshold, getModulus()) > 0) threshold = getInitThreshold();

         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Threshold has been rolled over, new value = " + threshold);
      }
      else
      {
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("No need to update the threshold, value remains = " + threshold);
      }

      CounterMonitorInfo newInfo = (CounterMonitorInfo)createMonitorInfo();
      newInfo.setThresholdNotified(info.isThresholdNotified());
      newInfo.setGauge(gauge);
      newInfo.setTimestamp(System.currentTimeMillis());
      newInfo.setThreshold(threshold);
      putMonitorInfo(name, newInfo);
   }

   private boolean compareAndSendNotification(Number gauge, Number threshold, CounterMonitorInfo info, ObjectName name, String attribute)
   {
      Logger logger = getLogger();

      if (info.isThresholdNotified() && compare(gauge, info.getGauge()) == 0)
      {
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Threshold exceeded already notified, gauge did not change: " + gauge);
         return false;
      }

      if (compare(gauge, threshold) >= 0)
      {
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Gauge above threshold: gauge=" + gauge + ", threshold=" + threshold);
         if (getNotify())
         {
            if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Sending threshold exceeded notification");
            info.setThresholdNotified(true);
            sendNotification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, "Threshold " + threshold + " exceeded: " + gauge, name, attribute, gauge, threshold);
         }
         else
         {
            info.setThresholdNotified(false);
            if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("CounterMonitor is configured in non-notification mode");
         }
         return true;
      }
      else
      {
         info.setThresholdNotified(false);
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Gauge below threshold: gauge=" + gauge + ", threshold=" + threshold);
         return false;
      }
   }

   protected MonitorInfo createMonitorInfo()
   {
      return new CounterMonitorInfo();
   }

   protected class CounterMonitorInfo extends MonitorInfo
   {
      private boolean thresholdNotified;
      private Number gauge = ZERO;
      private long timestamp;
      private Number threshold = ZERO;

      public void setThresholdNotified(boolean thresholdNotified)
      {
         this.thresholdNotified = thresholdNotified;
      }

      public void setGauge(Number gauge)
      {
         this.gauge = gauge;
      }

      public void setTimestamp(long timestamp)
      {
         this.timestamp = timestamp;
      }

      public void setThreshold(Number threshold)
      {
         this.threshold = threshold;
      }

      public boolean isThresholdNotified()
      {
         return thresholdNotified;
      }

      public Number getGauge()
      {
         return gauge;
      }

      public long getTimestamp()
      {
         return timestamp;
      }

      public Number getThreshold()
      {
         if (threshold == ZERO) return getInitThreshold();
         return threshold;
      }

      public String toString()
      {
         StringBuffer buffer = new StringBuffer(super.toString());
         buffer.append(", thresholdNotified=").append(isThresholdNotified());
         buffer.append(", gauge=").append(getGauge());
         buffer.append(", threshold=").append(threshold);
         return buffer.toString();
      }
   }
}

--- NEW FILE: MX4JStringMonitorMBean.java ---
/*
 * Copyright (C) The MX4J contributors.
 * All rights reserved.
 *
 * This software is distributed under the terms of the MX4J License version 1.0.
 * See the terms of the MX4J License in the documentation provided with this software.
 */

package mx4j.monitor;

import javax.management.ObjectName;

/**
 * @author <a href="mailto:[email protected]">Carlos Quiroz</a>
 * @version $Revision: 1.1 $
 */
public interface MX4JStringMonitorMBean extends MX4JMonitorMBean
{
   public String getDerivedGauge(ObjectName objectName);

   public long getDerivedGaugeTimeStamp(ObjectName objectName);

   public String getStringToCompare();

   public void setStringToCompare(String value) throws IllegalArgumentException;

   public boolean getNotifyMatch();

   public void setNotifyMatch(boolean value);

   public boolean getNotifyDiffer();

   public void setNotifyDiffer(boolean value);
}

--- NEW FILE: MX4JMonitorNotification.java ---
/*
 * Copyright (C) The MX4J contributors.
 * All rights reserved.
 *
 * This software is distributed under the terms of the MX4J License version 1.0.
 * See the terms of the MX4J License in the documentation provided with this software.
 */

package mx4j.monitor;

import javax.management.Notification;
import javax.management.ObjectName;

/**
 * @author <a href="mailto:[email protected]">Carlos Quiroz</a>
 * @version $Revision: 1.1 $
 */
public class MX4JMonitorNotification extends Notification
{
   private final ObjectName observedObject;
   private final String observedAttribute;
   private final Object derivedGauge;
   private final Object trigger;

   public MX4JMonitorNotification(String type, Object source, long sequenceNumber, long timeStamp, String message, ObjectName monitoredName, String attribute, Object gauge, Object trigger)
   {
      super(type, source, sequenceNumber, timeStamp, message);
      this.observedObject = monitoredName;
      this.observedAttribute = attribute;
      this.derivedGauge = gauge;
      this.trigger = trigger;
   }

   public ObjectName getObservedObject()
   {
      return observedObject;
   }

   public Object getDerivedGauge()
   {
      return derivedGauge;
   }

   public String getObservedAttribute()
   {
      return observedAttribute;
   }

   public Object getTrigger()
   {
      return trigger;
   }

   public String toString()
   {
      StringBuffer buffer = new StringBuffer("[");
      buffer.append(super.toString()).append(", ");
      buffer.append("observed=").append(getObservedObject()).append(", ");
      buffer.append("gauge=").append(getDerivedGauge()).append(", ");
      buffer.append("attribute=").append(getObservedAttribute()).append(", ");
      buffer.append("trigger=").append(getTrigger()).append("]");
      return buffer.toString();
   }
}

--- NEW FILE: MX4JMonitorMBean.java ---
/*
 * Copyright (C) The MX4J Contributors.
 * All rights reserved.
 *
 * This software is distributed under the terms of the MX4J License version 1.0.
 * See the terms of the MX4J License in the documentation provided with this software.
 */

package mx4j.monitor;

import javax.management.ObjectName;

/**
 * @author <a href="mailto:[email protected]">Simone Bordet</a>
 * @version $Revision: 1.1 $
 */
public interface MX4JMonitorMBean
{
   public void start();

   public void stop();

   public boolean isActive();

   public void addObservedObject(ObjectName object) throws IllegalArgumentException;

   public void removeObservedObject(ObjectName object);

   public boolean containsObservedObject(ObjectName object);

   public ObjectName[] getObservedObjects();

   public String getObservedAttribute();

   public void setObservedAttribute(String attribute);

   public long getGranularityPeriod();

   public void setGranularityPeriod(long period) throws java.lang.IllegalArgumentException;
}

--- NEW FILE: MX4JGaugeMonitor.java ---
/*
 * Copyright (C) The MX4J Contributors.
 * All rights reserved.
 *
 * This software is distributed under the terms of the MX4J License version 1.0.
 * See the terms of the MX4J License in the documentation provided with this software.
 */

package mx4j.monitor;

import java.math.BigDecimal;
import java.math.BigInteger;
import javax.management.MBeanNotificationInfo;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.management.monitor.MonitorNotification;

import mx4j.log.Logger;

/**
 * @author <a href="mailto:[email protected]">Simone Bordet</a>
 * @version $Revision: 1.1 $
 */
public class MX4JGaugeMonitor extends MX4JMonitor implements MX4JGaugeMonitorMBean
{
   private static Integer ZERO = new Integer(0);

   private Number highThreshold = ZERO;
   private Number lowThreshold = ZERO;
   private boolean notifyHigh;
   private boolean notifyLow;
   private boolean differenceMode;

   public MX4JGaugeMonitor() throws NotCompliantMBeanException
   {
      super(MX4JGaugeMonitorMBean.class);
   }

   protected MX4JGaugeMonitor(Class management) throws NotCompliantMBeanException
   {
      super(management);
   }

   public MBeanNotificationInfo[] getNotificationInfo()
   {
      // TODO
      return new MBeanNotificationInfo[0];
   }

   public synchronized Number getHighThreshold()
   {
      return highThreshold;
   }

   public synchronized Number getLowThreshold()
   {
      return lowThreshold;
   }

   public void setThresholds(Number highValue, Number lowValue) throws IllegalArgumentException
   {
      if (highValue == null) throw new IllegalArgumentException("High Threshold cannot be null");
      if (lowValue == null) throw new IllegalArgumentException("Low Threshold cannot be null");
      if (highValue.getClass() != lowValue.getClass()) throw new IllegalArgumentException("Thresholds must be of the same type");
      if (compare(highValue, lowValue) < 0) throw new IllegalArgumentException("High threshold cannot be lower than low threshold");
      highThreshold = highValue;
      lowThreshold = lowValue;
   }

   public synchronized boolean getNotifyHigh()
   {
      return notifyHigh;
   }

   public synchronized boolean getNotifyLow()
   {
      return notifyLow;
   }

   public synchronized void setNotifyHigh(boolean notifyHigh)
   {
      this.notifyHigh = notifyHigh;
   }

   public synchronized void setNotifyLow(boolean notifyLow)
   {
      this.notifyLow = notifyLow;
   }

   public synchronized boolean getDifferenceMode()
   {
      return differenceMode;
   }

   public synchronized void setDifferenceMode(boolean differenceMode)
   {
      this.differenceMode = differenceMode;
   }

   public Number getDerivedGauge(ObjectName objectName)
   {
      GaugeMonitorInfo info = (GaugeMonitorInfo)getMonitorInfo(objectName);
      return info.getGauge();
   }

   public long getDerivedGaugeTimeStamp(ObjectName objectName)
   {
      GaugeMonitorInfo info = (GaugeMonitorInfo)getMonitorInfo(objectName);
      return info.getTimestamp();
   }

   protected MonitorInfo createMonitorInfo()
   {
      return new GaugeMonitorInfo();
   }

   protected int compare(Number left, Number right)
   {
      if (left instanceof BigDecimal && right instanceof BigDecimal) return ((BigDecimal)left).compareTo((BigDecimal)right);
      if (left instanceof BigInteger && right instanceof BigInteger) return ((BigInteger)left).compareTo((BigInteger)right);
      return new Double(left.doubleValue()).compareTo(new Double(right.doubleValue()));
   }

   protected Number sub(Number left, Number right)
   {
      if (left instanceof BigDecimal && right instanceof BigDecimal) return ((BigDecimal)left).subtract((BigDecimal)right);
      if (left instanceof BigDecimal) return ((BigDecimal)left).subtract(new BigDecimal(right.doubleValue()));
      if (left instanceof BigInteger && right instanceof BigInteger) return ((BigInteger)left).subtract((BigInteger)right);
      if (left instanceof BigInteger) return ((BigInteger)left).subtract(BigInteger.valueOf(right.longValue()));
      if (left instanceof Double || right instanceof Double) return new Double(left.doubleValue() - right.doubleValue());
      if (left instanceof Float || right instanceof Float) return new Float(left.floatValue() - right.floatValue());
      if (left instanceof Long || right instanceof Long) return new Long(left.longValue() - right.longValue());
      if (left instanceof Integer || right instanceof Integer) return new Integer(left.intValue() - right.intValue());
      if (left instanceof Short || right instanceof Short) return new Short((short)(left.shortValue() - right.shortValue()));
      if (left instanceof Byte || right instanceof Byte) return new Byte((byte)(left.byteValue() - right.byteValue()));
      return null;
   }

   protected void monitor(ObjectName name, String attribute, Object value, MonitorInfo monitorInfo)
   {
      if (!(value instanceof Number))
      {
         sendErrorNotification(monitorInfo, MonitorNotification.OBSERVED_ATTRIBUTE_TYPE_ERROR, "Attribute type must be a Number, not " + value.getClass(), name, attribute);
         return;
      }

      Number gauge = (Number)value;

      // Spec requires that types of gauge, high threshold and low threshold be affine
      Number high = null;
      Number low = null;
      synchronized (this)
      {
         high = getHighThreshold();
         low = getLowThreshold();
      }
      Class gaugeClass = gauge.getClass();
      if (high != ZERO && high.getClass() != gaugeClass)
      {
         sendErrorNotification(monitorInfo, MonitorNotification.THRESHOLD_ERROR, "Threshold type " + high.getClass() + " must be of same type of the attribute " + gaugeClass, name, attribute);
         return;
      }
      if (low != ZERO && low.getClass() != gaugeClass)
      {
         sendErrorNotification(monitorInfo, MonitorNotification.THRESHOLD_ERROR, "Offset type " + low.getClass() + " must be of same type of the attribute " + gaugeClass, name, attribute);
         return;
      }

      Logger logger = getLogger();

      // Contains previous gauge
      GaugeMonitorInfo info = (GaugeMonitorInfo)monitorInfo;
      if (logger.isEnabledFor(Logger.DEBUG))
      {
         logger.debug("Computing gauge, previous values are: " + info);
         logger.debug("Current values are: gauge=" + gauge + ", highThreshold=" + high + ", lowThreshold=" + low);
      }

      if (getDifferenceMode())
      {
         Number diffGauge = sub(gauge, info.getGauge());
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("CounterMonitor in difference mode, difference gauge=" + diffGauge);
         compareAndSendNotification(diffGauge, low, high, info, name, attribute);
      }
      else
      {
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("CounterMonitor in absolute mode, gauge=" + gauge);
         compareAndSendNotification(gauge, low, high, info, name, attribute);
      }

      info.setGauge(gauge);
      info.setTimestamp(System.currentTimeMillis());
   }

   private void compareAndSendNotification(Number gauge, Number low, Number high, GaugeMonitorInfo info, ObjectName name, String attribute)
   {
      Logger logger = getLogger();

      if (info.isHighNotified() && compare(gauge, low) > 0)
      {
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("High threshold " + high + " already notified, gauge " + gauge + " not below low threshold " + low);
         return;
      }
      if (info.isLowNotified() && compare(gauge, high) < 0)
      {
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Low threshold " + low + " already notified, gauge " + gauge + " not above high threshold " + high);
         return;
      }

      if (compare(gauge, high) >= 0)
      {
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Gauge above high threshold: gauge=" + gauge + ", high threshold=" + high + ", low threshold=" + low);
         info.setLowNotified(false);
         if (getNotifyHigh())
         {
            if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Sending high threshold exceeded notification");
            info.setHighNotified(true);
            sendNotification(MonitorNotification.THRESHOLD_HIGH_VALUE_EXCEEDED, "High threshold " + high + " exceeded: " + gauge, name, attribute, gauge, high);
         }
         else
         {
            info.setHighNotified(false);
            if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("GaugeMonitor is configured in non-high-notification mode");
         }
      }
      else if (compare(gauge, low) <= 0)
      {
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Gauge below low threshold: gauge=" + gauge + ", low threshold=" + low + ", high threshold=" + high);
         info.setHighNotified(false);
         if (getNotifyLow())
         {
            if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Sending low threshold exceeded notification");
            info.setLowNotified(true);
            sendNotification(MonitorNotification.THRESHOLD_LOW_VALUE_EXCEEDED, "Low threshold " + low + " exceeded: " + gauge, name, attribute, gauge, low);
         }
         else
         {
            info.setLowNotified(false);
            if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("GaugeMonitor is configured in non-low-notification mode");
         }
      }
      else
      {
         info.setHighNotified(false);
         info.setLowNotified(false);
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Gauge between thresholds: gauge=" + gauge + ", low threshold=" + low + ", high threshold=" + high);
      }
   }

   protected class GaugeMonitorInfo extends MonitorInfo
   {
      private Number gauge = ZERO;
      private long timestamp;
      private boolean highNotified;
      private boolean lowNotified;

      public Number getGauge()
      {
         return gauge;
      }

      public void setGauge(Number gauge)
      {
         this.gauge = gauge;
      }

      public long getTimestamp()
      {
         return timestamp;
      }

      public void setTimestamp(long timestamp)
      {
         this.timestamp = timestamp;
      }

      public boolean isHighNotified()
      {
         return highNotified;
      }

      public void setHighNotified(boolean highNotified)
      {
         this.highNotified = highNotified;
      }

      public boolean isLowNotified()
      {
         return lowNotified;
      }

      public void setLowNotified(boolean lowNotified)
      {
         this.lowNotified = lowNotified;
      }

      public String toString()
      {
         StringBuffer buffer = new StringBuffer(super.toString());
         buffer.append(", gauge=").append(getGauge());
         buffer.append(", lowNotified=").append(isLowNotified());
         buffer.append(", highNotified=").append(isHighNotified());
         return buffer.toString();
      }
   }
}

--- NEW FILE: MX4JCounterMonitorMBean.java ---
/*
 * Copyright (C) The MX4J contributors.
 * All rights reserved.
 *
 * This software is distributed under the terms of the MX4J License version 1.0.
 * See the terms of the MX4J License in the documentation provided with this software.
 */

package mx4j.monitor;

import javax.management.ObjectName;

/**
 * @author <a href="mailto:[email protected]">Carlos Quiroz</a>
 * @version $Revision: 1.1 $
 */
public interface MX4JCounterMonitorMBean extends MX4JMonitorMBean
{
   public Number getDerivedGauge(ObjectName objectName);

   public long getDerivedGaugeTimeStamp(ObjectName objectName);

   public Number getThreshold(ObjectName objectName);

   public Number getInitThreshold();

   public void setInitThreshold(Number value) throws IllegalArgumentException;

   public Number getOffset();

   public void setOffset(Number value) throws IllegalArgumentException;

   public Number getModulus();

   public void setModulus(Number value) throws IllegalArgumentException;

   public boolean getNotify();

   public void setNotify(boolean value);

   public boolean getDifferenceMode();

   public void setDifferenceMode(boolean value);
}

--- NEW FILE: MX4JMonitor.java ---
/*
 * Copyright (C) The MX4J contributors.
 * All rights reserved.
 *
 * This software is distributed under the terms of the MX4J License version 1.0.
 * See the terms of the MX4J License in the documentation provided with this software.
 */

package mx4j.monitor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.StandardMBean;
import javax.management.monitor.MonitorNotification;

import mx4j.log.Log;
import mx4j.log.Logger;
import mx4j.timer.TimeQueue;
import mx4j.timer.TimeTask;

/**
 * The class that implements the Monitor behavior of the JMX specification.
 * IMPLEMENTATION NOTE:
 * There is one single thread that handles monitoring, for all monitor objects.
 * There is one single task per each monitor object that runs.
 * The queue will have possibly many tasks per each monitor type.
 * Each monitor handles many MBeans, but only one attribute; however, both MBeans and attribute can be changed,
 * though it would be a strange way to use the monitor.
 *
 * @author <a href="mailto:[email protected]">Simone Bordet</a>
 * @version $Revision: 1.1 $
 */
public abstract class MX4JMonitor extends StandardMBean implements MX4JMonitorMBean, MBeanRegistration, NotificationEmitter
{
   private static TimeQueue queue = new TimeQueue();
   private static int sequenceNumber;

   private NotificationBroadcasterSupport emitter;
   private MBeanServer server;
   private boolean active;
   private List observeds = new ArrayList();
   private volatile String attribute;
   private volatile long granularity = 10 * 1000; // Spec says default is 10 seconds
   private boolean errorNotified;

   static
   {
      queue.start();
   }

   private final TimeTask task = new MonitorTask();
   private final Map infos = new HashMap();

   protected MX4JMonitor(Class management) throws NotCompliantMBeanException
   {
      super(management);
   }

   // TODO: override descriptions for this MBean

   public ObjectName preRegister(MBeanServer server, ObjectName name)
   {
      this.server = server;
      return name;
   }

   public void postRegister(Boolean registrationDone)
   {
   }

   public void preDeregister()
   {
      stop();
   }

   public void postDeregister()
   {
      server = null;
   }

   protected NotificationBroadcasterSupport createNotificationEmitter()
   {
      return new NotificationBroadcasterSupport();
   }

   public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException
   {
      emitter.addNotificationListener(listener, filter, handback);
   }

   public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException
   {
      emitter.removeNotificationListener(listener);
   }

   public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException
   {
      emitter.removeNotificationListener(listener, filter, handback);
   }

   public void sendNotification(Notification notification)
   {
      emitter.sendNotification(notification);
   }

   public synchronized void start()
   {
      if (isActive()) return;
      active = true;
      startMonitor();
   }

   public synchronized void stop()
   {
      if (!isActive()) return;
      active = false;
      stopMonitor();
   }

   public synchronized boolean isActive()
   {
      return active;
   }

   public synchronized void addObservedObject(ObjectName name) throws IllegalArgumentException
   {
      if (name == null) throw new IllegalArgumentException("Observed ObjectName cannot be null");
      if (!containsObservedObject(name))
      {
         observeds.add(name);
         putMonitorInfo(name, createMonitorInfo());
      }
   }

   public synchronized void removeObservedObject(ObjectName name)
   {
      observeds.remove(name);
      removeMonitorInfo(name);
   }

   public synchronized boolean containsObservedObject(ObjectName name)
   {
      return observeds.contains(name);
   }

   public synchronized ObjectName[] getObservedObjects()
   {
      return (ObjectName[])observeds.toArray(new ObjectName[observeds.size()]);
   }

   public synchronized void clearObservedObjects()
   {
      observeds.clear();
   }

   public synchronized String getObservedAttribute()
   {
      return attribute;
   }

   public synchronized void setObservedAttribute(String attribute)
   {
      this.attribute = attribute;
   }

   public synchronized long getGranularityPeriod()
   {
      return granularity;
   }

   public synchronized void setGranularityPeriod(long granularity) throws IllegalArgumentException
   {
      if (granularity <= 0) throw new IllegalArgumentException("Granularity must be greater than zero");
      this.granularity = granularity;
   }

   protected void startMonitor()
   {
      if (emitter == null) this.emitter = createNotificationEmitter();
      queue.schedule(task);
   }

   protected void stopMonitor()
   {
      queue.unschedule(task);
   }

   protected Logger getLogger()
   {
      return Log.getLogger(getClass().getName());
   }

   protected void sendNotification(String type, String message, ObjectName name, String attribute, Object gauge, Object trigger)
   {
      int sequence = 0;
      synchronized (MX4JMonitor.class)
      {
         sequence = ++sequenceNumber;
      }

      Notification notification = createMonitorNotification(type, sequence, message, name, attribute, gauge, trigger);
      sendNotification(notification);
   }

   protected Notification createMonitorNotification(String type, long sequence, String message, ObjectName observed, String attribute, Object gauge, Object trigger)
   {
      return new MX4JMonitorNotification(type, this, sequence, System.currentTimeMillis(), message, observed, attribute, gauge, trigger);
   }

   protected abstract void monitor(ObjectName name, String attribute, Object value, MonitorInfo info);

   protected abstract MonitorInfo createMonitorInfo();

   protected synchronized MonitorInfo getMonitorInfo(ObjectName name)
   {
      return (MonitorInfo)infos.get(name);
   }

   protected synchronized void putMonitorInfo(ObjectName name, MonitorInfo info)
   {
      infos.put(name, info);
   }

   protected synchronized void removeMonitorInfo(ObjectName name)
   {
      infos.remove(name);
   }

   protected void sendErrorNotification(MonitorInfo info, String type, String message, ObjectName observed, String attribute)
   {
      if (!info.isErrorNotified())
      {
         info.setErrorNotified(true);
         sendNotification(type, message, observed, attribute, null, null);
      }
   }

   private class MonitorTask extends TimeTask
   {
      protected boolean isPeriodic()
      {
         return true;
      }

      protected long getPeriod()
      {
         return getGranularityPeriod();
      }

      public boolean getFixedRate()
      {
         return true;
      }

      public void run()
      {
         if (!isActive()) return;

         long start = System.currentTimeMillis();

         String attribute = getObservedAttribute();

         if (server == null)
         {
            if (!errorNotified)
            {
               errorNotified = true;
               sendNotification(MonitorNotification.RUNTIME_ERROR, "Monitors must be registered in the MBeanServer", null, attribute, null, null);
            }
         }
         else
         {
            errorNotified = false;

            // If no attribute, sleep and try again
            if (attribute != null)
            {
               ObjectName[] names = getObservedObjects();
               // If no names, sleep and try again
               for (int i = 0; i < names.length; i++)
               {
                  ObjectName name = names[i];
                  MonitorInfo info = getMonitorInfo(name);
                  if (info == null) continue;
                  try
                  {
                     Object value = server.getAttribute(name, attribute);
                     // If no value, sleep and try again
                     if (value != null)
                     {
                        monitor(name, attribute, value, info);
                     }
                  }
                  catch (InstanceNotFoundException x)
                  {
                     sendErrorNotification(info, MonitorNotification.OBSERVED_OBJECT_ERROR, "Could not find observed MBean", name, attribute);
                  }
                  catch (AttributeNotFoundException x)
                  {
                     sendErrorNotification(info, MonitorNotification.OBSERVED_ATTRIBUTE_ERROR, "Could not find observed attribute " + attribute, name, attribute);
                  }
                  catch (MBeanException x)
                  {
                     sendErrorNotification(info, MonitorNotification.RUNTIME_ERROR, x.toString(), name, attribute);
                  }
                  catch (ReflectionException x)
                  {
                     sendErrorNotification(info, MonitorNotification.RUNTIME_ERROR, x.toString(), name, attribute);
                  }
               }
            }
         }

         long end = System.currentTimeMillis();
         long elapsed = end - start;
         Logger logger = getLogger();
         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Monitored attribute " + attribute + " in " + elapsed + " ms");
      }
   }

   protected class MonitorInfo
   {
      private boolean errorNotified;

      public boolean isErrorNotified()
      {
         return errorNotified;
      }

      public void setErrorNotified(boolean errorNotified)
      {
         this.errorNotified = errorNotified;
      }

      public String toString()
      {
         return "errorNotified=" + isErrorNotified();
      }
   }
}



-------------------------------------------------------
This SF.Net email is sponsored by BEA Weblogic Workshop
FREE Java Enterprise J2EE developer tools!
Get your free copy of BEA WebLogic Workshop 8.1 today.
http://ads.osdn.com/?ad_id=5047&alloc_id=10808&op=click