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