/*
* 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 net.jini.discovery;
import com.sun.jini.config.Config;
import com.sun.jini.discovery.Discovery;
import com.sun.jini.discovery.DiscoveryConstraints;
import com.sun.jini.discovery.UnicastResponse;
import com.sun.jini.discovery.internal.MultiIPDiscovery;
import com.sun.jini.logging.Levels;
import com.sun.jini.logging.LogUtil;
import com.sun.jini.thread.RetryTask;
import com.sun.jini.thread.TaskManager;
import com.sun.jini.thread.WakeupManager;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.jini.config.Configuration;
import net.jini.config.ConfigurationException;
import net.jini.config.EmptyConfiguration;
import net.jini.config.NoSuchEntryException;
import net.jini.core.constraint.InvocationConstraints;
import net.jini.core.constraint.MethodConstraints;
import net.jini.core.constraint.RemoteMethodControl;
import net.jini.core.discovery.LookupLocator;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.security.BasicProxyPreparer;
import net.jini.security.ProxyPreparer;
/**
* This class encapsulates the functionality required of an entity that
* wishes to employ the unicast discovery protocol to discover a lookup
* service. This utility provides an implementation that makes the process
* of finding specific lookup services much simpler for both services and
* clients.
* <p>
* Because this class participates in only the unicast discovery protocol,
* and because the unicast discovery protocol imposes no restriction on the
* physical location of the entity relative to a lookup service, this utility
* can be used to discover lookup services running on hosts that are located
* far from, or near to, the host on which the entity is running. This lack
* of a restriction on location brings with it a requirement that the
* discovering entity supply this class with specific information about the
* desired lookup services; namely, the location of the device(s) hosting
* each lookup service. This information is supplied through an instance
* of the {@link net.jini.core.discovery.LookupLocator LookupLocator} class.
*
* @com.sun.jini.impl <!-- Implementation Specifics -->
*
* The following implementation-specific items are discussed below:
* <ul><li> <a href="#lldConfigEntries">Configuring LookupLocatorDiscovery</a>
* <li> <a href="#lldLogging">Logging</a>
* </ul>
*
* <a name="lldConfigEntries">
* <p>
* <b><font size="+1">Configuring LookupLocatorDiscovery</font></b>
* <p>
* </a>
*
* This implementation of <code>LookupLocatorDiscovery</code> supports the
* following configuration entries; where each configuration entry name
* is associated with the component name
* <code>net.jini.discovery.LookupLocatorDiscovery</code>. Note that the
* configuration entries specified here are specific to this implementation
* of <codeLookupLocatorDiscovery</code>. Unless otherwise stated, each
* entry is retrieved from the configuration only once per instance of
* this utility, where each such retrieval is performed in the constructor.
*
* <a name="initialUnicastDelayRange">
* <table summary="Describes the initialUnicastDelayRange
* configuration entry" border="0" cellpadding="2">
* <tr valign="top">
* <th scope="col" summary="layout"> <font size="+1">•</font>
* <th scope="col" align="left" colspan="2"> <font size="+1">
* <code>initialUnicastDelayRange</code></font>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Type: <td> <code>long</code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Default: <td> <code>0 milliseconds</code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Description:
* <td> With respect to when this utility is started, this entry controls
* how long to wait before attempting unicast discovery.
* If the value is positive, initial unicast discovery requests
* will be delayed by a random value between <code>0</code> and
* <code>initialUnicastDelayRange</code> milliseconds. Once the wait
* period is up, the <code>LookupLocator</code>s specified at construction
* time are used for initiating unicast discovery requests, unless the
* managed <code>LookupLocator</code>s have been changed in the interim;
* in which case, no delayed unicast discovery requests are performed.
* Note that this entry only has effect when this utility is initialized.
* It does not delay discovery requests that are initiated if the managed
* <code>LookupLocator</code>s are subsequently changed.
* </table>
* <a name="registrarPreparer">
* <table summary="Describes the registrarPreparer configuration entry"
* border="0" cellpadding="2">
* <tr valign="top">
* <th scope="col" summary="layout"> <font size="+1">•</font>
* <th scope="col" align="left" colspan="2"> <font size="+1">
* <code>registrarPreparer</code></font>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Type: <td> {@link net.jini.security.ProxyPreparer}
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Default: <td> <code>new {@link net.jini.security.BasicProxyPreparer}()
* </code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Description:
* <td> Preparer for the proxies to the lookup services that are
* discovered and used by this utility.
* <p>
* This preparer should perform all operations required to use a
* newly received proxy to a lookup service, which may including
* verifying trust in the proxy, granting permissions, and setting
* constraints.
* <p>
* Currently, none of the methods on the
* {@link net.jini.core.lookup.ServiceRegistrar ServiceRegistrar}
* returned by this preparer are invoked by this implementation of
* <code>LookupLocatorDiscovery</code>.
* </table>
*
* <a name="taskManager">
* <table summary="Describes the taskManager configuration entry"
* border="0" cellpadding="2">
* <tr valign="top">
* <th scope="col" summary="layout"> <font size="+1">•</font>
* <th scope="col" align="left" colspan="2"> <font size="+1">
* <code>taskManager</code></font>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Type: <td> {@link com.sun.jini.thread.TaskManager}
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Default: <td> <code>new
* {@link com.sun.jini.thread.TaskManager#TaskManager()
* TaskManager}(15, (15*1000), 1.0f)</code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Description:
* <td> The object that pools and manages the various threads
* executed by this utility. The default manager creates a
* maximum of 15 threads, waits 15 seconds before removing
* idle threads, and uses a load factor of 1.0 when
* determining whether to create a new thread. This object
* should not be shared with other components in the
* application that employs this utility.
* </table>
*
* <a name="wakeupManager">
* <table summary="Describes the wakeupManager configuration entry"
* border="0" cellpadding="2">
* <tr valign="top">
* <th scope="col" summary="layout"> <font size="+1">•</font>
* <th scope="col" align="left" colspan="2"> <font size="+1">
* <code>wakeupManager</code></font>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Type: <td> {@link com.sun.jini.thread.WakeupManager}
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Default: <td> <code>new
* {@link com.sun.jini.thread.WakeupManager#WakeupManager(
* com.sun.jini.thread.WakeupManager.ThreadDesc)
* WakeupManager}(new
* {@link com.sun.jini.thread.WakeupManager.ThreadDesc}(null,true))</code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Description:
* <td> Object that pools and manages the various tasks that are
* initially executed by the object corresponding to the
* <a href="#taskManager"><code>taskManager</code></a> entry
* of this component, but which fail during that initial execution.
* This object schedules the re-execution of such a failed task -
* in the <a href="#taskManager"><code>taskManager</code></a>
* object - at various times in the future, (employing a
* "backoff strategy"). The re-execution of the failed task will
* continue to be scheduled by this object until the task finally
* succeeds. This object should not be shared with other components
* in the application that employs this utility.
* </table>
*
* <a name="lldLogging">
* <p>
* <b><font size="+1">Logging</font></b>
* <p>
* </a>
*
* This implementation of <code>LookupLocatorDiscovery</code> uses the
* {@link Logger} named <code>net.jini.discovery.LookupLocatorDiscovery</code>
* to log information at the following logging levels: <p>
*
* <table border="1" cellpadding="5"
* summary="Describes the information logged by LookupLocatorDiscovery,
* and the levels at which that information is logged">
*
* <caption halign="center" valign="top">
* <b><code>net.jini.discovery.LookupLocatorDiscovery</code></b>
* </caption>
*
* <tr> <th scope="col"> Level</th>
* <th scope="col"> Description</th>
* </tr>
*
* <tr>
* <td>{@link java.util.logging.Level#INFO INFO}</td>
* <td>
* when any exception occurs in a task or thread, while attempting unicast
* discovery of a given locator
* </td>
* </tr>
* <tr>
* <td>{@link java.util.logging.Level#INFO INFO}</td>
* <td>when any exception occurs while attempting to prepare a proxy</td>
* </tr>
* <tr>
* <td>{@link com.sun.jini.logging.Levels#HANDLED HANDLED}</td>
* <td>
* when an exception is handled during unicast discovery.
* </td>
* </tr>
* <tr>
* <td>{@link java.util.logging.Level#FINEST FINEST}</td>
* <td>whenever any thread or task is started</td>
* </tr>
*
* <tr>
* <td>{@link java.util.logging.Level#FINEST FINEST}</td>
* <td>
* whenever any thread (except the <code>Notifier</code> thread) or task
* completes successfully
* </td>
* </tr>
*
* <tr>
* <td>{@link java.util.logging.Level#FINEST FINEST}</td>
* <td>whenever a discovered or discarded event is sent</td>
* </tr>
*
* <tr>
* <td>{@link java.util.logging.Level#FINEST FINEST}</td>
* <td>whenever a proxy is prepared</td>
* </tr>
*
* <tr>
* <td>{@link java.util.logging.Level#FINEST FINEST}</td>
* <td>
* when an <code>IOException</code> occurs upon attempting to close the
* socket after a unicast discovery attempt has either completed
* successfully or failed
* </td>
* </tr>
* </table>
* <p>
*
* This implementation of <code>LookupLocatorDiscovery</code> determines
* the constraints (if any) to apply to unicast discovery for a given
* {@link net.jini.core.discovery.LookupLocator LookupLocator} instance
* by calling the
* {@link net.jini.core.constraint.RemoteMethodControl#getConstraints
* getConstraints} method of that instance, if it implements the
* {@link net.jini.core.constraint.RemoteMethodControl RemoteMethodControl}
* interface. If the {@link net.jini.core.discovery.LookupLocator
* LookupLocator} instance does not implement
* {@link net.jini.core.constraint.RemoteMethodControl RemoteMethodControl},
* then no constraints are applied to unicast discovery for that instance.
* <p>
* For more information on constraining unicast discovery, refer to the
* documentation for the {@link net.jini.discovery.ConstrainableLookupLocator
* ConstrainableLookupLocator} class.
*
* @author Sun Microsystems, Inc.
*
* @see net.jini.core.discovery.LookupLocator
*/
public class LookupLocatorDiscovery implements DiscoveryManagement,
DiscoveryLocatorManagement
{
/* Name of this component; used in config entry retrieval and the logger.*/
private static final String COMPONENT_NAME
= "net.jini.discovery.LookupLocatorDiscovery";
/* Logger used by this utility. */
private static final Logger logger = Logger.getLogger(COMPONENT_NAME);
/** Maximum number of concurrent tasks that can be run in any task
* manager created by this class.
*/
private static final int MAX_N_TASKS = 15;
/** Default timeout to set on sockets used for unicast discovery. */
private static final int DEFAULT_SOCKET_TIMEOUT = 1*60*1000;
/** LookupLocator.getRegistrar method, used for looking up client
* constraints of contained lookup locators.
*/
private static final Method getRegistrarMethod;
static {
try {
getRegistrarMethod = LookupLocator.class.getDeclaredMethod(
"getRegistrar", new Class[0]);
} catch (NoSuchMethodException e) {
throw new AssertionError(e);
}
}
/** Task manager for the discovery tasks. On the first attempt to
* discover each locator, the tasks used to perform those discoveries
* are managed by this <code>TaskManager</code> so that the number of
* concurrent threads can be bounded. If one or more of those attempts
* fails, a <code>WakeupManager</code> is used (through the use of a
* <code>RetryTask</code>) to schedule - at a later time (employing a
* "backoff strategy") - the re-execution of each failed task in this
* <code>TaskManager</code>.
*/
private TaskManager discoveryTaskMgr;
/** Wakeup manager for the discovery tasks. For any locator, after
* an initial failure to discover the locator, the task used to
* perform all future discovery attempts is managed by this
* <code>WakeupManager</code>; which schedules the re-execution of
* the failed task - in the task manager - at various times in the
* future until the locator is successfully discovered. This wakeup
* manager is supplied to the <code>RetryTask</code>) that performs
* the actual discovery attempt(s) so that when termination of this
* lookup locator discovery utility is requested, all tasks scheduled
* for retry by this wakeup manager can be cancelled.
*/
private WakeupManager discoveryWakeupMgr;
/** Stores LookupLocators that have not been discovered yet. */
private final HashSet undiscoveredLocators = new HashSet(11);
/** Stores LookupLocators that have been discovered */
private final ArrayList discoveredLocators = new ArrayList(11);
/** Thread that handles pending notifications. */
private Notifier notifierThread;
/** Notifications to be sent to listeners. */
private LinkedList pendingNotifies = new LinkedList();
/** Stores DiscoveryListeners **/
private final ArrayList listeners = new ArrayList(1);
/** Flag indicating whether or not this class is still functional. */
private boolean terminated = false;
/* Preparer for the proxies to the lookup services that are discovered
* and used by this utility.
*/
private ProxyPreparer registrarPreparer;
/* Utility for participating in version 2 of the unicast discovery
* protocol.
*/
private Discovery protocol2 = Discovery.getProtocol2(null);
/*
* Controls how long to wait before attempting unicast discovery, on
* startup.
*/
private long initialUnicastDelayRange = 0;
/*
* Flag which indicates if discoverLocators was called during
* initialUnicastDelayRange delay.
*/
private boolean discoverLocatorsCalled = false;
/** Wrapper class in which each instance corresponds to a lookup service
* to discover via unicast discovery.
*/
private class LocatorReg {
public ServiceRegistrar proxy = null;
public final LookupLocator l;
public String[] memberGroups = null;
/* No need to sync on cnt since it's modified only in constructor */
private int cnt = 0;
private static final long MIN_RETRY = 15000;
private final long[] sleepTime = { 5*1000, 10*1000, 20*1000,
30*1000, 60*1000,
2*60*1000, 4*60*1000,
8*60*1000, 15*60*1000};
private int tryIndx = 0;
private long nextTryTime;
private final int id;
private long time = 0;
public LocatorReg(LookupLocator l) {
id = cnt++;
this.l = l;
nextTryTime = System.currentTimeMillis();
}//end constructor
/** Accessor that returns the absolute time at which the next
* discovery attempt should be made after the previous attempt
* has failed to find the desired lookup service.
*/
public synchronized long getNextTryTime() {
return nextTryTime;
}//end getNextTryTime
/** Computes the time (in milliseconds) used to determine when it
* is allowable -- after a previous failure -- to again attempt
* unicast discovery of the lookup service referenced in this class.
*
* Since this method is called multiple times for a particular lookup
* service only when there is difficulty discovering that lookup
* service, the value computed by this method increases in a
* graduated manner - increasing the amount of time to wait before
* the next discovery attempt should be made - upon each invocation,
* eventually reaching a maximum time interval over which discovery
* is re-tried. In this way, the network is not flooded with unicast
* discovery requests referencing a lookup service that may not be
* available for quite some time (if ever).
*/
public void calcNextTryTime() {
nextTryTime = System.currentTimeMillis() + sleepTime[tryIndx];
if(tryIndx < sleepTime.length-1) tryIndx++;
}//end calcNextTryTime
/** This method gets called only from the public discard() method.
* The purpose of this method is to prevent the discard method from
* being called too frequently. This method measures the time from
* when the listener's discovered() method gets called to when its
* discard() method gets called. If the time is less than MIN_RETRY,
* the next discovery attempt is delayed.
*/
public void fixupNextTryTime() {
long curTime = System.currentTimeMillis();
if( (curTime - time) > MIN_RETRY ) {
tryIndx = 0;
nextTryTime = curTime;
} else {
calcNextTryTime();
}//endif
}//end fixupNextTryTime
/** Initiates unicast discovery of the lookup service referenced
* in this class.
*/
public boolean tryGetProxy() {
if (proxy != null ) {
throw new IllegalArgumentException
("LookupLocator has been discovered already");
}
InvocationConstraints ic = InvocationConstraints.EMPTY;
if (l instanceof RemoteMethodControl) {
MethodConstraints mc =
((RemoteMethodControl) l).getConstraints();
if (mc != null) {
ic = mc.getConstraints(getRegistrarMethod);
}
}
try {
doUnicastDiscovery(l, ic);
time = System.currentTimeMillis();//mark the time of discovery
return true;
} catch (Throwable e) {
if( logger.isLoggable(Level.INFO) ) {
LogUtil.logThrow(logger,
Level.INFO,
this.getClass(),
"tryGetProxy",
"exception occured during unicast discovery to "
+ "{0}:{1,number,#} with constraints {2}",
new Object[] {
l.getHost(),
new Integer(l.getPort()),
ic
},
e);
}//endif
calcNextTryTime();//discovery failed; try again even later
return false;
}
}//end tryGetProxy
/** This method employs the unicast discovery protocol to discover
* the registrar having <code>LookupLocator</code> equal to the value
* contained in the <code>locator</code> parameter of this class.
*/
private void doUnicastDiscovery(LookupLocator locator,
InvocationConstraints ic)
throws IOException, ClassNotFoundException
{
UnicastResponse resp = new MultiIPDiscovery() {
protected UnicastResponse performDiscovery(
Discovery disco,
DiscoveryConstraints dc,
Socket s)
throws IOException, ClassNotFoundException
{
return disco.doUnicastDiscovery(
s,
dc.getUnfulfilledConstraints(),
null,
null,
null);
}
protected void socketCloseException(IOException e) {
logger.log(Level.FINEST,
"IOException on socket close upon "
+ "completion of unicast discovery",
e);
}
protected void singleResponseException(Exception e,
InetAddress addr,
int port)
{
logger.log(
Levels.HANDLED,
"Exception occured during unicast discovery " +
addr + ":" + port, e);
}
}.getResponse(locator.getHost(), locator.getPort(), ic);
/* Proxy preparation */
proxy = (ServiceRegistrar)registrarPreparer.prepareProxy
(resp.getRegistrar());
logger.log(Level.FINEST, "LookupLocatorDiscovery - prepared "
+"lookup service proxy: {0}", proxy);
memberGroups = resp.getGroups();
}//end doUnicastDiscovery
/** Returns true if the locators are equal. */
public boolean equals(Object obj) {
if( !(obj instanceof LocatorReg) ) return false;
return l.equals(((LocatorReg)obj).l);
}//end equals
/** Returns the hash code of the locator referenced in this class. */
public int hashCode() {
return l.hashCode();
}//end hashCode
}//end class LocatorReg
/** Data structure containing task data processed by the Notifier Thread */
private static class NotifyTask {
/** The listeners to notify */
public final ArrayList listeners;
/** Map of discovered registrars to groups in which each is a member */
public final Map groupsMap;
/** True if discarded, else discovered */
public final boolean discard;
public NotifyTask(ArrayList listeners,
Map groupsMap,
boolean discard)
{
this.listeners = listeners;
this.groupsMap = groupsMap;
this.discard = discard;
}
}//end class NotifyTask
/** Thread that retrieves data structures of type NotifyTask from a
* queue and, based on the contents of the data structure, sends the
* appropriate event (discovered/discarded) to each registered listener.
* <p>
* Only 1 instance of this thread is run.
*/
private class Notifier extends Thread {
/** Construct a daemon thread */
public Notifier() {
super("event notifier");
setDaemon(true);
}//end constructor
public void run() {
logger.finest("LookupLocatorDiscovery - Notifier thread started");
while (true) {
NotifyTask task;
synchronized (pendingNotifies) {
if (pendingNotifies.isEmpty()) {
notifierThread = null;
return;
}//endif
task = (NotifyTask)pendingNotifies.removeFirst();
}//end sync(pendingNotifies)
boolean firstListener = true;
for(Iterator iter = task.listeners.iterator();iter.hasNext();){
DiscoveryListener l = (DiscoveryListener)iter.next();
DiscoveryEvent e =
new DiscoveryEvent(LookupLocatorDiscovery.this,
deepCopy((HashMap)task.groupsMap) );
/* Log the event info about the lookup(s) */
if(firstListener && (logger.isLoggable(Level.FINEST)) ) {
String eType = (task.discard ?
"discarded":"discovered");
ServiceRegistrar[] regs = e.getRegistrars();
logger.finest(eType+" event -- "+regs.length
+" lookup(s)");
Map groupsMap = e.getGroups();
for(int i=0;i<regs.length;i++) {
LookupLocator loc = null;
try {
loc = regs[i].getLocator();
} catch (Throwable ex) { /* ignore */ }
String[] groups = (String[])groupsMap.get(regs[i]);
logger.finest(" "+eType+" locator = "+loc);
if(groups.length == 0) {
logger.finest(" "+eType
+" group = NO_GROUPS");
} else {
for(int j=0;j<groups.length;j++) {
logger.finest(" "+eType+" group["+j+"] "
+"= "+groups[j]);
}//end loop
}//endif(groups.length)
}//end loop
}//endif(firstListener && isLoggable(Level.FINEST)
if (task.discard) {
l.discarded(e);
} else {
l.discovered(e);
}//endif
}//end loop
}//end loop
}//end run
}//end class Notifier
/** Task which retrieves elements from the set of undiscoveredLocators
* and attempts, through the unicast discovery protocol, to discover
* the lookup service having the LookupLocator referenced by the element.
* If a particular instance of this class fails to find the lookup
* service that it references, this task will be rescheduled to be
* executed again at a later time, using a "backoff strategy" as defined
* by the method <code>com.sun.jini.thread.RetryTask.retryTime</code>.
*
* @see com.sun.jini.thread.RetryTask
* @see com.sun.jini.thread.WakeupManager
*/
private class DiscoveryTask extends RetryTask {
private LocatorReg reg;
public DiscoveryTask(LocatorReg reg,
TaskManager taskMgr,
WakeupManager wakeupMgr)
{
super(taskMgr,wakeupMgr);
this.reg = reg;
}//end constructor
/** Executes the current instance of this task once, attempting to
* discover - through unicast discovery - the lookup service
* referenced in that instance. Upon successfully discovering the
* indicated lookup service, this method returns <code>true</code>
* and the current instance of this task is not executed again.
* For each unsuccessful discovery attempt made by this method
* for the current instance of this task, this method returns
* <code>false</code>, which causes the task to be scheduled by
* the <code>WakeupManager</code> to be executed again at a later
* time as indicated by the value returned by <code>retryTime</code>.
*/
public boolean tryOnce() {
logger.finest("LookupLocatorDiscovery - DiscoveryTask started");
synchronized(LookupLocatorDiscovery.this) {
if (terminated) {
return true;
}
/* Locators may have been removed (ex. removeLocators or
* setLocators) between the time they were added to the map,
* and the time this task is finally executed. Determine if
* this task should continue.
*/
if( undiscoveredLocators.isEmpty() ) {
logger.finest("LookupLocatorDiscovery - DiscoveryTask "
+"completed");
return true;//true ==> done. Don't queue retry.
}//endif
if(!undiscoveredLocators.contains(reg)) {
logger.finest("LookupLocatorDiscovery - DiscoveryTask "
+"completed");
return true;//already removed, true ==> don't queue retry
}
}//end sync(LookupLocatorDiscovery.this)
/* Use the unicast discovery protocol to perform the actual
* discovery. Note that since this process involves remote,
* interprocess (socket) communication, it is important that
* this processing be performed outside of the sync block.
*/
boolean noRetry = regTryGetProxy(reg);//t -> done, f -> queue retry
synchronized (LookupLocatorDiscovery.this) {
if (terminated) {
return true;
}
if(noRetry) {
logger.finest("LookupLocatorDiscovery - DiscoveryTask "
+"completed");
} else {
logger.finest("LookupLocatorDiscovery - DiscoveryTask "
+"failed, will retry later");
}//endif
return noRetry;
}
}//end tryOnce
/** Returns the next absolute time (in milliseconds) at which another
* attempt to discover the lookup service referenced in this class
* should be made.
*
* Overrides the version of this method in the parent class.
*/
public long retryTime() {
return reg.getNextTryTime();
}//end retryTime
/** Returns true if current instance must be run after task(s) in
* task manager queue.
* @param tasks the tasks to consider.
* @param size elements with index less than size are considered.
*/
public boolean runAfter(java.util.List tasks, int size) {
return false;
}//end runAfter
}//end class DiscoveryTask
/**
* Creates an instance of this class (<code>LookupLocatorDiscovery</code>),
* with an initial array of <code>LookupLocator</code>s to be managed.
* For each managed <code>LookupLocator</code>, unicast discovery is
* performed to obtain a <code>ServiceRegistrar</code> proxy for that
* lookup service.
*
* @param locators the locators to discover
*
* @throws java.lang.NullPointerException input array contains at least
* one <code>null</code> element
*/
public LookupLocatorDiscovery(LookupLocator[] locators) {
try {
beginDiscovery(locators, EmptyConfiguration.INSTANCE);
} catch(ConfigurationException e) { /* swallow this exception */ }
}//end constructor
/**
* Constructs a new lookup locator discovery object, set to discover the
* given set of locators, and having the given <code>Configuration</code>.
* <p>
* For each managed <code>LookupLocator</code>, unicast discovery is
* performed to obtain a <code>ServiceRegistrar</code> proxy for that
* lookup service.
*
* @param locators the locators to discover
*
* @param config an instance of <code>Configuration</code>, used to
* obtain the objects needed to configure the current
* instance of this class
*
* @throws net.jini.config.ConfigurationException indicates an exception
* occurred while retrieving an item from the given
* <code>Configuration</code>
*
* @throws java.lang.NullPointerException input array contains at least
* one <code>null</code> element or <code>null</code> is input
* for the configuration
*/
public LookupLocatorDiscovery(LookupLocator[] locators,
Configuration config)
throws ConfigurationException
{
beginDiscovery(locators, config);
}//end constructor
/**
* Add a DiscoveryListener to the listener set. The listener's
* discovered method gets called right way with an array of
* ServiceRegistrars that have already been discovered, and will
* be called in the future whenever additional lookup services
* are discovered.
*
* @param l the new DiscoveryListener to add
*
* @throws java.lang.NullPointerException this exception occurs when
* <code>null</code> is input to the listener parameter
* <code>l</code>.
*
* @throws java.lang.IllegalStateException this exception occurs when
* this method is called after the <code>terminate</code>
* method has been called.
*
* @see #removeDiscoveryListener
*/
public void addDiscoveryListener(DiscoveryListener l) {
if(l == null) {
throw new NullPointerException("can't add null listener");
}
synchronized(this) {
if (terminated) {
throw new IllegalStateException("discovery terminated");
}
if(listeners.contains(l)) return; //already have this listener
listeners.add(l);
if(!discoveredLocators.isEmpty()) {
HashMap groupsMap = new HashMap(discoveredLocators.size());
Iterator iter = discoveredLocators.iterator();
for (int i = 0; iter.hasNext(); i++) {
LocatorReg reg = (LocatorReg)iter.next();
groupsMap.put(reg.proxy,reg.memberGroups);
}//end loop
ArrayList list = new ArrayList(1);
list.add(l);
addNotify(list, groupsMap, false);
}//endif
}//end sync
}//end addDiscoveryListener
/**
* Remove a DiscoveryListener from the listener set. It does
* nothing if the DiscoveryListener does not exist in the
* the listener set.
*
* @param l the existing DiscoveryListener to remove
*
* @throws java.lang.IllegalStateException this exception occurs when
* this method is called after the <code>terminate</code>
* method has been called.
*
* @see #addDiscoveryListener
*/
public synchronized void removeDiscoveryListener(DiscoveryListener l) {
if (terminated) {
throw new IllegalStateException("discovery terminated");
}
int index = listeners.indexOf(l);
if(index != -1) listeners.remove(index);
}//end removeDiscoveryListener
/**
* Returns an array of instances of <code>ServiceRegistrar</code>, each
* corresponding to a proxy to one of the currently discovered lookup
* services. For each invocation of this method, a new array is returned.
*
* @return array of instances of <code>ServiceRegistrar</code>, each
* corresponding to a proxy to one of the currently discovered
* lookup services
*
* @throws java.lang.IllegalStateException this exception occurs when
* this method is called after the <code>terminate</code>
* method has been called.
*
* @see net.jini.core.lookup.ServiceRegistrar
* @see net.jini.discovery.DiscoveryManagement#removeDiscoveryListener
*/
public ServiceRegistrar[] getRegistrars() {
synchronized(this) {
if (terminated) {
throw new IllegalStateException("discovery terminated");
}
if((discoveredLocators == null) || (discoveredLocators.isEmpty())){
return new ServiceRegistrar[0];
}
return buildServiceRegistrar();
}//end sync(this)
}//end getRegistrars
/**
* Removes an instance of <code>ServiceRegistrar</code> from the
* managed set of lookup services, making the corresponding lookup
* service eligible for re-discovery. This method takes no action if
* the parameter input to this method is <code>null</code>, or if it
* does not match (using <code>equals</code>) any of the elements in
* the managed set.
*
* @param proxy the instance of <code>ServiceRegistrar</code> to discard
* from the managed set of lookup services
*
* @throws java.lang.IllegalStateException this exception occurs when
* this method is called after the <code>terminate</code>
* method has been called.
*
* @see net.jini.core.lookup.ServiceRegistrar
* @see net.jini.discovery.DiscoveryManagement#discard
*/
public void discard(ServiceRegistrar proxy) {
synchronized(this) {
if (terminated) {
throw new IllegalStateException("discovery terminated");
}
if(proxy == null) return;
LookupLocator lct = findRegFromProxy(proxy);
if(lct == null) return;
/* Remove locator from the set of already-discovered locators */
LocatorReg reg = removeDiscoveredLocator(lct);
/* Prepare the information for the discarded event */
HashMap groupsMap = new HashMap(1);
groupsMap.put(reg.proxy,reg.memberGroups);
/* Prepare the discarded locatorReg for re-discovery */
reg.proxy = null;
reg.memberGroups = null;
reg.fixupNextTryTime();
addToMap(reg);//put discarded reg back in the not-discovered map
/* Send a discarded event to all registered listeners */
if(!listeners.isEmpty()) {
addNotify((ArrayList)listeners.clone(), groupsMap, true);
}//endif
}//end sync(this)
}//end discard
/**
* Terminates all threads, ending all locator discovery processing being
* performed by the current instance of this class.
* <p>
* After this method has been invoked, no new lookup services will
* be discovered, and the effect of any new operations performed
* on the current instance of this class are undefined.
*
* @see net.jini.discovery.DiscoveryManagement#terminate
*/
public synchronized void terminate() {
if(terminated) return;
terminated = true;
terminateTaskMgr();
synchronized(pendingNotifies) {
pendingNotifies.clear();
}//end sync
}//end terminate
/**
* Returns an array consisting of the elements of the managed set
* of locators; that is, instances of <code>LookupLocator</code> in
* which each instance corresponds to a specific lookup service to
* discover. The returned set will include both the set of
* <code>LookupLocator</code>s corresponding to lookup services
* that have already been discovered as well as the set of those
* that have not yet been discovered. If the managed set of locators
* is empty, this method will return the empty array. This method
* returns a new array upon each invocation.
*
* @return <code>LookupLocator</code> array consisting of the elements
* of the managed set of locators
*
* @throws java.lang.IllegalStateException this exception occurs when
* this method is called after the <code>terminate</code>
* method has been called.
*
* @see net.jini.discovery.DiscoveryLocatorManagement#getLocators
* @see #setLocators
*/
public synchronized LookupLocator[] getLocators() {
if (terminated) {
throw new IllegalStateException("discovery terminated");
}
/* Includes the set of already-discovered lookup services and
* the set of not-yet-discovered lookup services.
*/
int size = discoveredLocators.size() + undiscoveredLocators.size();
LookupLocator[] ret = new LookupLocator[size];
/* Retrieve the locators of the already-discovered lookup services */
int k = 0;
Iterator iter = discoveredLocators.iterator();
while(iter.hasNext()) {
ret[k++] = ((LocatorReg)iter.next()).l;
}//end loop
/* Append the locators of the not-yet-discovered lookup services */
iter = undiscoveredLocators.iterator();
while(iter.hasNext()) {
ret[k++] = ((LocatorReg)iter.next()).l;
}//end loop
return ret;
}//end getLocators
/**
* Adds a set of locators to the managed set of locators. Elements in the
* input set that duplicate (using the <code>LookupLocator.equals</code>
* method) elements already in the managed set will be ignored. If the
* empty array is input, the managed set of locators will not change.
*
* @param locators <code>LookupLocator</code> array consisting of the
* locators to add to the managed set.
*
* @throws java.lang.NullPointerException this exception occurs when
* either <code>null</code> is input to the <code>locators</code>
* parameter, or one or more of the elements of the
* <code>locators</code> parameter is <code>null</code>.
*
* @throws java.lang.IllegalStateException this exception occurs when
* this method is called after the <code>terminate</code>
* method has been called.
*
* @see net.jini.discovery.DiscoveryLocatorManagement#addLocators
* @see #removeLocators
*/
public synchronized void addLocators(LookupLocator[] locators) {
testSetForNull(locators);
if (terminated) {
throw new IllegalStateException("discovery terminated");
}
discoverLocators(locators);
}//end addLocators
/**
* Replaces all of the locators in the managed set with locators from
* a new set, and discards any already-discovered lookup service that
* corresponds to a locator that is removed from the managed set
* as a result of an invocation of this method. For any such lookup
* service that is discarded, a discard notification is sent; and that
* lookup service will not be eligible for re-discovery (assuming it is
* not currently eligible for discovery through other means, such as
* group discovery).
* <p>
* If the empty array is input, locator discovery will cease until this
* method is invoked with an input parameter that is non-<code>null</code>
* and non-empty.
*
* @param locators <code>LookupLocator</code> array consisting of the
* locators that will replace the current locators in the
* managed set.
*
* @throws java.lang.NullPointerException this exception occurs when
* either <code>null</code> is input to the <code>locators</code>
* parameter, or one or more of the elements of the
* <code>locators</code> parameter is <code>null</code>.
*
* @throws java.lang.IllegalStateException this exception occurs when
* this method is called after the <code>terminate</code>
* method has been called.
*
* @see net.jini.discovery.DiscoveryLocatorManagement#setLocators
* @see #getLocators
*/
public void setLocators(LookupLocator[] locators) {
testSetForNull(locators);
synchronized(this) {
if (terminated) {
throw new IllegalStateException("discovery terminated");
}
HashMap groupsMap = new HashMap(1);
/* From the set of already-discovered locators, remove each
* element that is NOT in the input set of locators.
*/
Iterator iter = discoveredLocators.iterator();
while(iter.hasNext()) {
LocatorReg reg = (LocatorReg)iter.next();
if(!isArrayContains(locators, reg.l)) {
iter.remove();
groupsMap.put(reg.proxy,reg.memberGroups);
}//endif
}//end loop
/* From the set of yet-to-be-discovered locators, remove each
* element that is NOT in the input set of replacement locators.
*
* Note that if the discovery task is currently attempting to
* discover a locator from this set, and if that locator is not
* contained in the given input set of replacement locators (that
* is, it is no longer desired that that locator be discovered),
* then the discovery task, when it completes (either successfully
* or un-successfully) the attempt to discover that locator, will
* end all discovery processing with respect to the affected
* locator.
*
* To inform the discovery task -- upon its return from the
* unicast discovery process -- of the desire to terminate all
* discovery processing for that particular locator, the element
* in the set of undiscoveredLocators that corresponds to that
* locator is removed. This means that if the discovery attempt
* failed, the locator will no longer be considered one of the
* yet-to-be-discovered locators; and if the attempt succeeded,
* prevents the locator from being placed in the set of
* already-discovered locators. It also prevents any discarded
* or discovered events from being sent.
*/
iter = undiscoveredLocators.iterator();
while(iter.hasNext()) {
LocatorReg reg = (LocatorReg)iter.next();
if(!isArrayContains(locators, reg.l)) {
iter.remove();
}//endif
}//end loop
/* Initiate discovery process for any new, un-discovered locators*/
discoverLocators(locators);
/* Send a discarded event to all registered listeners for any
* locators that were removed by this method.
*/
if(!groupsMap.isEmpty() && !listeners.isEmpty()) {
addNotify((ArrayList)listeners.clone(), groupsMap, true);
}//endif
}//end sync(this)
}//end setLocators
/**
* Deletes a set of locators from the managed set of locators, and discards
* any already-discovered lookup service that corresponds to a deleted
* locator. For any lookup service that is discarded as a result of an
* invocation of this method, a discard notification is sent; and that
* lookup service will not be eligible for re-discovery (assuming it is
* not currently eligible for discovery through other means, such as
* group discovery).
* <p>
* If the empty array is input, this method takes no action.
*
* @param locators <code>LookupLocator</code> array consisting of the
* locators that will be removed from the managed set.
*
* @throws java.lang.NullPointerException this exception occurs when
* either <code>null</code> is input to the <code>locators</code>
* parameter, or one or more of the elements of the
* <code>locators</code> parameter is <code>null</code>.
*
* @throws java.lang.IllegalStateException this exception occurs when
* this method is called after the <code>terminate</code>
* method has been called.
*
* @see net.jini.discovery.DiscoveryLocatorManagement#removeLocators
* @see #addLocators
*/
public void removeLocators(LookupLocator[] locators) {
testSetForNull(locators);
synchronized(this) {
if (terminated) {
throw new IllegalStateException("discovery terminated");
}
HashMap groupsMap = new HashMap(1);
for(int i=0; i<locators.length; i++) {
LocatorReg reg = removeDiscoveredLocator(locators[i]);
if(reg != null) {//removing an already-discovered reg
groupsMap.put(reg.proxy,reg.memberGroups);
continue;
}//endif
reg = findReg(locators[i]);
if(reg != null) {//reg not yet discovered, stop discovery of it
undiscoveredLocators.remove(reg);
}//endif
}//end loop
/* Send a discarded event to all registered listeners for any
* locators that were removed by this method.
*/
if(!groupsMap.isEmpty() && !listeners.isEmpty()) {
addNotify((ArrayList)listeners.clone(), groupsMap, true);
}//endif
}//end sync
}//end removeLocators
/**
* Returns the set of <code>LookupLocator</code> objects representing the
* desired lookup services that are currently discovered. If no lookup
* services are currently discovered, this method returns the empty array.
* This method returns a new array upon each invocation.
*
* @return <code>LookupLocator</code> array consisting of the elements
* from the managed set of locators that correspond to lookup
* services that have already been discovered.
*
* @throws java.lang.IllegalStateException this exception occurs when
* this method is called after the <code>terminate</code>
* method has been called.
*/
public synchronized LookupLocator[] getDiscoveredLocators() {
if (terminated) {
throw new IllegalStateException("discovery terminated");
}
int size = discoveredLocators.size();
LookupLocator[] ret = new LookupLocator[size];
/* Retrieve the locators of the already-discovered lookup services */
int k = 0;
Iterator iter = discoveredLocators.iterator();
while(iter.hasNext()) {
ret[k++] = ((LocatorReg)iter.next()).l;
}//end loop
return ret;
}//end getDiscoveredLocators
/**
* Returns the set of <code>LookupLocator</code> objects representing the
* desired lookup services that have not yet been discovered. If all
* of the desired lookup services are currently discovered, this method
* returns the empty array. This method returns a new array upon each
* invocation.
*
* @return <code>LookupLocator</code> array consisting of the elements
* from the managed set of locators that correspond to lookup
* services that have not yet been discovered.
*
* @throws java.lang.IllegalStateException this exception occurs when
* this method is called after the <code>terminate</code>
* method has been called.
*/
public synchronized LookupLocator[] getUndiscoveredLocators() {
if (terminated) {
throw new IllegalStateException("discovery terminated");
}
LookupLocator[] locs = new LookupLocator[undiscoveredLocators.size()];
Iterator iter = undiscoveredLocators.iterator();
for(int i=0;iter.hasNext();i++) {
locs[i] = ((LocatorReg)iter.next()).l;
}//end loop
return locs;
}//end getUndiscoveredLocators
/** Initiates the discovery process for the lookup services having the
* given locators.
*/
private void discoverLocators(LookupLocator[] lcts) {
assert Thread.holdsLock(this);
discoverLocatorsCalled = true;
if (lcts == null) return;
LookupLocator lct;
for(int i=0; i<lcts.length; i++) {
if(isDiscovered(lcts[i])) continue;
LocatorReg reg = findReg(lcts[i]);//in not-yet-discovered map?
if(reg == null) {
reg = new LocatorReg(lcts[i]);
addToMap(reg);
}//endif
}//end loop
}//end discoverLocators
/** From the sets of elements corresponding to yet-to-be-discovered
* locators, this methods finds and returns the instance of LocatorReg
* corresponding to the given LookupLocator. This method searches
* the set of undiscoveredLocators, and upon finding a matching
* LocatorReg object, that object is returned; otherwise, null is
* returned.
*/
private LocatorReg findReg(LookupLocator lct) {
Iterator iter = undiscoveredLocators.iterator();
while(iter.hasNext()) {
LocatorReg reg = (LocatorReg)iter.next();
if (reg.l.equals(lct)) return reg;
}//end loop
return null;
}//end findReg
/** This method searches the set of discovered LocatorReg objects
* for the element that contains the given ServiceRegistrar object.
* Upon finding such an element, the corresponding LookupLocator is
* returned; otherwise, null is returned.
*/
private LookupLocator findRegFromProxy(ServiceRegistrar proxy) {
Iterator iter = discoveredLocators.iterator();
while(iter.hasNext()) {
LocatorReg reg = (LocatorReg)iter.next();
if((reg.proxy).equals(proxy )) return reg.l;
}//end loop
return null;
}//end findRegFromProxy
/** Convenience method called from within the DiscoveryTask. Employing
* unicast discovery, this method attempts to discover the lookup service
* associated with the given LocatorReg. After successfully discovering
* the desired lookup service, this method queues the appropriate
* event for dissemination to the registered listeners, and then returns
* <code>true</code>; otherwise <code>false</code> is returned.
*/
private boolean regTryGetProxy(LocatorReg reg) {
/* The following call performs the actual unicast discovery attempt,
* and should not be made within a synchronization block.
*/
boolean b = reg.tryGetProxy();
/* While the discovery attempt was being made above, the locator
* corresponding to the given LocatorReg may have been removed from
* the managed set of locators (by a call to set/removeLocators). If
* it did happen to be removed, then there is no need to continue
* with the discovery process of that locator, whether its proxy was
* successfully retrieved or not. Thus, if it was removed from the
* set of undiscoveredLocators while unicast discovery was being
* performed, then return true to stop any queuing of a retry of
* the task used in the discovery attempt of the given locator.
*
* If it wasn't removed, but its proxy could not be successfully
* discovered (as indicated by a false return value), then leave
* it in the set of undiscoveredLocators and schedule - at a later
* time - a retry of the task which performs the discovery attempt.
*
* Finally, if the proxy of the locator was successfully discovered,
* then remove the locator from the set of undiscoveredLocators, add
* it to the set of discoveredLocators, notify all registered
* listeners that the locator has been discovered, and return true
* to prevent retries from being queued.
*/
synchronized (this) {
if(!undiscoveredLocators.contains(reg)) {
return true;//already removed, true ==> don't queue retry
}//endif
/* Discovery un-successful, leave in set, try new wakeup task */
if(!b) {
return false;//this causes a retry to be queued
}//endif
/* Discovery was successful, move reg from undiscoveredLocators
* to discoveredLocators, and notify listeners
*/
undiscoveredLocators.remove(reg);
discoveredLocators.add(reg);
if(!listeners.isEmpty()) {
addNotify((ArrayList)listeners.clone(),
mapRegToGroups(reg.proxy,reg.memberGroups),
false);
}//endif
return true;//done; don't queue any retries
}//end sync(this)
}//end regTryGetProxy
/** From each element of the set of LocatorReg objects that correspond
* to lookup services that have been discovered, this method extracts
* the ServiceRegistrar reference and returns all of the references
* in an array of ServiceRegistrar.
*/
private ServiceRegistrar[] buildServiceRegistrar() {
int k = 0;
ServiceRegistrar[] proxys =
new ServiceRegistrar[discoveredLocators.size()];
Iterator iter = discoveredLocators.iterator();
while(iter.hasNext()) {
LocatorReg reg = (LocatorReg)iter.next();
proxys[k++] = reg.proxy;
}//end loop
return proxys;
}//end buildServiceRegistrar
/**
* Adds the given LocatorReg object to the set containing the objects
* corresponding to the locators of desired lookup services that have
* not yet been discovered, and queues a DiscoveryTask to attempt,
* through unicast discovery, to discover the associated lookup service.
*/
private void addToMap(LocatorReg reg) {
undiscoveredLocators.add(reg);//add to set of not-yet-discovered locs
queueDiscoveryTask(reg);
}//end addToMap
/**
* Queues a DiscoveryTask to attempt, through unicast discovery, to
* discover the lookup service associated with the given LocatorReg
* instance.
*/
private void queueDiscoveryTask(LocatorReg reg) {
discoveryTaskMgr.add
(new DiscoveryTask(reg,discoveryTaskMgr,discoveryWakeupMgr));
}//end queueDiscoveryTask
/** Determines whether or not the lookup service associated with the
* given LookupLocator has already been discovered.
*/
private boolean isDiscovered(LookupLocator lct) {
Iterator iter = discoveredLocators.iterator();
while(iter.hasNext()) {
LocatorReg reg = (LocatorReg)iter.next();
if(reg.l.equals(lct)) return true;
}//end loop
return false;
}//end isDiscovered
/** Add a notification task to the pending queue, and start an instance of
* the Notifier thread if one isn't already running.
*/
private void addNotify(ArrayList notifies,
Map groupsMap,
boolean discard)
{
synchronized (pendingNotifies) {
pendingNotifies.addLast(new NotifyTask(notifies,
groupsMap,
discard));
if (notifierThread == null) {
notifierThread = new Notifier();
notifierThread.start();
}//endif
}//end sync
}//end addNotify
/** Convenience method used to remove the LocatorReg - corresponding to
* the given LookupLocator - from the set of LocatorReg objects that
* correspond to lookup services that have already been discovered.
*/
private LocatorReg removeDiscoveredLocator(LookupLocator lct) {
Iterator iter = discoveredLocators.iterator();
while(iter.hasNext()) {
LocatorReg reg = (LocatorReg)iter.next();
if(reg.l.equals(lct)) {
iter.remove();
return reg;
}//endif
}//end loop
return null;
}//end removeDiscoveredLocator
/** Convenience method that removes all pending and active tasks from the
* TaskManager, and removes all pending tasks from the WakeupManager.
*/
private void terminateTaskMgr() {
/* Cancel all tasks scheduled for future retry by the wakeup manager */
if(discoveryWakeupMgr != null) {
discoveryWakeupMgr.stop();//stop execution of the wakeup manager
discoveryWakeupMgr.cancelAll();//cancel all tickets
}//endif
/* Cancel/remove pending tasks from the task manager and terminate */
if(discoveryTaskMgr != null) {
ArrayList pendingTasks = discoveryTaskMgr.getPending();
for(int i=0;i<pendingTasks.size();i++) {
RetryTask pendingTask = (RetryTask)pendingTasks.get(i);
pendingTask.cancel();//cancel wakeup ticket
discoveryTaskMgr.remove(pendingTask);//remove from task mgr
}//end loop
discoveryTaskMgr.terminate();//interrupt all active tasks
discoveryTaskMgr = null;
discoveryWakeupMgr = null;
}//endif
}//end terminateTaskMgr
/** Determines if the given Object is an element of the given array. */
private boolean isArrayContains(Object[] a, Object obj) {
for(int i=0; i<a.length; i++ ) {
if(a[i].equals(obj)) return true;
}//end loop
return false;
}//end isArrayContains
/* Convenience method useful for debugging. */
private void printMap () {
Iterator iter = undiscoveredLocators.iterator();
while(iter.hasNext()) {
LocatorReg reg = (LocatorReg)iter.next();
System.out.println("printMap reg:" + reg.id);
}//end loop
}//end printMap
/**
* This method is used by the public methods of this class that are
* specified to throw a <code>NullPointerException</code> when the
* set of locators is either <code>null</code> or contains one or
* more <code>null</code> elements; in either case, this method
* throws a <code>NullPointerException</code> which should be allowed
* to propagate outward.
*
* @throws java.lang.NullPointerException this exception occurs when
* either <code>null</code> is input to the <code>locatorSet</code>
* parameter, or one or more of the elements of the
* <code>locatorSet</code> parameter is <code>null</code>.
*/
private void testSetForNull(LookupLocator[] locatorSet) {
if(locatorSet == null) {
throw new NullPointerException("null locator array");
}//endif
for(int i=0;i<locatorSet.length;i++) {
if(locatorSet[i] == null) {
throw new NullPointerException
("null element in locator array");
}//endif
}//end loop
}//end testSetForNull
/** Creates and returns a deep copy of the input parameter. This method
* assumes the input map is a HashMap of the registrar-to-groups mapping;
* and returns a clone not only of the map, but of each key-value pair
* contained in the mapping.
*
* @param groupsMap mapping from a set of registrars to the member groups
* of each registrar
*
* @return clone of the input map, and of each key-value pair contained
* in the input map
*/
private Map deepCopy(HashMap groupsMap) {
/* clone the input HashMap */
HashMap newMap = (HashMap)(groupsMap.clone());
/* clone the values of each mapping in place */
Set eSet = newMap.entrySet();
for(Iterator itr = eSet.iterator(); itr.hasNext(); ) {
Map.Entry pair = (Map.Entry)itr.next();
/* only need to clone the value of the order pair */
pair.setValue( ((String[])pair.getValue()).clone() );
}//end loop
return newMap;
}//end deepCopy
/** 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 Map mapRegToGroups(ServiceRegistrar reg, String[] curGroups) {
HashMap groupsMap = new HashMap(1);
groupsMap.put(reg,curGroups);
return groupsMap;
}//end mapRegToGroups
/**
* Using the given <code>Configuration</code>, initializes the current
* instance of this utility, and initiates the discovery process for
* the given set of locators.
*
* @param locators the set of locators to discover
*
* @param config an instance of <code>Configuration</code>, used to
* obtain the objects needed to configure this utility
*
* @throws net.jini.config.ConfigurationException indicates an exception
* occurred while retrieving an item from the given
* <code>Configuration</code>
*
* @throws java.lang.NullPointerException input array contains at least
* one <code>null</code> element or <code>null</code> is input
* for the configuration
*/
private void beginDiscovery(final LookupLocator[] locators,
Configuration config)
throws ConfigurationException
{
synchronized(this) {
init(config);
if (locators == null) {
return;
}
testSetForNull(locators);
if (initialUnicastDelayRange > 0) {
discoveryWakeupMgr.schedule(
System.currentTimeMillis() +
(long) (Math.random() * initialUnicastDelayRange),
new Runnable() {
public void run() {
synchronized (LookupLocatorDiscovery.this) {
if (terminated || discoverLocatorsCalled) {
// discoverLocatorsCalled will be true
// if there has been an intervening
// addLocators or setLocators call.
return;
}
discoverLocators(locators);
}
}
}
);
} else {
discoverLocators(locators);
}
}
}
/* Convenience method that encapsulates the retrieval of the configurable
* items from the given <code>Configuration</code> object.
*/
private void init(Configuration config) throws ConfigurationException {
if(config == null) throw new NullPointerException("config is null");
/* Lookup service proxy preparer */
registrarPreparer = (ProxyPreparer)config.getEntry
(COMPONENT_NAME,
"registrarPreparer",
ProxyPreparer.class,
new BasicProxyPreparer());
/* Task manager */
try {
discoveryTaskMgr = (TaskManager)config.getEntry(COMPONENT_NAME,
"taskManager",
TaskManager.class);
} catch(NoSuchEntryException e) { /* use default */
discoveryTaskMgr = new TaskManager(MAX_N_TASKS,(15*1000),1.0f);
}
/* Wakeup manager */
try {
discoveryWakeupMgr = (WakeupManager)config.getEntry
(COMPONENT_NAME,
"wakeupManager",
WakeupManager.class);
} catch(NoSuchEntryException e) { /* use default */
discoveryWakeupMgr = new WakeupManager
(new WakeupManager.ThreadDesc(null, true));
}
initialUnicastDelayRange = Config.getLongEntry(config,
COMPONENT_NAME,
"initialUnicastDelayRange",
0,
0,
Long.MAX_VALUE);
}//end init
}//end class LookupLocatorDiscovery