1. /*
  2. * @(#)StringMonitor.java 4.41 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.Date;
  11. import java.util.Timer;
  12. import java.util.TimerTask;
  13. // jmx imports
  14. //
  15. import javax.management.ObjectName;
  16. import javax.management.MBeanNotificationInfo;
  17. import javax.management.AttributeNotFoundException;
  18. import javax.management.InstanceNotFoundException;
  19. import javax.management.MBeanException;
  20. import javax.management.ReflectionException;
  21. /**
  22. * Defines a monitor MBean designed to observe the values of a string
  23. * attribute.
  24. * <P>
  25. * A string monitor sends notifications as follows:
  26. * <UL>
  27. * <LI> if the attribute value matches the string to compare value,
  28. * a {@link MonitorNotification#STRING_TO_COMPARE_VALUE_MATCHED
  29. * match notification} is sent.
  30. * The notify match flag must be set to <CODE>true</CODE>.
  31. * <BR>Subsequent matchings of the string to compare values do not
  32. * cause further notifications unless
  33. * the attribute value differs from the string to compare value.
  34. * <LI> if the attribute value differs from the string to compare value,
  35. * a {@link MonitorNotification#STRING_TO_COMPARE_VALUE_DIFFERED
  36. * differ notification} is sent.
  37. * The notify differ flag must be set to <CODE>true</CODE>.
  38. * <BR>Subsequent differences from the string to compare value do
  39. * not cause further notifications unless
  40. * the attribute value matches the string to compare value.
  41. * </UL>
  42. *
  43. * @version 4.41 05/18/04
  44. * @author Sun Microsystems, Inc
  45. *
  46. * @since 1.5
  47. */
  48. public class StringMonitor extends Monitor implements StringMonitorMBean {
  49. // TRACES & DEBUG
  50. //---------------
  51. String makeDebugTag() {
  52. return "StringMonitor";
  53. }
  54. /*
  55. * ------------------------------------------
  56. * PRIVATE VARIABLES
  57. * ------------------------------------------
  58. */
  59. private final static String[] types = {
  60. MonitorNotification.RUNTIME_ERROR,
  61. MonitorNotification.OBSERVED_OBJECT_ERROR,
  62. MonitorNotification.OBSERVED_ATTRIBUTE_ERROR,
  63. MonitorNotification.OBSERVED_ATTRIBUTE_TYPE_ERROR,
  64. MonitorNotification.STRING_TO_COMPARE_VALUE_MATCHED,
  65. MonitorNotification.STRING_TO_COMPARE_VALUE_DIFFERED};
  66. private final static MBeanNotificationInfo[] notifsInfo = {
  67. new MBeanNotificationInfo(types,
  68. "javax.management.monitor.MonitorNotification",
  69. "Notifications sent by the StringMonitor MBean")};
  70. /**
  71. * String to compare with the observed attribute.
  72. * <BR>The default value is an empty character sequence.
  73. */
  74. private String stringToCompare = "";
  75. /**
  76. * Flag indicating if the string monitor notifies when matching
  77. * the string to compare.
  78. * <BR>The default value is set to <CODE>false</CODE>.
  79. */
  80. private boolean notifyMatch = false;
  81. /**
  82. * Flag indicating if the string monitor notifies when differing
  83. * from the string to compare.
  84. * <BR>The default value is set to <CODE>false</CODE>.
  85. */
  86. private boolean notifyDiffer = false;
  87. /**
  88. * Derived gauges.
  89. * <BR>Each element in this array corresponds to an observed object
  90. * in the list.
  91. */
  92. private String derivedGauge[] = new String[capacityIncrement];
  93. /**
  94. * Derived gauge timestamps.
  95. * <BR>Each element in this array corresponds to an observed object
  96. * in the list.
  97. */
  98. private long derivedGaugeTimestamp[] = new long[capacityIncrement];
  99. /**
  100. * This attribute is used to handle the matching/differing mechanism.
  101. * <BR>Each element in this array corresponds to an observed object
  102. * in the list.
  103. */
  104. private int status[] = new int[capacityIncrement];
  105. // Flags needed to implement the matching/differing mechanism.
  106. //
  107. private static final int MATCHING = 0;
  108. private static final int DIFFERING = 1;
  109. private static final int MATCHING_OR_DIFFERING = 2;
  110. /**
  111. * Timer.
  112. */
  113. private transient Timer timer = null;
  114. /*
  115. * ------------------------------------------
  116. * CONSTRUCTORS
  117. * ------------------------------------------
  118. */
  119. /**
  120. * Default constructor.
  121. */
  122. public StringMonitor() {
  123. dbgTag = makeDebugTag();
  124. }
  125. /*
  126. * ------------------------------------------
  127. * PUBLIC METHODS
  128. * ------------------------------------------
  129. */
  130. /**
  131. * Starts the string monitor.
  132. */
  133. public synchronized void start() {
  134. if (isTraceOn()) {
  135. trace("start", "start the string monitor");
  136. }
  137. if (isActive) {
  138. if (isTraceOn()) {
  139. trace("start", "the string monitor is already activated");
  140. }
  141. return;
  142. }
  143. isActive = true;
  144. // Reset values.
  145. //
  146. for (int i = 0; i < elementCount; i++) {
  147. status[i] = MATCHING_OR_DIFFERING;
  148. }
  149. // Start the AlarmClock.
  150. //
  151. timer = new Timer();
  152. timer.schedule(new StringAlarmClock(this),
  153. getGranularityPeriod(), getGranularityPeriod());
  154. }
  155. /**
  156. * Stops the string monitor.
  157. */
  158. /* This method is not synchronized, because if it were there could
  159. be a deadlock with a thread that attempted to get the lock on
  160. the monitor before being interrupted or noticing that it had
  161. been interrupted. */
  162. public void stop() {
  163. if (isTraceOn()) {
  164. trace("stop", "stop the string monitor");
  165. }
  166. synchronized(this) {
  167. if (!isActive) {
  168. if (isTraceOn()) {
  169. trace("stop", "the string monitor is already deactivated");
  170. }
  171. return;
  172. }
  173. isActive = false;
  174. // Stop the AlarmClock.
  175. //
  176. if (timer != null) {
  177. timer.cancel();
  178. timer = null;
  179. }
  180. }
  181. }
  182. // GETTERS AND SETTERS
  183. //--------------------
  184. /**
  185. * Sets the granularity period (in milliseconds).
  186. * <BR>The default value of the granularity period is 10 seconds.
  187. *
  188. * @param period The granularity period value.
  189. * @exception java.lang.IllegalArgumentException The granularity
  190. * period is less than or equal to zero.
  191. *
  192. * @see Monitor#setGranularityPeriod(long)
  193. */
  194. public synchronized void setGranularityPeriod(long period)
  195. throws IllegalArgumentException {
  196. super.setGranularityPeriod(period);
  197. // Reschedule timer task if timer is already running
  198. //
  199. if (isActive) {
  200. timer.cancel();
  201. timer = new Timer();
  202. timer.schedule(new StringAlarmClock(this),
  203. getGranularityPeriod(), getGranularityPeriod());
  204. }
  205. }
  206. /**
  207. * Gets the derived gauge of the specified object, if this object is
  208. * contained in the set of observed MBeans, or <code>null</code> otherwise.
  209. *
  210. * @param object the name of the MBean whose derived gauge is required.
  211. *
  212. * @return The derived gauge of the specified object.
  213. *
  214. * @since.unbundled JMX 1.2
  215. */
  216. public synchronized String getDerivedGauge(ObjectName object) {
  217. int index = indexOf(object);
  218. if (index != -1)
  219. return derivedGauge[index];
  220. else
  221. return null;
  222. }
  223. /**
  224. * Gets the derived gauge timestamp of the specified object, if
  225. * this object is contained in the set of observed MBeans, or
  226. * <code>null</code> otherwise.
  227. *
  228. * @param object the name of the MBean whose derived gauge
  229. * timestamp is required.
  230. *
  231. * @return The derived gauge timestamp of the specified object.
  232. *
  233. * @since.unbundled JMX 1.2
  234. */
  235. public synchronized long getDerivedGaugeTimeStamp(ObjectName object) {
  236. int index = indexOf(object);
  237. if (index != -1)
  238. return derivedGaugeTimestamp[index];
  239. else
  240. return 0;
  241. }
  242. /**
  243. * Returns the derived gauge of the first object in the set of
  244. * observed MBeans.
  245. *
  246. * @return The derived gauge.
  247. * @deprecated As of JMX 1.2, replaced by {@link #getDerivedGauge(ObjectName)}
  248. */
  249. public synchronized String getDerivedGauge() {
  250. return derivedGauge[0];
  251. }
  252. /**
  253. * Gets the derived gauge timestamp of the first object in the set
  254. * of observed MBeans.
  255. *
  256. * @return The derived gauge timestamp.
  257. * @deprecated As of JMX 1.2, replaced by
  258. * {@link #getDerivedGaugeTimeStamp(ObjectName)}
  259. */
  260. @Deprecated
  261. public synchronized long getDerivedGaugeTimeStamp() {
  262. return derivedGaugeTimestamp[0];
  263. }
  264. /**
  265. * Gets the string to compare with the observed attribute common
  266. * to all observed MBeans.
  267. *
  268. * @return The string value.
  269. *
  270. * @see #setStringToCompare
  271. */
  272. public synchronized String getStringToCompare() {
  273. return stringToCompare;
  274. }
  275. /**
  276. * Sets the string to compare with the observed attribute common
  277. * to all observed MBeans.
  278. *
  279. * @param value The string value.
  280. * @exception java.lang.IllegalArgumentException The specified
  281. * string to compare is null.
  282. *
  283. * @see #getStringToCompare
  284. */
  285. public synchronized void setStringToCompare(String value)
  286. throws java.lang.IllegalArgumentException {
  287. if (value == null) {
  288. throw new IllegalArgumentException("Null string to compare");
  289. }
  290. stringToCompare = value;
  291. // Reset values.
  292. //
  293. for (int i = 0; i < elementCount; i++) {
  294. status[i] = MATCHING_OR_DIFFERING;
  295. }
  296. }
  297. /**
  298. * Gets the matching notification's on/off switch value common to
  299. * all observed MBeans.
  300. *
  301. * @return <CODE>true</CODE> if the string monitor notifies when
  302. * matching the string to compare, <CODE>false</CODE> otherwise.
  303. *
  304. * @see #setNotifyMatch
  305. */
  306. public synchronized boolean getNotifyMatch() {
  307. return notifyMatch;
  308. }
  309. /**
  310. * Sets the matching notification's on/off switch value common to
  311. * all observed MBeans.
  312. *
  313. * @param value The matching notification's on/off switch value.
  314. *
  315. * @see #getNotifyMatch
  316. */
  317. public synchronized void setNotifyMatch(boolean value) {
  318. notifyMatch = value;
  319. }
  320. /**
  321. * Gets the differing notification's on/off switch value common to
  322. * all observed MBeans.
  323. *
  324. * @return <CODE>true</CODE> if the string monitor notifies when
  325. * differing from the string to compare, <CODE>false</CODE> otherwise.
  326. *
  327. * @see #setNotifyDiffer
  328. */
  329. public synchronized boolean getNotifyDiffer() {
  330. return notifyDiffer;
  331. }
  332. /**
  333. * Sets the differing notification's on/off switch value common to
  334. * all observed MBeans.
  335. *
  336. * @param value The differing notification's on/off switch value.
  337. *
  338. * @see #getNotifyDiffer
  339. */
  340. public synchronized void setNotifyDiffer(boolean value) {
  341. notifyDiffer = value;
  342. }
  343. /**
  344. * Returns a <CODE>NotificationInfo</CODE> object containing the
  345. * name of the Java class of the notification
  346. * and the notification types sent by the string monitor.
  347. */
  348. public MBeanNotificationInfo[] getNotificationInfo() {
  349. return notifsInfo;
  350. }
  351. /*
  352. * ------------------------------------------
  353. * PRIVATE METHODS
  354. * ------------------------------------------
  355. */
  356. /**
  357. * Updates the derived gauge and the derived gauge timestamp attributes
  358. * of the observed object at the specified index.
  359. *
  360. * @param scanString The value of the observed attribute.
  361. * @param index The index of the observed object.
  362. */
  363. private synchronized void updateDerivedGauge(Object scanString,
  364. int index) {
  365. derivedGaugeTimestamp[index] = System.currentTimeMillis();
  366. derivedGauge[index] = (String)scanString;
  367. }
  368. /**
  369. * Updates the notification attributes of the observed object at the
  370. * specified index and notifies the listeners only once if the
  371. * notifyMatch/notifyDiffer flag is set to <CODE>true</CODE>.
  372. * @param index The index of the observed object.
  373. */
  374. private void updateNotifications(int index) {
  375. boolean sendNotify = false;
  376. String type = null;
  377. long timeStamp = 0;
  378. String msg = null;
  379. Object derGauge = null;
  380. Object trigger = null;
  381. synchronized(this) {
  382. // Send matching notification if notifyMatch is true.
  383. // Send differing notification if notifyDiffer is true.
  384. //
  385. if (status[index] == MATCHING_OR_DIFFERING) {
  386. if (derivedGauge[index].equals(stringToCompare)) {
  387. if (notifyMatch) {
  388. sendNotify = true;
  389. type =
  390. MonitorNotification.STRING_TO_COMPARE_VALUE_MATCHED;
  391. timeStamp = derivedGaugeTimestamp[index];
  392. msg = "";
  393. derGauge = derivedGauge[index];
  394. trigger = stringToCompare;
  395. }
  396. status[index] = DIFFERING;
  397. } else {
  398. if (notifyDiffer) {
  399. sendNotify = true;
  400. type =
  401. MonitorNotification.STRING_TO_COMPARE_VALUE_DIFFERED;
  402. timeStamp = derivedGaugeTimestamp[index];
  403. msg = "";
  404. derGauge = derivedGauge[index];
  405. trigger = stringToCompare;
  406. }
  407. status[index] = MATCHING;
  408. }
  409. } else {
  410. if (status[index] == MATCHING) {
  411. if (derivedGauge[index].equals(stringToCompare)) {
  412. if (notifyMatch) {
  413. sendNotify = true;
  414. type =
  415. MonitorNotification.STRING_TO_COMPARE_VALUE_MATCHED;
  416. timeStamp = derivedGaugeTimestamp[index];
  417. msg = "";
  418. derGauge = derivedGauge[index];
  419. trigger = stringToCompare;
  420. }
  421. status[index] = DIFFERING;
  422. }
  423. } else if (status[index] == DIFFERING) {
  424. if (!derivedGauge[index].equals(stringToCompare)) {
  425. if (notifyDiffer) {
  426. sendNotify = true;
  427. type =
  428. MonitorNotification.STRING_TO_COMPARE_VALUE_DIFFERED;
  429. timeStamp = derivedGaugeTimestamp[index];
  430. msg = "";
  431. derGauge = derivedGauge[index];
  432. trigger = stringToCompare;
  433. }
  434. status[index] = MATCHING;
  435. }
  436. }
  437. }
  438. }
  439. if (sendNotify) {
  440. sendNotification(type, timeStamp, msg, derGauge, trigger, index);
  441. }
  442. }
  443. /*
  444. * ------------------------------------------
  445. * PACKAGE METHODS
  446. * ------------------------------------------
  447. */
  448. /**
  449. * This method is called by the string monitor each time
  450. * the granularity period has been exceeded.
  451. * @param index The index of the observed object.
  452. */
  453. void notifyAlarmClock(int index) {
  454. boolean sendNotify = false;
  455. String type = null;
  456. long timeStamp = 0;
  457. String msg = null;
  458. Object derGauge = null;
  459. Object trigger = null;
  460. Object scan_string = null;
  461. String notif_type = null;
  462. synchronized(this) {
  463. if (!isActive)
  464. return;
  465. // Check if the observed object and observed attribute are valid.
  466. //
  467. // Check that neither the observed object nor the observed
  468. // attribute are null. If the observed object or observed
  469. // attribute is null, this means that the monitor started
  470. // before a complete initialization and nothing is done.
  471. //
  472. if ((getObservedObject(index) == null) ||
  473. (getObservedAttribute() == null))
  474. return;
  475. // Check that the observed object is registered in the
  476. // MBean server and that the observed attribute belongs to
  477. // the observed object.
  478. //
  479. try {
  480. scan_string = server.getAttribute(getObservedObject(index),
  481. getObservedAttribute());
  482. if (scan_string == null)
  483. return;
  484. } catch (NullPointerException np_ex) {
  485. if (alreadyNotified(index, RUNTIME_ERROR_NOTIFIED))
  486. return;
  487. else {
  488. notif_type = MonitorNotification.RUNTIME_ERROR;
  489. setAlreadyNotified(index, RUNTIME_ERROR_NOTIFIED);
  490. msg =
  491. "The string monitor must be registered in " +
  492. "the MBean server.";
  493. }
  494. } catch (InstanceNotFoundException inf_ex) {
  495. if (alreadyNotified(index, OBSERVED_OBJECT_ERROR_NOTIFIED))
  496. return;
  497. else {
  498. notif_type = MonitorNotification.OBSERVED_OBJECT_ERROR;
  499. setAlreadyNotified(index, OBSERVED_OBJECT_ERROR_NOTIFIED);
  500. msg =
  501. "The observed object must be registered in " +
  502. "the MBean server.";
  503. }
  504. } catch (AttributeNotFoundException anf_ex) {
  505. if (alreadyNotified(index, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED))
  506. return;
  507. else {
  508. notif_type = MonitorNotification.OBSERVED_ATTRIBUTE_ERROR;
  509. setAlreadyNotified(index,
  510. OBSERVED_ATTRIBUTE_ERROR_NOTIFIED);
  511. msg =
  512. "The observed attribute must be accessible in " +
  513. "the observed object.";
  514. }
  515. } catch (MBeanException mb_ex) {
  516. if (alreadyNotified(index, RUNTIME_ERROR_NOTIFIED))
  517. return;
  518. else {
  519. notif_type = MonitorNotification.RUNTIME_ERROR;
  520. setAlreadyNotified(index, RUNTIME_ERROR_NOTIFIED);
  521. msg = mb_ex.getMessage();
  522. }
  523. } catch (ReflectionException ref_ex) {
  524. if (alreadyNotified(index, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED))
  525. return;
  526. else {
  527. notif_type = MonitorNotification.OBSERVED_ATTRIBUTE_ERROR;
  528. setAlreadyNotified(index, OBSERVED_ATTRIBUTE_ERROR_NOTIFIED);
  529. msg = ref_ex.getMessage();
  530. }
  531. }
  532. if (msg == null) {
  533. // Check that the observed attribute is of type "String".
  534. //
  535. if (!(scan_string instanceof String)) {
  536. if (alreadyNotified(index,
  537. OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED))
  538. return;
  539. else {
  540. notif_type =
  541. MonitorNotification.OBSERVED_ATTRIBUTE_TYPE_ERROR;
  542. setAlreadyNotified(index,
  543. OBSERVED_ATTRIBUTE_TYPE_ERROR_NOTIFIED);
  544. msg =
  545. "The observed attribute type must be " +
  546. "a string type.";
  547. }
  548. }
  549. }
  550. if (msg == null) {
  551. // Clear all already notified flags.
  552. //
  553. resetAllAlreadyNotified(index);
  554. // Update the derived gauge attributes.
  555. //
  556. updateDerivedGauge(scan_string, index);
  557. // Notify the listeners.
  558. //
  559. updateNotifications(index);
  560. } else {
  561. // msg != null, will send the monitor error notification.
  562. timeStamp = derivedGaugeTimestamp[index];
  563. derGauge = derivedGauge[index];
  564. // Reset values.
  565. //
  566. status[index] = MATCHING_OR_DIFFERING;
  567. }
  568. }
  569. if (msg != null) {
  570. sendNotification(type, timeStamp, msg, derGauge, null, index);
  571. }
  572. }
  573. /**
  574. * This method is called when adding a new observed object in the vector.
  575. * It updates all the string specific arrays.
  576. * @param index The index of the observed object.
  577. */
  578. synchronized void insertSpecificElementAt(int index) {
  579. // Update derivedGauge, derivedGaugeTimestamp, and status arrays.
  580. //
  581. if (index != elementCount)
  582. throw new Error("Internal error: index != elementCount");
  583. if (elementCount >= derivedGauge.length) {
  584. derivedGauge = expandArray(derivedGauge);
  585. derivedGaugeTimestamp = expandArray(derivedGaugeTimestamp);
  586. status = expandArray(status);
  587. }
  588. derivedGauge[index] = "";
  589. derivedGaugeTimestamp[index] = System.currentTimeMillis();
  590. status[index] = MATCHING_OR_DIFFERING;
  591. }
  592. /**
  593. * This method is called when removing an observed object from the vector.
  594. * It updates all the string specific arrays.
  595. * @param index The index of the observed object.
  596. */
  597. synchronized void removeSpecificElementAt(int index) {
  598. if (index < 0 || index >= elementCount)
  599. return;
  600. // Update derivedGauge, derivedGaugeTimestamp, and status arrays.
  601. //
  602. removeElementAt(derivedGauge, index);
  603. removeElementAt(derivedGaugeTimestamp, index);
  604. removeElementAt(status, index);
  605. }
  606. /**
  607. * StringAlarmClock inner class:
  608. * This class provides a simple implementation of an alarm clock MBean.
  609. * The aim of this MBean is to set up an alarm which wakes up the
  610. * string monitor every granularity period.
  611. */
  612. private static class StringAlarmClock extends TimerTask {
  613. StringMonitor listener = null;
  614. /*
  615. * ------------------------------------------
  616. * CONSTRUCTORS
  617. * ------------------------------------------
  618. */
  619. public StringAlarmClock(StringMonitor listener) {
  620. this.listener = listener;
  621. }
  622. /*
  623. * ------------------------------------------
  624. * PUBLIC METHODS
  625. * ------------------------------------------
  626. */
  627. /**
  628. * This method is called by the StringAlarmClock thread when
  629. * it is started.
  630. */
  631. public void run() {
  632. if (listener.isActive) {
  633. for (int i = 0; i < listener.elementCount; i++) {
  634. listener.notifyAlarmClock(i);
  635. }
  636. }
  637. }
  638. }
  639. }