/* * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.management.monitor; import static com.sun.jmx.defaults.JmxProperties.MONITOR_LOGGER; import java.util.logging.Level; import javax.management.MBeanNotificationInfo; import javax.management.ObjectName; import static javax.management.monitor.Monitor.NumericalType.*; import static javax.management.monitor.MonitorNotification.*; /** * Defines a monitor MBean designed to observe the values of a gauge attribute. * * <P> A gauge monitor observes an attribute that is continuously * variable with time. A gauge monitor sends notifications as * follows: * * <UL> * * <LI> if the attribute value is increasing and becomes equal to or * greater than the high threshold value, a {@link * MonitorNotification#THRESHOLD_HIGH_VALUE_EXCEEDED threshold high * notification} is sent. The notify high flag must be set to * <CODE>true</CODE>. * * <BR>Subsequent crossings of the high threshold value do not cause * further notifications unless the attribute value becomes equal to * or less than the low threshold value.</LI> * * <LI> if the attribute value is decreasing and becomes equal to or * less than the low threshold value, a {@link * MonitorNotification#THRESHOLD_LOW_VALUE_EXCEEDED threshold low * notification} is sent. The notify low flag must be set to * <CODE>true</CODE>. * * <BR>Subsequent crossings of the low threshold value do not cause * further notifications unless the attribute value becomes equal to * or greater than the high threshold value.</LI> * * </UL> * * This provides a hysteresis mechanism to avoid repeated triggering * of notifications when the attribute value makes small oscillations * around the high or low threshold value. * * <P> If the gauge difference mode is used, the value of the derived * gauge is calculated as the difference between the observed gauge * values for two successive observations. * * <BR>The derived gauge value (V[t]) is calculated using the following method: * <UL> * <LI>V[t] = gauge[t] - gauge[t-GP]</LI> * </UL> * * This implementation of the gauge monitor requires the observed * attribute to be of the type integer or floating-point * (<CODE>Byte</CODE>, <CODE>Integer</CODE>, <CODE>Short</CODE>, * <CODE>Long</CODE>, <CODE>Float</CODE>, <CODE>Double</CODE>). * * * @since 1.5 */ public class GaugeMonitor extends Monitor implements GaugeMonitorMBean { /* * ------------------------------------------ * PACKAGE CLASSES * ------------------------------------------ */ static class GaugeMonitorObservedObject extends ObservedObject { public GaugeMonitorObservedObject(ObjectName observedObject) { super(observedObject); } public final synchronized boolean getDerivedGaugeValid() { return derivedGaugeValid; } public final synchronized void setDerivedGaugeValid( boolean derivedGaugeValid) { this.derivedGaugeValid = derivedGaugeValid; } public final synchronized NumericalType getType() { return type; } public final synchronized void setType(NumericalType type) { this.type = type; } public final synchronized Number getPreviousScanGauge() { return previousScanGauge; } public final synchronized void setPreviousScanGauge( Number previousScanGauge) { this.previousScanGauge = previousScanGauge; } public final synchronized int getStatus() { return status; } public final synchronized void setStatus(int status) { this.status = status; } private boolean derivedGaugeValid; private NumericalType type; private Number previousScanGauge; private int status; } /* * ------------------------------------------ * PRIVATE VARIABLES * ------------------------------------------ */ /** * Gauge high threshold. * * <BR>The default value is a null Integer object. */ private Number highThreshold = INTEGER_ZERO; /** * Gauge low threshold. * * <BR>The default value is a null Integer object. */ private Number lowThreshold = INTEGER_ZERO; /** * Flag indicating if the gauge monitor notifies when exceeding * the high threshold. * * <BR>The default value is <CODE>false</CODE>. */ private boolean notifyHigh = false; /** * Flag indicating if the gauge monitor notifies when exceeding * the low threshold. * * <BR>The default value is <CODE>false</CODE>. */ private boolean notifyLow = false; /** * Flag indicating if the gauge difference mode is used. If the * gauge difference mode is used, the derived gauge is the * difference between two consecutive observed values. Otherwise, * the derived gauge is directly the value of the observed * attribute. * * <BR>The default value is set to <CODE>false</CODE>. */ private boolean differenceMode = false; private static final String[] types = { RUNTIME_ERROR, OBSERVED_OBJECT_ERROR, OBSERVED_ATTRIBUTE_ERROR, OBSERVED_ATTRIBUTE_TYPE_ERROR, THRESHOLD_ERROR, THRESHOLD_HIGH_VALUE_EXCEEDED, THRESHOLD_LOW_VALUE_EXCEEDED }; private static final MBeanNotificationInfo[] notifsInfo = { new MBeanNotificationInfo( types, "javax.management.monitor.MonitorNotification", "Notifications sent by the GaugeMonitor MBean") }; // Flags needed to implement the hysteresis mechanism. // private static final int RISING = 0; private static final int FALLING = 1; private static final int RISING_OR_FALLING = 2; /* * ------------------------------------------ * CONSTRUCTORS * ------------------------------------------ */ /** * Default constructor. */ public GaugeMonitor() { } /* * ------------------------------------------ * PUBLIC METHODS * ------------------------------------------ */ /** * Starts the gauge monitor. */ public synchronized void start() { if (isActive()) { MONITOR_LOGGER.logp(Level.FINER, GaugeMonitor.class.getName(), "start", "the monitor is already active"); return; } // Reset values. // for (ObservedObject o : observedObjects) { final GaugeMonitorObservedObject gmo = (GaugeMonitorObservedObject) o; gmo.setStatus(RISING_OR_FALLING); gmo.setPreviousScanGauge(null); } doStart(); } /** * Stops the gauge monitor. */ public synchronized void stop() { doStop(); } // GETTERS AND SETTERS //-------------------- /** * Gets the derived gauge of the specified object, if this object is * contained in the set of observed MBeans, or <code>null</code> otherwise. * * @param object the name of the MBean. * * @return The derived gauge of the specified object. * */ @Override public synchronized Number getDerivedGauge(ObjectName object) { return (Number) super.getDerivedGauge(object); } /** * Gets the derived gauge timestamp of the specified object, if * this object is contained in the set of observed MBeans, or * <code>0</code> otherwise. * * @param object the name of the object whose derived gauge * timestamp is to be returned. * * @return The derived gauge timestamp of the specified object. * */ @Override public synchronized long getDerivedGaugeTimeStamp(ObjectName object) { return super.getDerivedGaugeTimeStamp(object); } /** * Returns the derived gauge of the first object in the set of * observed MBeans. * * @return The derived gauge. * * @deprecated As of JMX 1.2, replaced by * {@link #getDerivedGauge(ObjectName)} */ @Deprecated public synchronized Number getDerivedGauge() { if (observedObjects.isEmpty()) { return null; } else { return (Number) observedObjects.get(0).getDerivedGauge(); } } /** * Gets the derived gauge timestamp of the first object in the set * of observed MBeans. * * @return The derived gauge timestamp. * * @deprecated As of JMX 1.2, replaced by * {@link #getDerivedGaugeTimeStamp(ObjectName)} */ @Deprecated public synchronized long getDerivedGaugeTimeStamp() { if (observedObjects.isEmpty()) { return 0; } else { return observedObjects.get(0).getDerivedGaugeTimeStamp(); } } /** * Gets the high threshold value common to all observed MBeans. * * @return The high threshold value. * * @see #setThresholds */ public synchronized Number getHighThreshold() { return highThreshold; } /** * Gets the low threshold value common to all observed MBeans. * * @return The low threshold value. * * @see #setThresholds */ public synchronized Number getLowThreshold() { return lowThreshold; } /** * Sets the high and the low threshold values common to all * observed MBeans. * * @param highValue The high threshold value. * @param lowValue The low threshold value. * * @exception IllegalArgumentException The specified high/low * threshold is null or the low threshold is greater than the high * threshold or the high threshold and the low threshold are not * of the same type. * * @see #getHighThreshold * @see #getLowThreshold */ public synchronized void setThresholds(Number highValue, Number lowValue) throws IllegalArgumentException { if ((highValue == null) || (lowValue == null)) { throw new IllegalArgumentException("Null threshold value"); } if (highValue.getClass() != lowValue.getClass()) { throw new IllegalArgumentException("Different type " + "threshold values"); } if (isFirstStrictlyGreaterThanLast(lowValue, highValue, highValue.getClass().getName())) { throw new IllegalArgumentException("High threshold less than " + "low threshold"); } if (highThreshold.equals(highValue) && lowThreshold.equals(lowValue)) return; highThreshold = highValue; lowThreshold = lowValue; // Reset values. // int index = 0; for (ObservedObject o : observedObjects) { resetAlreadyNotified(o, index++, THRESHOLD_ERROR_NOTIFIED); final GaugeMonitorObservedObject gmo = (GaugeMonitorObservedObject) o; gmo.setStatus(RISING_OR_FALLING); } } /** * Gets the high notification's on/off switch value common to all * observed MBeans. * * @return <CODE>true</CODE> if the gauge monitor notifies when * exceeding the high threshold, <CODE>false</CODE> otherwise. * * @see #setNotifyHigh */ public synchronized boolean getNotifyHigh() { return notifyHigh; } /** * Sets the high notification's on/off switch value common to all * observed MBeans. * * @param value The high notification's on/off switch value. * * @see #getNotifyHigh */ public synchronized void setNotifyHigh(boolean value) { if (notifyHigh == value) return; notifyHigh = value; } /** * Gets the low notification's on/off switch value common to all * observed MBeans. * * @return <CODE>true</CODE> if the gauge monitor notifies when * exceeding the low threshold, <CODE>false</CODE> otherwise. * * @see #setNotifyLow */ public synchronized boolean getNotifyLow() { return notifyLow; } /** * Sets the low notification's on/off switch value common to all * observed MBeans. * * @param value The low notification's on/off switch value. * * @see #getNotifyLow */ public synchronized void setNotifyLow(boolean value) { if (notifyLow == value) return; notifyLow = value; } /** * Gets the difference mode flag value common to all observed MBeans. * * @return <CODE>true</CODE> if the difference mode is used, * <CODE>false</CODE> otherwise. * * @see #setDifferenceMode */ public synchronized boolean getDifferenceMode() { return differenceMode; } /** * Sets the difference mode flag value common to all observed MBeans. * * @param value The difference mode flag value. * * @see #getDifferenceMode */ public synchronized void setDifferenceMode(boolean value) { if (differenceMode == value) return; differenceMode = value; // Reset values. // for (ObservedObject o : observedObjects) { final GaugeMonitorObservedObject gmo = (GaugeMonitorObservedObject) o; gmo.setStatus(RISING_OR_FALLING); gmo.setPreviousScanGauge(null); } } /** * Returns a <CODE>NotificationInfo</CODE> object containing the * name of the Java class of the notification and the notification * types sent by the gauge monitor. */ @Override public MBeanNotificationInfo[] getNotificationInfo() { return notifsInfo.clone(); } /* * ------------------------------------------ * PRIVATE METHODS * ------------------------------------------ */ /** * Updates the derived gauge attribute of the observed object. * * @param scanGauge The value of the observed attribute. * @param o The observed object. * @return <CODE>true</CODE> if the derived gauge value is valid, * <CODE>false</CODE> otherwise. The derived gauge value is * invalid when the differenceMode flag is set to * <CODE>true</CODE> and it is the first notification (so we * haven't 2 consecutive values to update the derived gauge). */ private synchronized boolean updateDerivedGauge( Object scanGauge, GaugeMonitorObservedObject o) { boolean is_derived_gauge_valid; // The gauge difference mode is used. // if (differenceMode) { // The previous scan gauge has been initialized. // if (o.getPreviousScanGauge() != null) { setDerivedGaugeWithDifference((Number)scanGauge, o); is_derived_gauge_valid = true; } // The previous scan gauge has not been initialized. // We cannot update the derived gauge... // else { is_derived_gauge_valid = false; } o.setPreviousScanGauge((Number)scanGauge); } // The gauge difference mode is not used. // else { o.setDerivedGauge((Number)scanGauge); is_derived_gauge_valid = true; } return is_derived_gauge_valid; } /** * Updates the notification attribute of the observed object * and notifies the listeners only once if the notify flag * is set to <CODE>true</CODE>. * @param o The observed object. */ private synchronized MonitorNotification updateNotifications( GaugeMonitorObservedObject o) { MonitorNotification n = null; // Send high notification if notifyHigh is true. // Send low notification if notifyLow is true. // if (o.getStatus() == RISING_OR_FALLING) { if (isFirstGreaterThanLast((Number)o.getDerivedGauge(), highThreshold, o.getType())) { if (notifyHigh) { n = new MonitorNotification( THRESHOLD_HIGH_VALUE_EXCEEDED, this, 0, 0, "", null, null, null, highThreshold); } o.setStatus(FALLING); } else if (isFirstGreaterThanLast(lowThreshold, (Number)o.getDerivedGauge(), o.getType())) { if (notifyLow) { n = new MonitorNotification( THRESHOLD_LOW_VALUE_EXCEEDED, this, 0, 0, "", null, null, null, lowThreshold); } o.setStatus(RISING); } } else { if (o.getStatus() == RISING) { if (isFirstGreaterThanLast((Number)o.getDerivedGauge(), highThreshold, o.getType())) { if (notifyHigh) { n = new MonitorNotification( THRESHOLD_HIGH_VALUE_EXCEEDED, this, 0, 0, "", null, null, null, highThreshold); } o.setStatus(FALLING); } } else if (o.getStatus() == FALLING) { if (isFirstGreaterThanLast(lowThreshold, (Number)o.getDerivedGauge(), o.getType())) { if (notifyLow) { n = new MonitorNotification( THRESHOLD_LOW_VALUE_EXCEEDED, this, 0, 0, "", null, null, null, lowThreshold); } o.setStatus(RISING); } } } return n; } /** * Sets the derived gauge when the differenceMode flag is set to * <CODE>true</CODE>. Both integer and floating-point types are * allowed. * * @param scanGauge The value of the observed attribute. * @param o The observed object. */ private synchronized void setDerivedGaugeWithDifference( Number scanGauge, GaugeMonitorObservedObject o) { Number prev = o.getPreviousScanGauge(); Number der; switch (o.getType()) { case INTEGER: der = new Integer(((Integer)scanGauge).intValue() - ((Integer)prev).intValue()); break; case BYTE: der = new Byte((byte)(((Byte)scanGauge).byteValue() - ((Byte)prev).byteValue())); break; case SHORT: der = new Short((short)(((Short)scanGauge).shortValue() - ((Short)prev).shortValue())); break; case LONG: der = new Long(((Long)scanGauge).longValue() - ((Long)prev).longValue()); break; case FLOAT: der = new Float(((Float)scanGauge).floatValue() - ((Float)prev).floatValue()); break; case DOUBLE: der = new Double(((Double)scanGauge).doubleValue() - ((Double)prev).doubleValue()); break; default: // Should never occur... MONITOR_LOGGER.logp(Level.FINEST, GaugeMonitor.class.getName(), "setDerivedGaugeWithDifference", "the threshold type is invalid"); return; } o.setDerivedGauge(der); } /** * Tests if the first specified Number is greater than or equal to * the last. Both integer and floating-point types are allowed. * * @param greater The first Number to compare with the second. * @param less The second Number to compare with the first. * @param type The number type. * @return <CODE>true</CODE> if the first specified Number is * greater than or equal to the last, <CODE>false</CODE> * otherwise. */ private boolean isFirstGreaterThanLast(Number greater, Number less, NumericalType type) { switch (type) { case INTEGER: case BYTE: case SHORT: case LONG: return (greater.longValue() >= less.longValue()); case FLOAT: case DOUBLE: return (greater.doubleValue() >= less.doubleValue()); default: // Should never occur... MONITOR_LOGGER.logp(Level.FINEST, GaugeMonitor.class.getName(), "isFirstGreaterThanLast", "the threshold type is invalid"); return false; } } /** * Tests if the first specified Number is strictly greater than the last. * Both integer and floating-point types are allowed. * * @param greater The first Number to compare with the second. * @param less The second Number to compare with the first. * @param className The number class name. * @return <CODE>true</CODE> if the first specified Number is * strictly greater than the last, <CODE>false</CODE> otherwise. */ private boolean isFirstStrictlyGreaterThanLast(Number greater, Number less, String className) { if (className.equals("java.lang.Integer") || className.equals("java.lang.Byte") || className.equals("java.lang.Short") || className.equals("java.lang.Long")) { return (greater.longValue() > less.longValue()); } else if (className.equals("java.lang.Float") || className.equals("java.lang.Double")) { return (greater.doubleValue() > less.doubleValue()); } else { // Should never occur... MONITOR_LOGGER.logp(Level.FINEST, GaugeMonitor.class.getName(), "isFirstStrictlyGreaterThanLast", "the threshold type is invalid"); return false; } } /* * ------------------------------------------ * PACKAGE METHODS * ------------------------------------------ */ /** * Factory method for ObservedObject creation. * * @since 1.6 */ @Override ObservedObject createObservedObject(ObjectName object) { final GaugeMonitorObservedObject gmo = new GaugeMonitorObservedObject(object); gmo.setStatus(RISING_OR_FALLING); gmo.setPreviousScanGauge(null); return gmo; } /** * This method globally sets the derived gauge type for the given * "object" and "attribute" after checking that the type of the * supplied observed attribute value is one of the value types * supported by this monitor. */ @Override synchronized boolean isComparableTypeValid(ObjectName object, String attribute, Comparable<?> value) { final GaugeMonitorObservedObject o = (GaugeMonitorObservedObject) getObservedObject(object); if (o == null) return false; // Check that the observed attribute is either of type // "Integer" or "Float". // if (value instanceof Integer) { o.setType(INTEGER); } else if (value instanceof Byte) { o.setType(BYTE); } else if (value instanceof Short) { o.setType(SHORT); } else if (value instanceof Long) { o.setType(LONG); } else if (value instanceof Float) { o.setType(FLOAT); } else if (value instanceof Double) { o.setType(DOUBLE); } else { return false; } return true; } @Override synchronized Comparable<?> getDerivedGaugeFromComparable( ObjectName object, String attribute, Comparable<?> value) { final GaugeMonitorObservedObject o = (GaugeMonitorObservedObject) getObservedObject(object); if (o == null) return null; // Update the derived gauge attributes and check the // validity of the new value. The derived gauge value // is invalid when the differenceMode flag is set to // true and it is the first notification, i.e. we // haven't got 2 consecutive values to update the // derived gauge. // o.setDerivedGaugeValid(updateDerivedGauge(value, o)); return (Comparable<?>) o.getDerivedGauge(); } @Override synchronized void onErrorNotification(MonitorNotification notification) { final GaugeMonitorObservedObject o = (GaugeMonitorObservedObject) getObservedObject(notification.getObservedObject()); if (o == null) return; // Reset values. // o.setStatus(RISING_OR_FALLING); o.setPreviousScanGauge(null); } @Override synchronized MonitorNotification buildAlarmNotification( ObjectName object, String attribute, Comparable<?> value) { final GaugeMonitorObservedObject o = (GaugeMonitorObservedObject) getObservedObject(object); if (o == null) return null; // Notify the listeners if the updated derived // gauge value is valid. // final MonitorNotification alarm; if (o.getDerivedGaugeValid()) alarm = updateNotifications(o); else alarm = null; return alarm; } /** * Tests if the threshold high and threshold low are both of the * same type as the gauge. Both integer and floating-point types * are allowed. * * Note: * If the optional lowThreshold or highThreshold have not been * initialized, their default value is an Integer object with * a value equal to zero. * * @param object The observed object. * @param attribute The observed attribute. * @param value The sample value. * @return <CODE>true</CODE> if type is the same, * <CODE>false</CODE> otherwise. */ @Override synchronized boolean isThresholdTypeValid(ObjectName object, String attribute, Comparable<?> value) { final GaugeMonitorObservedObject o = (GaugeMonitorObservedObject) getObservedObject(object); if (o == null) return false; Class<? extends Number> c = classForType(o.getType()); return (isValidForType(highThreshold, c) && isValidForType(lowThreshold, c)); } }