--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/management/monitor/Monitor.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1627 @@
+/*
+ * Copyright 1999-2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package javax.management.monitor;
+
+import static com.sun.jmx.defaults.JmxProperties.MONITOR_LOGGER;
+import com.sun.jmx.mbeanserver.GetPropertyAction;
+import com.sun.jmx.remote.util.EnvHelp;
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Level;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import static javax.management.monitor.MonitorNotification.*;
+import javax.management.openmbean.CompositeData;
+
+/**
+ * Defines the part common to all monitor MBeans.
+ * A monitor MBean monitors values of an attribute common to a set of observed
+ * MBeans. The observed attribute is monitored at intervals specified by the
+ * granularity period. A gauge value (derived gauge) is derived from the values
+ * of the observed attribute.
+ *
+ *
+ * @since 1.5
+ */
+public abstract class Monitor
+ extends NotificationBroadcasterSupport
+ implements MonitorMBean, MBeanRegistration {
+
+ /*
+ * ------------------------------------------
+ * PACKAGE CLASSES
+ * ------------------------------------------
+ */
+
+ static class ObservedObject {
+
+ public ObservedObject(ObjectName observedObject) {
+ this.observedObject = observedObject;
+ }
+
+ public final ObjectName getObservedObject() {
+ return observedObject;
+ }
+ public final synchronized int getAlreadyNotified() {
+ return alreadyNotified;
+ }
+ public final synchronized void setAlreadyNotified(int alreadyNotified) {
+ this.alreadyNotified = alreadyNotified;
+ }
+ public final synchronized Object getDerivedGauge() {
+ return derivedGauge;
+ }
+ public final synchronized void setDerivedGauge(Object derivedGauge) {
+ this.derivedGauge = derivedGauge;
+ }
+ public final synchronized long getDerivedGaugeTimeStamp() {
+ return derivedGaugeTimeStamp;
+ }
+ public final synchronized void setDerivedGaugeTimeStamp(
+ long derivedGaugeTimeStamp) {
+ this.derivedGaugeTimeStamp = derivedGaugeTimeStamp;
+ }
+
+ private final ObjectName observedObject;
+ private int alreadyNotified;
+ private Object derivedGauge;
+ private long derivedGaugeTimeStamp;
+ }
+
+ /*
+ * ------------------------------------------
+ * PRIVATE VARIABLES
+ * ------------------------------------------
+ */
+
+ /**
+ * Attribute to observe.
+ */
+ private String observedAttribute;
+
+ /**
+ * Monitor granularity period (in milliseconds).
+ * The default value is set to 10 seconds.
+ */
+ private long granularityPeriod = 10000;
+
+ /**
+ * Monitor state.
+ * The default value is set to <CODE>false</CODE>.
+ */
+ private boolean isActive = false;
+
+ /**
+ * Monitor sequence number.
+ * The default value is set to 0.
+ */
+ private final AtomicLong sequenceNumber = new AtomicLong();
+
+ /**
+ * Complex type attribute flag.
+ * The default value is set to <CODE>false</CODE>.
+ */
+ private boolean isComplexTypeAttribute = false;
+
+ /**
+ * First attribute name extracted from complex type attribute name.
+ */
+ private String firstAttribute;
+
+ /**
+ * Remaining attribute names extracted from complex type attribute name.
+ */
+ private final List<String> remainingAttributes =
+ new CopyOnWriteArrayList<String>();
+
+ /**
+ * AccessControlContext of the Monitor.start() caller.
+ */
+ private AccessControlContext acc;
+
+ /**
+ * Scheduler Service.
+ */
+ private static final ScheduledExecutorService scheduler =
+ Executors.newSingleThreadScheduledExecutor(
+ new DaemonThreadFactory("Scheduler"));
+
+ /**
+ * Maximum Pool Size
+ */
+ private static final int maximumPoolSize;
+
+ /**
+ * Executor Service.
+ */
+ private static final ExecutorService executor;
+ static {
+ final String maximumPoolSizeSysProp = "jmx.x.monitor.maximum.pool.size";
+ final String maximumPoolSizeStr = AccessController.doPrivileged(
+ new GetPropertyAction(maximumPoolSizeSysProp));
+ if (maximumPoolSizeStr == null ||
+ maximumPoolSizeStr.trim().length() == 0) {
+ maximumPoolSize = 10;
+ } else {
+ int maximumPoolSizeTmp = 10;
+ try {
+ maximumPoolSizeTmp = Integer.parseInt(maximumPoolSizeStr);
+ } catch (NumberFormatException e) {
+ if (MONITOR_LOGGER.isLoggable(Level.FINER)) {
+ MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(),
+ "<static initializer>",
+ "Wrong value for " + maximumPoolSizeSysProp +
+ " system property", e);
+ MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(),
+ "<static initializer>",
+ maximumPoolSizeSysProp + " defaults to 10");
+ }
+ maximumPoolSizeTmp = 10;
+ }
+ if (maximumPoolSizeTmp < 1) {
+ maximumPoolSize = 1;
+ } else {
+ maximumPoolSize = maximumPoolSizeTmp;
+ }
+ }
+ executor = new ThreadPoolExecutor(
+ maximumPoolSize,
+ maximumPoolSize,
+ 60L,
+ TimeUnit.SECONDS,
+ new LinkedBlockingQueue<Runnable>(),
+ new DaemonThreadFactory("Executor"));
+ ((ThreadPoolExecutor)executor).allowCoreThreadTimeOut(true);
+ }
+
+ /**
+ * Monitor task to be executed by the Executor Service.
+ */
+ private final MonitorTask monitorTask = new MonitorTask();
+
+ /**
+ * Future associated to the current monitor task.
+ */
+ private Future<?> monitorFuture;
+
+ /**
+ * Scheduler task to be executed by the Scheduler Service.
+ */
+ private final SchedulerTask schedulerTask = new SchedulerTask(monitorTask);
+
+ /**
+ * ScheduledFuture associated to the current scheduler task.
+ */
+ private ScheduledFuture<?> schedulerFuture;
+
+ /*
+ * ------------------------------------------
+ * PROTECTED VARIABLES
+ * ------------------------------------------
+ */
+
+ /**
+ * The amount by which the capacity of the monitor arrays are
+ * automatically incremented when their size becomes greater than
+ * their capacity.
+ */
+ protected final static int capacityIncrement = 16;
+
+ /**
+ * The number of valid components in the vector of observed objects.
+ *
+ */
+ protected int elementCount = 0;
+
+ /**
+ * Monitor errors that have already been notified.
+ * @deprecated equivalent to {@link #alreadyNotifieds}[0].
+ */
+ @Deprecated
+ protected int alreadyNotified = 0;
+
+ /**
+ * <p>Selected monitor errors that have already been notified.</p>
+ *
+ * <p>Each element in this array corresponds to an observed object
+ * in the vector. It contains a bit mask of the flags {@link
+ * #OBSERVED_OBJECT_ERROR_NOTIFIED} etc, indicating whether the
+ * corresponding notification has already been sent for the MBean
+ * being monitored.</p>
+ *
+ */
+ protected int alreadyNotifieds[] = new int[capacityIncrement];
+
+ /**
+ * Reference to the MBean server. This reference is null when the
+ * monitor MBean is not registered in an MBean server. This
+ * reference is initialized before the monitor MBean is registered
+ * in the MBean server.
+ * @see #preRegister(MBeanServer server, ObjectName name)
+ */
+ protected MBeanServer server;
+
+ // Flags defining possible monitor errors.
+ //
+
+ /**
+ * This flag is used to reset the {@link #alreadyNotifieds
+ * alreadyNotifieds} monitor attribute.
+ */
+ protected static final int RESET_FLAGS_ALREADY_NOTIFIED = 0;
+
+ /**
+ * Flag denoting that a notification has occurred after changing
+ * the observed object. This flag is used to check that the new
+ * observed object is registered in the MBean server at the time
+ * of the first notification.
+ */
+ protected static final int OBSERVED_OBJECT_ERROR_NOTIFIED = 1;
+
+ /**
+ * Flag denoting that a notification has occurred after changing
+ * the observed attribute. This flag is used to check that the
+ * new observed attribute belongs to the observed object at the
+ * time of the first notification.
+ */
+ protected static final int OBSERVED_ATTRIBUTE_ERROR_NOTIFIED = 2;
+
+ /**
+ * Flag denoting that a notification has occurred after changing
+ * the observed object or the observed attribute. This flag is
+ * used to check that the observed attribute type is correct
+ * (depending on the monitor in use) at the time of the first
+ * notification.
+ */
+ protected static final int OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED = 4;
+
+ /**
+ * Flag denoting that a notification has occurred after changing
+ * the observed object or the observed attribute. This flag is
+ * used to notify any exception (except the cases described above)
+ * when trying to get the value of the observed attribute at the
+ * time of the first notification.
+ */
+ protected static final int RUNTIME_ERROR_NOTIFIED = 8;
+
+ /**
+ * This field is retained for compatibility but should not be referenced.
+ *
+ * @deprecated No replacement.
+ */
+ @Deprecated
+ protected String dbgTag = Monitor.class.getName();
+
+ /*
+ * ------------------------------------------
+ * PACKAGE VARIABLES
+ * ------------------------------------------
+ */
+
+ /**
+ * List of ObservedObjects to which the attribute to observe belongs.
+ */
+ final List<ObservedObject> observedObjects =
+ new CopyOnWriteArrayList<ObservedObject>();
+
+ /**
+ * Flag denoting that a notification has occurred after changing
+ * the threshold. This flag is used to notify any exception
+ * related to invalid thresholds settings.
+ */
+ static final int THRESHOLD_ERROR_NOTIFIED = 16;
+
+ /**
+ * Enumeration used to keep trace of the derived gauge type
+ * in counter and gauge monitors.
+ */
+ enum NumericalType { BYTE, SHORT, INTEGER, LONG, FLOAT, DOUBLE };
+
+ /**
+ * Constant used to initialize all the numeric values.
+ */
+ static final Integer INTEGER_ZERO = new Integer(0);
+
+
+ /*
+ * ------------------------------------------
+ * PUBLIC METHODS
+ * ------------------------------------------
+ */
+
+ /**
+ * Allows the monitor MBean to perform any operations it needs
+ * before being registered in the MBean server.
+ * <P>
+ * Initializes the reference to the MBean server.
+ *
+ * @param server The MBean server in which the monitor MBean will
+ * be registered.
+ * @param name The object name of the monitor MBean.
+ *
+ * @return The name of the monitor MBean registered.
+ *
+ * @exception Exception
+ */
+ public ObjectName preRegister(MBeanServer server, ObjectName name)
+ throws Exception {
+
+ MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(),
+ "preRegister(MBeanServer, ObjectName)",
+ "initialize the reference on the MBean server");
+
+ this.server = server;
+ return name;
+ }
+
+ /**
+ * Allows the monitor MBean to perform any operations needed after
+ * having been registered in the MBean server or after the
+ * registration has failed.
+ * <P>
+ * Not used in this context.
+ */
+ public void postRegister(Boolean registrationDone) {
+ }
+
+ /**
+ * Allows the monitor MBean to perform any operations it needs
+ * before being unregistered by the MBean server.
+ * <P>
+ * Stops the monitor.
+ *
+ * @exception Exception
+ */
+ public void preDeregister() throws Exception {
+
+ MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(),
+ "preDeregister()", "stop the monitor");
+
+ // Stop the Monitor.
+ //
+ stop();
+ }
+
+ /**
+ * Allows the monitor MBean to perform any operations needed after
+ * having been unregistered by the MBean server.
+ * <P>
+ * Not used in this context.
+ */
+ public void postDeregister() {
+ }
+
+ /**
+ * Starts the monitor.
+ */
+ public abstract void start();
+
+ /**
+ * Stops the monitor.
+ */
+ public abstract void stop();
+
+ // GETTERS AND SETTERS
+ //--------------------
+
+ /**
+ * Returns the object name of the first object in the set of observed
+ * MBeans, or <code>null</code> if there is no such object.
+ *
+ * @return The object being observed.
+ *
+ * @see #setObservedObject(ObjectName)
+ *
+ * @deprecated As of JMX 1.2, replaced by {@link #getObservedObjects}
+ */
+ @Deprecated
+ public synchronized ObjectName getObservedObject() {
+ if (observedObjects.isEmpty()) {
+ return null;
+ } else {
+ return observedObjects.get(0).getObservedObject();
+ }
+ }
+
+ /**
+ * Removes all objects from the set of observed objects, and then adds the
+ * specified object.
+ *
+ * @param object The object to observe.
+ * @exception IllegalArgumentException The specified
+ * object is null.
+ *
+ * @see #getObservedObject()
+ *
+ * @deprecated As of JMX 1.2, replaced by {@link #addObservedObject}
+ */
+ @Deprecated
+ public synchronized void setObservedObject(ObjectName object)
+ throws IllegalArgumentException {
+ if (object == null)
+ throw new IllegalArgumentException("Null observed object");
+ if (observedObjects.size() == 1 && containsObservedObject(object))
+ return;
+ observedObjects.clear();
+ addObservedObject(object);
+ }
+
+ /**
+ * Adds the specified object in the set of observed MBeans, if this object
+ * is not already present.
+ *
+ * @param object The object to observe.
+ * @exception IllegalArgumentException The specified object is null.
+ *
+ */
+ public synchronized void addObservedObject(ObjectName object)
+ throws IllegalArgumentException {
+
+ if (object == null) {
+ throw new IllegalArgumentException("Null observed object");
+ }
+
+ // Check that the specified object is not already contained.
+ //
+ if (containsObservedObject(object))
+ return;
+
+ // Add the specified object in the list.
+ //
+ ObservedObject o = createObservedObject(object);
+ o.setAlreadyNotified(RESET_FLAGS_ALREADY_NOTIFIED);
+ o.setDerivedGauge(null);
+ o.setDerivedGaugeTimeStamp(System.currentTimeMillis());
+ observedObjects.add(o);
+
+ // Update legacy protected stuff.
+ //
+ createAlreadyNotified();
+ }
+
+ /**
+ * Removes the specified object from the set of observed MBeans.
+ *
+ * @param object The object to remove.
+ *
+ */
+ public synchronized void removeObservedObject(ObjectName object) {
+ // Check for null object.
+ //
+ if (object == null)
+ return;
+
+ final ObservedObject o = getObservedObject(object);
+ if (o != null) {
+ // Remove the specified object from the list.
+ //
+ observedObjects.remove(o);
+ // Update legacy protected stuff.
+ //
+ createAlreadyNotified();
+ }
+ }
+
+ /**
+ * Tests whether the specified object is in the set of observed MBeans.
+ *
+ * @param object The object to check.
+ * @return <CODE>true</CODE> if the specified object is present,
+ * <CODE>false</CODE> otherwise.
+ *
+ */
+ public synchronized boolean containsObservedObject(ObjectName object) {
+ return getObservedObject(object) != null;
+ }
+
+ /**
+ * Returns an array containing the objects being observed.
+ *
+ * @return The objects being observed.
+ *
+ */
+ public synchronized ObjectName[] getObservedObjects() {
+ ObjectName[] names = new ObjectName[observedObjects.size()];
+ for (int i = 0; i < names.length; i++)
+ names[i] = observedObjects.get(i).getObservedObject();
+ return names;
+ }
+
+ /**
+ * Gets the attribute being observed.
+ * <BR>The observed attribute is not initialized by default (set to null).
+ *
+ * @return The attribute being observed.
+ *
+ * @see #setObservedAttribute
+ */
+ public synchronized String getObservedAttribute() {
+ return observedAttribute;
+ }
+
+ /**
+ * Sets the attribute to observe.
+ * <BR>The observed attribute is not initialized by default (set to null).
+ *
+ * @param attribute The attribute to observe.
+ * @exception IllegalArgumentException The specified
+ * attribute is null.
+ *
+ * @see #getObservedAttribute
+ */
+ public void setObservedAttribute(String attribute)
+ throws IllegalArgumentException {
+
+ if (attribute == null) {
+ throw new IllegalArgumentException("Null observed attribute");
+ }
+
+ // Update alreadyNotified array.
+ //
+ synchronized (this) {
+ if (observedAttribute != null &&
+ observedAttribute.equals(attribute))
+ return;
+ observedAttribute = attribute;
+
+ // Reset the complex type attribute information
+ // such that it is recalculated again.
+ //
+ cleanupIsComplexTypeAttribute();
+
+ int index = 0;
+ for (ObservedObject o : observedObjects) {
+ resetAlreadyNotified(o, index++,
+ OBSERVED_ATTRIBUTE_ERROR_NOTIFIED |
+ OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED);
+ }
+ }
+ }
+
+ /**
+ * Gets the granularity period (in milliseconds).
+ * <BR>The default value of the granularity period is 10 seconds.
+ *
+ * @return The granularity period value.
+ *
+ * @see #setGranularityPeriod
+ */
+ public synchronized long getGranularityPeriod() {
+ return granularityPeriod;
+ }
+
+ /**
+ * Sets the granularity period (in milliseconds).
+ * <BR>The default value of the granularity period is 10 seconds.
+ *
+ * @param period The granularity period value.
+ * @exception IllegalArgumentException The granularity
+ * period is less than or equal to zero.
+ *
+ * @see #getGranularityPeriod
+ */
+ public synchronized void setGranularityPeriod(long period)
+ throws IllegalArgumentException {
+
+ if (period <= 0) {
+ throw new IllegalArgumentException("Nonpositive granularity " +
+ "period");
+ }
+
+ if (granularityPeriod == period)
+ return;
+ granularityPeriod = period;
+
+ // Reschedule the scheduler task if the monitor is active.
+ //
+ if (isActive()) {
+ cleanupFutures();
+ schedulerFuture = scheduler.schedule(schedulerTask,
+ period,
+ TimeUnit.MILLISECONDS);
+ }
+ }
+
+ /**
+ * Tests whether the monitor MBean is active. A monitor MBean is
+ * marked active when the {@link #start start} method is called.
+ * It becomes inactive when the {@link #stop stop} method is
+ * called.
+ *
+ * @return <CODE>true</CODE> if the monitor MBean is active,
+ * <CODE>false</CODE> otherwise.
+ */
+ /* This method must be synchronized so that the monitoring thread will
+ correctly see modifications to the isActive variable. See the MonitorTask
+ action executed by the Scheduled Executor Service. */
+ public synchronized boolean isActive() {
+ return isActive;
+ }
+
+ /*
+ * ------------------------------------------
+ * PACKAGE METHODS
+ * ------------------------------------------
+ */
+
+ /**
+ * Starts the monitor.
+ */
+ void doStart() {
+ MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(),
+ "doStart()", "start the monitor");
+
+ synchronized (this) {
+ if (isActive()) {
+ MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(),
+ "doStart()", "the monitor is already active");
+ return;
+ }
+
+ isActive = true;
+
+ // Reset the complex type attribute information
+ // such that it is recalculated again.
+ //
+ cleanupIsComplexTypeAttribute();
+
+ // Cache the AccessControlContext of the Monitor.start() caller.
+ // The monitor tasks will be executed within this context.
+ //
+ acc = AccessController.getContext();
+
+ // Start the scheduler.
+ //
+ cleanupFutures();
+ schedulerFuture = scheduler.schedule(schedulerTask,
+ getGranularityPeriod(),
+ TimeUnit.MILLISECONDS);
+ }
+ }
+
+ /**
+ * Stops the monitor.
+ */
+ void doStop() {
+ MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(),
+ "doStop()", "stop the monitor");
+
+ synchronized (this) {
+ if (!isActive()) {
+ MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(),
+ "doStop()", "the monitor is not active");
+ return;
+ }
+
+ isActive = false;
+
+ // Cancel the scheduler task associated with the
+ // scheduler and its associated monitor task.
+ //
+ cleanupFutures();
+
+ // Reset the AccessControlContext.
+ //
+ acc = null;
+
+ // Reset the complex type attribute information
+ // such that it is recalculated again.
+ //
+ cleanupIsComplexTypeAttribute();
+ }
+ }
+
+ /**
+ * 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 object whose derived gauge is to
+ * be returned.
+ *
+ * @return The derived gauge of the specified object.
+ *
+ * @since 1.6
+ */
+ synchronized Object getDerivedGauge(ObjectName object) {
+ final ObservedObject o = getObservedObject(object);
+ return o == null ? null : o.getDerivedGauge();
+ }
+
+ /**
+ * 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.
+ *
+ */
+ synchronized long getDerivedGaugeTimeStamp(ObjectName object) {
+ final ObservedObject o = getObservedObject(object);
+ return o == null ? 0 : o.getDerivedGaugeTimeStamp();
+ }
+
+ Object getAttribute(MBeanServerConnection mbsc,
+ ObjectName object,
+ String attribute)
+ throws AttributeNotFoundException,
+ InstanceNotFoundException,
+ MBeanException,
+ ReflectionException,
+ IOException {
+ // Check for "ObservedAttribute" replacement.
+ // This could happen if a thread A called setObservedAttribute()
+ // while other thread B was in the middle of the monitor() method
+ // and received the old observed attribute value.
+ //
+ final boolean lookupMBeanInfo;
+ synchronized (this) {
+ if (!isActive())
+ throw new IllegalArgumentException(
+ "The monitor has been stopped");
+ if (!attribute.equals(getObservedAttribute()))
+ throw new IllegalArgumentException(
+ "The observed attribute has been changed");
+ lookupMBeanInfo =
+ (firstAttribute == null && attribute.indexOf('.') != -1);
+ }
+
+ // Look up MBeanInfo if needed
+ //
+ final MBeanInfo mbi;
+ if (lookupMBeanInfo) {
+ try {
+ mbi = mbsc.getMBeanInfo(object);
+ } catch (IntrospectionException e) {
+ throw new IllegalArgumentException(e);
+ }
+ } else {
+ mbi = null;
+ }
+
+ // Check for complex type attribute
+ //
+ final String fa;
+ synchronized (this) {
+ if (!isActive())
+ throw new IllegalArgumentException(
+ "The monitor has been stopped");
+ if (!attribute.equals(getObservedAttribute()))
+ throw new IllegalArgumentException(
+ "The observed attribute has been changed");
+ if (firstAttribute == null) {
+ if (attribute.indexOf('.') != -1) {
+ MBeanAttributeInfo mbaiArray[] = mbi.getAttributes();
+ for (MBeanAttributeInfo mbai : mbaiArray) {
+ if (attribute.equals(mbai.getName())) {
+ firstAttribute = attribute;
+ break;
+ }
+ }
+ if (firstAttribute == null) {
+ String tokens[] = attribute.split("\\.", -1);
+ firstAttribute = tokens[0];
+ for (int i = 1; i < tokens.length; i++)
+ remainingAttributes.add(tokens[i]);
+ isComplexTypeAttribute = true;
+ }
+ } else {
+ firstAttribute = attribute;
+ }
+ }
+ fa = firstAttribute;
+ }
+ return mbsc.getAttribute(object, fa);
+ }
+
+ Comparable<?> getComparableFromAttribute(ObjectName object,
+ String attribute,
+ Object value)
+ throws AttributeNotFoundException {
+ if (isComplexTypeAttribute) {
+ Object v = value;
+ for (String attr : remainingAttributes)
+ v = introspect(object, attr, v);
+ return (Comparable<?>) v;
+ } else {
+ return (Comparable<?>) value;
+ }
+ }
+
+ Object introspect(ObjectName object,
+ String attribute,
+ Object value)
+ throws AttributeNotFoundException {
+ try {
+ if (value.getClass().isArray() && attribute.equals("length")) {
+ return Array.getLength(value);
+ } else if (value instanceof CompositeData) {
+ return ((CompositeData) value).get(attribute);
+ } else {
+ // Java Beans introspection
+ //
+ BeanInfo bi = Introspector.getBeanInfo(value.getClass());
+ PropertyDescriptor[] pds = bi.getPropertyDescriptors();
+ for (PropertyDescriptor pd : pds)
+ if (pd.getName().equals(attribute))
+ return pd.getReadMethod().invoke(value);
+ throw new AttributeNotFoundException(
+ "Could not find the getter method for the property " +
+ attribute + " using the Java Beans introspector");
+ }
+ } catch (InvocationTargetException e) {
+ throw new IllegalArgumentException(e);
+ } catch (AttributeNotFoundException e) {
+ throw e;
+ } catch (Exception e) {
+ throw EnvHelp.initCause(
+ new AttributeNotFoundException(e.getMessage()), e);
+ }
+ }
+
+ boolean isComparableTypeValid(ObjectName object,
+ String attribute,
+ Comparable<?> value) {
+ return true;
+ }
+
+ String buildErrorNotification(ObjectName object,
+ String attribute,
+ Comparable<?> value) {
+ return null;
+ }
+
+ void onErrorNotification(MonitorNotification notification) {
+ }
+
+ Comparable<?> getDerivedGaugeFromComparable(ObjectName object,
+ String attribute,
+ Comparable<?> value) {
+ return (Comparable<?>) value;
+ }
+
+ MonitorNotification buildAlarmNotification(ObjectName object,
+ String attribute,
+ Comparable<?> value){
+ return null;
+ }
+
+ boolean isThresholdTypeValid(ObjectName object,
+ String attribute,
+ Comparable<?> value) {
+ return true;
+ }
+
+ static Class<? extends Number> classForType(NumericalType type) {
+ switch (type) {
+ case BYTE:
+ return Byte.class;
+ case SHORT:
+ return Short.class;
+ case INTEGER:
+ return Integer.class;
+ case LONG:
+ return Long.class;
+ case FLOAT:
+ return Float.class;
+ case DOUBLE:
+ return Double.class;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported numerical type");
+ }
+ }
+
+ static boolean isValidForType(Object value, Class<? extends Number> c) {
+ return ((value == INTEGER_ZERO) || c.isInstance(value));
+ }
+
+ /**
+ * Get the specified {@code ObservedObject} if this object is
+ * contained in the set of observed MBeans, or {@code null}
+ * otherwise.
+ *
+ * @param object the name of the {@code ObservedObject} to retrieve.
+ *
+ * @return The {@code ObservedObject} associated to the supplied
+ * {@code ObjectName}.
+ *
+ * @since 1.6
+ */
+ synchronized ObservedObject getObservedObject(ObjectName object) {
+ for (ObservedObject o : observedObjects)
+ if (o.getObservedObject().equals(object))
+ return o;
+ return null;
+ }
+
+ /**
+ * Factory method for ObservedObject creation.
+ *
+ * @since 1.6
+ */
+ ObservedObject createObservedObject(ObjectName object) {
+ return new ObservedObject(object);
+ }
+
+ /**
+ * Create the {@link #alreadyNotified} array from
+ * the {@code ObservedObject} array list.
+ */
+ synchronized void createAlreadyNotified() {
+ // Update elementCount.
+ //
+ elementCount = observedObjects.size();
+
+ // Update arrays.
+ //
+ alreadyNotifieds = new int[elementCount];
+ for (int i = 0; i < elementCount; i++) {
+ alreadyNotifieds[i] = observedObjects.get(i).getAlreadyNotified();
+ }
+ updateDeprecatedAlreadyNotified();
+ }
+
+ /**
+ * Update the deprecated {@link #alreadyNotified} field.
+ */
+ synchronized void updateDeprecatedAlreadyNotified() {
+ if (elementCount > 0)
+ alreadyNotified = alreadyNotifieds[0];
+ else
+ alreadyNotified = 0;
+ }
+
+ /**
+ * Update the {@link #alreadyNotifieds} array element at the given index
+ * with the already notified flag in the given {@code ObservedObject}.
+ * Ensure the deprecated {@link #alreadyNotified} field is updated
+ * if appropriate.
+ */
+ synchronized void updateAlreadyNotified(ObservedObject o, int index) {
+ alreadyNotifieds[index] = o.getAlreadyNotified();
+ if (index == 0)
+ updateDeprecatedAlreadyNotified();
+ }
+
+ /**
+ * Check if the given bits in the given element of {@link #alreadyNotifieds}
+ * are set.
+ */
+ synchronized boolean isAlreadyNotified(ObservedObject o, int mask) {
+ return ((o.getAlreadyNotified() & mask) != 0);
+ }
+
+ /**
+ * Set the given bits in the given element of {@link #alreadyNotifieds}.
+ * Ensure the deprecated {@link #alreadyNotified} field is updated
+ * if appropriate.
+ */
+ synchronized void setAlreadyNotified(ObservedObject o, int index,
+ int mask, int an[]) {
+ final int i = computeAlreadyNotifiedIndex(o, index, an);
+ if (i == -1)
+ return;
+ o.setAlreadyNotified(o.getAlreadyNotified() | mask);
+ updateAlreadyNotified(o, i);
+ }
+
+ /**
+ * Reset the given bits in the given element of {@link #alreadyNotifieds}.
+ * Ensure the deprecated {@link #alreadyNotified} field is updated
+ * if appropriate.
+ */
+ synchronized void resetAlreadyNotified(ObservedObject o,
+ int index, int mask) {
+ o.setAlreadyNotified(o.getAlreadyNotified() & ~mask);
+ updateAlreadyNotified(o, index);
+ }
+
+ /**
+ * Reset all bits in the given element of {@link #alreadyNotifieds}.
+ * Ensure the deprecated {@link #alreadyNotified} field is updated
+ * if appropriate.
+ */
+ synchronized void resetAllAlreadyNotified(ObservedObject o,
+ int index, int an[]) {
+ final int i = computeAlreadyNotifiedIndex(o, index, an);
+ if (i == -1)
+ return;
+ o.setAlreadyNotified(RESET_FLAGS_ALREADY_NOTIFIED);
+ updateAlreadyNotified(o, index);
+ }
+
+ /**
+ * Check if the {@link #alreadyNotifieds} array has been modified.
+ * If true recompute the index for the given observed object.
+ */
+ synchronized int computeAlreadyNotifiedIndex(ObservedObject o,
+ int index, int an[]) {
+ if (an == alreadyNotifieds) {
+ return index;
+ } else {
+ return observedObjects.indexOf(o);
+ }
+ }
+
+ /*
+ * ------------------------------------------
+ * PRIVATE METHODS
+ * ------------------------------------------
+ */
+
+ /**
+ * This method is used by the monitor MBean to create and send a
+ * monitor notification to all the listeners registered for this
+ * kind of notification.
+ *
+ * @param type The notification type.
+ * @param timeStamp The notification emission date.
+ * @param msg The notification message.
+ * @param derGauge The derived gauge.
+ * @param trigger The threshold/string (depending on the monitor
+ * type) that triggered off the notification.
+ * @param object The ObjectName of the observed object that triggered
+ * off the notification.
+ * @param onError Flag indicating if this monitor notification is
+ * an error notification or an alarm notification.
+ */
+ private void sendNotification(String type, long timeStamp, String msg,
+ Object derGauge, Object trigger,
+ ObjectName object, boolean onError) {
+ if (!isActive())
+ return;
+
+ if (MONITOR_LOGGER.isLoggable(Level.FINER)) {
+ MONITOR_LOGGER.logp(Level.FINER, Monitor.class.getName(),
+ "sendNotification", "send notification: " +
+ "\n\tNotification observed object = " + object +
+ "\n\tNotification observed attribute = " + observedAttribute +
+ "\n\tNotification derived gauge = " + derGauge);
+ }
+
+ long seqno = sequenceNumber.getAndIncrement();
+
+ MonitorNotification mn =
+ new MonitorNotification(type,
+ this,
+ seqno,
+ timeStamp,
+ msg,
+ object,
+ observedAttribute,
+ derGauge,
+ trigger);
+ if (onError)
+ onErrorNotification(mn);
+ sendNotification(mn);
+ }
+
+ /**
+ * This method is called by the monitor each time
+ * the granularity period has been exceeded.
+ * @param o The observed object.
+ */
+ private void monitor(ObservedObject o, int index, int an[]) {
+
+ String attribute = null;
+ String notifType = null;
+ String msg = null;
+ Object derGauge = null;
+ Object trigger = null;
+ ObjectName object = null;
+ Comparable<?> value = null;
+ MonitorNotification alarm = null;
+
+ if (!isActive())
+ return;
+
+ // 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.
+ //
+ synchronized (this) {
+ object = o.getObservedObject();
+ attribute = getObservedAttribute();
+ if (object == null || attribute == null) {
+ return;
+ }
+ }
+
+ // Check that the observed object is registered in the
+ // MBean server and that the observed attribute
+ // belongs to the observed object.
+ //
+ Object attributeValue = null;
+ try {
+ attributeValue = getAttribute(server, object, attribute);
+ if (attributeValue == null)
+ if (isAlreadyNotified(
+ o, OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED))
+ return;
+ else {
+ notifType = OBSERVED_ATTRIBUTE_TYPE_ERROR;
+ setAlreadyNotified(
+ o, index, OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED, an);
+ msg = "The observed attribute value is null.";
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", msg);
+ }
+ } catch (NullPointerException np_ex) {
+ if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED))
+ return;
+ else {
+ notifType = RUNTIME_ERROR;
+ setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an);
+ msg =
+ "The monitor must be registered in the MBean " +
+ "server or an MBeanServerConnection must be " +
+ "explicitly supplied.";
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", msg);
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", np_ex.toString());
+ }
+ } catch (InstanceNotFoundException inf_ex) {
+ if (isAlreadyNotified(o, OBSERVED_OBJECT_ERROR_NOTIFIED))
+ return;
+ else {
+ notifType = OBSERVED_OBJECT_ERROR;
+ setAlreadyNotified(
+ o, index, OBSERVED_OBJECT_ERROR_NOTIFIED, an);
+ msg =
+ "The observed object must be accessible in " +
+ "the MBeanServerConnection.";
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", msg);
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", inf_ex.toString());
+ }
+ } catch (AttributeNotFoundException anf_ex) {
+ if (isAlreadyNotified(o, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED))
+ return;
+ else {
+ notifType = OBSERVED_ATTRIBUTE_ERROR;
+ setAlreadyNotified(
+ o, index, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED, an);
+ msg =
+ "The observed attribute must be accessible in " +
+ "the observed object.";
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", msg);
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", anf_ex.toString());
+ }
+ } catch (MBeanException mb_ex) {
+ if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED))
+ return;
+ else {
+ notifType = RUNTIME_ERROR;
+ setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an);
+ msg = mb_ex.getMessage() == null ? "" : mb_ex.getMessage();
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", msg);
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", mb_ex.toString());
+ }
+ } catch (ReflectionException ref_ex) {
+ if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED)) {
+ return;
+ } else {
+ notifType = RUNTIME_ERROR;
+ setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an);
+ msg = ref_ex.getMessage() == null ? "" : ref_ex.getMessage();
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", msg);
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", ref_ex.toString());
+ }
+ } catch (IOException io_ex) {
+ if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED))
+ return;
+ else {
+ notifType = RUNTIME_ERROR;
+ setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an);
+ msg = io_ex.getMessage() == null ? "" : io_ex.getMessage();
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", msg);
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", io_ex.toString());
+ }
+ } catch (RuntimeException rt_ex) {
+ if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED))
+ return;
+ else {
+ notifType = RUNTIME_ERROR;
+ setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an);
+ msg = rt_ex.getMessage() == null ? "" : rt_ex.getMessage();
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", msg);
+ MONITOR_LOGGER.logp(Level.FINEST, Monitor.class.getName(),
+ "monitor", rt_ex.toString());
+ }
+ }
+
+ synchronized (this) {
+
+ // Check if the monitor has been stopped.
+ //
+ if (!isActive())
+ return;
+
+ // Check if the observed attribute has been changed.
+ //
+ // Avoid race condition where mbs.getAttribute() succeeded but
+ // another thread replaced the observed attribute meanwhile.
+ //
+ // Avoid setting computed derived gauge on erroneous attribute.
+ //
+ if (!attribute.equals(getObservedAttribute()))
+ return;
+
+ // Derive a Comparable object from the ObservedAttribute value
+ // if the type of the ObservedAttribute value is a complex type.
+ //
+ if (msg == null) {
+ try {
+ value = getComparableFromAttribute(object,
+ attribute,
+ attributeValue);
+ } catch (ClassCastException e) {
+ if (isAlreadyNotified(
+ o, OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED))
+ return;
+ else {
+ notifType = OBSERVED_ATTRIBUTE_TYPE_ERROR;
+ setAlreadyNotified(o, index,
+ OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED, an);
+ msg =
+ "The observed attribute value does not " +
+ "implement the Comparable interface.";
+ MONITOR_LOGGER.logp(Level.FINEST,
+ Monitor.class.getName(), "monitor", msg);
+ MONITOR_LOGGER.logp(Level.FINEST,
+ Monitor.class.getName(), "monitor", e.toString());
+ }
+ } catch (AttributeNotFoundException e) {
+ if (isAlreadyNotified(o, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED))
+ return;
+ else {
+ notifType = OBSERVED_ATTRIBUTE_ERROR;
+ setAlreadyNotified(
+ o, index, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED, an);
+ msg =
+ "The observed attribute must be accessible in " +
+ "the observed object.";
+ MONITOR_LOGGER.logp(Level.FINEST,
+ Monitor.class.getName(), "monitor", msg);
+ MONITOR_LOGGER.logp(Level.FINEST,
+ Monitor.class.getName(), "monitor", e.toString());
+ }
+ } catch (RuntimeException e) {
+ if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED))
+ return;
+ else {
+ notifType = RUNTIME_ERROR;
+ setAlreadyNotified(o, index,
+ RUNTIME_ERROR_NOTIFIED, an);
+ msg = e.getMessage() == null ? "" : e.getMessage();
+ MONITOR_LOGGER.logp(Level.FINEST,
+ Monitor.class.getName(), "monitor", msg);
+ MONITOR_LOGGER.logp(Level.FINEST,
+ Monitor.class.getName(), "monitor", e.toString());
+ }
+ }
+ }
+
+ // Check that the observed attribute type is supported by this
+ // monitor.
+ //
+ if (msg == null) {
+ if (!isComparableTypeValid(object, attribute, value)) {
+ if (isAlreadyNotified(
+ o, OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED))
+ return;
+ else {
+ notifType = OBSERVED_ATTRIBUTE_TYPE_ERROR;
+ setAlreadyNotified(o, index,
+ OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED, an);
+ msg = "The observed attribute type is not valid.";
+ MONITOR_LOGGER.logp(Level.FINEST,
+ Monitor.class.getName(), "monitor", msg);
+ }
+ }
+ }
+
+ // Check that threshold type is supported by this monitor.
+ //
+ if (msg == null) {
+ if (!isThresholdTypeValid(object, attribute, value)) {
+ if (isAlreadyNotified(o, THRESHOLD_ERROR_NOTIFIED))
+ return;
+ else {
+ notifType = THRESHOLD_ERROR;
+ setAlreadyNotified(o, index,
+ THRESHOLD_ERROR_NOTIFIED, an);
+ msg = "The threshold type is not valid.";
+ MONITOR_LOGGER.logp(Level.FINEST,
+ Monitor.class.getName(), "monitor", msg);
+ }
+ }
+ }
+
+ // Let someone subclassing the monitor to perform additional
+ // monitor consistency checks and report errors if necessary.
+ //
+ if (msg == null) {
+ msg = buildErrorNotification(object, attribute, value);
+ if (msg != null) {
+ if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED))
+ return;
+ else {
+ notifType = RUNTIME_ERROR;
+ setAlreadyNotified(o, index,
+ RUNTIME_ERROR_NOTIFIED, an);
+ MONITOR_LOGGER.logp(Level.FINEST,
+ Monitor.class.getName(), "monitor", msg);
+ }
+ }
+ }
+
+ // If no errors were found then clear all error flags and
+ // let the monitor decide if a notification must be sent.
+ //
+ if (msg == null) {
+ // Clear all already notified flags.
+ //
+ resetAllAlreadyNotified(o, index, an);
+
+ // Get derived gauge from comparable value.
+ //
+ derGauge = getDerivedGaugeFromComparable(object,
+ attribute,
+ value);
+
+ o.setDerivedGauge(derGauge);
+ o.setDerivedGaugeTimeStamp(System.currentTimeMillis());
+
+ // Check if an alarm must be fired.
+ //
+ alarm = buildAlarmNotification(object,
+ attribute,
+ (Comparable<?>) derGauge);
+ }
+
+ }
+
+ // Notify monitor errors
+ //
+ if (msg != null)
+ sendNotification(notifType,
+ System.currentTimeMillis(),
+ msg,
+ derGauge,
+ trigger,
+ object,
+ true);
+
+ // Notify monitor alarms
+ //
+ if (alarm != null && alarm.getType() != null)
+ sendNotification(alarm.getType(),
+ System.currentTimeMillis(),
+ alarm.getMessage(),
+ derGauge,
+ alarm.getTrigger(),
+ object,
+ false);
+ }
+
+ /**
+ * Cleanup the scheduler and monitor tasks futures.
+ */
+ private synchronized void cleanupFutures() {
+ if (schedulerFuture != null) {
+ schedulerFuture.cancel(false);
+ schedulerFuture = null;
+ }
+ if (monitorFuture != null) {
+ monitorFuture.cancel(false);
+ monitorFuture = null;
+ }
+ }
+
+ /**
+ * Cleanup the "is complex type attribute" info.
+ */
+ private synchronized void cleanupIsComplexTypeAttribute() {
+ firstAttribute = null;
+ remainingAttributes.clear();
+ isComplexTypeAttribute = false;
+ }
+
+ /**
+ * SchedulerTask nested class: This class implements the Runnable interface.
+ *
+ * The SchedulerTask is executed periodically with a given fixed delay by
+ * the Scheduled Executor Service.
+ */
+ private class SchedulerTask implements Runnable {
+
+ private Runnable task = null;
+
+ /*
+ * ------------------------------------------
+ * CONSTRUCTORS
+ * ------------------------------------------
+ */
+
+ public SchedulerTask(Runnable task) {
+ this.task = task;
+ }
+
+ /*
+ * ------------------------------------------
+ * PUBLIC METHODS
+ * ------------------------------------------
+ */
+
+ public void run() {
+ synchronized (Monitor.this) {
+ Monitor.this.monitorFuture = executor.submit(task);
+ }
+ }
+ }
+
+ /**
+ * MonitorTask nested class: This class implements the Runnable interface.
+ *
+ * The MonitorTask is executed periodically with a given fixed delay by the
+ * Scheduled Executor Service.
+ */
+ private class MonitorTask implements Runnable {
+
+ /*
+ * ------------------------------------------
+ * CONSTRUCTORS
+ * ------------------------------------------
+ */
+
+ public MonitorTask() {
+ }
+
+ /*
+ * ------------------------------------------
+ * PUBLIC METHODS
+ * ------------------------------------------
+ */
+
+ public void run() {
+ final ScheduledFuture<?> sf;
+ synchronized (Monitor.this) {
+ sf = Monitor.this.schedulerFuture;
+ }
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ if (Monitor.this.isActive()) {
+ final int an[] = alreadyNotifieds;
+ int index = 0;
+ for (ObservedObject o : Monitor.this.observedObjects) {
+ if (Monitor.this.isActive()) {
+ Monitor.this.monitor(o, index++, an);
+ }
+ }
+ }
+ return null;
+ }
+ }, Monitor.this.acc);
+ synchronized (Monitor.this) {
+ if (Monitor.this.isActive() &&
+ Monitor.this.schedulerFuture == sf) {
+ Monitor.this.monitorFuture = null;
+ Monitor.this.schedulerFuture =
+ scheduler.schedule(Monitor.this.schedulerTask,
+ Monitor.this.getGranularityPeriod(),
+ TimeUnit.MILLISECONDS);
+ }
+ }
+ }
+ }
+
+ /**
+ * Daemon thread factory used by the monitor executors.
+ * <P>
+ * This factory creates all new threads used by an Executor in
+ * the same ThreadGroup. If there is a SecurityManager, it uses
+ * the group of System.getSecurityManager(), else the group of
+ * the thread instantiating this DaemonThreadFactory. Each new
+ * thread is created as a daemon thread with priority
+ * Thread.NORM_PRIORITY. New threads have names accessible via
+ * Thread.getName() of "JMX Monitor <pool-name> Pool [Thread-M]",
+ * where M is the sequence number of the thread created by this
+ * factory.
+ */
+ private static class DaemonThreadFactory implements ThreadFactory {
+ final ThreadGroup group;
+ final AtomicInteger threadNumber = new AtomicInteger(1);
+ final String namePrefix;
+ final String nameSuffix = "]";
+
+ public DaemonThreadFactory(String poolName) {
+ SecurityManager s = System.getSecurityManager();
+ group = (s != null) ? s.getThreadGroup() :
+ Thread.currentThread().getThreadGroup();
+ namePrefix = "JMX Monitor " + poolName + " Pool [Thread-";
+ }
+
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(group,
+ r,
+ namePrefix +
+ threadNumber.getAndIncrement() +
+ nameSuffix,
+ 0);
+ t.setDaemon(true);
+ if (t.getPriority() != Thread.NORM_PRIORITY)
+ t.setPriority(Thread.NORM_PRIORITY);
+ return t;
+ }
+ }
+}