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