1. /*
  2. * @(#)Monitor.java 4.42 04/05/18
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.management.monitor;
  8. // java imports
  9. //
  10. import java.util.ArrayList;
  11. import java.util.Arrays;
  12. import java.util.Iterator;
  13. import java.util.List;
  14. // jmx imports
  15. //
  16. import javax.management.MBeanServer;
  17. import javax.management.MBeanRegistration;
  18. import javax.management.ObjectName;
  19. import com.sun.jmx.trace.Trace;
  20. /**
  21. * Defines the common part to all monitor MBeans.
  22. * A monitor MBean monitors values of an attribute common to a set of observed
  23. * MBeans. The observed attribute is monitored at intervals specified by the
  24. * granularity period. A gauge value (derived gauge) is derived from the values
  25. * of the observed attribute.
  26. *
  27. * @version 4.42 05/18/04
  28. * @author Sun Microsystems, Inc
  29. *
  30. * @since 1.5
  31. */
  32. public abstract class Monitor
  33. extends javax.management.NotificationBroadcasterSupport
  34. implements MonitorMBean, javax.management.MBeanRegistration
  35. {
  36. /*
  37. * ------------------------------------------
  38. * PRIVATE VARIABLES
  39. * ------------------------------------------
  40. */
  41. /**
  42. * List of MBeans to which the attribute to observe belongs.
  43. * <BR>The default value is set to null.
  44. */
  45. private List observedObjects = new ArrayList();
  46. /**
  47. * Attribute to observe.
  48. * <BR>The default value is set to null.
  49. */
  50. private String observedAttribute = null;
  51. /**
  52. * Monitor granularity period (in milliseconds).
  53. * <BR>The default value is set to 10 seconds.
  54. */
  55. private long granularityPeriod = 10000;
  56. /*
  57. * ------------------------------------------
  58. * PROTECTED VARIABLES
  59. * ------------------------------------------
  60. */
  61. /**
  62. * The amount by which the capacity of the monitor arrays are
  63. * automatically incremented when their size becomes greater than
  64. * their capacity.
  65. */
  66. protected final static int capacityIncrement = 16;
  67. /**
  68. * The number of valid components in the vector of observed objects.
  69. *
  70. * @since.unbundled JMX 1.2
  71. */
  72. protected int elementCount = 0;
  73. /**
  74. * Monitor errors that have already been notified.
  75. * @deprecated equivalent to {@link #alreadyNotifieds}[0].
  76. */
  77. @Deprecated
  78. protected int alreadyNotified = 0;
  79. /**
  80. * <p>Selected monitor errors that have already been notified.</p>
  81. *
  82. * <p>Each element in this array corresponds to an observed object
  83. * in the vector. It contains a bit mask of the flags {@link
  84. * #OBSERVED_OBJECT_ERROR_NOTIFIED} etc, indicating whether the
  85. * corresponding notification has already been sent for the MBean
  86. * being monitored.</p>
  87. *
  88. * @since.unbundled JMX 1.2
  89. */
  90. protected int alreadyNotifieds[] = new int[capacityIncrement];
  91. /**
  92. * Reference on the MBean server. This reference is null when the
  93. * monitor MBean is not registered in an MBean server. This
  94. * reference is initialized before the monitor MBean is registered
  95. * in the MBean server.
  96. * @see #preRegister(MBeanServer server, ObjectName name)
  97. */
  98. protected MBeanServer server = null;
  99. // Flags defining possible monitor errors.
  100. //
  101. /**
  102. * This flag is used to reset the {@link #alreadyNotifieds
  103. * alreadyNotifieds} monitor attribute.
  104. */
  105. protected static final int RESET_FLAGS_ALREADY_NOTIFIED = 0;
  106. /**
  107. * Flag denoting that a notification has occurred after changing
  108. * the observed object. This flag is used to check that the new
  109. * observed object is registered in the MBean server at the time
  110. * of the first notification.
  111. */
  112. protected static final int OBSERVED_OBJECT_ERROR_NOTIFIED = 1;
  113. /**
  114. * Flag denoting that a notification has occurred after changing
  115. * the observed attribute. This flag is used to check that the
  116. * new observed attribute belongs to the observed object at the
  117. * time of the first notification.
  118. */
  119. protected static final int OBSERVED_ATTRIBUTE_ERROR_NOTIFIED = 2;
  120. /**
  121. * Flag denoting that a notification has occurred after changing
  122. * the observed object or the observed attribute. This flag is
  123. * used to check that the observed attribute type is correct
  124. * (depending on the monitor in use) at the time of the first
  125. * notification.
  126. */
  127. protected static final int OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED = 4;
  128. /**
  129. * Flag denoting that a notification has occurred after changing
  130. * the observed object or the observed attribute. This flag is
  131. * used to notify any exception (except the cases described above)
  132. * when trying to get the value of the observed attribute at the
  133. * time of the first notification.
  134. */
  135. protected static final int RUNTIME_ERROR_NOTIFIED = 8;
  136. /**
  137. * This field is retained for compatibility but should not be referenced.
  138. *
  139. * @deprecated No replacement.
  140. */
  141. @Deprecated
  142. protected String dbgTag = "Monitor";
  143. /*
  144. * ------------------------------------------
  145. * PACKAGE VARIABLES
  146. * ------------------------------------------
  147. */
  148. /**
  149. * Monitor state.
  150. * The default value is set to <CODE>false</CODE>.
  151. */
  152. boolean isActive = false;
  153. /**
  154. * Monitor sequence number.
  155. * The default value is set to 0.
  156. */
  157. long sequenceNumber = 0;
  158. // TRACES & DEBUG
  159. //---------------
  160. static boolean isTraceOn() {
  161. return Trace.isSelected(Trace.LEVEL_TRACE, Trace.INFO_MONITOR);
  162. }
  163. static void trace(String clz, String func, String info) {
  164. Trace.send(Trace.LEVEL_TRACE, Trace.INFO_MONITOR, clz, func, info);
  165. }
  166. void trace(String func, String info) {
  167. trace(dbgTag, func, info);
  168. }
  169. static boolean isDebugOn() {
  170. return Trace.isSelected(Trace.LEVEL_DEBUG, Trace.INFO_MONITOR);
  171. }
  172. static void debug(String clz, String func, String info) {
  173. Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_MONITOR, clz, func, info);
  174. }
  175. void debug(String func, String info) {
  176. debug(dbgTag, func, info);
  177. }
  178. /*
  179. * ------------------------------------------
  180. * PUBLIC METHODS
  181. * ------------------------------------------
  182. */
  183. /**
  184. * Allows the monitor MBean to perform any operations it needs
  185. * before being registered in the MBean server.
  186. * <P>
  187. * Initializes the reference to the MBean server.
  188. *
  189. * @param server The MBean server in which the monitor MBean will
  190. * be registered.
  191. * @param name The object name of the monitor MBean.
  192. *
  193. * @return The name of the monitor MBean registered.
  194. *
  195. * @exception java.lang.Exception
  196. */
  197. public ObjectName preRegister(MBeanServer server, ObjectName name)
  198. throws java.lang.Exception {
  199. if (isTraceOn()) {
  200. trace("preRegister",
  201. "initialize the reference on the MBean server");
  202. }
  203. this.server = server;
  204. return name;
  205. }
  206. /**
  207. * Allows the monitor MBean to perform any operations needed after
  208. * having been registered in the MBean server or after the
  209. * registration has failed.
  210. * <P>
  211. * Not used in this context.
  212. */
  213. public void postRegister (Boolean registrationDone) {
  214. }
  215. /**
  216. * Allows the monitor MBean to perform any operations it needs
  217. * before being unregistered by the MBean server.
  218. * <P>
  219. * Stops the monitor.
  220. *
  221. * @exception java.lang.Exception
  222. */
  223. public void preDeregister() throws java.lang.Exception {
  224. if (isTraceOn()) {
  225. trace("preDeregister", "stop the monitor");
  226. }
  227. // Stop the Monitor.
  228. //
  229. stop();
  230. }
  231. /**
  232. * Allows the monitor MBean to perform any operations needed after
  233. * having been unregistered by the MBean server.
  234. * <P>
  235. * Not used in this context.
  236. */
  237. public void postDeregister() {
  238. }
  239. /**
  240. * Starts the monitor.
  241. */
  242. public abstract void start();
  243. /**
  244. * Stops the monitor.
  245. */
  246. public abstract void stop();
  247. // GETTERS AND SETTERS
  248. //--------------------
  249. /**
  250. * Returns the object name of the first object in the set of observed
  251. * MBeans, or <code>null</code> if there is no such object.
  252. *
  253. * @return The object being observed.
  254. *
  255. * @see #setObservedObject(ObjectName)
  256. *
  257. * @deprecated As of JMX 1.2, replaced by {@link #getObservedObjects}
  258. */
  259. @Deprecated
  260. public ObjectName getObservedObject() {
  261. synchronized(this) {
  262. if (observedObjects.isEmpty()) {
  263. return null;
  264. } else {
  265. return (ObjectName)observedObjects.get(0);
  266. }
  267. }
  268. }
  269. /**
  270. * Removes all objects from the set of observed objects, and then adds the
  271. * specified object.
  272. *
  273. * @param object The object to observe.
  274. * @exception java.lang.IllegalArgumentException The specified
  275. * object is null.
  276. *
  277. * @see #getObservedObject()
  278. *
  279. * @deprecated As of JMX 1.2, replaced by {@link #addObservedObject}
  280. */
  281. @Deprecated
  282. public synchronized void setObservedObject(ObjectName object)
  283. throws IllegalArgumentException {
  284. while (!observedObjects.isEmpty()) {
  285. removeObservedObject((ObjectName)observedObjects.get(0));
  286. }
  287. addObservedObject(object);
  288. }
  289. /**
  290. * Adds the specified object in the set of observed MBeans, if this object
  291. * is not already present.
  292. *
  293. * @param object The object to observe.
  294. * @exception IllegalArgumentException The specified object is null.
  295. *
  296. * @since.unbundled JMX 1.2
  297. */
  298. public synchronized void addObservedObject(ObjectName object)
  299. throws IllegalArgumentException {
  300. if (object == null) {
  301. throw new IllegalArgumentException("Null observed object");
  302. }
  303. // Check that the specified object is not already contained
  304. //
  305. if (observedObjects.contains(object)) {
  306. return;
  307. }
  308. // Add the specified object in the list.
  309. //
  310. observedObjects.add(object);
  311. // Update alreadyNotified array.
  312. //
  313. int value = RESET_FLAGS_ALREADY_NOTIFIED;
  314. value &= ~(OBSERVED_OBJECT_ERROR_NOTIFIED |
  315. OBSERVED_ATTRIBUTE_ERROR_NOTIFIED |
  316. OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED);
  317. if (alreadyNotifieds.length >= elementCount)
  318. alreadyNotifieds = expandArray(alreadyNotifieds);
  319. alreadyNotifieds[elementCount] = value;
  320. updateDeprecatedAlreadyNotified();
  321. // Update other specific arrays.
  322. //
  323. insertSpecificElementAt(elementCount);
  324. // Update elementCount.
  325. //
  326. elementCount++;
  327. }
  328. /**
  329. * Removes the specified object from the set of observed MBeans.
  330. *
  331. * @param object The object to remove.
  332. *
  333. * @since.unbundled JMX 1.2
  334. */
  335. public void removeObservedObject(ObjectName object) {
  336. synchronized(this) {
  337. int index = observedObjects.indexOf(object);
  338. if (index >= 0) {
  339. observedObjects.remove(index);
  340. // Update alreadyNotifieds array.
  341. //
  342. removeElementAt(alreadyNotifieds, index);
  343. updateDeprecatedAlreadyNotified();
  344. // Update other specific arrays.
  345. //
  346. removeSpecificElementAt(index);
  347. // Update elementCount.
  348. //
  349. elementCount--;
  350. }
  351. }
  352. }
  353. /**
  354. * Tests whether the specified object is in the set of observed MBeans.
  355. *
  356. * @param object The object to check.
  357. * @return <CODE>true</CODE> if the specified object is present,
  358. * <CODE>false</CODE> otherwise.
  359. *
  360. * @since.unbundled JMX 1.2
  361. */
  362. public boolean containsObservedObject(ObjectName object) {
  363. synchronized(this) {
  364. return observedObjects.contains(object);
  365. }
  366. }
  367. /**
  368. * Returns an array containing the objects being observed.
  369. *
  370. * @return The objects being observed.
  371. *
  372. * @since.unbundled JMX 1.2
  373. */
  374. public ObjectName[] getObservedObjects() {
  375. ObjectName[] objects;
  376. synchronized(this) {
  377. objects = new ObjectName[elementCount];
  378. for (int i=0; i<elementCount; i++) {
  379. objects[i] = (ObjectName)observedObjects.get(i);
  380. }
  381. }
  382. return objects;
  383. }
  384. /**
  385. * Gets the attribute being observed.
  386. * <BR>The observed attribute is not initialized by default (set to null).
  387. *
  388. * @return The attribute being observed.
  389. *
  390. * @see #setObservedAttribute
  391. */
  392. public String getObservedAttribute() {
  393. return observedAttribute;
  394. }
  395. /**
  396. * Sets the attribute to observe.
  397. * <BR>The observed attribute is not initialized by default (set to null).
  398. *
  399. * @param attribute The attribute to observe.
  400. * @exception java.lang.IllegalArgumentException The specified
  401. * attribute is null.
  402. *
  403. * @see #getObservedAttribute
  404. */
  405. public void setObservedAttribute(String attribute)
  406. throws IllegalArgumentException {
  407. if (attribute == null) {
  408. throw new IllegalArgumentException("Null observed attribute");
  409. }
  410. // Update alreadyNotified array.
  411. //
  412. synchronized(this) {
  413. observedAttribute = attribute;
  414. for (int i = 0; i < elementCount; i++) {
  415. resetAlreadyNotified(i,
  416. OBSERVED_ATTRIBUTE_ERROR_NOTIFIED |
  417. OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED);
  418. }
  419. }
  420. }
  421. /**
  422. * Gets the granularity period (in milliseconds).
  423. * <BR>The default value of the granularity period is 10 seconds.
  424. *
  425. * @return The granularity period value.
  426. *
  427. * @see #setGranularityPeriod
  428. */
  429. public synchronized long getGranularityPeriod() {
  430. return granularityPeriod;
  431. }
  432. /**
  433. * Sets the granularity period (in milliseconds).
  434. * <BR>The default value of the granularity period is 10 seconds.
  435. *
  436. * @param period The granularity period value.
  437. * @exception java.lang.IllegalArgumentException The granularity
  438. * period is less than or equal to zero.
  439. *
  440. * @see #getGranularityPeriod
  441. */
  442. public synchronized void setGranularityPeriod(long period)
  443. throws IllegalArgumentException {
  444. if (period <= 0) {
  445. throw new IllegalArgumentException("Nonpositive granularity " +
  446. "period");
  447. }
  448. granularityPeriod = period;
  449. }
  450. /**
  451. * Tests whether the monitor MBean is active. A monitor MBean is
  452. * marked active when the {@link #start start} method is called.
  453. * It becomes inactive when the {@link #stop stop} method is
  454. * called.
  455. *
  456. * @return <CODE>true</CODE> if the monitor MBean is active,
  457. * <CODE>false</CODE> otherwise.
  458. */
  459. /* This method must be synchronized so that the monitoring thread will
  460. correctly see modifications to the isActive variable. See the various
  461. AlarmClock threads in the subclasses. */
  462. public synchronized boolean isActive() {
  463. return isActive;
  464. }
  465. /*
  466. * ------------------------------------------
  467. * PACKAGE METHODS
  468. * ------------------------------------------
  469. */
  470. /**
  471. * Gets the {@link ObjectName} of the object at the specified
  472. * index in the list of observed MBeans.
  473. * @return The observed object at the specified index.
  474. * @exception ArrayIndexOutOfBoundsException If the index is invalid.
  475. */
  476. synchronized ObjectName getObservedObject(int index)
  477. throws ArrayIndexOutOfBoundsException {
  478. return (ObjectName)observedObjects.get(index);
  479. }
  480. /**
  481. * Update the deprecated {@link #alreadyNotified} field.
  482. */
  483. synchronized void updateDeprecatedAlreadyNotified() {
  484. if (elementCount > 0)
  485. alreadyNotified = alreadyNotifieds[0];
  486. else
  487. alreadyNotified = 0;
  488. }
  489. /**
  490. * Set the given bits in the given element of {@link #alreadyNotifieds}.
  491. * Ensure the deprecated {@link #alreadyNotified} field is updated
  492. * if appropriate.
  493. */
  494. synchronized void setAlreadyNotified(int index, int mask) {
  495. alreadyNotifieds[index] |= mask;
  496. if (index == 0)
  497. updateDeprecatedAlreadyNotified();
  498. }
  499. /**
  500. * Reset the given bits in the given element of {@link #alreadyNotifieds}.
  501. * Ensure the deprecated {@link #alreadyNotified} field is updated
  502. * if appropriate.
  503. */
  504. synchronized void resetAlreadyNotified(int index, int mask) {
  505. alreadyNotifieds[index] &= ~mask;
  506. if (index == 0)
  507. updateDeprecatedAlreadyNotified();
  508. }
  509. synchronized boolean alreadyNotified(int index, int mask) {
  510. return ((alreadyNotifieds[index] & mask) != 0);
  511. }
  512. /**
  513. * Reset all bits in the given element of {@link #alreadyNotifieds}.
  514. * Ensure the deprecated {@link #alreadyNotified} field is updated
  515. * if appropriate.
  516. */
  517. synchronized void resetAllAlreadyNotified(int index) {
  518. alreadyNotifieds[index] = 0;
  519. if (index == 0)
  520. updateDeprecatedAlreadyNotified();
  521. }
  522. int[] expandArray(int[] array) {
  523. int[] newArray = new int[array.length + capacityIncrement];
  524. System.arraycopy(array, 0, newArray, 0, array.length);
  525. return newArray;
  526. }
  527. long[] expandArray(long[] array) {
  528. long[] newArray = new long[array.length + capacityIncrement];
  529. System.arraycopy(array, 0, newArray, 0, array.length);
  530. return newArray;
  531. }
  532. Number[] expandArray(Number[] array) {
  533. Number[] newArray = new Number[array.length + capacityIncrement];
  534. System.arraycopy(array, 0, newArray, 0, array.length);
  535. return newArray;
  536. }
  537. boolean[] expandArray(boolean[] array) {
  538. boolean[] newArray = new boolean[array.length + capacityIncrement];
  539. System.arraycopy(array, 0, newArray, 0, array.length);
  540. return newArray;
  541. }
  542. String[] expandArray(String[] array) {
  543. String[] newArray = new String[array.length + capacityIncrement];
  544. System.arraycopy(array, 0, newArray, 0, array.length);
  545. return newArray;
  546. }
  547. /**
  548. * Removes the component at the specified index from the specified
  549. * int array.
  550. */
  551. synchronized void removeElementAt(int[] array, int index) {
  552. if (index < 0 || index >= elementCount)
  553. return;
  554. int j = elementCount - index - 1;
  555. if (j > 0) {
  556. System.arraycopy(array, index + 1, array, index, j);
  557. }
  558. }
  559. /**
  560. * Removes the component at the specified index from the specified
  561. * long array.
  562. */
  563. synchronized void removeElementAt(long[] array, int index) {
  564. if (index < 0 || index >= elementCount)
  565. return;
  566. int j = elementCount - index - 1;
  567. if (j > 0) {
  568. System.arraycopy(array, index + 1, array, index, j);
  569. }
  570. }
  571. /**
  572. * Removes the component at the specified index from the specified
  573. * boolean array.
  574. */
  575. synchronized void removeElementAt(boolean[] array, int index) {
  576. if (index < 0 || index >= elementCount)
  577. return;
  578. int j = elementCount - index - 1;
  579. if (j > 0) {
  580. System.arraycopy(array, index + 1, array, index, j);
  581. }
  582. }
  583. /**
  584. * Removes the component at the specified index from the specified
  585. * Number array.
  586. */
  587. synchronized void removeElementAt(Object[] array, int index) {
  588. if (index < 0 || index >= elementCount)
  589. return;
  590. int j = elementCount - index - 1;
  591. if (j > 0) {
  592. System.arraycopy(array, index + 1, array, index, j);
  593. }
  594. array[elementCount - 1] = null;
  595. }
  596. /**
  597. * Searches for the first occurence of the given argument, testing
  598. * for equality using the equals method.
  599. */
  600. synchronized int indexOf(ObjectName object) {
  601. return observedObjects.indexOf(object);
  602. }
  603. /**
  604. * This method is overridden by the specific monitor classes
  605. * (Counter, Gauge and String). It updates all the specific
  606. * arrays after adding a new observed object in the list.
  607. *
  608. * The method is not abstract so that this class can be subclassed
  609. * by classes outside this package.
  610. */
  611. void insertSpecificElementAt(int index) {}
  612. /**
  613. * This method is overridden by the specific monitor classes
  614. * (Counter, Gauge and String). It updates all the specific
  615. * arrays after removing an observed object from the vector.
  616. *
  617. * The method is not abstract so that this class can be subclassed
  618. * by classes outside this package.
  619. */
  620. void removeSpecificElementAt(int index) {}
  621. /**
  622. * This method is used by the monitor MBean create and send a
  623. * monitor notification to all the listeners registered for this
  624. * kind of notification.
  625. *
  626. * @param type The notification type.
  627. * @param timeStamp The notification emission date.
  628. * @param msg The notification message.
  629. * @param derGauge The derived gauge.
  630. * @param trigger The threshold/string (depending on the monitor
  631. * type) that triggered off the notification.
  632. * @param index The index of the observed object that triggered
  633. * off the notification.
  634. */
  635. void sendNotification(String type, long timeStamp, String msg,
  636. Object derGauge, Object trigger, int index) {
  637. if (isTraceOn()) {
  638. trace("sendNotification", "send notification:" +
  639. "\n\tNotification observed object = " +
  640. getObservedObject(index) +
  641. "\n\tNotification observed attribute = " +
  642. observedAttribute +
  643. "\n\tNotification derived gauge = " +
  644. derGauge);
  645. }
  646. long seqno;
  647. synchronized (this) {
  648. seqno = sequenceNumber++;
  649. }
  650. sendNotification(new MonitorNotification(type,
  651. this,
  652. seqno,
  653. timeStamp,
  654. msg,
  655. getObservedObject(index),
  656. observedAttribute,
  657. derGauge,
  658. trigger));
  659. }
  660. }