You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by ni...@apache.org on 2008/12/10 06:19:01 UTC
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...
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 ...]