blob: ee0d1a2a63075f6bfb9b35f34921b0531986d3ae [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2005 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
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<ListenerInfo>(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<ListenerInfo>(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 = (ListenerInfo)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<ListenerInfo>(listenerList);
110 final int size = newList.size();
111 for (int i = 0; i < size; i++) {
112 ListenerInfo li = (ListenerInfo) 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 = (ListenerInfo) 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 InternalError("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}