/* * 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.fiddler.Fiddler; import com.sun.jini.fiddler.FiddlerAdminProxy; import com.sun.jini.fiddler.FiddlerProxy; import com.sun.jini.fiddler.FiddlerRegistration; import com.sun.jini.fiddler.FiddlerRenewResults; 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.ReliableLog; import com.sun.jini.reliableLog.LogException; import com.sun.jini.reliableLog.LogHandler; 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 net.jini.activation.ActivationExporter; import net.jini.activation.ActivationGroup; import net.jini.config.Configuration; import net.jini.config.ConfigurationProvider; import net.jini.config.ConfigurationException; import net.jini.config.NoSuchEntryException; import net.jini.discovery.DiscoveryEvent; import net.jini.discovery.DiscoveryChangeListener; 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.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; import net.jini.security.proxytrust.ServerProxyTrust; import net.jini.security.proxytrust.TrustEquivalence; import net.jini.core.discovery.LookupLocator; 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.ServiceID; import net.jini.core.lookup.ServiceRegistrar; import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import java.lang.reflect.Array; import java.net.InetAddress; import java.net.MalformedURLException; import java.io.InterruptedIOException; import java.io.InputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; import java.rmi.activation.ActivationException; import java.rmi.activation.ActivationID; import java.rmi.activation.ActivationSystem; import java.rmi.MarshalledObject; import java.rmi.NoSuchObjectException; import java.rmi.RemoteException; import java.rmi.Remote; import java.rmi.server.ExportException; import java.rmi.server.RemoteObject; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Arrays; 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.Properties; import java.util.Set; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; /** * 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> * * @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>. * * @author Sun Microsystems, Inc. */ 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, String[] groups) { HashSet removeSet = new HashSet(); for(int i=0;i<groups.length;i++) { removeSet.add(groups[i]); }//end loop (regInfo.groups).removeAll(removeSet); }//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 RemoveGroupsTask /** 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 locators * that currently will be discovered for it. */ private final class AddLocatorsTask implements TaskManager.Task { /** Data structure record of the registration that made the request */ public final RegistrationInfo regInfo; /** The locator set with which to replace the registration's old set */ public final LookupLocator[] locators; /** Constructs an instance of this class and stores the input */ public AddLocatorsTask(RegistrationInfo regInfo, LookupLocator[] locators) { this.regInfo = regInfo; this.locators = locators; }//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 locators, * with duplicates removed, to that regInfo's current set of * desired locators * b. from the global mapping of all currently discovered * registrars to (locator,groups) pairs, retrieve the elements * that contain registrars having locators that regInfo * should now be interested in as a result of the call to * addLocators * 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 have locators equal to any of the new * locators regInfo is now interested in as a result of the * call to addLocators, queue a remote discovery event to be * sent to that regInfo's listener * e. if any of the new locators regInfo is now interested in as * as a result of the call to addLocators were not previously * in the local discovery manager's managed set of locators, * add the new locators to the local discovery manager so that * when that manager does discover one of those locators in * the future, a remote discovered event will be sent to the * given regInfo's listener */ concurrentObj.writeLock(); try { HashSet newLocSet = addRegInfoLocators(regInfo,locators);// a. if(newLocSet.size() > 0) { logInfoTasks("AddLocatorsTask.run(): adding to the " +"registration's locators"); HashMap discoveredRegs = getDesiredRegsByLocator (regInfo); // b. HashMap regsAdded = regInfo.addToDiscoveredRegs (discoveredRegs); // c. RemoteDiscoveryEvent event = buildEvent (regInfo,regsAdded,false); // d. if(event != null) { queueEvent(regInfo,event); // d. logInfoEvents("AddLocatorsTask.run(): DISCOVERED " +"Event was SENT\n"); }//endif updateDiscoveryMgrLocators(); // e. }//endif(newLocSet.size() > 0) } finally { concurrentObj.writeUnlock(); } }//end run /** Augments the registration's managed set of locators with the new * locators. * * @return the set of new locators added to regInfo's desired locators */ private HashSet addRegInfoLocators(RegistrationInfo regInfo, LookupLocator[] locators) { /* Build a HashSet (removes duplicates) from the input locators */ HashSet newLocSet = new HashSet(1); for(int i=0;i<locators.length;i++) { newLocSet.add(locators[i]); }//end loop /* If the input set was not empty, add the new locators to the * registration's managed set of locators. */ if( newLocSet.size() > 0 ) { (regInfo.locators).addAll(newLocSet); }//endif return newLocSet; }//end addRegInfoLocators /** 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 AddLocatorsTask /** 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 locators * that currently will be discovered for it. */ private final class SetLocatorsTask implements TaskManager.Task { /** Data structure record of the registration that made the request */ public final RegistrationInfo regInfo; /** The locator set with which to replace the registration's old set */ public final LookupLocator[] locators; /** Constructs an instance of this class and stores the input */ public SetLocatorsTask(RegistrationInfo regInfo, LookupLocator[] locators) { this.regInfo = regInfo; this.locators = locators; }//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 having locators 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 locators with the new set of desired * locators that resulted from the call to setLocators * c. again from the global mapping of all currently discovered * registrars to (locator,groups) pairs, retrieve the elements * that contain registrars having locators that regInfo should * now be interested in as a result of the call to setLocators * 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 have locators equal to the new locators * regInfo is now interested in as a result of the call to * setLocators, 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 setLocators * (the mapping retrieved in a. above), retrieve the elements * that contain registrars having locators that regInfo is * no longer interested in due to the call to setLocators * 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 locators regInfo is now interested in as * as a result of the call to setLocators were not previously * in the local discovery manager's managed set of locators, * add those locators to that discovery manager so that when * that manager does discover one of those locators in the * future, a remote discovery event will be sent to the given * regInfo's listener */ concurrentObj.writeLock(); try { logInfoTasks("SetLocatorsTask.run(): setting the " +"registration's locators"); Map oldDesiredRegs = getDesiredRegsByLocator(regInfo); // a. setRegInfoLocators(regInfo,locators); // b. HashMap newDesiredRegs = getDesiredRegsByLocator(regInfo);// c. HashMap regsAdded = regInfo.addToDiscoveredRegs (newDesiredRegs); // d. RemoteDiscoveryEvent event = buildEvent (regInfo,regsAdded,false); // e. if(event != null) { queueEvent(regInfo,event); // e. logInfoEvents("SetLocatorsTask.run(): DISCOVERED " +"Event was SENT\n"); }//endif Map undesiredRegs = getUndesiredRegsByLocator (oldDesiredRegs,regInfo); // f. HashMap discardRegs = new HashMap(undesiredRegs.size()); for(Iterator itr = (undesiredRegs.keySet()).iterator(); itr.hasNext(); ) { ServiceRegistrar reg = (ServiceRegistrar)itr.next(); (regInfo.discoveredRegsMap).remove(reg); // g. discardRegs.put(reg, ((LocatorGroupsStruct)allDiscoveredRegs.get(reg)).groups); }//end loop event = buildEvent(regInfo,discardRegs,true); // g. if(event != null) { queueEvent(regInfo,event); // g. logInfoEvents("SetLocatorsTask.run(): " +"DISCARDED Event was SENT\n"); }//endif updateDiscoveryMgrLocators(); // h. } finally { concurrentObj.writeUnlock(); } }//end run /** Replaces the registration's managed set of locators with the new * locators (even if the new set of locators is empty -- this just * means locator discovery will be "turned off" for this registration) */ private void setRegInfoLocators(RegistrationInfo regInfo, LookupLocator[] locators) { /* Build a HashSet from the input set */ HashSet newLocSet = new HashSet(); for(int i=0;i<locators.length;i++) { newLocSet.add(locators[i]); }//end loop /* Prepare the registration's managed set for replacement */ if(regInfo.locators == null) { regInfo.locators = new HashSet(); } else { (regInfo.locators).clear(); }//endif /* Replace the registration's managed set with the new set */ (regInfo.locators).addAll(newLocSet); }//end setRegInfoLocators /** 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 SetLocatorsTask /** 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 locators * from the current set of locators to discover for it. */ private final class RemoveLocatorsTask implements TaskManager.Task { /** Data structure record of the registration that made the request */ public final RegistrationInfo regInfo; /** The locators to remove from the registration's old set */ public final LookupLocator[] locators; /** Constructs an instance of this class and stores the input */ public RemoveLocatorsTask(RegistrationInfo regInfo, LookupLocator[] locators) { this.regInfo = regInfo; this.locators = locators; }//end constructor public void run() { concurrentObj.writeLock(); try { if(locators.length == 0) return; //nothing from which to remove logInfoTasks("RemoveLocatorsTask.run(): removing locators " +"from the registration's current locator set"); /* regInfo's discovered regs (by locator) previously desired */ Map oldDesiredRegs = getDesiredRegsByLocator(regInfo); /* update regInfo's desired regs */ removeRegInfoLocators(regInfo,locators); /* regInfo's discovered regs (by locator) no longer desired */ Map undesiredRegs = getUndesiredRegsByLocator (oldDesiredRegs,regInfo); /* remove regInfo's undesired regs from its discovered map, * and construct the registrars-to-groups map for the event */ HashMap discardRegs = new HashMap(undesiredRegs.size()); for(Iterator itr = (undesiredRegs.keySet()).iterator(); itr.hasNext(); ) { ServiceRegistrar reg = (ServiceRegistrar)itr.next(); (regInfo.discoveredRegsMap).remove(reg); discardRegs.put(reg, ((LocatorGroupsStruct)allDiscoveredRegs.get(reg)).groups); }//end loop /* Construct the registrars-to-groups map for the event */ RemoteDiscoveryEvent event = buildEvent (regInfo,discardRegs,true); if(event != null) { queueEvent(regInfo,event); logInfoEvents("SetLocatorsTask.run(): " +"DISCARDED Event was SENT\n"); }//endif updateDiscoveryMgrLocators(); // may send more discards } finally { concurrentObj.writeUnlock(); } }//end run /** Removes the elements of the given set from the given registration's * current set of locators to discover. */ private void removeRegInfoLocators(RegistrationInfo regInfo, LookupLocator[] locators) { /* Build a HashSet from the input set */ HashSet removeSet = new HashSet(); for(int i=0;i<locators.length;i++) { removeSet.add(locators[i]); }//end loop (regInfo.locators).removeAll(removeSet); }//end removeRegInfoLocators /** 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 RemoveLocatorsTask /** 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 remote event is to be sent to a given registration. * <p> * Remote events are sent in a separate task such as this to avoid * making the remote call to the registration's listener within a * synchronization block. */ private final class SendEventTask implements TaskManager.Task { /** Data structure record corresponding to registration to get event */ public final RegistrationInfo regInfo; /** The remote event to send to the given registration's listener */ public final RemoteDiscoveryEvent event; /** Constructs an instance of this class and stores the registration * information. */ public SendEventTask(RegistrationInfo regInfo, RemoteDiscoveryEvent event) { this.regInfo = regInfo; this.event = event; }//end constructor /** This method sends a <code>RemoteDiscoveryEvent</code> to the * listener of the registration that corresponds to the * <code>regInfo</code> field of this class. This method handles * all exceptions and error conditions in the appropriate manner. */ public void run() { try { regInfo.listener.notify(event); } catch (Throwable e) { problemLogger.log(Level.INFO, "Exception in SendEventTask", e); switch (ThrowableConstants.retryable(e)) { case ThrowableConstants.BAD_OBJECT: if(e instanceof Error) throw (Error)e; case ThrowableConstants.BAD_INVOCATION: case ThrowableConstants.UNCATEGORIZED: /* If the listener throws UnknownEvent or some other * definite exception, or the listener is gone, it's * okay to cancel the lease. */ concurrentObj.writeLock(); try { try { logInfoEvents (" Cancelling lease on registration: " +" (registrationID,leaseID) = (" +regInfo.registrationID+", " +regInfo.leaseID+")"); cancelLeaseDo(regInfo,regInfo.leaseID); addLogRecord(new LeaseCancelledLogObj (regInfo.registrationID, regInfo.leaseID)); } catch (UnknownLeaseException ee) { } catch (IOException ee) { } } finally { concurrentObj.writeUnlock(); } }//end switch }//end try }//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 SendEventTask /** * Handler class for the persistent storage facility. * <p> * At any point during processing in this service, there will exist * both a 'snapshot' of the service's state and a set of records * detailing each significant change that has occurred to the state * since the snapshot was taken. The snapshot information and the * incremental change information will be stored in separate files * called, respectively, the snapshot file and the log file. Together, * these files are used to recover the state of the service after a * crash or a network outage (or if the service or its ActivationGroup * is un-registered and then re-registered through the Activation Daemon). * <p> * This class contains the methods that are used to record and recover * the snapshot of the service's state; as well as the method used to * apply the state changes that were recorded in the log file. * <p> * When the ReliableLog class is instantiated, a new instance of this * class is passed to its constructor so that the methods of this * class may be invoked by the methods defined in the ReliableLog. * Because this class extends the LogHandler class associated with * the ReliableLog class, this class must provide implementations of * the abstract methods declared in the LogHandler. Also, some of the * methods defined in this class override the methods of the LogHandler * in order to customize the handling of snapshot creation and * retrieval. * <p> * Each significant change to the service's state is written to the * log file as an individual record (when addLogRecord() is invoked). * After the number of records logged exceeds a pre-defined threshold, * a snapshot of the state is recorded by invoking -- through the * ReliableLog and its LogHandler -- the snapshot() method defined in * this class. After the snapshot is taken, the log file is cleared * and the incremental log process starts over. * <p> * The contents of the snapshot file reflect the DATA contained in * the fields making up the current state of the service. That data * represents many changes -- over time -- to the service's state. * On the other hand, each record written to the log file is an object * that reflects both the data used and the ACTIONS taken to make one * change to the service's state at a particular point in time. * <p> * During recovery, the state of the service at the time of a crash * or outage is re-constructed by first retrieving the 'base' state from * the snapshot file; and then modifying that base state according to * the records retrieved from the log file. The reconstruction of the * base state is achieved by invoking the recover() method defined in * this class. The modifications recorded in the log file are then * applied to the base state by invoking the applyUpdate() method * defined in this class. Both recover() and applyUpdate() are invoked * through the ReliableLog and its associated LogHandler. * <p> * NOTE: The following lines must be added to the service's policy file * <pre> * permission java.io.FilePermission "dirname", "read,write,delete"; * permission java.io.FilePermission "dirname/-", "read,write,delete"; * </pre> * where 'dirname' is the name of the directory path (relative or * absolute) where the snapshot and log file will be maintained. */ private class LocalLogHandler extends LogHandler { /** No-arg public constructor */ public LocalLogHandler() { } /* Overrides snapshot() defined in ReliableLog's LogHandler class. */ public void snapshot(OutputStream out) throws IOException { takeSnapshot(out); }//end snapshot /* Overrides recover() defined in ReliableLog's LogHandler class. */ public void recover(InputStream in) throws IOException, ClassNotFoundException { recoverSnapshot(in); }//end recover /** * Required method that implements the abstract applyUpdate() * defined in ReliableLog's associated LogHandler class. * <p> * During state recovery, the recover() method defined in the * ReliableLog class is invoked. That method invokes the method * recoverUpdates() which invokes the method readUpdates(). Both * of those methods are defined in ReliableLog. The method * readUpdates() retrieves a record from the log file and then * invokes this method. * <p> * This method invokes the version of the method apply() that * corresponds to the particular type of 'log record' object * that is input as the first argument. The log record object and its * corresponding apply() method are defined in one of the so-called * LogObj classes. Any instance of one the LogObj classes is an * implementation of the LogRecord interface. The particular * implementation that is input to this method is dependent on the * type of record that was originally logged. The apply() method * will then modify the state of the service in a way dictated * by the type of record that was retrieved. */ public void applyUpdate(Object logRecObj) { ((LogRecord)logRecObj).apply(FiddlerImpl.this); }//end applyUpdate }//end class LocalLogHandler /* ******************* END Inner Class Definitions ********************* */ /* ******************* BEGIN Thread Class Definitions ****************** */ /** Thread which is used to monitor the current leases in effect and * cancel (expire) those leases with expiration times that have exceeded * the current time. */ private class LeaseExpireThread extends InterruptedStatusThread { /** Create a daemon thread */ public LeaseExpireThread() { super("lease expire"); setDaemon(true); }//end constructor public void run() { try { concurrentObj.writeLock(); } catch (ConcurrentLockException e) { return; } try { while (!hasBeenInterrupted()) { long curTime = System.currentTimeMillis(); minExpiration = Long.MAX_VALUE; /* Loop through registrationByTime removing registrations * with expiration times that are earlier than the current * time. The logic of this loop relies on the fact that * registrationByTime is a TreeMap in which the elements * are ordered (in ascending order) by the lease expiration * times. Thus, when one registration is encountered with * an expiration time that is later than the current time, * it can be assumed that all remaining registrations have * expiration times that are also later than the current * time; and the loop can be exited. Until such a * registration is encountered, each registration is * removed from its various storage locations. */ while (!registrationByTime.isEmpty()) { RegistrationInfo regInfo = (RegistrationInfo)registrationByTime.firstKey(); if (regInfo.leaseExpiration > curTime) { minExpiration = regInfo.leaseExpiration; break; } /* The removal of a registration typically involves the * the modification of the managed sets in the * discovery manager, which usually involves starting * the discovery protocol. An IOException can occur * when the discovery protocol fails to start. When * such an exception does occur, register an ERROR * status attribute (along with a Comment attribute * describing the nature of the problem) to all lookup * services with which this service is registered. * * Administrative clients, as well as clients that use * this service should have registered for notification * of the existence of this attribute. */ try { removeRegistration(regInfo); } catch(IOException e) { String eStr = "Failure while removing " +"registration (ID = " +regInfo.registrationID +") from service state"; if( problemLogger.isLoggable(Level.INFO) ) { problemLogger.log(Level.INFO, eStr, e); }//endif Entry[] errorAttrs = new Entry[] { new FiddlerStatus(StatusType.ERROR), new Comment(eStr) }; joinMgr.addAttributes(errorAttrs,true); } }//end while try { concurrentObj.writerWait(leaseExpireThreadSyncObj, (minExpiration - curTime)); } catch (ConcurrentLockException e) { return; } }//end while } finally { concurrentObj.writeUnlock(); } }//end run }//end class LeaseExpireThread /** * Snapshot-taking thread. * <p> * A snapshot is taken when -- after writing a new record to the * log file -- it is determined that the size of the log file has * exceeded a certain threshold. The code which adds the new record * to the log file and which, in turn, decides that a snapshot * must be taken is "wrapped" in a writer mutex. That is, synchronization * of processing is achieved in this service through a "reader/writer" * mutex construct. This construct allows only one writer at any one * time; but allows an unlimited number of simultaneous readers as * long as no writer has locked the mutex. During steady-state, it is * anticipated that far more "read actions" will occur (e.g. discovery * events being sent) than "write actions" (e.g. modifying the managed * sets). Since the process of taking a snapshot can be time-consuming, * if the whole snapshot-taking process occupies that single writer * mutex, then a significant number of read actions will be un-necessarily * blocked; possibly resulting in an unacceptable degradation in * response time. * <p> * It is for the above reason that the process of taking a snapshot is * performed in a separate thread. The thread waits on the monitor * belonging to the snapshotThreadSyncObj instance until it is notified * (or "signalled") that a snapshot must be taken. The notification * is sent by another thread, created by this service, which determines * when the conditions are right for a snapshot. The notification takes * the form of an interrupt indicating that the snapshot monitor is * available. Although the interrupt is sent while the writer mutex is * locked, the act of sending the notification is less time-consuming * than the act of taking the snapshot itself. When the thread receives * a notification, it awakens and requests a lock on the reader mutex * (this is all done in the readerWait() method). Because a reader -- not * a writer -- mutex is locked, read-only processes still have access * to the system state, so discovery events can be sent and the service's * state can be queried; but the reader mutex prevents changes to the * state while the snapshot is in progress. * <p> * Note that the current snapshot is guaranteed to complete before the * next snapshot request is received. This is because even though * the act of taking a snapshot can be viewed as a writer process, * the fact that the next snapshot notification will be wrapped in a * writer mutex, combined with the fact that a writer mutex can not * be locked while a reader mutex is locked, allows the snapshot to * be treated as a reader process. */ private class SnapshotThread extends InterruptedStatusThread { /** Create a daemon thread */ public SnapshotThread() { super("snapshot thread"); setDaemon(true); } public void run() { try { concurrentObj.readLock(); } catch (ConcurrentLockException e) { return; } try { while (!hasBeenInterrupted()) { try { concurrentObj.readerWait(snapshotThreadSyncObj, Long.MAX_VALUE); try { log.snapshot(); logFileSize = 0; } catch (Exception e) { if (hasBeenInterrupted()) return; /* If taking the snapshot fails for any reason, * then register an ERROR status attribute (along * with a Comment attribute describing the nature * of the problem) to all lookup services with * which this service is registered. * * Administrative clients, as well as clients that * use this service should have registered for * notification of the existence of this attribute. */ String eStr = "Failure while taking a snapshot of " +"the service state"; problemLogger.log(Level.INFO, eStr, e); Entry[] errorAttrs = new Entry[] { new FiddlerStatus(StatusType.ERROR), new Comment(eStr) }; joinMgr.addAttributes(errorAttrs,true); } } catch (ConcurrentLockException e) { return; } }//end while } finally { concurrentObj.readUnlock(); } }//end run }//end class SnapshotThread /** Thread which is used to terminate the current executing instance * of the Fiddler implementation of the lookup discovery service. * Termination processing is performed in a separate thread (that is, * in an instance of this class) in order to avoid deadlock that * can occur because ActivationGroup.inactive will block until all * in-progress RMI calls have completed. */ private class DestroyThread extends InterruptedStatusThread { /** Maximum delay for unexport attempts */ private static final long MAX_UNEXPORT_DELAY = 2*TimeConstants.MINUTES; /** Constructor that creates a non-daemon thread */ public DestroyThread() { super("destroy"); /* override inheritance from RMI daemon thread */ setDaemon(false); } public void run() { /* Must unregister before unexporting. Unregistering makes sure * that the object corresponding to the given activation ID can * no longer be activated through that ID. */ if (activationID != null) { try { activationSystem.unregisterObject(activationID); } catch (RemoteException e) { problemLogger.log(Level.WARNING, "aborting shutdown - " +"could not unregister activation ID", e); return;//give up until we can at least unregister } catch (ActivationException e) { problemLogger.log(Levels.HANDLED, "shutdown problem - " +"could not unregister activation ID", e); } } readyState.shutdown(); /* Unexport the object. This removes the object from the RMI * runtime so that the object can no longer accept incoming RMI * calls. * * An attempt to 'gracefully' unexport the object is initially * made. That is, for a finite period of time, an attempt is * made to allow all calls to the object that are in progress * or pending to complete before the object is unexported. If, * after that finite period of time, the object has not been * successfully unexported, the object is 'forcibly' unexported; * that is, the object is unexported even if there are calls to * the object that are in progress or still pending. */ final long endTime = System.currentTimeMillis()+MAX_UNEXPORT_DELAY; boolean unexported = false; /* Unexport only if there are no pending or in-progress calls*/ while(!unexported && (System.currentTimeMillis() < endTime)) { unexported = serverExporter.unexport(false); if(!unexported) Thread.yield(); }//end loop if(!unexported) {//Not yet unexported. Forcibly unexport serverExporter.unexport(true); }//endif /* all daemons must terminate before deleting persistent store */ leaseExpireThread.interrupt(); if(log != null) snapshotThread.interrupt(); taskMgr.terminate(); joinMgr.terminate(); joinMgrLDM.terminate(); discoveryMgr.terminate(); try { leaseExpireThread.join(); if(log != null) snapshotThread.join(); } catch (InterruptedException e) { } if(log != null) log.deletePersistentStore(); if (activationID != null) { /* Inform the activation system that the object corresponding * to the given activation ID is no longer active. */ try { ActivationGroup.inactive(activationID, serverExporter); } catch (RemoteException e) { } catch (ActivationException e) { } } else {//not activatable, tell starter it's ok to release for gc if(lifeCycle != null) lifeCycle.unregister(FiddlerImpl.this); }//endif(activationID != null) /* If applicable, logout of the JAAS login session */ if(loginContext != null) { try { loginContext.logout(); } catch(Exception e) { startupLogger.log(Level.INFO,"Problem logging out of " +"JAAS login session",e); } }//endif logInfoShutdown(); }//end run }//end class DestroyThread /* ******************* END Thread Class Definitions ******************** */ /* ************************ BEGIN Public Methods *********************** */ /* -------------------------------------------------------------------- * BEGIN net.jini.security.proxytrust.ServerProxyTrust */ /** * Returns a <code>TrustVerifier</code> specific to this service which * can be used to verify that a given proxy to this service can be * trusted. * <p> * The verifier returned by this method contains the method * {@link TrustVerifier#isTrustedObject isTrustedObject}. That method * can be called with a candidate proxy as the first argument, and * {@link net.jini.security.TrustVerifier.Context} * as the second argument. When called in this way, the * <code>isTrustedObject</code> determines whether or not the input * proxy is trusted. Thus, the verifier returned by this method should * be able to verify as trusted, all proxies to this service; including * proxies such as leases, event registrations, and administrative * proxies. * * @return a <code>TrustVerifier</code> which can be used to verify that * a given proxy to this service can be trusted. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws UnsupportedOperationException if the server proxy does not * implement both * {@link net.jini.core.constraint.RemoteMethodControl} * and {@link TrustEquivalence} * * @see net.jini.security.proxytrust.ServerProxyTrust#getProxyVerifier */ public TrustVerifier getProxyVerifier() throws NoSuchObjectException { readyState.check(); return new ProxyVerifier(innerProxy, proxyID); }//end getProxyVerifier /* END net.jini.security.proxytrust.ServerProxyTrust */ /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- * BEGIN net.jini.export.ProxyAccessor */ /** * Public method that facilitates the use of the mechanism provided by * {@link com.sun.jini.start.ServiceStarter} to create an activatable * instance of this server. * * @return the inner proxy (stub or dynamic proxy) for the server */ public Object getProxy() { return innerProxy; }//end getProxy /* END net.jini.export.ProxyAccessor */ /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- * BEGIN com.sun.jini.fiddler.Fiddler --> net.jini.admin.Administrable */ /** * Returns a proxy to the current instance of this class through which * a client may administer the lookup discovery service * * @return a proxy object through which the lookup discovery service * may be administered. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * server. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#getAdmin * @see net.jini.admin.Administrable#getAdmin */ public Object getAdmin() throws NoSuchObjectException, RemoteException { readyState.check(); return adminProxy; } /* END com.sun.jini.fiddler.Fiddler --> net.jini.admin.Administrable */ /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- * BEGIN com.sun.jini.fiddler.Fiddler * --> com.sun.jini.fiddler.FiddlerAdmin * --> net.jini.admin.JoinAdmin */ /** * Returns the current attribute sets for the lookup discovery service. * * @return array of net.jini.core.entry.Entry containing the current * attribute sets for the lookup discovery service * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#getLookupAttributes * @see net.jini.admin.JoinAdmin#getLookupAttributes */ public Entry[] getLookupAttributes() throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.readLock(); try { return thisServicesAttrs; } finally { concurrentObj.readUnlock(); } }//end getLookupAttributes /** * Adds attribute sets to the current set of attributes associated * with the lookup discovery service. The resulting set will be used * for all future registrations with lookup services. The new attribute * sets are also added to the lookup discovery service's attributes * on each lookup service with which the lookup discovery service * is currently registered. * * @param attrSets array of net.jini.core.entry.Entry containing the * attribute sets to add * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * attributes may or may not have been added successfully. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#addLookupAttributes * @see net.jini.admin.JoinAdmin#addLookupAttributes */ public void addLookupAttributes(Entry[] attrSets) throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.writeLock(); try { joinMgr.addAttributes(attrSets, true); thisServicesAttrs = joinMgr.getAttributes(); addLogRecord(new LookupAttrsAddedLogObj(this,attrSets)); } finally { concurrentObj.writeUnlock(); } }//end addLookupAttributes /** * Modifies the current set of attributes associated with the lookup * discovery service. The resulting set will be used for all future * registrations with lookup services. The same modifications are * also made to the lookup discovery service's attributes on each * lookup service with which the lookup discovery service is currently * registered. * * @param attrSetTemplates array of net.jini.core.entry.Entry containing * the templates to use for selecting the attributes (contained * within the set of existing attributes) that are to be * modified * @param attrSets array of net.jini.core.entry.Entry containing the * modifications to make to matching sets * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * attributes may or may not have been modified successfully. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#modifyLookupAttributes * @see net.jini.admin.JoinAdmin#modifyLookupAttributes */ public void modifyLookupAttributes(Entry[] attrSetTemplates, Entry[] attrSets) throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.writeLock(); try { joinMgr.modifyAttributes(attrSetTemplates, attrSets, true); thisServicesAttrs = joinMgr.getAttributes(); addLogRecord (new LookupAttrsModifiedLogObj(this,attrSetTemplates,attrSets)); } finally { concurrentObj.writeUnlock(); } }//end modifyLookupAttributes /** * Get the names of the groups whose members are lookup services the * lookup discovery services wishes to register with (join). * * @return String array containing the names of the groups whose members * are lookup services the lookup discovery service wishes to * join. * <p> * If the array returned is empty, the lookup discovery service * is configured to join no groups. If null is returned, the * lookup discovery service is configured to join all groups. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#getLookupGroups * @see net.jini.admin.JoinAdmin#getLookupGroups */ public String[] getLookupGroups() throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.readLock(); try { return thisServicesGroups; } finally { concurrentObj.readUnlock(); } }//end getLookupGroups /** * Add new names to the set consisting of the names of groups whose * members are lookup services the lookup discovery service wishes * to register with (join). Any lookup services belonging to the * new groups that the lookup discovery service has not yet registered * with, will be discovered and joined. * * @param String array containing the names of the groups to add * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * group names may or may not have been added successfully. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#addLookupGroups * @see net.jini.admin.JoinAdmin#addLookupGroups */ public void addLookupGroups(String[] groups) throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.writeLock(); try { try { ((DiscoveryGroupManagement)joinMgrLDM).addGroups(groups); } catch (IOException e) { throw new RuntimeException(e.toString()); } thisServicesGroups = ((DiscoveryGroupManagement)joinMgrLDM).getGroups(); addLogRecord(new LookupGroupsChangedLogObj(thisServicesGroups)); } finally { concurrentObj.writeUnlock(); } }//end addLookupGroups /** * Remove a set of group names from lookup discovery service's managed * set of groups (the set consisting of the names of groups whose * members are lookup services the lookup discovery service wishes * to join). Any leases granted to the lookup discovery service by * lookup services that are not members of the groups whose names * remain in the managed set will be cancelled at those lookup services. * * @param String array containing the names of the groups to remove * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * group names may or may not have been removed successfully. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#removeLookupGroups * @see net.jini.admin.JoinAdmin#removeLookupGroups */ public void removeLookupGroups(String[] groups) throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.writeLock(); try { ((DiscoveryGroupManagement)joinMgrLDM).removeGroups(groups); thisServicesGroups = ((DiscoveryGroupManagement)joinMgrLDM).getGroups(); addLogRecord(new LookupGroupsChangedLogObj(thisServicesGroups)); } finally { concurrentObj.writeUnlock(); } }//end removeLookupGroups /** * Replace the lookup discovery service's managed set of groups with a * new set of group names. Any leases granted to the lookup discovery * service by lookup services that are not members of the groups whose * names are in the new managed set will be cancelled at those lookup * services. Lookup services that are members of groups reflected in * the new managed set will be discovered and joined. * * @param String array containing the names of the new groups * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * group names may or may not have been replaced successfully. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#setLookupGroups * @see net.jini.admin.JoinAdmin#setLookupGroups */ public void setLookupGroups(String[] groups) throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.writeLock(); try { try { ((DiscoveryGroupManagement)joinMgrLDM).setGroups(groups); } catch (IOException e) { throw new RuntimeException(e.toString()); } thisServicesGroups = ((DiscoveryGroupManagement)joinMgrLDM).getGroups(); addLogRecord(new LookupGroupsChangedLogObj(thisServicesGroups)); } finally { concurrentObj.writeUnlock(); } }//end setLookupGroups /** * Get the lookup discovery service's managed set of locators. The * managed set of locators is the set of LookupLocator objects * corresponding to the specific lookup services with which the lookup * discovery service wishes to register (join). * * @return array of objects of type net.jini.core.discovery.LookupLocator, * each of which corresponds to a specific lookup service the * lookup discovery service wishes to join. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#getLookupLocators * @see net.jini.admin.JoinAdmin#getLookupLocators */ public LookupLocator[] getLookupLocators() throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.readLock(); try { return thisServicesLocators; } finally { concurrentObj.readUnlock(); } }//end getLookupLocators /** * Add a set of LookupLocator objects to the lookup discovery service's * managed set of locators. The managed set of locators is the set of * LookupLocator objects corresponding to the specific lookup services * with which the lookup discovery service wishes to register (join). * <p> * Any lookup services corresponding to the new locators that the lookup * discovery service has not yet joined, will be discovered and joined. * * @param array of net.jini.core.discovery.LookupLocator objects to add * to the managed set of locators * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * new locators may or may not have been added successfully. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#addLookupLocators * @see net.jini.admin.JoinAdmin#addLookupLocators */ public void addLookupLocators(LookupLocator[] locators) throws NoSuchObjectException, RemoteException { readyState.check(); /* Prepare outside of sync block because of possible remote call */ prepareNewLocators(locatorToJoinPreparer,locators); concurrentObj.writeLock(); try { ((DiscoveryLocatorManagement)joinMgrLDM).addLocators(locators); thisServicesLocators = ((DiscoveryLocatorManagement)joinMgrLDM).getLocators(); addLogRecord (new LookupLocatorsChangedLogObj(thisServicesLocators)); } finally { concurrentObj.writeUnlock(); } }//end addLookupLocators /** * Remove a set of LookupLocator objects from the lookup discovery * service's managed set of locators. The managed set of locators is the * set of LookupLocator objects corresponding to the specific lookup * services with which the lookup discovery service wishes to register * (join). * <p> * Note that any leases granted to the lookup discovery service by * lookup services that do not correspond to any of the locators * remaining in the managed set will be cancelled at those lookup * services. * * @param array of net.jini.core.discovery.LookupLocator objects to * remove from the managed set of locators * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * new locators may or may not have been removed successfully. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#removeLookupLocators * @see net.jini.admin.JoinAdmin#removeLookupLocators */ public void removeLookupLocators(LookupLocator[] locators) throws NoSuchObjectException, RemoteException { readyState.check(); /* Prepare outside of sync block because of possible remote call */ prepareNewLocators(locatorToJoinPreparer,locators); concurrentObj.writeLock(); try { ((DiscoveryLocatorManagement)joinMgrLDM).removeLocators(locators); thisServicesLocators = ((DiscoveryLocatorManagement)joinMgrLDM).getLocators(); addLogRecord (new LookupLocatorsChangedLogObj(thisServicesLocators)); } finally { concurrentObj.writeUnlock(); } }//end removeLookupLocators /** * Replace the lookup discovery service's managed set of locators with * a new set of locators. The managed set of locators is the set of * LookupLocator objects corresponding to the specific lookup services * with which the lookup discovery service wishes to register (join). * <p> * Note that any leases granted to the lookup discovery service by * lookup services whose corresponding locator is removed from the * managed set will be cancelled at those lookup services. The lookup * services corresponding to the new locators in the managed set * will be discovered and joined. * * @param array of net.jini.core.discovery.LookupLocator objects with * which to replace the current managed set of locators * remove from the managed set of locators * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * locators in the managed set may or may not have been replaced * successfully. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#setLookupLocators * @see net.jini.admin.JoinAdmin#setLookupLocators */ public void setLookupLocators(LookupLocator[] locators) throws NoSuchObjectException, RemoteException { readyState.check(); /* Prepare outside of sync block because of possible remote call */ prepareNewLocators(locatorToJoinPreparer,locators); concurrentObj.writeLock(); try { ((DiscoveryLocatorManagement)joinMgrLDM).setLocators(locators); thisServicesLocators = ((DiscoveryLocatorManagement)joinMgrLDM).getLocators(); addLogRecord (new LookupLocatorsChangedLogObj(thisServicesLocators)); } finally { concurrentObj.writeUnlock(); } }//end setLookupLocators /* END com.sun.jini.fiddler.Fiddler --> com.sun.jini.fiddler.FiddlerAdmin * --> net.jini.admin.JoinAdmin * -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- * BEGIN com.sun.jini.fiddler.Fiddler * --> com.sun.jini.fiddler.FiddlerAdmin * --> com.sun.jini.admin.DestroyAdmin */ /** * Destroy the lookup discovery service, if possible, including its * persistent storage. This method will typically spawn a separate * thread to do the actual work asynchronously, so a successful * return from this method usually does not mean that the service * has been destroyed. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * lookup discovery service may or may not have been successfully * destroyed. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#destroy * @see com.sun.jini.admin.DestroyAdmin#destroy */ public void destroy() throws NoSuchObjectException, RemoteException { readyState.check(); destroyDo(); }//end destroy /* END com.sun.jini.fiddler.Fiddler * --> com.sun.jini.fiddler.FiddlerAdmin * --> com.sun.jini.admin.DestroyAdmin * -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- * BEGIN com.sun.jini.fiddler.Fiddler * --> com.sun.jini.fiddler.FiddlerAdmin */ /** * Changes the least upper bound applied to all lease durations granted * by the lookup discovery service. * <p> * This method is a mechanism for an entity with the appropriate * privileges to administratively change the value of the least upper * bound that will be applied by the Fiddler implementation of the lookup * discovery service when determining the duration to assign to the lease * on a requested registration. * * @param newBound <code>long</code> value representing the new least * upper bound (in milliseconds) on the set of all possible * lease durations that may be granted * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * bound value may or may not have been changed successfully. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#setLeaseBound * @see com.sun.jini.fiddler.FiddlerAdmin#setLeaseBound */ public void setLeaseBound(long newBound) throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.writeLock(); try { if (newBound > leaseMax) { throw new IllegalArgumentException("max duration exceeded"); }//endif leaseBound = newBound; addLogRecord(new LeaseBoundSetLogObj(newBound)); } finally { concurrentObj.writeUnlock(); } }//end setLeaseBound /** * Retrieves the least upper bound applied to all lease durations granted * by the lookup discovery service. * * @return <code>long</code> value representing the current least * upper bound (in milliseconds) on the set of all possible * lease durations that may be granted * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#getLeaseBound * @see com.sun.jini.fiddler.FiddlerAdmin#getLeaseBound */ public long getLeaseBound() throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.readLock(); try { return leaseBound; } finally { concurrentObj.readUnlock(); } }//end getLeaseBound /** * Change the weight factor applied by the lookup discovery service * to the snapshot size during the test to determine whether or not * to take a "snapshot" of the system state. * * @param weight weight factor for snapshot size * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * weight factor may or may not have been changed successfully. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#setPersistenceSnapshotWeight * @see com.sun.jini.fiddler.FiddlerAdmin#setPersistenceSnapshotWeight */ public void setPersistenceSnapshotWeight(float weight) throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.writeLock(); try { snapshotWt = weight; addLogRecord(new SnapshotWeightSetLogObj(weight)); } finally { concurrentObj.writeUnlock(); } }//end setPersistenceSnapshotWeight /** * Retrieve the weight factor applied by the lookup discovery service * to the snapshot size during the test to determine whether or not to * take a "snapshot" of the system state. * * @return float value corresponding to the weight factor for snapshot * size * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. * * @see com.sun.jini.fiddler.FiddlerAdminProxy#getPersistenceSnapshotWeight * @see com.sun.jini.fiddler.FiddlerAdmin#getPersistenceSnapshotWeight */ public float getPersistenceSnapshotWeight() throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.readLock(); try { return snapshotWt; } finally { concurrentObj.readUnlock(); } }//end getPersistenceSnapshotWeight /** * Change the value of the size threshold of the snapshot; which is * employed by the lookup discovery service in the test to determine * whether or not to take a "snapshot" of the system state. * * @param threshold size threshold for taking a snapshot * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * threshold may or may not have been changed successfully. * * @see com.sun.jini.fiddler.FiddlerAdminProxy * #setPersistenceSnapshotThreshold * @see com.sun.jini.fiddler.FiddlerAdmin#setPersistenceSnapshotThreshold */ public void setPersistenceSnapshotThreshold(int threshold) throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.writeLock(); try { snapshotThresh = threshold; addLogRecord(new SnapshotThresholdSetLogObj(threshold)); } finally { concurrentObj.writeUnlock(); } }//end setPersistenceSnapshotThreshold /** * Retrieve the value of the size threshold of the snapshot; which is * employed by the lookup discovery service in the test to determine * whether or not to take a "snapshot" of the system state. * * @return int value corresponding to the size threshold of the snapshot * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. * * @see com.sun.jini.fiddler.FiddlerAdminProxy * #getPersistenceSnapshotThreshold * @see com.sun.jini.fiddler.FiddlerAdmin#getPersistenceSnapshotThreshold */ public int getPersistenceSnapshotThreshold() throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.readLock(); try { return snapshotThresh; } finally { concurrentObj.readUnlock(); } }//end getPersistenceSnapshotThreshold /* END com.sun.jini.fiddler.Fiddler --> com.sun.jini.fiddler.FiddlerAdmin * -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- * BEGIN com.sun.jini.fiddler.Fiddler * --> com.sun.jini.start.ServiceProxyAccessor */ /** * Public method that facilitates the use of the mechanism provided by * {@link com.sun.jini.start.ServiceStarter} to create an activatable * instance of this server. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @return the outer (smart) proxy for the server */ public Object getServiceProxy() throws NoSuchObjectException { readyState.check(); return outerProxy; }//end getServiceProxy /* END com.sun.jini.fiddler.Fiddler * --> com.sun.jini.start.ServiceProxyAccessor */ /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- * BEGIN com.sun.jini.fiddler.Fiddler */ /** * Returns the unique identifier generated (or recovered) by the backend * implementation of the lookup discovery service when an instance of * that service is constructed. This ID is typically used to determine * equality between the proxies of any two instances of the lookup * discovery service. * * @return the unique ID that was generated (or recovered) by the * backend implementation of the lookup discovery service * at creation time * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * server. When this exception does occur, the registration may * or may not have completed successfully. */ public Uuid getProxyID() throws NoSuchObjectException, RemoteException { readyState.check(); return proxyID; }//end getProxyID /** * Registers with the lookup discovery service. When a client invokes * this method, it requests that the lookup discovery service perform * discovery processing on its behalf. * * @param groups String array, none of whose elements may be null, * consisting of zero or more names of groups to * which lookup services to discover belong. * A null value or an empty array * (DiscoveryGroupManagement.ALL_GROUPS or * DiscoveryGroupManagement.NO_GROUPS) are both * acceptable. * @param locators array of zero or more non-null LookupLocator * objects, each corresponding to a specific lookup * service to discover. If either the empty array * or null is passed to this argument, then no * locator discovery will be performed for the * associated registration. * @param listener a non-null instance of RemoteEventListener. This * argument specifies the entity that will receive * events notifying the registration that a lookup * service of interest has been discovered. A * non-null value must be passed to this argument, * otherwise a NullPointerException will be thrown * and the registration. * @param handback null or an instance of MarshalledObject. This * argument specifies an object that will be * included in the notification event that the * lookup discovery service sends to the registered * listener. * @param leaseDuration long value representing the amount of time (in * milliseconds) for which the resources of the * lookup discovery service are being requested. * * @return an instance of FiddlerRegistration which implements the * LookupDiscoveryRegistration interface, and acts as a proxy * to the registration-related methods of the backend server * of the Fiddler implementation of the lookup discovery service * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * server. When this exception does occur, the registration may * or may not have completed successfully. * * @throws java.lang.NullPointerException this exception occurs when * null is input to the <code>listener</code> parameter, as well * as when one or more of the elements of the <code>groups</code> * parameter is null. * * @throws java.lang.IllegalArgumentException this exception occurs when * the value input to the <code>leaseDuration</code> parameter * is neither positive, Lease.FOREVER, nor Lease.ANY. * * @see net.jini.discovery.LookupDiscoveryService */ public LookupDiscoveryRegistration register(String[] groups, LookupLocator[] locators, RemoteEventListener listener, MarshalledObject handback, long leaseDuration) throws NoSuchObjectException, RemoteException { readyState.check(); /* The spec says that a null locators array implies no loc discovery */ if( locators == null) { locators = new LookupLocator[0]; }//endif if(containsNullElement(groups)) { throw new NullPointerException(" on call to register() method, at " +"least one null element in groups"); } else if (containsNullElement(locators)) { throw new NullPointerException(" on call to register() method, at " +"least one null element in locators"); } else if (listener == null) { throw new NullPointerException(" null listener input to " +"register() method"); }//endif /* Prepare the locators associated with the requested registration * outside of the sync block because of possible remote call. */ prepareNewLocators(locatorToDiscoverPreparer,locators); LookupDiscoveryRegistration reg = null; concurrentObj.writeLock(); try { /* Grant the registration request and add the registration to * to this service's state. * * The addition of a registration to this service's state * typically involves the modification of the managed sets in * the discovery manager, which usually involves starting the * discovery protocol. An IOException can occur when the * discovery protocol fails to start. When such an exception * does occur, register an ERROR status attribute (along with * a Comment attribute describing the nature of the problem) to * all lookup services with which this service is registered. * * Administrative clients, as well as clients that use this * service should have registered for notification of the * existence of this attribute. */ reg = registerDo(groups,locators,listener,handback,leaseDuration); } catch(RemoteException e) { /* Catch, log, and rethrow so RemoteException is not included * in the catch block for IOException below. */ problemLogger.log(Level.INFO, "cannot grant registration request", e); throw e; } catch(IOException e) { problemLogger.log(Level.INFO, "cannot grant registration " +"request - multicast problem", e); Entry[] errorAttrs = new Entry[] { new FiddlerStatus(StatusType.ERROR), new Comment("Failure during registration") }; joinMgr.addAttributes(errorAttrs,true); } finally { concurrentObj.writeUnlock(); } return reg; }//end register /** * This method is the "backend" server counterpart to the method of * the same name provided by the <code>LookupDiscoveryRegistration</code> * proxy (an instance of <code>FiddlerRegistration</code>) that is * returned by this service when a client requests a registration. * <p> * This method returns an array consisting of proxies to the lookup * service(s) that have already been discovered for the registration * corresponding to the <code>registrationID</code> input parameter. * Each element of the return set is a marshalled instance of the * <code>ServiceRegistrar</code> interface. * * @param registrationID unique identifier assigned to the registration * from which the set of registrars is being * retrieved * * @return an array of MarshalledObject objects where each element is * is a marshalled instance of ServiceRegistrar. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. * * @throws com.sun.jini.proxy.ThrowThis which is a non-remote "wrapper" * class used to wrap various remote exceptions (for example, * NoSuchObjectException) that this method wishes to throw. * When a service is implemented as a smart proxy with a * backend server, and a method on the backend which was invoked * through the proxy wishes to explicitly throw a particular * remote exception, it cannot simply throw that exception if * it wishes that exception to be visible to the proxy running * on the "client side". This is because when the backend throws * any remote exception, the RMI sub-system automatically wraps * that exception in a java.rmi.ServerException. Thus, the proxy * will only be able to "see" the ServerException (the actual * exception that the backend tried to throw is "buried" in the * detail field of the ServerException). Thus, in order to allow * the proxy access to the actual remote exception this method * throws, that exception wraps the desired remote exception in * the non-remote exception ThrowThis; which will not be wrapped * in a ServerException. * * This method throws a NoSuchObjectException wrapped in a * ThrowThis exception whenever the <code>registrationID</code> * parameter references an invalid or non-existent registration. * * @see com.sun.jini.fiddler.FiddlerRegistration#getRegistrars * @see net.jini.discovery.LookupDiscoveryRegistration#getRegistrars */ public MarshalledObject[] getRegistrars(Uuid registrationID) throws NoSuchObjectException, RemoteException, ThrowThis { readyState.check(); concurrentObj.readLock(); try { RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get(registrationID)); if(regInfo == null) { throw new ThrowThis (new NoSuchObjectException("Invalid registration " +"ID on call to " +"getRegistrars() method")); }//endif Collection mVals = (regInfo.discoveredRegsMap).values(); return ( (MarshalledObject[])(mVals).toArray (new MarshalledObject[mVals.size()]) ); } finally { concurrentObj.readUnlock(); } }//end getRegistrars /** * This method is the "backend" server counterpart to the method of * the same name provided by the <code>LookupDiscoveryRegistration</code> * proxy (an instance of <code>FiddlerRegistration</code>) that is * returned by this service when a client requests a registration. * <p> * This method returns an array consisting of the names of the groups * whose members are lookup services the lookup discovery service will * attempt to discover for the registration corresponding to the current * instance of this class. This set of group names is referred to as the * registration's 'managed set of groups'. * <p> * If the registration's managed set of groups is currently empty, then * the empty array is returned. If the lookup discovery service currently * has no managed set of groups for the registration through which the * request is being made, then null will be returned. * * @param registrationID unique identifier assigned to the registration * from which the set of groups is being retrieved * * @return a String array containing the elements of the managed set of * groups for the registration. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. * * @throws java.rmi.NoSuchObjectException wrapped in an instance of * com.sun.jini.proxy.ThrowThis exception whenever the * <code>registrationID</code> parameter references an invalid * or non-existent registration. Refer to the description of the * <code>getRegistrars</code> method for more information on * this exception. * * @see com.sun.jini.fiddler.FiddlerRegistration#getGroups * @see net.jini.discovery.LookupDiscoveryRegistration#getGroups */ public String[] getGroups(Uuid registrationID) throws NoSuchObjectException, RemoteException, ThrowThis { readyState.check(); concurrentObj.readLock(); try { RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get(registrationID)); if(regInfo == null) { throw new ThrowThis (new NoSuchObjectException("Invalid registration " +"ID on call to " +"getGroups() method")); }//endif String[] groups = null; if(regInfo.groups == null) { groups = DiscoveryGroupManagement.ALL_GROUPS; } else { groups = (String[])(regInfo.groups).toArray (new String[regInfo.groups.size()]); }//endif (regInfo.groups == null) return groups; } finally { concurrentObj.readUnlock(); } }//end getGroups /** * This method is the "backend" server counterpart to the method of * the same name provided by the <code>LookupDiscoveryRegistration</code> * proxy (an instance of <code>FiddlerRegistration</code>) that is * returned by this service when a client requests a registration. * <p> * This method returns an array consisting of the the LookupLocator * objects corresponding to specific lookup services the lookup discovery * service will attempt to discover for for the registration * corresponding to the current instance of this class. This set of * locators is referred to as the registration's 'managed set of locators'. * <p> * If the registration's managed set of locators is currently empty, then * the empty array is returned. If the lookup discovery service currently * has no managed set of locators for the registration through which the * request is being made, then null will be returned. * * @param registrationID unique identifier assigned to the registration * from which the set of locators is being retrieved * * @return array consisting of net.jini.core.discovery.LookupLocator * objects corresponding to the elements of the managed set of * locators for the registration. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. * * @throws java.rmi.NoSuchObjectException wrapped in an instance of * com.sun.jini.proxy.ThrowThis exception whenever the * <code>registrationID</code> parameter references an invalid * or non-existent registration. Refer to the description of the * <code>getRegistrars</code> method for more information on * this exception. * * @see com.sun.jini.fiddler.FiddlerRegistration#getLocators * @see net.jini.discovery.LookupDiscoveryRegistration#getLocators */ public LookupLocator[] getLocators(Uuid registrationID) throws NoSuchObjectException, RemoteException, ThrowThis { readyState.check(); concurrentObj.readLock(); try { RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get(registrationID)); if(regInfo == null) { throw new ThrowThis (new NoSuchObjectException("Invalid registration " +"ID on call to " +"getLocators() method")); }//endif return (LookupLocator[])(regInfo.locators).toArray (new LookupLocator[regInfo.locators.size()]); } finally { concurrentObj.readUnlock(); } }//end getLocators /** * This method is the "backend" server counterpart to the method of * the same name provided by the <code>LookupDiscoveryRegistration</code> * proxy (an instance of <code>FiddlerRegistration</code>) that is * returned by this service when a client requests a registration. * <p> * This method first tests the input set of group names for validity and * throws the appropriate exception should any irregularities be found. * It then adds the input set of group names to the managed set of groups * associated with the registration. * * @param registrationID unique identifier assigned to the registration * to which the set of groups being augmented * corresponds * @param groups a String array, none of whose elements may be * null, consisting of the group names with which to * augment the registration's managed set of groups. * <p> * If any element of this parameter duplicates any * other element of this parameter, the duplicate * will be ignored. If any element of this parameter * duplicates any element of the registration's * current managed set of groups, the duplicate will * be ignored. * <p> * If the empty set is input, then the registration's * managed set of groups will not change. If null is * input, this method will throw a * <code>NullPointerException</code>. * * @throws java.lang.IllegalStateException this exception occurs when * the <code>addGroups</code> method of the discovery * manager is invoked after the <code>terminate</code> method * of that manager is called. When this happens, in addition to * propagating this exception, this method also registers an * ERROR status attribute (along with a Comment attribute * describing the nature of the problem) with all lookup services * with which this service is registered. Administrative clients, * as well as clients that use this service should register * for notification of the existence of this attribute. * * @throws java.lang.UnsupportedOperationException this exception * occurs when the registration corresponding to the * <code>registrationID</code> parameter has no managed set of * groups to which to add the elements of the input parameter. * That is, the registration's current managed set of groups is * null. When a registration's managed set of groups is null, * it means that all groups are being discovered for that * registration; thus, requesting that a set of groups be added * to the set of all groups makes no sense. * * @throws java.lang.NullPointerException this exception occurs when * either null is input to the <code>groups</code> parameter, * or one or more of the elements of the <code>groups</code> * parameter is null. If a null <code>groups</code> parameter * is input, the registration is requesting that all groups be * added to its current managed set of groups; which is not * allowed. (Note that if a registration wishes to change its * managed set of groups from a finite set of names to "all * groups", it should invoke setGroups with a null input.) * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * registration's managed set of groups may or may not have been * successfully augmented. * * @throws java.rmi.NoSuchObjectException wrapped in an instance of * com.sun.jini.proxy.ThrowThis exception whenever the * <code>registrationID</code> parameter references an invalid * or non-existent registration. Refer to the description of the * <code>getRegistrars</code> method for more information on * this exception. * * @see com.sun.jini.fiddler.FiddlerRegistration#addGroups * @see net.jini.discovery.LookupDiscoveryRegistration#addGroups */ public void addGroups(Uuid registrationID, String[] groups) throws NoSuchObjectException, RemoteException, ThrowThis { readyState.check(); concurrentObj.writeLock(); try { RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get(registrationID)); if(regInfo == null) { throw new ThrowThis (new NoSuchObjectException("Invalid registration " +"ID on call to " +"addGroups() method")); }//endif /* Check the input for validity */ if(groups == null) { // asking that all groups be added throw new NullPointerException(" on call to addGroups() " +"method, cannot add " +"'ALL_GROUPS' (the null set) " +"to a registration's set of " +"groups to discover"); } else if(containsNullElement(groups)) { // null element throw new NullPointerException(" on call to addGroups() " +"method, at least one null " +"element in groups parameter"); } else if (regInfo.groups == null) { // all groups being discovered throw new UnsupportedOperationException (" on call to addGroups() " +"method, cannot add a set of" +"groups to a set already " +"configured for 'ALL_GROUPS' " +"(the null set)"); }//endif /* Augment the current set of groups with the input set */ addGroupsDo(regInfo, groups); addLogRecord(new GroupsAddedToRegistrationLogObj (regInfo.registrationID,groups)); } finally { concurrentObj.writeUnlock(); } }//end addGroups /** * This method is the "backend" server counterpart to the method of * the same name provided by the <code>LookupDiscoveryRegistration</code> * proxy (an instance of <code>FiddlerRegistration</code>) that is * returned by this service when a client requests a registration. * <p> * This method first tests the input set of group names for validity and * throws the appropriate exception should any irregularities be found. * It then queues a <code>SetGroupsTask</code> which performs the * actual replacement. * <p> * * @param registrationID unique identifier assigned to the registration * to which the set of groups being replaced * corresponds * @param groups a String array, none of whose elements may be * null, consisting of the group names with which to * replace the names in this registration's managed * set of groups. * <p> * If any element of this parameter duplicates any * other element of this parameter, the duplicate * will be ignored. * <p> * If the empty set is input, then group discovery * for the registration will cease. If null is input, * the lookup discovery service will attempt to * discover all as yet undiscovered lookup services * located within its multicast radius and, upon * discovery of any such lookup service, will send * to the registration's listener an event signaling * that discovery. * * @throws java.lang.NullPointerException this exception occurs when one * or more of the elements of the groups parameter is null. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * registration's managed set of groups may or may not have been * successfully replaced. * * @throws java.rmi.NoSuchObjectException wrapped in an instance of * com.sun.jini.proxy.ThrowThis exception whenever the * <code>registrationID</code> parameter references an invalid * or non-existent registration. Refer to the description of the * <code>getRegistrars</code> method for more information on * this exception. * * @see com.sun.jini.fiddler.FiddlerRegistration#setGroups * @see net.jini.discovery.LookupDiscoveryRegistration#setGroups */ public void setGroups(Uuid registrationID, String[] groups) throws NoSuchObjectException, RemoteException, ThrowThis { readyState.check(); concurrentObj.writeLock(); try { RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get(registrationID)); if(regInfo == null) { throw new ThrowThis (new NoSuchObjectException("Invalid registration " +"ID on call to " +"setGroups() method")); }//endif /* Check the input for validity */ if(containsNullElement(groups)) { // null element throw new NullPointerException(" on call to setGroups() " +"method, at least one null " +"element in groups parameter"); } else if ((groups == null) && (regInfo.groups == null)) { /* null input, but already set to ALL groups; do nothing */ return; }//endif /* Replace the current groups with the current groups */ setGroupsDo(regInfo, groups); addLogRecord(new GroupsSetInRegistrationLogObj (regInfo.registrationID,groups)); } finally { concurrentObj.writeUnlock(); } }//end setGroups /** * This method is the "backend" server counterpart to the method of * the same name provided by the <code>LookupDiscoveryRegistration</code> * proxy (an instance of <code>FiddlerRegistration</code>) that is * returned by this service when a client requests a registration. * <p> * This method first tests the input set of group names for validity and * throws the appropriate exception should any irregularities be found. * It then queues a <code>RemoveGroupsTask</code> which performs the * actual removal. * * @param registrationID unique identifier assigned to the registration * to which the set of groups being removed * corresponds * @param groups a String array, none of whose elements may be * null, consisting of the group names to delete * from the registration's managed set of groups. * <p> * If any element of this parameter duplicates any * other element of this parameter, the duplicate * will be ignored. If any element of this parameter * is not currently contained in the registration's * managed set, no action is taken with respect to * that element. * <p> * If the empty set is input, the registration's * managed set of groups will not change. If null is * input, this method will throw a * <code>NullPointerException</code>. * * @throws java.lang.UnsupportedOperationException this exception * occurs when the registration corresponding to the * <code>registrationID</code> parameter has no managed set * of groups from which to remove elements of the input parameter. * That is, the registration's current managed set of groups is * null. Thus, requesting that a set of groups be removed from * null set makes no sense. * * @throws java.lang.NullPointerException this exception occurs when * either null is input to the <code>groups</code> parameter, * or one or more of the elements of the <code>groups</code> * parameter is null. If a null <code>groups</code> parameter * is input, the registration is requesting that all groups be * removed from its current managed set of groups; which is not * allowed. (Note that if a registration wishes to change its * managed set of groups from "all groups" to "no groups", it * it should invoke setGroups with a zero length * <code>String</code> input.) * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * registration's managed set of groups may or may not have been * successfully modified. * * @throws java.rmi.NoSuchObjectException wrapped in an instance of * com.sun.jini.proxy.ThrowThis exception whenever the * <code>registrationID</code> parameter references an invalid * or non-existent registration. Refer to the description of the * <code>getRegistrars</code> method for more information on * this exception. * * @see com.sun.jini.fiddler.FiddlerRegistration#removeGroups * @see net.jini.discovery.LookupDiscoveryRegistration#removeGroups */ public void removeGroups(Uuid registrationID, String[] groups) throws NoSuchObjectException, RemoteException, ThrowThis { readyState.check(); concurrentObj.writeLock(); try { RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get(registrationID)); if(regInfo == null) { throw new ThrowThis (new NoSuchObjectException("Invalid registration " +"ID on call to " +"removeGroups() method")); }//endif /* Check the input for validity */ if(groups == null) { // asking that all groups be removed throw new NullPointerException(" on call to removeGroups() " +"method, cannot remove " +"'ALL_GROUPS' (the null set) " +"from a registration's set of " +"groups to discover"); } else if(containsNullElement(groups)) { // null element throw new NullPointerException(" on call to removeGroups() " +"method, at least one null " +"element in groups parameter"); } else if (regInfo.groups == null) { // all groups being discovered throw new UnsupportedOperationException (" on call to removeGroups() " +"method, cannot remove a set of" +"groups from a set already " +"configured for 'ALL_GROUPS' " +"(the null set)"); }//endif /* Remove the requested groups */ removeGroupsDo(regInfo, groups); logInfoGroups("\nAfter Group Removal --"); addLogRecord(new GroupsRemovedFromRegistrationLogObj (regInfo.registrationID,groups)); } finally { concurrentObj.writeUnlock(); } }//end removeGroups /** * This method is the "backend" server counterpart to the method of * the same name provided by the <code>LookupDiscoveryRegistration</code> * proxy (an instance of <code>FiddlerRegistration</code>) that is * returned by this service when a client requests a registration. * <p> * This method first tests the input set of group names for validity and * throws the appropriate exception should any irregularities be found. * It then adds the input set of LookupLocator objects to the managed set * of locators associated with the registration. * * @param registrationID unique identifier assigned to the registration * to which the set of locators being augmented * corresponds * @param locators an array, none of whose elements may be null, * consisting of the LookupLocator objects with * which to augment the registration's managed set * of locators. * <p> * If any element of this parameter duplicates any * other element of this parameter, the duplicate * will be ignored. If any element of this parameter * duplicates any element of the registration's * managed set of locators, the duplicate will be * ignored. * <p> * If the empty set is input, then the registration's * managed set of locators will not change. If null * is input, this method will throw a * <code>NullPointerException</code>. * * @throws java.lang.IllegalStateException this exception occurs when * the <code>addLocators</code> method of the discovery * manager is invoked after the <code>terminate</code> method * of that manager is called. When this happens, in addition to * propagating this exception, this method also registers an * ERROR status attribute (along with a Comment attribute * describing the nature of the problem) with all lookup services * with which this service is registered. Administrative clients, * as well as clients that use this service should register * for notification of the existence of this attribute. * * @throws java.lang.NullPointerException this exception occurs when * either null is input to the <code>locators</code> parameter, * or one or more of the elements of the <code>locators</code> * parameter is null. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * registration's managed set of locators may or may not have * been successfully augmented. * * @throws java.rmi.NoSuchObjectException wrapped in an instance of * com.sun.jini.proxy.ThrowThis exception whenever the * <code>registrationID</code> parameter references an invalid * or non-existent registration. Refer to the description of the * <code>getRegistrars</code> method for more information on * this exception. * * @see com.sun.jini.fiddler.FiddlerRegistration#addLocators * @see net.jini.discovery.LookupDiscoveryRegistration#addLocators */ public void addLocators(Uuid registrationID, LookupLocator[] locators) throws NoSuchObjectException, RemoteException, ThrowThis { readyState.check(); /* Prepare outside of sync block because of possible remote call */ prepareNewLocators(locatorToDiscoverPreparer,locators); concurrentObj.writeLock(); try { RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get(registrationID)); if(regInfo == null) { throw new ThrowThis (new NoSuchObjectException("Invalid registration " +"ID on call to " +"addLocators() method")); }//endif /* Check the input for validity */ if(locators == null) { throw new NullPointerException(" on call to addLocators() " +"method, cannot add null " +"to a registration's set of " +"locators to discover"); } else if(containsNullElement(locators)) { // null element throw new NullPointerException(" on call to addLocators() " +"method, at least one null " +"element in locators parameter"); }//endif(locators == null) /* Augment the current set of locators with the input set */ addLocatorsDo(regInfo, locators); addLogRecord(new LocsAddedToRegistrationLogObj (regInfo.registrationID,locators)); } finally { concurrentObj.writeUnlock(); } }//end addLocators /** * This method is the "backend" server counterpart to the method of * the same name provided by the <code>LookupDiscoveryRegistration</code> * proxy (an instance of <code>FiddlerRegistration</code>) that is * returned by this service when a client requests a registration. * <p> * This method first tests the input set of locators for validity and * throws the appropriate exception should any irregularities be found. * It then queues a <code>SetLocatorsTask</code> which performs the * actual replacement. * * @param registrationID unique identifier assigned to the registration * to which the set of locators being replaced * corresponds * @param locators an array, none of whose elements may be null, * consisting of the LookupLocator objects with * which to replace the locators in the * registration's managed set of locators. * <p> * If any element of this parameter duplicates any * other element of this parameter, the duplicate * will be ignored. * <p> * If the empty array is input, then locator * discovery for the registration will cease. If * null is input, this method will throw a * <code>NullPointerException</code>. * * @throws java.lang.NullPointerException this exception occurs when * either null is input to the <code>locators</code> parameter, * or one or more of the elements of the <code>locators</code> * parameter is null. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * registration's managed set of locators may or may not have * been successfully replaced. * * @throws java.rmi.NoSuchObjectException wrapped in an instance of * com.sun.jini.proxy.ThrowThis exception whenever the * <code>registrationID</code> parameter references an invalid * or non-existent registration. Refer to the description of the * <code>getRegistrars</code> method for more information on * this exception. * * @see com.sun.jini.fiddler.FiddlerRegistration#setLocators * @see net.jini.discovery.LookupDiscoveryRegistration#setLocators */ public void setLocators(Uuid registrationID, LookupLocator[] locators) throws NoSuchObjectException, RemoteException, ThrowThis { readyState.check(); /* Prepare outside of sync block because of possible remote call */ prepareNewLocators(locatorToDiscoverPreparer,locators); concurrentObj.writeLock(); try { RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get(registrationID)); if(regInfo == null) { throw new ThrowThis (new NoSuchObjectException("Invalid registration " +"ID on call to " +"setLocators() method")); }//endif /* Check the input for validity */ if(locators == null) { throw new NullPointerException(" on call to setLocators() " +"method, cannot replace a " +"registration's current set " +"of locators with null"); } else if(containsNullElement(locators)) { // null element throw new NullPointerException(" on call to setLocators() " +"method, at least one null " +"element in locators parameter"); }//endif(locators == null) setLocatorsDo(regInfo, locators); addLogRecord(new LocsSetInRegistrationLogObj (regInfo.registrationID,locators)); } finally { concurrentObj.writeUnlock(); } }//end setLocators /** * This method is the "backend" server counterpart to the method of * the same name provided by the <code>LookupDiscoveryRegistration</code> * proxy (an instance of <code>FiddlerRegistration</code>) that is * returned by this service when a client requests a registration. * <p> * This method first tests the input set of locators for validity and * throws the appropriate exception should any irregularities be found. * It then queues a <code>RemoveLocatorsTask</code> which performs the * actual removal. * * @param registrationID unique identifier assigned to the registration * to which the set of locators being removed * corresponds * @param locators an array, none of whose elements may be null, * consisting of the LookupLocator objects to remove * from the registration's managed set of locators. * <p> * If any element of this parameter duplicates any * other element of this parameter, the duplicate * will be ignored. * <p> * If the empty set is input, the managed set of * locators will not change. If null is input, * this method will throw a * <code>NullPointerException</code>. * * @throws java.lang.NullPointerException this exception occurs when * either null is input to the <code>locators</code> parameter, * or one or more of the elements of the <code>locators</code> * parameter is null. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, the * registration's managed set of locators may or may not have * been successfully modified. * * @throws java.rmi.NoSuchObjectException wrapped in an instance of * com.sun.jini.proxy.ThrowThis exception whenever the * <code>registrationID</code> parameter references an invalid * or non-existent registration. Refer to the description of the * <code>getRegistrars</code> method for more information on * this exception. * * @see com.sun.jini.fiddler.FiddlerRegistration#removeLocators * @see net.jini.discovery.LookupDiscoveryRegistration#removeLocators */ public void removeLocators(Uuid registrationID, LookupLocator[] locators) throws NoSuchObjectException, RemoteException, ThrowThis { readyState.check(); /* Prepare outside of sync block because of possible remote call */ prepareNewLocators(locatorToDiscoverPreparer,locators); concurrentObj.writeLock(); try { RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get(registrationID)); if(regInfo == null) { throw new ThrowThis (new NoSuchObjectException("Invalid registration " +"ID on call to " +"removeLocators() method")); }//endif /* Check the input for validity */ if(locators == null) { throw new NullPointerException(" on call to removeLocators() " +"method, cannot remove null " +"from a registration's set of " +"locators to discover"); } else if(containsNullElement(locators)) { // null element throw new NullPointerException(" on call to removeLocators() " +"method, at least one null " +"element in locators parameter"); }//endif(locators == null) /* Remove the requested set of locators from the current set */ removeLocatorsDo(regInfo, locators); addLogRecord(new LocsRemovedFromRegistrationLogObj (regInfo.registrationID,locators)); } finally { concurrentObj.writeUnlock(); } }//end removeLocators /** * This method is the "backend" server counterpart to the method of * the same name provided by the <code>LookupDiscoveryRegistration</code> * proxy (an instance of <code>FiddlerRegistration</code>) that is * returned by this service when a client requests a registration. * <p> * This method informs the lookup discovery service of the existence of * an unavailable lookup service and requests that the lookup discovery * service discard the unavailable lookup service and make it eligible * to be re-discovered. * * @param registrationID unique identifier assigned to the registration * making the current discard request * @param registrar a reference to the lookup service that the lookup * discovery service is being asked to discard. * <p> * If this parameter equals none of the lookup * services contained in the managed set of lookup * services for this registration, no action will * be taken. * * @throws java.lang.NullPointerException this exception occurs when * null is input to the registrar parameter. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * lookup discovery service. When this exception does occur, * the lookup service may or may not have been successfully * discarded. * * @throws java.rmi.NoSuchObjectException wrapped in an instance of * com.sun.jini.proxy.ThrowThis exception whenever the * <code>registrationID</code> parameter references an invalid * or non-existent registration. Refer to the description of the * <code>getRegistrars</code> method for more information on * this exception. * * @see com.sun.jini.fiddler.FiddlerRegistration#discard * @see net.jini.discovery.LookupDiscoveryRegistration#discard */ public void discard(Uuid registrationID, ServiceRegistrar registrar) throws NoSuchObjectException, RemoteException, ThrowThis { readyState.check(); concurrentObj.writeLock(); try { logInfoDiscard("\ndiscard: ",registrationID); RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get(registrationID)); if(regInfo == null) { throw new ThrowThis (new NoSuchObjectException("Invalid registration " +"ID on call to " +"discard() method")); }//endif if(registrar == null) { throw new NullPointerException(" on call to discard() " +"method, null input for " +"registrar to discard"); }//endif if( regIsElementOfRegSet(registrar,discoveryMgr.getRegistrars()) ){ /* This must be the first discard request for this registrar * because the discovery manager has not discarded it yet. * When the discovery manager discards the registrar, a * local discarded event is sent to the listener.discarded * method which queues a DiscardedEventTask which will * remove the discarded registrar from the registration's * set of discovered registrars and then send a remote * discarded event to the registration's listener. */ logInfoDiscard(" Registrar IS an element of Mgr's " +"discovered registrars ... discarding " +"from discovery manager"); regInfo.discardFlag = true; //discard due to external request discoveryMgr.discard(registrar); } else { logInfoDiscard(" Registrar NOT an element of Mgr's " +"discovered registrars ... queuing " +"new DiscardRegistrarTask"); /* For all subsequent discard requests, remove the registrar * from the registration's set of discovered registrars and * send a remote discarded event, but don't ask the discovery * manager to discard the registrar. */ taskMgr.add(new DiscardRegistrarTask(regInfo,registrar)); }//endif } finally { concurrentObj.writeUnlock(); } }//end discard /** * This method is the "backend" server counterpart to the * <code>renew</code> method specified by the <code>Lease</code> interface, * implemented in the <code>com.sun.jini.lease.AbstractLease</code> class, * and invoked by way of the <code>doRenew</code> method of the * <code>FiddlerLease</code> class; an instance of which is * returned by the <code>getLease</code> method of the * <code>LookupDiscoveryRegistration</code> proxy (an instance of * <code>FiddlerRegistration</code>) that is returned by this service * when a client requests a registration. * <p> * This method renews the lease corresponding to the given * <code>registrationID</code> and <code>leaseID</code> parameters, * granting a new duration that is less than or equal to the requested * duration value contained in the <code>duration</code> parameter. * * @param registrationID unique identifier assigned to the registration * to which the lease being renewed corresponds * @param leaseID identifier assigned by the lease grantor to the * lease being renewed * @param duration the requested duration for the lease being renewed * * @return <code>long</code> value representing the actual duration that * was granted for the renewed lease. Note that the actual * duration granted and returned by this method may be less than * the duration requested. * * @throws net.jini.core.lease.UnknownLeaseException this exception occurs * when the lease being renewed does not exist, or is unknown * to the lease grantor; typically because the lease has expired. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * server. When this exception does occur, the lease may or may * not have been renewed successfully. * * @see net.jini.core.lease.Lease#renew * @see com.sun.jini.lease.AbstractLease#renew * @see com.sun.jini.lease.AbstractLease#doRenew * @see com.sun.jini.fiddler.FiddlerLease#doRenew */ public long renewLease(Uuid registrationID, Uuid leaseID, long duration) throws UnknownLeaseException, NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.priorityWriteLock(); try { RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get(registrationID)); if(regInfo == null) { throw new UnknownLeaseException ("\n Invalid registration ID on call to " +"cancelLease() method" +"\n The lease may have expired or been " +"cancelled"); }//endif /* Renew the lease */ long newDuration = 0; newDuration = renewLeaseDo(regInfo, leaseID, duration); logInfoLease("Renewed lease: ",registrationID,leaseID); /* The call to addLogRecord is in renewLeaseDo */ return newDuration; } finally { concurrentObj.writeUnlock(); } }//end renewLease /** * This methods renews all leases from a <code>LeaseMap</code>, * where each element of the map is a lease on a registration with * ID corresponding to an element of the <code>registrationIDs</code> * parameter. * <p> * This method is the "backend" server counterpart to the * <code>renewAll</code> method specified by the * <code>LeaseMap</code> interface, implemented in the * <code>com.sun.jini.lease.AbstractLeaseMap</code> class, and * invoked by way of the <code>renewAll</code> method of the * <code>FiddlerLease</code> class; an instance of which is * returned by the <code>getLease</code> method of the * <code>LookupDiscoveryRegistration</code> proxy (an instance of * <code>FiddlerRegistration</code>) that is returned by this service * when a client requests a registration. * * @param registrationIDs array containing the unique identifiers assigned * to the each registration to which each lease * to be renewed corresponds * @param leaseIDs array containing the identifiers assigned by the * lease grantor to each lease being renewed * @param durations array containing the requested durations for * each lease being renewed * * @return an instance of FiddlerRenewResults containing data corresponding * to the results (granted durations or exceptions) of each * renewal attempt * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * server. When this exception does occur, this method may or * may not have complete its processing successfully. * * @see net.jini.core.lease.LeaseMap#renewAll */ public FiddlerRenewResults renewLeases(Uuid[] registrationIDs, Uuid[] leaseIDs, long[] durations) throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.priorityWriteLock(); try { return renewLeasesDo(registrationIDs, leaseIDs, durations); /* The call to addLogRecord is in renewLeasesDo */ } finally { concurrentObj.writeUnlock(); } }//end renewLeases /** * This method is the "backend" server counterpart to the * <code>cancel</code> method specified by the <code>Lease</code> * interface and implemented in the <code>FiddlerLease</code> class; an * instance of which is returned by the <code>getLease</code> method * of the <code>LookupDiscoveryRegistration</code> proxy (an instance of * <code>FiddlerRegistration</code>) that is returned by this service * when a client requests a registration. * <p> * This method cancels the lease corresponding to the given * <code>registrationID</code> and <code>leaseID</code> parameters. * * The cancellation of a lease typically involves the modification of the * managed sets in the discovery manager, which usually involves starting * the discovery protocol. An IOException can occur when the discovery * protocol fails to start. When such an exception does occur, this * method registers an ERROR status attribute (along with a Comment * attribute describing the nature of the problem) to all lookup services * with which this service is registered. * * Administrative clients, as well as clients that use this service should * have registered for notification of the existence of this attribute. * * @param registrationID unique identifier assigned to the registration * to which the lease being cancelled corresponds * @param leaseID identifier assigned by the lease grantor to the * lease that is to be cancelled * * @throws net.jini.core.lease.UnknownLeaseException this exception occurs * when the lease being cancelled is unknown to the lease grantor. * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * server. When this exception does occur, the lease may or may * not have been cancelled successfully. * * @see net.jini.core.lease.Lease#cancel */ public void cancelLease(Uuid registrationID, Uuid leaseID) throws UnknownLeaseException, NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.writeLock(); try { RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get(registrationID)); if(regInfo == null) { throw new UnknownLeaseException ("\n Invalid registration ID on call to " +"cancelLease() method" +"\n The lease may have expired or been " +"cancelled"); }//endif /* Cancel the lease */ try { cancelLeaseDo(regInfo, leaseID); logInfoLease("Cancelled lease: ",registrationID,leaseID); } catch(IOException e) { String eStr = "Failure while cancelling the lease on " +"registration with ID = "+registrationID; if( problemLogger.isLoggable(Level.INFO) ) { problemLogger.log(Level.INFO, eStr, e); }//endif Entry[] errorAttrs = new Entry[] { new FiddlerStatus(StatusType.ERROR), new Comment(eStr) }; joinMgr.addAttributes(errorAttrs,true); } addLogRecord(new LeaseCancelledLogObj (regInfo.registrationID, leaseID)); } finally { concurrentObj.writeUnlock(); } }//end cancelLease /** * Cancels all leases from a <code>LeaseMap</code>. * <p> * For each element in the <code>registrationIDs</code> parameter, * this method will cancel the corresponding element in the * <code>leaseIDs</code> parameter. * * @param registrationIDs array containing the unique identifiers assigned * to the each registration to which each lease * to be cancelled corresponds * @param leaseIDs array containing the identifiers assigned by the * lease grantor to each lease being cancelled * * @throws java.rmi.NoSuchObjectException if this method is called during * service initialization or shutdown processing * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * server. When this exception does occur, this method may or * may not have complete its processing successfully. * * @return array consisting of any exceptions that may have occurred * while attempting to cancel one of the leases in the map. * * @see net.jini.core.lease.LeaseMap#cancelAll */ public Exception[] cancelLeases(Uuid[] registrationIDs, Uuid[] leaseIDs) throws NoSuchObjectException, RemoteException { readyState.check(); concurrentObj.writeLock(); try { /* don't bother to weed out unknown leases, so log first */ addLogRecord( new LeasesCancelledLogObj(registrationIDs,leaseIDs)); return cancelLeasesDo(registrationIDs, leaseIDs); } finally { concurrentObj.writeUnlock(); } }//end cancelLeases /* END com.sun.jini.fiddler.Fiddler ----------------------------------- */ /* ************************* END Public Methods *********************** */ /* **************** BEGIN Private Static Utility Methods ************** */ /** Return a new array containing the elements of the input array parameter * with the input element parameter appended to the end of the array. */ private static Object[] appendArray(Object[] array, Object elt) { int len = array.length; Object[] newArray = (Object[])Array.newInstance(array.getClass().getComponentType(), len + 1); System.arraycopy(array, 0, newArray, 0, len); newArray[len] = elt; return newArray; }//end appendArray /** Bounds the duration by the value of the <code>bound</code> parameter, * and checks for negative value. */ private static long applyBoundToLeaseDuration(long leaseDuration, long bound) { long newLeaseDuration = leaseDuration; if ( (leaseDuration == Lease.ANY) || (leaseDuration > bound) ) { newLeaseDuration = bound; } else if (leaseDuration < 0) { throw new IllegalArgumentException("negative lease duration"); }//endif return newLeaseDuration; }//end applyBoundToLeaseDuration /** Determines if any element in the input array is null. * @param arr Object array to examine for null elements * @return true if any element is found to be null, false otherwise. * Note that this means that if the array itself is null * or has a non-positive length, false is returned (because * the input parameter still does not contain a null element). */ private static boolean containsNullElement(Object[] arr) { if( (arr == null) || (arr.length == 0) ) return false; for(int i=0;i<arr.length;i++) { if(arr[i] == null) return true; }//end loop return false; }//end containsNullElement /** This method determines if a particular registration (regInfo) is * interested in discovering, through group discovery, the registrar * belonging to a given set of member groups. * * @param regGroups array of the member groups from the registrar * (cannot be null) * @param desiredGroups groups the registration wishes to discover * (can be null = ALL_GROUPS) * * @return <code>true</code> if at least one of the registrar's member * groups is contained in the registration's set of groups to * discover; <code>false</code> otherwise */ private static boolean interested(String[] regGroups, Set desiredGroups) { if(desiredGroups == null) return true; if(desiredGroups.size() == 0) return false; for(int i=0;i<regGroups.length;i++) { if( desiredGroups.contains(regGroups[i]) ) return true; }//end loop return false; }//end interested /** This method determines if a particular registration (regInfo) is * interested in discovering, through either locator discovery * or group discovery, the registrar having a given locator and * belonging to a given set of member groups. * * @param regLoc locator of the registrar (cannot be null) * @param regGroups array of the member groups from the registrar * (cannot be null) * @param desiredLocators locators the registration wishes to discover * @param desiredGroups groups the registration wishes to discover * (can be null = ALL_GROUPS) * * @return <code>true</code> if either the registrar's locator is * contained in the registration's set of locators to discover, * or at least one of the registrar's member groups is contained * in the registration's set of groups to discover; * <code>false</code> otherwise */ private static boolean interested(LookupLocator regLoc, String[] regGroups, Set desiredLocators, Set desiredGroups) { if(locSetContainsLoc(desiredLocators,regLoc)) return true; return interested(regGroups,desiredGroups); }//end interested /** This method returns a mapping in which the key values are registrars, * and the map values are the member groups of the corresponding * registrar key. The registrar and member groups from the input map * are selected to be included in the returned mapping if and only if * the key value under consideration is a registrar that belongs to none * of the desired groups of the given registration (<code>regInfo</code>). * That is, the registrars referenced in the returned mapping are the * registrars that are no longer of interest - through group discovery * - to the given registration. * * @param regsMap map whose key values are registrars, and whose map * values are data structures of type * <code>LocatorGroupsStruct</code> that contain the * associated locator and member groups of the * corresponding registrar key; the elements of the * return map are selected from this mapping * @param regInfo the data structure record corresponding to the * registration whose groups-to-discover will be used * to select the elements from <code>regMap</code> to * include in the return mapping * * @return a registrar-to-groups map in which each registrar in the map * is from the <code>regMap</code> parameter, and belongs to none * of the desired groups referenced in the <code>regInfo</code> * parameter */ private static HashMap getUndesiredRegsByGroup(Map regMap, RegistrationInfo regInfo) { HashSet desiredGroups = regInfo.groups; HashMap undesiredRegMap = new HashMap(regMap.size()); Set eSet = regMap.entrySet(); for(Iterator itr = eSet.iterator(); itr.hasNext(); ) { Map.Entry pair = (Map.Entry)itr.next(); String[] regGroups = ((LocatorGroupsStruct)pair.getValue()).groups; if( !interested(regGroups,desiredGroups) ) { undesiredRegMap.put((ServiceRegistrar)pair.getKey(),regGroups); }//endif }//end loop return undesiredRegMap; }//end getUndesiredRegsByGroup /** This method returns a subset of the given registrar-to-locators * mapping (<code>regMap</code>). An element of the given mapping is * selected to be included in the returned mapping if and only if * the key value of the element is a registrar whose locator equals * none of the desired locators of the given registration * (<code>regInfo</code>). That is, the registrars referenced in * the returned mapping are the registrars that are no longer of * interest - through locator discovery - to the given registration. * * This method returns a mapping in which the key values are registrars, * and the map values are the locators of the corresponding registrar * key. The registrar and locators from the input map are selected to * be included in the returned mapping if and only if the key value * under consideration is a registrar whose locator equals none of the * desired locators of the given registration (<code>regInfo</code>). * That is, the registrars referenced in the returned mapping are the * registrars that are no longer of interest - through locator discovery * - to the given registration. * * @param regsMap map whose key values are registrars, and whose map * values are data structures of type * <code>LocatorGroupsStruct</code> that contain the * associated locator and member groups of the * corresponding registrar key; the elements of the * return map are selected from this mapping * @param regInfo the data structure record corresponding to the * registration whose locators-to-discover will be used * to select the elements from <code>regMap</code> to * include in the return mapping * * @return a registrars-to-locators map in which each registrar key in * the map is from the <code>regMap</code> parameter, and has * a locator equal to none of the desired locators referenced * in the <code>regInfo</code> parameter */ private static Map getUndesiredRegsByLocator(Map regMap, RegistrationInfo regInfo) { HashSet desiredLocators = regInfo.locators; HashMap undesiredRegMap = new HashMap(regMap.size()); Set eSet = regMap.entrySet(); for(Iterator itr = eSet.iterator(); itr.hasNext(); ) { Map.Entry pair = (Map.Entry)itr.next(); LookupLocator regLocator = ((LocatorGroupsStruct)pair.getValue()).locator; if(!locSetContainsLoc(desiredLocators,regLocator)) { undesiredRegMap.put((ServiceRegistrar)pair.getKey(), regLocator); }//endif }//end loop return undesiredRegMap; }//end getUndesiredRegsByLocator /** * Marshals each element of the <code>Entry[]</code> array parameter. * This method is <code>static</code> so that it may called from * the <code>static</code> <code>LogRecord</code> classes when a set * of attributes is being logged to persistent storage. * * @param fiddlerImpl reference to the current instance of this service * @param attrs <code>Entry[]</code> array consisting of the * attributes to marshal * @return array of <code>MarshalledObject[]</code>, where each element * corresponds to an attribute in marshalled form */ private static MarshalledObject[] marshalAttributes (FiddlerImpl fiddlerImpl, Entry[] attrs) { if(attrs == null) return new MarshalledObject[0]; ArrayList marshalledAttrs = new ArrayList(); for(int i=0;i<attrs.length;i++) { /* Do not let an attribute problem prevent the service from * continuing to operate */ try { marshalledAttrs.add(new MarshalledObject(attrs[i])); } catch(Throwable e) { if( problemLogger.isLoggable(Level.INFO) ) { problemLogger.log(Level.INFO, "Error while marshalling attribute["+i +"] ("+attrs[i]+")", e); }//endif } }//end loop return ((MarshalledObject[])(marshalledAttrs.toArray (new MarshalledObject[marshalledAttrs.size()]))); }//end marshalAttributes /** * Unmarshals each element of the <code>MarshalledObject[]</code> array * parameter. This method is <code>static</code> so that it may called * from the <code>static</code> <code>LogRecord</code> classes when a * set of attributes is being recovered from persistent storage. * * @param fiddlerImpl reference to the current instance of this service * @param marshalledAttrs <code>MarshalledObject[]</code> array consisting * of the attributes to unmarshal * @return array of <code>Entry[]</code>, where each element corresponds * to an attribute that was successfully unmarshalled */ private static Entry[] unmarshalAttributes (FiddlerImpl fiddlerImpl, MarshalledObject[] marshalledAttrs) { if(marshalledAttrs == null) return new Entry[0]; ArrayList attrs = new ArrayList(); for(int i=0;i<marshalledAttrs.length;i++) { /* Do not let an attribute problem prevent the service from * continuing to operate */ try { attrs.add( (Entry)( marshalledAttrs[i].get() ) ); } catch(Throwable e) { if( problemLogger.isLoggable(Level.INFO) ) { problemLogger.log(Level.INFO, "Error while unmarshalling attribute["+i +"]", e); }//endif } }//end loop return ((Entry[])(attrs.toArray(new Entry[attrs.size()]))); }//end unmarshalAttributes /** Using the given <code>ProxyPreparer</code>, attempts to prepare each * element of the given <code>LookupLocator</code> array; replacing the * original element of the array with the result of the call to the * method <code>ProxyPreparer.prepareProxy</code>. If any attempt to * prepare an element of the given array fails due to an exception, * this method will propagate that exception. * * This method is a convenience method that is typically used to * prepare new locators the service should discover and join that * are inserted into, or removed from, the service's state through * the use of one of the following methods: * <ul><li> <code>addLookupLocators</code> * <li> <code>setLookupLocators</code> * <li> <code>removeLookupLocators</code> * <li> <code>addLocators</code> * <li> <code>setLocators</code> * <li> <code>removeLocators</code> * </ul> * * @param preparer the preparer to use to prepare each element of the * input array * @param locators array of <code>LookupLocator</code> instances in which * each element will be prepared. * * @throws RemoteException when preparation of any of the elements * of the input array fails because of a * <code>RemoteException</code> * @throws SecurityException when preparation of any of the elements * of the input array fails because of a * <code>SecurityException</code> */ private static void prepareNewLocators(ProxyPreparer preparer, LookupLocator[] locators) throws RemoteException { for (int i=0; i<locators.length; i++) { locators[i] = (LookupLocator)preparer.prepareProxy(locators[i]); }//end loop }//end prepareNewLocators /** Using the given <code>ProxyPreparer</code>, attempts to prepare each * element of the given <code>LookupLocator</code> array; and returns * a new array containing the prepared locators. If any attempt to * prepare an element of the given array fails due to an exception, * this method will skip to the next locator in that input array. * * This method is a convenience method that is typically used to * re-prepare the previously prepared locators that are retrieved * from the service's persisted state during recovery. * * @param preparer the preparer to use to prepare each element of the * input array * @param locators array of <code>LookupLocator</code> instances in which * each element will be prepared. * * @return array of <code>LookupLocator</code> instances in which each * element of the returned array is the result of successful proxy * preparation of the corresponding element of the input array */ private static LookupLocator[] prepareOldLocators(ProxyPreparer preparer, LookupLocator[] locators) { ArrayList locsList = new ArrayList(locators.length); for(int i=0; i<locators.length; i++) { try { locsList.add(preparer.prepareProxy(locators[i]) ); } catch(Throwable e) { if( problemLogger.isLoggable(Level.INFO) ) { problemLogger.log(Level.INFO,"failure preparing recovered " +"lookup locator["+i+"]", e); }//endif } }//end loop if(locators.length != locsList.size()) { if( problemLogger.isLoggable(Levels.HANDLED) ) { problemLogger.log(Levels.HANDLED, "number of requested recovered " +"lookup locators = "+locators.length); problemLogger.log(Levels.HANDLED, "number of successfully " +"prepared recovered lookup locators = " +locsList.size()); for(int i=0; i<locsList.size(); i++) { problemLogger.log(Levels.HANDLED, "successfully prepared " +"recovered lookup locator = " +locsList.get(i)); }//end loop }//endif }//endif return ( (LookupLocator[])locsList.toArray (new LookupLocator[locsList.size()]) ); }//end prepareOldLocators /** Using the given <code>ProxyPreparer</code>, attempts to prepare each * element of the given <code>Set</code> of <code>LookupLocator</code> * instances; and returns a new <code>Set</code> containing the prepared * locators. If any attempt to prepare an element of the given * <code>Set</code> fails due to an exception, this method will skip * to the next locator in that input <code>Set</code>. * * This method is a convenience method that is typically used to * re-prepare the previously prepared locators that are retrieved * from the service's persisted state during recovery. * * @param preparer the preparer to use to prepare each element of the * input array * @param locators <code>Set</code> of <code>LookupLocator</code> * instances in which each element will be prepared. * * @return <code>Set</code> of <code>LookupLocator</code> instances in * which each element of the returned <code>Set</code> is the * result of successful proxy preparation of the corresponding * element of the input <code>Set</code> */ private static Set prepareOldLocators(ProxyPreparer preparer, Set locators) { Set locSet = new HashSet(locators.size()); LookupLocator[] locsArray = prepareOldLocators( preparer, (LookupLocator[])locators.toArray (new LookupLocator[locators.size()]) ); for(int i=0; i<locsArray.length; i++) { locSet.add(locsArray[i]); }//end loop return locSet; }//end prepareOldLocators /** Searches the given set of locators for the given individual locator, * returning <code>true</code> if the indicated locator is found in the * set; <code>false</code> otherwise. * * This method is a convenience method that is called instead of calling * only the <code>contains</code> method on the <code>Set</code> * parameter. This is necessary because the <code>equals</code> method * on <code>LookupLocator</code> performs a simple <code>String</code> * compare of the host names referenced by the locators being compared. * Such a comparison can result in a "false negative" when the hostname * returned by a remote system provides a fully-qualified hostname * (ex. "myhost.subdomain.mycompany.com"), but clients of this service * indicate interest in a locator using only the unqualified hostname * (ex. "myhost"). In this case, both host names are legal and * functionally equivalent, but the <code>equals</code> method on * <code>LookupLocator</code> will interpret them as unequal; resulting * in failure to discover locators that actually should be discovered. * * To address the problem described above, this method will do the * following when attempting to determine whether the given locator * is contained in the given set of locators: * * 1. Apply <code>Set</code>.<code>contains</code> which uses * <code>LookupLocator</code>.<code>equals</code> to determine * if the given locator is an element of the given set of locators. * 2. If the <code>Set</code>.<code>contains</code> method returns * <code>false</code>, then iterate through the elements of the * given set, retrieving and comparing the port and * <code>InetAddress</code> of each element to the port and * <code>InetAddress</code> of the given locator. * * @param locSet this method will determine whether or not the given * locator is contained in this <code>Set</code> of * <code>LookupLocator</code>s. * @param loc this method will determine whether or not this * <code>LookupLocator</code> is contained in the given set. * * @return <code>true</code> if the given set of locators contains the * given locator; <code>false</code> otherwise. */ private static boolean locSetContainsLoc(Set locSet, LookupLocator loc) { if( locSet.contains(loc) ) return true;//try LookupLocator.equals first /* Set containment test failed. Iterate through the set. */ int port0 = loc.getPort(); InetAddress addr0 = null; for(Iterator itr = locSet.iterator(); itr.hasNext(); ) { LookupLocator nextLoc = (LookupLocator)itr.next(); if(nextLoc.getPort() != port0) continue;//try next port in set if(addr0 == null) {//only need to retrieve addr0 once try { addr0 = InetAddress.getByName(loc.getHost()); } catch(Exception e) { problemLogger.log(Levels.HANDLED, "problem retrieving address by name", e); return false; } }//endif InetAddress addr1 = null; try { addr1 = InetAddress.getByName(nextLoc.getHost()); } catch(Exception e) { problemLogger.log(Level.FINEST, "problem retrieving address by name", e); continue;//try next address in set } if( addr1.equals(addr0) ) return true; }//end loop return false; }//end locSetContainsLoc /* **************** END Private Static Utility Methods ***************** */ /* ************** BEGIN Private NON-Static Utility Methods ************* */ /* BEGIN Private Startup Methods --------------------------------------- */ /** Common entry point for initialization of the service in any of its * possible modes: transient, non-activatable-persistent, or * activatable-persistent; with or without performing a JAAS login. */ private void init(String[] configArgs, boolean persistent) throws IOException, ConfigurationException, LoginException { config = ConfigurationProvider.getInstance ( configArgs, (this.getClass()).getClassLoader() ); loginContext = (LoginContext)config.getEntry(COMPONENT_NAME, "loginContext", LoginContext.class, null); if(loginContext != null) { initWithLogin(config, persistent, loginContext); } else { doInit(config, persistent); }//endif }//end init /** Initialization with JAAS login as the <code>Subject</code> referenced * in the given <code>loginContext</code>. */ private void initWithLogin(final Configuration config, final boolean persistent, LoginContext loginContext) throws IOException, ConfigurationException, LoginException { loginContext.login(); try { Subject.doAsPrivileged( loginContext.getSubject(), new PrivilegedExceptionAction() { public Object run() throws Exception { doInit(config, persistent); return null; }//end run }, null );//end doAsPrivileged } catch (Throwable e) { if(e instanceof PrivilegedExceptionAction) e = e.getCause(); if(e instanceof IOException) throw (IOException)e; if(e instanceof ConfigurationException) throw (ConfigurationException)e; throw new RuntimeException(e); } }//end initWithLogin /** Initialization common to all modes in which instances of this service * runs: activatable/persistent, non-activatable/persistent, and * transient (non-activatable /non-persistent). */ private void doInit(final Configuration config, final boolean persistent) throws IOException, ConfigurationException { /* Note that two discovery managers are retrieved/created in this * method. One manager is used internally by this service to provide * the lookup discovery capabilities offered to the clients that * register to use this service. That manager (discoveryMgr) is * considered part of this service's implementation, and thus is * created rather than retrieved from the configuration. When * created here, this manager is configured to initially discover * NO_GROUPS and NO LOCATORS. The sets of groups and locators to * discover on behalf of the registered clients will be populated * during recovery; and then the discovery mechanism provided by * this manager will be started later, after recovery and * configuration retrieval has occurred, and after this service * has been exported. Thus, it is important that this manager be * created prior to recovery; as is done below. * * The second discovery manager (joinMgrLDM) is passed to the join * manager that is employed by this service to advertise itself to * clients through lookup services. This discovery manager is * retrieved from the configuration, and must satisfy the following * requirements: must be an instance of both DiscoveryGroupManagement * and DiscoveryLocatorManagement, and should be initially configured * to discover NO_GROUPS and NO LOCATORS. This discovery manager is * retrieved from the configuration after recovery of any persistent * state, and after retrieval of any initial configuration items. */ discoveryMgr = new LookupDiscoveryManager (DiscoveryGroupManagement.NO_GROUPS, new LookupLocator[0], null, config); /* Get the proxy preparers for the remote event listeners */ listenerPreparer = (ProxyPreparer)Config.getNonNullEntry (config, COMPONENT_NAME, "listenerPreparer", ProxyPreparer.class, new BasicProxyPreparer()); /* Get the proxy preparers for the lookup locators to join */ locatorToJoinPreparer = (ProxyPreparer)Config.getNonNullEntry (config, COMPONENT_NAME, "locatorToJoinPreparer", ProxyPreparer.class, new BasicProxyPreparer()); /* Get the proxy preparers for the lookup locators to discover */ locatorToDiscoverPreparer = (ProxyPreparer)Config.getNonNullEntry (config, COMPONENT_NAME, "locatorToDiscoverPreparer", ProxyPreparer.class, new BasicProxyPreparer()); if(persistent) { /* Retrieve the proxy preparers that will only be applied during * the state recovery process below, when any previously stored * listeners or locators are recovered from persistent storage. */ recoveredListenerPreparer = (ProxyPreparer)Config.getNonNullEntry(config, COMPONENT_NAME, "recoveredListenerPreparer", ProxyPreparer.class, new BasicProxyPreparer()); recoveredLocatorToJoinPreparer = (ProxyPreparer)Config.getNonNullEntry (config, COMPONENT_NAME, "recoveredLocatorToJoinPreparer", ProxyPreparer.class, new BasicProxyPreparer()); recoveredLocatorToDiscoverPreparer = (ProxyPreparer)Config.getNonNullEntry (config, COMPONENT_NAME, "recoveredLocatorToDiscoverPreparer", ProxyPreparer.class, new BasicProxyPreparer()); /* Get the log directory for persisting this service's state */ persistDir = (String)Config.getNonNullEntry(config, COMPONENT_NAME, "persistenceDirectory", String.class); /* Recover the state that was persisted on prior runs (if any) */ log = new ReliableLog(persistDir, new LocalLogHandler()); inRecovery = true; log.recover(); inRecovery = false; }//endif(persistent) /* For the two persistent versions of this service (activatable and * non-activatable), state recovery is complete. For the non-persistent * version of this service, no state recovery occurred (because it * wasn't necessary). * * For the two persistent versions, there is a circumstance in which * 'one time', initial items must be retrieved from the configuration: * when the service is started for the very first time. For the * non-persistent version, those items will be retrieved every time * the service is started. * * The flag 'initialStartup' is used below to determine whether * or not to retrieve the initial configuration items. This is the * only purpose for that flag. * * For either persistent version of the service, the flag's value * will be changed to false during the startup process only when * there already exists a 'snapshot' of the service's state from * a previous run. This is because the flag's value is only * changed during the recovery of the snapshot (see the method * recoverSnapshot()). Note that the only time such a snapshot * should NOT already exist at startup, is when the service is * being started for the very first time. Thus, when either * persistent version of the service is started for the first * time, the service's configuration is consulted for the initial * values of the items below; otherwise, when the service is being * re-started (after a crash for example), the values used for * those items will be the values retrieved above during recovery * of the service's persistent state. * * With respect to the non-persistent version of the service, the * values of the items below will always be retrieved at startup. * This is because the non-persistent version of the service never * attempts to recover previously stored state; thus, the flag's * value will never change. Note that this will be true even if a * snapshot exists from a previous run of one of the persistent * versions of the service. * * The service's Uuid is also handled here. */ if(initialStartup) { if(log != null) { snapshotWt = ((Float)config.getEntry (COMPONENT_NAME, "initialPersistenceSnapshotWeight", float.class, new Float(snapshotWt))).floatValue(); snapshotThresh = Config.getIntEntry (config, COMPONENT_NAME, "initialPersistenceSnapshotThreshold", snapshotThresh, 0, Integer.MAX_VALUE); }//endif(log != null) leaseBound = Config.getLongEntry(config, COMPONENT_NAME, "initialLeaseBound", leaseBound, 0, Long.MAX_VALUE); /* Get any additional attributes with which to associate this * service when registering it with any lookup services. */ Entry[] initAttrs = (Entry[])config.getEntry (COMPONENT_NAME, "initialLookupAttributes", Entry[].class, null ); if(initAttrs != null) { ArrayList attrsList = new ArrayList(thisServicesAttrs.length+initAttrs.length); for(int i=0;i<thisServicesAttrs.length;i++) { attrsList.add(thisServicesAttrs[i]); }//end loop for(int i=0;i<initAttrs.length;i++) { attrsList.add(initAttrs[i]); }//end loop thisServicesAttrs = (Entry[])attrsList.toArray (new Entry[attrsList.size()]); }//endif(initAttrs != null) /* Get the initial groups this service should join. */ thisServicesGroups = (String[])config.getEntry(COMPONENT_NAME, "initialLookupGroups", String[].class, thisServicesGroups); /* Get the initial locators this service should join. */ thisServicesLocators = (LookupLocator[])config.getEntry(COMPONENT_NAME, "initialLookupLocators", LookupLocator[].class, new LookupLocator[0]); if(thisServicesLocators == null) { thisServicesLocators = new LookupLocator[0]; }//endif /* Generate the private, universally unique (over space and time) * ID that will be used by the outer proxy to test for equality * with other proxies. */ proxyID = UuidFactory.generate(); }//endif(initialStartup) /* The proxyID should never be null at this point. It should have * been either recovered from the persisted state, or generated above. */ if(proxyID == null) throw new NullPointerException("proxyID == null"); /* Get the various configurable constants */ leaseMax = Config.getLongEntry(config, COMPONENT_NAME, "leaseMax", leaseMax, 0, Long.MAX_VALUE); /* Take a snapshot of the current state to "clean up" the log file, * and to record the items set above. */ if(log != null) log.snapshot(); /* The service ID used to register this service with lookup services * is always derived from the proxyID that is associated with the * service for the lifetime of the service. */ serviceID = new ServiceID(proxyID.getMostSignificantBits(), proxyID.getLeastSignificantBits()); /* Get a general-purpose task manager for this service */ taskMgr = (TaskManager)Config.getNonNullEntry (config, COMPONENT_NAME, "taskManager", TaskManager.class, new TaskManager(10,1000*15,1.0f) ); /* Get the discovery manager to pass to this service's join manager. */ try { joinMgrLDM = (DiscoveryManagement)Config.getNonNullEntry (config, COMPONENT_NAME, "discoveryManager", DiscoveryManagement.class); if( joinMgrLDM instanceof DiscoveryGroupManagement ) { String[] groups0 = ((DiscoveryGroupManagement)joinMgrLDM).getGroups(); if( (groups0 == DiscoveryGroupManagement.ALL_GROUPS) || (groups0.length != 0) ) { throw new ConfigurationException ("discoveryManager entry must be configured " +"to initially discover/join NO_GROUPS"); }//endif } else {// !(joinMgrLDM instanceof DiscoveryGroupManagement) throw new ConfigurationException ("discoveryManager entry must " +"implement DiscoveryGroupManagement"); }//endif if( joinMgrLDM instanceof DiscoveryLocatorManagement ) { LookupLocator[] locs0 = ((DiscoveryLocatorManagement)joinMgrLDM).getLocators(); if( (locs0 != null) && (locs0.length != 0) ) { throw new ConfigurationException ("discoveryManager entry must be configured " +"to initially discover/join no locators"); }//endif } else {// !(joinMgrLDM instanceof DiscoveryLocatorManagement) throw new ConfigurationException ("discoveryManager entry must " +"implement DiscoveryLocatorManagement"); }//endif } catch (NoSuchEntryException e) { joinMgrLDM = new LookupDiscoveryManager(DiscoveryGroupManagement.NO_GROUPS, new LookupLocator[0], null,config); } /* Handle items and duties related to exporting this service. */ ServerEndpoint endpoint = TcpServerEndpoint.getInstance(0); InvocationLayerFactory ilFactory = new BasicILFactory(); Exporter defaultExporter = new BasicJeriExporter(endpoint, ilFactory, false, true); /* For the activatable server */ if(activationID != null) { ProxyPreparer aidPreparer = (ProxyPreparer)Config.getNonNullEntry(config, COMPONENT_NAME, "activationIdPreparer", ProxyPreparer.class, new BasicProxyPreparer()); ProxyPreparer aSysPreparer = (ProxyPreparer)Config.getNonNullEntry(config, COMPONENT_NAME, "activationSystemPreparer", ProxyPreparer.class, new BasicProxyPreparer()); activationID = (ActivationID)aidPreparer.prepareProxy (activationID); activationSystem = (ActivationSystem)aSysPreparer.prepareProxy (activationSystem); defaultExporter = new ActivationExporter(activationID, defaultExporter); }//endif(activationID != null) /* Get the exporter that will be used to export this service */ try { serverExporter = (Exporter)Config.getNonNullEntry(config, COMPONENT_NAME, "serverExporter", Exporter.class, defaultExporter, activationID); } catch(ConfigurationException e) {// exception, use default throw new ExportException("Configuration exception while " +"retrieving service's exporter", e); } /* Export this service */ innerProxy = (Fiddler)serverExporter.export(this); /* Create the outer (smart) proxy that is registered with lookups */ outerProxy = FiddlerProxy.createServiceProxy(innerProxy, proxyID); /* Create the proxy that can be used to administer this service */ adminProxy = FiddlerAdminProxy.createAdminProxy(innerProxy, proxyID); /* Create the following threads here, after a possible JAAS login, * rather than in the constructor, before the login. This must * be done so that the threads will have the correct subject. */ leaseExpireThread = new LeaseExpireThread(); if(log != null) snapshotThread = new SnapshotThread(); /* Start the discovery mechanism for all recovered registrations */ discoveryMgr.addDiscoveryListener(discoveryListener); /* Advertise the services provided by this entity */ joinMgr = new JoinManager(outerProxy, thisServicesAttrs, serviceID, joinMgrLDM, null, config); ((DiscoveryLocatorManagement)joinMgrLDM).setLocators (thisServicesLocators); ((DiscoveryGroupManagement)joinMgrLDM).setGroups(thisServicesGroups); /* start up all the daemon threads */ leaseExpireThread.start(); if(log != null) snapshotThread.start(); logInfoStartup(); readyState.ready(); }//end doInit /* END Private Startup Methods ----------------------------------------- */ /* BEGIN Private Shutdown Methods -------------------------------------- */ /* Called in the constructor when failure occurs during the initialization * process. Un-does any work that may have already been completed; for * example, un-exports the service if it has already been exported, * terminates any threads that may have been started, etc. */ private void cleanupInitFailure() { if(innerProxy != null) { try { serverExporter.unexport(true); } catch(Throwable t) { } }//endif if(taskMgr != null) { try { taskMgr.terminate(); } catch(Throwable t) { } }//endif if(joinMgr != null) { try { joinMgr.terminate(); } catch(Throwable t) { } }//endif if(joinMgrLDM != null) { try { joinMgrLDM.terminate(); } catch(Throwable t) { } }//endif if(discoveryMgr != null) { try { discoveryMgr.terminate(); } catch(Throwable t) { } }//endif if(leaseExpireThread != null) { try { leaseExpireThread.interrupt(); leaseExpireThread.join(); } catch(Throwable t) { } }//endif if(snapshotThread != null) { try { snapshotThread.interrupt(); snapshotThread.join(); } catch(Throwable t) { } }//endif }//end cleanupInitFailure /* Convenience method called in the constructor or the activatable version * of this service when failure occurs during the initialization process. * Logs and rethrows the given <code>Throwable</code> so the constructor * doesn't have to. */ private void handleActivatableInitThrowable(Throwable t) throws IOException, ActivationException, ConfigurationException, LoginException, ClassNotFoundException { handleInitThrowable(t); if (t instanceof ActivationException) { throw (ActivationException)t; } else if (t instanceof LoginException) { throw (ClassNotFoundException)t; } else { throw new AssertionError(t); }//endif }//end handleInitThrowable /* Convenience method called in the constructor or the non-activatable * version of this service when failure occurs during the initialization * process. Logs and rethrows the given <code>Throwable</code> so the * constructor doesn't have to. */ private void handleInitThrowable(Throwable t) throws IOException, ConfigurationException, LoginException { problemLogger.log(Level.SEVERE, "cannot initialize the service", t); if (t instanceof IOException) { throw (IOException)t; } else if (t instanceof ConfigurationException) { throw (ConfigurationException)t; } else if (t instanceof LoginException) { throw (LoginException)t; } else if (t instanceof RuntimeException) { throw (RuntimeException)t; } else if (t instanceof Error) { throw (Error)t; }//endif }//end handleInitThrowable /** * Called by the public method <code>destroy</code> as well as by the * <code>apply</code> method in the various LogObj classes that, * during recovery, modify the managed sets of the discovery manager; * and, while doing so, experience an IOException when the multicast * request protocol fails to start during recovery (an un-recoverable * exception). * * This method destroys the lookup discovery service, if possible, * including its persistent storage. This method spawns a separate * thread to do the actual work asynchronously, so a successful * return from this method usually does not mean that the service * has been destroyed. * * @see com.sun.jini.fiddler.FiddlerImpl#destroy */ private void destroyDo() { (new DestroyThread()).start(); }//end destroyDo /* END Private Shutdown Methods ---------------------------------------- */ /* BEGIN Private Registration Methods ---------------------------------- */ /** * This method is called by the public method <code>register</code>. * This method creates a registration object that is an instance of * <code>FiddlerRegistration</code> which implements the interface * <code>LookupDiscoveryRegistration</code>, and acts as a proxy to the * registration-related methods of the backend server of the Fiddler * implementation of the lookup discovery service. * <p> * This method also associates with the registration all information * needed by the registration to participate in the lookup discovery * service, information such as: IDs, lease information, event information. * * @param groups names of groups whose members are the lookup * services to discover. * @param locators instances of LookupLocator, each corresponding to * a specific lookup service to discover. * @param listener the entity that will receive events notifying the * registration that a lookup service of interest has * been discovered. * @param handback the object that will be included in every * notification event sent to the registered listener. * @param leaseDuration long value representing the amount of time (in * milliseconds) for which the resources of the * lookup discovery service are being requested. * * @return an instance of FiddlerRegistration which implements the * LookupDiscoveryRegistration interface, and acts as a proxy * to the registration-related methods of the backend server * of the Fiddler implementation of the lookup discovery service * * @throws java.io.IOException this exception occurs when the multicast * request protocol fails to start. * * @throws java.rmi.RemoteException this exception occurs when the * attempt to prepare the listener fails due to a * <code>RemoteException</code> * * @throws java.lang.SecurityException this exception occurs when the * attempt to prepare the listener fails due to a * <code>SecurityException</code> * * @see com.sun.jini.fiddler.FiddlerImpl#register */ private LookupDiscoveryRegistration registerDo (String[] groups, LookupLocator[] locators, RemoteEventListener listener, MarshalledObject handback, long leaseDuration) throws RemoteException, IOException { /* Input okay. Create the registration and associated information */ long curTime = System.currentTimeMillis(); leaseDuration = applyBoundToLeaseDuration(leaseDuration, leaseBound); Uuid regID = UuidFactory.generate(); Uuid leaseID = regID;//use same ID since Reg "wraps" the lease long expiration = curTime + leaseDuration; /* Prepare the new listener */ listener = (RemoteEventListener)listenerPreparer.prepareProxy (listener); RegistrationInfo regInfo = new RegistrationInfo( regID, groups,locators, leaseID,expiration, curEventID,handback, listener ); curEventID++; addRegistration(regInfo); logInfoRegistration("\nadded registration: registrationID = ",regID); addLogRecord(new RegistrationGrantedLogObj(regInfo)); /* Queue task for sending a discovered event */ taskMgr.add(new NewRegistrationTask(regInfo)); /* See if the expire thread needs to wake up earlier */ if (expiration < minExpiration) { minExpiration = expiration; concurrentObj.waiterNotify(leaseExpireThreadSyncObj); } FiddlerLease regLease = FiddlerLease.createLease (innerProxy, proxyID, regID, leaseID, expiration); EventRegistration eventReg = new EventRegistration( regInfo.eventID, outerProxy, regLease, regInfo.seqNum ); logInfoGroups(); logInfoLocators(); FiddlerRegistration regObj = FiddlerRegistration.createRegistration (innerProxy, regID, eventReg); logInfoRegistration("\ncreated registration: registrationID = ", regObj); return regObj; }//end registerDo /** * Places the registration corresponding to the <code>regInfo</code> * parameter in both the <code>registrationByID</code> map and the * <code>registrationByTime</code> map. This method also updates the * managed sets in the discovery manager in the appropriate way. This * should be called whenever a new registration has been created * and needs to be added to the data base (for example, the methods * <code>registerDo</code>, <code>recoverSnapshot</code> and the * <code>apply</code> method of the <code>RegistrationGrantedLogObj</code> * class all call this method). * * @param regInfo the data structure record corresponding to the * registration that is to be added to the data base * * @throws java.io.IOException this exception occurs when the multicast * request protocol fails to start. * * @see com.sun.jini.fiddler.FiddlerImpl#registerDo * @see com.sun.jini.fiddler.FiddlerImpl#recoverSnapshot */ private void addRegistration(RegistrationInfo regInfo) throws IOException { if (regInfo.listener == null) { /* failed to recover from log */ if( problemLogger.isLoggable(Level.INFO) ) { problemLogger.log(Level.INFO, "cannot add registration (ID = " +regInfo.registrationID+"); failed to " +"unmarshal listener during recovery"); }//endif return; }//endif /* First add the indicated registration */ registrationByID.put(regInfo.registrationID, regInfo); registrationByTime.put(regInfo,regInfo); /* Update the set of groups managed by the discovery manager */ updateDiscoveryMgrGroups(); /* Update the set of locators managed by the discovery manager */ updateDiscoveryMgrLocators(); }//end addRegistration /** * Removes the registration corresponding to the <code>regInfo</code> * parameter from this service's state. Removes the registration from * both the <code>registrationByID</code> map and the * <code>registrationByTime</code> map. This method also updates the * managed sets in the discovery manager in the appropriate way. This * should be called whenever a current registration needs to be removed * from the data base (for example, when the registration's lease is * is expired in the <code>LeaseExpireThread</code>, and when the * registration's lease is cancelled in the <code>cancelLeaseDo</code> * method). * * @param regInfo the data structure record corresponding to the * registration that is to be removed from the data base * * @throws java.io.IOException this exception occurs when the multicast * request protocol fails to start. * * @see com.sun.jini.fiddler.FiddlerImpl#cancelLeaseDo */ private void removeRegistration(RegistrationInfo regInfo) throws IOException { /* First remove the current registration */ registrationByID.remove(regInfo.registrationID); registrationByTime.remove(regInfo); logInfoRegistration("\nremoved registration: registrationID = ", regInfo.registrationID); /* Update the set of groups managed by the discovery manager */ updateDiscoveryMgrGroups(); /* Update the set of locators managed by the discovery manager */ updateDiscoveryMgrLocators(); logInfoGroups(); logInfoLocators(); }//end removeRegistration /* END Private Registration Methods ------------------------------------ */ /* BEGIN Private Group Management Methods ------------------------------ */ /** * Called by the public method <code>addGroups</code>. This method * queues an <code>AddGroupsTask</code> which performs the actual * augmentation of the given registration's desired groups. * * @param regInfo the data structure record corresponding to the * registration whose managed set of groups is to be * augmented * @param groups a String array, none of whose elements may be null, * consisting of the group names with which to augment the * registration's managed set of groups. * * @see com.sun.jini.fiddler.FiddlerImpl#addGroups * @see com.sun.jini.fiddler.FiddlerRegistration#addGroups * @see net.jini.discovery.LookupDiscoveryRegistration#addGroups */ private void addGroupsDo(RegistrationInfo regInfo, String[] groups) { taskMgr.add(new AddGroupsTask(regInfo,groups)); }//end addGroupsDo /** * Called by the <code>apply</code> method of the class * <code>GroupsAddedToRegistrationLogObj</code> (which is invoked * during state recovery). This method queues an * <code>AddGroupsTask</code> which performs the actual augmentation * of the given registration's desired groups. * <p> * @param registrationID the ID of the data structure record * corresponding to the registration whose * managed set of groups is to be augmented * @param registrationByID the map containing all active registrations * managed by this service * @param groups a <code>String</code> array, none of whose * elements may be null, consisting of the group * names with which to augment the registration's * managed set of groups. * * @see com.sun.jini.fiddler.FiddlerImpl#addGroupsDo * @see com.sun.jini.fiddler.FiddlerImpl#addGroups * @see com.sun.jini.fiddler.FiddlerRegistration#addGroups * @see net.jini.discovery.LookupDiscoveryRegistration#addGroups */ private void addGroupsDo(Uuid registrationID, HashMap registrationByID, String[] groups) { addGroupsDo((RegistrationInfo)(registrationByID.get(registrationID)), groups); }//end addGroupsDo /** * Called by the public method <code>setGroups</code>. This method * queues a <code>SetGroupsTask</code> which performs the actual * replacement. * * @param regInfo the data structure record corresponding to the * registration whose managed set of groups is to be * replaced * @param groups a String array, none of whose elements may be null, * consisting of the group names with which to replace the * registration's managed set of groups. * * @see com.sun.jini.fiddler.FiddlerImpl#setGroups * @see com.sun.jini.fiddler.FiddlerRegistration#setGroups * @see net.jini.discovery.LookupDiscoveryRegistration#setGroups */ private void setGroupsDo(RegistrationInfo regInfo, String[] groups) { taskMgr.add(new SetGroupsTask(regInfo,groups)); }//end setGroupsDo /** * Called by the <code>apply</code> method of the class * <code>GroupsSetInRegistrationLogObj</code> (which is invoked * during state recovery). This method queues a * <code>SetGroupsTask</code> which performs the actual replacement. * <p> * @param registrationID the ID of the data structure record * corresponding to the registration whose * managed set of groups is to be replaced * @param registrationByID the map containing all active registrations * managed by this service * @param groups a <code>String</code> array, none of whose * elements may be null, consisting of the group * names with which to replace the registration's * managed set of groups. * * @see com.sun.jini.fiddler.FiddlerImpl#setGroupsDo * @see com.sun.jini.fiddler.FiddlerImpl#setGroups * @see com.sun.jini.fiddler.FiddlerRegistration#setGroups * @see net.jini.discovery.LookupDiscoveryRegistration#setGroups */ private void setGroupsDo(Uuid registrationID, HashMap registrationByID, String[] groups) { setGroupsDo((RegistrationInfo)(registrationByID.get(registrationID)), groups); }//end setGroupsDo /** * Called by the public method <code>removeGroups</code>. This method * queues a <code>RemoveGroupsTask</code> which performs the actual * removal. * * @param regInfo the data structure record corresponding to the * registration from whose managed set of groups the input * set of groups is to be removed * @param groups a String array, none of whose elements may be null, * consisting of the group names to remove from the * registration's managed set of groups * * @see com.sun.jini.fiddler.FiddlerImpl#removeGroups * @see com.sun.jini.fiddler.FiddlerRegistration#removeGroups * @see net.jini.discovery.LookupDiscoveryRegistration#removeGroups */ private void removeGroupsDo(RegistrationInfo regInfo, String[] groups) { taskMgr.add(new RemoveGroupsTask(regInfo,groups)); }//end removeGroupsDo /** * Called by the <code>apply</code> method of the class * <code>GroupsRemovedFromRegistrationLogObj</code> (which is * invoked during state recovery). This method queues a * <code>RemoveGroupsTask</code> which performs the actual removal. * <p> * @param registrationID the ID of the data structure record * corresponding to the registration from whose * managed set of groups the input set of groups * is to be removed * @param registrationByID the map containing all active registrations * managed by this service * @param groups a <code>String</code> array, none of whose * elements may be null, consisting of the group * names to remove from the registration's managed * set of groups * * @see com.sun.jini.fiddler.FiddlerImpl#removeGroupsDo * @see com.sun.jini.fiddler.FiddlerImpl#removeGroups * @see com.sun.jini.fiddler.FiddlerRegistration#removeGroups * @see net.jini.discovery.LookupDiscoveryRegistration#removeGroups */ private void removeGroupsDo(Uuid registrationID, HashMap registrationByID, String[] groups) { removeGroupsDo ((RegistrationInfo)(registrationByID.get(registrationID)), groups); }//end removeGroupsDo /** Builds a set containing the groups from all registrations. * * Iterates through all the active registrations, retrieving the groups * to discover for each registration (minus duplicates). If at least one * registration has requested that all groups be discovered, stops * iterating (because the set must be ALL_GROUPS) and returns null. */ private String[] getGroupsFromAllRegs() { HashSet groupSet = new HashSet(); for(Iterator itr=registrationByID.values().iterator();itr.hasNext();) { RegistrationInfo rInfo = (RegistrationInfo)itr.next(); if(rInfo.groups == null) { return DiscoveryGroupManagement.ALL_GROUPS; } else { groupSet.addAll(rInfo.groups); }//end if }//end loop return (String[])groupSet.toArray(new String[groupSet.size()]); }//end getGroupsFromAllRegs /** This method returns a registrar-to-data-structure map in which each * registrar key in the returned map is one of the keys from the * the global map <code>allDiscoveredRegs</code>, and the corresponding * value is the (locator,groups) pair that corresponds to that registrar * key in <code>allDiscoveredRegs</code> * * An element of <code>allDiscoveredRegs</code> is selected to have * its registrar and associated (locator,groups) pair be included in the * returned mapping if and only if the key value of the element is a * registrar that belongs to at least one of the desired groups of the * given registration (<code>regInfo</code> parameter). That is, the * registrars referenced in the returned mapping are the registrars that * are of interest - through group discovery - to the given registration. * * Note that this method must be called from within a synchronization * block. * * @param regInfo the data structure record corresponding to the * registration whose groups-to-discover will be used to * select the elements from <code>allDiscoveredRegs</code> * to include in the return mapping * * @return a mapping in which the key values are registrars that, * in addition to being elements of the global map * <code>allDiscoveredRegs</code>, also belong to at least one * of the desired groups referenced in the <code>regInfo</code> * parameter; and whose map values are data structures of type * <code>LocatorGroupsStruct</code> that contain the associated * locator and member groups of the corresponding registrar key */ private HashMap getDesiredRegsByGroup(RegistrationInfo regInfo) { HashSet desiredGroups = regInfo.groups; HashMap desiredRegMap = new HashMap(allDiscoveredRegs.size()); Set eSet = allDiscoveredRegs.entrySet(); for(Iterator itr = eSet.iterator(); itr.hasNext(); ) { Map.Entry pair = (Map.Entry)itr.next(); LocatorGroupsStruct locGroupsStruct = (LocatorGroupsStruct)pair.getValue(); if( interested(locGroupsStruct.groups,desiredGroups) ) { desiredRegMap.put((ServiceRegistrar)pair.getKey(), locGroupsStruct); }//endif }//end loop return desiredRegMap; }//end getDesiredRegsByGroup /** Modifies the discovery manager's managed set of groups in the * appropriate way; that is, adds to the discovery manager's managed * set of groups, any new groups-of-interest, across all registrations, * and removes any groups that are no longer of interest to any of the * registrations. This method is typically called after the groups of * a particular registration have been modified. * * Note that this method must be called from within a synchronization * block. */ private void updateDiscoveryMgrGroups() { /* Build the set containing the groups from all registrations. */ String[] groupsAcrossAllRegs = getGroupsFromAllRegs(); /* Update the discovery manager's set of groups to discover. Let * the manager sort out duplicates and any already-discovered groups. */ try { discoveryMgr.setGroups( groupsAcrossAllRegs ); } catch (IOException e) { String warnStr = "IOException: on call to setGroups() " +"method of discovery manager"; problemLogger.log(Level.INFO, warnStr, e); Entry[] warnAttrs = new Entry[] { new FiddlerStatus(StatusType.WARNING), new Comment(warnStr) }; joinMgr.addAttributes(warnAttrs,true); } catch(IllegalStateException e) { String errStr = "IllegalStateException: discovery " +"manager's setGroups() method was called " +"after the manager was terminated"; problemLogger.log(Level.INFO, errStr, e); Entry[] errorAttrs = new Entry[] { new FiddlerStatus(StatusType.ERROR), new Comment(errStr) }; joinMgr.addAttributes(errorAttrs,true); throw new IllegalStateException(" discovery manager's " +"setGroups() method was " +"called after the manager " +"was terminated"); }//end try }//end updateDiscoveryMgrGroups /* END Private Group Management Methods -------------------------------- */ /* BEGIN Private Locator Management Methods ---------------------------- */ /** * Called by the public method <code>addLocators</code>. This method * queues an <code>AddLocatorsTask</code> which performs the actual * augmentation of the given registration's desired locators. * * @param regInfo the data structure record corresponding to the * registration whose managed set of locators is to be * augmented * @param locators an array, none of whose elements may be null, * consisting of the <code>LookupLocator</code> objects * with which to augment the registration's managed set * of locators. * * @see com.sun.jini.fiddler.FiddlerImpl#addLocators * @see com.sun.jini.fiddler.FiddlerRegistration#addLocators * @see net.jini.discovery.LookupDiscoveryRegistration#addLocators */ private void addLocatorsDo(RegistrationInfo regInfo, LookupLocator[] locators) { taskMgr.add(new AddLocatorsTask(regInfo,locators)); }//end addLocatorsDo /** * Called by the <code>apply</code> method of the class * <code>LocsAddedToRegistrationLogObj</code> (which is invoked * during state recovery). This method queues an * <code>AddLocatorsTask</code> which performs the actual augmentation * of the given registration's desired locators. * <p> * @param registrationID the ID of the data structure record * corresponding to the registration whose * managed set of locators is to be augmented * @param registrationByID the map containing all active registrations * managed by this service * @param locators an array, none of whose elements may be null, * consisting of the <code>LookupLocator</code> * objects with which to augment the * registration's managed set of locators. * * @see com.sun.jini.fiddler.FiddlerImpl#addLocatorsDo * @see com.sun.jini.fiddler.FiddlerImpl#addLocators * @see com.sun.jini.fiddler.FiddlerRegistration#addLocators * @see net.jini.discovery.LookupDiscoveryRegistration#addLocators */ private void addLocatorsDo(Uuid registrationID, HashMap registrationByID, LookupLocator[] locators) { addLocatorsDo((RegistrationInfo)(registrationByID.get(registrationID)), locators); }//end addLocatorsDo /** * Called by the public method <code>setLocators</code>. This method * queues a <code>SetLocatorsTask</code> which performs the * actual replacement. * * @param regInfo the data structure record corresponding to the * registration whose managed set of locators is to be * replaced * @param locators an array, none of whose elements may be null, * consisting of the <code>LookupLocator</code> objects * with which to replace the registration's managed set * of locators. * * @see com.sun.jini.fiddler.FiddlerImpl#setLocators * @see com.sun.jini.fiddler.FiddlerRegistration#setLocators * @see net.jini.discovery.LookupDiscoveryRegistration#setLocators */ private void setLocatorsDo(RegistrationInfo regInfo, LookupLocator[] locators) { taskMgr.add(new SetLocatorsTask(regInfo,locators)); }//end setLocatorsDo /** * Called by the <code>apply</code> method of the class * <code>LocsSetInRegistrationLogObj</code> (which is invoked * during state recovery). This method queues a * <code>SetLocatorsTask</code> which performs the actual replacement. * <p> * @param registrationID the ID of the data structure record * corresponding to the registration whose * managed set of locators is to be replaced * @param registrationByID the map containing all active registrations * managed by this service * @param locators an array, none of whose elements may be null, * consisting of the <code>LookupLocator</code> * objects with which to replace the * registration's managed set of locators. * * @see com.sun.jini.fiddler.FiddlerImpl#setLocatorsDo * @see com.sun.jini.fiddler.FiddlerImpl#setLocators * @see com.sun.jini.fiddler.FiddlerRegistration#setLocators * @see net.jini.discovery.LookupDiscoveryRegistration#setLocators */ private void setLocatorsDo(Uuid registrationID, HashMap registrationByID, LookupLocator[] locators) { setLocatorsDo((RegistrationInfo)(registrationByID.get(registrationID)), locators); }//end setLocatorsDo /** * Called by the public method <code>removeLocators</code>. This method * queues a <code>RemoveLocatorsTask</code> which performs the * actual removal. * * @param regInfo the data structure record corresponding to the * registration from whose managed set of locators the * input set of locators is to be removed * @param locators an array, none of whose elements may be null, * consisting of the <code>LookupLocator</code> objects * to remove from the registration's managed set of * locators. * * @see com.sun.jini.fiddler.FiddlerImpl#removeLocators * @see com.sun.jini.fiddler.FiddlerRegistration#removeLocators * @see net.jini.discovery.LookupDiscoveryRegistration#removeLocators */ private void removeLocatorsDo(RegistrationInfo regInfo, LookupLocator[] locators) { taskMgr.add(new RemoveLocatorsTask(regInfo,locators)); }//end removeLocatorsDo /** * Called by the <code>apply</code> method of the class * <code>LocsRemovedFromRegistrationLogObj</code> (which is * invoked during state recovery). This method queues a * <code>RemoveLocatorsTask</code> which performs the actual removal. * <p> * @param registrationID the ID of the data structure record * corresponding to the registration from whose * managed set of groups the input set of locators * is to be removed * @param registrationByID the map containing all active registrations * managed by this service * @param locators an array, none of whose elements may be null, * consisting of the <code>LookupLocator</code> * objects to remove from the registration's * managed set of locators. * * @see com.sun.jini.fiddler.FiddlerImpl#removeLocatorsDo * @see com.sun.jini.fiddler.FiddlerImpl#removeLocators * @see com.sun.jini.fiddler.FiddlerRegistration#removeLocators * @see net.jini.discovery.LookupDiscoveryRegistration#removeLocators */ private void removeLocatorsDo(Uuid registrationID, HashMap registrationByID, LookupLocator[] locators) { removeLocatorsDo ((RegistrationInfo)(registrationByID.get(registrationID)), locators); }//end removeLocatorsDo /** Builds a set containing the locators from all registrations. * * Iterates through all the active registrations, retrieving the locators * to discover for each registration (minus duplicates). */ private LookupLocator[] getLocatorsFromAllRegs() { HashSet locatorSet = new HashSet(); for(Iterator itr=registrationByID.values().iterator();itr.hasNext();) { RegistrationInfo rInfo = (RegistrationInfo)itr.next(); if(rInfo.locators == null) { throw new AssertionError ("registration contains a null set of locators"); } else { locatorSet.addAll(rInfo.locators); }//end if }//end loop return (LookupLocator[])locatorSet.toArray (new LookupLocator[locatorSet.size()]); }//end getLocatorsFromAllRegs /** This method returns a registrar-to-data-structure map in which each * registrar key in the returned map is one of the keys from the * the global map <code>allDiscoveredRegs</code>, and the corresponding * value is the (locator,groups) pair that corresponds to that registrar * key in <code>allDiscoveredRegs</code> * * An element of <code>allDiscoveredRegs</code> is selected to have * its registrar and associated (locator,groups) pair be included in the * returned mapping if and only if the key value under consideration is * a registrar whose locator equals one of the desired locators of the * given registration (<code>regInfo</code> parameter). That is, the * registrars referenced in the returned mapping are the registrars that * are of interest - through locator discovery - to the given * registration. * * @param regInfo the data structure record corresponding to the * registration whose locators-to-discover will be used to * select the elements from <code>allDiscoveredRegs</code> * to include in the return mapping * * @return a mapping in which the key values are registrars that, * in addition to being elements of the global map * <code>allDiscoveredRegs</code>, have locators that are * elements of the set of desired locators referenced in the * <code>regInfo</code> parameter; and whose map values are * data structures of type <code>LocatorGroupsStruct</code> that * contain the associated locator and member groups of the * corresponding registrar key */ private HashMap getDesiredRegsByLocator(RegistrationInfo regInfo) { HashSet desiredLocators = regInfo.locators; HashMap desiredRegMap = new HashMap(allDiscoveredRegs.size()); Set eSet = allDiscoveredRegs.entrySet(); for(Iterator itr = eSet.iterator(); itr.hasNext(); ) { Map.Entry pair = (Map.Entry)itr.next(); LocatorGroupsStruct locGroupsStruct = (LocatorGroupsStruct)pair.getValue(); if(locSetContainsLoc(desiredLocators,locGroupsStruct.locator)) { desiredRegMap.put((ServiceRegistrar)pair.getKey(), locGroupsStruct); }//endif }//end loop return desiredRegMap; }//end getDesiredRegsByLocator /** Modifies the discovery manager's managed set of locators in the * appropriate way; that is, adds to the discovery manager's managed * set of locators, any new locators-of-interest, across all * registrations, and removes any locators that are no longer of * interest to any of the registrations. This method is typically * called after the locators of a particular registration have been * modified. * * Note that this method must be called from within a synchronization * block. */ private void updateDiscoveryMgrLocators() { /* Build the set containing the locators from all registrations. */ LookupLocator[] locsAcrossAllRegs = getLocatorsFromAllRegs(); /* Update the discovery manager's set of locators to discover. Let * the manager sort out duplicates and any already-discovered locators. */ try { discoveryMgr.setLocators( locsAcrossAllRegs ); } catch(IllegalStateException e) { String errStr = "IllegalStateException: discovery " +"manager's setLocators() method was called " +"after the manager was terminated"; problemLogger.log(Level.INFO, errStr, e); Entry[] errorAttrs = new Entry[] { new FiddlerStatus(StatusType.ERROR), new Comment(errStr) }; joinMgr.addAttributes(errorAttrs,true); throw new IllegalStateException(" discovery manager's " +"setLocators() method was " +"called after the manager " +"was terminated"); }//end try }//end updateDiscoveryMgrLocators /* END Private Locator Management Methods ------------------------------ */ /* BEGIN Private Lease Renewal Methods --------------------------------- */ /** * Called by the public method <code>renewLease</code>. This method renews * the lease corresponding to the <code>registrationID</code> and * <code>leaseID</code> parameters, granting a new duration that is * less than or equal to the requested duration value contained in * the <code>duration</code> parameter. * * @param regInfo the data structure record corresponding to the * registration whose lease is to be renewed * @param leaseID identifier assigned by the lease grantor to the lease * that is to be renewed * @param duration the requested duration for the lease being renewed * * @throws net.jini.core.lease.UnknownLeaseException this exception occurs * when the lease being renewed is unknown to the lease grantor. * * @return <code>long</code> value representing the actual duration that * was granted for the renewed lease. Note that the actual * duration granted and returned by this method may be less than * the duration requested. * * @see com.sun.jini.fiddler.FiddlerImpl#renewLease */ private long renewLeaseDo(RegistrationInfo regInfo, Uuid leaseID, long duration) throws UnknownLeaseException { long curTime = System.currentTimeMillis(); long newExpiration = renewLeaseInt(regInfo,leaseID,duration,curTime); addLogRecord(new LeaseRenewedLogObj (regInfo.registrationID, leaseID, newExpiration)); return (newExpiration - curTime); }//end renewLeaseDo /** * Called by the method <code>renewLeaseDo</code>. This method takes * a requested duration for the lease corresponding to the * <code>registrationID</code> and <code>leaseID</code> parameters, * converts that duration to an absolute expiration (based on the local * clock) with the appropriate bound applied, and attempts to renew * the lease, granting the new expiration time on the lease. * * @param regInfo the data structure record corresponding to the * registration whose lease is to be renewed * @param leaseID identifier assigned by the lease grantor to the lease * that is to be renewed * @param duration the requested duration for the lease being renewed * * @return <code>long</code> value representing the new absolute * expiration time granted for the renewed lease. * * @throws net.jini.core.lease.UnknownLeaseException this exception occurs * when the lease being renewed is unknown to the lease grantor. * * @see com.sun.jini.fiddler.FiddlerImpl#renewLeaseDo */ private long renewLeaseInt(RegistrationInfo regInfo, Uuid leaseID, long duration, long curTime) throws UnknownLeaseException { if(duration == Lease.ANY) { duration = leaseBound; } else if(duration <= 0) { throw new IllegalArgumentException("non-positive lease duration"); }//endif if ( (regInfo == null) || ( !(regInfo.leaseID).equals(leaseID) ) || (regInfo.leaseExpiration <= curTime) ) { throw new UnknownLeaseException(); }//endif if( (duration > leaseBound) && (duration > (regInfo.leaseExpiration - curTime)) ) { duration = Math.max( (regInfo.leaseExpiration - curTime), leaseBound ); }//endif long newExpiration = curTime + duration; /* Force a re-sort in the registrationByTime map */ registrationByTime.remove(regInfo); // force first re-sort regInfo.leaseExpiration = newExpiration; // modify outside of map registrationByTime.put(regInfo, regInfo); // insert & force new sort /* see if the expire thread needs to wake up earlier */ if (newExpiration < minExpiration) { minExpiration = newExpiration; concurrentObj.waiterNotify(leaseExpireThreadSyncObj); }//endif return newExpiration; }//end renewLeaseInt /** * This method performs the final steps in the process of renewing the * lease on the registration corresponding to the <code>regInfo</code> * and <code>leaseID</code> parameters, granting a requested absolute * expiration time for that lease. * * @param regInfo the data structure record corresponding to the * registration whose lease is to be renewed * @param leaseID identifier assigned by the lease grantor to the lease * that is to be renewed * @param expiration the requested absolute expiration time for the lease * being renewed */ private void renewLeaseAbs(RegistrationInfo regInfo, Uuid leaseID, long expiration) { if ( (regInfo == null) || (regInfo.leaseID != leaseID) ) return; /* The act of renewing a registration's lease simply involves * changing the expiration time stored in the registration's * data structure. But because the registrationByTime sorts its * elements by time, changing the value of the leaseExpiration field * of the regInfo parameter "in place" would typically result in * the registrationByTime map being out of order. Contrast this with * the modification of the registration's non-time-dependent fields * (for example, adding, replacing or removing groups or locators), * where modifying the input regInfo parameter will modify the * appropriate field of the corresponding element of both the * registrationByID and registrationByTime maps; and the order * will still be valid in the registrationByTime map because the * time-dependent data has not changed. * * Thus, in order to maintain a valid ordering in the * registrationByTime map, a re-sort is forced by first removing * from that map the element corresponding to regInfo, resetting * the leaseExpiration field of that object, and then re-inserting * the modified regInfo into the map, which forces another sort. */ registrationByTime.remove(regInfo); // force first re-sort regInfo.leaseExpiration = expiration; // modify outside of map registrationByTime.put(regInfo, regInfo); // insert & force new sort }//end renewLeaseAbs /** * Called by the <code>apply</code> method of the class * <code>LeaseRenewedLogObj</code> (which is invoked during state * recovery). This method performs the final steps in the process * of renewing the lease on the registration corresponding to the * <code>registrationID</code> and <code>leaseID</code> parameters, * granting a requested absolute expiration time for that lease. * * @param registrationID the ID of the data structure record * corresponding to the registration whose * lease is to be renewed * @param registrationByID the map containing all active registrations * managed by this service * @param leaseID identifier assigned by the lease grantor to * the lease that is to be renewed * @param expiration the requested absolute expiration time for the * lease being renewed * * @see com.sun.jini.fiddler.FiddlerImpl#renewLeaseDo */ private void renewLeaseAbs(Uuid registrationID, HashMap registrationByID, Uuid leaseID, long expiration) { renewLeaseAbs((RegistrationInfo)(registrationByID.get(registrationID)), leaseID,expiration); }//end renewLeaseAbs /** * Called by the public method renewLeases to renew all of * the leases from a <code>LeaseMap</code> that correspond to the * elements of the <code>registrationIDs</code> and <code>leaseIDs</code> * parameters, granting as new durations the corresponding elements * of the <code>duration</code> parameter. * <p> * Note that all of the input parameters must be the same length. * * @param registrationIDs array containing the unique identifiers assigned * to the each registration to which each lease * to be renewed corresponds * @param leaseIDs array containing the identifiers assigned by the * lease grantor to each lease being renewed * @param durations array containing the requested durations for * each lease being renewed * * @return an instance of FiddlerRenewResults containing data corresponding * to the results (granted durations or exceptions) of each * renewal attempt * * @see com.sun.jini.fiddler.FiddlerImpl#renewLeases */ private FiddlerRenewResults renewLeasesDo(Uuid[] registrationIDs, Uuid[] leaseIDs, long[] durations) { long curTime = System.currentTimeMillis(); Exception[] exceptions = null; for (int i = 0; i < registrationIDs.length; i++) { RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get(registrationIDs[i])); try { durations[i] = renewLeaseInt(regInfo, leaseIDs[i], durations[i], curTime); } catch (Exception e) { durations[i] = -1; if (exceptions == null) { exceptions = new Exception[]{e}; } else { exceptions = (Exception[])appendArray(exceptions, e); }//endif } }//end loop /* don't bother to weed out problem leases */ addLogRecord ( new LeasesRenewedLogObj(registrationIDs,leaseIDs,durations) ); for (int i = registrationIDs.length; --i >= 0; ) { if (durations[i] >= 0) { durations[i] -= curTime; }//endif }//end loop return new FiddlerRenewResults(durations, exceptions); }//end renewLeasesDo /** * Using the absolute expiration times contained in the * <code>expirations</code> parameter, renews all of the leases * from a <code>LeaseMap</code> that correspond to the elements * of the <code>registrationIDs</code> and <code>leaseIDs</code> * parameters; skipping any negative expiration times. * <p> * Note that all of the input parameters must be the same length. * * @param registrationIDs array containing the unique identifiers assigned * to the each registration to which each lease * to be renewed corresponds * @param leaseIDs array containing the identifiers assigned by the * lease grantor to each lease being renewed * @param expirations array containing the absolute expiration times * to which to renew each lease * * @see com.sun.jini.fiddler.FiddlerImpl#renewLeases * @see com.sun.jini.fiddler.FiddlerImpl#renewLeaseAbs */ private void renewLeasesAbs(Uuid[] registrationIDs, Uuid[] leaseIDs, long[] expirations) { for (int i = registrationIDs.length; --i >= 0; ) { long expiration = expirations[i]; if (expiration < 0) continue; RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get(registrationIDs[i])); renewLeaseAbs(regInfo, leaseIDs[i], expiration); }//end loop }//end renewLeasesAbs /* END Private Lease Renewal Methods ----------------------------------- */ /* BEGIN Private Lease Cancellation Methods ---------------------------- */ /** * Called by the public method <code>cancelLease</code>. This method * cancels the lease on the registration corresponding to the * <code>regInfo</code> and <code>leaseID</code> parameters. * * @param regInfo the data structure record corresponding to the * registration whose lease is to be cancelled * @param leaseID identifier assigned by the lease grantor to the lease * that is to be cancelled * * @throws net.jini.core.lease.UnknownLeaseException this exception occurs * when the lease being cancelled is unknown to the lease grantor. * * @throws java.io.IOException this exception occurs when the multicast * request protocol experiences a failure. * * @see com.sun.jini.fiddler.FiddlerImpl#cancelLease */ private void cancelLeaseDo(RegistrationInfo regInfo, Uuid leaseID) throws UnknownLeaseException, IOException { long curTime = System.currentTimeMillis(); if ( (regInfo == null) || (regInfo.leaseExpiration <= curTime) ) { throw new UnknownLeaseException(); }//endif removeRegistration(regInfo); /* wake up thread if this might be the (only) earliest time */ if (regInfo.leaseExpiration == minExpiration) { concurrentObj.waiterNotify(leaseExpireThreadSyncObj); }//endif }//end cancelLeaseDo /** * Called by the <code>apply</code> method of the class * <code>LeaseCancelledLogObj</code> (which is invoked during state * recovery). This method cancels the lease on the registration * corresponding to the <code>registrationID</code> and * <code>leaseID</code> parameters. * * @param regInfo the data structure record corresponding to the * registration whose lease is to be cancelled * @param registrationID the ID of the data structure record * corresponding to the registration whose * lease is to be cancelled * @param registrationByID the map containing all active registrations * managed by this service * @param leaseID identifier assigned by the lease grantor to * the lease that is to be cancelled * * @throws net.jini.core.lease.UnknownLeaseException this exception occurs * when the lease being cancelled is unknown to the lease grantor. * * @throws java.io.IOException this exception occurs when the multicast * request protocol experiences a failure. * * @see com.sun.jini.fiddler.FiddlerImpl#cancelLeaseDo * @see com.sun.jini.fiddler.FiddlerImpl#cancelLease */ private void cancelLeaseDo(Uuid registrationID, HashMap registrationByID, Uuid leaseID) throws UnknownLeaseException, IOException { cancelLeaseDo((RegistrationInfo)(registrationByID.get(registrationID)), leaseID); }//end cancelLeaseDo /** * Called by the public method cancelLeases to the cancel all of * the leases from a <code>LeaseMap</code> that correspond to the * elements of the <code>registrationIDs</code> and <code>leaseIDs</code> * parameters. * <p> * The two input arrays must be the same length. If there are no * exceptions, the return value is null. Otherwise, the return value * has the same length as the <code>registrationIDs</code> parameter, * and has nulls for the leases that were successfully renewed. * * @return an array containing the instances of Exception (or null for * successfully renewed leases), each corresponding to the * exception that occurred upon attempting renewal. * * @see com.sun.jini.fiddler.FiddlerImpl#cancelLeases */ private Exception[] cancelLeasesDo(Uuid[] registrationIDs, Uuid[] leaseIDs) { Exception[] exceptions = null; for (int i = registrationIDs.length; --i >= 0; ) { try { RegistrationInfo regInfo = (RegistrationInfo)(registrationByID.get (registrationIDs[i])); cancelLeaseDo(regInfo, leaseIDs[i]); } catch (Exception e) { if (exceptions == null) { exceptions = new Exception[registrationIDs.length]; }//endif exceptions[i] = e; } }//end loop return exceptions; }//end cancelLeasesDo /* END Private Lease Cancellation Methods ------------------------------ */ /* BEGIN Private Event-Related Methods --------------------------------- */ /** Returns the set of registrars common to both input parameters */ private ServiceRegistrar[] intersectRegSets(ServiceRegistrar[] regs0, ServiceRegistrar[] regs1) { HashSet regSet = new HashSet(); //no duplicates /* Compare each input element of regs0 with each element of regs1, * storing matches along the way */ nextRegistrar: for(int i=0;i<regs0.length;i++) { for(int j=0;j<regs1.length;j++) { if( (regs0[i]).equals(regs1[j]) ) { regSet.add(regs0[i]); continue nextRegistrar; // match found, next reg }//endif }//end loop(j) }//end loop(i) return ( (ServiceRegistrar[])(regSet).toArray (new ServiceRegistrar[regSet.size()]) ); }//end intersectRegSets /** Returns true if the input registrar is an element of the given array */ private boolean regIsElementOfRegSet(ServiceRegistrar reg, ServiceRegistrar[] regSet) { for(int i=0;i<regSet.length;i++) { if( (regSet[i]).equals(reg) ) return true; }//end loop return false; }//end regIsElementOfRegSet /** * This method determines which of the registrars in the * <code>regsMap</code> parameter belong to the set of registrars * the given <code>regInfo</code> parameter wishes to discover. It * then adds those registrars to the regInfo's map of discovered * registrars (including only those registrars that can be successfully * serialized). Finally, a discovered event containing the discovery * information that is of interest to the regInfo is built and sent * to the regInfo's listener. * * @param regInfo the data structure record corresponding to the * registration whose listener will receive the event * @param regsMap mapping in which the key values are previously discovered * registrars, and the map values are data structures of * type <code>LocatorGroupsStruct</code> that contain the * locator and member groups of the corresponding * registrar key */ private void maybeSendDiscoveredEvent(RegistrationInfo regInfo, Map regsMap) { /* Determine the registrars that are common to both the input set of * registrars (the currently or newly discovered registrars) and the * registration's set of desired registrars. These are the registrars * to send in the event. */ HashMap regsToAdd = new HashMap(regsMap.size()); Set eSet = regsMap.entrySet(); for(Iterator itr = eSet.iterator(); itr.hasNext(); ) { Map.Entry pair = (Map.Entry)itr.next(); ServiceRegistrar reg = (ServiceRegistrar)pair.getKey(); LocatorGroupsStruct curStructVal = (LocatorGroupsStruct)pair.getValue(); /* If regInfo wants ALL_GROUPS, then every registrar is desired */ if(regInfo.groups == null) { // ALL_GROUPS regsToAdd.put(reg,curStructVal); } else {//not ALL_GROUPS, reg's locator/groups desired by regInfo? if( interested(curStructVal.locator,curStructVal.groups, regInfo.locators,regInfo.groups) ) { regsToAdd.put(reg,curStructVal); }//endif }//endif }//end loop /* Add the registrars that are both desired and discovered to the * registration's map of discovered registrars. If any registrars * cannot be serialized, drop them. */ HashMap regsAdded = regInfo.addToDiscoveredRegs(regsToAdd); /* Build and send a "discovered event" */ RemoteDiscoveryEvent event = buildEvent(regInfo,regsAdded,false); if(event != null) { queueEvent(regInfo,event); logInfoEvents("NewReg/Discovered EventTask.run(): " +"DISCOVERED Event was SENT\n"); }//endif }//end maybeSendDiscoveredEvent /** * Examines the <code>discardFlag</code> for each active registration * until a value of <code>true</code> is encountered or the set of * registrations is exhausted. If that flag is <code>false</code> for * all registrations, this method will return <code>null</code>; * otherwise, it will return the registration found, but with the * the value of the flag set back to <code>false</code>. * * @return the first instance of <code>RegistrationInfo</code> whose * <code>discardFlag</code> is set to <code>true</code>; * returns <code>null</code> if no such registration exists. */ private RegistrationInfo externalDiscardRequest() { /* Loop thru regInfo's, until one is found with a true flag*/ for(Iterator itr=registrationByID.values().iterator();itr.hasNext();) { RegistrationInfo regInfo = (RegistrationInfo)itr.next(); if(regInfo.discardFlag == true) { logInfoDiscard("\nexternalDiscardRequest: " +"discardFlag == true\n"); regInfo.discardFlag = false; return regInfo; }//endif }//end loop return null; }//end externalDiscardRequest /** * Convenience method that creates and returns a mapping of a single * <code>ServiceRegistrar</code> instance to a set of groups. * * @param reg instance of <code>ServiceRegistrar</code> * corresponding to the registrar to use as the key * to the mapping * @param curGroups <code>String</code> array containing the current * member groups of the registrar referenced by the * <code>reg</code> parameter; and which is used * as the value of the mapping * * @return <code>Map</code> instance containing a single mapping from * a given registrar to its current member groups */ private HashMap mapRegToGroups(ServiceRegistrar reg, String[] groups) { HashMap groupsMap = new HashMap(1); groupsMap.put(reg,groups); return groupsMap; }//end mapRegToGroups /** Given a set of registrars that have just been discarded, this method * determines which of those registrars are contained in none of the * discovered sets of the active registrations; and then removes * those registrars from the global maps of registrars that are * maintained across all registrations. * * Note that this method must be called from within a synchronization * block. * * @param discardedRegs set of registrars that were just discarded */ private void maybeRemoveDiscardedRegsFromGlobalSet(Set discardedRegs) { for(Iterator itr = discardedRegs.iterator();itr.hasNext(); ) { maybeRemoveDiscardedRegFromGlobalSet(itr.next()); }//end loop(itr) }//end maybeRemoveDiscardedRegsFromGlobalSet /** Given a registrar that has just been discarded, this method * determines if that registrar is contained in none of the discovered * sets of the active registrations; and then removes that registrar * from the global maps of registrars that are maintained across all * registrations. * * Note that this method must be called from within a synchronization * block. * * @param discardedRegs set of registrars that were just discarded */ private void maybeRemoveDiscardedRegFromGlobalSet(Object dReg) { for(Iterator jtr=registrationByID.values().iterator();jtr.hasNext();){ RegistrationInfo regInfo = (RegistrationInfo)jtr.next(); if( (regInfo.discoveredRegsMap).containsKey(dReg) ) { return; // this dReg is in at least 1 regInfo, goto next reg }//endif }//end loop(jtr) /* discarded reg contained in no regInfo, remove it from map */ allDiscoveredRegs.remove(dReg); }//end maybeRemoveDiscardedRegFromGlobalSet /** For each registrar referenced in the global map allDiscoveredRegs, * this method replaces the associated set of member groups with the * corresponding set in the given registrars-to-groups map. This * method should be called only after a changed event is received * from the discovery manager. * * Note that this method must be called from within a synchronization * block. * * @param groupsMap map containing the registrars and their new groups */ private void updateGroupsInGlobalSet(Map groupsMap) { Set eSet = groupsMap.entrySet(); for(Iterator itr = eSet.iterator(); itr.hasNext(); ) { Map.Entry pair = (Map.Entry)itr.next(); ServiceRegistrar reg = (ServiceRegistrar)pair.getKey(); if(allDiscoveredRegs.containsKey(reg)) { LookupLocator loc = ((LocatorGroupsStruct)allDiscoveredRegs.get(reg)).locator; String[] newGroups = (String[])pair.getValue(); LocatorGroupsStruct locGroups = new LocatorGroupsStruct(loc,newGroups); allDiscoveredRegs.put(reg,locGroups); }//endif }//end loop }//end updateGroupsInGlobalSet /** * This method constructs the appropriate remote discovery event from the * information contained in the input parameters. This method encapsulates * common exception-handling functionality. If the remote discovery * event cannot be successfully constructed, null will be returned. * * @param regInfo the data structure record corresponding to the * registration whose listener will receive the event * @param groupsMap map containing the registrars, and their corresponding * member groups, to include in the event * @param discarded flag indicating whether the registrars included in * the event have been discarded or discovered * * @return instance of <code>RemoteDiscoveryEvent</code> containing the * appropriate information corresponding to the input registration * record */ private RemoteDiscoveryEvent buildEvent(RegistrationInfo regInfo, Map groupsMap, boolean discarded) { RemoteDiscoveryEvent newEvent = null; if(groupsMap.size() > 0) { try { newEvent = new RemoteDiscoveryEvent(outerProxy, regInfo.eventID, ++regInfo.seqNum, regInfo.handback, discarded, groupsMap); logInfoEvents(groupsMap,regInfo.eventID,regInfo.seqNum, regInfo.handback,discarded, eventsLogger, Level.FINE); } catch (IOException e) { /* The constructor for <code>RemoteDiscoveryEvent</code> * throws an <code>IOException</code> if the constructor * fails to successfully serialize even one registrar from * the input set (which means there is no need to send the * event since there are no marshalled registrars to send). * When such a situation occurs, register an ERROR status * attribute (along with a Comment attribute describing the * nature of the problem) to all lookup services with which * this service is registered. * * Administrative clients, as well as clients that use this * service should have registered for notification of the * existence of this attribute. */ String eStr = "Failed to serialize ALL registrars during " +"event construction ... could not send event"; problemLogger.log(Level.INFO, eStr, e); Entry[] errorAttrs = new Entry[] { new FiddlerStatus(StatusType.WARNING), new Comment(eStr) }; joinMgr.addAttributes(errorAttrs,true); }//end try }//endif return newEvent; }//end buildEvent /** * This method simply queues a new <code>SendEventTask</code> instance * that will send the given remote event to the given registration's * listener. * * @param regInfo the data structure record corresponding to the * registration whose listener will receive the event * * @param event the instance of <code>RemoteDiscoveryEvent</code> to * send to the registration's listener */ private void queueEvent(RegistrationInfo regInfo, RemoteDiscoveryEvent event) { taskMgr.add(new SendEventTask(regInfo,event)); }//end queueEvent /* END Private Event-Related Methods ----------------------------------- */ /* BEGIN Persistent State Logging Interfaces & Classes ----------------- */ /** * Writes the current state of this service to persistent storage. * <p> * A 'snapshot' of the service's current state is represented by * the data contained in certain fields defined in the service. * That data represents many changes -- over time -- to the service's * state. This method will record that data to a file referred to as * the snapshot file. * <p> * The data written by this method to the snapshot file -- as well as * the format of the file -- is shown below: * <ul> * <li> the service's class name * <li> log format version number * <li> the unique ID of the proxy to the current instance of this service * <li> the current event ID * <li> the current join state of this service * <li> the current configuration parameters of this service * <li> contents of the container holding the current set of registrations * <li> null (termination 'marker' for the set of registrations) * </ul> * Each data item is written to the snapshot file in serialized form. * * @see FiddlerImpl.LocalLogHandler */ private void takeSnapshot(OutputStream out) throws IOException { ObjectOutputStream stream = new ObjectOutputStream(out); /* Stable information about the current instance of this service */ stream.writeUTF(FiddlerImpl.class.getName()); stream.writeInt(LOG_VERSION); stream.writeObject(proxyID); /* The current event ID assigned to the discovery events being sent */ stream.writeLong(curEventID); /* The current join state of this service */ stream.writeObject(thisServicesGroups); stream.writeObject(thisServicesLocators); stream.writeObject( marshalAttributes(this,thisServicesAttrs) ); /* The current configuration parameters of this service */ stream.writeLong(leaseBound); stream.writeInt(snapshotThresh); stream.writeFloat(snapshotWt); /* The current set of registrations */ for(Iterator iter=registrationByID.values().iterator();iter.hasNext();) { stream.writeObject(iter.next()); }//end loop stream.writeObject(null); //termination marker stream.flush(); }//end takeSnapshot /** * Retrieve the contents of the snapshot file and reconstitute the 'base' * state of this service from the retrieved data. * <p> * Refer to <code>takeSnapshot</code> for a description of the data * retrieved by this method from the snapshot file. * <p> * During recovery, the state of the service at the time of a crash * or outage is re-constructed by first reconstituting the 'base state' * from the snapshot file; and then modifying that base state according * to the records retrieved from the log file. This method is invoked to * perform the first step in that reconstruction. As each registered * service or event is retrieved, it is inserted into its appropriate * container object. * <p> * Because events can be generated before the next snapshot is taken, * the event sequence numbers must be incremented. This is because the * event specification requires that a set of event sequence numbers * corresponding to an particular event type (a particular event ID) * be monotonically increasing. Since the number of events that might * have been sent is arbitrary, each sequence number will be incremented * by a 'large' number so as to guarantee adherence to the specification. * * @see FiddlerImpl.LocalLogHandler#takeSnapshot * @see FiddlerImpl.LocalLogHandler */ private void recoverSnapshot(InputStream in) throws IOException, ClassNotFoundException { ObjectInputStream stream = new ObjectInputStream(in); /* Retrieve the service's stable information */ if (!FiddlerImpl.class.getName().equals(stream.readUTF())) { throw new IOException("log from wrong implementation"); }//endif if (stream.readInt() != LOG_VERSION) { throw new IOException("wrong log format version"); }//endif proxyID = (Uuid)stream.readObject(); /* Retrieve the current event ID */ curEventID = stream.readLong(); /* Retrieve the current join state of this service. Groups first. */ thisServicesGroups = (String[])stream.readObject(); /* Retrieve and re-prepare the locators to join (drop bad locs) */ thisServicesLocators = prepareOldLocators ( recoveredLocatorToJoinPreparer, (LookupLocator[])stream.readObject() ); /* Retrieve the attributes to register with each lookup service. */ MarshalledObject[] marshalledAttrs = (MarshalledObject[])stream.readObject(); thisServicesAttrs = unmarshalAttributes(this, marshalledAttrs); /* Retrieve the current configuration parameters of this service */ leaseBound = stream.readLong(); snapshotThresh = stream.readInt(); snapshotWt = stream.readFloat(); RegistrationInfo regInfo; while ((regInfo = (RegistrationInfo)stream.readObject()) != null) { regInfo.seqNum += Integer.MAX_VALUE; addRegistration(regInfo); }//end loop initialStartup = false; }//end recoverSnapshot /** * Add a state-change record to persistent storage. * <p> * Whenever a significant change occurs to Fiddler's state, this * method is invoked to record that change in a file called a log file. * Each record written to the log file is an object reflecting both * the data used and the ACTIONS taken to make one change to that * state at a particular point in time. If the number of records * contained in the log file exceeds the pre-defined threshold, * a snapshot of Fiddler's current state will be recorded. * <p> * Whenever one of the following state changes occurs, this method * will be invoked with the appropriate implementation of the * LogRecord interface as the input argument. * <ul> * <li> new attributes were added to Fiddler's attributes with lookup * <li> Fiddler's existing attributes with lookup were modified * * <li> new groups were added to the set of groups Fiddler should join * <li> Fiddler's existing groups to join were replaced with a new set * <li> Fiddler's existing groups to join were removed * * <li> new locators were added to the set of locators Fiddler should join * <li> Fiddler's existing locators to join were replaced with a new set * <li> Fiddler's existing locators to join were removed * * <li> the bound on granted lease durations was set to a new value * <li> weight factor used to determine when to take a snapshot was changed * <li> threshold used to determine when to take a snapshot was changed * * <li> a registration request for use of Fiddler's resources was granted * * <li> groups were added to a registration's set of groups to discover * <li> the set of groups to discover for a registration was replaced * <li> the set of groups to discover for a registration was removed * * <li> locators were added to a registration's set of locators to discover * <li> the set of locators to discover for a registration was replaced * <li> the set of locators to discover for a registration was removed * * <li> a lease on a registration with Fiddler was renewed * <li> a set of registration leases with Fiddler were renewed * <li> a lease on a registration with Fiddler was cancelled * <li> a set of registration leases with Fiddler were cancelled * * </ul> * * @see RegistrarImpl.LocalLogHandler */ private void addLogRecord(LogRecord rec) { if(log == null) return;//not persistent, don't log logInfoAddLogRecord(rec); try { log.update(rec, true); if (++logFileSize >= snapshotThresh) { int snapshotSize = registrationByID.size(); if ((float)logFileSize >= snapshotWt*((float)snapshotSize)) { /* take snapshot */ concurrentObj.waiterNotify(snapshotThreadSyncObj); }//endif }//endif } catch (Exception e) { if (!Thread.currentThread().isInterrupted()) { /* If log updating fails, then register an ERROR status * attribute (along with a Comment attribute describing the * nature of the problem) to all lookup services with which * this service is registered. * * Administrative clients, as well as clients that use this * service should have registered for notification of the * existence of this attribute. */ String eStr = "Failure while updating the persistent log " +"containing the service state"; problemLogger.log(Level.INFO, eStr, e); Entry[] errorAttrs = new Entry[] { new FiddlerStatus(StatusType.ERROR), new Comment(eStr) }; joinMgr.addAttributes(errorAttrs,true); }//endif } }//end addLogRecord /** * Interface defining the method(s) that must be implemented by each of * the concrete LogObj classes. This allows for the definition of * object-dependent invocations of the appropriate implementation of * the method(s) declared in this interface. */ private static interface LogRecord extends Serializable { void apply(FiddlerImpl fiddlerImpl); } /* 1. Definitions related to state changes generated by JoinAdmin */ /** * LogObj class whose instances are recorded to the log file whenever * new attributes are added to this service's existing set of attributes. * * @see FiddlerImpl.LocalLogHandler */ private static class LookupAttrsAddedLogObj implements LogRecord { private static final long serialVersionUID = 4983778026976938585L; /** The attributes that were added to each lookup service with which * this service is registered, written out in marshalled form. * @serial */ private MarshalledObject[] marshalledAttrs; /** Constructs this class and stores the attributes that were added */ public LookupAttrsAddedLogObj(FiddlerImpl fiddlerImpl, Entry[] attrs) { this.marshalledAttrs = marshalAttributes(fiddlerImpl,attrs); } /** Modifies this service's state by adding (after unmarshalling) the * elements of marshalledAttrs to the service's existing set of * attributes. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist("Log recovery: apply adding lookup attributes"); Entry[] attrs = unmarshalAttributes(fiddlerImpl, marshalledAttrs); fiddlerImpl.thisServicesAttrs = LookupAttributes.add(fiddlerImpl.thisServicesAttrs,attrs); } }//end LookupAttrsAddedLogObj /** * LogObj class whose instances are recorded to the log file whenever * the attributes currently associated with this service in the lookup * services with which it is registered are modified. * * @see FiddlerImpl.LocalLogHandler */ private static class LookupAttrsModifiedLogObj implements LogRecord { private static final long serialVersionUID = 2671139518188470469L; /** The attribute set templates used to select the attributes (from * the service's existing set of attributes) that were modified, * written out in marshalled form. * @serial */ private MarshalledObject[] marshalledAttrTmpls; /** The attributes with which this service's existing attributes * were modified, written out in marshalled form. * @serial */ private MarshalledObject[] marshalledModAttrs; /** Constructs this class and stores the modified attributes */ public LookupAttrsModifiedLogObj(FiddlerImpl fiddlerImpl, Entry[] attrTmpls, Entry[] modAttrs) { this.marshalledAttrTmpls = marshalAttributes (fiddlerImpl,attrTmpls); this.marshalledModAttrs = marshalAttributes(fiddlerImpl,modAttrs); } /** Modifies this service's state by modifying (after unmarshalling) * the service's existing attributes according to the contents of * marshalledAttrTmpls and marshalledModAttrs. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist("Log recovery: apply modifying lookup attributes"); Entry[] attrTmpls = unmarshalAttributes (fiddlerImpl, marshalledAttrTmpls); Entry[] modAttrs = unmarshalAttributes (fiddlerImpl, marshalledModAttrs); fiddlerImpl.thisServicesAttrs = LookupAttributes.modify(fiddlerImpl.thisServicesAttrs, attrTmpls, modAttrs); } }//end LookupAttrsModifiedLogObj /** * LogObj class whose instances are recorded to the log file whenever * the set containing the names of the groups whose members are lookup * services the lookup discovery service wishes to register with (join) * is modified in some way; for example, through the invocation of: * <code>JoinAdmin.addLookupGroups</code>, * <code>JoinAdmin.removeLookupGroups</code> or * <code>JoinAdmin.setLookupGroups</code>. * * @see FiddlerImpl.LocalLogHandler */ private static class LookupGroupsChangedLogObj implements LogRecord { private static final long serialVersionUID = -6973676200404539919L; /** The new groups whose members are the lookup services this * service should join. * @serial */ private String[] groups; /** Constructs this class and stores the new groups */ public LookupGroupsChangedLogObj(String[] groups) { this.groups = groups; } /** Modifies this service's state by modifying this service's existing * set of 'groups to join'. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist ("Log recovery: apply changing lookup groups to join"); fiddlerImpl.thisServicesGroups = groups; } }//end LookupGroupsChangedLogObj /** * LogObj class whose instances are recorded to the log file whenever * the set containing the instances of <code>LookupLocator</code> that * correspond to specific lookup services the lookup discovery service * wishes to register with (join) is modified in some way; for example, * through the invocation of: * <code>JoinAdmin.addLookupLocators</code>, * <code>JoinAdmin.removeLookupLocators</code> or * <code>JoinAdmin.setLookupLocators</code>. * * @see FiddlerImpl.LocalLogHandler */ private static class LookupLocatorsChangedLogObj implements LogRecord { private static final long serialVersionUID = 6448427261140043291L; /** The locators that correspond to the new lookup services this * service should join. * @serial */ private LookupLocator[] locators; /** Constructs this class and stores the new locators */ public LookupLocatorsChangedLogObj(LookupLocator[] locators) { this.locators = locators; } /** Modifies this service's state by modifying this service's existing * set of 'locators to join'. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist ("Log recovery: apply changing lookup locators to join"); fiddlerImpl.thisServicesLocators = prepareOldLocators(recoveredLocatorToJoinPreparer,locators); } }//end LookupLocatorsChangedLogObj /* 2. Definitions related to state changes generated by FiddlerAdmin */ /** * LogObj class whose instances are recorded to the log file whenever a * new value is assigned to the least upper bound applied during the * determination of the duration of the lease on a requested registration. * * @see FiddlerImpl.LocalLogHandler */ private static class LeaseBoundSetLogObj implements LogRecord { private static final long serialVersionUID = 6084059678114714281L; /** The new least upper bound used to determine a lease's duration. * @serial */ private long newLeaseBound; /** Constructs this class and stores the new lease duration bound */ public LeaseBoundSetLogObj(long newLeaseBound) { this.newLeaseBound = newLeaseBound; } /** Modifies this service's state by setting the value of the private * leaseBound field to the value of the new bound. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist ("Log recovery: apply changing lease duration bound"); fiddlerImpl.leaseBound = newLeaseBound; } }//end LeaseBoundSetLogObj /** * LogObj class whose instances are recorded to the log file whenever a * new value is assigned to the Weight Factor applied to the Snapshot File * size used in the "time-to-take-a-snapshot determination" expression. * * @see FiddlerImpl.LocalLogHandler */ private static class SnapshotWeightSetLogObj implements LogRecord { private static final long serialVersionUID = -4079318959709953285L; /** The new snapshot weight factor. * @serial */ private float newWeight; /** Constructs this class and stores the new weight factor */ public SnapshotWeightSetLogObj(float newWeight) { this.newWeight = newWeight; } /** Modifies this service's state by setting the value of the private * snapshotWt field to the value of the new weight factor. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist ("Log recovery: apply changing snapshot weight factor"); fiddlerImpl.snapshotWt = newWeight; } }//end SnapshotWeightSetLogObj /** * LogObj class whose instances are recorded to the log file * whenever a new value is assigned to the Threshold used in the * "time-to-take-a-snapshot determination" expression. * * @see FiddlerImpl.LocalLogHandler */ private static class SnapshotThresholdSetLogObj implements LogRecord { private static final long serialVersionUID = 2952948867001823559L; /** The new snapshot threshold. * @serial */ private int newThreshold; /** Constructs this class and stores the new snapshot threshold */ public SnapshotThresholdSetLogObj(int newThreshold) { this.newThreshold = newThreshold; } /** Modifies this service's state by setting the value of the private * snapshotThresh field to the value of the new threshold. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist ("Log recovery: apply changing log-to-snapshot threshold"); fiddlerImpl.snapshotThresh = newThreshold; } }//end SnapshotThresholdSetLogObj /* 3. Definitions related to state changes made to the set of active * registrations, generated by client requests made through the * <code>LookupDiscoveryRegistration</code> interface. */ /** * LogObj class whose instances are recorded to the log file whenever * a registration is created and returned to a client. * * @see FiddlerImpl.LocalLogHandler */ private static class RegistrationGrantedLogObj implements LogRecord { private static final long serialVersionUID = 3983963008572188308L; /** Object which acts as a record of the current registration with the * lookup discovery service; containing all of the information about * that registration: IDs, managed sets, lease information, and * event registration information. * @serial */ private RegistrationInfo regInfo; /** Constructs this class and stores the registration information */ public RegistrationGrantedLogObj(RegistrationInfo regInfo) { this.regInfo = regInfo; } /** Modifies this service's state by registering the information * stored in the regInfo parameter; and by updating both the event * sequence number and the event ID for the registration. * * Note that the granting of a registration to a client typically * involves the modification of the managed sets in the discovery * manager, which usually involves starting the discovery protocol. * Since an IOException can occur when the discovery protocol fails * to start, and since such a situation is un-recoverable, this * method does the following: catches the exception, informs this * service's administrator by displaying the stack trace, and exits. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist("Log recovery: apply recording granted registration" +" info\n (ID = " +regInfo.registrationID+")"); regInfo.seqNum += Integer.MAX_VALUE; try { /* Note that the locators of this recovered registration * were already successfully prepared in the method * RegistrationInfo.readObject() when this object was * deserialized during recovery. */ fiddlerImpl.addRegistration(regInfo); } catch(IOException e) { if( problemLogger.isLoggable(Level.SEVERE) ) { problemLogger.log(Level.SEVERE, "During log recovery " +"(apply addRegistration) -- failure in " +"multicast request protocol\n", e); }//endif fiddlerImpl.destroyDo(); } fiddlerImpl.curEventID++; } }//end RegistrationGrantedLogObj /* 4. Definitions related to state changes made to the set of active * registrations, generated by client requests made through the * <code>LookupDiscoveryService</code> interface. */ /** * LogObj class whose instances are recorded to the log file whenever * the managed set of groups corresponding to a registration is * is augmented with new elements. * * @see FiddlerImpl.LocalLogHandler */ private static class GroupsAddedToRegistrationLogObj implements LogRecord { private static final long serialVersionUID = 2L; /** The ID of the data structure record corresponding to the * registration whose managed set of groups was augmented. * @serial */ private Uuid registrationID; /** The set of groups added to the registration's managed set of groups * @serial */ private String[] groups; /** Constructs this class and stores the ID and new set of groups */ public GroupsAddedToRegistrationLogObj(Uuid registrationID, String[] groups) { this.registrationID = registrationID; this.groups = groups; } /** Modifies this service's state by adding the set of group names to * registration's managed set of groups, as well as by updating the * set of all groups (across all registrations) to discover. * * Note that the augmentation of a registration's set of groups * typically involves the modification of the managed sets in the * discovery manager, which usually involves starting the discovery * protocol. Since an IOException can occur when the discovery * protocol fails to start, and since such a situation is * un-recoverable, this method does the following: catches the * exception, informs this service's administrator by displaying * the stack trace, and exits. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist("Log recovery: apply adding to groups to discover " +"for registration\n (ID = " +registrationID+")"); try { fiddlerImpl.addGroupsDo(registrationID, fiddlerImpl.registrationByID, groups); } catch(Exception e) { if( problemLogger.isLoggable(Level.SEVERE) ) { problemLogger.log(Level.SEVERE, "During log recovery " +"(apply addGroupsDO) -- failure in " +"multicast request protocol\n", e); }//endif fiddlerImpl.destroyDo(); } }//end apply }//end GroupsAddedToRegistrationLogObj /** * LogObj class whose instances are recorded to the log file whenever * a the managed set of groups corresponding to a registration is * is replaced (set) with a new set of group names. * * @see FiddlerImpl.LocalLogHandler */ private static class GroupsSetInRegistrationLogObj implements LogRecord { static final long serialVersionUID = 2L; /** The ID of the data structure record corresponding to the * registration whose managed set of groups was replaced. * @serial */ private Uuid registrationID; /** The set of groups that replaced the registration's current * managed set of groups. * @serial */ private String[] groups; /** Constructs this class and stores the ID and new set of groups */ public GroupsSetInRegistrationLogObj(Uuid registrationID, String[] groups) { this.registrationID = registrationID; this.groups = groups; } /** Modifies this service's state by replacing the registration's * current managed set of groups with the set of group names * stored in this class by the constructor, as well as by updating * the set of all groups (across all registrations) to discover. * * Note that the replacement of a registration's set of groups * typically involves the modification of the managed sets in the * discovery manager, which usually involves starting the discovery * protocol. Since an IOException can occur when the discovery * protocol fails to start, and since such a situation is * un-recoverable, this method does the following: catches the * exception, informs this service's administrator by displaying * the stack trace, and exits. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist("Log recovery: apply replacing groups to discover " +"for registration\n (ID = " +registrationID+") ..."); try { fiddlerImpl.setGroupsDo(registrationID, fiddlerImpl.registrationByID, groups); } catch(Exception e) { if( problemLogger.isLoggable(Level.SEVERE) ) { problemLogger.log(Level.SEVERE, "Failure during log " +"recovery (apply setGroups) -- \n", e); }//endif fiddlerImpl.destroyDo(); } } }//end GroupsSetInRegistrationLogObj /** * LogObj class whose instances are recorded to the log file whenever * one or more elements of the managed set of groups corresponding to a * registration are removed. * * @see FiddlerImpl.LocalLogHandler */ private static class GroupsRemovedFromRegistrationLogObj implements LogRecord { private static final long serialVersionUID = 2L; /** The ID of the data structure record corresponding to the * registration with managed set from which groups were removed. * @serial */ private Uuid registrationID; /** The set of groups removed from the registration's managed set * of groups. * @serial */ private String[] groups; /** Constructs this class and stores the ID and groups to remove */ public GroupsRemovedFromRegistrationLogObj(Uuid registrationID, String[] groups) { this.registrationID = registrationID; this.groups = groups; } /** Modifies this service's state by removing the set of group names * from registration's managed set of groups, as well as by updating * the set of all groups (across all registrations) to discover. * * Note that the removal of one or more group names from a * registration's set of groups typically involves the modification * of the managed sets in the discovery manager, which usually * involves starting the discovery protocol. Since an IOException * can occur when the discovery protocol fails to start, and since * such a situation is un-recoverable, this method does the * following: catches the exception, informs this service's * administrator by displaying the stack trace, and exits. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist("Log recovery: apply removing from groups to " +"discover for registration\n (ID = " +registrationID+")"); try { fiddlerImpl.removeGroupsDo(registrationID, fiddlerImpl.registrationByID, groups); } catch(Exception e) { if( problemLogger.isLoggable(Level.SEVERE) ) { problemLogger.log(Level.SEVERE, "Failure during log " +"recovery (apply removeGroups) -- \n", e); }//endif fiddlerImpl.destroyDo(); } } }//end GroupsRemovedFromRegistrationLogObj /** * LogObj class whose instances are recorded to the log file whenever * the managed set of locators corresponding to a registration is * is augmented with new elements. * * @see FiddlerImpl.LocalLogHandler */ private static class LocsAddedToRegistrationLogObj implements LogRecord { private static final long serialVersionUID = 2L; /** The ID of the data structure record corresponding to the * registration whose managed set of locators was augmented. * @serial */ private Uuid registrationID; /** The set of locators added to the registration's managed set * @serial */ private LookupLocator[] locators; /** Constructs this class and stores the ID and new set of locators */ public LocsAddedToRegistrationLogObj(Uuid registrationID, LookupLocator[] locators) { this.registrationID = registrationID; this.locators = locators; } /** Modifies this service's state by adding the set of locators to * registration's managed set of locators, as well as by updating the * set of all locators (across all registrations) to discover. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist("Log recovery: apply adding to locators to discover" +" for registration\n (ID = " +registrationID+")"); int nUnprepared = locators.length; /* Prepare the recovered locators */ locators = prepareOldLocators(recoveredLocatorToDiscoverPreparer, locators); /* If all the locs were successfully prepared, add them to the * associated registration; otherwise, remove the registration * from the managed set. (For more information, see the comment * in RegistrationInfo.readObject()). */ if(nUnprepared == locators.length) { fiddlerImpl.addLocatorsDo( registrationID, fiddlerImpl.registrationByID, locators ); } else { if( problemLogger.isLoggable(Level.WARNING) ) { problemLogger.log(Level.WARNING, "failure preparing " +"locator while recovering " +"LocsAddedToRegistrationLogObj " +"... removing registration with ID = " +registrationID); }//endif try { fiddlerImpl.removeRegistration ( (RegistrationInfo)(fiddlerImpl.registrationByID.get (registrationID)) ); } catch(IOException e) { String eStr = "failure removing registration (ID = " +registrationID +") after locator preparation failure"; if( problemLogger.isLoggable(Level.INFO) ) { problemLogger.log(Level.INFO, eStr, e); }//endif Entry[] errorAttrs = new Entry[] { new FiddlerStatus(StatusType.ERROR), new Comment(eStr) }; fiddlerImpl.joinMgr.addAttributes(errorAttrs,true); } }//endif }//end apply }//end LocsAddedToRegistrationLogObj /** * LogObj class whose instances are recorded to the log file whenever * the managed set of locators corresponding to a registration is * is replaced (set) with a new set of locators. * * @see FiddlerImpl.LocalLogHandler */ private static class LocsSetInRegistrationLogObj implements LogRecord { private static final long serialVersionUID = 2L; /** The ID of the data structure record corresponding to the * registration whose managed set of locators was replaced. * @serial */ private Uuid registrationID; /** The set of locators that replaced the registration's current * managed set of locators. * @serial */ private LookupLocator[] locators; /** Constructs this class and stores the ID and new set of locators */ public LocsSetInRegistrationLogObj(Uuid registrationID, LookupLocator[] locators) { this.registrationID = registrationID; this.locators = locators; } /** Modifies this service's state by replacing the registration's * current managed set of locators with the set of locators * stored in this class by the constructor, as well as by updating * the set of all locators (across all registrations) to discover. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist("Log recovery: apply replacing locators to discover" +" for registration\n (ID = " +registrationID+")"); int nUnprepared = locators.length; /* Prepare the recovered locators */ locators = prepareOldLocators(recoveredLocatorToDiscoverPreparer, locators); /* If all the locs were successfully prepared, set them in the * associated registration; otherwise, remove the registration * from the managed set. (For more information, see the comment * in RegistrationInfo.readObject()). */ if(nUnprepared == locators.length) { fiddlerImpl.setLocatorsDo( registrationID, fiddlerImpl.registrationByID, locators ); } else { if( problemLogger.isLoggable(Level.WARNING) ) { problemLogger.log(Level.WARNING, "failure preparing locator while " +"recovering LocsSetInRegistrationLogObj" +" ... removing registration with ID = " +registrationID); }//endif try { fiddlerImpl.removeRegistration ( (RegistrationInfo)(fiddlerImpl.registrationByID.get (registrationID)) ); } catch(IOException e) { String eStr = "failure removing registration (ID = " +registrationID +") after locator preparation failure"; if( problemLogger.isLoggable(Level.WARNING) ) { problemLogger.log(Level.WARNING, eStr, e); }//endif Entry[] errorAttrs = new Entry[] { new FiddlerStatus(StatusType.ERROR), new Comment(eStr) }; fiddlerImpl.joinMgr.addAttributes(errorAttrs,true); } }//endif } }//end LocsSetInRegistrationLogObj /** * LogObj class whose instances are recorded to the log file whenever * one or more elements of the managed set of locators corresponding to a * registration are removed. * * @see FiddlerImpl.LocalLogHandler */ private static class LocsRemovedFromRegistrationLogObj implements LogRecord { private static final long serialVersionUID = 2L; /** The ID of the data structure record corresponding to the * registration with managed set from which locators were removed. * @serial */ private Uuid registrationID; /** The set of locators removed from the registration's managed set * of locators. * @serial */ private LookupLocator[] locators; /** Constructs this class and stores the ID and locators to remove */ public LocsRemovedFromRegistrationLogObj(Uuid registrationID, LookupLocator[] locators) { this.registrationID = registrationID; this.locators = locators; } /** Modifies this service's state by removing the set of locators * from registration's managed set of locators, as well as by updating * the set of all locators (across all registrations) to discover. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist("Log recovery: apply removing from locators to " +"discover for registration\n (ID = " +registrationID+") ..."); int nUnprepared = locators.length; /* Prepare the recovered locators */ locators = prepareOldLocators(recoveredLocatorToDiscoverPreparer, locators); /* If all the locs were successfully prepared, remove them from * the associated registration; otherwise, remove the registration * from the managed set. (For more information, see the comment * in RegistrationInfo.readObject()). */ if(nUnprepared == locators.length) { fiddlerImpl.removeLocatorsDo( registrationID, fiddlerImpl.registrationByID, locators ); } else { if( problemLogger.isLoggable(Level.WARNING) ) { problemLogger.log(Level.WARNING, "failure preparing " +"locator while recovering" +"LocsRemovedFromRegistrationLogObj " +"... removing registration with ID = " +registrationID); }//endif try { fiddlerImpl.removeRegistration ( (RegistrationInfo)(fiddlerImpl.registrationByID.get (registrationID)) ); } catch(IOException e) { String eStr = "failure removing registration (ID = " +registrationID +") after locator preparation failure"; if( problemLogger.isLoggable(Level.WARNING) ) { problemLogger.log(Level.WARNING, eStr, e); }//endif Entry[] errorAttrs = new Entry[] { new FiddlerStatus(StatusType.ERROR), new Comment(eStr) }; fiddlerImpl.joinMgr.addAttributes(errorAttrs,true); } }//endif } }//end LocsRemovedFromRegistrationLogObj /** * LogObj class whose instances are recorded to the log file whenever * a lease on an existing registration (granted by the current backend * server of the lookup discovery service -- the lease grantor) is renewed. * * @see FiddlerImpl.LocalLogHandler */ private static class LeaseRenewedLogObj implements LogRecord { private static final long serialVersionUID = 2L; /** The ID of the data structure record corresponding to the * registration whose lease was renewed. * @serial */ private Uuid registrationID; /** The identifier assigned by the lease grantor to the lease that was * renewed. * @serial */ private Uuid leaseID; /** The new absolute time of expiration of the lease that was renewed. * @serial */ private long expiration; /** Constructs this class and stores the IDs and the expiration time */ public LeaseRenewedLogObj(Uuid registrationID, Uuid leaseID, long expiration) { this.registrationID = registrationID; this.leaseID = leaseID; this.expiration = expiration; } /** Modifies this service's state by renewing the lease with ID equal * to this class' leaseID field, and which corresponds to the * regInfo record. The lease will be renewed to have a new expiration * time equal to the value of the expiration field. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist("Log recovery: apply renewing lease for " +"registration\n (ID = " +registrationID+")"); fiddlerImpl.renewLeaseAbs(registrationID, fiddlerImpl.registrationByID, leaseID, expiration); } }//end LeaseRenewedLogObj /** * LogObj class whose instances are recorded to the log file whenever * a set of leases from a <code>LeaseMap</code> are renewed. * * @see FiddlerImpl.LocalLogHandler */ private static class LeasesRenewedLogObj implements LogRecord { private static final long serialVersionUID = 2L; /** The set of unique identifiers each assigned to a registration that * corresponds to one of the leases that was renewed. * @serial */ private Uuid[] registrationIDs; /** The set of identifiers each assigned by the lease grantor to one * of the leases that was renewed. * @serial */ private Uuid[] leaseIDs; /** The set of new absolute expiration times of each lease that was * renewed. * @serial */ private long[] expirations; /** Constructs this class and stores the sets of IDs and the set of * expiration times. */ public LeasesRenewedLogObj(Uuid[] registrationIDs, Uuid[] leaseIDs, long[] expirations) { this.registrationIDs = registrationIDs; this.leaseIDs = leaseIDs; this.expirations = expirations; } /** Modifies this service's state by renewing, with the corresponding * expiration time, each of the leases specified by the stored IDs. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist("Log recovery: apply renewing leases corresponding " +"to "+registrationIDs.length +" registration IDs"); fiddlerImpl.renewLeasesAbs(registrationIDs, leaseIDs, expirations); } }//end LeasesRenewedLogObj /** * LogObj class whose instances are recorded to the log file whenever * a lease on an existing registration (granted by the current backend * server of the lookup discovery service -- the lease grantor) is * cancelled. * * @see FiddlerImpl.LocalLogHandler */ private static class LeaseCancelledLogObj implements LogRecord { private static final long serialVersionUID = 2L; /** The ID of the data structure record corresponding to the * registration whose lease was cancelled. * @serial */ private Uuid registrationID; /** The identifier assigned by the lease grantor to the lease that was * cancelled. * @serial */ private Uuid leaseID; /** Constructs this class and stores the IDs corresponding to the * lease that was cancelled. */ public LeaseCancelledLogObj(Uuid registrationID, Uuid leaseID) { this.registrationID = registrationID; this.leaseID = leaseID; } /** Modifies this service's state by canceling the lease on the * registration with ID equal to that stored by the constructor, * as well as by updating the managed set of groups and locators * (across all registrations) in the appropriate way. * * Note that the cancellation of a lease typically involves the * modification of the managed sets in the discovery manager, which * usually involves starting the discovery protocol. Since an * IOException can occur when the discovery protocol fails to start, * and since such a situation is un-recoverable, this method does * the following: catches the exception, informs this service's * administrator by displaying the stack trace, and exits. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { try { logInfoPersist("Log recovery: apply cancelling lease for " +"registration\n (ID = " +registrationID+")"); fiddlerImpl.cancelLeaseDo(registrationID, fiddlerImpl.registrationByID, leaseID); } catch(IOException e) { if( problemLogger.isLoggable(Level.SEVERE) ) { problemLogger.log(Level.SEVERE, "During log recovery " +"(apply cancelLease) -- failure in " +"multicast request protocol\n", e); }//endif fiddlerImpl.destroyDo(); } catch (UnknownLeaseException e) { /* this exception should never occur when recovering */ } } }//end LeaseCancelledLogObj /** * LogObj class whose instances are recorded to the log file whenever * a set of leases from a <code>LeaseMap</code> are cancelled. * * @see FiddlerImpl.LocalLogHandler */ private static class LeasesCancelledLogObj implements LogRecord { private static final long serialVersionUID = 2L; /** The set of unique identifiers each assigned to a registration that * corresponds to one of the leases that was cancelled. * @serial */ private Uuid[] registrationIDs; /** The set of identifiers each assigned by the lease grantor to one * of the leases that was cancelled. * @serial */ private Uuid[] leaseIDs; /** Constructs this class and stores the IDs corresponding to the * leases that were cancelled. */ public LeasesCancelledLogObj(Uuid[] registrationIDs, Uuid[] leaseIDs) { this.registrationIDs = registrationIDs; this.leaseIDs = leaseIDs; } /** Modifies this service's state by canceling each of the leases * specified by the stored IDs. * @see FiddlerImpl.LocalLogHandler#applyUpdate */ public void apply(FiddlerImpl fiddlerImpl) { logInfoPersist("Log recovery: apply cancelling leases " +"corresponding to "+registrationIDs.length +" registration IDs"); /* Because unknown leases were not weeded out, exceptions can * occur and be propagated upward, but they can be ignored. */ fiddlerImpl.cancelLeasesDo(registrationIDs, leaseIDs); } }//end LeasesCancelledLogObj /* END Persistent State Logging Interfaces & Classes ------------------- */ /* BEGIN Private Logging Facility Methods ------------------------------ */ /* Returns a String containing the elements of the array */ private static String writeArrayElementsToString(Object[] arr) { if(arr == null) return new String("[]"); if(arr.length <= 0) { return new String("[]"); }//endif StringBuffer strBuf = new StringBuffer("["+arr[0]); for(int i=1;i<arr.length;i++){ strBuf.append(", ").append(arr[i]); }//end loop strBuf.append("]"); return strBuf.toString(); }//end writeArrayElementsToString /* Logs the elements of the array to a single line of output */ private static void writeArrayElements(Object[] arr, Logger logger, Level level) { if((arr == null) || (logger == null) || !(logger.isLoggable(level))) { return; } String writeStr = writeArrayElementsToString(arr); logger.log(level,writeStr); }//end writeArrayElements /* Returns a String containing the group names in writable form */ private static String writeGroupArrayToString(String[] groups) { if(groups == null) { return new String("[ALL_GROUPS]"); }//endif if(groups.length <= 0) { return new String("[]"); }//endif StringBuffer strBuf = null; if(groups[0].compareTo("") == 0) { strBuf = new StringBuffer("[The PUBLIC Group"); } else { strBuf = new StringBuffer("["+groups[0]); }//endif for(int i=1;i<groups.length;i++) { if(groups[i].compareTo("") == 0) { strBuf.append(", The PUBLIC Group"); } else { strBuf.append(", ").append(groups[i]); }//endif }//end loop strBuf.append("]"); return strBuf.toString(); }//end writeGroupArrayToString /* Logs the group names to a single line of output */ private static void writeGroupArray(String[] groups, Logger logger, Level level) { if( (logger == null) || !(logger.isLoggable(level)) ) return; String writeStr = writeGroupArrayToString(groups); logger.log(level,writeStr); }//end writeGroupArray /* Logs each registrar's locator on a single line of output */ private static void writeRegistrarsArray(ServiceRegistrar[] regs, Logger logger, Level level) { if((regs == null) || (logger == null) || !(logger.isLoggable(level))) { return; } if(regs.length == 0) { logger.log(level, "[NO REGISTRARS for Event]"); }//endif if(regs.length == 1) { try{ LookupLocator loc = regs[0].getLocator(); logger.log(level, "["+loc+"]"); }catch(SecurityException e){ logger.log(level, "[SecurityException]"); }catch(Exception e){ logger.log(level, "[Exception]"); } }//endif(regs.length == 1) if(regs.length > 1) { try{ LookupLocator loc = regs[0].getLocator(); logger.log(level, "["+loc+","); }catch(SecurityException e){ logger.log(level, "[SecurityException,"); }catch(Exception e){ logger.log(level, "[Exception,"); } for(int i=1;i<(regs.length-1);i++){ try{ LookupLocator loc = regs[i].getLocator(); logger.log(level, loc+","); }catch(SecurityException e){ logger.log(level, "SecurityException,"); }catch(Exception e){ logger.log(level, "Exception,"); } }//end loop try{ LookupLocator loc = regs[regs.length-1].getLocator(); logger.log(level, loc+"]"); }catch(SecurityException e){ logger.log(level, "SecurityException]"); }catch(Exception e){ logger.log(level, "Exception]"); } }//endif(regs.length > 1) }//end writeRegistrarsArray /* Logs data on a single attribute to a file or standard output */ private static void writeAttribute(Entry attr, Logger logger, Level level) { if((attr == null) || (logger == null) || !(logger.isLoggable(level))) { return; } if(attr instanceof BasicServiceType) { logger.log(level, " attribute = BasicServiceType"); logger.log(level, " Display Name = " +((BasicServiceType)(attr)).getDisplayName()); logger.log(level, " Description = " +((BasicServiceType)(attr)).getShortDescription()); } else if(attr instanceof ServiceInfo) { logger.log(level, " attribute = ServiceInfo"); logger.log(level, " Service Name = " +((ServiceInfo)(attr)).name); logger.log(level, " Service Manufacturer = " +((ServiceInfo)(attr)).manufacturer); logger.log(level, " Service Vendor = " +((ServiceInfo)(attr)).vendor); logger.log(level, " Service Version = " +((ServiceInfo)(attr)).version); logger.log(level, " Service Model = " +((ServiceInfo)(attr)).model); logger.log(level, " Service Serial # = " +((ServiceInfo)(attr)).serialNumber); } else { logger.log(level, " attribute = "+attr); }//endif }//end writeAttribute /* Logs data on each attribute in a set to a file or standard output */ private static void writeAttributes(Entry[] attrs, Logger logger, Level level) { if((attrs == null) || (logger == null) || !(logger.isLoggable(level))){ return; } for(int i=0;i<attrs.length;i++) { if(attrs[i] == null) continue; writeAttribute(attrs[i],logger,level); }//end loop logger.log(level,""); }//end writeAttributes /* Logs startup data to file or standard output */ private void logInfoStartup() { if (startupLogger.isLoggable(Level.INFO)) { startupLogger.log (Level.INFO, "Fiddler started: {0}, {1}, {2}", new Object[] { (FiddlerImpl.this.serviceID).toString(), writeGroupArrayToString(thisServicesGroups), writeArrayElementsToString(thisServicesLocators) } ); }//endif if( startupLogger.isLoggable(Level.CONFIG) ) { if(persistDir != null) { startupLogger.log(Level.CONFIG, " Persistent state directory: {0}", persistDir); }//endif startupLogger.log(Level.CONFIG, "Attributes to register in each lookup service: "); writeAttributes(thisServicesAttrs,startupLogger,Level.CONFIG); }//endif }//end logInfoStartup /* Logs shutdown/destroy data to file or standard output */ private void logInfoShutdown() { if (startupLogger.isLoggable(Level.INFO)) { startupLogger.log (Level.INFO, "Fiddler destroyed: {0}, {1}, {2}", new Object[] { (FiddlerImpl.this.serviceID).toString(), writeGroupArrayToString(thisServicesGroups), writeArrayElementsToString(thisServicesLocators) } ); }//endif }//end logInfoShutdown /* Logs information related to the run method of a particular task */ private void logInfoTasks(String str) { if( tasksLogger.isLoggable(Level.FINEST) ) { tasksLogger.log(Level.FINEST, str); }//endif }//end logInfoTasks /* Logs information about events that are sent */ private void logInfoEvents(String str) { if( eventsLogger.isLoggable(Level.FINE) ) { eventsLogger.log(Level.FINE, str); }//endif }//end logInfoEvents /* Logs information about events that are sent */ private void logInfoEvents(Map groupsMap, long eventID, long seqNum, MarshalledObject handback, boolean discarded, Logger logger, Level level) { if( (logger == null) || !(logger.isLoggable(level)) ) { return; } String discardedStr = (discarded == true ? "DISCARDED":"DISCOVERED"); Object hb = null; if(handback != null) { try { hb = handback.get(); } catch (ClassNotFoundException e) { problemLogger.log(Levels.HANDLED, "ClassNotFoundException when " +"unmarshalling handback",e); } catch (IOException e) { problemLogger.log(Levels.HANDLED, "IOException when unmarshalling " +"handback",e); } }//endif logger.log(level, "\n"+discardedStr+" Event:"); logger.log(level, " EventID = "+eventID); logger.log(level, " SeqNum = "+seqNum); logger.log(level, " handback = "+hb); ServiceRegistrar[] regs = (ServiceRegistrar[])(groupsMap.keySet()).toArray (new ServiceRegistrar[groupsMap.size()]); logger.log(level, " Registrars = "); writeRegistrarsArray(regs,logger,level); for(int i=0;i<regs.length;i++){ String[] curGroups = (String[])groupsMap.get(regs[i]); logger.log(level, " member groups ["+i+"] = "); writeGroupArray(curGroups,logger,level); }//end loop logger.log(level, ""); }//end logInfoEvents /* Logs group state information over all active registrations */ private void logInfoGroups() { String[] allGroups = discoveryMgr.getGroups(); if( groupsLogger.isLoggable(Level.FINER) ) { groupsLogger.log(Level.FINER, "Group(s) over all registrations: "); writeGroupArray(allGroups,groupsLogger,Level.FINER); }//endif }//end logInfoGroups /* Logs group state information over all active registrations */ private void logInfoGroups(String headerStr) { if( (headerStr != null) && (groupsLogger.isLoggable(Level.FINER)) ) { groupsLogger.log(Level.FINER, headerStr); }//endif logInfoGroups(); }//end logInfoGroups /* Logs locator state information over all active registrations */ private void logInfoLocators() { LookupLocator[] allLocators = discoveryMgr.getLocators(); if( locatorsLogger.isLoggable(Level.FINER) ) { locatorsLogger.log(Level.FINER, "Locator(s) over all registrations: "); writeArrayElements(allLocators,locatorsLogger,Level.FINER); }//endif }//end logInfoLocators /* Logs information useful to debugging the discard process */ private void logInfoDiscard(String str, Uuid regID) { if( (str != null) && (regID != null) && (discardLogger.isLoggable(Level.FINE)) ) { discardLogger.log(Level.FINE, str+" registrationID = "+regID); }//endif }//end logInfoDiscard /* Logs information useful to debugging the discard process */ private void logInfoDiscard(String str) { if( discardLogger.isLoggable(Level.FINE) ) { discardLogger.log(Level.FINE, str); }//endif }//end logInfoDiscard /* Logs information useful to debugging the leasing mechanism */ private void logInfoLease(String str, Uuid regID, Uuid leaseID) { if( (str != null) && (regID != null) && (leaseLogger.isLoggable(Level.FINER)) ) { leaseLogger.log(Level.FINER, str+" (registrationID,leaseID) = (" +regID+", "+leaseID+")"); }//endif }//end logInfoLease /* Logs information useful to debugging the registration mechanism */ private void logInfoRegistration(String str, Object regInfo) { if( (str != null) && (regInfo != null) && (registrationLogger.isLoggable(Level.FINER)) ) { registrationLogger.log(Level.FINER, str+" {0}", regInfo); }//endif }//end logInfoRegistration /* Logs information useful to debugging the logging mechanism */ private static void logInfoPersist(String str) { if( persistLogger.isLoggable(Level.FINEST) ) { persistLogger.log(Level.FINEST, str); }//endif }//end logInfoPersist /* Logs information useful to debugging the addLogRecord method */ private void logInfoAddLogRecord(LogRecord rec) { if( !(persistLogger.isLoggable(Level.FINEST)) ) return; String logStr = "Logging a state change: Unknown log record instance"; /* JoinAdmin */ if(rec instanceof LookupAttrsAddedLogObj) { logStr = "Logging state change: lookup attributes added"; } else if (rec instanceof LookupAttrsModifiedLogObj) { logStr = "Logging state change: lookup attributes modified"; } else if (rec instanceof LookupGroupsChangedLogObj) { logStr = "Logging state change: groups to join changed to " +writeGroupArrayToString(thisServicesGroups); } else if (rec instanceof LookupLocatorsChangedLogObj) { logStr = "Logging state change: locators to join changed to " +writeArrayElementsToString(thisServicesLocators); /* FiddlerAdmin */ } else if (rec instanceof LeaseBoundSetLogObj) { logStr = "Logging state change: lease duration bound changed"; } else if (rec instanceof SnapshotWeightSetLogObj) { logStr = "Logging state change: snapshot weight factor changed"; } else if (rec instanceof SnapshotThresholdSetLogObj) { logStr = "Logging state change: log-to-snapshot threshold changed"; /* LookupDiscoveryService */ } else if (rec instanceof RegistrationGrantedLogObj) { logStr = "Logging state change: new registration granted"; /* LookupDiscoveryRegistration */ } else if (rec instanceof GroupsAddedToRegistrationLogObj) { logStr = "Logging state change: added new groups to " +"registration's set of groups to discover"; } else if (rec instanceof GroupsSetInRegistrationLogObj) { logStr = "Logging state change: replaced registration's set of " +"groups to discover"; } else if (rec instanceof GroupsRemovedFromRegistrationLogObj) { logStr = "Logging state change: removed groups from " +"registration's set of groups to discover"; } else if (rec instanceof LocsAddedToRegistrationLogObj) { logStr = "Logging state change: added new locators to " +"registration's set of locators to discover"; } else if (rec instanceof LocsSetInRegistrationLogObj) { logStr = "Logging state change: replaced registration's set of " +"locators to discover"; } else if (rec instanceof LocsRemovedFromRegistrationLogObj) { logStr = "Logging state change: removed locators from " +"registration's set of locators to discover"; } else if (rec instanceof LeaseRenewedLogObj) { logStr = "Logging state change: registration's lease renewed"; } else if (rec instanceof LeasesRenewedLogObj) { logStr = "Logging state change: set of leases renewed for a " +"set of registrations"; } else if (rec instanceof LeaseCancelledLogObj) { logStr = "Logging state change: registration's lease cancelled"; } else if (rec instanceof LeasesCancelledLogObj) { logStr = "Logging state change: set of leases cancelled for a " +"set of registrations"; }//endif logInfoPersist(logStr); }//end logInfoAddLogRecord /* END Private Logging Facility Methods -------------------------------- */ /* *************** END Private NON-Static Utility Methods ************** */ }/*end class FiddlerImpl */