/*
* Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
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 =
Integer.
valueOf(((
Integer)
scanGauge).
intValue() -
((
Integer)
prev).
intValue());
break;
case
BYTE:
der =
Byte.
valueOf((byte)(((
Byte)
scanGauge).
byteValue() -
((
Byte)
prev).
byteValue()));
break;
case
SHORT:
der =
Short.
valueOf((short)(((
Short)
scanGauge).
shortValue() -
((
Short)
prev).
shortValue()));
break;
case
LONG:
der =
Long.
valueOf(((
Long)
scanGauge).
longValue() -
((
Long)
prev).
longValue());
break;
case
FLOAT:
der =
Float.
valueOf(((
Float)
scanGauge).
floatValue() -
((
Float)
prev).
floatValue());
break;
case
DOUBLE:
der =
Double.
valueOf(((
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));
}
}