src/java.management/share/classes/javax/management/NotificationBroadcasterSupport.java
changeset 47216 71c04702a3d5
parent 25859 3317bb8137f4
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     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(null, null)}.
       
    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, null, null);
       
   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 }