1. /*
  2. * @(#)ArrayNotificationBuffer.java 1.21 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.jmx.remote.internal;
  8. import java.io.IOException;
  9. import java.security.AccessController;
  10. import java.security.PrivilegedAction;
  11. import java.security.PrivilegedActionException;
  12. import java.security.PrivilegedExceptionAction;
  13. import java.util.ArrayList;
  14. import java.util.Collection;
  15. import java.util.HashSet;
  16. import java.util.Iterator;
  17. import java.util.List;
  18. import java.util.Set;
  19. import java.util.HashMap;
  20. import java.util.Map;
  21. import javax.management.InstanceNotFoundException;
  22. import javax.management.ListenerNotFoundException;
  23. import javax.management.MalformedObjectNameException;
  24. import javax.management.MBeanServer;
  25. import javax.management.MBeanServerNotification;
  26. import javax.management.Notification;
  27. import javax.management.NotificationBroadcaster;
  28. import javax.management.NotificationFilter;
  29. import javax.management.NotificationFilterSupport;
  30. import javax.management.NotificationListener;
  31. import javax.management.ObjectName;
  32. import javax.management.QueryEval;
  33. import javax.management.QueryExp;
  34. import javax.management.remote.NotificationResult;
  35. import javax.management.remote.TargetedNotification;
  36. import com.sun.jmx.remote.util.EnvHelp;
  37. import com.sun.jmx.remote.util.ClassLogger;
  38. /** A circular buffer of notifications received from an MBean server. */
  39. public class ArrayNotificationBuffer implements NotificationBuffer {
  40. public static final int DEFAULT_BUFFER_SIZE = 1000;
  41. public static final String BUFFER_SIZE_PROPERTY =
  42. "jmx.remote.x.buffer.size";
  43. private boolean disposed = false;
  44. // FACTORY STUFF, INCLUDING SHARING
  45. private static final
  46. HashMap/*<MBeanServer,ArrayNotificationBuffer>*/ mbsToBuffer =
  47. new HashMap(1);
  48. private final Collection/*<ShareBuffer>*/ sharers = new HashSet(1);
  49. public static synchronized NotificationBuffer
  50. getNotificationBuffer(MBeanServer mbs, Map env) {
  51. //Find out queue size
  52. int defaultQueueSize = DEFAULT_BUFFER_SIZE;
  53. try {
  54. String s = (String)
  55. AccessController.doPrivileged(new PrivilegedAction() {
  56. public Object run() {
  57. return System.getProperty(BUFFER_SIZE_PROPERTY);
  58. }
  59. });
  60. if (s != null)
  61. defaultQueueSize = Integer.parseInt(s);
  62. } catch (RuntimeException e) {
  63. logger.warning("ServerNotifForwarder",
  64. "Can't use System property "+
  65. BUFFER_SIZE_PROPERTY+ ": " + e);
  66. logger.debug("ServerNotifForwarder", e);
  67. }
  68. int queueSize = defaultQueueSize;
  69. try {
  70. queueSize = (int)
  71. EnvHelp.getIntegerAttribute(env,BUFFER_SIZE_PROPERTY,
  72. defaultQueueSize,0,
  73. Integer.MAX_VALUE);
  74. } catch (RuntimeException e) {
  75. logger.warning("ServerNotifForwarder",
  76. "Can't determine queuesize (using default): "+
  77. e);
  78. logger.debug("ServerNotifForwarder", e);
  79. }
  80. ArrayNotificationBuffer buf =
  81. (ArrayNotificationBuffer) mbsToBuffer.get(mbs);
  82. if (buf == null) {
  83. buf = new ArrayNotificationBuffer(mbs, queueSize);
  84. mbsToBuffer.put(mbs, buf);
  85. }
  86. return buf.new ShareBuffer(queueSize);
  87. }
  88. public static synchronized void removeNotificationBuffer(MBeanServer mbs){
  89. mbsToBuffer.remove(mbs);
  90. }
  91. synchronized void addSharer(ShareBuffer sharer) {
  92. if (sharer.getSize() > queueSize)
  93. resize(sharer.getSize());
  94. sharers.add(sharer);
  95. }
  96. synchronized void removeSharer(ShareBuffer sharer) {
  97. sharers.remove(sharer);
  98. if (sharers.isEmpty())
  99. dispose();
  100. else {
  101. int max = 0;
  102. for (Iterator it = sharers.iterator(); it.hasNext(); ) {
  103. ShareBuffer buf = (ShareBuffer) it.next();
  104. int bufsize = buf.getSize();
  105. if (bufsize > max)
  106. max = bufsize;
  107. }
  108. if (max < queueSize)
  109. resize(max);
  110. }
  111. }
  112. private void resize(int newSize) {
  113. if (newSize == queueSize)
  114. return;
  115. while (queue.size() > newSize)
  116. dropNotification();
  117. queue.resize(newSize);
  118. queueSize = newSize;
  119. }
  120. private class ShareBuffer implements NotificationBuffer {
  121. ShareBuffer(int size) {
  122. this.size = size;
  123. addSharer(this);
  124. }
  125. public NotificationResult
  126. fetchNotifications(Set/*<ListenerInfo>*/ listeners,
  127. long startSequenceNumber,
  128. long timeout,
  129. int maxNotifications)
  130. throws InterruptedException {
  131. NotificationBuffer buf = ArrayNotificationBuffer.this;
  132. return buf.fetchNotifications(listeners, startSequenceNumber,
  133. timeout, maxNotifications);
  134. }
  135. public void dispose() {
  136. ArrayNotificationBuffer.this.removeSharer(this);
  137. }
  138. int getSize() {
  139. return size;
  140. }
  141. private final int size;
  142. }
  143. // ARRAYNOTIFICATIONBUFFER IMPLEMENTATION
  144. private ArrayNotificationBuffer(MBeanServer mbs, int queueSize) {
  145. if (logger.traceOn())
  146. logger.trace("Constructor", "queueSize=" + queueSize);
  147. if (mbs == null || queueSize < 1)
  148. throw new IllegalArgumentException("Bad args");
  149. this.mBeanServer = mbs;
  150. this.queueSize = queueSize;
  151. this.queue = new ArrayQueue(queueSize);
  152. this.earliestSequenceNumber = System.currentTimeMillis();
  153. this.nextSequenceNumber = this.earliestSequenceNumber;
  154. createListeners();
  155. logger.trace("Constructor", "ends");
  156. }
  157. private synchronized boolean isDisposed() {
  158. return disposed;
  159. }
  160. public void dispose() {
  161. logger.trace("dispose", "starts");
  162. synchronized(this) {
  163. removeNotificationBuffer(mBeanServer);
  164. disposed = true;
  165. //Notify potential waiting fetchNotification call
  166. notifyAll();
  167. }
  168. destroyListeners();
  169. logger.trace("dispose", "ends");
  170. }
  171. /**
  172. * <p>Fetch notifications that match the given listeners.</p>
  173. *
  174. * <p>The operation only considers notifications with a sequence
  175. * number at least <code>startSequenceNumber</code>. It will take
  176. * no longer than <code>timeout</code>, and will return no more
  177. * than <code>maxNotifications</code> different notifications.</p>
  178. *
  179. * <p>If there are no notifications matching the criteria, the
  180. * operation will block until one arrives, subject to the
  181. * timeout.</p>
  182. *
  183. * @param listeners a Set of {@link ListenerInfo} that reflects
  184. * the filters to be applied to notifications. Accesses to this
  185. * Set are synchronized on the Set object. The Set is consulted
  186. * for selected notifications that are present when the method
  187. * starts, and for selected notifications that arrive while it is
  188. * executing. The contents of the Set can be modified, with
  189. * appropriate synchronization, while the method is running.
  190. * @param startSequenceNumber the first sequence number to
  191. * consider.
  192. * @param timeout the maximum time to wait. May be 0 to indicate
  193. * not to wait if there are no notifications.
  194. * @param maxNotifications the maximum number of notifications to
  195. * return. May be 0 to indicate a wait for eligible notifications
  196. * that will return a usable <code>nextSequenceNumber</code>. The
  197. * {@link TargetedNotification} array in the returned {@link
  198. * NotificationResult} may contain more than this number of
  199. * elements but will not contain more than this number of
  200. * different notifications.
  201. */
  202. public NotificationResult
  203. fetchNotifications(Set/*<ListenerInfo>*/ listeners,
  204. long startSequenceNumber,
  205. long timeout,
  206. int maxNotifications)
  207. throws InterruptedException {
  208. logger.trace("fetchNotifications", "starts");
  209. if (startSequenceNumber < 0 || isDisposed()) {
  210. synchronized(this) {
  211. return new NotificationResult(earliestSequenceNumber(),
  212. nextSequenceNumber(),
  213. new TargetedNotification[0]);
  214. }
  215. }
  216. // Check arg validity
  217. if (listeners == null
  218. || startSequenceNumber < 0 || timeout < 0
  219. || maxNotifications < 0) {
  220. logger.trace("fetchNotifications", "Bad args");
  221. throw new IllegalArgumentException("Bad args to fetch");
  222. }
  223. if (logger.debugOn()) {
  224. logger.trace("fetchNotifications",
  225. "listener-length=" + listeners.size() + "; startSeq=" +
  226. startSequenceNumber + "; timeout=" + timeout +
  227. "; max=" + maxNotifications);
  228. }
  229. if (startSequenceNumber > nextSequenceNumber()) {
  230. final String msg = "Start sequence number too big: " +
  231. startSequenceNumber + " > " + nextSequenceNumber();
  232. logger.trace("fetchNotifications", msg);
  233. throw new IllegalArgumentException(msg);
  234. }
  235. /* Determine the end time corresponding to the timeout value.
  236. Caller may legitimately supply Long.MAX_VALUE to indicate no
  237. timeout. In that case the addition will overflow and produce
  238. a negative end time. Set end time to Long.MAX_VALUE in that
  239. case. We assume System.currentTimeMillis() is positive. */
  240. long endTime = System.currentTimeMillis() + timeout;
  241. if (endTime < 0) // overflow
  242. endTime = Long.MAX_VALUE;
  243. if (logger.debugOn())
  244. logger.debug("fetchNotifications", "endTime=" + endTime);
  245. /* We set earliestSeq the first time through the loop. If we
  246. set it here, notifications could be dropped before we
  247. started examining them, so earliestSeq might not correspond
  248. to the earliest notification we examined. */
  249. long earliestSeq = -1;
  250. long nextSeq = startSequenceNumber;
  251. List/*<TargetedNotification>*/ notifs = new ArrayList();
  252. /* On exit from this loop, notifs, earliestSeq, and nextSeq must
  253. all be correct values for the returned NotificationResult. */
  254. while (true) {
  255. logger.debug("fetchNotifications", "main loop starts");
  256. NamedNotification candidate;
  257. /* Get the next available notification regardless of filters,
  258. or wait for one to arrive if there is none. */
  259. synchronized (this) {
  260. /* First time through. The current earliestSequenceNumber
  261. is the first one we could have examined. */
  262. if (earliestSeq < 0) {
  263. earliestSeq = earliestSequenceNumber();
  264. if (logger.debugOn()) {
  265. logger.debug("fetchNotifications",
  266. "earliestSeq=" + earliestSeq);
  267. }
  268. if (nextSeq < earliestSeq) {
  269. nextSeq = earliestSeq;
  270. logger.debug("fetchNotifications",
  271. "nextSeq=earliestSeq");
  272. }
  273. } else
  274. earliestSeq = earliestSequenceNumber();
  275. /* If many notifications have been dropped since the
  276. last time through, nextSeq could now be earlier
  277. than the current earliest. If so, notifications
  278. may have been lost and we return now so the caller
  279. can see this next time it calls. */
  280. if (nextSeq < earliestSeq) {
  281. logger.trace("fetchNotifications",
  282. "nextSeq=" + nextSeq + " < " + "earliestSeq=" +
  283. earliestSeq + " so may have lost notifs");
  284. break;
  285. }
  286. if (nextSeq < nextSequenceNumber()) {
  287. candidate = notificationAt(nextSeq);
  288. if (logger.debugOn()) {
  289. logger.debug("fetchNotifications", "candidate: " +
  290. candidate);
  291. logger.debug("fetchNotifications", "nextSeq now " +
  292. nextSeq);
  293. }
  294. } else {
  295. /* nextSeq is the largest sequence number. If we
  296. already got notifications, return them now.
  297. Otherwise wait for some to arrive, with
  298. timeout. */
  299. if (notifs.size() > 0) {
  300. logger.debug("fetchNotifications",
  301. "no more notifs but have some so don't wait");
  302. break;
  303. }
  304. long toWait = endTime - System.currentTimeMillis();
  305. if (toWait <= 0) {
  306. logger.debug("fetchNotifications", "timeout");
  307. break;
  308. }
  309. /* dispose called */
  310. if (isDisposed()) {
  311. if (logger.debugOn())
  312. logger.debug("fetchNotifications",
  313. "dispose callled, no wait");
  314. return new NotificationResult(earliestSequenceNumber(),
  315. nextSequenceNumber(),
  316. new TargetedNotification[0]);
  317. }
  318. if (logger.debugOn())
  319. logger.debug("fetchNotifications",
  320. "wait(" + toWait + ")");
  321. wait(toWait);
  322. continue;
  323. }
  324. }
  325. /* We have a candidate notification. See if it matches
  326. our filters. We do this outside the synchronized block
  327. so we don't hold up everyone accessing the buffer
  328. (including notification senders) while we evaluate
  329. potentially slow filters. */
  330. ObjectName name = candidate.getObjectName();
  331. Notification notif = candidate.getNotification();
  332. List/*<TargetedNotification>*/ matchedNotifs = new ArrayList();
  333. logger.debug("fetchNotifications",
  334. "applying filters to candidate");
  335. synchronized (listeners) {
  336. for (Iterator it = listeners.iterator(); it.hasNext(); ) {
  337. ListenerInfo li = (ListenerInfo) it.next();
  338. ObjectName pattern = li.getObjectName();
  339. NotificationFilter filter = li.getNotificationFilter();
  340. if (logger.debugOn()) {
  341. logger.debug("fetchNotifications",
  342. "pattern=<" + pattern + "> filter=" + filter);
  343. }
  344. if (pattern.apply(name)) {
  345. logger.debug("fetchNotifications", "pattern matches");
  346. if (filter == null
  347. || filter.isNotificationEnabled(notif)) {
  348. logger.debug("fetchNotifications",
  349. "filter matches");
  350. Integer listenerID = li.getListenerID();
  351. TargetedNotification tn =
  352. new TargetedNotification(notif, listenerID);
  353. matchedNotifs.add(tn);
  354. }
  355. }
  356. }
  357. }
  358. if (matchedNotifs.size() > 0) {
  359. /* We only check the max size now, so that our
  360. returned nextSeq is as large as possible. This
  361. prevents the caller from thinking it missed
  362. interesting notifications when in fact we knew they
  363. weren't. */
  364. if (maxNotifications <= 0) {
  365. logger.debug("fetchNotifications",
  366. "reached maxNotifications");
  367. break;
  368. }
  369. --maxNotifications;
  370. if (logger.debugOn())
  371. logger.debug("fetchNotifications", "add: " +
  372. matchedNotifs);
  373. notifs.addAll(matchedNotifs);
  374. }
  375. ++nextSeq;
  376. } // end while
  377. /* Construct and return the result. */
  378. int nnotifs = notifs.size();
  379. TargetedNotification[] resultNotifs =
  380. new TargetedNotification[nnotifs];
  381. notifs.toArray(resultNotifs);
  382. NotificationResult nr =
  383. new NotificationResult(earliestSeq, nextSeq, resultNotifs);
  384. if (logger.debugOn())
  385. logger.debug("fetchNotifications", nr.toString());
  386. logger.trace("fetchNotifications", "ends");
  387. return nr;
  388. }
  389. synchronized long earliestSequenceNumber() {
  390. return earliestSequenceNumber;
  391. }
  392. synchronized long nextSequenceNumber() {
  393. return nextSequenceNumber;
  394. }
  395. synchronized void addNotification(NamedNotification notif) {
  396. if (logger.traceOn())
  397. logger.trace("addNotification", notif.toString());
  398. while (queue.size() >= queueSize) {
  399. dropNotification();
  400. if (logger.debugOn()) {
  401. logger.debug("addNotification",
  402. "dropped oldest notif, earliestSeq=" +
  403. earliestSequenceNumber);
  404. }
  405. }
  406. queue.add(notif);
  407. nextSequenceNumber++;
  408. if (logger.debugOn())
  409. logger.debug("addNotification", "nextSeq=" + nextSequenceNumber);
  410. notifyAll();
  411. }
  412. private void dropNotification() {
  413. queue.remove(0);
  414. earliestSequenceNumber++;
  415. }
  416. synchronized NamedNotification notificationAt(long seqNo) {
  417. long index = seqNo - earliestSequenceNumber;
  418. if (index < 0 || index > Integer.MAX_VALUE) {
  419. final String msg = "Bad sequence number: " + seqNo + " (earliest "
  420. + earliestSequenceNumber + ")";
  421. logger.trace("notificationAt", msg);
  422. throw new IllegalArgumentException(msg);
  423. }
  424. return (NamedNotification) queue.get((int) index);
  425. }
  426. private static class NamedNotification {
  427. NamedNotification(ObjectName sender, Notification notif) {
  428. this.sender = sender;
  429. this.notification = notif;
  430. }
  431. ObjectName getObjectName() {
  432. return sender;
  433. }
  434. Notification getNotification() {
  435. return notification;
  436. }
  437. public String toString() {
  438. return "NamedNotification(" + sender + ", " + notification + ")";
  439. }
  440. private final ObjectName sender;
  441. private final Notification notification;
  442. }
  443. /*
  444. * Add our listener to every NotificationBroadcaster MBean
  445. * currently in the MBean server and to every
  446. * NotificationBroadcaster later created.
  447. *
  448. * It would be really nice if we could just do
  449. * mbs.addNotificationListener(new ObjectName("*:*"), ...);
  450. * Definitely something for the next version of JMX.
  451. *
  452. * There is a nasty race condition that we can't easily solve. We
  453. * first register for MBean-creation notifications so we can add
  454. * listeners to new MBeans, then we query the existing MBeans to
  455. * add listeners to them. The problem is that a new MBean could
  456. * arrive after we register for creations but before the query has
  457. * completed. Then we could see the MBean both in the query and
  458. * in an MBean-creation notification, and we would end up
  459. * registering our listener twice.
  460. *
  461. * To solve this problem, we have separate listener instances for
  462. * the MBeans found by the query and the MBeans found by the
  463. * creation notifications. When we get a creation notification,
  464. * we add the listener for that, then we remove the listener for
  465. * queries if it is there. This means that there's a very small
  466. * window of time when we could get the same notification from
  467. * both listeners. However, for that to happen we would have to
  468. * hit TWO unlikely race conditions: an MBean created during our
  469. * initial query, and a notification from that MBean emitted while
  470. * we were adding the listener from the creation notification.
  471. * And the behaviour if we do hit those two conditions is that one
  472. * or more notifications are duplicated during a very small
  473. * period.
  474. */
  475. private synchronized void createListeners() {
  476. logger.debug("createListeners", "starts");
  477. try {
  478. AccessController.doPrivileged(new PrivilegedExceptionAction() {
  479. public Object run() throws InstanceNotFoundException {
  480. mBeanServer.addNotificationListener(delegateName,
  481. creationListener,
  482. creationFilter,
  483. null);
  484. return null;
  485. }
  486. });
  487. logger.debug("createListeners", "added creationListener");
  488. } catch (Exception pe) {
  489. final Exception e = extractException(pe);
  490. final String msg = "Can't add listener to MBean server delegate: ";
  491. RuntimeException re = new IllegalArgumentException(msg + e);
  492. EnvHelp.initCause(re, e);
  493. logger.fine("createListeners", msg + e);
  494. logger.debug("createListeners", e);
  495. throw re;
  496. }
  497. Set names;
  498. try {
  499. names = (Set)
  500. AccessController.doPrivileged(new PrivilegedAction() {
  501. public Object run() {
  502. return mBeanServer.queryNames(null, broadcasterQuery);
  503. }
  504. });
  505. } catch (RuntimeException e) {
  506. logger.fine("createListeners", "Failed to query names: " + e);
  507. logger.debug("createListeners", e);
  508. throw e;
  509. }
  510. for (Iterator it = names.iterator(); it.hasNext(); ) {
  511. ObjectName name = (ObjectName) it.next();
  512. addBufferListener(name, queryBufferListener);
  513. }
  514. logger.debug("createListeners", "ends");
  515. }
  516. private void addBufferListener(final ObjectName name,
  517. final NotificationListener bufferListener) {
  518. if (logger.debugOn())
  519. logger.debug("addBufferListener", ""+name);
  520. try {
  521. AccessController.doPrivileged(new PrivilegedExceptionAction() {
  522. public Object run() throws InstanceNotFoundException {
  523. mBeanServer.addNotificationListener(name,
  524. bufferListener,
  525. null,
  526. name);
  527. return null;
  528. }
  529. });
  530. } catch (Exception e) {
  531. logger.trace("addBufferListener", extractException(e));
  532. /* This can happen if the MBean was unregistered just
  533. after the query. Or user NotificationBroadcaster might
  534. throw unexpected exception. */
  535. }
  536. }
  537. private synchronized void createdNotification(MBeanServerNotification n) {
  538. if (destroyed) {
  539. logger.trace("createNotification",
  540. "NotificationBuffer was destroyed");
  541. return;
  542. }
  543. final String shouldEqual =
  544. MBeanServerNotification.REGISTRATION_NOTIFICATION;
  545. if (!n.getType().equals(shouldEqual)) {
  546. logger.warning("createNotification", "bad type: " + n.getType());
  547. return;
  548. }
  549. final ObjectName name = n.getMBeanName();
  550. if (logger.debugOn())
  551. logger.debug("createdNotification", "for: " + name);
  552. try {
  553. Boolean instanceOf = (Boolean)
  554. AccessController.doPrivileged(new PrivilegedExceptionAction() {
  555. public Object run() throws InstanceNotFoundException {
  556. return new Boolean(
  557. mBeanServer.isInstanceOf(name,
  558. broadcasterClass));
  559. }
  560. });
  561. if (!instanceOf.booleanValue()) {
  562. logger.debug("createdNotification",
  563. "not a NotificationBroadcaster");
  564. return;
  565. }
  566. } catch (Exception e) {
  567. logger.trace("createdNotification", extractException(e));
  568. /* Could happen if the MBean was immediately unregistered. */
  569. return;
  570. }
  571. addBufferListener(name, creationBufferListener);
  572. try {
  573. AccessController.doPrivileged(new PrivilegedExceptionAction() {
  574. public Object run() throws
  575. InstanceNotFoundException, ListenerNotFoundException {
  576. mBeanServer.removeNotificationListener(
  577. name,
  578. queryBufferListener);
  579. return null;
  580. }
  581. });
  582. logger.trace("createdNotification",
  583. "remove queryBufferListener worked!");
  584. } catch (PrivilegedActionException pe) {
  585. final Exception e = extractException(pe);
  586. if (e instanceof ListenerNotFoundException) {
  587. logger.debug("createdNotification",
  588. "remove queryBufferListener got " +
  589. "ListenerNotFoundException as expected");
  590. // Expected: see comment before createListeners()
  591. } else {
  592. logger.trace("createdNotification", e);
  593. }
  594. }
  595. }
  596. private class BufferListener implements NotificationListener {
  597. public void handleNotification(Notification notif, Object handback) {
  598. if (logger.debugOn()) {
  599. logger.debug("BufferListener.handleNotification",
  600. "notif=" + notif + "; handback=" + handback);
  601. }
  602. ObjectName name = (ObjectName) handback;
  603. addNotification(new NamedNotification(name, notif));
  604. }
  605. }
  606. private final NotificationListener queryBufferListener =
  607. new BufferListener();
  608. private final NotificationListener creationBufferListener =
  609. new BufferListener();
  610. private static class BroadcasterQuery
  611. extends QueryEval implements QueryExp {
  612. public boolean apply(final ObjectName name) {
  613. final MBeanServer mbs = QueryEval.getMBeanServer();
  614. try {
  615. Boolean isBroadcaster = (Boolean)
  616. AccessController.doPrivileged(
  617. new PrivilegedExceptionAction() {
  618. public Object run()
  619. throws InstanceNotFoundException {
  620. return new Boolean(
  621. mbs.isInstanceOf(name,
  622. broadcasterClass));
  623. }
  624. });
  625. if (logger.debugOn())
  626. logger.debug("BroadcasterQuery", name + " -> " +
  627. isBroadcaster);
  628. return isBroadcaster.booleanValue();
  629. } catch (PrivilegedActionException pe) {
  630. logger.debug("BroadcasterQuery", extractException(pe));
  631. return false;
  632. }
  633. }
  634. }
  635. private static final QueryExp broadcasterQuery = new BroadcasterQuery();
  636. private static final NotificationFilter creationFilter;
  637. static {
  638. NotificationFilterSupport nfs = new NotificationFilterSupport();
  639. nfs.enableType(MBeanServerNotification.REGISTRATION_NOTIFICATION);
  640. creationFilter = nfs;
  641. }
  642. private final NotificationListener creationListener =
  643. new NotificationListener() {
  644. public void handleNotification(Notification notif,
  645. Object handback) {
  646. logger.debug("creationListener", "handleNotification called");
  647. createdNotification((MBeanServerNotification) notif);
  648. }
  649. };
  650. private synchronized void destroyListeners() {
  651. logger.debug("destroyListeners", "starts");
  652. destroyed = true;
  653. Set names = (Set)
  654. AccessController.doPrivileged(new PrivilegedAction() {
  655. public Object run() {
  656. return mBeanServer.queryNames(null, broadcasterQuery);
  657. }
  658. });
  659. for (Iterator it = names.iterator(); it.hasNext(); ) {
  660. final ObjectName name = (ObjectName) it.next();
  661. if (logger.debugOn())
  662. logger.debug("destroyListeners",
  663. "remove listener from " + name);
  664. // remove creationBufferListener or queryBufferListener
  665. for (int i = 0; i < 2; i++) {
  666. final boolean creation = (i == 0);
  667. final NotificationListener listener =
  668. creation ? creationBufferListener : queryBufferListener;
  669. final String what =
  670. (creation ?
  671. "creationBufferListener" :
  672. "queryBufferListener");
  673. try {
  674. AccessController.doPrivileged(
  675. new PrivilegedExceptionAction() {
  676. public Object run()
  677. throws
  678. InstanceNotFoundException,
  679. ListenerNotFoundException {
  680. mBeanServer.removeNotificationListener(
  681. name,
  682. listener);
  683. return null;
  684. }
  685. });
  686. if (logger.debugOn()) {
  687. logger.debug("destroyListeners", "removed " + what);
  688. }
  689. } catch (PrivilegedActionException pe) {
  690. final Exception e = extractException(pe);
  691. if (e instanceof ListenerNotFoundException) {
  692. if (logger.debugOn()) {
  693. logger.debug("destroyListeners",
  694. "ListenerNotFoundException for " + what +
  695. " (normal)");
  696. }
  697. } else {
  698. logger.trace("destroyListeners", e);
  699. }
  700. }
  701. }
  702. }
  703. logger.debug("destroyListeners", "ends");
  704. }
  705. /**
  706. * Iterate until we extract the real exception
  707. * from a stack of PrivilegedActionExceptions.
  708. */
  709. private static Exception extractException(Exception e) {
  710. while (e instanceof PrivilegedActionException) {
  711. e = ((PrivilegedActionException)e).getException();
  712. }
  713. return e;
  714. }
  715. private static final ClassLogger logger =
  716. new ClassLogger("javax.management.remote.misc",
  717. "ArrayNotificationBuffer");
  718. private static final ObjectName delegateName;
  719. static {
  720. try {
  721. delegateName =
  722. ObjectName.getInstance("JMImplementation:" +
  723. "type=MBeanServerDelegate");
  724. } catch (MalformedObjectNameException e) {
  725. RuntimeException re =
  726. new RuntimeException("Can't create delegate name: " + e);
  727. EnvHelp.initCause(re, e);
  728. logger.error("<init>", "Can't create delegate name: " + e);
  729. logger.debug("<init>",e);
  730. throw re;
  731. }
  732. }
  733. private final MBeanServer mBeanServer;
  734. private final ArrayQueue queue;
  735. private int queueSize;
  736. private long earliestSequenceNumber;
  737. private long nextSequenceNumber;
  738. private boolean destroyed;
  739. static final String broadcasterClass =
  740. NotificationBroadcaster.class.getName();
  741. }