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