001 /* NotificationBroadcasterSupport.java -- Supporting implementation. 002 Copyright (C) 2007 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 package javax.management; 039 040 import gnu.javax.management.ListenerData; 041 042 import java.util.ArrayList; 043 import java.util.Iterator; 044 import java.util.List; 045 046 import java.util.concurrent.Executor; 047 048 /** 049 * <p> 050 * Provides an implementation of the {@link NotificationEmitter} 051 * interface, which beans may utilise by extension. By default, 052 * a synchronous dispatch system is provided, whereby the 053 * {@link #handleNotification(NotificationListener, Notification, 054 * Object)} is called once per listener by 055 * {*@link #sendNotification(Notification)} before returning. 056 * Thus, unless the listener is remote, it will have received 057 * the notification before the method returns. 058 * This may be changed by overriding the <code>handleNotification</code> 059 * method, or by providing an {@link java.util.concurrent.Executor} to 060 * use. With the latter, the dispatch of a notification to a particular 061 * listener will form one task performed by the executor. 062 * </p> 063 * <p> 064 * Any exceptions thrown by the dispatch process will be caught, allowing 065 * other listeners to still receive the notification. However, any 066 * {@link Error}s that are thrown will be propogated to the caller of 067 * {@link #sendNotification(Notification)}. 068 * </p> 069 * 070 * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 071 * @since 1.5 072 */ 073 public class NotificationBroadcasterSupport 074 implements NotificationEmitter 075 { 076 077 /** 078 * The executor for dispatch, or 079 * <code>null</code> if this thread should 080 * handle dispatch itself. 081 */ 082 private Executor executor; 083 084 /** 085 * An array containing information on each 086 * notification, or <code>null</code> if an 087 * empty array should be returned by 088 * {@link #getNotificationInfo()}. 089 */ 090 private MBeanNotificationInfo[] info; 091 092 /** 093 * The list of listeners registered with 094 * this broadcaster. 095 */ 096 private final List<ListenerData> listeners = 097 new ArrayList<ListenerData>(); 098 099 /** 100 * Constructs a {@link NotificationBroadcasterSupport} using 101 * the default synchronous dispatch model, where a single 102 * thread sends the notification to all listeners. This 103 * is equivalent to calling 104 * <code>NotificationBroadcasterSupport(null, null)</code>. 105 */ 106 public NotificationBroadcasterSupport() 107 { 108 this(null, (MBeanNotificationInfo[]) null); 109 } 110 111 /** 112 * Constructs a {@link NotificationBroadcasterSupport} where 113 * the specified (@link java.util.concurrent.Executor} is used 114 * to perform each invocation of the 115 * {@link #handleNotification(NotificationListener, Notification, 116 * Object)} method. Filtering is performed beforehand, by this 117 * thread; only calls which have successfully passed through the 118 * filter are sent to the executor. This is equivalent to calling 119 * <code>NotificationBroadcasterSupport(executor, null)</code>. 120 * 121 * @param executor the executor to use for each call to 122 * <code>handleNotification()</code>. 123 * @since 1.6 124 */ 125 public NotificationBroadcasterSupport(Executor executor) 126 { 127 this(executor, (MBeanNotificationInfo) null); 128 } 129 130 /** 131 * Constructs a {@link NotificationBroadcasterSupport} using 132 * the default synchronous dispatch model, where a single 133 * thread sends the notification to all listeners. The specified 134 * {@link MBeanNotificationInfo} array is used to provide 135 * information about the notifications on calls to 136 * {@link #getNotificationInfo()}, where a clone will be 137 * returned if the array is non-empty. This is equivalent to 138 * calling <code>NotificationBroadcasterSupport(null, info)</code>. 139 * 140 * @param info an array of {@link MBeanNotificationInfo} objects 141 * describing the notifications delivered by this 142 * broadcaster. This may be <code>null</code>, which 143 * is taken as being equivalent to an empty array. 144 */ 145 public NotificationBroadcasterSupport(MBeanNotificationInfo... info) 146 { 147 this(null, info); 148 } 149 150 /** 151 * Constructs a {@link NotificationBroadcasterSupport} where 152 * the specified (@link java.util.concurrent.Executor} is used 153 * to perform each invocation of the 154 * {@link #handleNotification(NotificationListener, Notification, 155 * Object)} method. Filtering is performed beforehand, by this 156 * thread; only calls which have successfully passed through the 157 * filter are sent to the executor. The specified 158 * {@link MBeanNotificationInfo} array is used to provide 159 * information about the notifications on calls to 160 * {@link #getNotificationInfo()}, where a clone will be 161 * returned if the array is non-empty. 162 * 163 * @param executor the executor to use for each call to 164 * <code>handleNotification()</code>. 165 * @param info an array of {@link MBeanNotificationInfo} objects 166 * describing the notifications delivered by this 167 * broadcaster. This may be <code>null</code>, which 168 * is taken as being equivalent to an empty array. 169 * @since 1.6 170 */ 171 public NotificationBroadcasterSupport(Executor executor, 172 MBeanNotificationInfo... info) 173 { 174 this.executor = executor; 175 this.info = info; 176 } 177 178 /** 179 * Registers the specified listener as a new recipient of 180 * notifications from this bean. If non-null, the filter 181 * argument will be used to select which notifications are 182 * delivered. The supplied object will also be passed to 183 * the recipient with each notification. This should not 184 * be modified by the broadcaster, but instead should be 185 * passed unmodified to the listener. 186 * 187 * @param listener the new listener, who will receive 188 * notifications from this broadcasting bean. 189 * @param filter a filter to determine which notifications are 190 * delivered to the listener, or <code>null</code> 191 * if no filtering is required. 192 * @param passback an object to be passed to the listener with 193 * each notification. 194 * @throws IllegalArgumentException if <code>listener</code> is 195 * <code>null</code>. 196 * @see #removeNotificationListener(NotificationListener) 197 */ 198 public void addNotificationListener(NotificationListener listener, 199 NotificationFilter filter, 200 Object passback) 201 throws IllegalArgumentException 202 { 203 if (listener == null) 204 throw new IllegalArgumentException("Null listener added to bean."); 205 listeners.add(new ListenerData(listener, filter, passback)); 206 } 207 208 /** 209 * Returns an array describing the notifications this 210 * bean may send to its registered listeners. Ideally, this 211 * array should be complete, but in some cases, this may 212 * not be possible. However, be aware that some listeners 213 * may expect this to be so. 214 * 215 * @return the array of possible notifications. 216 */ 217 public MBeanNotificationInfo[] getNotificationInfo() 218 { 219 if (info == null || info.length == 0) 220 return new MBeanNotificationInfo[0]; 221 return (MBeanNotificationInfo[]) info.clone(); 222 } 223 224 /** 225 * This method is called on a per-listener basis, either 226 * from this thread or the supplied executor, and may be 227 * overridden by subclasses which wish to change how 228 * notifications are delivered. The default 229 * implementation simply calls 230 * <code>listener.handleNotification(notif, passback)</code>. 231 * 232 * @param listener the listener to send the notification to. 233 * @param notif the notification to dispatch. 234 * @param passback the passback object of the listener. 235 */ 236 protected void handleNotification(NotificationListener listener, 237 Notification notif, 238 Object passback) 239 { 240 listener.handleNotification(notif, passback); 241 } 242 243 /** 244 * Removes the specified listener from the list of recipients 245 * of notifications from this bean. This includes all combinations 246 * of filters and passback objects registered for this listener. 247 * For more specific removal of listeners, see the subinterface 248 * {@link NotificationEmitter}. 249 * 250 * @param listener the listener to remove. 251 * @throws ListenerNotFoundException if the specified listener 252 * is not registered with this bean. 253 * @see #addNotificationListener(NotificationListener, NotificationFilter, 254 * java.lang.Object) 255 */ 256 public void removeNotificationListener(NotificationListener listener) 257 throws ListenerNotFoundException 258 { 259 Iterator<ListenerData> it = listeners.iterator(); 260 boolean foundOne = false; 261 while (it.hasNext()) 262 { 263 if (it.next().getListener() == listener) 264 { 265 it.remove(); 266 foundOne = true; 267 } 268 } 269 if (!foundOne) 270 throw new ListenerNotFoundException("The specified listener, " + listener + 271 "is not registered with this bean."); 272 } 273 274 /** 275 * Removes the specified listener from the list of recipients 276 * of notifications from this bean. Only the first instance with 277 * the supplied filter and passback object is removed. 278 * <code>null</code> is used as a valid value for these parameters, 279 * rather than as a way to remove all registration instances for 280 * the specified listener; for this behaviour instead, see the details 281 * of the same method in {@link NotificationBroadcaster}. 282 * 283 * @param listener the listener to remove. 284 * @param filter the filter of the listener to remove. 285 * @param passback the passback object of the listener to remove. 286 * @throws ListenerNotFoundException if the specified listener 287 * is not registered with this bean. 288 * @see #addNotificationListener(NotificationListener, NotificationFilter, 289 * java.lang.Object) 290 */ 291 public void removeNotificationListener(NotificationListener listener, 292 NotificationFilter filter, 293 Object passback) 294 throws ListenerNotFoundException 295 { 296 if (!(listeners.remove(new ListenerData(listener, filter, passback)))) 297 { 298 throw new ListenerNotFoundException("The specified listener, " + listener + 299 " with filter " + filter + 300 "and passback " + passback + 301 ", is not registered with this bean."); 302 } 303 } 304 305 /** 306 * <p> 307 * Performs delivery of the notification. If an executor 308 * was specified on construction, this will be used to call 309 * {@link #handleNotification(NotificationListener, Notification, 310 * Object)}. If the executor is <code>null</code>, however, 311 * this thread calls the method itself in order to perform a 312 * synchronous dispatch of the notification to all eligible 313 * listeners. 314 * </p> 315 * <p> 316 * Prior to either process taking place, the listeners are filtered. 317 * Notifications are only delivered if the filter is either 318 * <code>null</code> or returns true from the 319 * {@link NotificationFilter#isNotificationEnabled(Notification)} 320 * method. 321 * </p> 322 * 323 * @param notif the notification to send. 324 */ 325 public void sendNotification(Notification notif) 326 { 327 for (ListenerData ldata : listeners) 328 { 329 NotificationFilter filter = ldata.getFilter(); 330 if (filter == null || filter.isNotificationEnabled(notif)) 331 { 332 if (executor == null) 333 try 334 { 335 handleNotification(ldata.getListener(), notif, 336 ldata.getPassback()); 337 } 338 catch (Exception e) { /* Ignore */ } 339 else 340 executor.execute(new DispatchTask(ldata, notif)); 341 } 342 } 343 } 344 345 /** 346 * The dispatch task to be performed by an executor. 347 */ 348 private final class DispatchTask 349 implements Runnable 350 { 351 352 /** 353 * The data on the listener being called. 354 */ 355 private ListenerData ldata; 356 357 /** 358 * The notification to send. 359 */ 360 private Notification notif; 361 362 /** 363 * Construct a new {@link DispatchTask}. 364 * 365 * @param ldata the listener data. 366 * @param notif the notification to send. 367 */ 368 public DispatchTask(ListenerData ldata, 369 Notification notif) 370 { 371 this.ldata = ldata; 372 this.notif = notif; 373 } 374 375 /** 376 * Dispatch the notification. 377 */ 378 public void run() 379 { 380 try 381 { 382 handleNotification(ldata.getListener(), notif, 383 ldata.getPassback()); 384 } 385 catch (Exception e) { /* Ignore */ } 386 } 387 } 388 389 }