1. /*
  2. * @(#)NotificationBroadcasterSupport.java 1.55 04/02/10
  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;
  8. import java.util.ArrayList;
  9. import java.util.Collections;
  10. import java.util.List;
  11. import com.sun.jmx.trace.Trace;
  12. /**
  13. * <p>Provides an implementation of {@link
  14. * javax.management.NotificationEmitter NotificationEmitter}
  15. * interface. This can be used as the super class of an MBean that
  16. * sends notifications.</p>
  17. *
  18. * <p>It is not specified whether the notification dispatch model is
  19. * synchronous or asynchronous. That is, when a thread calls {@link
  20. * #sendNotification sendNotification}, the {@link
  21. * NotificationListener#handleNotification
  22. * NotificationListener.handleNotification} method of each listener
  23. * may be called within that thread (a synchronous model) or within
  24. * some other thread (an asynchronous model).</p>
  25. *
  26. * <p>Applications should not depend on notification dispatch being
  27. * synchronous or being asynchronous. Thus:</p>
  28. *
  29. * <ul>
  30. *
  31. * <li>Applications should not assume a synchronous model. When the
  32. * {@link #sendNotification sendNotification} method returns, it is
  33. * not guaranteed that every listener's {@link
  34. * NotificationListener#handleNotification handleNotification} method
  35. * has been called. It is not guaranteed either that a listener will
  36. * see notifications in the same order as they were generated.
  37. * Listeners that depend on order should use the sequence number of
  38. * notifications to determine their order (see {@link
  39. * Notification#getSequenceNumber()}).
  40. *
  41. * <li>Applications should not assume an asynchronous model. If the
  42. * actions performed by a listener are potentially slow, the listener
  43. * should arrange for them to be performed in another thread, to avoid
  44. * holding up other listeners and the caller of {@link
  45. * #sendNotification sendNotification}.
  46. *
  47. * </ul>
  48. *
  49. * @since 1.5
  50. */
  51. public class NotificationBroadcasterSupport implements NotificationEmitter {
  52. /**
  53. * Adds a listener.
  54. *
  55. * @param listener The listener to receive notifications.
  56. * @param filter The filter object. If filter is null, no filtering will be performed before handling notifications.
  57. * @param handback An opaque object to be sent back to the listener when a notification is emitted. This object
  58. * cannot be used by the Notification broadcaster object. It should be resent unchanged with the notification
  59. * to the listener.
  60. *
  61. * @exception IllegalArgumentException thrown if the listener is null.
  62. *
  63. * @see #removeNotificationListener
  64. */
  65. public void addNotificationListener(NotificationListener listener,
  66. NotificationFilter filter,
  67. Object handback) {
  68. if (listener == null) {
  69. throw new IllegalArgumentException ("Listener can't be null") ;
  70. }
  71. /* Adding a new listener takes O(n) time where n is the number
  72. of existing listeners. If you have a very large number of
  73. listeners performance could degrade. That's a fairly
  74. surprising configuration, and it is hard to avoid this
  75. behaviour while still retaining the property that the
  76. listenerList is not synchronized while notifications are
  77. being sent through it. If this becomes a problem, a
  78. possible solution would be a multiple-readers single-writer
  79. setup, so any number of sendNotification() calls could run
  80. concurrently but they would exclude an
  81. add/removeNotificationListener. A simpler but less
  82. efficient solution would be to clone the listener list
  83. every time a notification is sent. */
  84. synchronized (this) {
  85. List newList = new ArrayList(listenerList.size() + 1);
  86. newList.addAll(listenerList);
  87. newList.add(new ListenerInfo(listener, filter, handback));
  88. listenerList = newList;
  89. }
  90. }
  91. public void removeNotificationListener(NotificationListener listener)
  92. throws ListenerNotFoundException {
  93. synchronized (this) {
  94. List newList = new ArrayList(listenerList);
  95. /* We scan the list of listeners in reverse order because
  96. in forward order we would have to repeat the loop with
  97. the same index after a remove. */
  98. for (int i=newList.size()-1; i>=0; i--) {
  99. ListenerInfo li = (ListenerInfo)newList.get(i);
  100. if (li.listener == listener)
  101. newList.remove(i);
  102. }
  103. if (newList.size() == listenerList.size())
  104. throw new ListenerNotFoundException("Listener not registered");
  105. listenerList = newList;
  106. }
  107. }
  108. public void removeNotificationListener(NotificationListener listener,
  109. NotificationFilter filter,
  110. Object handback)
  111. throws ListenerNotFoundException {
  112. boolean found = false;
  113. synchronized (this) {
  114. List newList = new ArrayList(listenerList);
  115. final int size = newList.size();
  116. for (int i = 0; i < size; i++) {
  117. ListenerInfo li = (ListenerInfo) newList.get(i);
  118. if (li.listener == listener) {
  119. found = true;
  120. if (li.filter == filter
  121. && li.handback == handback) {
  122. newList.remove(i);
  123. listenerList = newList;
  124. return;
  125. }
  126. }
  127. }
  128. }
  129. if (found) {
  130. /* We found this listener, but not with the given filter
  131. * and handback. A more informative exception message may
  132. * make debugging easier. */
  133. throw new ListenerNotFoundException("Listener not registered " +
  134. "with this filter and " +
  135. "handback");
  136. } else {
  137. throw new ListenerNotFoundException("Listener not registered");
  138. }
  139. }
  140. public MBeanNotificationInfo[] getNotificationInfo() {
  141. return new MBeanNotificationInfo[0];
  142. }
  143. /**
  144. * Sends a notification.
  145. *
  146. * @param notification The notification to send.
  147. */
  148. public void sendNotification(Notification notification) {
  149. if (notification == null) {
  150. return;
  151. }
  152. List currentList;
  153. synchronized (this) {
  154. currentList = listenerList;
  155. }
  156. final int size = currentList.size();
  157. for (int i = 0; i < size; i++) {
  158. ListenerInfo li = (ListenerInfo) currentList.get(i);
  159. if (li.filter == null
  160. || li.filter.isNotificationEnabled(notification)) {
  161. try {
  162. this.handleNotification(li.listener, notification,
  163. li.handback);
  164. } catch (Exception e) {
  165. trace("sendNotification",
  166. "exception from listener: " + e);
  167. }
  168. }
  169. }
  170. }
  171. /**
  172. * <p>This method is called by {@link #sendNotification
  173. * sendNotification} for each listener in order to send the
  174. * notification to that listener. It can be overridden in
  175. * subclasses to change the behavior of notification delivery,
  176. * for instance to deliver the notification in a separate
  177. * thread.</p>
  178. *
  179. * <p>It is not guaranteed that this method is called by the same
  180. * thread as the one that called {@link #sendNotification
  181. * sendNotification}.</p>
  182. *
  183. * <p>The default implementation of this method is equivalent to
  184. * <pre>
  185. * listener.handleNotification(notif, handback);
  186. * </pre>
  187. *
  188. * @param listener the listener to which the notification is being
  189. * delivered.
  190. * @param notif the notification being delivered to the listener.
  191. * @param handback the handback object that was supplied when the
  192. * listener was added.
  193. *
  194. * @since.unbundled JMX 1.2
  195. */
  196. protected void handleNotification(NotificationListener listener,
  197. Notification notif, Object handback) {
  198. listener.handleNotification(notif, handback);
  199. }
  200. // private stuff
  201. private static void trace(String method, String message) {
  202. if (Trace.isSelected(Trace.LEVEL_TRACE, Trace.INFO_MISC)) {
  203. Trace.send(Trace.LEVEL_TRACE, Trace.INFO_MISC,
  204. NotificationBroadcasterSupport.class.getName(),
  205. method, message);
  206. }
  207. }
  208. private class ListenerInfo {
  209. public NotificationListener listener;
  210. NotificationFilter filter;
  211. Object handback;
  212. public ListenerInfo(NotificationListener listener,
  213. NotificationFilter filter,
  214. Object handback) {
  215. this.listener = listener;
  216. this.filter = filter;
  217. this.handback = handback;
  218. }
  219. }
  220. /**
  221. * Current list of listeners, a List of ListenerInfo. The object
  222. * referenced by this field is never modified. Instead, the field
  223. * is set to a new object when a listener is added or removed,
  224. * within a synchronized(this). In this way, there is no need to
  225. * synchronize when traversing the list to send a notification to
  226. * the listeners in it. That avoids potential deadlocks if the
  227. * listeners end up depending on other threads that are themselves
  228. * accessing this NotificationBroadcasterSupport.
  229. */
  230. private List listenerList = Collections.EMPTY_LIST;
  231. }