001: /*
002: * Copyright 1999-2000 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.jndi.ldap;
027:
028: import java.util.Hashtable;
029: import java.util.Vector;
030: import java.util.Enumeration;
031: import java.util.EventObject;
032:
033: import javax.naming.*;
034: import javax.naming.directory.*;
035: import javax.naming.event.*;
036: import javax.naming.directory.SearchControls;
037: import javax.naming.ldap.UnsolicitedNotificationListener;
038: import javax.naming.ldap.UnsolicitedNotificationEvent;
039: import javax.naming.ldap.UnsolicitedNotification;
040:
041: /**
042: * This is a utility class that can be used by a context that supports
043: * event notification. You can use an instance of this class as a member field
044: * of your context and delegate various work to it.
045: * It is currently structured so that each context should have its own
046: * EventSupport (instead of static version shared by all contexts
047: * of a service provider).
048: *<p>
049: * This class supports two types of listeners: those that register for
050: * NamingEvents, and those for UnsolicitedNotificationEvents (they can be mixed
051: * into the same listener).
052: * For NamingEvent listeners, it maintains a hashtable that maps
053: * registration requests--the key--to
054: * <em>notifiers</em>--the value. Each registration request consists of:
055: *<ul>
056: *<li>The name argument of the registration.
057: *<li>The filter (default is "(objectclass=*)").
058: *<li>The search controls (default is null SearchControls).
059: *<li>The events that the listener is interested in. This is determined by
060: * finding out which <tt>NamingListener</tt> interface the listener supports.
061: *</ul>
062: *<p>
063: *A notifier (<tt>NamingEventNotifier</tt>) is a worker thread that is responsible
064: *for gathering information for generating events requested by its listeners.
065: *Each notifier maintains its own list of listeners; these listeners have
066: *all made the same registration request (at different times) and implements
067: *the same <tt>NamingListener</tt> interfaces.
068: *<p>
069: *For unsolicited listeners, this class maintains a vector, unsolicited.
070: *When an unsolicited listener is registered, this class adds itself
071: *to the context's LdapClient. When LdapClient receives an unsolicited
072: *notification, it notifies this EventSupport to fire an event to the
073: *the listeners. Special handling in LdapClient is done for the DISCONNECT
074: *notification. [It results in the EventSupport firing also a
075: *NamingExceptionEvent to the unsolicited listeners.]
076: *<p>
077: *
078: *When a context no longer needs this EventSupport, it should invoke
079: *cleanup() on it.
080: *<p>
081: *<h4>Registration</h4>
082: *When a registration request is made, this class attempts to find an
083: *existing notifier that's already working on the request. If one is
084: *found, the listener is added to the notifier's list. If one is not found,
085: *a new notifier is created for the listener.
086: *
087: *<h4>Deregistration</h4>
088: *When a deregistration request is made, this class attemps to find its
089: *corresponding notifier. If the notifier is found, the listener is removed
090: *from the notifier's list. If the listener is the last listener on the list,
091: *the notifier's thread is terminated and removed from this class's hashtable.
092: *Nothing happens if the notifier is not found.
093: *
094: *<h4>Event Dispatching</h4>
095: *The notifiers are responsible for gather information for generating events
096: *requested by their respective listeners. When a notifier gets sufficient
097: *information to generate an event, it creates invokes the
098: *appropriate <tt>fireXXXEvent</tt> on this class with the information and list of
099: *listeners. This causes an event and the list of listeners to be added
100: *to the <em>event queue</em>.
101: *This class maintains an event queue and a dispatching thread that dequeues
102: *events from the queue and dispatches them to the listeners.
103: *
104: *<h4>Synchronization</h4>
105: *This class is used by the main thread (LdapCtx) to add/remove listeners.
106: *It is also used asynchronously by NamingEventNotifiers threads and
107: *the context's Connection thread. It is used by the notifier threads to
108: *queue events and to update the notifiers list when the notifiers exit.
109: *It is used by the Connection thread to fire unsolicited notifications.
110: *Methods that access/update the 'unsolicited' and 'notifiers' lists are
111: *thread-safe.
112: *
113: * @author Rosanna Lee
114: */
115: final class EventSupport {
116: final static private boolean debug = false;
117:
118: private LdapCtx ctx;
119:
120: /**
121: * NamingEventNotifiers; hashed by search arguments;
122: */
123: private Hashtable notifiers = new Hashtable(11);
124:
125: /**
126: * List of unsolicited notification listeners.
127: */
128: private Vector unsolicited = null;
129:
130: /**
131: * Constructs EventSupport for ctx.
132: * <em>Do we need to record the name of the target context?
133: * Or can we assume that EventSupport is called on a resolved
134: * context? Do we need other add/remove-NamingListener methods?
135: * package private;
136: */
137: EventSupport(LdapCtx ctx) {
138: this .ctx = ctx;
139: }
140:
141: /**
142: * Adds <tt>l</tt> to list of listeners interested in <tt>nm</tt>.
143: */
144: /*
145: * Make the add/removeNamingListeners synchronized to:
146: * 1. protect usage of 'unsolicited', which may be read by
147: * the Connection thread when dispatching unsolicited notification.
148: * 2. ensure that NamingEventNotifier thread's access to 'notifiers'
149: * is safe
150: */
151: synchronized void addNamingListener(String nm, int scope,
152: NamingListener l) throws NamingException {
153:
154: if (l instanceof ObjectChangeListener
155: || l instanceof NamespaceChangeListener) {
156: NotifierArgs args = new NotifierArgs(nm, scope, l);
157:
158: NamingEventNotifier notifier = (NamingEventNotifier) notifiers
159: .get(args);
160: if (notifier == null) {
161: notifier = new NamingEventNotifier(this , ctx, args, l);
162: notifiers.put(args, notifier);
163: } else {
164: notifier.addNamingListener(l);
165: }
166: }
167: if (l instanceof UnsolicitedNotificationListener) {
168: // Add listener to this's list of unsolicited notifiers
169: if (unsolicited == null) {
170: unsolicited = new Vector(3);
171: }
172:
173: unsolicited.addElement(l);
174: }
175: }
176:
177: /**
178: * Adds <tt>l</tt> to list of listeners interested in <tt>nm</tt>
179: * and filter.
180: */
181: synchronized void addNamingListener(String nm, String filter,
182: SearchControls ctls, NamingListener l)
183: throws NamingException {
184:
185: if (l instanceof ObjectChangeListener
186: || l instanceof NamespaceChangeListener) {
187: NotifierArgs args = new NotifierArgs(nm, filter, ctls, l);
188:
189: NamingEventNotifier notifier = (NamingEventNotifier) notifiers
190: .get(args);
191: if (notifier == null) {
192: notifier = new NamingEventNotifier(this , ctx, args, l);
193: notifiers.put(args, notifier);
194: } else {
195: notifier.addNamingListener(l);
196: }
197: }
198: if (l instanceof UnsolicitedNotificationListener) {
199: // Add listener to this's list of unsolicited notifiers
200: if (unsolicited == null) {
201: unsolicited = new Vector(3);
202: }
203: unsolicited.addElement(l);
204: }
205: }
206:
207: /**
208: * Removes <tt>l</tt> from all notifiers in this context.
209: */
210: synchronized void removeNamingListener(NamingListener l) {
211: Enumeration allnotifiers = notifiers.elements();
212: NamingEventNotifier notifier;
213:
214: if (debug)
215: System.err.println("EventSupport removing listener");
216:
217: // Go through list of notifiers, remove 'l' from each.
218: // If 'l' is notifier's only listener, remove notifier too.
219: while (allnotifiers.hasMoreElements()) {
220: notifier = (NamingEventNotifier) allnotifiers.nextElement();
221: if (notifier != null) {
222: if (debug)
223: System.err
224: .println("EventSupport removing listener from notifier");
225: notifier.removeNamingListener(l);
226: if (!notifier.hasNamingListeners()) {
227: if (debug)
228: System.err
229: .println("EventSupport stopping notifier");
230: notifier.stop();
231: notifiers.remove(notifier.info);
232: }
233: }
234: }
235:
236: // Remove from list of unsolicited notifier
237: if (debug)
238: System.err.println("EventSupport removing unsolicited: "
239: + unsolicited);
240: if (unsolicited != null) {
241: unsolicited.removeElement(l);
242: }
243:
244: }
245:
246: synchronized boolean hasUnsolicited() {
247: return (unsolicited != null && unsolicited.size() > 0);
248: }
249:
250: /**
251: * package private;
252: * Called by NamingEventNotifier to remove itself when it encounters
253: * a NamingException.
254: */
255: synchronized void removeDeadNotifier(NotifierArgs info) {
256: if (debug) {
257: System.err.println("EventSupport.removeDeadNotifier: "
258: + info.name);
259: }
260: notifiers.remove(info);
261: }
262:
263: /**
264: * Fire an event to unsolicited listeners.
265: * package private;
266: * Called by LdapCtx when its clnt receives an unsolicited notification.
267: */
268: synchronized void fireUnsolicited(Object obj) {
269: if (debug) {
270: System.err.println("EventSupport.fireUnsolicited: " + obj
271: + " " + unsolicited);
272: }
273: if (unsolicited == null || unsolicited.size() == 0) {
274: // This shouldn't really happen, but might in case
275: // there is a timing problem that removes a listener
276: // before a fired event event reaches here.
277: return;
278: }
279:
280: if (obj instanceof UnsolicitedNotification) {
281:
282: // Fire UnsolicitedNotification to unsolicited listeners
283:
284: UnsolicitedNotificationEvent evt = new UnsolicitedNotificationEvent(
285: ctx, (UnsolicitedNotification) obj);
286: queueEvent(evt, unsolicited);
287:
288: } else if (obj instanceof NamingException) {
289:
290: // Fire NamingExceptionEvent to unsolicited listeners.
291:
292: NamingExceptionEvent evt = new NamingExceptionEvent(ctx,
293: (NamingException) obj);
294: queueEvent(evt, unsolicited);
295:
296: // When an exception occurs, the unsolicited listeners
297: // are automatically deregistered.
298: // When LdapClient.processUnsolicited() fires a NamingException,
299: // it will update its listener list so we don't have to.
300: // Likewise for LdapCtx.
301:
302: unsolicited = null;
303: }
304: }
305:
306: /**
307: * Stops notifier threads that are collecting event data and
308: * stops the event queue from dispatching events.
309: * Package private; used by LdapCtx.
310: */
311: synchronized void cleanup() {
312: if (debug)
313: System.err.println("EventSupport clean up");
314: if (notifiers != null) {
315: for (Enumeration ns = notifiers.elements(); ns
316: .hasMoreElements();) {
317: ((NamingEventNotifier) ns.nextElement()).stop();
318: }
319: notifiers = null;
320: }
321: if (eventQueue != null) {
322: eventQueue.stop();
323: eventQueue = null;
324: }
325: // %%% Should we fire NamingExceptionEvents to unsolicited listeners?
326: }
327:
328: /*
329: * The queue of events to be delivered.
330: */
331: private EventQueue eventQueue;
332:
333: /**
334: * Add the event and vector of listeners to the queue to be delivered.
335: * An event dispatcher thread dequeues events from the queue and dispatches
336: * them to the registered listeners.
337: * Package private; used by NamingEventNotifier to fire events
338: */
339: synchronized void queueEvent(EventObject event, Vector vector) {
340: if (eventQueue == null)
341: eventQueue = new EventQueue();
342:
343: /*
344: * Copy the vector in order to freeze the state of the set
345: * of EventListeners the event should be delivered to prior
346: * to delivery. This ensures that any changes made to the
347: * Vector from a target listener's method during the delivery
348: * of this event will not take effect until after the event is
349: * delivered.
350: */
351: Vector v = (Vector) vector.clone();
352: eventQueue.enqueue(event, v);
353: }
354:
355: // No finalize() needed because EventSupport is always owned by
356: // an LdapCtx. LdapCtx's finalize() and close() always call cleanup() so
357: // there is no need for EventSupport to have a finalize().
358: }
|