author | martin |
Thu, 30 Oct 2014 07:31:41 -0700 | |
changeset 28059 | e576535359cc |
parent 25859 | 3317bb8137f4 |
child 30655 | d83f50188ca9 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
10324
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
2 |
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. |
2 | 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 |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 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 |
* |
|
5506 | 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. |
|
2 | 24 |
*/ |
25 |
||
26 |
package com.sun.jndi.ldap; |
|
27 |
||
28 |
import java.util.Hashtable; |
|
29 |
import java.util.Vector; |
|
30 |
import java.util.EventObject; |
|
31 |
||
32 |
import javax.naming.*; |
|
33 |
import javax.naming.event.*; |
|
34 |
import javax.naming.directory.SearchControls; |
|
35 |
import javax.naming.ldap.UnsolicitedNotificationListener; |
|
36 |
import javax.naming.ldap.UnsolicitedNotificationEvent; |
|
37 |
import javax.naming.ldap.UnsolicitedNotification; |
|
38 |
||
39 |
/** |
|
40 |
* This is a utility class that can be used by a context that supports |
|
41 |
* event notification. You can use an instance of this class as a member field |
|
42 |
* of your context and delegate various work to it. |
|
43 |
* It is currently structured so that each context should have its own |
|
44 |
* EventSupport (instead of static version shared by all contexts |
|
45 |
* of a service provider). |
|
46 |
*<p> |
|
47 |
* This class supports two types of listeners: those that register for |
|
48 |
* NamingEvents, and those for UnsolicitedNotificationEvents (they can be mixed |
|
49 |
* into the same listener). |
|
50 |
* For NamingEvent listeners, it maintains a hashtable that maps |
|
51 |
* registration requests--the key--to |
|
52 |
* <em>notifiers</em>--the value. Each registration request consists of: |
|
53 |
*<ul> |
|
54 |
*<li>The name argument of the registration. |
|
55 |
*<li>The filter (default is "(objectclass=*)"). |
|
56 |
*<li>The search controls (default is null SearchControls). |
|
57 |
*<li>The events that the listener is interested in. This is determined by |
|
58 |
* finding out which <tt>NamingListener</tt> interface the listener supports. |
|
59 |
*</ul> |
|
60 |
*<p> |
|
61 |
*A notifier (<tt>NamingEventNotifier</tt>) is a worker thread that is responsible |
|
62 |
*for gathering information for generating events requested by its listeners. |
|
63 |
*Each notifier maintains its own list of listeners; these listeners have |
|
64 |
*all made the same registration request (at different times) and implements |
|
65 |
*the same <tt>NamingListener</tt> interfaces. |
|
66 |
*<p> |
|
67 |
*For unsolicited listeners, this class maintains a vector, unsolicited. |
|
68 |
*When an unsolicited listener is registered, this class adds itself |
|
69 |
*to the context's LdapClient. When LdapClient receives an unsolicited |
|
70 |
*notification, it notifies this EventSupport to fire an event to the |
|
71 |
*the listeners. Special handling in LdapClient is done for the DISCONNECT |
|
72 |
*notification. [It results in the EventSupport firing also a |
|
73 |
*NamingExceptionEvent to the unsolicited listeners.] |
|
74 |
*<p> |
|
75 |
* |
|
76 |
*When a context no longer needs this EventSupport, it should invoke |
|
77 |
*cleanup() on it. |
|
78 |
*<p> |
|
79 |
*<h4>Registration</h4> |
|
80 |
*When a registration request is made, this class attempts to find an |
|
81 |
*existing notifier that's already working on the request. If one is |
|
82 |
*found, the listener is added to the notifier's list. If one is not found, |
|
83 |
*a new notifier is created for the listener. |
|
84 |
* |
|
85 |
*<h4>Deregistration</h4> |
|
25808 | 86 |
*When a deregistration request is made, this class attempts to find its |
2 | 87 |
*corresponding notifier. If the notifier is found, the listener is removed |
88 |
*from the notifier's list. If the listener is the last listener on the list, |
|
89 |
*the notifier's thread is terminated and removed from this class's hashtable. |
|
90 |
*Nothing happens if the notifier is not found. |
|
91 |
* |
|
92 |
*<h4>Event Dispatching</h4> |
|
93 |
*The notifiers are responsible for gather information for generating events |
|
94 |
*requested by their respective listeners. When a notifier gets sufficient |
|
95 |
*information to generate an event, it creates invokes the |
|
96 |
*appropriate <tt>fireXXXEvent</tt> on this class with the information and list of |
|
97 |
*listeners. This causes an event and the list of listeners to be added |
|
98 |
*to the <em>event queue</em>. |
|
99 |
*This class maintains an event queue and a dispatching thread that dequeues |
|
100 |
*events from the queue and dispatches them to the listeners. |
|
101 |
* |
|
102 |
*<h4>Synchronization</h4> |
|
103 |
*This class is used by the main thread (LdapCtx) to add/remove listeners. |
|
104 |
*It is also used asynchronously by NamingEventNotifiers threads and |
|
105 |
*the context's Connection thread. It is used by the notifier threads to |
|
106 |
*queue events and to update the notifiers list when the notifiers exit. |
|
107 |
*It is used by the Connection thread to fire unsolicited notifications. |
|
108 |
*Methods that access/update the 'unsolicited' and 'notifiers' lists are |
|
109 |
*thread-safe. |
|
110 |
* |
|
111 |
* @author Rosanna Lee |
|
112 |
*/ |
|
113 |
final class EventSupport { |
|
114 |
final static private boolean debug = false; |
|
115 |
||
116 |
private LdapCtx ctx; |
|
117 |
||
118 |
/** |
|
119 |
* NamingEventNotifiers; hashed by search arguments; |
|
120 |
*/ |
|
10324
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
121 |
private Hashtable<NotifierArgs, NamingEventNotifier> notifiers = |
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
122 |
new Hashtable<>(11); |
2 | 123 |
|
124 |
/** |
|
125 |
* List of unsolicited notification listeners. |
|
126 |
*/ |
|
10324
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
127 |
private Vector<UnsolicitedNotificationListener> unsolicited = null; |
2 | 128 |
|
129 |
/** |
|
130 |
* Constructs EventSupport for ctx. |
|
131 |
* <em>Do we need to record the name of the target context? |
|
132 |
* Or can we assume that EventSupport is called on a resolved |
|
133 |
* context? Do we need other add/remove-NamingListener methods? |
|
134 |
* package private; |
|
135 |
*/ |
|
136 |
EventSupport(LdapCtx ctx) { |
|
137 |
this.ctx = ctx; |
|
138 |
} |
|
139 |
||
140 |
/** |
|
141 |
* Adds <tt>l</tt> to list of listeners interested in <tt>nm</tt>. |
|
142 |
*/ |
|
143 |
/* |
|
144 |
* Make the add/removeNamingListeners synchronized to: |
|
145 |
* 1. protect usage of 'unsolicited', which may be read by |
|
146 |
* the Connection thread when dispatching unsolicited notification. |
|
147 |
* 2. ensure that NamingEventNotifier thread's access to 'notifiers' |
|
148 |
* is safe |
|
149 |
*/ |
|
150 |
synchronized void addNamingListener(String nm, int scope, |
|
151 |
NamingListener l) throws NamingException { |
|
152 |
||
153 |
if (l instanceof ObjectChangeListener || |
|
154 |
l instanceof NamespaceChangeListener) { |
|
155 |
NotifierArgs args = new NotifierArgs(nm, scope, l); |
|
156 |
||
10324
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
157 |
NamingEventNotifier notifier = notifiers.get(args); |
2 | 158 |
if (notifier == null) { |
159 |
notifier = new NamingEventNotifier(this, ctx, args, l); |
|
160 |
notifiers.put(args, notifier); |
|
161 |
} else { |
|
162 |
notifier.addNamingListener(l); |
|
163 |
} |
|
164 |
} |
|
165 |
if (l instanceof UnsolicitedNotificationListener) { |
|
166 |
// Add listener to this's list of unsolicited notifiers |
|
167 |
if (unsolicited == null) { |
|
10324
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
168 |
unsolicited = new Vector<>(3); |
2 | 169 |
} |
170 |
||
10324
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
171 |
unsolicited.addElement((UnsolicitedNotificationListener)l); |
2 | 172 |
} |
173 |
} |
|
174 |
||
175 |
/** |
|
176 |
* Adds <tt>l</tt> to list of listeners interested in <tt>nm</tt> |
|
177 |
* and filter. |
|
178 |
*/ |
|
179 |
synchronized void addNamingListener(String nm, String filter, |
|
180 |
SearchControls ctls, NamingListener l) throws NamingException { |
|
181 |
||
182 |
if (l instanceof ObjectChangeListener || |
|
183 |
l instanceof NamespaceChangeListener) { |
|
184 |
NotifierArgs args = new NotifierArgs(nm, filter, ctls, l); |
|
185 |
||
10324
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
186 |
NamingEventNotifier notifier = notifiers.get(args); |
2 | 187 |
if (notifier == null) { |
188 |
notifier = new NamingEventNotifier(this, ctx, args, l); |
|
189 |
notifiers.put(args, notifier); |
|
190 |
} else { |
|
191 |
notifier.addNamingListener(l); |
|
192 |
} |
|
193 |
} |
|
194 |
if (l instanceof UnsolicitedNotificationListener) { |
|
195 |
// Add listener to this's list of unsolicited notifiers |
|
196 |
if (unsolicited == null) { |
|
10324
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
197 |
unsolicited = new Vector<>(3); |
2 | 198 |
} |
10324
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
199 |
unsolicited.addElement((UnsolicitedNotificationListener)l); |
2 | 200 |
} |
201 |
} |
|
202 |
||
203 |
/** |
|
204 |
* Removes <tt>l</tt> from all notifiers in this context. |
|
205 |
*/ |
|
206 |
synchronized void removeNamingListener(NamingListener l) { |
|
207 |
if (debug) System.err.println("EventSupport removing listener"); |
|
208 |
||
209 |
// Go through list of notifiers, remove 'l' from each. |
|
210 |
// If 'l' is notifier's only listener, remove notifier too. |
|
10324
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
211 |
for (NamingEventNotifier notifier : notifiers.values()) { |
2 | 212 |
if (notifier != null) { |
213 |
if (debug) |
|
214 |
System.err.println("EventSupport removing listener from notifier"); |
|
215 |
notifier.removeNamingListener(l); |
|
216 |
if (!notifier.hasNamingListeners()) { |
|
217 |
if (debug) |
|
218 |
System.err.println("EventSupport stopping notifier"); |
|
219 |
notifier.stop(); |
|
220 |
notifiers.remove(notifier.info); |
|
221 |
} |
|
222 |
} |
|
223 |
} |
|
224 |
||
225 |
// Remove from list of unsolicited notifier |
|
226 |
if (debug) System.err.println("EventSupport removing unsolicited: " + |
|
227 |
unsolicited); |
|
228 |
if (unsolicited != null) { |
|
229 |
unsolicited.removeElement(l); |
|
230 |
} |
|
231 |
||
232 |
} |
|
233 |
||
234 |
synchronized boolean hasUnsolicited() { |
|
235 |
return (unsolicited != null && unsolicited.size() > 0); |
|
236 |
} |
|
237 |
||
238 |
/** |
|
239 |
* package private; |
|
240 |
* Called by NamingEventNotifier to remove itself when it encounters |
|
241 |
* a NamingException. |
|
242 |
*/ |
|
243 |
synchronized void removeDeadNotifier(NotifierArgs info) { |
|
244 |
if (debug) { |
|
245 |
System.err.println("EventSupport.removeDeadNotifier: " + info.name); |
|
246 |
} |
|
247 |
notifiers.remove(info); |
|
248 |
} |
|
249 |
||
250 |
/** |
|
251 |
* Fire an event to unsolicited listeners. |
|
252 |
* package private; |
|
253 |
* Called by LdapCtx when its clnt receives an unsolicited notification. |
|
254 |
*/ |
|
255 |
synchronized void fireUnsolicited(Object obj) { |
|
256 |
if (debug) { |
|
257 |
System.err.println("EventSupport.fireUnsolicited: " + obj + " " |
|
258 |
+ unsolicited); |
|
259 |
} |
|
260 |
if (unsolicited == null || unsolicited.size() == 0) { |
|
261 |
// This shouldn't really happen, but might in case |
|
262 |
// there is a timing problem that removes a listener |
|
28059
e576535359cc
8067377: My hobby: caning, then then canning, the the can-can
martin
parents:
25859
diff
changeset
|
263 |
// before a fired event reaches here. |
2 | 264 |
return; |
265 |
} |
|
266 |
||
267 |
if (obj instanceof UnsolicitedNotification) { |
|
268 |
||
269 |
// Fire UnsolicitedNotification to unsolicited listeners |
|
270 |
||
271 |
UnsolicitedNotificationEvent evt = |
|
272 |
new UnsolicitedNotificationEvent(ctx, (UnsolicitedNotification)obj); |
|
273 |
queueEvent(evt, unsolicited); |
|
274 |
||
275 |
} else if (obj instanceof NamingException) { |
|
276 |
||
277 |
// Fire NamingExceptionEvent to unsolicited listeners. |
|
278 |
||
279 |
NamingExceptionEvent evt = |
|
280 |
new NamingExceptionEvent(ctx, (NamingException)obj); |
|
281 |
queueEvent(evt, unsolicited); |
|
282 |
||
283 |
// When an exception occurs, the unsolicited listeners |
|
284 |
// are automatically deregistered. |
|
285 |
// When LdapClient.processUnsolicited() fires a NamingException, |
|
286 |
// it will update its listener list so we don't have to. |
|
287 |
// Likewise for LdapCtx. |
|
288 |
||
289 |
unsolicited = null; |
|
290 |
} |
|
291 |
} |
|
292 |
||
293 |
/** |
|
294 |
* Stops notifier threads that are collecting event data and |
|
295 |
* stops the event queue from dispatching events. |
|
296 |
* Package private; used by LdapCtx. |
|
297 |
*/ |
|
298 |
synchronized void cleanup() { |
|
299 |
if (debug) System.err.println("EventSupport clean up"); |
|
300 |
if (notifiers != null) { |
|
10324
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
301 |
for (NamingEventNotifier notifier : notifiers.values()) { |
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
302 |
notifier.stop(); |
2 | 303 |
} |
304 |
notifiers = null; |
|
305 |
} |
|
306 |
if (eventQueue != null) { |
|
307 |
eventQueue.stop(); |
|
308 |
eventQueue = null; |
|
309 |
} |
|
310 |
// %%% Should we fire NamingExceptionEvents to unsolicited listeners? |
|
311 |
} |
|
312 |
||
313 |
/* |
|
314 |
* The queue of events to be delivered. |
|
315 |
*/ |
|
316 |
private EventQueue eventQueue; |
|
317 |
||
318 |
/** |
|
319 |
* Add the event and vector of listeners to the queue to be delivered. |
|
320 |
* An event dispatcher thread dequeues events from the queue and dispatches |
|
321 |
* them to the registered listeners. |
|
322 |
* Package private; used by NamingEventNotifier to fire events |
|
323 |
*/ |
|
10324
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
324 |
synchronized void queueEvent(EventObject event, |
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
325 |
Vector<? extends NamingListener> vector) { |
2 | 326 |
if (eventQueue == null) |
327 |
eventQueue = new EventQueue(); |
|
328 |
||
329 |
/* |
|
330 |
* Copy the vector in order to freeze the state of the set |
|
331 |
* of EventListeners the event should be delivered to prior |
|
332 |
* to delivery. This ensures that any changes made to the |
|
333 |
* Vector from a target listener's method during the delivery |
|
334 |
* of this event will not take effect until after the event is |
|
335 |
* delivered. |
|
336 |
*/ |
|
10324
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
337 |
@SuppressWarnings("unchecked") // clone() |
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
338 |
Vector<NamingListener> v = |
e28265130e4f
7072353: JNDI libraries do not build with javac -Xlint:all -Werror
jjg
parents:
5506
diff
changeset
|
339 |
(Vector<NamingListener>)vector.clone(); |
2 | 340 |
eventQueue.enqueue(event, v); |
341 |
} |
|
342 |
||
343 |
// No finalize() needed because EventSupport is always owned by |
|
344 |
// an LdapCtx. LdapCtx's finalize() and close() always call cleanup() so |
|
345 |
// there is no need for EventSupport to have a finalize(). |
|
346 |
} |