1 /*
2  * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */

25
26 package javax.management;
27
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Objects;
31 import java.util.concurrent.CopyOnWriteArrayList;
32 import java.util.concurrent.Executor;
33
34 import com.sun.jmx.remote.util.ClassLogger;
35
36 /**
37  * <p>Provides an implementation of {@link
38  * javax.management.NotificationEmitter NotificationEmitter}
39  * interface.  This can be used as the super class of an MBean that
40  * sends notifications.</p>
41  *
42  * <p>By default, the notification dispatch model is synchronous.
43  * That is, when a thread calls sendNotification, the
44  * <code>NotificationListener.handleNotification</code> method of each listener
45  * is called within that thread. You can override this default
46  * by overriding <code>handleNotification</code> in a subclass, or by passing an
47  * Executor to the constructor.</p>
48  *
49  * <p>If the method call of a filter or listener throws an {@link Exception},
50  * then that exception does not prevent other listeners from being invoked.  However,
51  * if the method call of a filter or of {@code Executor.execute} or of
52  * {@code handleNotification} (when no {@code Excecutor} is specified) throws an
53  * {@link Error}, then that {@code Error} is propagated to the caller of
54  * {@link #sendNotification sendNotification}.</p>
55  *
56  * <p>Remote listeners added using the JMX Remote API (see JMXConnector) are not
57  * usually called synchronously.  That is, when sendNotification returns, it is
58  * not guaranteed that any remote listeners have yet received the notification.</p>
59  *
60  * @since 1.5
61  */

62 public class NotificationBroadcasterSupport implements NotificationEmitter {
63     /**
64      * Constructs a NotificationBroadcasterSupport where each listener is invoked by the
65      * thread sending the notification. This constructor is equivalent to
66      * {@link NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor,
67      * MBeanNotificationInfo[] info) NotificationBroadcasterSupport(nullnull)}.
68      */

69     public NotificationBroadcasterSupport() {
70         this(null, (MBeanNotificationInfo[]) null);
71     }
72
73     /**
74      * Constructs a NotificationBroadcasterSupport where each listener is invoked using
75      * the given {@link java.util.concurrent.Executor}. When {@link #sendNotification
76      * sendNotification} is called, a listener is selected if it was added with a null
77      * {@link NotificationFilter}, or if {@link NotificationFilter#isNotificationEnabled
78      * isNotificationEnabled} returns true for the notification being sent. The call to
79      * <code>NotificationFilter.isNotificationEnabled</code> takes place in the thread
80      * that called <code>sendNotification</code>. Then, for each selected listener,
81      * {@link Executor#execute executor.execute} is called with a command
82      * that calls the <code>handleNotification</code> method.
83      * This constructor is equivalent to
84      * {@link NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor,
85      * MBeanNotificationInfo[] info) NotificationBroadcasterSupport(executor, null)}.
86      * @param executor an executor used by the method <code>sendNotification</code> to
87      * send each notification. If it is null, the thread calling <code>sendNotification</code>
88      * will invoke the <code>handleNotification</code> method itself.
89      * @since 1.6
90      */

91     public NotificationBroadcasterSupport(Executor executor) {
92         this(executor, (MBeanNotificationInfo[]) null);
93     }
94
95     /**
96      * <p>Constructs a NotificationBroadcasterSupport with information
97      * about the notifications that may be sent.  Each listener is
98      * invoked by the thread sending the notification.  This
99      * 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,
177                                         Object handback) {
178
179         if (listener == null) {
180             throw new IllegalArgumentException ("Listener can't be null") ;
181         }
182
183         listenerList.add(new ListenerInfo(listener, filter, handback));
184     }
185
186     public void removeNotificationListener(NotificationListener listener)
187             throws ListenerNotFoundException {
188
189         ListenerInfo wildcard = new WildcardListenerInfo(listener);
190         boolean removed =
191             listenerList.removeAll(Collections.singleton(wildcard));
192         if (!removed)
193             throw new ListenerNotFoundException("Listener not registered");
194     }
195
196     public void removeNotificationListener(NotificationListener listener,
197                                            NotificationFilter filter,
198                                            Object handback)
199             throws ListenerNotFoundException {
200
201         ListenerInfo li = new ListenerInfo(listener, filter, handback);
202         boolean removed = listenerList.remove(li);
203         if (!removed) {
204             throw new ListenerNotFoundException("Listener not registered " +
205                                                 "(with this filter and " +
206                                                 "handback)");
207             // or perhaps not registered at all
208         }
209     }
210
211     public MBeanNotificationInfo[] getNotificationInfo() {
212         if (notifInfo.length == 0)
213             return notifInfo;
214         else
215             return notifInfo.clone();
216     }
217
218
219     /**
220      * Sends a notification.
221      *
222      * If an {@code Executor} was specified in the constructor, it will be given one
223      * task per selected listener to deliver the notification to that listener.
224      *
225      * @param notification The notification to send.
226      */

227     public void sendNotification(Notification notification) {
228
229         if (notification == null) {
230             return;
231         }
232
233         boolean enabled;
234
235         for (ListenerInfo li : listenerList) {
236             try {
237                 enabled = li.filter == null ||
238                     li.filter.isNotificationEnabled(notification);
239             } catch (Exception e) {
240                 if (logger.debugOn()) {
241                     logger.debug("sendNotification", e);
242                 }
243
244                 continue;
245             }
246
247             if (enabled) {
248                 executor.execute(new SendNotifJob(notification, li));
249             }
250         }
251     }
252
253     /**
254      * <p>This method is called by {@link #sendNotification
255      * sendNotification} for each listener in order to send the
256      * notification to that listener.  It can be overridden in
257      * subclasses to change the behavior of notification delivery,
258      * for instance to deliver the notification in a separate
259      * thread.</p>
260      *
261      * <p>The default implementation of this method is equivalent to
262      * <pre>
263      * listener.handleNotification(notif, handback);
264      * </pre>
265      *
266      * @param listener the listener to which the notification is being
267      * delivered.
268      * @param notif the notification being delivered to the listener.
269      * @param handback the handback object that was supplied when the
270      * listener was added.
271      *
272      */

273     protected void handleNotification(NotificationListener listener,
274                                       Notification notif, Object handback) {
275         listener.handleNotification(notif, handback);
276     }
277
278     // private stuff
279     private static class ListenerInfo {
280         NotificationListener listener;
281         NotificationFilter filter;
282         Object handback;
283
284         ListenerInfo(NotificationListener listener,
285                      NotificationFilter filter,
286                      Object handback) {
287             this.listener = listener;
288             this.filter = filter;
289             this.handback = handback;
290         }
291
292         @Override
293         public boolean equals(Object o) {
294             if (!(o instanceof ListenerInfo))
295                 return false;
296             ListenerInfo li = (ListenerInfo) o;
297             if (li instanceof WildcardListenerInfo)
298                 return (li.listener == listener);
299             else
300                 return (li.listener == listener && li.filter == filter
301                         && li.handback == handback);
302         }
303
304         @Override
305         public int hashCode() {
306             return Objects.hashCode(listener);
307         }
308     }
309
310     private static class WildcardListenerInfo extends ListenerInfo {
311         WildcardListenerInfo(NotificationListener listener) {
312             super(listener, nullnull);
313         }
314
315         @Override
316         public boolean equals(Object o) {
317             assert (!(o instanceof WildcardListenerInfo));
318             return o.equals(this);
319         }
320
321         @Override
322         public int hashCode() {
323             return super.hashCode();
324         }
325     }
326
327     private List<ListenerInfo> listenerList =
328         new CopyOnWriteArrayList<ListenerInfo>();
329
330     // since 1.6
331     private final Executor executor;
332     private final MBeanNotificationInfo[] notifInfo;
333
334     private final static Executor defaultExecutor = new Executor() {
335             // DirectExecutor using caller thread
336             public void execute(Runnable r) {
337                 r.run();
338             }
339         };
340
341     private static final MBeanNotificationInfo[] NO_NOTIFICATION_INFO =
342         new MBeanNotificationInfo[0];
343
344     private class SendNotifJob implements Runnable {
345         public SendNotifJob(Notification notif, ListenerInfo listenerInfo) {
346             this.notif = notif;
347             this.listenerInfo = listenerInfo;
348         }
349
350         public void run() {
351             try {
352                 handleNotification(listenerInfo.listener,
353                                    notif, listenerInfo.handback);
354             } catch (Exception e) {
355                 if (logger.debugOn()) {
356                     logger.debug("SendNotifJob-run", e);
357                 }
358             }
359         }
360
361         private final Notification notif;
362         private final ListenerInfo listenerInfo;
363     }
364
365     private static final ClassLogger logger =
366         new ClassLogger("javax.management""NotificationBroadcasterSupport");
367 }
368