Frames | No Frames |
1: /* NotificationBroadcasterSupport.java -- Supporting implementation. 2: Copyright (C) 2007 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package javax.management; 39: 40: import gnu.javax.management.ListenerData; 41: 42: import java.util.ArrayList; 43: import java.util.Iterator; 44: import java.util.List; 45: 46: import java.util.concurrent.Executor; 47: 48: /** 49: * <p> 50: * Provides an implementation of the {@link NotificationEmitter} 51: * interface, which beans may utilise by extension. By default, 52: * a synchronous dispatch system is provided, whereby the 53: * {@link #handleNotification(NotificationListener, Notification, 54: * Object)} is called once per listener by 55: * {*@link #sendNotification(Notification)} before returning. 56: * Thus, unless the listener is remote, it will have received 57: * the notification before the method returns. 58: * This may be changed by overriding the <code>handleNotification</code> 59: * method, or by providing an {@link java.util.concurrent.Executor} to 60: * use. With the latter, the dispatch of a notification to a particular 61: * listener will form one task performed by the executor. 62: * </p> 63: * <p> 64: * Any exceptions thrown by the dispatch process will be caught, allowing 65: * other listeners to still receive the notification. However, any 66: * {@link Error}s that are thrown will be propogated to the caller of 67: * {@link #sendNotification(Notification)}. 68: * </p> 69: * 70: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 71: * @since 1.5 72: */ 73: public class NotificationBroadcasterSupport 74: implements NotificationEmitter 75: { 76: 77: /** 78: * The executor for dispatch, or 79: * <code>null</code> if this thread should 80: * handle dispatch itself. 81: */ 82: private Executor executor; 83: 84: /** 85: * An array containing information on each 86: * notification, or <code>null</code> if an 87: * empty array should be returned by 88: * {@link #getNotificationInfo()}. 89: */ 90: private MBeanNotificationInfo[] info; 91: 92: /** 93: * The list of listeners registered with 94: * this broadcaster. 95: */ 96: private final List<ListenerData> listeners = 97: new ArrayList<ListenerData>(); 98: 99: /** 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: }