src/java.management/share/classes/sun/management/NotificationEmitterSupport.java
changeset 47216 71c04702a3d5
parent 30355 e37c7eba132f
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2003, 2015, 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 sun.management;
       
    27 
       
    28 import javax.management.ListenerNotFoundException;
       
    29 import javax.management.MBeanNotificationInfo;
       
    30 import javax.management.Notification;
       
    31 import javax.management.NotificationEmitter;
       
    32 import javax.management.NotificationFilter;
       
    33 import javax.management.NotificationListener;
       
    34 
       
    35 import java.util.List;
       
    36 import java.util.ArrayList;
       
    37 import java.util.Collections;
       
    38 
       
    39 /**
       
    40  * Abstract helper class for notification emitter support.
       
    41  */
       
    42 public abstract class NotificationEmitterSupport implements NotificationEmitter {
       
    43 
       
    44     protected NotificationEmitterSupport() {
       
    45     }
       
    46 
       
    47     private Object listenerLock = new Object();
       
    48 
       
    49     // Implementation of NotificationEmitter interface
       
    50     // Cloned from JMX NotificationBroadcasterSupport class.
       
    51     public void addNotificationListener(NotificationListener listener,
       
    52                                         NotificationFilter filter,
       
    53                                         Object handback) {
       
    54 
       
    55         if (listener == null) {
       
    56             throw new IllegalArgumentException ("Listener can't be null") ;
       
    57         }
       
    58 
       
    59         /* Adding a new listener takes O(n) time where n is the number
       
    60            of existing listeners.  If you have a very large number of
       
    61            listeners performance could degrade.  That's a fairly
       
    62            surprising configuration, and it is hard to avoid this
       
    63            behaviour while still retaining the property that the
       
    64            listenerList is not synchronized while notifications are
       
    65            being sent through it.  If this becomes a problem, a
       
    66            possible solution would be a multiple-readers single-writer
       
    67            setup, so any number of sendNotification() calls could run
       
    68            concurrently but they would exclude an
       
    69            add/removeNotificationListener.  A simpler but less
       
    70            efficient solution would be to clone the listener list
       
    71            every time a notification is sent.  */
       
    72         synchronized (listenerLock) {
       
    73             List<ListenerInfo> newList = new ArrayList<>(listenerList.size() + 1);
       
    74             newList.addAll(listenerList);
       
    75             newList.add(new ListenerInfo(listener, filter, handback));
       
    76             listenerList = newList;
       
    77         }
       
    78     }
       
    79 
       
    80     public void removeNotificationListener(NotificationListener listener)
       
    81         throws ListenerNotFoundException {
       
    82 
       
    83         synchronized (listenerLock) {
       
    84             List<ListenerInfo> newList = new ArrayList<>(listenerList);
       
    85             /* We scan the list of listeners in reverse order because
       
    86                in forward order we would have to repeat the loop with
       
    87                the same index after a remove.  */
       
    88             for (int i=newList.size()-1; i>=0; i--) {
       
    89                 ListenerInfo li = newList.get(i);
       
    90 
       
    91                 if (li.listener == listener)
       
    92                     newList.remove(i);
       
    93             }
       
    94             if (newList.size() == listenerList.size())
       
    95                 throw new ListenerNotFoundException("Listener not registered");
       
    96             listenerList = newList;
       
    97         }
       
    98     }
       
    99 
       
   100     public void removeNotificationListener(NotificationListener listener,
       
   101                                            NotificationFilter filter,
       
   102                                            Object handback)
       
   103             throws ListenerNotFoundException {
       
   104 
       
   105         boolean found = false;
       
   106 
       
   107         synchronized (listenerLock) {
       
   108             List<ListenerInfo> newList = new ArrayList<>(listenerList);
       
   109             final int size = newList.size();
       
   110             for (int i = 0; i < size; i++) {
       
   111                 ListenerInfo li =  newList.get(i);
       
   112 
       
   113                 if (li.listener == listener) {
       
   114                     found = true;
       
   115                     if (li.filter == filter
       
   116                         && li.handback == handback) {
       
   117                         newList.remove(i);
       
   118                         listenerList = newList;
       
   119                         return;
       
   120                     }
       
   121                 }
       
   122             }
       
   123         }
       
   124 
       
   125         if (found) {
       
   126             /* We found this listener, but not with the given filter
       
   127              * and handback.  A more informative exception message may
       
   128              * make debugging easier.  */
       
   129             throw new ListenerNotFoundException("Listener not registered " +
       
   130                                                 "with this filter and " +
       
   131                                                 "handback");
       
   132         } else {
       
   133             throw new ListenerNotFoundException("Listener not registered");
       
   134         }
       
   135     }
       
   136 
       
   137     public void sendNotification(Notification notification) {
       
   138 
       
   139         if (notification == null) {
       
   140             return;
       
   141         }
       
   142 
       
   143         List<ListenerInfo> currentList;
       
   144         synchronized (listenerLock) {
       
   145             currentList = listenerList;
       
   146         }
       
   147 
       
   148         final int size = currentList.size();
       
   149         for (int i = 0; i < size; i++) {
       
   150             ListenerInfo li =  currentList.get(i);
       
   151 
       
   152             if (li.filter == null
       
   153                 || li.filter.isNotificationEnabled(notification)) {
       
   154                 try {
       
   155                     li.listener.handleNotification(notification, li.handback);
       
   156                 } catch (Exception e) {
       
   157                     e.printStackTrace();
       
   158                     throw new AssertionError("Error in invoking listener");
       
   159                 }
       
   160             }
       
   161         }
       
   162     }
       
   163 
       
   164     public boolean hasListeners() {
       
   165         synchronized (listenerLock) {
       
   166             return !listenerList.isEmpty();
       
   167         }
       
   168     }
       
   169 
       
   170     private class ListenerInfo {
       
   171         public NotificationListener listener;
       
   172         NotificationFilter filter;
       
   173         Object handback;
       
   174 
       
   175         public ListenerInfo(NotificationListener listener,
       
   176                             NotificationFilter filter,
       
   177                             Object handback) {
       
   178             this.listener = listener;
       
   179             this.filter = filter;
       
   180             this.handback = handback;
       
   181         }
       
   182     }
       
   183 
       
   184     /**
       
   185      * Current list of listeners, a List of ListenerInfo.  The object
       
   186      * referenced by this field is never modified.  Instead, the field
       
   187      * is set to a new object when a listener is added or removed,
       
   188      * within a synchronized(this).  In this way, there is no need to
       
   189      * synchronize when traversing the list to send a notification to
       
   190      * the listeners in it.  That avoids potential deadlocks if the
       
   191      * listeners end up depending on other threads that are themselves
       
   192      * accessing this NotificationBroadcasterSupport.
       
   193      */
       
   194     private List<ListenerInfo> listenerList = Collections.emptyList();
       
   195 
       
   196     abstract public MBeanNotificationInfo[] getNotificationInfo();
       
   197 }