1 /* 2 * Copyright (c) 1999, 2007, 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.concurrent.CopyOnWriteArrayList; 31 import java.util.concurrent.Executor; 32 33 import com.sun.jmx.remote.util.ClassLogger; 34 35 /** 36 * <p>Provides an implementation of {@link 37 * javax.management.NotificationEmitter NotificationEmitter} 38 * interface. This can be used as the super class of an MBean that 39 * sends notifications.</p> 40 * 41 * <p>By default, the notification dispatch model is synchronous. 42 * That is, when a thread calls sendNotification, the 43 * <code>NotificationListener.handleNotification</code> method of each listener 44 * is called within that thread. You can override this default 45 * by overriding <code>handleNotification</code> in a subclass, or by passing an 46 * Executor to the constructor.</p> 47 * 48 * <p>If the method call of a filter or listener throws an {@link Exception}, 49 * then that exception does not prevent other listeners from being invoked. However, 50 * if the method call of a filter or of {@code Executor.execute} or of 51 * {@code handleNotification} (when no {@code Excecutor} is specified) throws an 52 * {@link Error}, then that {@code Error} is propagated to the caller of 53 * {@link #sendNotification sendNotification}.</p> 54 * 55 * <p>Remote listeners added using the JMX Remote API (see JMXConnector) are not 56 * usually called synchronously. That is, when sendNotification returns, it is 57 * not guaranteed that any remote listeners have yet received the notification.</p> 58 * 59 * @since 1.5 60 */ 61 public class NotificationBroadcasterSupport implements NotificationEmitter { 62 /** 63 * Constructs a NotificationBroadcasterSupport where each listener is invoked by the 64 * thread sending the notification. This constructor is equivalent to 65 * {@link NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor, 66 * MBeanNotificationInfo[] info) NotificationBroadcasterSupport(null, null)}. 67 */ 68 public NotificationBroadcasterSupport() { 69 this(null, (MBeanNotificationInfo[]) null); 70 } 71 72 /** 73 * Constructs a NotificationBroadcasterSupport where each listener is invoked using 74 * the given {@link java.util.concurrent.Executor}. When {@link #sendNotification 75 * sendNotification} is called, a listener is selected if it was added with a null 76 * {@link NotificationFilter}, or if {@link NotificationFilter#isNotificationEnabled 77 * isNotificationEnabled} returns true for the notification being sent. The call to 78 * <code>NotificationFilter.isNotificationEnabled</code> takes place in the thread 79 * that called <code>sendNotification</code>. Then, for each selected listener, 80 * {@link Executor#execute executor.execute} is called with a command 81 * that calls the <code>handleNotification</code> method. 82 * This constructor is equivalent to 83 * {@link NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor, 84 * MBeanNotificationInfo[] info) NotificationBroadcasterSupport(executor, null)}. 85 * @param executor an executor used by the method <code>sendNotification</code> to 86 * send each notification. If it is null, the thread calling <code>sendNotification</code> 87 * will invoke the <code>handleNotification</code> method itself. 88 * @since 1.6 89 */ 90 public NotificationBroadcasterSupport(Executor executor) { 91 this(executor, (MBeanNotificationInfo[]) null); 92 } 93 94 /** 95 * <p>Constructs a NotificationBroadcasterSupport with information 96 * about the notifications that may be sent. Each listener is 97 * invoked by the thread sending the notification. This 98 * constructor is equivalent to {@link 99 * NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor, 100 * MBeanNotificationInfo[] info) 101 * NotificationBroadcasterSupport(null, info)}.</p> 102 * 103 * <p>If the <code>info</code> array is not empty, then it is 104 * cloned by the constructor as if by {@code info.clone()}, and 105 * each call to {@link #getNotificationInfo()} returns a new 106 * clone.</p> 107 * 108 * @param info an array indicating, for each notification this 109 * MBean may send, the name of the Java class of the notification 110 * and the notification type. Can be null, which is equivalent to 111 * an empty array. 112 * 113 * @since 1.6 114 */ 115 public NotificationBroadcasterSupport(MBeanNotificationInfo... info) { 116 this(null, info); 117 } 118 119 /** 120 * <p>Constructs a NotificationBroadcasterSupport with information about the notifications that may be sent, 121 * and where each listener is invoked using the given {@link java.util.concurrent.Executor}.</p> 122 * 123 * <p>When {@link #sendNotification sendNotification} is called, a 124 * listener is selected if it was added with a null {@link 125 * NotificationFilter}, or if {@link 126 * NotificationFilter#isNotificationEnabled isNotificationEnabled} 127 * returns true for the notification being sent. The call to 128 * <code>NotificationFilter.isNotificationEnabled</code> takes 129 * place in the thread that called 130 * <code>sendNotification</code>. Then, for each selected 131 * listener, {@link Executor#execute executor.execute} is called 132 * with a command that calls the <code>handleNotification</code> 133 * method.</p> 134 * 135 * <p>If the <code>info</code> array is not empty, then it is 136 * cloned by the constructor as if by {@code info.clone()}, and 137 * each call to {@link #getNotificationInfo()} returns a new 138 * clone.</p> 139 * 140 * @param executor an executor used by the method 141 * <code>sendNotification</code> to send each notification. If it 142 * is null, the thread calling <code>sendNotification</code> will 143 * invoke the <code>handleNotification</code> method itself. 144 * 145 * @param info an array indicating, for each notification this 146 * MBean may send, the name of the Java class of the notification 147 * and the notification type. Can be null, which is equivalent to 148 * an empty array. 149 * 150 * @since 1.6 151 */ 152 public NotificationBroadcasterSupport(Executor executor, 153 MBeanNotificationInfo... info) { 154 this.executor = (executor != null) ? executor : defaultExecutor; 155 156 notifInfo = info == null ? NO_NOTIFICATION_INFO : info.clone(); 157 } 158 159 /** 160 * Adds a listener. 161 * 162 * @param listener The listener to receive notifications. 163 * @param filter The filter object. If filter is null, no 164 * filtering will be performed before handling notifications. 165 * @param handback An opaque object to be sent back to the 166 * listener when a notification is emitted. This object cannot be 167 * used by the Notification broadcaster object. It should be 168 * resent unchanged with the notification to the listener. 169 * 170 * @exception IllegalArgumentException thrown if the listener is null. 171 * 172 * @see #removeNotificationListener 173 */ 174 public void addNotificationListener(NotificationListener listener, 175 NotificationFilter filter, 176 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 = 190 listenerList.removeAll(Collections.singleton(wildcard)); 191 if (!removed) 192 throw new ListenerNotFoundException("Listener not registered"); 193 } 194 195 public void removeNotificationListener(NotificationListener listener, 196 NotificationFilter filter, 197 Object handback) 198 throws ListenerNotFoundException { 199 200 ListenerInfo li = new ListenerInfo(listener, filter, handback); 201 boolean removed = listenerList.remove(li); 202 if (!removed) { 203 throw new ListenerNotFoundException("Listener not registered " + 204 "(with this filter and " + 205 "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 /** 219 * Sends a notification. 220 * 221 * If an {@code Executor} was specified in the constructor, it will be given one 222 * task per selected listener to deliver the notification to that listener. 223 * 224 * @param notification The notification to send. 225 */ 226 public void sendNotification(Notification notification) { 227 228 if (notification == null) { 229 return; 230 } 231 232 boolean enabled; 233 234 for (ListenerInfo li : listenerList) { 235 try { 236 enabled = li.filter == null || 237 li.filter.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, 285 Object handback) { 286 this.listener = listener; 287 this.filter = filter; 288 this.handback = handback; 289 } 290 291 public boolean equals(Object o) { 292 if (!(o instanceof ListenerInfo)) 293 return false; 294 ListenerInfo li = (ListenerInfo) o; 295 if (li instanceof WildcardListenerInfo) 296 return (li.listener == listener); 297 else 298 return (li.listener == listener && li.filter == filter 299 && li.handback == handback); 300 } 301 } 302 303 private static class WildcardListenerInfo extends ListenerInfo { 304 WildcardListenerInfo(NotificationListener listener) { 305 super(listener, null, null); 306 } 307 308 public boolean equals(Object o) { 309 assert (!(o instanceof WildcardListenerInfo)); 310 return o.equals(this); 311 } 312 } 313 314 private List<ListenerInfo> listenerList = 315 new CopyOnWriteArrayList<ListenerInfo>(); 316 317 // since 1.6 318 private final Executor executor; 319 private final MBeanNotificationInfo[] notifInfo; 320 321 private final static Executor defaultExecutor = new Executor() { 322 // DirectExecutor using caller thread 323 public void execute(Runnable r) { 324 r.run(); 325 } 326 }; 327 328 private static final MBeanNotificationInfo[] NO_NOTIFICATION_INFO = 329 new MBeanNotificationInfo[0]; 330 331 private class SendNotifJob implements Runnable { 332 public SendNotifJob(Notification notif, ListenerInfo listenerInfo) { 333 this.notif = notif; 334 this.listenerInfo = listenerInfo; 335 } 336 337 public void run() { 338 try { 339 handleNotification(listenerInfo.listener, 340 notif, listenerInfo.handback); 341 } catch (Exception e) { 342 if (logger.debugOn()) { 343 logger.debug("SendNotifJob-run", e); 344 } 345 } 346 } 347 348 private final Notification notif; 349 private final ListenerInfo listenerInfo; 350 } 351 352 private static final ClassLogger logger = 353 new ClassLogger("javax.management", "NotificationBroadcasterSupport"); 354 }