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