Source for javax.management.NotificationBroadcasterSupport

   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, 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, 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 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: }