KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > management > NotificationBroadcasterSupport


1 /*
2  * @(#)NotificationBroadcasterSupport.java 1.56 04/09/08
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.management;
9
10 import java.util.ArrayList JavaDoc;
11 import java.util.Collections JavaDoc;
12 import java.util.List JavaDoc;
13
14 import com.sun.jmx.trace.Trace;
15
16 /**
17  * <p>Provides an implementation of {@link
18  * javax.management.NotificationEmitter NotificationEmitter}
19  * interface. This can be used as the super class of an MBean that
20  * sends notifications.</p>
21  *
22  * <p>It is not specified whether the notification dispatch model is
23  * synchronous or asynchronous. That is, when a thread calls {@link
24  * #sendNotification sendNotification}, the {@link
25  * NotificationListener#handleNotification
26  * NotificationListener.handleNotification} method of each listener
27  * may be called within that thread (a synchronous model) or within
28  * some other thread (an asynchronous model).</p>
29  *
30  * <p>Applications should not depend on notification dispatch being
31  * synchronous or being asynchronous. Thus:</p>
32  *
33  * <ul>
34  *
35  * <li>Applications should not assume a synchronous model. When the
36  * {@link #sendNotification sendNotification} method returns, it is
37  * not guaranteed that every listener's {@link
38  * NotificationListener#handleNotification handleNotification} method
39  * has been called. It is not guaranteed either that a listener will
40  * see notifications in the same order as they were generated.
41  * Listeners that depend on order should use the sequence number of
42  * notifications to determine their order (see {@link
43  * Notification#getSequenceNumber()}).
44  *
45  * <li>Applications should not assume an asynchronous model. If the
46  * actions performed by a listener are potentially slow, the listener
47  * should arrange for them to be performed in another thread, to avoid
48  * holding up other listeners and the caller of {@link
49  * #sendNotification sendNotification}.
50  *
51  * </ul>
52  *
53  * @since 1.5
54  */

55 public class NotificationBroadcasterSupport implements NotificationEmitter JavaDoc {
56     
57     /**
58      * Adds a listener.
59      *
60      * @param listener The listener to receive notifications.
61      * @param filter The filter object. If filter is null, no filtering will be performed before handling notifications.
62      * @param handback An opaque object to be sent back to the listener when a notification is emitted. This object
63      * cannot be used by the Notification broadcaster object. It should be resent unchanged with the notification
64      * to the listener.
65      *
66      * @exception IllegalArgumentException thrown if the listener is null.
67      *
68      * @see #removeNotificationListener
69      */

70     public void addNotificationListener(NotificationListener JavaDoc listener,
71                     NotificationFilter JavaDoc filter,
72                     Object JavaDoc handback) {
73
74         if (listener == null) {
75             throw new IllegalArgumentException JavaDoc ("Listener can't be null") ;
76         }
77
78     /* Adding a new listener takes O(n) time where n is the number
79        of existing listeners. If you have a very large number of
80        listeners performance could degrade. That's a fairly
81        surprising configuration, and it is hard to avoid this
82        behaviour while still retaining the property that the
83        listenerList is not synchronized while notifications are
84        being sent through it. If this becomes a problem, a
85        possible solution would be a multiple-readers single-writer
86        setup, so any number of sendNotification() calls could run
87        concurrently but they would exclude an
88        add/removeNotificationListener. A simpler but less
89        efficient solution would be to clone the listener list
90        every time a notification is sent. */

91     synchronized (lock) {
92         List JavaDoc newList = new ArrayList JavaDoc(listenerList.size() + 1);
93         newList.addAll(listenerList);
94         newList.add(new ListenerInfo(listener, filter, handback));
95         listenerList = newList;
96     }
97     }
98
99     public void removeNotificationListener(NotificationListener JavaDoc listener)
100         throws ListenerNotFoundException JavaDoc {
101
102     synchronized (lock) {
103         List JavaDoc newList = new ArrayList JavaDoc(listenerList);
104         /* We scan the list of listeners in reverse order because
105            in forward order we would have to repeat the loop with
106            the same index after a remove. */

107         for (int i=newList.size()-1; i>=0; i--) {
108         ListenerInfo li = (ListenerInfo)newList.get(i);
109
110         if (li.listener == listener)
111             newList.remove(i);
112         }
113         if (newList.size() == listenerList.size())
114         throw new ListenerNotFoundException JavaDoc("Listener not registered");
115         listenerList = newList;
116     }
117     }
118
119     public void removeNotificationListener(NotificationListener JavaDoc listener,
120                        NotificationFilter JavaDoc filter,
121                        Object JavaDoc handback)
122         throws ListenerNotFoundException JavaDoc {
123
124     boolean found = false;
125
126     synchronized (lock) {
127         List JavaDoc newList = new ArrayList JavaDoc(listenerList);
128         final int size = newList.size();
129         for (int i = 0; i < size; i++) {
130         ListenerInfo li = (ListenerInfo) newList.get(i);
131
132         if (li.listener == listener) {
133             found = true;
134             if (li.filter == filter
135             && li.handback == handback) {
136             newList.remove(i);
137             listenerList = newList;
138             return;
139             }
140         }
141         }
142     }
143
144     if (found) {
145         /* We found this listener, but not with the given filter
146          * and handback. A more informative exception message may
147          * make debugging easier. */

148         throw new ListenerNotFoundException JavaDoc("Listener not registered " +
149                         "with this filter and " +
150                         "handback");
151     } else {
152         throw new ListenerNotFoundException JavaDoc("Listener not registered");
153     }
154     }
155
156     public MBeanNotificationInfo JavaDoc[] getNotificationInfo() {
157         return new MBeanNotificationInfo JavaDoc[0];
158     }
159
160
161     /**
162      * Sends a notification.
163      *
164      * @param notification The notification to send.
165      */

166     public void sendNotification(Notification JavaDoc notification) {
167
168     if (notification == null) {
169         return;
170     }
171         
172     List JavaDoc currentList;
173     synchronized (lock) {
174         currentList = listenerList;
175     }
176
177     final int size = currentList.size();
178     for (int i = 0; i < size; i++) {
179         ListenerInfo li = (ListenerInfo) currentList.get(i);
180
181         if (li.filter == null
182         || li.filter.isNotificationEnabled(notification)) {
183         try {
184             this.handleNotification(li.listener, notification,
185                         li.handback);
186         } catch (Exception JavaDoc e) {
187             trace("sendNotification",
188               "exception from listener: " + e);
189         }
190         }
191     }
192     }
193
194     /**
195      * <p>This method is called by {@link #sendNotification
196      * sendNotification} for each listener in order to send the
197      * notification to that listener. It can be overridden in
198      * subclasses to change the behavior of notification delivery,
199      * for instance to deliver the notification in a separate
200      * thread.</p>
201      *
202      * <p>It is not guaranteed that this method is called by the same
203      * thread as the one that called {@link #sendNotification
204      * sendNotification}.</p>
205      *
206      * <p>The default implementation of this method is equivalent to
207      * <pre>
208      * listener.handleNotification(notif, handback);
209      * </pre>
210      *
211      * @param listener the listener to which the notification is being
212      * delivered.
213      * @param notif the notification being delivered to the listener.
214      * @param handback the handback object that was supplied when the
215      * listener was added.
216      *
217      * @since.unbundled JMX 1.2
218      */

219     protected void handleNotification(NotificationListener JavaDoc listener,
220                       Notification JavaDoc notif, Object JavaDoc handback) {
221     listener.handleNotification(notif, handback);
222     }
223
224     // private stuff
225

226     private static void trace(String JavaDoc method, String JavaDoc message) {
227     if (Trace.isSelected(Trace.LEVEL_TRACE, Trace.INFO_MISC)) {
228         Trace.send(Trace.LEVEL_TRACE, Trace.INFO_MISC,
229                NotificationBroadcasterSupport JavaDoc.class.getName(),
230                method, message);
231     }
232     }
233
234     private class ListenerInfo {
235     public NotificationListener JavaDoc listener;
236     NotificationFilter JavaDoc filter;
237     Object JavaDoc handback;
238
239     public ListenerInfo(NotificationListener JavaDoc listener,
240                 NotificationFilter JavaDoc filter,
241                 Object JavaDoc handback) {
242         this.listener = listener;
243         this.filter = filter;
244         this.handback = handback;
245     }
246     }
247
248     /**
249      * Current list of listeners, a List of ListenerInfo. The object
250      * referenced by this field is never modified. Instead, the field
251      * is set to a new object when a listener is added or removed,
252      * within a synchronized(lock). In this way, there is no need to
253      * synchronize when traversing the list to send a notification to
254      * the listeners in it. That avoids potential deadlocks if the
255      * listeners end up depending on other threads that are themselves
256      * accessing this NotificationBroadcasterSupport.
257      */

258     private List JavaDoc listenerList = Collections.EMPTY_LIST;
259
260     /**
261      * We don't want to synchronize on "this", since a subclass might
262      * use the "this" lock for its own purposes and we could get a
263      * deadlock (bug 5093922). We can't synchronize on listenerList
264      * because when we want to change it we would be replacing the
265      * object we are synchronizing on. (In fact, it *might* be possible
266      * to synchronize on listenerList provided the code verified after
267      * getting the lock that the listenerList field still corresponds
268      * to the object synchronized on. This is the sort of thing that
269      * might be all right with the new memory model. But let's not
270      * make life unnecessarily difficult for ourselves just to save
271      * one field.
272      * In a future version we will use CopyOnWriteArrayList instead,
273      * since it does pretty much exactly what we want. There are a
274      * few tricky details related to the semantics of the two
275      * removeNotificationListener operations, however.
276      */

277     private final Object JavaDoc lock = new Object JavaDoc();
278 }
279
Popular Tags