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