src/java.management/share/classes/javax/management/monitor/Monitor.java
changeset 47216 71c04702a3d5
parent 43235 da1786d695b6
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package javax.management.monitor;
       
    27 
       
    28 import static com.sun.jmx.defaults.JmxProperties.MONITOR_LOGGER;
       
    29 import com.sun.jmx.mbeanserver.GetPropertyAction;
       
    30 import com.sun.jmx.mbeanserver.Introspector;
       
    31 import java.io.IOException;
       
    32 import java.security.AccessControlContext;
       
    33 import java.security.AccessController;
       
    34 import java.security.PrivilegedAction;
       
    35 import java.security.ProtectionDomain;
       
    36 import java.util.List;
       
    37 import java.util.Map;
       
    38 import java.util.WeakHashMap;
       
    39 import java.util.concurrent.CopyOnWriteArrayList;
       
    40 import java.util.concurrent.Executors;
       
    41 import java.util.concurrent.Future;
       
    42 import java.util.concurrent.LinkedBlockingQueue;
       
    43 import java.util.concurrent.ScheduledExecutorService;
       
    44 import java.util.concurrent.ScheduledFuture;
       
    45 import java.util.concurrent.ThreadFactory;
       
    46 import java.util.concurrent.ThreadPoolExecutor;
       
    47 import java.util.concurrent.TimeUnit;
       
    48 import java.util.concurrent.atomic.AtomicInteger;
       
    49 import java.util.concurrent.atomic.AtomicLong;
       
    50 import java.lang.System.Logger.Level;
       
    51 import javax.management.AttributeNotFoundException;
       
    52 import javax.management.InstanceNotFoundException;
       
    53 import javax.management.IntrospectionException;
       
    54 import javax.management.MBeanAttributeInfo;
       
    55 import javax.management.MBeanException;
       
    56 import javax.management.MBeanInfo;
       
    57 import javax.management.MBeanRegistration;
       
    58 import javax.management.MBeanServer;
       
    59 import javax.management.MBeanServerConnection;
       
    60 import javax.management.NotificationBroadcasterSupport;
       
    61 import javax.management.ObjectName;
       
    62 import javax.management.ReflectionException;
       
    63 import static javax.management.monitor.MonitorNotification.*;
       
    64 
       
    65 /**
       
    66  * Defines the part common to all monitor MBeans.
       
    67  * A monitor MBean monitors values of an attribute common to a set of observed
       
    68  * MBeans. The observed attribute is monitored at intervals specified by the
       
    69  * granularity period. A gauge value (derived gauge) is derived from the values
       
    70  * of the observed attribute.
       
    71  *
       
    72  *
       
    73  * @since 1.5
       
    74  */
       
    75 public abstract class Monitor
       
    76     extends NotificationBroadcasterSupport
       
    77     implements MonitorMBean, MBeanRegistration {
       
    78 
       
    79     /*
       
    80      * ------------------------------------------
       
    81      *  PACKAGE CLASSES
       
    82      * ------------------------------------------
       
    83      */
       
    84 
       
    85     static class ObservedObject {
       
    86 
       
    87         public ObservedObject(ObjectName observedObject) {
       
    88             this.observedObject = observedObject;
       
    89         }
       
    90 
       
    91         public final ObjectName getObservedObject() {
       
    92             return observedObject;
       
    93         }
       
    94         public final synchronized int getAlreadyNotified() {
       
    95             return alreadyNotified;
       
    96         }
       
    97         public final synchronized void setAlreadyNotified(int alreadyNotified) {
       
    98             this.alreadyNotified = alreadyNotified;
       
    99         }
       
   100         public final synchronized Object getDerivedGauge() {
       
   101             return derivedGauge;
       
   102         }
       
   103         public final synchronized void setDerivedGauge(Object derivedGauge) {
       
   104             this.derivedGauge = derivedGauge;
       
   105         }
       
   106         public final synchronized long getDerivedGaugeTimeStamp() {
       
   107             return derivedGaugeTimeStamp;
       
   108         }
       
   109         public final synchronized void setDerivedGaugeTimeStamp(
       
   110                                                  long derivedGaugeTimeStamp) {
       
   111             this.derivedGaugeTimeStamp = derivedGaugeTimeStamp;
       
   112         }
       
   113 
       
   114         private final ObjectName observedObject;
       
   115         private int alreadyNotified;
       
   116         private Object derivedGauge;
       
   117         private long derivedGaugeTimeStamp;
       
   118     }
       
   119 
       
   120     /*
       
   121      * ------------------------------------------
       
   122      *  PRIVATE VARIABLES
       
   123      * ------------------------------------------
       
   124      */
       
   125 
       
   126     /**
       
   127      * Attribute to observe.
       
   128      */
       
   129     private String observedAttribute;
       
   130 
       
   131     /**
       
   132      * Monitor granularity period (in milliseconds).
       
   133      * The default value is set to 10 seconds.
       
   134      */
       
   135     private long granularityPeriod = 10000;
       
   136 
       
   137     /**
       
   138      * Monitor state.
       
   139      * The default value is set to <CODE>false</CODE>.
       
   140      */
       
   141     private boolean isActive = false;
       
   142 
       
   143     /**
       
   144      * Monitor sequence number.
       
   145      * The default value is set to 0.
       
   146      */
       
   147     private final AtomicLong sequenceNumber = new AtomicLong();
       
   148 
       
   149     /**
       
   150      * Complex type attribute flag.
       
   151      * The default value is set to <CODE>false</CODE>.
       
   152      */
       
   153     private boolean isComplexTypeAttribute = false;
       
   154 
       
   155     /**
       
   156      * First attribute name extracted from complex type attribute name.
       
   157      */
       
   158     private String firstAttribute;
       
   159 
       
   160     /**
       
   161      * Remaining attribute names extracted from complex type attribute name.
       
   162      */
       
   163     private final List<String> remainingAttributes =
       
   164         new CopyOnWriteArrayList<String>();
       
   165 
       
   166     /**
       
   167      * AccessControlContext of the Monitor.start() caller.
       
   168      */
       
   169     private static final AccessControlContext noPermissionsACC =
       
   170             new AccessControlContext(
       
   171             new ProtectionDomain[] {new ProtectionDomain(null, null)});
       
   172     private volatile AccessControlContext acc = noPermissionsACC;
       
   173 
       
   174     /**
       
   175      * Scheduler Service.
       
   176      */
       
   177     private static final ScheduledExecutorService scheduler =
       
   178         Executors.newSingleThreadScheduledExecutor(
       
   179             new DaemonThreadFactory("Scheduler"));
       
   180 
       
   181     /**
       
   182      * Map containing the thread pool executor per thread group.
       
   183      */
       
   184     private static final Map<ThreadPoolExecutor, Void> executors =
       
   185             new WeakHashMap<ThreadPoolExecutor, Void>();
       
   186 
       
   187     /**
       
   188      * Lock for executors map.
       
   189      */
       
   190     private static final Object executorsLock = new Object();
       
   191 
       
   192     /**
       
   193      * Maximum Pool Size
       
   194      */
       
   195     private static final int maximumPoolSize;
       
   196     static {
       
   197         final String maximumPoolSizeSysProp = "jmx.x.monitor.maximum.pool.size";
       
   198         final String maximumPoolSizeStr = AccessController.doPrivileged(
       
   199             new GetPropertyAction(maximumPoolSizeSysProp));
       
   200         if (maximumPoolSizeStr == null ||
       
   201             maximumPoolSizeStr.trim().length() == 0) {
       
   202             maximumPoolSize = 10;
       
   203         } else {
       
   204             int maximumPoolSizeTmp = 10;
       
   205             try {
       
   206                 maximumPoolSizeTmp = Integer.parseInt(maximumPoolSizeStr);
       
   207             } catch (NumberFormatException e) {
       
   208                 if (MONITOR_LOGGER.isLoggable(Level.TRACE)) {
       
   209                     MONITOR_LOGGER.log(Level.TRACE,
       
   210                             "Wrong value for " + maximumPoolSizeSysProp +
       
   211                             " system property", e);
       
   212                     MONITOR_LOGGER.log(Level.TRACE,
       
   213                             maximumPoolSizeSysProp + " defaults to 10");
       
   214                 }
       
   215                 maximumPoolSizeTmp = 10;
       
   216             }
       
   217             if (maximumPoolSizeTmp < 1) {
       
   218                 maximumPoolSize = 1;
       
   219             } else {
       
   220                 maximumPoolSize = maximumPoolSizeTmp;
       
   221             }
       
   222         }
       
   223     }
       
   224 
       
   225     /**
       
   226      * Future associated to the current monitor task.
       
   227      */
       
   228     private Future<?> monitorFuture;
       
   229 
       
   230     /**
       
   231      * Scheduler task to be executed by the Scheduler Service.
       
   232      */
       
   233     private final SchedulerTask schedulerTask = new SchedulerTask();
       
   234 
       
   235     /**
       
   236      * ScheduledFuture associated to the current scheduler task.
       
   237      */
       
   238     private ScheduledFuture<?> schedulerFuture;
       
   239 
       
   240     /*
       
   241      * ------------------------------------------
       
   242      *  PROTECTED VARIABLES
       
   243      * ------------------------------------------
       
   244      */
       
   245 
       
   246     /**
       
   247      * The amount by which the capacity of the monitor arrays are
       
   248      * automatically incremented when their size becomes greater than
       
   249      * their capacity.
       
   250      */
       
   251     protected final static int capacityIncrement = 16;
       
   252 
       
   253     /**
       
   254      * The number of valid components in the vector of observed objects.
       
   255      *
       
   256      */
       
   257     protected int elementCount = 0;
       
   258 
       
   259     /**
       
   260      * Monitor errors that have already been notified.
       
   261      * @deprecated equivalent to {@link #alreadyNotifieds}[0].
       
   262      */
       
   263     @Deprecated
       
   264     protected int alreadyNotified = 0;
       
   265 
       
   266     /**
       
   267      * <p>Selected monitor errors that have already been notified.</p>
       
   268      *
       
   269      * <p>Each element in this array corresponds to an observed object
       
   270      * in the vector.  It contains a bit mask of the flags {@link
       
   271      * #OBSERVED_OBJECT_ERROR_NOTIFIED} etc, indicating whether the
       
   272      * corresponding notification has already been sent for the MBean
       
   273      * being monitored.</p>
       
   274      *
       
   275      */
       
   276     protected int alreadyNotifieds[] = new int[capacityIncrement];
       
   277 
       
   278     /**
       
   279      * Reference to the MBean server.  This reference is null when the
       
   280      * monitor MBean is not registered in an MBean server.  This
       
   281      * reference is initialized before the monitor MBean is registered
       
   282      * in the MBean server.
       
   283      * @see #preRegister(MBeanServer server, ObjectName name)
       
   284      */
       
   285     protected MBeanServer server;
       
   286 
       
   287     // Flags defining possible monitor errors.
       
   288     //
       
   289 
       
   290     /**
       
   291      * This flag is used to reset the {@link #alreadyNotifieds
       
   292      * alreadyNotifieds} monitor attribute.
       
   293      */
       
   294     protected static final int RESET_FLAGS_ALREADY_NOTIFIED             = 0;
       
   295 
       
   296     /**
       
   297      * Flag denoting that a notification has occurred after changing
       
   298      * the observed object.  This flag is used to check that the new
       
   299      * observed object is registered in the MBean server at the time
       
   300      * of the first notification.
       
   301      */
       
   302     protected static final int OBSERVED_OBJECT_ERROR_NOTIFIED           = 1;
       
   303 
       
   304     /**
       
   305      * Flag denoting that a notification has occurred after changing
       
   306      * the observed attribute.  This flag is used to check that the
       
   307      * new observed attribute belongs to the observed object at the
       
   308      * time of the first notification.
       
   309      */
       
   310     protected static final int OBSERVED_ATTRIBUTE_ERROR_NOTIFIED        = 2;
       
   311 
       
   312     /**
       
   313      * Flag denoting that a notification has occurred after changing
       
   314      * the observed object or the observed attribute.  This flag is
       
   315      * used to check that the observed attribute type is correct
       
   316      * (depending on the monitor in use) at the time of the first
       
   317      * notification.
       
   318      */
       
   319     protected static final int OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED   = 4;
       
   320 
       
   321     /**
       
   322      * Flag denoting that a notification has occurred after changing
       
   323      * the observed object or the observed attribute.  This flag is
       
   324      * used to notify any exception (except the cases described above)
       
   325      * when trying to get the value of the observed attribute at the
       
   326      * time of the first notification.
       
   327      */
       
   328     protected static final int RUNTIME_ERROR_NOTIFIED                   = 8;
       
   329 
       
   330     /**
       
   331      * This field is retained for compatibility but should not be referenced.
       
   332      *
       
   333      * @deprecated No replacement.
       
   334      */
       
   335     @Deprecated
       
   336     protected String dbgTag = Monitor.class.getName();
       
   337 
       
   338     /*
       
   339      * ------------------------------------------
       
   340      *  PACKAGE VARIABLES
       
   341      * ------------------------------------------
       
   342      */
       
   343 
       
   344     /**
       
   345      * List of ObservedObjects to which the attribute to observe belongs.
       
   346      */
       
   347     final List<ObservedObject> observedObjects =
       
   348         new CopyOnWriteArrayList<ObservedObject>();
       
   349 
       
   350     /**
       
   351      * Flag denoting that a notification has occurred after changing
       
   352      * the threshold. This flag is used to notify any exception
       
   353      * related to invalid thresholds settings.
       
   354      */
       
   355     static final int THRESHOLD_ERROR_NOTIFIED                           = 16;
       
   356 
       
   357     /**
       
   358      * Enumeration used to keep trace of the derived gauge type
       
   359      * in counter and gauge monitors.
       
   360      */
       
   361     enum NumericalType { BYTE, SHORT, INTEGER, LONG, FLOAT, DOUBLE };
       
   362 
       
   363     /**
       
   364      * Constant used to initialize all the numeric values.
       
   365      */
       
   366     static final Integer INTEGER_ZERO = 0;
       
   367 
       
   368 
       
   369     /*
       
   370      * ------------------------------------------
       
   371      *  PUBLIC METHODS
       
   372      * ------------------------------------------
       
   373      */
       
   374 
       
   375     /**
       
   376      * Allows the monitor MBean to perform any operations it needs
       
   377      * before being registered in the MBean server.
       
   378      * <P>
       
   379      * Initializes the reference to the MBean server.
       
   380      *
       
   381      * @param server The MBean server in which the monitor MBean will
       
   382      * be registered.
       
   383      * @param name The object name of the monitor MBean.
       
   384      *
       
   385      * @return The name of the monitor MBean registered.
       
   386      *
       
   387      * @exception Exception if something goes wrong
       
   388      */
       
   389     public ObjectName preRegister(MBeanServer server, ObjectName name)
       
   390         throws Exception {
       
   391 
       
   392         MONITOR_LOGGER.log(Level.TRACE,
       
   393                 "initialize the reference on the MBean server");
       
   394 
       
   395         this.server = server;
       
   396         return name;
       
   397     }
       
   398 
       
   399     /**
       
   400      * Allows the monitor MBean to perform any operations needed after
       
   401      * having been registered in the MBean server or after the
       
   402      * registration has failed.
       
   403      * <P>
       
   404      * Not used in this context.
       
   405      */
       
   406     public void postRegister(Boolean registrationDone) {
       
   407     }
       
   408 
       
   409     /**
       
   410      * Allows the monitor MBean to perform any operations it needs
       
   411      * before being unregistered by the MBean server.
       
   412      * <P>
       
   413      * Stops the monitor.
       
   414      *
       
   415      * @exception Exception if something goes wrong
       
   416      */
       
   417     public void preDeregister() throws Exception {
       
   418 
       
   419         MONITOR_LOGGER.log(Level.TRACE, "stop the monitor");
       
   420 
       
   421         // Stop the Monitor.
       
   422         //
       
   423         stop();
       
   424     }
       
   425 
       
   426     /**
       
   427      * Allows the monitor MBean to perform any operations needed after
       
   428      * having been unregistered by the MBean server.
       
   429      * <P>
       
   430      * Not used in this context.
       
   431      */
       
   432     public void postDeregister() {
       
   433     }
       
   434 
       
   435     /**
       
   436      * Starts the monitor.
       
   437      */
       
   438     public abstract void start();
       
   439 
       
   440     /**
       
   441      * Stops the monitor.
       
   442      */
       
   443     public abstract void stop();
       
   444 
       
   445     // GETTERS AND SETTERS
       
   446     //--------------------
       
   447 
       
   448     /**
       
   449      * Returns the object name of the first object in the set of observed
       
   450      * MBeans, or <code>null</code> if there is no such object.
       
   451      *
       
   452      * @return The object being observed.
       
   453      *
       
   454      * @see #setObservedObject(ObjectName)
       
   455      *
       
   456      * @deprecated As of JMX 1.2, replaced by {@link #getObservedObjects}
       
   457      */
       
   458     @Deprecated
       
   459     public synchronized ObjectName getObservedObject() {
       
   460         if (observedObjects.isEmpty()) {
       
   461             return null;
       
   462         } else {
       
   463             return observedObjects.get(0).getObservedObject();
       
   464         }
       
   465     }
       
   466 
       
   467     /**
       
   468      * Removes all objects from the set of observed objects, and then adds the
       
   469      * specified object.
       
   470      *
       
   471      * @param object The object to observe.
       
   472      * @exception IllegalArgumentException The specified
       
   473      * object is null.
       
   474      *
       
   475      * @see #getObservedObject()
       
   476      *
       
   477      * @deprecated As of JMX 1.2, replaced by {@link #addObservedObject}
       
   478      */
       
   479     @Deprecated
       
   480     public synchronized void setObservedObject(ObjectName object)
       
   481         throws IllegalArgumentException {
       
   482         if (object == null)
       
   483             throw new IllegalArgumentException("Null observed object");
       
   484         if (observedObjects.size() == 1 && containsObservedObject(object))
       
   485             return;
       
   486         observedObjects.clear();
       
   487         addObservedObject(object);
       
   488     }
       
   489 
       
   490     /**
       
   491      * Adds the specified object in the set of observed MBeans, if this object
       
   492      * is not already present.
       
   493      *
       
   494      * @param object The object to observe.
       
   495      * @exception IllegalArgumentException The specified object is null.
       
   496      *
       
   497      */
       
   498     public synchronized void addObservedObject(ObjectName object)
       
   499         throws IllegalArgumentException {
       
   500 
       
   501         if (object == null) {
       
   502             throw new IllegalArgumentException("Null observed object");
       
   503         }
       
   504 
       
   505         // Check that the specified object is not already contained.
       
   506         //
       
   507         if (containsObservedObject(object))
       
   508             return;
       
   509 
       
   510         // Add the specified object in the list.
       
   511         //
       
   512         ObservedObject o = createObservedObject(object);
       
   513         o.setAlreadyNotified(RESET_FLAGS_ALREADY_NOTIFIED);
       
   514         o.setDerivedGauge(INTEGER_ZERO);
       
   515         o.setDerivedGaugeTimeStamp(System.currentTimeMillis());
       
   516         observedObjects.add(o);
       
   517 
       
   518         // Update legacy protected stuff.
       
   519         //
       
   520         createAlreadyNotified();
       
   521     }
       
   522 
       
   523     /**
       
   524      * Removes the specified object from the set of observed MBeans.
       
   525      *
       
   526      * @param object The object to remove.
       
   527      *
       
   528      */
       
   529     public synchronized void removeObservedObject(ObjectName object) {
       
   530         // Check for null object.
       
   531         //
       
   532         if (object == null)
       
   533             return;
       
   534 
       
   535         final ObservedObject o = getObservedObject(object);
       
   536         if (o != null) {
       
   537             // Remove the specified object from the list.
       
   538             //
       
   539             observedObjects.remove(o);
       
   540             // Update legacy protected stuff.
       
   541             //
       
   542             createAlreadyNotified();
       
   543         }
       
   544     }
       
   545 
       
   546     /**
       
   547      * Tests whether the specified object is in the set of observed MBeans.
       
   548      *
       
   549      * @param object The object to check.
       
   550      * @return <CODE>true</CODE> if the specified object is present,
       
   551      * <CODE>false</CODE> otherwise.
       
   552      *
       
   553      */
       
   554     public synchronized boolean containsObservedObject(ObjectName object) {
       
   555         return getObservedObject(object) != null;
       
   556     }
       
   557 
       
   558     /**
       
   559      * Returns an array containing the objects being observed.
       
   560      *
       
   561      * @return The objects being observed.
       
   562      *
       
   563      */
       
   564     public synchronized ObjectName[] getObservedObjects() {
       
   565         ObjectName[] names = new ObjectName[observedObjects.size()];
       
   566         for (int i = 0; i < names.length; i++)
       
   567             names[i] = observedObjects.get(i).getObservedObject();
       
   568         return names;
       
   569     }
       
   570 
       
   571     /**
       
   572      * Gets the attribute being observed.
       
   573      * <BR>The observed attribute is not initialized by default (set to null).
       
   574      *
       
   575      * @return The attribute being observed.
       
   576      *
       
   577      * @see #setObservedAttribute
       
   578      */
       
   579     public synchronized String getObservedAttribute() {
       
   580         return observedAttribute;
       
   581     }
       
   582 
       
   583     /**
       
   584      * Sets the attribute to observe.
       
   585      * <BR>The observed attribute is not initialized by default (set to null).
       
   586      *
       
   587      * @param attribute The attribute to observe.
       
   588      * @exception IllegalArgumentException The specified
       
   589      * attribute is null.
       
   590      *
       
   591      * @see #getObservedAttribute
       
   592      */
       
   593     public void setObservedAttribute(String attribute)
       
   594         throws IllegalArgumentException {
       
   595 
       
   596         if (attribute == null) {
       
   597             throw new IllegalArgumentException("Null observed attribute");
       
   598         }
       
   599 
       
   600         // Update alreadyNotified array.
       
   601         //
       
   602         synchronized (this) {
       
   603             if (observedAttribute != null &&
       
   604                 observedAttribute.equals(attribute))
       
   605                 return;
       
   606             observedAttribute = attribute;
       
   607 
       
   608             // Reset the complex type attribute information
       
   609             // such that it is recalculated again.
       
   610             //
       
   611             cleanupIsComplexTypeAttribute();
       
   612 
       
   613             int index = 0;
       
   614             for (ObservedObject o : observedObjects) {
       
   615                 resetAlreadyNotified(o, index++,
       
   616                                      OBSERVED_ATTRIBUTE_ERROR_NOTIFIED |
       
   617                                      OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED);
       
   618             }
       
   619         }
       
   620     }
       
   621 
       
   622     /**
       
   623      * Gets the granularity period (in milliseconds).
       
   624      * <BR>The default value of the granularity period is 10 seconds.
       
   625      *
       
   626      * @return The granularity period value.
       
   627      *
       
   628      * @see #setGranularityPeriod
       
   629      */
       
   630     public synchronized long getGranularityPeriod() {
       
   631         return granularityPeriod;
       
   632     }
       
   633 
       
   634     /**
       
   635      * Sets the granularity period (in milliseconds).
       
   636      * <BR>The default value of the granularity period is 10 seconds.
       
   637      *
       
   638      * @param period The granularity period value.
       
   639      * @exception IllegalArgumentException The granularity
       
   640      * period is less than or equal to zero.
       
   641      *
       
   642      * @see #getGranularityPeriod
       
   643      */
       
   644     public synchronized void setGranularityPeriod(long period)
       
   645         throws IllegalArgumentException {
       
   646 
       
   647         if (period <= 0) {
       
   648             throw new IllegalArgumentException("Nonpositive granularity " +
       
   649                                                "period");
       
   650         }
       
   651 
       
   652         if (granularityPeriod == period)
       
   653             return;
       
   654         granularityPeriod = period;
       
   655 
       
   656         // Reschedule the scheduler task if the monitor is active.
       
   657         //
       
   658         if (isActive()) {
       
   659             cleanupFutures();
       
   660             schedulerFuture = scheduler.schedule(schedulerTask,
       
   661                                                  period,
       
   662                                                  TimeUnit.MILLISECONDS);
       
   663         }
       
   664     }
       
   665 
       
   666     /**
       
   667      * Tests whether the monitor MBean is active.  A monitor MBean is
       
   668      * marked active when the {@link #start start} method is called.
       
   669      * It becomes inactive when the {@link #stop stop} method is
       
   670      * called.
       
   671      *
       
   672      * @return <CODE>true</CODE> if the monitor MBean is active,
       
   673      * <CODE>false</CODE> otherwise.
       
   674      */
       
   675     /* This method must be synchronized so that the monitoring thread will
       
   676        correctly see modifications to the isActive variable. See the MonitorTask
       
   677        action executed by the Scheduled Executor Service. */
       
   678     public synchronized boolean isActive() {
       
   679         return isActive;
       
   680     }
       
   681 
       
   682     /*
       
   683      * ------------------------------------------
       
   684      *  PACKAGE METHODS
       
   685      * ------------------------------------------
       
   686      */
       
   687 
       
   688     /**
       
   689      * Starts the monitor.
       
   690      */
       
   691     void doStart() {
       
   692             MONITOR_LOGGER.log(Level.TRACE, "start the monitor");
       
   693 
       
   694         synchronized (this) {
       
   695             if (isActive()) {
       
   696                 MONITOR_LOGGER.log(Level.TRACE, "the monitor is already active");
       
   697                 return;
       
   698             }
       
   699 
       
   700             isActive = true;
       
   701 
       
   702             // Reset the complex type attribute information
       
   703             // such that it is recalculated again.
       
   704             //
       
   705             cleanupIsComplexTypeAttribute();
       
   706 
       
   707             // Cache the AccessControlContext of the Monitor.start() caller.
       
   708             // The monitor tasks will be executed within this context.
       
   709             //
       
   710             acc = AccessController.getContext();
       
   711 
       
   712             // Start the scheduler.
       
   713             //
       
   714             cleanupFutures();
       
   715             schedulerTask.setMonitorTask(new MonitorTask());
       
   716             schedulerFuture = scheduler.schedule(schedulerTask,
       
   717                                                  getGranularityPeriod(),
       
   718                                                  TimeUnit.MILLISECONDS);
       
   719         }
       
   720     }
       
   721 
       
   722     /**
       
   723      * Stops the monitor.
       
   724      */
       
   725     void doStop() {
       
   726         MONITOR_LOGGER.log(Level.TRACE, "stop the monitor");
       
   727 
       
   728         synchronized (this) {
       
   729             if (!isActive()) {
       
   730                 MONITOR_LOGGER.log(Level.TRACE, "the monitor is not active");
       
   731                 return;
       
   732             }
       
   733 
       
   734             isActive = false;
       
   735 
       
   736             // Cancel the scheduler task associated with the
       
   737             // scheduler and its associated monitor task.
       
   738             //
       
   739             cleanupFutures();
       
   740 
       
   741             // Reset the AccessControlContext.
       
   742             //
       
   743             acc = noPermissionsACC;
       
   744 
       
   745             // Reset the complex type attribute information
       
   746             // such that it is recalculated again.
       
   747             //
       
   748             cleanupIsComplexTypeAttribute();
       
   749         }
       
   750     }
       
   751 
       
   752     /**
       
   753      * Gets the derived gauge of the specified object, if this object is
       
   754      * contained in the set of observed MBeans, or <code>null</code> otherwise.
       
   755      *
       
   756      * @param object the name of the object whose derived gauge is to
       
   757      * be returned.
       
   758      *
       
   759      * @return The derived gauge of the specified object.
       
   760      *
       
   761      * @since 1.6
       
   762      */
       
   763     synchronized Object getDerivedGauge(ObjectName object) {
       
   764         final ObservedObject o = getObservedObject(object);
       
   765         return o == null ? null : o.getDerivedGauge();
       
   766     }
       
   767 
       
   768     /**
       
   769      * Gets the derived gauge timestamp of the specified object, if
       
   770      * this object is contained in the set of observed MBeans, or
       
   771      * <code>0</code> otherwise.
       
   772      *
       
   773      * @param object the name of the object whose derived gauge
       
   774      * timestamp is to be returned.
       
   775      *
       
   776      * @return The derived gauge timestamp of the specified object.
       
   777      *
       
   778      */
       
   779     synchronized long getDerivedGaugeTimeStamp(ObjectName object) {
       
   780         final ObservedObject o = getObservedObject(object);
       
   781         return o == null ? 0 : o.getDerivedGaugeTimeStamp();
       
   782     }
       
   783 
       
   784     Object getAttribute(MBeanServerConnection mbsc,
       
   785                         ObjectName object,
       
   786                         String attribute)
       
   787         throws AttributeNotFoundException,
       
   788                InstanceNotFoundException,
       
   789                MBeanException,
       
   790                ReflectionException,
       
   791                IOException {
       
   792         // Check for "ObservedAttribute" replacement.
       
   793         // This could happen if a thread A called setObservedAttribute()
       
   794         // while other thread B was in the middle of the monitor() method
       
   795         // and received the old observed attribute value.
       
   796         //
       
   797         final boolean lookupMBeanInfo;
       
   798         synchronized (this) {
       
   799             if (!isActive())
       
   800                 throw new IllegalArgumentException(
       
   801                     "The monitor has been stopped");
       
   802             if (!attribute.equals(getObservedAttribute()))
       
   803                 throw new IllegalArgumentException(
       
   804                     "The observed attribute has been changed");
       
   805             lookupMBeanInfo =
       
   806                 (firstAttribute == null && attribute.indexOf('.') != -1);
       
   807         }
       
   808 
       
   809         // Look up MBeanInfo if needed
       
   810         //
       
   811         final MBeanInfo mbi;
       
   812         if (lookupMBeanInfo) {
       
   813             try {
       
   814                 mbi = mbsc.getMBeanInfo(object);
       
   815             } catch (IntrospectionException e) {
       
   816                 throw new IllegalArgumentException(e);
       
   817             }
       
   818         } else {
       
   819             mbi = null;
       
   820         }
       
   821 
       
   822         // Check for complex type attribute
       
   823         //
       
   824         final String fa;
       
   825         synchronized (this) {
       
   826             if (!isActive())
       
   827                 throw new IllegalArgumentException(
       
   828                     "The monitor has been stopped");
       
   829             if (!attribute.equals(getObservedAttribute()))
       
   830                 throw new IllegalArgumentException(
       
   831                     "The observed attribute has been changed");
       
   832             if (firstAttribute == null) {
       
   833                 if (attribute.indexOf('.') != -1) {
       
   834                     MBeanAttributeInfo mbaiArray[] = mbi.getAttributes();
       
   835                     for (MBeanAttributeInfo mbai : mbaiArray) {
       
   836                         if (attribute.equals(mbai.getName())) {
       
   837                             firstAttribute = attribute;
       
   838                             break;
       
   839                         }
       
   840                     }
       
   841                     if (firstAttribute == null) {
       
   842                         String tokens[] = attribute.split("\\.", -1);
       
   843                         firstAttribute = tokens[0];
       
   844                         for (int i = 1; i < tokens.length; i++)
       
   845                             remainingAttributes.add(tokens[i]);
       
   846                         isComplexTypeAttribute = true;
       
   847                     }
       
   848                 } else {
       
   849                     firstAttribute = attribute;
       
   850                 }
       
   851             }
       
   852             fa = firstAttribute;
       
   853         }
       
   854         return mbsc.getAttribute(object, fa);
       
   855     }
       
   856 
       
   857     Comparable<?> getComparableFromAttribute(ObjectName object,
       
   858                                              String attribute,
       
   859                                              Object value)
       
   860         throws AttributeNotFoundException {
       
   861         if (isComplexTypeAttribute) {
       
   862             Object v = value;
       
   863             for (String attr : remainingAttributes)
       
   864                 v = Introspector.elementFromComplex(v, attr);
       
   865             return (Comparable<?>) v;
       
   866         } else {
       
   867             return (Comparable<?>) value;
       
   868         }
       
   869     }
       
   870 
       
   871     boolean isComparableTypeValid(ObjectName object,
       
   872                                   String attribute,
       
   873                                   Comparable<?> value) {
       
   874         return true;
       
   875     }
       
   876 
       
   877     String buildErrorNotification(ObjectName object,
       
   878                                   String attribute,
       
   879                                   Comparable<?> value) {
       
   880         return null;
       
   881     }
       
   882 
       
   883     void onErrorNotification(MonitorNotification notification) {
       
   884     }
       
   885 
       
   886     Comparable<?> getDerivedGaugeFromComparable(ObjectName object,
       
   887                                                 String attribute,
       
   888                                                 Comparable<?> value) {
       
   889         return (Comparable<?>) value;
       
   890     }
       
   891 
       
   892     MonitorNotification buildAlarmNotification(ObjectName object,
       
   893                                                String attribute,
       
   894                                                Comparable<?> value){
       
   895         return null;
       
   896     }
       
   897 
       
   898     boolean isThresholdTypeValid(ObjectName object,
       
   899                                  String attribute,
       
   900                                  Comparable<?> value) {
       
   901         return true;
       
   902     }
       
   903 
       
   904     static Class<? extends Number> classForType(NumericalType type) {
       
   905         switch (type) {
       
   906             case BYTE:
       
   907                 return Byte.class;
       
   908             case SHORT:
       
   909                 return Short.class;
       
   910             case INTEGER:
       
   911                 return Integer.class;
       
   912             case LONG:
       
   913                 return Long.class;
       
   914             case FLOAT:
       
   915                 return Float.class;
       
   916             case DOUBLE:
       
   917                 return Double.class;
       
   918             default:
       
   919                 throw new IllegalArgumentException(
       
   920                     "Unsupported numerical type");
       
   921         }
       
   922     }
       
   923 
       
   924     static boolean isValidForType(Object value, Class<? extends Number> c) {
       
   925         return ((value == INTEGER_ZERO) || c.isInstance(value));
       
   926     }
       
   927 
       
   928     /**
       
   929      * Get the specified {@code ObservedObject} if this object is
       
   930      * contained in the set of observed MBeans, or {@code null}
       
   931      * otherwise.
       
   932      *
       
   933      * @param object the name of the {@code ObservedObject} to retrieve.
       
   934      *
       
   935      * @return The {@code ObservedObject} associated to the supplied
       
   936      * {@code ObjectName}.
       
   937      *
       
   938      * @since 1.6
       
   939      */
       
   940     synchronized ObservedObject getObservedObject(ObjectName object) {
       
   941         for (ObservedObject o : observedObjects)
       
   942             if (o.getObservedObject().equals(object))
       
   943                 return o;
       
   944         return null;
       
   945     }
       
   946 
       
   947     /**
       
   948      * Factory method for ObservedObject creation.
       
   949      *
       
   950      * @since 1.6
       
   951      */
       
   952     ObservedObject createObservedObject(ObjectName object) {
       
   953         return new ObservedObject(object);
       
   954     }
       
   955 
       
   956     /**
       
   957      * Create the {@link #alreadyNotified} array from
       
   958      * the {@code ObservedObject} array list.
       
   959      */
       
   960     synchronized void createAlreadyNotified() {
       
   961         // Update elementCount.
       
   962         //
       
   963         elementCount = observedObjects.size();
       
   964 
       
   965         // Update arrays.
       
   966         //
       
   967         alreadyNotifieds = new int[elementCount];
       
   968         for (int i = 0; i < elementCount; i++) {
       
   969             alreadyNotifieds[i] = observedObjects.get(i).getAlreadyNotified();
       
   970         }
       
   971         updateDeprecatedAlreadyNotified();
       
   972     }
       
   973 
       
   974     /**
       
   975      * Update the deprecated {@link #alreadyNotified} field.
       
   976      */
       
   977     synchronized void updateDeprecatedAlreadyNotified() {
       
   978         if (elementCount > 0)
       
   979             alreadyNotified = alreadyNotifieds[0];
       
   980         else
       
   981             alreadyNotified = 0;
       
   982     }
       
   983 
       
   984     /**
       
   985      * Update the {@link #alreadyNotifieds} array element at the given index
       
   986      * with the already notified flag in the given {@code ObservedObject}.
       
   987      * Ensure the deprecated {@link #alreadyNotified} field is updated
       
   988      * if appropriate.
       
   989      */
       
   990     synchronized void updateAlreadyNotified(ObservedObject o, int index) {
       
   991         alreadyNotifieds[index] = o.getAlreadyNotified();
       
   992         if (index == 0)
       
   993             updateDeprecatedAlreadyNotified();
       
   994     }
       
   995 
       
   996     /**
       
   997      * Check if the given bits in the given element of {@link #alreadyNotifieds}
       
   998      * are set.
       
   999      */
       
  1000     synchronized boolean isAlreadyNotified(ObservedObject o, int mask) {
       
  1001         return ((o.getAlreadyNotified() & mask) != 0);
       
  1002     }
       
  1003 
       
  1004     /**
       
  1005      * Set the given bits in the given element of {@link #alreadyNotifieds}.
       
  1006      * Ensure the deprecated {@link #alreadyNotified} field is updated
       
  1007      * if appropriate.
       
  1008      */
       
  1009     synchronized void setAlreadyNotified(ObservedObject o, int index,
       
  1010                                          int mask, int an[]) {
       
  1011         final int i = computeAlreadyNotifiedIndex(o, index, an);
       
  1012         if (i == -1)
       
  1013             return;
       
  1014         o.setAlreadyNotified(o.getAlreadyNotified() | mask);
       
  1015         updateAlreadyNotified(o, i);
       
  1016     }
       
  1017 
       
  1018     /**
       
  1019      * Reset the given bits in the given element of {@link #alreadyNotifieds}.
       
  1020      * Ensure the deprecated {@link #alreadyNotified} field is updated
       
  1021      * if appropriate.
       
  1022      */
       
  1023     synchronized void resetAlreadyNotified(ObservedObject o,
       
  1024                                            int index, int mask) {
       
  1025         o.setAlreadyNotified(o.getAlreadyNotified() & ~mask);
       
  1026         updateAlreadyNotified(o, index);
       
  1027     }
       
  1028 
       
  1029     /**
       
  1030      * Reset all bits in the given element of {@link #alreadyNotifieds}.
       
  1031      * Ensure the deprecated {@link #alreadyNotified} field is updated
       
  1032      * if appropriate.
       
  1033      */
       
  1034     synchronized void resetAllAlreadyNotified(ObservedObject o,
       
  1035                                               int index, int an[]) {
       
  1036         final int i = computeAlreadyNotifiedIndex(o, index, an);
       
  1037         if (i == -1)
       
  1038             return;
       
  1039         o.setAlreadyNotified(RESET_FLAGS_ALREADY_NOTIFIED);
       
  1040         updateAlreadyNotified(o, index);
       
  1041     }
       
  1042 
       
  1043     /**
       
  1044      * Check if the {@link #alreadyNotifieds} array has been modified.
       
  1045      * If true recompute the index for the given observed object.
       
  1046      */
       
  1047     synchronized int computeAlreadyNotifiedIndex(ObservedObject o,
       
  1048                                                  int index, int an[]) {
       
  1049         if (an == alreadyNotifieds) {
       
  1050             return index;
       
  1051         } else {
       
  1052             return observedObjects.indexOf(o);
       
  1053         }
       
  1054     }
       
  1055 
       
  1056     /*
       
  1057      * ------------------------------------------
       
  1058      *  PRIVATE METHODS
       
  1059      * ------------------------------------------
       
  1060      */
       
  1061 
       
  1062     /**
       
  1063      * This method is used by the monitor MBean to create and send a
       
  1064      * monitor notification to all the listeners registered for this
       
  1065      * kind of notification.
       
  1066      *
       
  1067      * @param type The notification type.
       
  1068      * @param timeStamp The notification emission date.
       
  1069      * @param msg The notification message.
       
  1070      * @param derGauge The derived gauge.
       
  1071      * @param trigger The threshold/string (depending on the monitor
       
  1072      * type) that triggered off the notification.
       
  1073      * @param object The ObjectName of the observed object that triggered
       
  1074      * off the notification.
       
  1075      * @param onError Flag indicating if this monitor notification is
       
  1076      * an error notification or an alarm notification.
       
  1077      */
       
  1078     private void sendNotification(String type, long timeStamp, String msg,
       
  1079                                   Object derGauge, Object trigger,
       
  1080                                   ObjectName object, boolean onError) {
       
  1081         if (!isActive())
       
  1082             return;
       
  1083 
       
  1084         if (MONITOR_LOGGER.isLoggable(Level.TRACE)) {
       
  1085             MONITOR_LOGGER.log(Level.TRACE, "send notification: " +
       
  1086                     "\n\tNotification observed object = " + object +
       
  1087                     "\n\tNotification observed attribute = " + observedAttribute +
       
  1088                     "\n\tNotification derived gauge = " + derGauge);
       
  1089         }
       
  1090 
       
  1091         long seqno = sequenceNumber.getAndIncrement();
       
  1092 
       
  1093         MonitorNotification mn =
       
  1094             new MonitorNotification(type,
       
  1095                                     this,
       
  1096                                     seqno,
       
  1097                                     timeStamp,
       
  1098                                     msg,
       
  1099                                     object,
       
  1100                                     observedAttribute,
       
  1101                                     derGauge,
       
  1102                                     trigger);
       
  1103         if (onError)
       
  1104             onErrorNotification(mn);
       
  1105         sendNotification(mn);
       
  1106     }
       
  1107 
       
  1108     /**
       
  1109      * This method is called by the monitor each time
       
  1110      * the granularity period has been exceeded.
       
  1111      * @param o The observed object.
       
  1112      */
       
  1113     private void monitor(ObservedObject o, int index, int an[]) {
       
  1114 
       
  1115         String attribute;
       
  1116         String notifType = null;
       
  1117         String msg = null;
       
  1118         Object derGauge = null;
       
  1119         Object trigger = null;
       
  1120         ObjectName object;
       
  1121         Comparable<?> value = null;
       
  1122         MonitorNotification alarm = null;
       
  1123 
       
  1124         if (!isActive())
       
  1125             return;
       
  1126 
       
  1127         // Check that neither the observed object nor the
       
  1128         // observed attribute are null.  If the observed
       
  1129         // object or observed attribute is null, this means
       
  1130         // that the monitor started before a complete
       
  1131         // initialization and nothing is done.
       
  1132         //
       
  1133         synchronized (this) {
       
  1134             object = o.getObservedObject();
       
  1135             attribute = getObservedAttribute();
       
  1136             if (object == null || attribute == null) {
       
  1137                 return;
       
  1138             }
       
  1139         }
       
  1140 
       
  1141         // Check that the observed object is registered in the
       
  1142         // MBean server and that the observed attribute
       
  1143         // belongs to the observed object.
       
  1144         //
       
  1145         Object attributeValue = null;
       
  1146         try {
       
  1147             attributeValue = getAttribute(server, object, attribute);
       
  1148             if (attributeValue == null)
       
  1149                 if (isAlreadyNotified(
       
  1150                         o, OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED))
       
  1151                     return;
       
  1152                 else {
       
  1153                     notifType = OBSERVED_ATTRIBUTE_TYPE_ERROR;
       
  1154                     setAlreadyNotified(
       
  1155                         o, index, OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED, an);
       
  1156                     msg = "The observed attribute value is null.";
       
  1157                     MONITOR_LOGGER.log(Level.TRACE, msg);
       
  1158                 }
       
  1159         } catch (NullPointerException np_ex) {
       
  1160             if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED))
       
  1161                 return;
       
  1162             else {
       
  1163                 notifType = RUNTIME_ERROR;
       
  1164                 setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an);
       
  1165                 msg =
       
  1166                     "The monitor must be registered in the MBean " +
       
  1167                     "server or an MBeanServerConnection must be " +
       
  1168                     "explicitly supplied.";
       
  1169                 MONITOR_LOGGER.log(Level.TRACE, msg);
       
  1170                 MONITOR_LOGGER.log(Level.TRACE, np_ex::toString);
       
  1171             }
       
  1172         } catch (InstanceNotFoundException inf_ex) {
       
  1173             if (isAlreadyNotified(o, OBSERVED_OBJECT_ERROR_NOTIFIED))
       
  1174                 return;
       
  1175             else {
       
  1176                 notifType = OBSERVED_OBJECT_ERROR;
       
  1177                 setAlreadyNotified(
       
  1178                     o, index, OBSERVED_OBJECT_ERROR_NOTIFIED, an);
       
  1179                 msg =
       
  1180                     "The observed object must be accessible in " +
       
  1181                     "the MBeanServerConnection.";
       
  1182                 MONITOR_LOGGER.log(Level.TRACE, msg);
       
  1183                 MONITOR_LOGGER.log(Level.TRACE, inf_ex::toString);
       
  1184             }
       
  1185         } catch (AttributeNotFoundException anf_ex) {
       
  1186             if (isAlreadyNotified(o, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED))
       
  1187                 return;
       
  1188             else {
       
  1189                 notifType = OBSERVED_ATTRIBUTE_ERROR;
       
  1190                 setAlreadyNotified(
       
  1191                     o, index, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED, an);
       
  1192                 msg =
       
  1193                     "The observed attribute must be accessible in " +
       
  1194                     "the observed object.";
       
  1195                 MONITOR_LOGGER.log(Level.TRACE, msg);
       
  1196                 MONITOR_LOGGER.log(Level.TRACE, anf_ex::toString);
       
  1197             }
       
  1198         } catch (MBeanException mb_ex) {
       
  1199             if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED))
       
  1200                 return;
       
  1201             else {
       
  1202                 notifType = RUNTIME_ERROR;
       
  1203                 setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an);
       
  1204                 msg = mb_ex.getMessage() == null ? "" : mb_ex.getMessage();
       
  1205                 MONITOR_LOGGER.log(Level.TRACE, msg);
       
  1206                 MONITOR_LOGGER.log(Level.TRACE, mb_ex::toString);
       
  1207             }
       
  1208         } catch (ReflectionException ref_ex) {
       
  1209             if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED)) {
       
  1210                 return;
       
  1211             } else {
       
  1212                 notifType = RUNTIME_ERROR;
       
  1213                 setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an);
       
  1214                 msg = ref_ex.getMessage() == null ? "" : ref_ex.getMessage();
       
  1215                 MONITOR_LOGGER.log(Level.TRACE, msg);
       
  1216                 MONITOR_LOGGER.log(Level.TRACE, ref_ex::toString);
       
  1217             }
       
  1218         } catch (IOException io_ex) {
       
  1219             if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED))
       
  1220                 return;
       
  1221             else {
       
  1222                 notifType = RUNTIME_ERROR;
       
  1223                 setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an);
       
  1224                 msg = io_ex.getMessage() == null ? "" : io_ex.getMessage();
       
  1225                 MONITOR_LOGGER.log(Level.TRACE, msg);
       
  1226                 MONITOR_LOGGER.log(Level.TRACE, io_ex::toString);
       
  1227             }
       
  1228         } catch (RuntimeException rt_ex) {
       
  1229             if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED))
       
  1230                 return;
       
  1231             else {
       
  1232                 notifType = RUNTIME_ERROR;
       
  1233                 setAlreadyNotified(o, index, RUNTIME_ERROR_NOTIFIED, an);
       
  1234                 msg = rt_ex.getMessage() == null ? "" : rt_ex.getMessage();
       
  1235                 MONITOR_LOGGER.log(Level.TRACE, msg);
       
  1236                 MONITOR_LOGGER.log(Level.TRACE, rt_ex::toString);
       
  1237             }
       
  1238         }
       
  1239 
       
  1240         synchronized (this) {
       
  1241 
       
  1242             // Check if the monitor has been stopped.
       
  1243             //
       
  1244             if (!isActive())
       
  1245                 return;
       
  1246 
       
  1247             // Check if the observed attribute has been changed.
       
  1248             //
       
  1249             // Avoid race condition where mbs.getAttribute() succeeded but
       
  1250             // another thread replaced the observed attribute meanwhile.
       
  1251             //
       
  1252             // Avoid setting computed derived gauge on erroneous attribute.
       
  1253             //
       
  1254             if (!attribute.equals(getObservedAttribute()))
       
  1255                 return;
       
  1256 
       
  1257             // Derive a Comparable object from the ObservedAttribute value
       
  1258             // if the type of the ObservedAttribute value is a complex type.
       
  1259             //
       
  1260             if (msg == null) {
       
  1261                 try {
       
  1262                     value = getComparableFromAttribute(object,
       
  1263                                                        attribute,
       
  1264                                                        attributeValue);
       
  1265                 } catch (ClassCastException e) {
       
  1266                     if (isAlreadyNotified(
       
  1267                             o, OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED))
       
  1268                         return;
       
  1269                     else {
       
  1270                         notifType = OBSERVED_ATTRIBUTE_TYPE_ERROR;
       
  1271                         setAlreadyNotified(o, index,
       
  1272                             OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED, an);
       
  1273                         msg =
       
  1274                             "The observed attribute value does not " +
       
  1275                             "implement the Comparable interface.";
       
  1276                         MONITOR_LOGGER.log(Level.TRACE, msg);
       
  1277                         MONITOR_LOGGER.log(Level.TRACE, e::toString);
       
  1278                     }
       
  1279                 } catch (AttributeNotFoundException e) {
       
  1280                     if (isAlreadyNotified(o, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED))
       
  1281                         return;
       
  1282                     else {
       
  1283                         notifType = OBSERVED_ATTRIBUTE_ERROR;
       
  1284                         setAlreadyNotified(
       
  1285                             o, index, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED, an);
       
  1286                         msg =
       
  1287                             "The observed attribute must be accessible in " +
       
  1288                             "the observed object.";
       
  1289                         MONITOR_LOGGER.log(Level.TRACE, msg);
       
  1290                         MONITOR_LOGGER.log(Level.TRACE, e::toString);
       
  1291                     }
       
  1292                 } catch (RuntimeException e) {
       
  1293                     if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED))
       
  1294                         return;
       
  1295                     else {
       
  1296                         notifType = RUNTIME_ERROR;
       
  1297                         setAlreadyNotified(o, index,
       
  1298                             RUNTIME_ERROR_NOTIFIED, an);
       
  1299                         msg = e.getMessage() == null ? "" : e.getMessage();
       
  1300                         MONITOR_LOGGER.log(Level.TRACE, msg);
       
  1301                         MONITOR_LOGGER.log(Level.TRACE, e::toString);
       
  1302                     }
       
  1303                 }
       
  1304             }
       
  1305 
       
  1306             // Check that the observed attribute type is supported by this
       
  1307             // monitor.
       
  1308             //
       
  1309             if (msg == null) {
       
  1310                 if (!isComparableTypeValid(object, attribute, value)) {
       
  1311                     if (isAlreadyNotified(
       
  1312                             o, OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED))
       
  1313                         return;
       
  1314                     else {
       
  1315                         notifType = OBSERVED_ATTRIBUTE_TYPE_ERROR;
       
  1316                         setAlreadyNotified(o, index,
       
  1317                             OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED, an);
       
  1318                         msg = "The observed attribute type is not valid.";
       
  1319                         MONITOR_LOGGER.log(Level.TRACE, msg);
       
  1320                     }
       
  1321                 }
       
  1322             }
       
  1323 
       
  1324             // Check that threshold type is supported by this monitor.
       
  1325             //
       
  1326             if (msg == null) {
       
  1327                 if (!isThresholdTypeValid(object, attribute, value)) {
       
  1328                     if (isAlreadyNotified(o, THRESHOLD_ERROR_NOTIFIED))
       
  1329                         return;
       
  1330                     else {
       
  1331                         notifType = THRESHOLD_ERROR;
       
  1332                         setAlreadyNotified(o, index,
       
  1333                             THRESHOLD_ERROR_NOTIFIED, an);
       
  1334                         msg = "The threshold type is not valid.";
       
  1335                         MONITOR_LOGGER.log(Level.TRACE, msg);
       
  1336                     }
       
  1337                 }
       
  1338             }
       
  1339 
       
  1340             // Let someone subclassing the monitor to perform additional
       
  1341             // monitor consistency checks and report errors if necessary.
       
  1342             //
       
  1343             if (msg == null) {
       
  1344                 msg = buildErrorNotification(object, attribute, value);
       
  1345                 if (msg != null) {
       
  1346                     if (isAlreadyNotified(o, RUNTIME_ERROR_NOTIFIED))
       
  1347                         return;
       
  1348                     else {
       
  1349                         notifType = RUNTIME_ERROR;
       
  1350                         setAlreadyNotified(o, index,
       
  1351                             RUNTIME_ERROR_NOTIFIED, an);
       
  1352                         MONITOR_LOGGER.log(Level.TRACE, msg);
       
  1353                     }
       
  1354                 }
       
  1355             }
       
  1356 
       
  1357             // If no errors were found then clear all error flags and
       
  1358             // let the monitor decide if a notification must be sent.
       
  1359             //
       
  1360             if (msg == null) {
       
  1361                 // Clear all already notified flags.
       
  1362                 //
       
  1363                 resetAllAlreadyNotified(o, index, an);
       
  1364 
       
  1365                 // Get derived gauge from comparable value.
       
  1366                 //
       
  1367                 derGauge = getDerivedGaugeFromComparable(object,
       
  1368                                                          attribute,
       
  1369                                                          value);
       
  1370 
       
  1371                 o.setDerivedGauge(derGauge);
       
  1372                 o.setDerivedGaugeTimeStamp(System.currentTimeMillis());
       
  1373 
       
  1374                 // Check if an alarm must be fired.
       
  1375                 //
       
  1376                 alarm = buildAlarmNotification(object,
       
  1377                                                attribute,
       
  1378                                                (Comparable<?>) derGauge);
       
  1379             }
       
  1380 
       
  1381         }
       
  1382 
       
  1383         // Notify monitor errors
       
  1384         //
       
  1385         if (msg != null)
       
  1386             sendNotification(notifType,
       
  1387                              System.currentTimeMillis(),
       
  1388                              msg,
       
  1389                              derGauge,
       
  1390                              trigger,
       
  1391                              object,
       
  1392                              true);
       
  1393 
       
  1394         // Notify monitor alarms
       
  1395         //
       
  1396         if (alarm != null && alarm.getType() != null)
       
  1397             sendNotification(alarm.getType(),
       
  1398                              System.currentTimeMillis(),
       
  1399                              alarm.getMessage(),
       
  1400                              derGauge,
       
  1401                              alarm.getTrigger(),
       
  1402                              object,
       
  1403                              false);
       
  1404     }
       
  1405 
       
  1406     /**
       
  1407      * Cleanup the scheduler and monitor tasks futures.
       
  1408      */
       
  1409     private synchronized void cleanupFutures() {
       
  1410         if (schedulerFuture != null) {
       
  1411             schedulerFuture.cancel(false);
       
  1412             schedulerFuture = null;
       
  1413         }
       
  1414         if (monitorFuture != null) {
       
  1415             monitorFuture.cancel(false);
       
  1416             monitorFuture = null;
       
  1417         }
       
  1418     }
       
  1419 
       
  1420     /**
       
  1421      * Cleanup the "is complex type attribute" info.
       
  1422      */
       
  1423     private synchronized void cleanupIsComplexTypeAttribute() {
       
  1424         firstAttribute = null;
       
  1425         remainingAttributes.clear();
       
  1426         isComplexTypeAttribute = false;
       
  1427     }
       
  1428 
       
  1429     /**
       
  1430      * SchedulerTask nested class: This class implements the Runnable interface.
       
  1431      *
       
  1432      * The SchedulerTask is executed periodically with a given fixed delay by
       
  1433      * the Scheduled Executor Service.
       
  1434      */
       
  1435     private class SchedulerTask implements Runnable {
       
  1436 
       
  1437         private MonitorTask task;
       
  1438 
       
  1439         /*
       
  1440          * ------------------------------------------
       
  1441          *  CONSTRUCTORS
       
  1442          * ------------------------------------------
       
  1443          */
       
  1444 
       
  1445         public SchedulerTask() {
       
  1446         }
       
  1447 
       
  1448         /*
       
  1449          * ------------------------------------------
       
  1450          *  GETTERS/SETTERS
       
  1451          * ------------------------------------------
       
  1452          */
       
  1453 
       
  1454         public void setMonitorTask(MonitorTask task) {
       
  1455             this.task = task;
       
  1456         }
       
  1457 
       
  1458         /*
       
  1459          * ------------------------------------------
       
  1460          *  PUBLIC METHODS
       
  1461          * ------------------------------------------
       
  1462          */
       
  1463 
       
  1464         public void run() {
       
  1465             synchronized (Monitor.this) {
       
  1466                 Monitor.this.monitorFuture = task.submit();
       
  1467             }
       
  1468         }
       
  1469     }
       
  1470 
       
  1471     /**
       
  1472      * MonitorTask nested class: This class implements the Runnable interface.
       
  1473      *
       
  1474      * The MonitorTask is executed periodically with a given fixed delay by the
       
  1475      * Scheduled Executor Service.
       
  1476      */
       
  1477     private class MonitorTask implements Runnable {
       
  1478 
       
  1479         private ThreadPoolExecutor executor;
       
  1480 
       
  1481         /*
       
  1482          * ------------------------------------------
       
  1483          *  CONSTRUCTORS
       
  1484          * ------------------------------------------
       
  1485          */
       
  1486 
       
  1487         public MonitorTask() {
       
  1488             // Find out if there's already an existing executor for the calling
       
  1489             // thread and reuse it. Otherwise, create a new one and store it in
       
  1490             // the executors map. If there is a SecurityManager, the group of
       
  1491             // System.getSecurityManager() is used, else the group of the thread
       
  1492             // instantiating this MonitorTask, i.e. the group of the thread that
       
  1493             // calls "Monitor.start()".
       
  1494             SecurityManager s = System.getSecurityManager();
       
  1495             ThreadGroup group = (s != null) ? s.getThreadGroup() :
       
  1496                 Thread.currentThread().getThreadGroup();
       
  1497             synchronized (executorsLock) {
       
  1498                 for (ThreadPoolExecutor e : executors.keySet()) {
       
  1499                     DaemonThreadFactory tf =
       
  1500                             (DaemonThreadFactory) e.getThreadFactory();
       
  1501                     ThreadGroup tg = tf.getThreadGroup();
       
  1502                     if (tg == group) {
       
  1503                         executor = e;
       
  1504                         break;
       
  1505                     }
       
  1506                 }
       
  1507                 if (executor == null) {
       
  1508                     executor = new ThreadPoolExecutor(
       
  1509                             maximumPoolSize,
       
  1510                             maximumPoolSize,
       
  1511                             60L,
       
  1512                             TimeUnit.SECONDS,
       
  1513                             new LinkedBlockingQueue<Runnable>(),
       
  1514                             new DaemonThreadFactory("ThreadGroup<" +
       
  1515                             group.getName() + "> Executor", group));
       
  1516                     executor.allowCoreThreadTimeOut(true);
       
  1517                     executors.put(executor, null);
       
  1518                 }
       
  1519             }
       
  1520         }
       
  1521 
       
  1522         /*
       
  1523          * ------------------------------------------
       
  1524          *  PUBLIC METHODS
       
  1525          * ------------------------------------------
       
  1526          */
       
  1527 
       
  1528         public Future<?> submit() {
       
  1529             return executor.submit(this);
       
  1530         }
       
  1531 
       
  1532         public void run() {
       
  1533             final ScheduledFuture<?> sf;
       
  1534             final AccessControlContext ac;
       
  1535             synchronized (Monitor.this) {
       
  1536                 sf = Monitor.this.schedulerFuture;
       
  1537                 ac = Monitor.this.acc;
       
  1538             }
       
  1539             PrivilegedAction<Void> action = new PrivilegedAction<Void>() {
       
  1540                 public Void run() {
       
  1541                     if (Monitor.this.isActive()) {
       
  1542                         final int an[] = alreadyNotifieds;
       
  1543                         int index = 0;
       
  1544                         for (ObservedObject o : Monitor.this.observedObjects) {
       
  1545                             if (Monitor.this.isActive()) {
       
  1546                                 Monitor.this.monitor(o, index++, an);
       
  1547                             }
       
  1548                         }
       
  1549                     }
       
  1550                     return null;
       
  1551                 }
       
  1552             };
       
  1553             if (ac == null) {
       
  1554                 throw new SecurityException("AccessControlContext cannot be null");
       
  1555             }
       
  1556             AccessController.doPrivileged(action, ac);
       
  1557             synchronized (Monitor.this) {
       
  1558                 if (Monitor.this.isActive() &&
       
  1559                     Monitor.this.schedulerFuture == sf) {
       
  1560                     Monitor.this.monitorFuture = null;
       
  1561                     Monitor.this.schedulerFuture =
       
  1562                         scheduler.schedule(Monitor.this.schedulerTask,
       
  1563                                            Monitor.this.getGranularityPeriod(),
       
  1564                                            TimeUnit.MILLISECONDS);
       
  1565                 }
       
  1566             }
       
  1567         }
       
  1568     }
       
  1569 
       
  1570     /**
       
  1571      * Daemon thread factory used by the monitor executors.
       
  1572      * <P>
       
  1573      * This factory creates all new threads used by an Executor in
       
  1574      * the same ThreadGroup. If there is a SecurityManager, it uses
       
  1575      * the group of System.getSecurityManager(), else the group of
       
  1576      * the thread instantiating this DaemonThreadFactory. Each new
       
  1577      * thread is created as a daemon thread with priority
       
  1578      * Thread.NORM_PRIORITY. New threads have names accessible via
       
  1579      * Thread.getName() of "{@literal JMX Monitor <pool-name> Pool [Thread-M]}",
       
  1580      * where M is the sequence number of the thread created by this
       
  1581      * factory.
       
  1582      */
       
  1583     private static class DaemonThreadFactory implements ThreadFactory {
       
  1584         final ThreadGroup group;
       
  1585         final AtomicInteger threadNumber = new AtomicInteger(1);
       
  1586         final String namePrefix;
       
  1587         static final String nameSuffix = "]";
       
  1588 
       
  1589         public DaemonThreadFactory(String poolName) {
       
  1590             SecurityManager s = System.getSecurityManager();
       
  1591             group = (s != null) ? s.getThreadGroup() :
       
  1592                                   Thread.currentThread().getThreadGroup();
       
  1593             namePrefix = "JMX Monitor " + poolName + " Pool [Thread-";
       
  1594         }
       
  1595 
       
  1596         public DaemonThreadFactory(String poolName, ThreadGroup threadGroup) {
       
  1597             group = threadGroup;
       
  1598             namePrefix = "JMX Monitor " + poolName + " Pool [Thread-";
       
  1599         }
       
  1600 
       
  1601         public ThreadGroup getThreadGroup() {
       
  1602             return group;
       
  1603         }
       
  1604 
       
  1605         public Thread newThread(Runnable r) {
       
  1606             Thread t = new Thread(
       
  1607                 group,
       
  1608                 r,
       
  1609                 namePrefix + threadNumber.getAndIncrement() + nameSuffix,
       
  1610                 0,
       
  1611                 false
       
  1612             );
       
  1613 
       
  1614             t.setDaemon(true);
       
  1615             if (t.getPriority() != Thread.NORM_PRIORITY)
       
  1616                 t.setPriority(Thread.NORM_PRIORITY);
       
  1617             return t;
       
  1618         }
       
  1619     }
       
  1620 }