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