river-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nic...@apache.org
Subject svn commit: r724989 [3/8] - in /incubator/river/jtsk/skunk/niclas1/services/fiddler: ./ src/ src/main/ src/main/java/ src/main/java/com/ src/main/java/com/sun/ src/main/java/com/sun/jini/ src/main/java/com/sun/jini/fiddler/ src/main/resources/ src/main...
Date Wed, 10 Dec 2008 05:19:01 GMT
Added: incubator/river/jtsk/skunk/niclas1/services/fiddler/src/main/java/com/sun/jini/fiddler/FiddlerImpl.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/niclas1/services/fiddler/src/main/java/com/sun/jini/fiddler/FiddlerImpl.java?rev=724989&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/niclas1/services/fiddler/src/main/java/com/sun/jini/fiddler/FiddlerImpl.java (added)
+++ incubator/river/jtsk/skunk/niclas1/services/fiddler/src/main/java/com/sun/jini/fiddler/FiddlerImpl.java Tue Dec  9 21:19:00 2008
@@ -0,0 +1,9520 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.sun.jini.fiddler;
+
+import com.sun.jini.config.Config;
+import com.sun.jini.constants.ThrowableConstants;
+import com.sun.jini.constants.TimeConstants;
+import com.sun.jini.constants.VersionConstants;
+import com.sun.jini.logging.Levels;
+import com.sun.jini.lookup.entry.BasicServiceType;
+import com.sun.jini.lookup.entry.LookupAttributes;
+import com.sun.jini.proxy.ThrowThis;
+import com.sun.jini.reliableLog.LogHandler;
+import com.sun.jini.reliableLog.ReliableLog;
+import com.sun.jini.start.LifeCycle;
+import com.sun.jini.thread.InterruptedStatusThread;
+import com.sun.jini.thread.ReadersWriter;
+import com.sun.jini.thread.ReadersWriter.ConcurrentLockException;
+import com.sun.jini.thread.ReadyState;
+import com.sun.jini.thread.TaskManager;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.net.InetAddress;
+import java.rmi.MarshalledObject;
+import java.rmi.NoSuchObjectException;
+import java.rmi.RemoteException;
+import java.rmi.activation.ActivationException;
+import java.rmi.activation.ActivationID;
+import java.rmi.activation.ActivationSystem;
+import java.rmi.server.ExportException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import net.jini.activation.ActivationExporter;
+import net.jini.activation.ActivationGroup;
+import net.jini.config.Configuration;
+import net.jini.config.ConfigurationException;
+import net.jini.config.ConfigurationProvider;
+import net.jini.config.NoSuchEntryException;
+import net.jini.core.entry.Entry;
+import net.jini.core.event.EventRegistration;
+import net.jini.core.event.RemoteEventListener;
+import net.jini.core.lease.Lease;
+import net.jini.core.lease.UnknownLeaseException;
+import net.jini.core.lookup.LookupLocator;
+import net.jini.core.lookup.ServiceID;
+import net.jini.core.lookup.ServiceRegistrar;
+import net.jini.discovery.DiscoveryChangeListener;
+import net.jini.discovery.DiscoveryEvent;
+import net.jini.discovery.DiscoveryGroupManagement;
+import net.jini.discovery.DiscoveryLocatorManagement;
+import net.jini.discovery.DiscoveryManagement;
+import net.jini.discovery.LookupDiscoveryManager;
+import net.jini.discovery.LookupDiscoveryRegistration;
+import net.jini.discovery.RemoteDiscoveryEvent;
+import net.jini.export.Exporter;
+import net.jini.export.ProxyAccessor;
+import net.jini.export.proxytrust.ServerProxyTrust;
+import net.jini.export.proxytrust.TrustEquivalence;
+import net.jini.id.Uuid;
+import net.jini.id.UuidFactory;
+import net.jini.jeri.BasicILFactory;
+import net.jini.jeri.BasicJeriExporter;
+import net.jini.jeri.InvocationLayerFactory;
+import net.jini.jeri.ServerEndpoint;
+import net.jini.jeri.tcp.TcpServerEndpoint;
+import net.jini.lookup.JoinManager;
+import net.jini.lookup.entry.Comment;
+import net.jini.lookup.entry.ServiceInfo;
+import net.jini.lookup.entry.Status;
+import net.jini.lookup.entry.StatusType;
+import net.jini.security.BasicProxyPreparer;
+import net.jini.security.ProxyPreparer;
+import net.jini.security.TrustVerifier;
+
+/**
+ * This class is the server side of an implementation of the lookup
+ * discovery service. Multiple client-side proxy classes are used to
+ * interact and communicate with this backend server. Those proxy
+ * classes are: <code>FiddlerProxy</code> (the proxy for the
+ * <code>LookupDiscoveryService</code> interface which defines how clients
+ * register with the lookup discovery service), <code>FiddlerAdminProxy</code>
+ * (the proxy for the <code>FiddlerAdmin</code> interface which specifies
+ * the methods through which administration duties such as joining, persistent
+ * state logging policy, and shutting down the lookup discovery service can
+ * be performed), and <code>FiddlerRegistration</code> (the proxy for the
+ * <code>LookupDiscoveryRegistration</code> interface which defines the
+ * methods through which clients can perform duties such as group and
+ * locator management, state retrieval, and discarding discovered but
+ * unavailable lookup services so they are eligible for re-discovery).
+ * <p>
+ * It is through the proxies that communicate with this class that clients
+ * interact with the lookup discovery service. When a client makes a method
+ * invocation on one of the proxies, the proxy makes a corresponding call
+ * on the methods specified in the <code>Fiddler</code> interface which
+ * are implemented in this class, ultimately executing on the backend server.
+ * <p>
+ *
+ * @author Sun Microsystems, Inc.
+ * @com.sun.jini.impl This implementation of the lookup discovery service employs a number of
+ * {@link java.util.logging.Logger}s. The details of each such
+ * {@link java.util.logging.Logger} are described
+ * <a href="./package-summary.html#fiddlerLoggers">here</a>.
+ * <p>
+ * The configuration entries supported by this implementation are described
+ * <a href="./package-summary.html#fiddlerConfigEntries">here</a>.
+ */
+class FiddlerImpl implements ServerProxyTrust, ProxyAccessor, Fiddler
+{
+
+    /* Name of this component; used in config entry retrieval and the logger.*/
+    private static final String COMPONENT_NAME = "com.sun.jini.fiddler";
+    /* Loggers used by this implementation of the service. */
+    static final Logger problemLogger = Logger.getLogger( COMPONENT_NAME +
+                                                          ".problem" );
+    static final Logger startupLogger = Logger.getLogger( COMPONENT_NAME +
+                                                          ".startup" );
+    static final Logger tasksLogger = Logger.getLogger( COMPONENT_NAME +
+                                                        ".tasks" );
+    static final Logger eventsLogger = Logger.getLogger( COMPONENT_NAME +
+                                                         ".events" );
+    static final Logger groupsLogger = Logger.getLogger( COMPONENT_NAME +
+                                                         ".groups" );
+    static final Logger locatorsLogger = Logger.getLogger( COMPONENT_NAME +
+                                                           ".locators" );
+    static final Logger discardLogger = Logger.getLogger( COMPONENT_NAME +
+                                                          ".discard" );
+    static final Logger leaseLogger = Logger.getLogger( COMPONENT_NAME +
+                                                        ".lease" );
+    static final Logger registrationLogger = Logger.getLogger( COMPONENT_NAME +
+                                                               ".registration" );
+    static final Logger persistLogger = Logger.getLogger( COMPONENT_NAME +
+                                                          ".persist" );
+
+    /**
+     * Data structure - associated with a <code>ServiceRegistrar</code> -
+     * containing the <code>LookupLocator</code> and the member groups of
+     * the registrar
+     */
+    protected final class LocatorGroupsStruct
+    {
+        public LookupLocator locator;
+        public String[] groups;
+
+        LocatorGroupsStruct( LookupLocator locator, String[] groups )
+        {
+            this.locator = locator;
+            this.groups = groups;
+        }
+    }//end class LocatorGroupsStruct
+
+    /* ServiceInfo values */
+    private static final String PRODUCT = "Lookup Discovery Service";
+    private static final String MANUFACTURER = "Sun Microsystems, Inc.";
+    private static final String VENDOR = MANUFACTURER;
+    private static final String VERSION = VersionConstants.SERVER_VERSION;
+
+    /**
+     * When re-setting the bound on lease durations, that bound cannot be
+     * set to a value larger than this value
+     */
+    private static final long MAX_LEASE = 1000L * 60 * 60 * 24 * 365 * 1000;
+    /**
+     * Log format version
+     */
+    private static final int LOG_VERSION = 2;
+
+    /**
+     * The outer (smart) proxy to this server
+     */
+    private FiddlerProxy outerProxy;
+    /**
+     * The inner proxy (stub or dynamic proxy) to this server
+     */
+    private Fiddler innerProxy;
+    /**
+     * The admin proxy to this server
+     */
+    private FiddlerAdminProxy adminProxy;
+    /**
+     * The service ID associated with this service when it is registered
+     * with any lookup service.
+     */
+    private ServiceID serviceID = null;
+    /**
+     * The activation id of the current instance of the lookup discovery
+     * service, if it happens to be and activatable instance
+     */
+    private ActivationID activationID;
+    /* Holds the prepared proxy to the ActivationSystem */
+    private ActivationSystem activationSystem;
+    /**
+     * The unique identifier generated (or recovered) when an instance of
+     * this service is constructed. This ID is typically used to determine
+     * equality between the proxies of any two instances of this service.
+     */
+    private Uuid proxyID = null;
+    /**
+     * Map from the set of all currently discovered registrars to their
+     * corresponding [locator,member groups] pairs (locatorGroupsStuct).
+     */
+    private final HashMap allDiscoveredRegs = new HashMap( 11 );
+    /**
+     * Map from registrationID to registrationInfo.  Every registration is in
+     * this map under its registrationID.
+     */
+    private final HashMap registrationByID = new HashMap( 11 );
+    /**
+     * Map from registrationInfo to registrationInfo (that is, to itself),
+     * where the elements of the map are ordered by lease expiration time.
+     */
+    private final TreeMap registrationByTime = new TreeMap();
+    /**
+     * Performs all group and locator discovery on behalf of clients
+     */
+    private LookupDiscoveryManager discoveryMgr = null;
+    /**
+     * The listener registered for both group discovery events and locator
+     * discovery events.
+     */
+    private final LookupDiscoveryListener discoveryListener
+        = new LookupDiscoveryListener();
+    /**
+     * For each registration created by the lookup discovery service, an
+     * event identifier that uniquely maps the registration to the
+     * registration's listener and managed sets will be generated and
+     * associated with the registration through the EventRegistration
+     * field of the registrationInfo. This event ID is unique across
+     * all registrations with the current instance of the lookup discovery
+     * service.
+     */
+    private long curEventID = 0;
+    /**
+     * Earliest expiration time over all active registrations
+     */
+    private long minExpiration = Long.MAX_VALUE;
+    /**
+     * The lookup discovery manager this service's join manager will use
+     */
+    private DiscoveryManagement joinMgrLDM;
+    /**
+     * Manager for discovering and registering with lookup services
+     */
+    private JoinManager joinMgr;
+    /**
+     * Task manager for sending remote discovery events
+     */
+    private TaskManager taskMgr;
+    /**
+     * Registration lease expiration thread
+     */
+    private Thread leaseExpireThread;
+    /**
+     * Snapshot-taking thread
+     */
+    private Thread snapshotThread;
+
+    /**
+     * Concurrent object to control read and write access
+     */
+    private final ReadersWriter concurrentObj = new ReadersWriter();
+    /**
+     * Object for synchronizing with the registration expire thread
+     */
+    private final Object leaseExpireThreadSyncObj = new Object();
+    /**
+     * Object on which the snapshot-taking thread will synchronize
+     */
+    private final Object snapshotThreadSyncObj = new Object();
+
+    /**
+     * Reliable log object to hold persistent state of the service.
+     * This object is also used as a flag: non-null ==> persistent service
+     * null ==> non-persistent service
+     */
+    private ReliableLog log = null;
+    /**
+     * Flag indicating whether system is in a state of recovery
+     */
+    private boolean inRecovery;
+    /**
+     * Current number of records in the Log File since the last snapshot
+     */
+    private int logFileSize = 0;
+    /**
+     * The name of the directory to which the persistent modes of this service
+     * will log the service's state (using ReliableLog).
+     */
+    private String persistDir;
+    /**
+     * least upper bound applied to all granted lease durations
+     */
+    private long leaseBound = 1000 * 60 * 30;
+    /**
+     * Weight factor applied to snapshotSize when deciding to take snapshot
+     */
+    private float snapshotWt = 10;
+    /**
+     * Log File must contain this many records before snapshot allowed
+     */
+    private int snapshotThresh = 200;
+    /**
+     * Groups whose members are lookup services this service should join.
+     * Unless configured otherwise, this service will initially join the
+     * un-named public group. The desired join groups for this service can
+     * be set administratively after start up.
+     */
+    private String[] thisServicesGroups = new String[]{ "" };
+    /**
+     * Locators of specific lookup services this service should join.
+     * This service will initially join no lookups found through locator
+     * discovery. The locators of the specific lookup services that are
+     * desired for this service to join should be set administratively
+     * after start up.
+     */
+    private LookupLocator[] thisServicesLocators = { };
+    /**
+     * The attributes to use when joining lookup service(s)
+     */
+    private Entry[] thisServicesAttrs
+        = new Entry[]
+        { new ServiceInfo( PRODUCT, MANUFACTURER, VENDOR, VERSION, "", "" ),
+          new BasicServiceType( "Lookup Discovery Service" )
+        };
+    /* Object used to obtain the configuration items for this service. */
+    private Configuration config;
+    /* The JAAS login context to use when performing a JAAS login. */
+    private LoginContext loginContext = null;
+    /* The exporter used to export this service. */
+    private Exporter serverExporter;
+    /* Maximum value of upper bound on lease durations.*/
+    private long leaseMax = MAX_LEASE;
+    /* Flag indicating this service is being started for the very 1st time */
+    private boolean initialStartup = true;
+    /**
+     * Object that, if non-<code>null</code>, will cause the object's
+     * <code>unregister</code> method to be invoked during service shutdown
+     * to notify the service starter framework that the reference to this
+     * service's implementation can be 'released' for garbage collection;
+     * the framework is notified that it does not have to hold on to the
+     * service reference any longer.  Note hat this object is used only
+     * in the non-activatable case.
+     */
+    private LifeCycle lifeCycle = null;
+    /* Preparer for proxies to remote listeners newly registered with this
+     * service.
+     */
+    private static ProxyPreparer listenerPreparer;
+    /* Preparer for proxies to remote listeners, previously prepared, and
+     * recovered from this service's persisted state.
+     */
+    private static ProxyPreparer recoveredListenerPreparer;
+    /* Preparer for initial and new lookup locators this service should
+     * discover and join.
+     */
+    private static ProxyPreparer locatorToJoinPreparer;
+    /* Preparer for lookup locators this service should discover and join
+     * that were previously prepared and which were recovered from this
+     * service's persisted state.
+     */
+    private static ProxyPreparer recoveredLocatorToJoinPreparer;
+    /* Preparer for initial and new lookup locators this service should
+     * discover on behalf of the clients that register with it.
+     */
+    private static ProxyPreparer locatorToDiscoverPreparer;
+    /* Preparer for lookup locators this service should discover on behalf
+     * of its registered clients that were previously prepared and which
+     * were recovered from this service's persisted state.
+     */
+    private static ProxyPreparer recoveredLocatorToDiscoverPreparer;
+    /**
+     * Object used to prevent access to this service during the service's
+     * initialization or shutdown processing.
+     */
+    private final ReadyState readyState = new ReadyState();
+
+    /* ************************* BEGIN Constructors ************************ */
+    /**
+     * Constructs a new instance of FiddlerImpl. This version of the
+     * constructor is used to create an activatable instance of the lookup
+     * discovery service that logs its state information to persistent storage.
+     * <p>
+     * A constructor having this signature is required for the class to be
+     * activatable. This constructor is automatically called by the
+     * activation group when the lookup discovery service is activated.
+     *
+     * @param activationID the activation ID generated by the activation
+     *                     system and assigned to the instance of the server
+     *                     being activated
+     * @param data         state data (represented as a
+     *                     <code>MarshalledObject</code>) which is needed to
+     *                     re-activate this server
+     * @throws IOException            this exception can occur when there is
+     *                                a problem recovering data from disk,
+     *                                exporting the server that's being
+     *                                activated, or when unmarshalling the
+     *                                given <code>data</code> parameter.
+     * @throws ConfigurationException this exception can occur when a
+     *                                problem occurs while retrieving an item
+     *                                from the <code>Configuration</code>
+     *                                generated from the contents of the
+     *                                given <code>data</code> parameter
+     * @throws ActivationException    this exception can occur when a problem
+     *                                occurs while activating the service
+     * @throws LoginException         this exception occurs when authentication
+     *                                fails while performing a JAAS login for
+     *                                this service
+     * @throws ClassNotFoundException this exception can occur while
+     *                                unmarshalling the given <code>data</code>
+     *                                parameter; when a class needed in the
+     *                                unmarshalling process cannot be found.
+     * @throws ClassCastException     this exception can occur while
+     *                                unmarshalling the given <code>data</code>
+     *                                parameter; when the contents of that
+     *                                parameter is not a <code>String</code>
+     *                                array.
+     */
+    FiddlerImpl( ActivationID activationID,
+                 MarshalledObject data ) throws IOException,
+                                                ActivationException,
+                                                ConfigurationException,
+                                                LoginException,
+                                                ClassNotFoundException
+    {
+        this.activationID = activationID;
+        try
+        {
+            activationSystem = ActivationGroup.getSystem();
+            init( (String[]) data.get(), true );//true ==> persistent
+        }
+        catch( Throwable e )
+        {
+            cleanupInitFailure();
+            handleActivatableInitThrowable( e );
+        }
+    }//end activatable constructor
+
+    /**
+     * Constructs a new instance of FiddlerImpl. This version of the
+     * constructor is used to create a NON-activatable instance of the
+     * lookup discovery service.
+     *
+     * @param configArgs <code>String</code> array whose elements are
+     *                   the arguments to use when creating this version of
+     *                   the server
+     * @param lifeCycle  instance of <code>LifeCycle</code> that, if
+     *                   non-<code>null</code>, will cause this object's
+     *                   <code>unregister</code> method to be invoked during
+     *                   shutdown to notify the service starter framework that
+     *                   the reference to this service's implementation can be
+     *                   'released' for garbage collection. A value of
+     *                   <code>null</code> for this argument is allowed.
+     * @param persistent if <code>true</code>, then the service should persist
+     *                   its state.
+     * @throws IOException            this exception can occur when there is
+     *                                a problem recovering data from disk, or
+     *                                while exporting the server that's being
+     *                                created.
+     * @throws ConfigurationException this exception can occur when an
+     *                                problem occurs while retrieving an item
+     *                                from the <code>Configuration</code>
+     *                                generated from the contents of the
+     *                                given <code>configArgs</code> parameter
+     * @throws LoginException         this exception occurs when authentication
+     *                                fails while performing a JAAS login for
+     *                                this service
+     */
+    FiddlerImpl( String[] configArgs, LifeCycle lifeCycle, boolean persistent )
+        throws IOException,
+               ConfigurationException,
+               LoginException
+    {
+        try
+        {
+            this.lifeCycle = lifeCycle;
+            init( configArgs, persistent );
+        }
+        catch( Throwable e )
+        {
+            cleanupInitFailure();
+            handleInitThrowable( e );
+        }
+    }//end non-activatable constructor
+    /* ************************** END Constructors ************************* */
+
+    /* ******************* BEGIN Inner Class Definitions ******************* */
+
+    /**
+     * Class which is used to communicate the status of this service to
+     * interested entities. In particular, when certain errors occur during
+     * operation, an instance of this class will be registered as an
+     * attribute in all of the lookup services with which this service
+     * is registered. By registering for notification (from the lookup
+     * services) of the existence of this attribute, interested entities
+     * such as administrative clients and clients wishing to use this
+     * service will be informed when this service can not proceed with its
+     * processing, and can take appropriate action.
+     */
+    private static class FiddlerStatus extends Status
+    {
+        private static final long serialVersionUID = -8511826097053446749L;
+
+        public FiddlerStatus( StatusType severity )
+        {
+            super( severity );
+        }
+    }//end class FiddlerStatus
+
+    /**
+     * Class whose discovered() method is invoked by threads in the
+     * LookupDiscovery class whenever a new lookup service is discovered
+     * on behalf of a client registration
+     */
+    private class LookupDiscoveryListener implements DiscoveryChangeListener
+    {
+        public LookupDiscoveryListener()
+        {
+            super();
+        }
+
+        public void discovered( DiscoveryEvent event )
+        {
+            taskMgr.add( new DiscoveredEventTask( event ) );
+        }
+
+        public void discarded( DiscoveryEvent event )
+        {
+            taskMgr.add( new DiscardedEventTask( event ) );
+        }
+
+        public void changed( DiscoveryEvent event )
+        {
+            taskMgr.add( new ChangedEventTask( event ) );
+        }
+    }//end class LookupDiscoveryListener
+
+    /**
+     * This class acts as a record of one registration with the lookup
+     * discovery service; containing all of the information about that
+     * registration.
+     */
+    private final static class RegistrationInfo
+        implements Comparable, Serializable
+    {
+        private static final long serialVersionUID = 2L;
+
+        /**
+         * The unique identifier assigned to the registration to which the
+         * data in the current implementation of this class corresponds.
+         * This identifier is unique across all other active registrations
+         * generated with the current instance of the lookup discovery
+         * service.
+         *
+         * @serial
+         */
+        public final Uuid registrationID;
+        /**
+         * Map from the set of instances of the <code>ServiceRegistrar</code>
+         * interface, to the set of marshalled instances of the
+         * <code>ServiceRegistrar</code> interface, where each key and
+         * each value (which is the marshalled form of its corresponding
+         * key) is a proxy to one of the lookup service(s) that have been
+         * discovered for the current registration. The contents of
+         * this set represents the 'remote state' of the registration's
+         * currently discovered lookup service(s).
+         *
+         * @serial
+         */
+        public HashMap discoveredRegsMap;
+        /**
+         * The managed set containing the names of the groups whose
+         * members are the lookup services the lookup discovery service
+         * should attempt to discover for the current registration.
+         * (HashSet is used to prevent duplicates.)
+         *
+         * @serial
+         */
+        public HashSet groups;
+        /**
+         * The managed set containing the locators of the specific lookup
+         * services the lookup discovery service should attempt to discover
+         * for the current registration. (HashSet is used to prevent
+         * duplicates.)
+         *
+         * @serial
+         */
+        public HashSet locators;
+        /**
+         * The ID of the lease placed on the current registration.
+         *
+         * @serial
+         */
+        public final Uuid leaseID;
+        /**
+         * The absolute expiration time of the current lease.
+         *
+         * @serial
+         */
+        public long leaseExpiration;
+        /**
+         * The identifier that maps the current registration to the remote
+         * event listener and the managed set of groups and locators.
+         */
+        public long eventID;
+        /**
+         * The current sequence number of the set of remote discovery events
+         * sent to the current registration's listener. When a registration
+         * is granted, this class is instantiated to contain the information
+         * related to that particular registration. The event sequence
+         * number is initialized to 0 upon instantiation because the
+         * remote discovery events are sent to the listeners of each
+         * separate registration. Thus, each registration has its own
+         * sequence of events.
+         *
+         * @serial
+         */
+        public long seqNum;
+        /**
+         * The handback object returned with every remote discovery event
+         * sent to the current registration's listener.
+         *
+         * @serial
+         */
+        public final MarshalledObject handback;
+        /**
+         * When the lookup discovery service discards a registrar as a
+         * result of some internal condition (such as multicast announcements
+         * ceasing) and not as a result of a request from a registration,
+         * every registration configured for group discovery of that discarded
+         * registrar will be sent a remote discarded event. On the other
+         * hand, for the case where a registrar is discarded as a result
+         * of a request from a registration, only those registrations that
+         * actually request that the registrar be discarded will be sent
+         * a remote discarded event. This flag is used to determine whether
+         * to send a remote discarded event to one or multiple listeners.
+         */
+        public boolean discardFlag;
+        /**
+         * The remote event listener registered by the client. This field
+         * is transient because it is marshalled separately from the rest
+         * of this class when being serialized. (See the description for
+         * <code>writeObject</code> below.)
+         */
+        public transient RemoteEventListener listener;
+
+        /**
+         * Constructs an instance of this class and stores the information
+         * related to the current registration: IDs, managed sets, lease
+         * information, and event registration information.
+         */
+        public RegistrationInfo( Uuid registrationID,
+                                 String[] groups,
+                                 LookupLocator[] locators,
+                                 Uuid leaseID,
+                                 long leaseExpiration,
+                                 long eventID,
+                                 MarshalledObject handback,
+                                 RemoteEventListener listener )
+        {
+            this.registrationID = registrationID;
+            /* Initialize the groups field, removing nulls and duplicates */
+            if( groups != null )
+            {
+                this.groups = new HashSet();
+                for( int i = 0; i < groups.length; i++ )
+                {
+                    if( groups[ i ] == null )
+                    {
+                        continue;
+                    }
+                    this.groups.add( groups[ i ] );
+                }
+            }
+            /* Initialize the locators field, removing nulls and duplicates */
+            this.locators = new HashSet();
+            if( ( locators != null ) && ( locators.length > 0 ) )
+            {
+                for( int i = 0; i < locators.length; i++ )
+                {
+                    if( locators[ i ] == null )
+                    {
+                        continue;
+                    }
+                    this.locators.add( locators[ i ] );
+                }
+            }
+            this.discoveredRegsMap = new HashMap( 11 );
+            this.leaseID = leaseID;
+            this.leaseExpiration = leaseExpiration;
+
+            this.eventID = eventID;
+            this.seqNum = 0; // initialize to 0
+            this.handback = handback;
+            this.discardFlag = false;//set true only on first discard request
+            this.listener = listener;
+        }//end constructor
+
+        /**
+         * Attempts to marshal each element of the input set of instances of
+         * the <code>ServiceRegistrar</code> interface and then map the
+         * registrar to its marshalled form, and store the mapping in this
+         * registration's <code>discoveredRegsMap</code> field.
+         * <p>
+         * This method is typically invoked to handle discovered (as opposed
+         * to discarded) registrars. Note that if a particular registrar
+         * cannot be serialized (marshalled), it is not included in the
+         * mapping; nor is it included in the return set.
+         *
+         * @param regMapIn mapping in which the key values are the registrars
+         *                 to serialize and store, and the map values are data
+         *                 structures of type <code>LocatorGroupsStruct</code>
+         *                 that contain the locator and member groups of the
+         *                 corresponding registrar key
+         * @return a <code>HashMap</code> whose keys are the registrars
+         *         whose marshalled form and un-marshalled form were inserted
+         *         as key/value pairs into the <code>discoveredRegsMap</code>
+         *         field of this regInfo; and whose values are the member
+         *         groups of each corresponding registrar key.
+         */
+        public HashMap addToDiscoveredRegs( HashMap regMapIn )
+        {
+            HashMap regMapOut = new HashMap( regMapIn.size() );
+            Iterator itr = ( regMapIn.entrySet() ).iterator();
+            nextReg:
+            for( int i = 0; itr.hasNext(); i++ )
+            {
+                Map.Entry pair = (Map.Entry) itr.next();
+                ServiceRegistrar reg = (ServiceRegistrar) pair.getKey();
+                /* If reg is already in map, go to next registrar */
+                if( discoveredRegsMap.containsKey( reg ) )
+                {
+                    continue nextReg;
+                }
+                /* It doesn't contain it, try to marshal it */
+                MarshalledObject mReg = null;
+                try
+                {
+                    mReg = new MarshalledObject( reg );
+                }
+                catch( IOException e )
+                {
+                    continue nextReg;
+                } //failed, next reg
+                /* Succeeded, map registrar to its marshalled form */
+                discoveredRegsMap.put( reg, mReg );
+                /* Map the registrar to its member groups for the return map */
+                regMapOut.put( reg,
+                               ( (LocatorGroupsStruct) pair.getValue() ).groups );
+            }//end loop
+            return regMapOut;
+        }//end addToDiscoveredRegs
+
+        /**
+         * Performs a primary sort by leaseExpiration, and a secondary sort
+         * by registrationID. The secondary sort is immaterial, except to
+         * ensure a total order (required by <code>TreeMap</code>).
+         */
+        public int compareTo( Object obj )
+        {
+            RegistrationInfo regInfo = (RegistrationInfo) obj;
+            if( this == regInfo )
+            {
+                return 0;
+            }
+            if( ( leaseExpiration < regInfo.leaseExpiration )
+                || ( ( leaseExpiration == regInfo.leaseExpiration )
+                     && ( eventID < regInfo.eventID ) ) )
+            {
+                return -1;
+            }//endif
+            return 1;
+        }//end compareTo
+
+        /**
+         * When a registration is granted to a client, the client registers
+         * a remote listener with the lookup discovery service so that the
+         * lookup discovery service may send remote discovery events to the
+         * client. The client typically annotates the listener with an RMI
+         * codebase from which the backend server can download the remote
+         * listener's proxy (stub). When the current registration is logged
+         * to persistent storage (for example, a snapshot is taken), the
+         * listener is written to the output snapshot or log file through
+         * an <code>ObjectOutputStream</code> which only serializes the
+         * listener; it does not marshal the listener. Thus, when the
+         * listener field of this class is logged, unless special action
+         * is taken, the codebase from which to retrieve the listener will
+         * not be included in the output.
+         *
+         * In order to include the codebase with the listener when saving
+         * state, the following custom <code>writeObject</code> method
+         * is provided which first serializes the current instance of
+         * this class (excluding the transient <code>listener</code> field),
+         * and then explicitly marshals the listener to preserve the
+         * codebase upon writing to the file. In this way, the listener --
+         * along with its codebase -- is persisted through a mechanism that
+         * is separate from the normal mechanism applied to the remaining
+         * fields of this class.
+         */
+        private void writeObject( ObjectOutputStream stream ) throws IOException
+        {
+            stream.defaultWriteObject();
+            stream.writeObject( new MarshalledObject( listener ) );
+        }//end writeObject
+
+        /**
+         * When this class is deserialized, this method is invoked. This
+         * method first deserializes the non-transient elements of this
+         * class, and then unmarshals the remote event listener. (See the
+         * description for <code>writeObject</code> above.)
+         */
+        private void readObject( ObjectInputStream stream )
+            throws IOException, ClassNotFoundException
+        {
+            stream.defaultReadObject();
+            MarshalledObject mo = (MarshalledObject) stream.readObject();
+            try
+            {
+                listener = (RemoteEventListener) mo.get();
+                /* Re-prepare the recovered listener */
+                listener =
+                    (RemoteEventListener) recoveredListenerPreparer.prepareProxy
+                        ( listener );
+            }
+            catch( Throwable e )
+            {
+                problemLogger.log( Level.INFO, "problem recovering listener "
+                                               + "for recovered registration", e );
+                if( ( e instanceof Error ) && ( ThrowableConstants.retryable( e )
+                                                == ThrowableConstants.BAD_OBJECT ) )
+                {
+                    throw (Error) e;
+                }//endif
+            }
+            /* Prepare the locators recovered from the stream */
+            int nUnprepared = ( locators ).size();
+            locators = (HashSet) prepareOldLocators
+                ( recoveredLocatorToDiscoverPreparer,
+                  locators );
+            if( nUnprepared != ( locators ).size() )
+            {
+                /* Failure occurred when preparing one of the locs. Because
+                 * this breaks the contract with the client, this registration
+                 * will not be recovered so that the client will eventually
+                 * be notified. To facilitate this, the listener is set to
+                 * null so that the registration will not be added to the
+                 * managed set, and so that when the client eventually attempts
+                 * to renew the lease on that registration, an exception will
+                 * occur; causing the client to be "notified" that there was
+                 * a problem with that registration. The client can then
+                 * retry the registration; and if problems still exist, the
+                 * exception the client receives may give the client more
+                 * useful information from which the client can determine
+                 * how to proceed.
+                 */
+                listener = null;
+                if( problemLogger.isLoggable( Level.WARNING ) )
+                {
+                    problemLogger.log( Level.WARNING, "failure preparing "
+                                                      + "locator while recovering registration"
+                                                      + "... discarding recovered"
+                                                      + "registration" );
+                }//endif
+            }//endif
+        }//end readObject
+    }//end class RegistrationInfo
+
+    /**
+     * This class represents a <code>Task</code> object that is placed
+     * in the <code>TaskManager</code> queue for processing in the thread
+     * pool. Instances of this class are placed on the task queue when
+     * registrations are granted.
+     * <p>
+     * The <code>run</code> method of this class will determine if any of
+     * the new registration's desired lookup service(s) have already been
+     * discovered, and will send the appropriate remote discovery event to
+     * the registration's listener.
+     */
+    private final class NewRegistrationTask implements TaskManager.Task
+    {
+        /**
+         * The data structure record corresponding to the new registration
+         */
+        public final RegistrationInfo regInfo;
+
+        /**
+         * Constructs an instance of this class and stores the registration
+         * information.
+         */
+        public NewRegistrationTask( RegistrationInfo regInfo )
+        {
+            this.regInfo = regInfo;
+        }//end constructor
+
+        /**
+         * This method processes the information associated with the new
+         * registration and determines, based on the current state of the
+         * set of 'already-discovered' lookup service(s), whether to send
+         * a <code>RemoteDiscoveryEvent</code> to the new registration's
+         * listener.
+         */
+        public void run()
+        {
+            concurrentObj.writeLock();
+            try
+            {
+                logInfoTasks( "NewRegistrationTask.run(): "
+                              + "new Registration added" );
+                maybeSendDiscoveredEvent( regInfo, allDiscoveredRegs );
+            }
+            finally
+            {
+                concurrentObj.writeUnlock();
+            }
+        }//end run
+
+        /**
+         * This method returns true if the current instance of this class
+         * must be run after at least one task in the input task list with
+         * an index less than the <code>size</code> parameter (size may be
+         * less than tasks.size()).
+         * <p>
+         * Note that using List.get will be more efficient than List.iterator.
+         *
+         * @param tasks the tasks to consider.  A read-only List, with all
+         *              elements being an instanceof Task.
+         * @param size  elements with index less than size should be considered
+         */
+        public boolean runAfter( List tasks, int size )
+        {
+            return false;
+        }//end runAfter
+    }//end class NewRegistrationTask
+
+    /**
+     * This class represents a <code>Task</code> object that is placed
+     * in the <code>TaskManager</code> queue for processing in the thread
+     * pool. An instance of this class is placed on the task queue when a
+     * <code>DiscoveryEvent</code> instance indicating a discovered event
+     * is received from the local discovery process.
+     * <p>
+     * The <code>run</code> method of this class will process discovery
+     * event information and determine to which active registrations the
+     * appropriate <code>RemoteDiscoveryEvent</code> should be sent; and
+     * then sends that event.
+     */
+    private final class DiscoveredEventTask implements TaskManager.Task
+    {
+        /**
+         * The local event sent by the discovery manager.
+         */
+        public final DiscoveryEvent event;
+
+        /**
+         * Constructs an instance of this class and stores the event
+         */
+        public DiscoveredEventTask( DiscoveryEvent event )
+        {
+            this.event = event;
+        }//end constructor
+
+        /**
+         * This method processes the local discovery event information and
+         * determines, based on the current state of each active
+         * registration, to which such registration the appropriate
+         * <code>RemoteDiscoveryEvent</code> should be sent. After making
+         * the determination, the remote event appropriate for each
+         * registration is constructed and sent.
+         */
+        public void run()
+        {
+            /* Get locators before sync block (no remote calls in sync block)*/
+            Map groupsMap = event.getGroups();
+            ServiceRegistrar[] regs = event.getRegistrars();
+            HashMap regMap = new HashMap( regs.length );
+            for( int i = 0; i < regs.length; i++ )
+            {
+                try
+                {
+                    LookupLocator regLoc = regs[ i ].getLocator();
+                    String[] regGroups = (String[]) groupsMap.get( regs[ i ] );
+                    LocatorGroupsStruct regLocGroups
+                        = new LocatorGroupsStruct( regLoc, regGroups );
+                    regMap.put( regs[ i ], regLocGroups );
+                }
+                catch( Exception e )
+                {
+                    problemLogger.log( Levels.FAILED,
+                                       "problem retrieving locator "
+                                       + "from discovered lookup service ... "
+                                       + "discarded the lookup service", e );
+                    discoveryMgr.discard( regs[ i ] );
+                }
+            }//end loop
+            /* Synchronization block -- no remote calls here */
+            concurrentObj.writeLock();
+            logInfoTasks( "DiscoveredEventTask.run(): processing DISCOVERED "
+                          + "event from discovery manager" );
+            try
+            {
+                /* Update the global allDiscoveredRegs map with new pairs */
+                Set eSet = regMap.entrySet();
+                for( Iterator itr = eSet.iterator(); itr.hasNext(); )
+                {
+                    Map.Entry pair = (Map.Entry) itr.next();
+                    allDiscoveredRegs.put( pair.getKey(), pair.getValue() );
+                }//end loop
+                /* Loop thru regInfo's, adding only those not already known */
+                for( Iterator itr = registrationByID.values().iterator();
+                     itr.hasNext(); )
+                {
+                    RegistrationInfo regInfo = (RegistrationInfo) itr.next();
+                    /* Build and send the "discovered event" if appropriate */
+                    maybeSendDiscoveredEvent( regInfo, regMap );
+                }//end loop
+            }
+            finally
+            {
+                concurrentObj.writeUnlock();
+            }
+        }//end run
+
+        /**
+         * This method returns true if the current instance of this class
+         * must be run after at least one task in the input task list with
+         * an index less than the <code>size</code> parameter (size may be
+         * less than tasks.size()).
+         * <p>
+         * Note that using List.get will be more efficient than List.iterator.
+         *
+         * @param tasks the tasks to consider.  A read-only List, with all
+         *              elements being an instanceof Task.
+         * @param size  elements with index less than size should be considered
+         */
+        public boolean runAfter( List tasks, int size )
+        {
+            return false;
+        }//end runAfter
+    }//end class DiscoveredEventTask
+
+    /**
+     * This class represents a <code>Task</code> object that is placed
+     * in the <code>TaskManager</code> queue for processing in the thread
+     * pool.  An instance of this class is placed on the task queue when a
+     * <code>DiscoveryEvent</code> instance indicating a discarded event
+     * is received from the local discovery process.
+     * <p>
+     * The <code>run</code> method of this class will process event
+     * information resulting from the "discarding" of one or more
+     * lookup services (registrars), and will determine to which active
+     * registrations the appropriate <code>RemoteDiscoveryEvent</code>
+     * should be sent; and then sends that event.
+     */
+    private final class DiscardedEventTask implements TaskManager.Task
+    {
+        /**
+         * The local event sent by the discovery manager.
+         */
+        public final DiscoveryEvent event;
+
+        /**
+         * Constructs an instance of this class and stores the event
+         */
+        public DiscardedEventTask( DiscoveryEvent event )
+        {
+            this.event = event;
+        }//end constructor
+
+        /**
+         * This method processes the local discovery event information and
+         * determines, based on the current state of each active
+         * registration, to which such registration the appropriate
+         * <code>RemoteDiscoveryEvent</code> should be sent. After making
+         * the determination, the remote event appropriate for each
+         * registration is constructed and sent.
+         */
+        public void run()
+        {
+            concurrentObj.writeLock();
+            logInfoTasks( "DiscardedEventTask.run(): processing DISCARDED "
+                          + "event from discovery manager" );
+            try
+            {
+                /* Get the registrars that were just discarded */
+                Map groupsMap = event.getGroups();
+                HashSet allDiscardedRegs = new HashSet( groupsMap.size() );
+                /* Determine if we're here because of an external request for
+                 * discard from one of the regInfo's (an active communication
+                 * discard), or because the discovery manager has determined
+                 * one or more of the discovered registrars has become 
+                 * unreachable (a passive communication discard)
+                 */
+                RegistrationInfo regInfo = externalDiscardRequest();
+                /* If an external request, send the discarded event to only
+                 * the regInfo that requested the discard; otherwise, send
+                 * it to all regInfo's that might be interested.
+                 */
+                if( regInfo != null )
+                {
+                    /* Send discard event to only this one registration */
+                    HashSet discardedRegs = maybeSendDiscardedEvent
+                        ( regInfo, groupsMap, true );
+                    /* Transfer the just-discarded regs to the summary set */
+                    for( Iterator jtr = discardedRegs.iterator(); jtr.hasNext(); )
+                    {
+                        allDiscardedRegs.add( jtr.next() );
+                    }
+                }
+                else
+                {
+                    /* Send discard event to each "eligible" registration */
+                    for( Iterator itr = registrationByID.values().iterator();
+                         itr.hasNext(); )
+                    {
+                        regInfo = (RegistrationInfo) itr.next();
+                        HashSet discardedRegs = maybeSendDiscardedEvent
+                            ( regInfo, groupsMap, false );
+                        /* Transfer the just-discarded regs to summary set */
+                        for( Iterator jtr = discardedRegs.iterator();
+                             jtr.hasNext(); )
+                        {
+                            allDiscardedRegs.add( jtr.next() );
+                        }
+                    }//end loop
+                }//endif
+                maybeRemoveDiscardedRegsFromGlobalSet( allDiscardedRegs );
+            }
+            finally
+            {
+                concurrentObj.writeUnlock();
+            }
+        }//end run
+
+        /**
+         * This method determines, based on the current state of the
+         * <code>regInfo</code> parameter, whether or not to send a
+         * remote discarded event to the regInfo's listener, and then builds
+         * and sends the event if appropriate. This method is called in
+         * response to one of the following situations:
+         * <p>
+         * 1 after invocation of the public <code>discard</code> method
+         * 2 after receipt of a "passive" discarded event from the discovery
+         * manager.
+         * <p>
+         * For case 1, such an event typically indicates what is referred to
+         * as an "active, communication" discarded event. This term is used
+         * in this situation because the regInfo takes the specific action
+         * of requesting that a registrar the client has determined is
+         * unreachable be discarded.
+         * <p>
+         * For case 2, such an event typically indicates what is referred to
+         * as a "passive, communication" discarded event. This term is used
+         * here because the discovery manager - not the client - has determined
+         * that one or more of the previously discovered registrars are now
+         * unreachable. In this case, the client remains "passive", and it
+         * is the discovery manager that discards the unreachable registrars
+         * and notifies the client(s).
+         *
+         * @param regInfo   the data structure record corresponding to the
+         *                  registration whose listener will receive the event
+         * @param groupsMap mapping from the registrars referenced in the
+         *                  just-received event to their corresponding set of
+         *                  member groups
+         * @param active    flag indicating whether the event is an "active"
+         *                  or a "passive" discarded event
+         * @return set of registrars that were discarded for the given regInfo
+         */
+        private HashSet maybeSendDiscardedEvent( RegistrationInfo regInfo,
+                                                 Map groupsMap,
+                                                 boolean active )
+        {
+            HashSet discardedRegs = new HashSet( groupsMap.size() ); //return val
+            /* If no interest in groups or locators, go to next regInfo*/
+            if( ( regInfo.groups != null ) && ( ( regInfo.groups ).size() == 0 )
+                && ( ( regInfo.locators ).size() == 0 ) )
+            {
+                return discardedRegs;
+            }
+            HashMap discardMap = new HashMap( groupsMap.size() );
+            /* loop thru the (registrar,groups) pairs, find regs to discard */
+            Set eSet = groupsMap.entrySet();
+            for( Iterator itr = eSet.iterator(); itr.hasNext(); )
+            {
+                Map.Entry pair = (Map.Entry) itr.next();
+                ServiceRegistrar reg = (ServiceRegistrar) pair.getKey();
+                /* Include the current reg in the discard map only if that
+                 * reg is in the regInfo's discovered set.
+                 */
+                if( ( regInfo.discoveredRegsMap ).containsKey( reg ) )
+                {
+                    /* The groups corresponding to the discarded registrar that
+                     * arrived in the event may be more up-to-date than the
+                     * groups associated with the registrar in the global map.
+                     * Thus, if the event is a passive communication discarded
+                     * event, when determining whether the regInfo is still
+                     * interested in the discarded registrar, use the old group
+                     * info rather than the group info sent in the event.
+                     */
+                    String[] regGroups = ( active ? (String[]) pair.getValue() :
+                                           ( (LocatorGroupsStruct) allDiscoveredRegs.get( reg ) ).groups );
+
+                    if( active || interested( regGroups, regInfo.groups ) )
+                    {
+                        discardMap.put( reg, regGroups );
+                        discardedRegs.add( reg );
+                        ( regInfo.discoveredRegsMap ).remove( reg );
+                    }//end if
+                }//end if
+            }//end loop
+            /* Build and send the "discarded event" */
+            RemoteDiscoveryEvent event = buildEvent( regInfo, discardMap, true );
+            if( event != null )
+            {
+                queueEvent( regInfo, event );
+                logInfoEvents( "DiscardedEventTask.run(): "
+                               + "DISCARDED Event SENT to regInfo\n" );
+            }
+            return discardedRegs;
+        }//end maybeSendDiscardedEvent
+
+        /**
+         * This method returns true if the current instance of this class
+         * must be run after at least one task in the input task list with
+         * an index less than the <code>size</code> parameter (size may be
+         * less than tasks.size()).
+         * <p>
+         * Note that using List.get will be more efficient than List.iterator.
+         *
+         * @param tasks the tasks to consider.  A read-only List, with all
+         *              elements being an instanceof Task.
+         * @param size  elements with index less than size should be considered
+         */
+        public boolean runAfter( List tasks, int size )
+        {
+            return false;
+        }//end runAfter
+
+    }//end class DiscardedEventTask
+
+    /**
+     * This class represents a <code>Task</code> object that is placed
+     * in the <code>TaskManager</code> queue for processing in the thread
+     * pool. Instances of this class are placed on the task queue when
+     * registrations request that a given registrar be discarded.
+     * <p>
+     * The <code>run</code> method of this class will remove the indicated
+     * registrar from the registration's set of discovered registrars and
+     * if successfully removed, will build and send a remote discarded event
+     * to the registration's listener.
+     */
+    private final class DiscardRegistrarTask implements TaskManager.Task
+    {
+        /**
+         * Data structure record corresponding to the registration that has
+         * requested to have one of its discovered registrars discarded
+         */
+        public final RegistrationInfo regInfo;
+        /**
+         * The registrar to discard
+         */
+        public final ServiceRegistrar registrar;
+
+        /**
+         * Constructs an instance of this class and stores the registration
+         * information.
+         */
+        public DiscardRegistrarTask( RegistrationInfo regInfo,
+                                     ServiceRegistrar registrar )
+        {
+            this.regInfo = regInfo;
+            this.registrar = registrar;
+        }//end constructor
+
+        /**
+         * This method attempts to remove the indicated registrar from
+         * the registration's set of discovered registrars. If successful,
+         * this method builds and sends a remote discarded event to the
+         * registration's listener.
+         */
+        public void run()
+        {
+            concurrentObj.writeLock();
+            try
+            {
+                logInfoTasks( "DiscardRegistrarTask.run(): "
+                              + "registrar requested to be discarded" );
+                /* Remove registrar from regInfo's set and send event */
+                if( ( regInfo.discoveredRegsMap ).remove( registrar ) != null )
+                {
+                    HashMap groupsMap = mapRegToGroups( registrar,
+                                                        ( (LocatorGroupsStruct) allDiscoveredRegs.get( registrar ) ).groups );
+
+                    RemoteDiscoveryEvent event = buildEvent
+                        ( regInfo, groupsMap, true );
+                    if( event != null )
+                    {
+                        queueEvent( regInfo, event );
+                        logInfoEvents( "DiscardRegistrarTask.run(): "
+                                       + "DISCARDED Event was SENT\n" );
+                    }//endif
+                    maybeRemoveDiscardedRegFromGlobalSet( registrar );
+                }
+            }
+            finally
+            {
+                concurrentObj.writeUnlock();
+            }
+        }//end run
+
+        /**
+         * This method returns true if the current instance of this class
+         * must be run after at least one task in the input task list with
+         * an index less than the <code>size</code> parameter (size may be
+         * less than tasks.size()).
+         * <p>
+         * Note that using List.get will be more efficient than List.iterator.
+         *
+         * @param tasks the tasks to consider.  A read-only List, with all
+         *              elements being an instanceof Task.
+         * @param size  elements with index less than size should be considered
+         */
+        public boolean runAfter( List tasks, int size )
+        {
+            return false;
+        }//end runAfter
+    }//end class DiscardRegistrarTask
+
+    /**
+     * This class represents a <code>Task</code> object that is placed
+     * in the <code>TaskManager</code> queue for processing in the thread
+     * pool.  An instance of this class is placed on the task queue when a
+     * <code>DiscoveryEvent</code> instance indicating a changed event
+     * is received from the local discovery process.
+     * <p>
+     * The <code>run</code> method of this class will process event
+     * information resulting from a change in the state of the member
+     * groups of one or more lookup services (registrars). This task
+     * analyzes the group information in the event and, based on that
+     * information, determines which active registrations are no longer
+     * interested in the registrars referenced in the event. A
+     * <code>RemoteDiscoveryEvent</code> indicating a discarded event
+     * will be sent to each active registration that has lost interest
+     * in any of the registrars of the event.
+     */
+    private final class ChangedEventTask implements TaskManager.Task
+    {
+        /**
+         * The local event sent by the discovery manager.
+         */
+        public final DiscoveryEvent event;
+
+        /**
+         * Constructs an instance of this class and stores the event
+         */
+        public ChangedEventTask( DiscoveryEvent event )
+        {
+            this.event = event;
+        }//end constructor
+
+        /**
+         * This method processes the local discovery event information and
+         * determines, based on the current state of each active
+         * registration, to which such registration the appropriate
+         * <code>RemoteDiscoveryEvent</code> should be sent. After making
+         * the determination, the remote event appropriate for each
+         * registration is constructed and sent.
+         */
+        public void run()
+        {
+            concurrentObj.writeLock();
+            logInfoTasks( "ChangedEventTask.run(): processing CHANGED "
+                          + "event from discovery manager" );
+            try
+            {
+                Map groupsMap = event.getGroups();
+                HashSet allDiscardedRegs = new HashSet( groupsMap.size() );
+                HashMap locatorMap = new HashMap( groupsMap.size() );
+                /* Retrieve the locators of each registrar in the event */
+                for( Iterator itr = ( groupsMap.keySet() ).iterator();
+                     itr.hasNext(); )
+                {
+                    ServiceRegistrar reg = (ServiceRegistrar) itr.next();
+                    locatorMap.put( reg,
+                                    ( (LocatorGroupsStruct) allDiscoveredRegs.get( reg ) ).locator );
+                }//end loop
+
+                for( Iterator itr = registrationByID.values().iterator();
+                     itr.hasNext(); )
+                {
+                    RegistrationInfo regInfo = (RegistrationInfo) itr.next();
+                    HashSet discardedRegs = maybeSendDiscardedEvent
+                        ( regInfo, groupsMap, locatorMap );
+                    /* Transfer the just-discarded regs to the summary set */
+                    for( Iterator jtr = discardedRegs.iterator(); jtr.hasNext(); )
+                    {
+                        allDiscardedRegs.add( jtr.next() );
+                    }//end loop
+                }//end loop
+                maybeRemoveDiscardedRegsFromGlobalSet( allDiscardedRegs );
+                updateGroupsInGlobalSet( groupsMap ); //replace with new groups
+            }
+            finally
+            {
+                concurrentObj.writeUnlock();
+            }
+        }//end run
+
+        /**
+         * This method determines, based on the current state of the
+         * <code>regInfo</code> parameter, whether or not to send a
+         * remote discarded event to the regInfo's listener, and then builds
+         * and sends the event if appropriate. This method is called in
+         * response to the receipt of a changed event from the discovery
+         * manager.
+         * <p>
+         * Such an event may indicate what is referred to as a
+         * "passive, no interest" discard; passive because the event
+         * resulted from action taken by the discovery manager rather than
+         * the client, and no interest because the discovery manager sends
+         * such an event when it determines that one or more of the
+         * previously discovered registrars - although still reachable -
+         * have changed their member groups in such a way that they may
+         * now be of no interest to one or more of the client registrations.
+         * <p>
+         * Note that changed events can be sent for registrars having a
+         * locator and member groups that the current regInfo never asked
+         * to be discovered. This can happen because some other regInfo
+         * asked that the registrar's locator or groups be discovered.
+         * <p>
+         * If a particular registrar is contained in the discovered set
+         * of the given regInfo, then we know that that regInfo must
+         * have requested discovery of the registrar (through either
+         * locator or group discovery). If the registrar is not contained
+         * in that set, then there's no need to proceed with the processing
+         * of the registrar since we don't want to send a discarded event
+         * to a regInfo that was never interested in that registrar in the
+         * first place.
+         * <p>
+         * If the locator of the registrar is contained in the regInfo's
+         * set of locators to discover, then that regInfo is considered
+         * "still interested" in the registrar; and so no discarded event
+         * is sent to the regInfo.
+         * <p>
+         * Thus, a discarded event is sent to the given regInfo only
+         * if the regInfo is not interested in discovering the registrar
+         * through locator discovery, and the registrar's member groups have
+         * changed in such a way that it now belongs to groups that
+         * the regInfo is not interested in discovering and joining.
+         *
+         * @param regInfo    the data structure record corresponding to the
+         *                   registration whose listener will receive the event
+         * @param groupsMap  mapping from the registrars referenced in the
+         *                   just-received event to their corresponding set of
+         *                   member groups
+         * @param locatorMap mapping from the registrars referenced in the
+         *                   just-received event to their corresponding locator
+         * @return the registrars that were discarded for the given regInfo
+         */
+        private HashSet maybeSendDiscardedEvent( RegistrationInfo regInfo,
+                                                 Map groupsMap,
+                                                 Map locatorMap )
+        {
+            /* For each registrar discard candidate, send a discarded event if:
+             *   The candidate is in the discovered set and
+             *    a. regInfo is configured for at least group discovery
+             *    b. candidate is NOT to be discovered by locator discovery
+             *    c. regInfo is no longer interested in the candidate's groups
+             */
+            HashSet discardedRegs = new HashSet( groupsMap.size() ); //return val
+            /* If this regInfo isn't interested in groups, go to next regInfo*/
+            if( ( regInfo.groups != null ) && ( ( regInfo.groups ).size() == 0 ) )
+            {
+                return discardedRegs;
+            }
+            HashMap discardMap = new HashMap( groupsMap.size() );
+            /* loop thru the (registrar,groups) pairs, find regs to discard */
+            Set eSet = groupsMap.entrySet();
+            for( Iterator itr = eSet.iterator(); itr.hasNext(); )
+            {
+                Map.Entry pair = (Map.Entry) itr.next();
+                ServiceRegistrar reg = (ServiceRegistrar) pair.getKey();
+                String[] regGroups = (String[]) pair.getValue();
+                LookupLocator regLoc = (LookupLocator) locatorMap.get( reg );
+                /* Include the current reg in the discard map only if that
+                 * reg is in the regInfo's discovered set, the regInfo
+                 * is not interested in discovering the reg through locator
+                 * discovery, and the reg's member groups have changed in
+                 * such a way that it now belongs to groups that the regInfo
+                 * is not interested in discovering and joining.
+                 */
+                if( ( ( regInfo.discoveredRegsMap ).containsKey( reg ) )
+                    && ( !interested( regLoc, regGroups,
+                                      regInfo.locators, regInfo.groups ) ) )
+                {
+                    discardMap.put( reg, regGroups );
+                    discardedRegs.add( reg );
+                    ( regInfo.discoveredRegsMap ).remove( reg );
+                }
+            }//end loop
+            /* Build and send the "discarded event" */
+            RemoteDiscoveryEvent event = buildEvent( regInfo, discardMap, true );
+            if( event != null )
+            {
+                queueEvent( regInfo, event );
+                logInfoEvents( "ChangedEventTask.run(): "
+                               + "DISCARDED Event was SENT\n" );
+            }//endif
+            return discardedRegs;
+        }//end maybeSendDiscardedEvent
+
+        /**
+         * This method returns true if the current instance of this class
+         * must be run after at least one task in the input task list with
+         * an index less than the <code>size</code> parameter (size may be
+         * less than tasks.size()).
+         * <p>
+         * Note that using List.get will be more efficient than List.iterator.
+         *
+         * @param tasks the tasks to consider.  A read-only List, with all
+         *              elements being an instanceof Task.
+         * @param size  elements with index less than size should be considered
+         */
+        public boolean runAfter( List tasks, int size )
+        {
+            return false;
+        }//end runAfter
+
+    }//end class ChangedEventTask
+
+    /**
+     * This class represents a <code>Task</code> object that is placed
+     * in the <code>TaskManager</code> queue for processing in the thread
+     * pool. Instances of this class are placed on the task queue when
+     * a registration has requested the augmentation of the set of groups
+     * that currently will be discovered for it.
+     */
+    private final class AddGroupsTask implements TaskManager.Task
+    {
+        /**
+         * Data structure record of the registration that made the request
+         */
+        public final RegistrationInfo regInfo;
+        /**
+         * The group set with which to replace the registration's old set
+         */
+        public final String[] groups;
+
+        /**
+         * Constructs an instance of this class and stores the input
+         */
+        public AddGroupsTask( RegistrationInfo regInfo, String[] groups )
+        {
+            this.regInfo = regInfo;
+            this.groups = groups;
+        }//end constructor
+
+        public void run()
+        {
+            /* For the regInfo associated with the current instance of this
+             * task, do the following:
+             * a. in the given regInfo data structure, add the new groups,
+             *    with duplicates removed, to that regInfo's current set of
+             *    desired groups
+             * b. from the global mapping of all currently discovered 
+             *    registrars to (locator,groups) pairs, retrieve the elements
+             *    that contain registrars belonging to groups that regInfo
+             *    should now be interested in as a result of the call to
+             *    addGroups
+             * c. for each registrar-to-(locator,groups) mapping retrieved in
+             *    b. above, add that mapping to the given regInfo data
+             *    structure's discovered state (these are the registrars that
+             *    were previously discovered for OTHER regInfo's, not the
+             *    current regInfo)
+             * d. for each of the registrars previously discovered for other
+             *    registrations that belong to any of the new groups regInfo
+             *    is now interested in as a result of the call to addGroups,
+             *    queue a remote discovery event to be sent to that regInfo's
+             *    listener
+             * e. if any of the new groups regInfo is now interested in as
+             *    as a result of the call to addGroups were not previously
+             *    in the local discovery manager's managed set of groups,
+             *    (and the local discovery manager is currently not configured
+             *    to discover ALL_GROUPS), add the new groups to the local
+             *    discovery manager so that when that manager does discover
+             *    one of those groups in the future, a remote discovered
+             *    event will be sent to the given regInfo's listener
+             */
+            concurrentObj.writeLock();
+            try
+            {
+                HashSet newGroupSet = addRegInfoGroups( regInfo, groups );  // a.
+                if( newGroupSet.size() > 0 )
+                {
+                    logInfoTasks( "AddGroupsTask.run(): adding to the "
+                                  + "registration's groups" );
+                    HashMap discoveredRegs = getDesiredRegsByGroup
+                        ( regInfo ); // b.
+                    HashMap regsAdded = regInfo.addToDiscoveredRegs
+                        ( discoveredRegs ); // c.
+                    RemoteDiscoveryEvent event = buildEvent
+                        ( regInfo, regsAdded, false ); // d.
+                    if( event != null )
+                    {
+                        queueEvent( regInfo, event );                       // d.
+                        logInfoEvents( "AddGroupsTask.run(): DISCOVERED "
+                                       + "Event was SENT\n" );
+                    }//endif
+                    updateDiscoveryMgrGroups();                          // e.
+                }//endif(newGroupSet.size() > 0)
+            }
+            finally
+            {
+                concurrentObj.writeUnlock();
+            }
+        }//end run
+
+        /**
+         * Augments the registration's managed set of groups with the new
+         * groups.
+         *
+         * @return the set of new groups added to regInfo's desired groups
+         */
+        private HashSet addRegInfoGroups( RegistrationInfo regInfo,
+                                          String[] groups )
+        {
+            /* Build a HashSet (removes duplicates) from the input groups */
+            HashSet newGroupSet = new HashSet( 1 );
+            for( int i = 0; i < groups.length; i++ )
+            {
+                newGroupSet.add( groups[ i ] );
+            }//end loop
+            /* If the input set was not empty, add the new groups to the 
+             * registration's managed set of groups.
+             */
+            if( newGroupSet.size() > 0 )
+            {
+                ( regInfo.groups ).addAll( newGroupSet );
+            }//endif
+            return newGroupSet;
+        }//end addRegInfoGroups
+
+        /**
+         * This method returns true if the current instance of this class
+         * must be run after at least one task in the input task list with
+         * an index less than the <code>size</code> parameter (size may be
+         * less than tasks.size()).
+         * <p>
+         * Note that using List.get will be more efficient than List.iterator.
+         *
+         * @param tasks the tasks to consider.  A read-only List, with all
+         *              elements being an instanceof Task.
+         * @param size  elements with index less than size should be considered
+         */
+        public boolean runAfter( List tasks, int size )
+        {
+            return false;
+        }//end runAfter
+    }//end class AddGroupsTask
+
+    /**
+     * This class represents a <code>Task</code> object that is placed
+     * in the <code>TaskManager</code> queue for processing in the thread
+     * pool. Instances of this class are placed on the task queue when
+     * a registration has requested the replacement of the set of groups
+     * that currently will be discovered for it.
+     */
+    private final class SetGroupsTask implements TaskManager.Task
+    {
+        /**
+         * Data structure record of the registration that made the request
+         */
+        public final RegistrationInfo regInfo;
+        /**
+         * The group set with which to replace the registration's old set
+         */
+        public final String[] groups;
+
+        /**
+         * Constructs an instance of this class and stores the input
+         */
+        public SetGroupsTask( RegistrationInfo regInfo, String[] groups )
+        {
+            this.regInfo = regInfo;
+            this.groups = groups;
+        }//end constructor
+
+        public void run()
+        {
+            /* For the regInfo associated with the current instance of this
+             * task, do the following:
+             * a. from the global mapping of all currently discovered 
+             *    registrars to their (locator,groups) pair, retrieve the
+             *    elements that contain registrars belonging to groups that
+             *    regInfo was interested in PRIOR to the call to setGroups
+             * b. in the given regInfo data structure, replace that regInfo's
+             *    current set of desired groups with the new set of desired
+             *    groups that resulted from the call to setGroups
+             * c. again from the global mapping of all currently discovered 
+             *    registrars to (locator,groups) pairs, retrieve the elements
+             *    that contain registrars belonging to groups that regInfo
+             *    should now be interested in as a result of the call to
+             *    setGroups
+             * d. for each registrar-to-(locator,groups) mapping retrieved in
+             *    c. above, add that mapping to the given regInfo data
+             *    structure's state (these are the registrars that were
+             *    previously discovered for OTHER regInfo's, not the current
+             *    regInfo)
+             * e. for each of the registrars previously discovered for other
+             *    registrations that belong to any of the new groups regInfo
+             *    is now interested in as a result of the call to setGroups,
+             *    queue a remote discovery event to be sent to that regInfo's
+             *    listener
+             * f. from the mapping of already-discovered registrars that 
+             *    regInfo was interested in prior to the call to setGroups
+             *    (the mapping retrieved in a. above), retrieve the elements
+             *    that contain registrars belonging to groups that regInfo is
+             *    no longer interested in due to the call to setGroups
+             * g. for each registrar-to-(locator,groups) mapping retrieved in
+             *    f. above, remove that mapping from the given regInfo data
+             *    structure's state, and queue a remote discarded event to be
+             *    sent to that regInfo's listener
+             * h. if any of the new groups regInfo is now interested in as
+             *    as a result of the call to setGroups were not previously
+             *    in the local discovery manager's managed set of groups,
+             *    add those groups to that discovery manager so that when
+             *    that manager does discover one of those groups in the  
+             *    future, a remote discovered event will be sent to the given
+             *    regInfo's listener
+             */
+            concurrentObj.writeLock();
+            try
+            {
+                logInfoTasks( "SetGroupsTask.run(): setting the "
+                              + "registration's groups" );
+                Map oldDesiredRegs = getDesiredRegsByGroup( regInfo );     // a.
+                setRegInfoGroups( regInfo, groups );                        // b.
+                HashMap newDesiredRegs = getDesiredRegsByGroup( regInfo ); // c.
+                HashMap regsAdded = regInfo.addToDiscoveredRegs
+                    ( newDesiredRegs ); // d.
+                RemoteDiscoveryEvent event = buildEvent
+                    ( regInfo, regsAdded, false ); // e.
+                if( event != null )
+                {
+                    queueEvent( regInfo, event );                           // e.
+                    logInfoEvents( "SetGroupsTask.run(): DISCOVERED "
+                                   + "Event was SENT\n" );
+                }//endif
+                Map discardRegs = getUndesiredRegsByGroup
+                    ( oldDesiredRegs, regInfo ); // f.
+                for( Iterator itr = ( discardRegs.keySet() ).iterator();
+                     itr.hasNext(); )
+                {
+                    ( regInfo.discoveredRegsMap ).remove( itr.next() );     // g.
+                }//end loop
+                event = buildEvent( regInfo, discardRegs, true );           // g.
+                if( event != null )
+                {
+                    queueEvent( regInfo, event );                          // g.
+                    logInfoEvents( "SetGroupsTask.run(): "
+                                   + "DISCARDED Event was SENT\n" );
+                }//endif
+                updateDiscoveryMgrGroups();                             // h.
+            }
+            finally
+            {
+                concurrentObj.writeUnlock();
+            }
+        }//end run
+
+        /**
+         * Replaces the registration's managed set of groups with the new
+         * groups (even if the new set of groups is empty -- this just means
+         * group discovery will be "turned off" for this registration).
+         */
+        private void setRegInfoGroups( RegistrationInfo regInfo,
+                                       String[] groups )
+        {
+            if( groups == DiscoveryGroupManagement.ALL_GROUPS )
+            {
+                regInfo.groups = null;
+            }
+            else
+            {
+                /* Build a HashSet from the input set */
+                HashSet newGroups = new HashSet();
+                for( int i = 0; i < groups.length; i++ )
+                {
+                    newGroups.add( groups[ i ] );
+                }//end loop
+                /* Prepare the registration's managed set for replacement */
+                if( regInfo.groups == null )
+                {
+                    regInfo.groups = new HashSet();
+                }
+                else
+                {
+                    ( regInfo.groups ).clear();
+                }//endif
+                /* Replace the registration's managed set with the new set */
+                ( regInfo.groups ).addAll( newGroups );
+            }//end if (groups == DiscoveryGroupManagement.ALL_GROUPS)
+        }//end setRegInfoGroups
+
+        /**
+         * This method returns true if the current instance of this class
+         * must be run after at least one task in the input task list with
+         * an index less than the <code>size</code> parameter (size may be
+         * less than tasks.size()).
+         * <p>
+         * Note that using List.get will be more efficient than List.iterator.
+         *
+         * @param tasks the tasks to consider.  A read-only List, with all
+         *              elements being an instanceof Task.
+         * @param size  elements with index less than size should be considered
+         */
+        public boolean runAfter( List tasks, int size )
+        {
+            return false;
+        }//end runAfter
+    }//end class SetGroupsTask
+
+    /**
+     * This class represents a <code>Task</code> object that is placed
+     * in the <code>TaskManager</code> queue for processing in the thread
+     * pool. Instances of this class are placed on the task queue when
+     * a registration has requested the removal of a set of groups from
+     * the current set of groups to discover for it.
+     */
+    private final class RemoveGroupsTask implements TaskManager.Task
+    {
+        /**
+         * Data structure record of the registration that made the request
+         */
+        public final RegistrationInfo regInfo;
+        /**
+         * The groups to remove from the registration's old set
+         */
+        public final String[] groups;
+
+        /**
+         * Constructs an instance of this class and stores the input
+         */
+        public RemoveGroupsTask( RegistrationInfo regInfo, String[] groups )
+        {
+            this.regInfo = regInfo;
+            this.groups = groups;
+        }//end constructor
+
+        public void run()
+        {
+            concurrentObj.writeLock();
+            try
+            {
+                if( groups.length == 0 )
+                {
+                    return; // nothing from which to remove
+                }
+                logInfoTasks( "RemoveGroupsTask.run(): removing groups from "
+                              + "the registration's current group set" );
+                /* regInfo's discovered regs (by group) previously desired */
+                Map oldDesiredRegs = getDesiredRegsByGroup( regInfo );
+                /* update regInfo's desired regs */
+                removeRegInfoGroups( regInfo, groups );
+                /* regInfo's discovered regs (by group) no longer desired */
+                Map discardRegs = getUndesiredRegsByGroup( oldDesiredRegs,
+                                                           regInfo );
+                /* remove regInfo's undesired regs from its discovered map */
+                for( Iterator itr = ( discardRegs.keySet() ).iterator();
+                     itr.hasNext(); )
+                {
+                    ( regInfo.discoveredRegsMap ).remove( itr.next() );
+                }//end loop
+                RemoteDiscoveryEvent event = buildEvent
+                    ( regInfo, discardRegs, true );
+                if( event != null )
+                {
+                    queueEvent( regInfo, event );
+                    logInfoEvents( "RemoveGroupsTask.run(): "
+                                   + "DISCARDED Event was SENT\n" );
+                }//endif
+                updateDiscoveryMgrGroups(); // may send more discards
+            }
+            finally
+            {
+                concurrentObj.writeUnlock();
+            }
+        }//end run
+
+        /**
+         * Removes the elements of the given set from the given registration's
+         * current set of groups to discover.
+         */
+        private void removeRegInfoGroups( RegistrationInfo regInfo,

[... 7597 lines stripped ...]


Mime
View raw message