001 /*
002 * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.management;
027
028 import java.util.Collections;
029 import java.util.List;
030 import java.util.concurrent.CopyOnWriteArrayList;
031 import java.util.concurrent.Executor;
032
033 import com.sun.jmx.remote.util.ClassLogger;
034
035 /**
036 * <p>Provides an implementation of {@link
037 * javax.management.NotificationEmitter NotificationEmitter}
038 * interface. This can be used as the super class of an MBean that
039 * sends notifications.</p>
040 *
041 * <p>By default, the notification dispatch model is synchronous.
042 * That is, when a thread calls sendNotification, the
043 * <code>NotificationListener.handleNotification</code> method of each listener
044 * is called within that thread. You can override this default
045 * by overriding <code>handleNotification</code> in a subclass, or by passing an
046 * Executor to the constructor.</p>
047 *
048 * <p>If the method call of a filter or listener throws an {@link Exception},
049 * then that exception does not prevent other listeners from being invoked. However,
050 * if the method call of a filter or of {@code Executor.execute} or of
051 * {@code handleNotification} (when no {@code Excecutor} is specified) throws an
052 * {@link Error}, then that {@code Error} is propagated to the caller of
053 * {@link #sendNotification sendNotification}.</p>
054 *
055 * <p>Remote listeners added using the JMX Remote API (see JMXConnector) are not
056 * usually called synchronously. That is, when sendNotification returns, it is
057 * not guaranteed that any remote listeners have yet received the notification.</p>
058 *
059 * @since 1.5
060 */
061 public class NotificationBroadcasterSupport implements
062 NotificationEmitter {
063 /**
064 * Constructs a NotificationBroadcasterSupport where each listener is invoked by the
065 * thread sending the notification. This constructor is equivalent to
066 * {@link NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor,
067 * MBeanNotificationInfo[] info) NotificationBroadcasterSupport(null, null)}.
068 */
069 public NotificationBroadcasterSupport() {
070 this (null, (MBeanNotificationInfo[]) null);
071 }
072
073 /**
074 * Constructs a NotificationBroadcasterSupport where each listener is invoked using
075 * the given {@link java.util.concurrent.Executor}. When {@link #sendNotification
076 * sendNotification} is called, a listener is selected if it was added with a null
077 * {@link NotificationFilter}, or if {@link NotificationFilter#isNotificationEnabled
078 * isNotificationEnabled} returns true for the notification being sent. The call to
079 * <code>NotificationFilter.isNotificationEnabled</code> takes place in the thread
080 * that called <code>sendNotification</code>. Then, for each selected listener,
081 * {@link Executor#execute executor.execute} is called with a command
082 * that calls the <code>handleNotification</code> method.
083 * This constructor is equivalent to
084 * {@link NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor,
085 * MBeanNotificationInfo[] info) NotificationBroadcasterSupport(executor, null)}.
086 * @param executor an executor used by the method <code>sendNotification</code> to
087 * send each notification. If it is null, the thread calling <code>sendNotification</code>
088 * will invoke the <code>handleNotification</code> method itself.
089 * @since 1.6
090 */
091 public NotificationBroadcasterSupport(Executor executor) {
092 this (executor, (MBeanNotificationInfo[]) null);
093 }
094
095 /**
096 * <p>Constructs a NotificationBroadcasterSupport with information
097 * about the notifications that may be sent. Each listener is
098 * invoked by the thread sending the notification. This
099 * constructor is equivalent to {@link
100 * NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor,
101 * MBeanNotificationInfo[] info)
102 * NotificationBroadcasterSupport(null, info)}.</p>
103 *
104 * <p>If the <code>info</code> array is not empty, then it is
105 * cloned by the constructor as if by {@code info.clone()}, and
106 * each call to {@link #getNotificationInfo()} returns a new
107 * clone.</p>
108 *
109 * @param info an array indicating, for each notification this
110 * MBean may send, the name of the Java class of the notification
111 * and the notification type. Can be null, which is equivalent to
112 * an empty array.
113 *
114 * @since 1.6
115 */
116 public NotificationBroadcasterSupport(MBeanNotificationInfo... info) {
117 this (null, info);
118 }
119
120 /**
121 * <p>Constructs a NotificationBroadcasterSupport with information about the notifications that may be sent,
122 * and where each listener is invoked using the given {@link java.util.concurrent.Executor}.</p>
123 *
124 * <p>When {@link #sendNotification sendNotification} is called, a
125 * listener is selected if it was added with a null {@link
126 * NotificationFilter}, or if {@link
127 * NotificationFilter#isNotificationEnabled isNotificationEnabled}
128 * returns true for the notification being sent. The call to
129 * <code>NotificationFilter.isNotificationEnabled</code> takes
130 * place in the thread that called
131 * <code>sendNotification</code>. Then, for each selected
132 * listener, {@link Executor#execute executor.execute} is called
133 * with a command that calls the <code>handleNotification</code>
134 * method.</p>
135 *
136 * <p>If the <code>info</code> array is not empty, then it is
137 * cloned by the constructor as if by {@code info.clone()}, and
138 * each call to {@link #getNotificationInfo()} returns a new
139 * clone.</p>
140 *
141 * @param executor an executor used by the method
142 * <code>sendNotification</code> to send each notification. If it
143 * is null, the thread calling <code>sendNotification</code> will
144 * invoke the <code>handleNotification</code> method itself.
145 *
146 * @param info an array indicating, for each notification this
147 * MBean may send, the name of the Java class of the notification
148 * and the notification type. Can be null, which is equivalent to
149 * an empty array.
150 *
151 * @since 1.6
152 */
153 public NotificationBroadcasterSupport(Executor executor,
154 MBeanNotificationInfo... info) {
155 this .executor = (executor != null) ? executor : defaultExecutor;
156
157 notifInfo = info == null ? NO_NOTIFICATION_INFO : info.clone();
158 }
159
160 /**
161 * Adds a listener.
162 *
163 * @param listener The listener to receive notifications.
164 * @param filter The filter object. If filter is null, no
165 * filtering will be performed before handling notifications.
166 * @param handback An opaque object to be sent back to the
167 * listener when a notification is emitted. This object cannot be
168 * used by the Notification broadcaster object. It should be
169 * resent unchanged with the notification to the listener.
170 *
171 * @exception IllegalArgumentException thrown if the listener is null.
172 *
173 * @see #removeNotificationListener
174 */
175 public void addNotificationListener(NotificationListener listener,
176 NotificationFilter filter, Object handback) {
177
178 if (listener == null) {
179 throw new IllegalArgumentException("Listener can't be null");
180 }
181
182 listenerList.add(new ListenerInfo(listener, filter, handback));
183 }
184
185 public void removeNotificationListener(NotificationListener listener)
186 throws ListenerNotFoundException {
187
188 ListenerInfo wildcard = new WildcardListenerInfo(listener);
189 boolean removed = listenerList.removeAll(Collections
190 .singleton(wildcard));
191 if (!removed)
192 throw new ListenerNotFoundException(
193 "Listener not registered");
194 }
195
196 public void removeNotificationListener(
197 NotificationListener listener, NotificationFilter filter,
198 Object handback) throws ListenerNotFoundException {
199
200 ListenerInfo li = new ListenerInfo(listener, filter, handback);
201 boolean removed = listenerList.remove(li);
202 if (!removed) {
203 throw new ListenerNotFoundException(
204 "Listener not registered "
205 + "(with this filter and " + "handback)");
206 // or perhaps not registered at all
207 }
208 }
209
210 public MBeanNotificationInfo[] getNotificationInfo() {
211 if (notifInfo.length == 0)
212 return notifInfo;
213 else
214 return notifInfo.clone();
215 }
216
217 /**
218 * Sends a notification.
219 *
220 * If an {@code Executor} was specified in the constructor, it will be given one
221 * task per selected listener to deliver the notification to that listener.
222 *
223 * @param notification The notification to send.
224 */
225 public void sendNotification(Notification notification) {
226
227 if (notification == null) {
228 return;
229 }
230
231 boolean enabled;
232
233 for (ListenerInfo li : listenerList) {
234 try {
235 enabled = li.filter == null
236 || li.filter
237 .isNotificationEnabled(notification);
238 } catch (Exception e) {
239 if (logger.debugOn()) {
240 logger.debug("sendNotification", e);
241 }
242
243 continue;
244 }
245
246 if (enabled) {
247 executor.execute(new SendNotifJob(notification, li));
248 }
249 }
250 }
251
252 /**
253 * <p>This method is called by {@link #sendNotification
254 * sendNotification} for each listener in order to send the
255 * notification to that listener. It can be overridden in
256 * subclasses to change the behavior of notification delivery,
257 * for instance to deliver the notification in a separate
258 * thread.</p>
259 *
260 * <p>The default implementation of this method is equivalent to
261 * <pre>
262 * listener.handleNotification(notif, handback);
263 * </pre>
264 *
265 * @param listener the listener to which the notification is being
266 * delivered.
267 * @param notif the notification being delivered to the listener.
268 * @param handback the handback object that was supplied when the
269 * listener was added.
270 *
271 */
272 protected void handleNotification(NotificationListener listener,
273 Notification notif, Object handback) {
274 listener.handleNotification(notif, handback);
275 }
276
277 // private stuff
278 private static class ListenerInfo {
279 NotificationListener listener;
280 NotificationFilter filter;
281 Object handback;
282
283 ListenerInfo(NotificationListener listener,
284 NotificationFilter filter, Object handback) {
285 this .listener = listener;
286 this .filter = filter;
287 this .handback = handback;
288 }
289
290 public boolean equals(Object o) {
291 if (!(o instanceof ListenerInfo))
292 return false;
293 ListenerInfo li = (ListenerInfo) o;
294 if (li instanceof WildcardListenerInfo)
295 return (li.listener == listener);
296 else
297 return (li.listener == listener && li.filter == filter && li.handback == handback);
298 }
299 }
300
301 private static class WildcardListenerInfo extends ListenerInfo {
302 WildcardListenerInfo(NotificationListener listener) {
303 super (listener, null, null);
304 }
305
306 public boolean equals(Object o) {
307 assert (!(o instanceof WildcardListenerInfo));
308 return o.equals(this );
309 }
310 }
311
312 private List<ListenerInfo> listenerList = new CopyOnWriteArrayList<ListenerInfo>();
313
314 // since 1.6
315 private final Executor executor;
316 private final MBeanNotificationInfo[] notifInfo;
317
318 private final static Executor defaultExecutor = new Executor() {
319 // DirectExecutor using caller thread
320 public void execute(Runnable r) {
321 r.run();
322 }
323 };
324
325 private static final MBeanNotificationInfo[] NO_NOTIFICATION_INFO = new MBeanNotificationInfo[0];
326
327 private class SendNotifJob implements Runnable {
328 public SendNotifJob(Notification notif,
329 ListenerInfo listenerInfo) {
330 this .notif = notif;
331 this .listenerInfo = listenerInfo;
332 }
333
334 public void run() {
335 try {
336 handleNotification(listenerInfo.listener, notif,
337 listenerInfo.handback);
338 } catch (Exception e) {
339 if (logger.debugOn()) {
340 logger.debug("SendNotifJob-run", e);
341 }
342 }
343 }
344
345 private final Notification notif;
346 private final ListenerInfo listenerInfo;
347 }
348
349 private static final ClassLogger logger = new ClassLogger(
350 "javax.management", "NotificationBroadcasterSupport");
351 }
|