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