Package net.jini.discovery

Source Code of net.jini.discovery.LookupLocatorDiscovery$DiscoveryTask

/*
* 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">&#X2022;</font>
*     <th scope="col" align="left" colspan="2"> <font size="+1">
*     <code>initialUnicastDelayRange</code></font>
*
*   <tr valign="top"> <td> &nbsp <th scope="row" align="right">
*     Type: <td> <code>long</code>
*
*   <tr valign="top"> <td> &nbsp <th scope="row" align="right">
*     Default: <td> <code>0 milliseconds</code>
*
*   <tr valign="top"> <td> &nbsp <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">&#X2022;</font>
*     <th scope="col" align="left" colspan="2"> <font size="+1">
*     <code>registrarPreparer</code></font>
*
*   <tr valign="top"> <td> &nbsp <th scope="row" align="right">
*       Type: <td> {@link net.jini.security.ProxyPreparer}
*
*   <tr valign="top"> <td> &nbsp <th scope="row" align="right">
*       Default: <td> <code>new {@link net.jini.security.BasicProxyPreparer}()
*                     </code>
*
*   <tr valign="top"> <td> &nbsp <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">&#X2022;</font>
*     <th scope="col" align="left" colspan="2"> <font size="+1">
*     <code>taskManager</code></font>
*
*   <tr valign="top"> <td> &nbsp <th scope="row" align="right">
*     Type: <td> {@link com.sun.jini.thread.TaskManager}
*
*   <tr valign="top"> <td> &nbsp <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> &nbsp <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">&#X2022;</font>
*     <th scope="col" align="left" colspan="2"> <font size="+1">
*     <code>wakeupManager</code></font>
*
*   <tr valign="top"> <td> &nbsp <th scope="row" align="right">
*     Type: <td> {@link com.sun.jini.thread.WakeupManager}
*
*   <tr valign="top"> <td> &nbsp <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> &nbsp <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-1tryIndx++;
  }//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 != -1listeners.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 == nullreturn;
  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 == nullthrow 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
TOP

Related Classes of net.jini.discovery.LookupLocatorDiscovery$DiscoveryTask

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