1 /*
2  * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package javax.management.monitor;
27 
28 import static com.sun.jmx.defaults.JmxProperties.MONITOR_LOGGER;
29 import com.sun.jmx.mbeanserver.GetPropertyAction;
30 import com.sun.jmx.mbeanserver.Introspector;
31 import java.io.IOException;
32 import java.security.AccessControlContext;
33 import java.security.AccessController;
34 import java.security.PrivilegedAction;
35 import java.security.ProtectionDomain;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.WeakHashMap;
39 import java.util.concurrent.CopyOnWriteArrayList;
40 import java.util.concurrent.Executors;
41 import java.util.concurrent.Future;
42 import java.util.concurrent.LinkedBlockingQueue;
43 import java.util.concurrent.ScheduledExecutorService;
44 import java.util.concurrent.ScheduledFuture;
45 import java.util.concurrent.ThreadFactory;
46 import java.util.concurrent.ThreadPoolExecutor;
47 import java.util.concurrent.TimeUnit;
48 import java.util.concurrent.atomic.AtomicInteger;
49 import java.util.concurrent.atomic.AtomicLong;
50 import java.lang.System.Logger.Level;
51 import javax.management.AttributeNotFoundException;
52 import javax.management.InstanceNotFoundException;
53 import javax.management.IntrospectionException;
54 import javax.management.MBeanAttributeInfo;
55 import javax.management.MBeanException;
56 import javax.management.MBeanInfo;
57 import javax.management.MBeanRegistration;
58 import javax.management.MBeanServer;
59 import javax.management.MBeanServerConnection;
60 import javax.management.NotificationBroadcasterSupport;
61 import javax.management.ObjectName;
62 import javax.management.ReflectionException;
63 import static javax.management.monitor.MonitorNotification.*;
64 
65 /**
66  * Defines the part common to all monitor MBeans.
67  * A monitor MBean monitors values of an attribute common to a set of observed
68  * MBeans. The observed attribute is monitored at intervals specified by the
69  * granularity period. A gauge value (derived gauge) is derived from the values
70  * of the observed attribute.
71  *
72  *
73  * @since 1.5
74  */
75 public abstract class Monitor
76     extends NotificationBroadcasterSupport
77     implements MonitorMBean, MBeanRegistration {
78 
79     /*
80      * ------------------------------------------
81      *  PACKAGE CLASSES
82      * ------------------------------------------
83      */
84 
85     static class ObservedObject {
86 
ObservedObject(ObjectName observedObject)87         public ObservedObject(ObjectName observedObject) {
88             this.observedObject = observedObject;
89         }
90 
getObservedObject()91         public final ObjectName getObservedObject() {
92             return observedObject;
93         }
getAlreadyNotified()94         public final synchronized int getAlreadyNotified() {
95             return alreadyNotified;
96         }
setAlreadyNotified(int alreadyNotified)97         public final synchronized void setAlreadyNotified(int alreadyNotified) {
98             this.alreadyNotified = alreadyNotified;
99         }
getDerivedGauge()100         public final synchronized Object getDerivedGauge() {
101             return derivedGauge;
102         }
setDerivedGauge(Object derivedGauge)103         public final synchronized void setDerivedGauge(Object derivedGauge) {
104             this.derivedGauge = derivedGauge;
105         }
getDerivedGaugeTimeStamp()106         public final synchronized long getDerivedGaugeTimeStamp() {
107             return derivedGaugeTimeStamp;
108         }
setDerivedGaugeTimeStamp( long derivedGaugeTimeStamp)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      */
preRegister(MBeanServer server, ObjectName name)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      */
postRegister(Boolean registrationDone)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      */
preDeregister()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      */
postDeregister()432     public void postDeregister() {
433     }
434 
435     /**
436      * Starts the monitor.
437      */
start()438     public abstract void start();
439 
440     /**
441      * Stops the monitor.
442      */
stop()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
getObservedObject()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
setObservedObject(ObjectName object)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      */
addObservedObject(ObjectName object)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      */
removeObservedObject(ObjectName object)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      */
containsObservedObject(ObjectName object)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      */
getObservedObjects()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      */
getObservedAttribute()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      */
setObservedAttribute(String attribute)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      */
getGranularityPeriod()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      */
setGranularityPeriod(long period)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. */
isActive()678     public synchronized boolean isActive() {
679         return isActive;
680     }
681 
682     /*
683      * ------------------------------------------
684      *  PACKAGE METHODS
685      * ------------------------------------------
686      */
687 
688     /**
689      * Starts the monitor.
690      */
doStart()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      */
doStop()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      */
getDerivedGauge(ObjectName object)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      */
getDerivedGaugeTimeStamp(ObjectName object)779     synchronized long getDerivedGaugeTimeStamp(ObjectName object) {
780         final ObservedObject o = getObservedObject(object);
781         return o == null ? 0 : o.getDerivedGaugeTimeStamp();
782     }
783 
getAttribute(MBeanServerConnection mbsc, ObjectName object, String attribute)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 
getComparableFromAttribute(ObjectName object, String attribute, Object value)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 
isComparableTypeValid(ObjectName object, String attribute, Comparable<?> value)871     boolean isComparableTypeValid(ObjectName object,
872                                   String attribute,
873                                   Comparable<?> value) {
874         return true;
875     }
876 
buildErrorNotification(ObjectName object, String attribute, Comparable<?> value)877     String buildErrorNotification(ObjectName object,
878                                   String attribute,
879                                   Comparable<?> value) {
880         return null;
881     }
882 
onErrorNotification(MonitorNotification notification)883     void onErrorNotification(MonitorNotification notification) {
884     }
885 
getDerivedGaugeFromComparable(ObjectName object, String attribute, Comparable<?> value)886     Comparable<?> getDerivedGaugeFromComparable(ObjectName object,
887                                                 String attribute,
888                                                 Comparable<?> value) {
889         return (Comparable<?>) value;
890     }
891 
buildAlarmNotification(ObjectName object, String attribute, Comparable<?> value)892     MonitorNotification buildAlarmNotification(ObjectName object,
893                                                String attribute,
894                                                Comparable<?> value){
895         return null;
896     }
897 
isThresholdTypeValid(ObjectName object, String attribute, Comparable<?> value)898     boolean isThresholdTypeValid(ObjectName object,
899                                  String attribute,
900                                  Comparable<?> value) {
901         return true;
902     }
903 
classForType(NumericalType type)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 
isValidForType(Object value, Class<? extends Number> c)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      */
getObservedObject(ObjectName object)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      */
createObservedObject(ObjectName object)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      */
createAlreadyNotified()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      */
updateDeprecatedAlreadyNotified()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      */
updateAlreadyNotified(ObservedObject o, int index)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      */
isAlreadyNotified(ObservedObject o, int mask)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      */
setAlreadyNotified(ObservedObject o, int index, int mask, int an[])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      */
resetAlreadyNotified(ObservedObject o, int index, int mask)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      */
resetAllAlreadyNotified(ObservedObject o, int index, int an[])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      */
computeAlreadyNotifiedIndex(ObservedObject o, int index, int an[])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      */
sendNotification(String type, long timeStamp, String msg, Object derGauge, Object trigger, ObjectName object, boolean onError)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      */
monitor(ObservedObject o, int index, int an[])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      */
cleanupFutures()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      */
cleanupIsComplexTypeAttribute()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 
SchedulerTask()1445         public SchedulerTask() {
1446         }
1447 
1448         /*
1449          * ------------------------------------------
1450          *  GETTERS/SETTERS
1451          * ------------------------------------------
1452          */
1453 
setMonitorTask(MonitorTask task)1454         public void setMonitorTask(MonitorTask task) {
1455             this.task = task;
1456         }
1457 
1458         /*
1459          * ------------------------------------------
1460          *  PUBLIC METHODS
1461          * ------------------------------------------
1462          */
1463 
run()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 
MonitorTask()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 
submit()1528         public Future<?> submit() {
1529             return executor.submit(this);
1530         }
1531 
run()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 
DaemonThreadFactory(String poolName)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 
DaemonThreadFactory(String poolName, ThreadGroup threadGroup)1596         public DaemonThreadFactory(String poolName, ThreadGroup threadGroup) {
1597             group = threadGroup;
1598             namePrefix = "JMX Monitor " + poolName + " Pool [Thread-";
1599         }
1600 
getThreadGroup()1601         public ThreadGroup getThreadGroup() {
1602             return group;
1603         }
1604 
newThread(Runnable r)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