/* * @(#)GaugeMonitor.java 1.73 04/05/18 * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package javax.management.monitor; // java imports // import java.util.Date; import java.util.Timer; import java.util.TimerTask; // RI imports // import javax.management.ObjectName; import javax.management.MBeanNotificationInfo; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; import javax.management.MBeanException; import javax.management.ReflectionException; /** * Defines a monitor MBean designed to observe the values of a gauge attribute. * *
A gauge monitor observes an attribute that is continuously * variable with time. A gauge monitor sends notifications as * follows: * *
true
*
* true
.
*
* 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.
*
*
The derived gauge value (V[t]) is calculated using the following method:
*
Byte
, Integer
, Short
,
* Long
, Float
, Double
).
*
* @version 1.73 05/18/04
* @author Sun Microsystems, Inc
*
* @since 1.5
*/
public class GaugeMonitor extends Monitor implements GaugeMonitorMBean {
/*
* ------------------------------------------
* PRIVATE VARIABLES
* ------------------------------------------
*/
private static final Integer INTEGER_ZERO = new Integer(0);
/**
* Gauge high threshold.
* false
.
*/
private boolean notifyHigh = false;
/**
* Flag indicating if the gauge monitor notifies when exceeding
* the low threshold.
*
* false
.
*/
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.
*
* false
.
*/
private boolean differenceMode = false;
/**
* Derived gauges.
* null
otherwise.
*
* @param object the name of the MBean.
*
* @return The derived gauge of the specified object.
*
* @since.unbundled JMX 1.2
*/
public synchronized Number getDerivedGauge(ObjectName object) {
int index = indexOf(object);
if (index != -1)
return derivedGauge[index];
else
return null;
}
/**
* Gets the derived gauge timestamp of the specified object, if
* this object is contained in the set of observed MBeans, or
* null
otherwise.
*
* @param object the name of the MBean.
*
* @return The derived gauge timestamp of the specified object.
*
* @since.unbundled JMX 1.2
*/
public synchronized long getDerivedGaugeTimeStamp(ObjectName object) {
int index = indexOf(object);
if (index != -1)
return derivedGaugeTimestamp[index];
else
return 0;
}
/**
* 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() {
return derivedGauge[0];
}
/**
* 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() {
return derivedGaugeTimestamp[0];
}
/**
* Gets the high threshold value common to all observed MBeans.
*
* @return The high threshold value.
*/
public synchronized Number getHighThreshold() {
return highThreshold;
}
/**
* Gets the low threshold value common to all observed MBeans.
*
* @return The low threshold value.
*/
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.
*/
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");
}
highThreshold = highValue;
lowThreshold = lowValue;
for (int i = 0; i < elementCount; i++) {
resetAlreadyNotified(i, THRESHOLD_ERROR_NOTIFIED);
// Reset values.
//
status[i] = RISING_OR_FALLING;
}
}
/**
* Gets the high notification's on/off switch value common to all
* observed MBeans.
*
* @see #setNotifyHigh
*
* @return true
if the gauge monitor notifies when
* exceeding the high threshold, false
otherwise.
*/
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) {
notifyHigh = value;
}
/**
* Gets the low notification's on/off switch value common to all
* observed MBeans.
*
* @return true
if the gauge monitor notifies when
* exceeding the low threshold, false
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) {
notifyLow = value;
}
/**
* Gets the difference mode flag value common to all observed MBeans.
*
* @return true
if the difference mode is used,
* false
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) {
differenceMode = value;
// Reset values.
//
for (int i = 0; i < elementCount; i++) {
status[i] = RISING_OR_FALLING;
previousScanGauge[i] = null;
}
}
/**
* Returns a NotificationInfo
object containing the
* name of the Java class of the notification and the notification
* types sent by the gauge monitor.
*/
public MBeanNotificationInfo[] getNotificationInfo() {
String[] types = { MonitorNotification.RUNTIME_ERROR,
MonitorNotification.OBSERVED_OBJECT_ERROR,
MonitorNotification.OBSERVED_ATTRIBUTE_ERROR,
MonitorNotification.OBSERVED_ATTRIBUTE_TYPE_ERROR,
MonitorNotification.THRESHOLD_ERROR,
MonitorNotification.THRESHOLD_HIGH_VALUE_EXCEEDED,
MonitorNotification.THRESHOLD_LOW_VALUE_EXCEEDED};
MBeanNotificationInfo[] notifsInfo = {
new MBeanNotificationInfo(types,
"javax.management.monitor.MonitorNotification",
"Notifications sent by the GaugeMonitor MBean")
};
return notifsInfo;
}
/*
* ------------------------------------------
* PRIVATE METHODS
* ------------------------------------------
*/
/**
* Updates the derived gauge and the derived gauge timestamp attributes
* of the observed object at the specified index.
*
* @param scanGauge The value of the observed attribute.
* @param index The index of the observed object.
* @return true
if the derived gauge value is valid,
* false
otherwise. The derived gauge value is
* invalid when the differenceMode flag is set to
* true
and it is the first notification (so we
* haven't 2 consecutive values to update the derived gauge).
*/
private synchronized boolean updateDerivedGauge(Object scanGauge,
int index) {
boolean is_derived_gauge_valid;
derivedGaugeTimestamp[index] = System.currentTimeMillis();
// The gauge difference mode is used.
//
if (differenceMode) {
// The previous scan gauge has been initialized.
//
if (previousScanGauge[index] != null) {
setDerivedGaugeWithDifference((Number)scanGauge, index);
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;
}
previousScanGauge[index] = (Number)scanGauge;
}
// The gauge difference mode is not used.
//
else {
derivedGauge[index] = (Number)scanGauge;
is_derived_gauge_valid = true;
}
return is_derived_gauge_valid;
}
/**
* Updates the notification attribute of the observed object at the
* specified index and notifies the listeners only once if the notify flag
* is set to true
.
* @param index The index of the observed object.
*/
private void updateNotifications(int index) {
boolean sendNotify = false;
String notifType = null;
long timeStamp = 0;
String msg = null;
Object derGauge = null;
Object trigger = null;
// Send high notification if notifyHigh is true.
// Send low notification if notifyLow is true.
//
synchronized(this) {
if (status[index] == RISING_OR_FALLING) {
if (isFirstGreaterThanLast(derivedGauge[index], highThreshold,
type[index])) {
if (notifyHigh) {
sendNotify = true;
notifType =
MonitorNotification.THRESHOLD_HIGH_VALUE_EXCEEDED;
timeStamp = derivedGaugeTimestamp[index];
msg = "";
derGauge = derivedGauge[index];
trigger = highThreshold;
}
status[index] = FALLING;
} else if (isFirstGreaterThanLast(lowThreshold,
derivedGauge[index],
type[index])) {
if (notifyLow) {
sendNotify = true;
notifType =
MonitorNotification.THRESHOLD_LOW_VALUE_EXCEEDED;
timeStamp = derivedGaugeTimestamp[index];
msg ="";
derGauge = derivedGauge[index];
trigger = lowThreshold;
}
status[index] = RISING;
}
} else {
if (status[index] == RISING) {
if (isFirstGreaterThanLast(derivedGauge[index],
highThreshold,
type[index])) {
if (notifyHigh) {
sendNotify = true;
notifType =
MonitorNotification.THRESHOLD_HIGH_VALUE_EXCEEDED;
timeStamp = derivedGaugeTimestamp[index];
msg = "";
derGauge = derivedGauge[index];
trigger = highThreshold;
}
status[index] = FALLING;
}
} else if (status[index] == FALLING) {
if (isFirstGreaterThanLast(lowThreshold,
derivedGauge[index],
type[index])) {
if (notifyLow) {
sendNotify = true;
notifType =
MonitorNotification.THRESHOLD_LOW_VALUE_EXCEEDED;
timeStamp = derivedGaugeTimestamp[index];
msg = "";
derGauge = derivedGauge[index];
trigger = lowThreshold;
}
status[index] = RISING;
}
}
}
}
if (sendNotify) {
sendNotification(notifType, timeStamp, msg, derGauge, trigger, index);
}
}
/**
* 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 index The index of the observed object.
* @return true
if type is the same,
* false
otherwise.
*/
private synchronized boolean isThresholdTypeValid(int index) {
switch(type[index]) {
case INTEGER:
return (((highThreshold == INTEGER_ZERO) ||
(highThreshold instanceof Integer)) &&
((lowThreshold == INTEGER_ZERO) ||
(lowThreshold instanceof Integer)));
case BYTE:
return (((highThreshold == INTEGER_ZERO) ||
(highThreshold instanceof Byte)) &&
((lowThreshold == INTEGER_ZERO) ||
(lowThreshold instanceof Byte)));
case SHORT:
return (((highThreshold == INTEGER_ZERO) ||
(highThreshold instanceof Short)) &&
((lowThreshold == INTEGER_ZERO) ||
(lowThreshold instanceof Short)));
case LONG:
return (((highThreshold == INTEGER_ZERO) ||
(highThreshold instanceof Long)) &&
((lowThreshold == INTEGER_ZERO) ||
(lowThreshold instanceof Long)));
case FLOAT:
return (((highThreshold == INTEGER_ZERO) ||
(highThreshold instanceof Float)) &&
((lowThreshold == INTEGER_ZERO) ||
(lowThreshold instanceof Float)));
case DOUBLE:
return (((highThreshold == INTEGER_ZERO) ||
(highThreshold instanceof Double)) &&
((lowThreshold == INTEGER_ZERO) ||
(lowThreshold instanceof Double)));
default:
// Should never occur...
if (isDebugOn()) {
debug("isThresholdTypeValid", "The threshold type is invalid");
}
return false;
}
}
/**
* Sets the derived gauge when the differenceMode flag is set to
* true
. Both integer and floating-point types are
* allowed.
*
* @param scanGauge The value of the observed attribute.
* @param index The index of the observed object.
*/
private synchronized void setDerivedGaugeWithDifference(Number scanGauge,
int index) {
Number prev = previousScanGauge[index];
Number der;
switch (type[index]) {
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());
default:
// Should never occur...
if (isDebugOn()) {
debug("setDerivedGaugeWithDifference",
"the threshold type is invalid");
}
return;
}
derivedGauge[index] = 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 true
if the first specified Number is
* greater than or equal to the last, false
* otherwise.
*/
private boolean isFirstGreaterThanLast(Number greater,
Number less, int 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...
if (isDebugOn()) {
debug("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 true
if the first specified Number is
* strictly greater than the last, false
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...
if (isDebugOn()) {
debug("isFirstStrictlyGreaterThanLast",
"the threshold type is invalid");
}
return false;
}
}
/*
* ------------------------------------------
* PACKAGE METHODS
* ------------------------------------------
*/
/**
* This method is called by the gauge monitor each time
* the granularity period has been exceeded.
* @param index The index of the observed object.
*/
void notifyAlarmClock(int index) {
long timeStamp = 0;
String msg = null;
Object derGauge = null;
Object scan_gauge = null;
String notif_type = null;
synchronized(this) {
if (!isActive())
return;
// Check if the observed object and observed attribute are valid.
//
// Check that neither the observed object nor the observed
// attribute are null. If the observed object or observed
// attribute is null, this means that the monitor started
// before a complete initialization and nothing is done.
//
if ((getObservedObject(index) == null) ||
(getObservedAttribute() == null))
return;
// Check that the observed object is registered in the
// MBean server and that the observed attribute belongs to
// the observed object.
//
try {
scan_gauge = server.getAttribute(getObservedObject(index),
getObservedAttribute());
if (scan_gauge == null)
return;
} catch (NullPointerException np_ex) {
if (alreadyNotified(index, RUNTIME_ERROR_NOTIFIED))
return;
else {
notif_type = MonitorNotification.RUNTIME_ERROR;
setAlreadyNotified(index, RUNTIME_ERROR_NOTIFIED);
msg =
"The gauge monitor must be registered in " +
"the MBean server.";
}
} catch (InstanceNotFoundException inf_ex) {
if (alreadyNotified(index, OBSERVED_OBJECT_ERROR_NOTIFIED))
return;
else {
notif_type = MonitorNotification.OBSERVED_OBJECT_ERROR;
setAlreadyNotified(index,
OBSERVED_OBJECT_ERROR_NOTIFIED);
msg =
"The observed object must be registered in " +
"the MBean server.";
}
} catch (AttributeNotFoundException anf_ex) {
if (alreadyNotified(index,
OBSERVED_ATTRIBUTE_ERROR_NOTIFIED))
return;
else {
notif_type =
MonitorNotification.OBSERVED_ATTRIBUTE_ERROR;
setAlreadyNotified(index,
OBSERVED_ATTRIBUTE_ERROR_NOTIFIED);
msg =
"The observed attribute must be accessible in " +
"the observed object.";
}
} catch (MBeanException mb_ex) {
if (alreadyNotified(index, RUNTIME_ERROR_NOTIFIED))
return;
else {
notif_type = MonitorNotification.RUNTIME_ERROR;
setAlreadyNotified(index, RUNTIME_ERROR_NOTIFIED);
msg = mb_ex.getMessage();
}
} catch (ReflectionException ref_ex) {
if (alreadyNotified(index,
OBSERVED_ATTRIBUTE_ERROR_NOTIFIED))
return;
else {
notif_type =
MonitorNotification.OBSERVED_ATTRIBUTE_ERROR;
setAlreadyNotified(index,
OBSERVED_ATTRIBUTE_ERROR_NOTIFIED);
msg = ref_ex.getMessage();
}
}
if (msg == null) {
// Check that the observed attribute is either of type
// "Integer" or "Float".
//
if (scan_gauge instanceof Integer) {
type[index] = INTEGER;
} else if (scan_gauge instanceof Byte) {
type[index] = BYTE;
} else if (scan_gauge instanceof Short) {
type[index] = SHORT;
} else if (scan_gauge instanceof Long) {
type[index] = LONG;
} else if (scan_gauge instanceof Float) {
type[index] = FLOAT;
} else if (scan_gauge instanceof Double) {
type[index] = DOUBLE;
} else {
if (alreadyNotified(index,
OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED))
return;
else {
notif_type =
MonitorNotification.OBSERVED_ATTRIBUTE_TYPE_ERROR;
setAlreadyNotified(index,
OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED);
msg =
"The observed attribute type must be an " +
"integer type or a floating-point type.";
}
}
}
if (msg == null) {
// Check that threshold high and threshold low are of
// the same type as the gauge.
//
if (!isThresholdTypeValid(index)) {
if (alreadyNotified(index, THRESHOLD_ERROR_NOTIFIED))
return;
else {
notif_type = MonitorNotification.THRESHOLD_ERROR;
setAlreadyNotified(index, THRESHOLD_ERROR_NOTIFIED);
msg =
"The threshold high and threshold low must be " +
"of the same type as the gauge.";
}
}
}
if (msg == null) {
// Clear all already notified flags.
//
resetAllAlreadyNotified(index);
// 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 (so we haven't 2 consecutive
// values to update the derived gauge).
//
boolean is_derived_gauge_valid =
updateDerivedGauge(scan_gauge, index);
// Notify the listeners if the updated derived
// gauge value is valid.
//
if (is_derived_gauge_valid)
updateNotifications(index);
} else {
// msg != null, will send an error notification
timeStamp = derivedGaugeTimestamp[index];
derGauge = derivedGauge[index];
// Reset values.
//
status[index] = RISING_OR_FALLING;
previousScanGauge[index] = null;
}
}
if (msg != null) {
sendNotification(notif_type,
timeStamp,
msg,
derGauge,
null,
index);
}
}
/**
* This method is called when adding a new observed object in the vector.
* It updates all the gauge specific arrays.
* @param index The index of the observed object.
*/
synchronized void insertSpecificElementAt(int index) {
// Update derivedGauge, derivedGaugeTimestamp, previousScanGauge,
// status and type values.
if (index != elementCount)
throw new Error("Internal error: index != elementCount");
if (elementCount >= derivedGauge.length) {
derivedGauge = expandArray(derivedGauge);
previousScanGauge = expandArray(previousScanGauge);
derivedGaugeTimestamp = expandArray(derivedGaugeTimestamp);
status = expandArray(status);
type = expandArray(type);
}
derivedGauge[index] = INTEGER_ZERO;
previousScanGauge[index] = null;
derivedGaugeTimestamp[index] = System.currentTimeMillis();
status[index] = RISING_OR_FALLING;
type[index] = INTEGER;
}
/**
* This method is called when removing an observed object from the vector.
* It updates all the gauge specific arrays.
* @param index The index of the observed object.
*/
synchronized void removeSpecificElementAt(int index) {
if (index < 0 || index >= elementCount)
return;
// Update derivedGauge, derivedGaugeTimestamp, previousScanGauge,
// status and type arrays.
//
removeElementAt(derivedGauge, index);
removeElementAt(previousScanGauge, index);
removeElementAt(derivedGaugeTimestamp, index);
removeElementAt(status, index);
removeElementAt(type, index);
}
/**
* GaugeAlarmClock inner class:
* This class provides a simple implementation of an alarm clock MBean.
* The aim of this MBean is to set up an alarm which wakes up the
* gauge monitor every granularity period.
*/
private static class GaugeAlarmClock extends TimerTask {
GaugeMonitor listener = null;
/*
* ------------------------------------------
* CONSTRUCTORS
* ------------------------------------------
*/
public GaugeAlarmClock(GaugeMonitor listener) {
this.listener = listener;
}
/*
* ------------------------------------------
* PUBLIC METHODS
* ------------------------------------------
*/
/**
* This method is called by the GaugeAlarmClock thread when it
* is started.
*/
public void run() {
if (listener.isActive()) {
for (int i = 0; i < listener.elementCount; i++) {
listener.notifyAlarmClock(i);
}
}
}
}
}