353 lines
14 KiB
Java
353 lines
14 KiB
Java
/*
|
|
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package com.sun.jndi.ldap;
|
|
|
|
import java.util.Hashtable;
|
|
import java.util.Vector;
|
|
import java.util.EventObject;
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
|
|
import javax.naming.*;
|
|
import javax.naming.event.*;
|
|
import javax.naming.directory.SearchControls;
|
|
import javax.naming.ldap.UnsolicitedNotificationListener;
|
|
import javax.naming.ldap.UnsolicitedNotificationEvent;
|
|
import javax.naming.ldap.UnsolicitedNotification;
|
|
|
|
/**
|
|
* This is a utility class that can be used by a context that supports
|
|
* event notification. You can use an instance of this class as a member field
|
|
* of your context and delegate various work to it.
|
|
* It is currently structured so that each context should have its own
|
|
* EventSupport (instead of static version shared by all contexts
|
|
* of a service provider).
|
|
*<p>
|
|
* This class supports two types of listeners: those that register for
|
|
* NamingEvents, and those for UnsolicitedNotificationEvents (they can be mixed
|
|
* into the same listener).
|
|
* For NamingEvent listeners, it maintains a hashtable that maps
|
|
* registration requests--the key--to
|
|
* <em>notifiers</em>--the value. Each registration request consists of:
|
|
*<ul>
|
|
*<li>The name argument of the registration.
|
|
*<li>The filter (default is "(objectclass=*)").
|
|
*<li>The search controls (default is null SearchControls).
|
|
*<li>The events that the listener is interested in. This is determined by
|
|
* finding out which <tt>NamingListener</tt> interface the listener supports.
|
|
*</ul>
|
|
*<p>
|
|
*A notifier (<tt>NamingEventNotifier</tt>) is a worker thread that is responsible
|
|
*for gathering information for generating events requested by its listeners.
|
|
*Each notifier maintains its own list of listeners; these listeners have
|
|
*all made the same registration request (at different times) and implements
|
|
*the same <tt>NamingListener</tt> interfaces.
|
|
*<p>
|
|
*For unsolicited listeners, this class maintains a vector, unsolicited.
|
|
*When an unsolicited listener is registered, this class adds itself
|
|
*to the context's LdapClient. When LdapClient receives an unsolicited
|
|
*notification, it notifies this EventSupport to fire an event to the
|
|
*the listeners. Special handling in LdapClient is done for the DISCONNECT
|
|
*notification. [It results in the EventSupport firing also a
|
|
*NamingExceptionEvent to the unsolicited listeners.]
|
|
*<p>
|
|
*
|
|
*When a context no longer needs this EventSupport, it should invoke
|
|
*cleanup() on it.
|
|
*<p>
|
|
*<h4>Registration</h4>
|
|
*When a registration request is made, this class attempts to find an
|
|
*existing notifier that's already working on the request. If one is
|
|
*found, the listener is added to the notifier's list. If one is not found,
|
|
*a new notifier is created for the listener.
|
|
*
|
|
*<h4>Deregistration</h4>
|
|
*When a deregistration request is made, this class attemps to find its
|
|
*corresponding notifier. If the notifier is found, the listener is removed
|
|
*from the notifier's list. If the listener is the last listener on the list,
|
|
*the notifier's thread is terminated and removed from this class's hashtable.
|
|
*Nothing happens if the notifier is not found.
|
|
*
|
|
*<h4>Event Dispatching</h4>
|
|
*The notifiers are responsible for gather information for generating events
|
|
*requested by their respective listeners. When a notifier gets sufficient
|
|
*information to generate an event, it creates invokes the
|
|
*appropriate <tt>fireXXXEvent</tt> on this class with the information and list of
|
|
*listeners. This causes an event and the list of listeners to be added
|
|
*to the <em>event queue</em>.
|
|
*This class maintains an event queue and a dispatching thread that dequeues
|
|
*events from the queue and dispatches them to the listeners.
|
|
*
|
|
*<h4>Synchronization</h4>
|
|
*This class is used by the main thread (LdapCtx) to add/remove listeners.
|
|
*It is also used asynchronously by NamingEventNotifiers threads and
|
|
*the context's Connection thread. It is used by the notifier threads to
|
|
*queue events and to update the notifiers list when the notifiers exit.
|
|
*It is used by the Connection thread to fire unsolicited notifications.
|
|
*Methods that access/update the 'unsolicited' and 'notifiers' lists are
|
|
*thread-safe.
|
|
*
|
|
* @author Rosanna Lee
|
|
*/
|
|
final class EventSupport {
|
|
final static private boolean debug = false;
|
|
|
|
private LdapCtx ctx;
|
|
|
|
/**
|
|
* NamingEventNotifiers; hashed by search arguments;
|
|
*/
|
|
private Hashtable<NotifierArgs, NamingEventNotifier> notifiers =
|
|
new Hashtable<>(11);
|
|
|
|
/**
|
|
* List of unsolicited notification listeners.
|
|
*/
|
|
private Vector<UnsolicitedNotificationListener> unsolicited = null;
|
|
|
|
/**
|
|
* Constructs EventSupport for ctx.
|
|
* <em>Do we need to record the name of the target context?
|
|
* Or can we assume that EventSupport is called on a resolved
|
|
* context? Do we need other add/remove-NamingListener methods?
|
|
* package private;
|
|
*/
|
|
EventSupport(LdapCtx ctx) {
|
|
this.ctx = ctx;
|
|
}
|
|
|
|
/**
|
|
* Adds <tt>l</tt> to list of listeners interested in <tt>nm</tt>.
|
|
*/
|
|
/*
|
|
* Make the add/removeNamingListeners synchronized to:
|
|
* 1. protect usage of 'unsolicited', which may be read by
|
|
* the Connection thread when dispatching unsolicited notification.
|
|
* 2. ensure that NamingEventNotifier thread's access to 'notifiers'
|
|
* is safe
|
|
*/
|
|
synchronized void addNamingListener(String nm, int scope,
|
|
NamingListener l) throws NamingException {
|
|
|
|
if (l instanceof ObjectChangeListener ||
|
|
l instanceof NamespaceChangeListener) {
|
|
NotifierArgs args = new NotifierArgs(nm, scope, l);
|
|
|
|
NamingEventNotifier notifier = notifiers.get(args);
|
|
if (notifier == null) {
|
|
notifier = new NamingEventNotifier(this, ctx, args, l);
|
|
notifiers.put(args, notifier);
|
|
} else {
|
|
notifier.addNamingListener(l);
|
|
}
|
|
}
|
|
if (l instanceof UnsolicitedNotificationListener) {
|
|
// Add listener to this's list of unsolicited notifiers
|
|
if (unsolicited == null) {
|
|
unsolicited = new Vector<>(3);
|
|
}
|
|
|
|
unsolicited.addElement((UnsolicitedNotificationListener)l);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds <tt>l</tt> to list of listeners interested in <tt>nm</tt>
|
|
* and filter.
|
|
*/
|
|
synchronized void addNamingListener(String nm, String filter,
|
|
SearchControls ctls, NamingListener l) throws NamingException {
|
|
|
|
if (l instanceof ObjectChangeListener ||
|
|
l instanceof NamespaceChangeListener) {
|
|
NotifierArgs args = new NotifierArgs(nm, filter, ctls, l);
|
|
|
|
NamingEventNotifier notifier = notifiers.get(args);
|
|
if (notifier == null) {
|
|
notifier = new NamingEventNotifier(this, ctx, args, l);
|
|
notifiers.put(args, notifier);
|
|
} else {
|
|
notifier.addNamingListener(l);
|
|
}
|
|
}
|
|
if (l instanceof UnsolicitedNotificationListener) {
|
|
// Add listener to this's list of unsolicited notifiers
|
|
if (unsolicited == null) {
|
|
unsolicited = new Vector<>(3);
|
|
}
|
|
unsolicited.addElement((UnsolicitedNotificationListener)l);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes <tt>l</tt> from all notifiers in this context.
|
|
*/
|
|
synchronized void removeNamingListener(NamingListener l) {
|
|
if (debug) {
|
|
System.err.println("EventSupport removing listener");
|
|
}
|
|
// Go through list of notifiers, remove 'l' from each.
|
|
// If 'l' is notifier's only listener, remove notifier too.
|
|
Iterator<NamingEventNotifier> iterator = notifiers.values().iterator();
|
|
while (iterator.hasNext()) {
|
|
NamingEventNotifier notifier = iterator.next();
|
|
if (notifier != null) {
|
|
if (debug) {
|
|
System.err.println("EventSupport removing listener from notifier");
|
|
}
|
|
notifier.removeNamingListener(l);
|
|
if (!notifier.hasNamingListeners()) {
|
|
if (debug) {
|
|
System.err.println("EventSupport stopping notifier");
|
|
}
|
|
notifier.stop();
|
|
iterator.remove();
|
|
}
|
|
}
|
|
}
|
|
// Remove from list of unsolicited notifier
|
|
if (debug) {
|
|
System.err.println("EventSupport removing unsolicited: " + unsolicited);
|
|
}
|
|
if (unsolicited != null) {
|
|
unsolicited.removeElement(l);
|
|
}
|
|
}
|
|
|
|
synchronized boolean hasUnsolicited() {
|
|
return (unsolicited != null && unsolicited.size() > 0);
|
|
}
|
|
|
|
/**
|
|
* package private;
|
|
* Called by NamingEventNotifier to remove itself when it encounters
|
|
* a NamingException.
|
|
*/
|
|
synchronized void removeDeadNotifier(NotifierArgs info) {
|
|
if (debug) {
|
|
System.err.println("EventSupport.removeDeadNotifier: " + info.name);
|
|
}
|
|
notifiers.remove(info);
|
|
}
|
|
|
|
/**
|
|
* Fire an event to unsolicited listeners.
|
|
* package private;
|
|
* Called by LdapCtx when its clnt receives an unsolicited notification.
|
|
*/
|
|
synchronized void fireUnsolicited(Object obj) {
|
|
if (debug) {
|
|
System.err.println("EventSupport.fireUnsolicited: " + obj + " "
|
|
+ unsolicited);
|
|
}
|
|
if (unsolicited == null || unsolicited.size() == 0) {
|
|
// This shouldn't really happen, but might in case
|
|
// there is a timing problem that removes a listener
|
|
// before a fired event event reaches here.
|
|
return;
|
|
}
|
|
|
|
if (obj instanceof UnsolicitedNotification) {
|
|
|
|
// Fire UnsolicitedNotification to unsolicited listeners
|
|
|
|
UnsolicitedNotificationEvent evt =
|
|
new UnsolicitedNotificationEvent(ctx, (UnsolicitedNotification)obj);
|
|
queueEvent(evt, unsolicited);
|
|
|
|
} else if (obj instanceof NamingException) {
|
|
|
|
// Fire NamingExceptionEvent to unsolicited listeners.
|
|
|
|
NamingExceptionEvent evt =
|
|
new NamingExceptionEvent(ctx, (NamingException)obj);
|
|
queueEvent(evt, unsolicited);
|
|
|
|
// When an exception occurs, the unsolicited listeners
|
|
// are automatically deregistered.
|
|
// When LdapClient.processUnsolicited() fires a NamingException,
|
|
// it will update its listener list so we don't have to.
|
|
// Likewise for LdapCtx.
|
|
|
|
unsolicited = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stops notifier threads that are collecting event data and
|
|
* stops the event queue from dispatching events.
|
|
* Package private; used by LdapCtx.
|
|
*/
|
|
synchronized void cleanup() {
|
|
if (debug) System.err.println("EventSupport clean up");
|
|
if (notifiers != null) {
|
|
for (NamingEventNotifier notifier : notifiers.values()) {
|
|
notifier.stop();
|
|
}
|
|
notifiers = null;
|
|
}
|
|
if (eventQueue != null) {
|
|
eventQueue.stop();
|
|
eventQueue = null;
|
|
}
|
|
// %%% Should we fire NamingExceptionEvents to unsolicited listeners?
|
|
}
|
|
|
|
/*
|
|
* The queue of events to be delivered.
|
|
*/
|
|
private EventQueue eventQueue;
|
|
|
|
/**
|
|
* Add the event and vector of listeners to the queue to be delivered.
|
|
* An event dispatcher thread dequeues events from the queue and dispatches
|
|
* them to the registered listeners.
|
|
* Package private; used by NamingEventNotifier to fire events
|
|
*/
|
|
synchronized void queueEvent(EventObject event,
|
|
Vector<? extends NamingListener> vector) {
|
|
if (eventQueue == null)
|
|
eventQueue = new EventQueue();
|
|
|
|
/*
|
|
* Copy the vector in order to freeze the state of the set
|
|
* of EventListeners the event should be delivered to prior
|
|
* to delivery. This ensures that any changes made to the
|
|
* Vector from a target listener's method during the delivery
|
|
* of this event will not take effect until after the event is
|
|
* delivered.
|
|
*/
|
|
@SuppressWarnings("unchecked") // clone()
|
|
Vector<NamingListener> v =
|
|
(Vector<NamingListener>)vector.clone();
|
|
eventQueue.enqueue(event, v);
|
|
}
|
|
|
|
// No finalize() needed because EventSupport is always owned by
|
|
// an LdapCtx. LdapCtx's finalize() and close() always call cleanup() so
|
|
// there is no need for EventSupport to have a finalize().
|
|
}
|