jdk/src/share/classes/javax/management/namespace/VirtualEventManager.java
changeset 1156 bbc2d15aaf7a
equal deleted inserted replaced
1155:a9a142fcf1b5 1156:bbc2d15aaf7a
       
     1 /*
       
     2  * Copyright 2008 Sun Microsystems, Inc.  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.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package javax.management.namespace;
       
    27 
       
    28 import com.sun.jmx.remote.util.ClassLogger;
       
    29 import java.util.ArrayList;
       
    30 import java.util.HashMap;
       
    31 import java.util.Iterator;
       
    32 import java.util.List;
       
    33 import java.util.Map;
       
    34 import javax.management.InstanceNotFoundException;
       
    35 import javax.management.ListenerNotFoundException;
       
    36 import javax.management.MBeanNotificationInfo;
       
    37 import javax.management.Notification;
       
    38 import javax.management.NotificationEmitter;
       
    39 import javax.management.NotificationFilter;
       
    40 import javax.management.NotificationListener;
       
    41 import javax.management.ObjectName;
       
    42 import javax.management.event.EventConsumer;
       
    43 
       
    44 /**
       
    45  * <p>This class maintains a list of subscribers for ObjectName patterns and
       
    46  * allows a notification to be sent to all subscribers for a given ObjectName.
       
    47  * It is typically used in conjunction with {@link MBeanServerSupport}
       
    48  * to implement a namespace with Virtual MBeans that can emit notifications.
       
    49  * The {@code VirtualEventManager} keeps track of the listeners that have been
       
    50  * added to each Virtual MBean. When an event occurs that should trigger a
       
    51  * notification from a Virtual MBean, the {@link #publish publish} method can
       
    52  * be used to send it to the appropriate listeners.</p>
       
    53  * @since 1.7
       
    54  */
       
    55 public class VirtualEventManager implements EventConsumer {
       
    56     /**
       
    57      * <p>Create a new {@code VirtualEventManager}.</p>
       
    58      */
       
    59     public VirtualEventManager() {
       
    60     }
       
    61 
       
    62     public void subscribe(
       
    63             ObjectName name,
       
    64             NotificationListener listener,
       
    65             NotificationFilter filter,
       
    66             Object handback) {
       
    67 
       
    68         if (logger.traceOn())
       
    69             logger.trace("subscribe", "" + name);
       
    70 
       
    71         if (name == null)
       
    72             throw new IllegalArgumentException("Null MBean name");
       
    73 
       
    74         if (listener == null)
       
    75             throw new IllegalArgumentException("Null listener");
       
    76 
       
    77         Map<ObjectName, List<ListenerInfo>> map =
       
    78                 name.isPattern() ? patternSubscriptionMap : exactSubscriptionMap;
       
    79 
       
    80         final ListenerInfo li = new ListenerInfo(listener, filter, handback);
       
    81         List<ListenerInfo> list;
       
    82 
       
    83         synchronized (map) {
       
    84             list = map.get(name);
       
    85             if (list == null) {
       
    86                 list = new ArrayList<ListenerInfo>();
       
    87                 map.put(name, list);
       
    88             }
       
    89             list.add(li);
       
    90         }
       
    91     }
       
    92 
       
    93     public void unsubscribe(
       
    94             ObjectName name, NotificationListener listener)
       
    95             throws ListenerNotFoundException {
       
    96 
       
    97         if (logger.traceOn())
       
    98             logger.trace("unsubscribe2", "" + name);
       
    99 
       
   100         if (name == null)
       
   101             throw new IllegalArgumentException("Null MBean name");
       
   102 
       
   103         if (listener == null)
       
   104             throw new ListenerNotFoundException();
       
   105 
       
   106         Map<ObjectName, List<ListenerInfo>> map =
       
   107                 name.isPattern() ? patternSubscriptionMap : exactSubscriptionMap;
       
   108 
       
   109         final ListenerInfo li = new ListenerInfo(listener, null, null);
       
   110         List<ListenerInfo> list;
       
   111         synchronized (map) {
       
   112             list = map.get(name);
       
   113             if (list == null || !list.remove(li))
       
   114                 throw new ListenerNotFoundException();
       
   115 
       
   116             if (list.isEmpty())
       
   117                 map.remove(name);
       
   118         }
       
   119     }
       
   120 
       
   121     /**
       
   122      * <p>Unsubscribes a listener which is listening to an MBean or a set of
       
   123      * MBeans represented by an {@code ObjectName} pattern.</p>
       
   124      *
       
   125      * <p>The listener to be removed must have been added by the {@link
       
   126      * #subscribe subscribe} method with the given {@code name}, {@code filter},
       
   127      * and {@code handback}. If the {@code
       
   128      * name} is a pattern, then the {@code subscribe} must have used the same
       
   129      * pattern. If the same listener has been subscribed more than once to the
       
   130      * {@code name} with the same filter and handback, only one listener is
       
   131      * removed.</p>
       
   132      *
       
   133      * @param name The name of the MBean or an {@code ObjectName} pattern
       
   134      * representing a set of MBeans to which the listener was subscribed.
       
   135      * @param listener A listener that was previously subscribed to the
       
   136      * MBean(s).
       
   137      *
       
   138      * @throws ListenerNotFoundException The given {@code listener} was not
       
   139      * subscribed to the given {@code name}.
       
   140      *
       
   141      * @see #subscribe
       
   142      */
       
   143     public void unsubscribe(
       
   144             ObjectName name, NotificationListener listener,
       
   145             NotificationFilter filter, Object handback)
       
   146             throws ListenerNotFoundException {
       
   147 
       
   148         if (logger.traceOn())
       
   149             logger.trace("unsubscribe4", "" + name);
       
   150 
       
   151         if (name == null)
       
   152             throw new IllegalArgumentException("Null MBean name");
       
   153 
       
   154         if (listener == null)
       
   155             throw new ListenerNotFoundException();
       
   156 
       
   157         Map<ObjectName, List<ListenerInfo>> map =
       
   158                 name.isPattern() ? patternSubscriptionMap : exactSubscriptionMap;
       
   159 
       
   160         List<ListenerInfo> list;
       
   161         synchronized (map) {
       
   162             list = map.get(name);
       
   163             boolean removed = false;
       
   164             for (Iterator<ListenerInfo> it = list.iterator(); it.hasNext(); ) {
       
   165                 ListenerInfo li = it.next();
       
   166                 if (li.equals(listener, filter, handback)) {
       
   167                     it.remove();
       
   168                     removed = true;
       
   169                     break;
       
   170                 }
       
   171             }
       
   172             if (!removed)
       
   173                 throw new ListenerNotFoundException();
       
   174 
       
   175             if (list.isEmpty())
       
   176                 map.remove(name);
       
   177         }
       
   178     }
       
   179 
       
   180     /**
       
   181      * <p>Sends a notification to the subscribers for a given MBean.</p>
       
   182      *
       
   183      * <p>For each listener subscribed with an {@code ObjectName} that either
       
   184      * is equal to {@code emitterName} or is a pattern that matches {@code
       
   185      * emitterName}, if the associated filter accepts the notification then it
       
   186      * is forwarded to the listener.</p>
       
   187      *
       
   188      * @param emitterName The name of the MBean emitting the notification.
       
   189      * @param n The notification being sent by the MBean called
       
   190      * {@code emitterName}.
       
   191      *
       
   192      * @throws IllegalArgumentException If the emitterName of the
       
   193      * notification is null or is an {@code ObjectName} pattern.
       
   194      */
       
   195     public void publish(ObjectName emitterName, Notification n) {
       
   196         if (logger.traceOn())
       
   197             logger.trace("publish", "" + emitterName);
       
   198 
       
   199         if (n == null)
       
   200             throw new IllegalArgumentException("Null notification");
       
   201 
       
   202         if (emitterName == null) {
       
   203             throw new IllegalArgumentException(
       
   204                     "Null emitter name");
       
   205         } else if (emitterName.isPattern()) {
       
   206             throw new IllegalArgumentException(
       
   207                     "The emitter must not be an ObjectName pattern");
       
   208         }
       
   209 
       
   210         final List<ListenerInfo> listeners = new ArrayList<ListenerInfo>();
       
   211 
       
   212         // If there are listeners for this exact name, add them.
       
   213         synchronized (exactSubscriptionMap) {
       
   214             List<ListenerInfo> exactListeners =
       
   215                     exactSubscriptionMap.get(emitterName);
       
   216             if (exactListeners != null)
       
   217                 listeners.addAll(exactListeners);
       
   218         }
       
   219 
       
   220         // Loop over subscription patterns, and add all listeners for each
       
   221         // one that matches the emitterName name.
       
   222         synchronized (patternSubscriptionMap) {
       
   223             for (ObjectName on : patternSubscriptionMap.keySet()) {
       
   224                 if (on.apply(emitterName))
       
   225                     listeners.addAll(patternSubscriptionMap.get(on));
       
   226             }
       
   227         }
       
   228 
       
   229         // Send the notification to all the listeners we found.
       
   230         sendNotif(listeners, n);
       
   231     }
       
   232 
       
   233     /**
       
   234      * <p>Returns a {@link NotificationEmitter} object which can be used to
       
   235      * subscribe or unsubscribe for notifications with the named
       
   236      * mbean.  The returned object implements {@link
       
   237      * NotificationEmitter#addNotificationListener
       
   238      * addNotificationListener(listener, filter, handback)} as
       
   239      * {@link #subscribe this.subscribe(name, listener, filter, handback)}
       
   240      * and the two {@code removeNotificationListener} methods from {@link
       
   241      * NotificationEmitter} as the corresponding {@code unsubscribe} methods
       
   242      * from this class.</p>
       
   243      *
       
   244      * @param name   The name of the MBean whose notifications are being
       
   245      *        subscribed, or unsuscribed.
       
   246      *
       
   247      * @return A {@link NotificationEmitter}
       
   248      *         that can be used to subscribe or unsubscribe for
       
   249      *         notifications emitted by the named MBean, or {@code null} if
       
   250      *         the MBean does not emit notifications and should not
       
   251      *         be considered as a {@code NotificationBroadcaster}.  This class
       
   252      *         never returns null but a subclass is allowed to.
       
   253      *
       
   254      * @throws InstanceNotFoundException if {@code name} does not exist.
       
   255      * This implementation never throws {@code InstanceNotFoundException} but
       
   256      * a subclass is allowed to override this method to do so.
       
   257      */
       
   258     public NotificationEmitter
       
   259             getNotificationEmitterFor(final ObjectName name)
       
   260             throws InstanceNotFoundException {
       
   261         final NotificationEmitter emitter = new NotificationEmitter() {
       
   262             public void addNotificationListener(NotificationListener listener,
       
   263                     NotificationFilter filter, Object handback)
       
   264                     throws IllegalArgumentException {
       
   265                 subscribe(name, listener, filter, handback);
       
   266             }
       
   267 
       
   268             public void removeNotificationListener(
       
   269                     NotificationListener listener)
       
   270                     throws ListenerNotFoundException {
       
   271                 unsubscribe(name, listener);
       
   272             }
       
   273 
       
   274             public void removeNotificationListener(NotificationListener listener,
       
   275                                                    NotificationFilter filter,
       
   276                                                    Object handback)
       
   277                     throws ListenerNotFoundException {
       
   278                 unsubscribe(name, listener, filter, handback);
       
   279             }
       
   280 
       
   281             public MBeanNotificationInfo[] getNotificationInfo() {
       
   282                 // Never called.
       
   283                 return null;
       
   284             }
       
   285         };
       
   286         return emitter;
       
   287     }
       
   288 
       
   289     // ---------------------------------
       
   290     // private stuff
       
   291     // ---------------------------------
       
   292 
       
   293     private static class ListenerInfo {
       
   294         public final NotificationListener listener;
       
   295         public final NotificationFilter filter;
       
   296         public final Object handback;
       
   297 
       
   298         public ListenerInfo(NotificationListener listener,
       
   299                 NotificationFilter filter,
       
   300                 Object handback) {
       
   301 
       
   302             if (listener == null) {
       
   303                 throw new IllegalArgumentException("Null listener.");
       
   304             }
       
   305 
       
   306             this.listener = listener;
       
   307             this.filter = filter;
       
   308             this.handback = handback;
       
   309         }
       
   310 
       
   311         /* Two ListenerInfo instances are equal if they have the same
       
   312          * NotificationListener.  This means that we can use List.remove
       
   313          * to implement the two-argument removeNotificationListener.
       
   314          */
       
   315         @Override
       
   316         public boolean equals(Object o) {
       
   317             if (o == this) {
       
   318                 return true;
       
   319             }
       
   320 
       
   321             if (!(o instanceof ListenerInfo)) {
       
   322                 return false;
       
   323             }
       
   324 
       
   325             return listener.equals(((ListenerInfo)o).listener);
       
   326         }
       
   327 
       
   328         /* Method that compares all four fields, appropriate for the
       
   329          * four-argument removeNotificationListener.
       
   330          */
       
   331         boolean equals(
       
   332                 NotificationListener listener,
       
   333                 NotificationFilter filter,
       
   334                 Object handback) {
       
   335             return (this.listener == listener && same(this.filter, filter)
       
   336                     && same(this.handback, handback));
       
   337         }
       
   338 
       
   339         private static boolean same(Object x, Object y) {
       
   340             if (x == y)
       
   341                 return true;
       
   342             if (x == null)
       
   343                 return false;
       
   344             return x.equals(y);
       
   345         }
       
   346 
       
   347         @Override
       
   348         public int hashCode() {
       
   349             return listener.hashCode();
       
   350         }
       
   351     }
       
   352 
       
   353     private static void sendNotif(List<ListenerInfo> listeners, Notification n) {
       
   354         for (ListenerInfo li : listeners) {
       
   355             if (li.filter == null ||
       
   356                     li.filter.isNotificationEnabled(n)) {
       
   357                 try {
       
   358                     li.listener.handleNotification(n, li.handback);
       
   359                 } catch (Exception e) {
       
   360                     logger.trace("sendNotif", "handleNotification", e);
       
   361                 }
       
   362             }
       
   363         }
       
   364     }
       
   365 
       
   366     // ---------------------------------
       
   367     // private variables
       
   368     // ---------------------------------
       
   369 
       
   370     private final Map<ObjectName, List<ListenerInfo>> exactSubscriptionMap =
       
   371             new HashMap<ObjectName, List<ListenerInfo>>();
       
   372     private final Map<ObjectName, List<ListenerInfo>> patternSubscriptionMap =
       
   373             new HashMap<ObjectName, List<ListenerInfo>>();
       
   374 
       
   375     // trace issue
       
   376     private static final ClassLogger logger =
       
   377             new ClassLogger("javax.management.event", "EventManager");
       
   378 }