Package com.sun.jndi.ldap

Source Code of com.sun.jndi.ldap.EventSupport

/*
* Copyright (c) 1999, 2000, 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.Enumeration;
import java.util.EventObject;

import javax.naming.*;
import javax.naming.directory.*;
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 notifiers = new Hashtable(11);

    /**
     * List of unsolicited notification listeners.
     */
    private Vector 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 =
                (NamingEventNotifier) 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(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 =
                (NamingEventNotifier) 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(l);
        }
    }

    /**
     * Removes <tt>l</tt> from all notifiers in this context.
     */
    synchronized void removeNamingListener(NamingListener l) {
        Enumeration allnotifiers = notifiers.elements();
        NamingEventNotifier notifier;

        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.
        while (allnotifiers.hasMoreElements()) {
            notifier = (NamingEventNotifier)allnotifiers.nextElement();
            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();
                    notifiers.remove(notifier.info);
                }
            }
        }

        // 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 (Enumeration ns = notifiers.elements(); ns.hasMoreElements(); ) {
                ((NamingEventNotifier) ns.nextElement()).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 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.
         */
        Vector v = (Vector)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().
}
TOP

Related Classes of com.sun.jndi.ldap.EventSupport

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.