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 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.Collections; 38 39/** 40 * Abstract helper class for notification emitter support. 41 */ 42public 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} 198