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