Package net.jini.lease

Source Code of net.jini.lease.LeaseRenewalManager$RenewTask

/*
* 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.lease;

import com.sun.jini.config.Config;
import com.sun.jini.constants.ThrowableConstants;
import com.sun.jini.logging.Levels;
import com.sun.jini.logging.LogManager;
import com.sun.jini.proxy.ConstrainableProxyUtil;
import com.sun.jini.thread.TaskManager;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import net.jini.config.Configuration;
import net.jini.config.ConfigurationException;
import net.jini.core.constraint.RemoteMethodControl;
import net.jini.core.lease.Lease;
import net.jini.core.lease.LeaseException;
import net.jini.core.lease.LeaseMap;
import net.jini.core.lease.LeaseMapException;
import net.jini.core.lease.UnknownLeaseException;

/**
* Provides for the systematic renewal and overall management of a set
* of leases associated with one or more remote entities on behalf of a
* local entity.
* <p>
* This class removes much of the administrative burden associated with
* lease renewal. Clients of the renewal manager simply give their
* leases to the manager and the manager renews each lease as necessary
* to achieve a <em>desired expiration</em> time (which may be later
* than the lease's current <em>actual expiration</em> time). Failures
* encountered while renewing a lease can optionally be reflected to the
* client via <code>LeaseRenewalEvent</code> instances.
* <p>
* Note that this class is not remote. Entities wishing to use this
* class must create an instance of this class in their own virtual
* machine to locally manage the leases granted to them. If the virtual
* machine that the manager was created in exits or crashes, the renewal
* manager will be destroyed.
* <p>
* The <code>LeaseRenewalManager</code> distinguishes between two time
* values associated with lease expiration: the <em>desired
* expiration</em> time for the lease, and the <em>actual
* expiration</em> time granted when the lease is created or last
* renewed. The desired expiration represents when the client would like
* the lease to expire. The actual expiration represents when the lease
* is going to expire if it is not renewed. Both time values are
* absolute times, not relative time durations. The desired expiration
* time can be retrieved using the renewal manager's
* <code>getExpiration</code> method. The actual expiration time of a
* lease object can be retrieved by invoking the lease's
* <code>getExpiration</code> method.
* <p>
* Each lease in the managed set also has two other associated
* attributes: a desired <em>renewal duration</em>, and a <em>remaining
* desired duration</em>. The desired renewal duration is specified
* (directly or indirectly) when the lease is added to the set. This
* duration must normally be a positive number; however, it may be
* <code>Lease.ANY</code> if the lease's desired expiration is
* <code>Lease.FOREVER</code>. The remaining desired duration is always
* the desired expiration less the current time.
* <p>
* Each time a lease is renewed, the renewal manager will ask for an
* extension equal to the lease's renewal duration if the renewal
* duration is:
* <ul>
* <li> <code>Lease.ANY</code>, or
* <li> less than the remaining desired duration,
* </ul>
* otherwise it will ask for an extension equal to the lease's remaining
* desired duration.
* <p>
* Once a lease is given to a lease renewal manager, the manager will
* continue to renew the lease until one of the following occurs:
* <ul>
* <li> The lease's desired or actual expiration time is reached.
* <li> An explicit removal of the lease from the set is requested via a
<code>cancel</code>, <code>clear</code>, or <code>remove</code>
*  call on the renewal manager.
* <li> The renewal manager tries to renew the lease and gets a bad
*  object exception, bad invocation exception, or
<code>LeaseException</code>.
* </ul>
* <p>
* The methods of this class are appropriately synchronized for
* concurrent operation. Additionally, this class makes certain
* guarantees with respect to concurrency. When this class makes a
* remote call (for example, when requesting the renewal of a lease),
* any invocations made on the methods of this class will not be
* blocked. Similarly, this class makes a reentrancy guarantee with
* respect to the listener objects registered with this class. Should
* this class invoke a method on a registered listener (a local call),
* calls from that method to any other method of this class are
* guaranteed not to result in a deadlock condition.
*
* @author Sun Microsystems, Inc.
* @see Lease
* @see LeaseException
* @see LeaseRenewalEvent
*
* @com.sun.jini.impl <!-- Implementation Specifics -->
*
* The following implementation-specific items are discussed below:
* <ul>
* <li><a href="#configEntries">Configuring LeaseRenewalManager</a>
* <li><a href="#logging">Logging</a>
* <li><a href="#algorithm">The renewal algorithm</a>
* </ul>
*
* <a name="configEntries">
* <p><b><font size="+1">Configuring LeaseRenewalManager</font></b><p>
* </a>
*
* This implementation of <code>LeaseRenewalManager</code> supports the
* following configuration entries, with component
* <code>net.jini.lease.LeaseRenewalManager</code>:
*
* <table summary="Describes the renewBatchTimeWindow 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>
*   renewBatchTimeWindow</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>5 * 60 * 1000 // 5 minutes</code>
*   <tr valign="top"> <td> &nbsp <th scope="row" align="right">
*     Description: <td> The maximum number of milliseconds earlier than
*     a lease would typically be renewed to allow it to be renewed in
*     order to permit batching its renewal with that of other
*     leases. The value must not be negative. This entry is obtained
*     in the constructor.
* </table>
* <table summary="Describes the roundTripTime 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>
*   roundTripTime</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>10 * 1000 // 10 seconds</code>
*   <tr valign="top"> <td> &nbsp <th scope="row" align="right">
*     Description: <td> The worst-case latency, expressed in milliseconds,
*     to assume for a remote call to renew a lease. The value must be greater
*     than zero. Unrealistically low values for this entry may
*     result in failure to renew a lease. Leases managed by this manager
*     should have durations exceeding the <code>roundTripTime</code>.
*     This entry is obtained in the constructor.
* </table>
* <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 TaskManager}
*   <tr valign="top"> <td> &nbsp <th scope="row" align="right">
*     Default: <td> <code>new TaskManager(11, 15000, 1.0f)</code>
*   <tr valign="top"> <td> &nbsp <th scope="row" align="right">
*     Description: <td> The object used to manage queuing tasks
*     involved with renewing leases and sending notifications. The
*     value must not be <code>null</code>. The default value creates
*     a maximum of 11 threads for performing operations, waits 15
*     seconds before removing idle threads, and uses a load factor of
*     1.0 when determining whether to create a new thread. Note that
*     the implementation of the renewal algorithm includes an assumption
*     that the <code>TaskManager</code> uses a load factor of 1.0.
* </table>
*
* <a name="logging">
* <p><b><font size="+1">Logging</font></b><p>
* </a>
*
* This implementation uses the {@link Logger} named
* <code>net.jini.lease.LeaseRenewalManager</code> to log information at
* the following logging levels: <p>
*
* <table border="1" cellpadding="5"
*    summary="Describes logging performed by the
*       LeaseRenewalManager at different logging levels">
*
* <caption halign="center" valign="top"><b><code>
*      net.jini.lease.LeaseRenewalManager</code></b></caption>
*
* <tr> <th scope="col"> Level <th scope="col"> Description
*
* <tr> <td> {@link Levels#FAILED FAILED}
<td> Lease renewal failure events, or leases that expire before
*           reaching the desired expiration time
*
* <tr> <td> {@link Levels#HANDLED HANDLED}
<td> Lease renewal attempts that produce indefinite exceptions
*
* <tr> <td> {@link Level#FINE FINE}
<td> Adding and removing leases, lease renewal attempts, and desired
*       lease expiration events
*
* </table> <p>
*
* For a way of using the <code>FAILED</code> and <code>HANDLED</code> logging
* levels in standard logging configuration files, see the {@link LogManager}
* class.
*
* <a name="algorithm">
* <p><b><font size="+1">The renewal algorithm</font></b><p>
* </a>
* The time at which a lease is scheduled for renewal is based on the
* expiration time of the lease, possibly adjusted to account for the
* latency of the remote renewal call. The configuration entry
* <code>roundTripTime</code>, which defaults to ten seconds, represents
* the total time to make the remote call.
* <p>
* The following pseudocode was derived from the code which computes
* the renewal time. In this code, <code>rtt</code> represents the
* value of the <code>roundTripTime</code>:
*
* <pre>   
*          endTime = lease.getExpiration();
*          delta = endTime - now;
*          if (delta <= rtt * 2) {
*          delta = rtt;
*          } else if (delta <= rtt * 8) {
*          delta /= 2;
*          } else if (delta <= 1000 * 60 * 60 * 24 * 7) {
*          delta /= 8;
*          } else if (delta <= 1000 * 60 * 60 * 24 * 14) {
*          delta = 1000 * 60 * 60 * 24;
*          } else {
*          delta = 1000 * 60 * 60 * 24 * 3;
*          }
*          renew = endTime - delta;
*</pre>
*
* It is important to note that <code>delta</code> is never less than
* <code>rtt</code> when the renewal time is computed. A lease which
* would expire within this time range will be scheduled for immediate
* renewal. The use of very short lease durations (at or below <code>rtt</code>)
* can cause the renewal manager to effectively ignore the lease duration
* and repeatedly schedule the lease for immediate renewal.
* <p>
* If an attempt to renew a lease fails with an indefinite exception, a
* renewal is rescheduled with an updated renewal time as computed by the
* following pseudocode:
*
* <pre>
*          delta = endTime - renew;
*          if (delta > rtt) {
*              if (delta <= rtt * 3) {
*              delta = rtt;
*              } else if (delta <= 1000 * 60 * 60) {
*              delta /= 3;
*              } else if (delta <= 1000 * 60 * 60 * 24) {
*              delta = 1000 * 60 * 30;
*              } else if (delta <= 1000 * 60 * 60 * 24 * 7) {
*              delta = 1000 * 60 * 60 * 3;
*              } else {
*              delta = 1000 * 60 * 60 * 8;
*              }
*              renew += delta;
*          }
* </pre>
*
* Client leases are maintained in a collection sorted by descending renewal
* time. A renewal thread is spawned whenever the renewal time of the last lease
* in the collection is reached. This renewal thread examines all of the leases
* in the collection whose renewal time falls within
* <code>renewBatchTimeWindow</code> milliseconds of the renewal time of the
* last lease. If any of these leases can be batch renewed with the last lease (as
* determined by calling the {@link Lease#canBatch canBatch} method of
* the last lease) then a {@link LeaseMap} is created, all eligible leases
* are added to it and the {@link LeaseMap#renewAll} method is called. Otherwise, the
* last lease is renewed directly.
* <p>
* The <code>TaskManager</code> that manages the renewal threads has a bound on
* the number of simultaneous threads it will support. The renewal time of
* leases may be adjusted earlier in time to reduce the likelihood that the
* renewal of a lease will be delayed due to exhaustion of the thread pool.
* Actual renewal times are determined by starting with the lease with the
* latest (farthest off) desired renewal time and working backwards.  When
* computing the actual renewal time for a lease, the renewals of all leases
* with later renewal times, which will be initiated during the round trip time
* of the current lease's renewal, are considered.  If using the desired
* renewal time for the current lease would result in more in-progress renewals
* than the number of threads allowed, the renewal time of the current lease is
* shifted earlier in time, such that the maximum number of threads is not
* exceeded.
*
*/

public class LeaseRenewalManager {

    private static final String LRM = "net.jini.lease.LeaseRenewalManager";

    private static final Logger logger = Logger.getLogger(LRM);

    /* Method objects for manipulating method constraints */
    private static final Method cancelMethod;
    private static final Method cancelAllMethod;
    private static final Method renewMethod;
    private static final Method renewAllMethod;
    static {
  try {
      cancelMethod = Lease.class.getMethod(
    "cancel", new Class[] { });
      cancelAllMethod = LeaseMap.class.getMethod(
    "cancelAll", new Class[] { });
      renewMethod = Lease.class.getMethod(
    "renew", new Class[] { long.class });
      renewAllMethod = LeaseMap.class.getMethod(
    "renewAll", new Class[] { });
  } catch (NoSuchMethodException e) {
      throw new NoSuchMethodError(e.getMessage());
  }
    }

    /* Methods for comparing lease constraints. */
    private static final Method[] leaseToLeaseMethods = {
  cancelMethod, cancelMethod, renewMethod, renewMethod
    };

    /* Methods for converting lease constraints to lease map constraints. */
    private static final Method[] leaseToLeaseMapMethods = {
  cancelMethod, cancelAllMethod, renewMethod, renewAllMethod
    };

    /** Time window in which to look for batchable leases */
    private long renewBatchTimeWindow = 1000 * 60 * 5;

    /** Task manager for queuing and renewing leases */
    TaskManager taskManager = new TaskManager(11, 1000 * 15, 1.0f);

    /**
     * The worst-case renewal round-trip-time
     */
    private static long renewalRTT = 10 * 1000;

    /**
     * Entries for leases that are not actively being renewed.
     * Lease with the earliest renewal is last in the map.
     */
    private final SortedMap leases = new TreeMap();

    /** Entries for leases that are actively being renewed */
    private final List leaseInRenew = new ArrayList(1);
    /** The queuer task */
    private QueuerTask queuer = null;

    /**
     * Used to determine concurrency constraints when calculating actual
     * renewals.  The list is stored in a field to avoid reallocating it.
     */
    private List calcList;

    private final class RenewTask implements TaskManager.Task {
  /** Entries of leases to renew (if multiple, all can be batched) */
  private final List bList;

  /**
   * True if this task only holds leases that have reached their
   * actual or desired expiration
   */
  private final boolean noRenewals;

  /**
   * Create a collection of entries whose leases can be batch
   * renewed with the last lease in the map, or a list of entries
   * whose leases need to be removed.  Which is created depends on
   * the state of the last lease in the map.  Remove each entry
   * from the map, and add them to leaseInRenew.
   */
  RenewTask(long now) {
      bList = new ArrayList(1);
      Entry e = (Entry) leases.lastKey();

      if (e.renewalsDone() || e.endTime <= now) {
    noRenewals = true;
    Map lMap = leases.tailMap(new Entry(now));
    for (Iterator iter = lMap.values().iterator();
         iter.hasNext(); )
    {
        Entry be = (Entry) iter.next();
        if (be.renewalsDone() || be.endTime <= now) {
      iter.remove();
      logExpiration(be);
      /*
       * Only add to bList if we need to tell someone
       * about this lease's departure
       */
      if (be.listener != null)
          bList.add(be);
        }
    }
      } else {
    noRenewals = false;
    Map lMap = leases.tailMap(
        new Entry(e.renew + renewBatchTimeWindow));
    for (Iterator iter = lMap.values().iterator();
         iter.hasNext(); )
    {
        Entry be = (Entry) iter.next();
        if (be == e || be.canBatch(e)) {
      iter.remove();
      leaseInRenew.add(be);
      bList.add(be);
        }
    }
      }
  }

  public void run() {
      if (noRenewals) {
    // Just notify
    tell(bList);
      } else {
    /*
     * Get rid of any leases that have expired and then do
     * renewals
     */
    long now = System.currentTimeMillis();
    List bad = processBadLeases(now);
    if (!bList.isEmpty())
        renewAll(bList, now);
    if (bad != null)
        tell(bad);
      }
  }

  /** No ordering. */
  public boolean runAfter(List tasks, int size) {
      return false;
  }

  /**
   * Find any expired leases, remove them from bList and
   * leaseInRenew, and return any with listeners.
   */
  private List processBadLeases(long now) {
      List bad = null;
      synchronized (LeaseRenewalManager.this) {
    for (Iterator iter = bList.iterator(); iter.hasNext(); ) {
        Entry e = (Entry) iter.next();
        if (e.endTime <= now) {
      iter.remove();
      logExpiration(e);
      removeLeaseInRenew(e);
      if (e.listener != null) {
          if (bad == null)
        bad = new ArrayList(1);
          bad.add(e);
      }
        }
    }
      }
      return bad;
  }
    }

    private static class Entry implements Comparable {
  /*
   * Since the cnt only gets modified in the constructor, and the
   * constructor is always called from synchronized code, the cnt
   * does not need to be synchronized.
   */
  private static long cnt = 0;

  /** Unique id */
  public final long id;
  /** The lease */
  public final Lease lease;
  /** Desired expiration */
  public long expiration;
  /** Renew duration */
  public long renewDuration;
  /** The listener, or null */
  public final LeaseListener listener;
  /** Current actual expiration */
  public long endTime;

  /**
   * The next time we have to do something with this lease.
   * Usually a renewal, but could be removing it from the managed
   * set because its desired expiration has been reached.
   */
  public long renew;

  /** Actual time to renew, given concurrency limitations */
  public long actualRenew;
  /** Renewal exception, or null */
  public Throwable ex = null;

  public Entry(Lease lease,
         long expiration,
         long renewDuration,
         LeaseListener listener)
  {
      this.endTime = lease.getExpiration();
      this.lease = lease;
      this.expiration = expiration;
      this.renewDuration = renewDuration;
      this.listener = listener;
      id = cnt++;
  }

  /** Create a fake entry for tailMap */
  public Entry(long renew) {
      this.renew = renew;
      id = Long.MAX_VALUE;
      lease = null;
      listener = null;
  }

  /**
   * If the renewDuration is ANY, return ANY, otherwise return the
   * minimum of the renewDuration and the time remaining until the
   * desired expiration.
   */
  public long getRenewDuration(long now) {
      if (renewDuration == Lease.ANY)
    return renewDuration;
      return Math.min(expiration - now, renewDuration);
  }

  /** Calculate the renew time for the lease entry */
  public void calcRenew(long now) {
      endTime = lease.getExpiration();
      if (renewalsDone()) {
    if (null == desiredExpirationListener()) {
        // Nothing urgent needs to be done with this lease
        renew = Long.MAX_VALUE;
    } else {
        /*
         * Tell listener about dropping this lease in a
         * timely fashion
         */
        renew = expiration;
    }
    return;
      }
      long delta = endTime - now;
      if (delta <= renewalRTT * 2) {
    delta = renewalRTT;
      } else if (delta <= renewalRTT * 8) {
    delta /= 2;
      } else if (delta <= 1000 * 60 * 60 * 24 * 7) {
    delta /= 8;
      } else if (delta <= 1000 * 60 * 60 * 24 * 14) {
    delta = 1000 * 60 * 60 * 24;
      } else {
    delta = 1000 * 60 * 60 * 24 * 3;
      }
      renew = endTime - delta;
  }

  /** Calculate a new renew time due to an indefinite exception */
  public void delayRenew() {
      long delta = endTime - renew;
      if (delta <= renewalRTT) {
    return;
      } else if (delta <= renewalRTT * 3) {
     delta = renewalRTT;
      } else if (delta <= 1000 * 60 * 60) {
    delta /= 3;
      } else if (delta <= 1000 * 60 * 60 * 24) {
    delta = 1000 * 60 * 30;
      } else if (delta <= 1000 * 60 * 60 * 24 * 7) {
    delta = 1000 * 60 * 60 * 3;
      } else {
    delta = 1000 * 60 * 60 * 8;
      }
      renew += delta;
  }

  /** Sort by decreasing renew time, secondary sort by decreasing id */
  public int compareTo(Object obj) {
      if (this == obj)
    return 0;
      Entry e = (Entry) obj;
      if (renew < e.renew || (renew == e.renew && id < e.id))
    return 1;
      return -1;
  }

  /**
   * Returns true if the renewal of this lease can be batched with
   * the (earlier) renewal of the given lease.  This method must
   * be called with an entry such that e.renew <= this.renew. <p>
   *
   * First checks that both leases require renewal, have the same
   * client constraints, and can be batched.  Then enforces
   * additional requirements to avoid renewing the lease too much
   * more often than necessary. <p>
   *
   * One of the following must be true: <ul>
   *
   * <li> This lease has a renewal duration of Lease.ANY, meaning
   * it doesn't specify its renewal duration.
   *
   * <li> The amount of time from the other lease's renewal time
   * to this one's is less than half of the estimated time needed
   * to perform renewals (renewalRTT).  In this case, the renewal
   * times are so close together that the renewal duration
   * shouldn't be materially affected.
   *
   * <li> This lease's expiration time is no more than half its
   * renewal duration greater than the renewal time of the other
   * lease.  This case insures that this lease is not renewed
   * until at least half of it's renewal duration has
   * elapsed. </ul> <p>
   *
   * In addition, one of the following must be true: <ul>
   *
   * <li> The other lease has a renewal duration of Lease.ANY,
   * meaning we don't know how long its next renewal will be.
   *
   * <li> The other lease is not going to be renewed again before
   * this lease's renewal time, because either its next renewal
   * will last until after this lease's renewal time or it will
   * only be renewed once more. </ul>
   */
  public boolean canBatch(Entry e) {
      return (!renewalsDone() &&
        !e.renewalsDone() &&
        sameConstraints(lease, e.lease) &&
        lease.canBatch(e.lease) &&
        (renewDuration == Lease.ANY ||
         renew - e.renew <= renewalRTT / 2 ||
         endTime - e.renew <= renewDuration / 2) &&
        (e.renewDuration == Lease.ANY ||
         e.renew > renew - e.renewDuration ||
         e.renew >= e.expiration - e.renewDuration));
  }

  /**
   * Returns true if the two leases both implement RemoteMethodControl
   * and have the same constraints for Lease methods, or both don't
   * implement RemoteMethodControl, else returns false.
   */
  private static boolean sameConstraints(Lease l1, Lease l2) {
      if (!(l1 instanceof RemoteMethodControl)) {
    return !(l2 instanceof RemoteMethodControl);
      } else if (!(l2 instanceof RemoteMethodControl)) {
    return false;
      } else {
    return ConstrainableProxyUtil.equivalentConstraints(
        ((RemoteMethodControl) l1).getConstraints(),
        ((RemoteMethodControl) l2).getConstraints(),
        leaseToLeaseMethods);
      }
  }

  /**
   * Return the DesiredExpirationListener associated with this
   * lease, or null if there is none.
   */
  public DesiredExpirationListener desiredExpirationListener() {
      if (listener == null)
    return null;

      if (listener instanceof DesiredExpirationListener)
    return (DesiredExpirationListener) listener;

      return null;
  }

  /**
   * Return true if the actual expiration is greater than or equal
   * to the desired expiration (e.g. we don't need to renew this
   * lease any more.
   */
  public boolean renewalsDone() {
      return expiration <= endTime;
  }
    }

    /**
     * No-argument constructor that creates an instance of this class
     * that initially manages no leases.
     */
    public LeaseRenewalManager() {
    }

    /**
     * Constructs an instance of this class that initially manages no leases
     * and that uses <code>config</code> to control implementation-specific
     * details of the behavior of the instance created.
     *
     * @param config supplies entries that control the configuration of this
     *        instance
     * @throws ConfigurationException if a problem occurs when obtaining
     *         entries from the configuration
     * @throws NullPointerException if the configuration is <code>null</code>
     */
    public LeaseRenewalManager(Configuration config)
  throws ConfigurationException
    {
  if (config == null) {
      throw new NullPointerException("config is null");
  }
  renewBatchTimeWindow = Config.getLongEntry(
      config, LRM, "renewBatchTimeWindow",
      renewBatchTimeWindow, 0, Long.MAX_VALUE);
  renewalRTT = Config.getLongEntry(
      config, LRM, "roundTripTime",
      renewalRTT, 1, Long.MAX_VALUE);
  taskManager = (TaskManager) Config.getNonNullEntry(
      config, LRM, "taskManager", TaskManager.class, taskManager);
    }

    /**
     * Constructs an instance of this class that will initially manage a
     * single lease. Employing this form of the constructor is
     * equivalent to invoking the no-argument form of the constructor
     * followed by an invocation of the three-argument form of the
     * <code>renewUntil</code> method. See <code>renewUntil</code> for
     * details on the arguments and what exceptions may be thrown by
     * this constructor.
     *
     * @param lease reference to the initial lease to manage
     * @param desiredExpiration the desired expiration for
     *        <code>lease</code>
     * @param listener reference to the <code>LeaseListener</code>
     *        object that will receive notifications of any exceptional
     *        conditions that occur during renewal attempts. If
     *        <code>null</code> no notifications will be sent.
     * @throws NullPointerException if <code>lease</code> is
     *         <code>null</code>
     * @see LeaseListener
     * @see #renewUntil
     */
    public LeaseRenewalManager(Lease lease,
             long desiredExpiration,
             LeaseListener listener)
    {
  renewUntil(lease, desiredExpiration, listener);
    }

    /**
     * Include a lease in the managed set until a specified time.
     * <p>
     * If <code>desiredExpiration</code> is <code>Lease.ANY</code>
     * calling this method is equivalent the following call:
     * <pre>
     *     renewUntil(lease, Lease.FOREVER, Lease.ANY, listener)
     * </pre>
     * otherwise it is equivalent to this call:
     * <pre>
     *     renewUntil(lease, desiredExpiration, Lease.FOREVER, listener)
     * </pre>
     * <p>
     * @param lease the <code>Lease</code> to be managed
     * @param desiredExpiration when the client wants the lease to
     *        expire, in milliseconds since the beginning of the epoch
     * @param listener reference to the <code>LeaseListener</code>
     *        object that will receive notifications of any exceptional
     *        conditions that occur during renewal attempts. If
     *        <code>null</code> no notifications will be sent.
     * @throws NullPointerException if <code>lease</code> is
     *         <code>null</code>
     * @see #renewUntil
     */
    public void renewUntil(Lease lease,
         long desiredExpiration,
         LeaseListener listener)
    {
  if (desiredExpiration == Lease.ANY) {
      renewUntil(lease, Lease.FOREVER, Lease.ANY, listener);
  } else {
      renewUntil(lease, desiredExpiration, Lease.FOREVER, listener);
  }
    }

    /**
     * Include a lease in the managed set until a specified time and
     * with a specified renewal duration.
     * <p>
     * This method takes as arguments: a reference to the lease to
     * manage, the desired expiration time of the lease, the renewal
     * duration time for the lease, and a reference to the
     * <code>LeaseListener</code> object that will receive notification
     * of exceptional conditions when attempting to renew this
     * lease. The <code>LeaseListener</code> argument may be
     * <code>null</code>.
     * <p>
     * If the <code>lease</code> argument is <code>null</code>, a
     * <code>NullPointerException</code> will be thrown. If the
     * <code>desiredExpiration</code> argument is
     * <code>Lease.FOREVER</code>, the <code>renewDuration</code>
     * argument may be <code>Lease.ANY</code> or any positive value;
     * otherwise, the <code>renewDuration</code> argument must be a
     * positive value. If the <code>renewDuration</code> argument does
     * not meet these requirements, an
     * <code>IllegalArgumentException</code> will be thrown.
     * <p>
     * If the lease passed to this method is already in the set of
     * managed leases, the listener object, the desired expiration, and
     * the renewal duration associated with that lease will be replaced
     * with the new listener, desired expiration, and renewal duration.
     * <p>
     * The lease will remain in the set until one of the following
     * occurs:
     * <ul>
     * <li> The lease's desired or actual expiration time is reached.
     * <li> An explicit removal of the lease from the set is requested
     *      via a <code>cancel</code>, <code>clear</code>, or
     *      <code>remove</code> call on the renewal manager.
     * <li> The renewal manager tries to renew the lease and gets a bad
     *      object exception, bad invocation exception, or
     *      <code>LeaseException</code>.
     * </ul>
     * <p>
     * This method will interpret the value of the
     * <code>desiredExpiration</code> argument as the desired absolute
     * system time after which the lease is no longer valid. This
     * argument provides the ability to indicate an expiration time that
     * extends beyond the actual expiration of the lease. If the value
     * passed for this argument does indeed extend beyond the lease's
     * actual expiration time, then the lease will be systematically
     * renewed at appropriate times until one of the conditions listed
     * above occurs. If the value is less than or equal to the actual
     * expiration time, nothing will be done to modify the time when the
     * lease actually expires. That is, the lease will not be renewed
     * with an expiration time that is less than the actual expiration
     * time of the lease at the time of the call.
     * <p>
     * If the <code>LeaseListener</code> argument is a
     * non-<code>null</code> object reference, it will receive
     * notification of exceptional conditions occurring upon a renewal
     * attempt of the lease. In particular, exceptional conditions
     * include the reception of a <code>LeaseException</code>, bad
     * object exception, or bad invocation exception (collectively these
     * are referred to as <em>definite exceptions</em>) during a renewal
     * attempt or the lease's actual expiration being reached before its
     * desired expiration.
     * <p>
     * If a definite exception occurs during a lease renewal request,
     * the exception will be wrapped in an instance of the
     * <code>LeaseRenewalEvent</code> class and sent to the listener.
     * <p>
     * If an indefinite exception occurs during a renewal request for
     * the lease, renewal requests will continue to be made for that
     * lease until: the lease is renewed successfully, a renewal attempt
     * results in a definite exception, or the lease's actual expiration
     * time has been exceeded. If the lease cannot be successfully
     * renewed before its actual expiration is reached, the exception
     * associated with the most recent renewal attempt will be wrapped
     * in an instance of the <code>LeaseRenewalEvent</code> class and
     * sent to the listener.
     * <p>
     * If the lease's actual expiration is reached before the lease's
     * desired expiration time, and either 1) the last renewal attempt
     * succeeded or 2) there have been no renewal attempts, a
     * <code>LeaseRenewalEvent</code> containing a <code>null</code>
     * exception will be sent to the listener.
     *
     * @param lease the <code>Lease</code> to be managed
     * @param desiredExpiration when the client wants the lease to
     *        expire, in milliseconds since the beginning of the epoch
     * @param renewDuration the renewal duration to associate with the
     *        lease, in milliseconds
     * @param listener reference to the <code>LeaseListener</code>
     *        object that will receive notifications of any exceptional
     *        conditions that occur during renewal attempts. If
     *        <code>null</code>, no notifications will be sent.
     * @throws NullPointerException if <code>lease</code> is
     *         <code>null</code>
     * @throws IllegalArgumentException if <code>renewDuration</code> is
     *         invalid
     * @see LeaseRenewalEvent
     * @see LeaseException
     */
    public void renewUntil(Lease lease,
         long desiredExpiration,
         long renewDuration,
         LeaseListener listener)
    {
  validateDuration(renewDuration, desiredExpiration == Lease.FOREVER,
       "desiredExpiration");
  addLease(lease, desiredExpiration, renewDuration, listener,
     System.currentTimeMillis());
    }

    /**
     * Include a lease in the managed set for a specified duration.
     * <p>
     * Calling this method is equivalent the following call:
     * <pre>
     *     renewFor(lease, desiredDuration, Lease.FOREVER, listener)
     * </pre>
     *
     * @param lease reference to the new lease to manage
     * @param desiredDuration the desired duration (relative time) that
     *        the caller wants <code>lease</code> to be valid for, in
     *        milliseconds
     * @param listener reference to the <code>LeaseListener</code>
     *        object that will receive notifications of any exceptional
     *        conditions that occur during renewal attempts. If
     *        <code>null</code>, no notifications will be sent.
     * @throws NullPointerException if <code>lease</code> is
     *         <code>null</code>
     * @see #renewFor
     */
    public void renewFor(Lease lease, long desiredDuration,
       LeaseListener listener)
    {
  renewFor(lease, desiredDuration, Lease.FOREVER, listener);
    }  

    /**
     * Include a lease in the managed set for a specified duration and
     * with specified renewal duration.
     * <p>
     * The semantics of this method are similar to those of the
     * four-argument form of <code>renewUntil</code>, with
     * <code>desiredDuration</code> + current time being used for the
     * value of the <code>desiredExpiration</code> argument of
     * <code>renewUntil</code>. The only exception to this is that, in
     * the context of <code>renewFor</code>, the value of the
     * <code>renewDuration</code> argument may only be
     * <code>Lease.ANY</code> if the value of the
     * <code>desiredDuration</code> argument is <em>exactly</em>
     * <code>Lease.FOREVER.</code>
     * <p>
     * This method tests for arithmetic overflow in the desired
     * expiration time computed from the value of
     * <code>desiredDuration</code> argument
     * (<code>desiredDuration</code> + current time). Should such
     * overflow be present, a value of <code>Lease.FOREVER</code> is
     * used to represent the lease's desired expiration time.
     *
     * @param lease reference to the new lease to manage
     * @param desiredDuration the desired duration (relative time) that
     *        the caller wants <code>lease</code> to be valid for, in
     *        milliseconds
     * @param renewDuration the renewal duration to associate with the
     *        lease, in milliseconds
     * @param listener reference to the <code>LeaseListener</code>
     *        object that will receive notifications of any exceptional
     *        conditions that occur during renewal attempts. If
     *        <code>null</code>, no notifications will be sent.
     * @throws NullPointerException if <code>lease</code> is
     *         <code>null</code>
     * @throws IllegalArgumentException if <code>renewDuration</code> is
     *         invalid
     * @see #renewUntil
     */
    public void renewFor(Lease lease,
       long desiredDuration,
       long renewDuration,
       LeaseListener listener)
    {
  /*
   * Validate before calculating effective desiredExpiration, if
   * they want a renewDuration of Lease.ANY, desiredDuration has
   * to be exactly Lease.FOREVER
   */
  validateDuration(renewDuration, desiredDuration == Lease.FOREVER,
       "desiredDuration");

  long now = System.currentTimeMillis();
  long desiredExpiration;
  if (desiredDuration < Lease.FOREVER - now) { // check overflow.
      desiredExpiration = now + desiredDuration;
  } else {
      desiredExpiration = Lease.FOREVER;
  }
  addLease(lease, desiredExpiration, renewDuration, listener, now);
    }

    /**
     * Error checking function that ensures renewDuration is valid taking
     * into account the whether or not the desired expiration/duration is
     * Lease.FOREVER. Throws an appropriate IllegalArgumentException if
     * an invalid renewDuration is passed.
     *
     * @param renewDuration renew duration the clients wants
     * @param isForever should be true if client asked for a desired
     *        expiration/duration of exactly Lease.FOREVER
     * @param name name of the desired expiration/duration field, used
     *        to construct exception
     * @throws IllegalArgumentException if renewDuration is invalid
     */
    private void validateDuration(long renewDuration, boolean isForever,
          String name)
    {
  if (renewDuration <= 0 &&
      !(renewDuration == Lease.ANY && isForever))
  {
      /*
       * A negative renew duration and is not lease.ANY with a
       * forever desired expiration
       */
      if (renewDuration == Lease.ANY) {
    /*
     * Must have been Lease.ANY with a non-FOREVER desired
     * expiration
     */
    throw new IllegalArgumentException("A renewDuration of " +
         "Lease.ANY can only be used with a " + name + " of " +
         "Lease.FOREVER");
      }

      if (isForever) {
    // Must have been a non-Lease.ANY, non-positive renewDuration
    throw new IllegalArgumentException("When " + name + " is " +
        "Lease.FOREVER the only valid values for renewDuration " +
        "are a positive number, Lease.ANY, or Lease.FOREVER");
      }

      /*
       * Must be a non-positive renewDuration with a non-Forever
       * desired expiration
       */
      throw new IllegalArgumentException("When the " + name +
    " is not Lease.FOREVER the only valid values for " +
    "renewDuration are a positive number or Lease.FOREVER");
  }
    }

    private synchronized void addLease(Lease lease,
               long desiredExpiration,
               long renewDuration,
               LeaseListener listener,
               long now)
    {     
  Entry e = findEntryDo(lease);
  if (e != null && !removeLeaseInRenew(e))
      leases.remove(e);
  insertEntry(new Entry(lease, desiredExpiration, renewDuration,
            listener),
        now);
  calcActualRenews(now);
  logger.log(Level.FINE, "Added lease {0}", lease);
    }

    /** Calculate the preferred renew time, and put in the map */
    private void insertEntry(Entry e, long now) {
  e.calcRenew(now);
  leases.put(e, e);
    }

    /**
     * Returns the current desired expiration time associated with a
     * particular lease, (not the actual expiration that was granted
     * when the lease was created or last renewed).
     *
     * @param lease the lease the caller wants the current desired
     *        expiration for
     * @return a <code>long</code> value corresponding to the current
     *         desired expiration time associated with <code>lease</code>
     * @throws UnknownLeaseException if the lease passed to this method
     *         is not in the set of managed leases
     * @see UnknownLeaseException
     * @see #setExpiration
     */
    public synchronized long getExpiration(Lease lease)
  throws UnknownLeaseException
    {
  return findEntry(lease).expiration;
    }

    /**
     * Replaces the current desired expiration of a given lease from the
     * managed set with a new desired expiration time.
     * <p>
     * Note that an invocation of this method with a lease that is
     * currently a member of the managed set is equivalent to an
     * invocation of the <code>renewUntil</code> method with the lease's
     * current listener as that method's <code>listener</code>
     * argument. Specifically, if the value of the
     * <code>expiration</code> argument is less than or equal to the
     * lease's current desired expiration, this method takes no action.
     *
     * @param lease the lease whose desired expiration time should be
     *        replaced
     * @param expiration <code>long</code> value representing the new
     *        desired expiration time for the <code>lease</code>
     *        argument
     * @throws UnknownLeaseException if the lease passed to this method
     *         is not in the set of managed leases
     * @see #renewUntil
     * @see UnknownLeaseException
     * @see #getExpiration
     */
    public synchronized void setExpiration(Lease lease, long expiration)
  throws UnknownLeaseException
    {
  Entry e = findEntry(lease);
  e.expiration = expiration;
  if (expiration != Lease.FOREVER && e.renewDuration == Lease.ANY)
      e.renewDuration = Lease.FOREVER;
  if (leaseInRenew.indexOf(e) < 0) {
      leases.remove(e);
      long now = System.currentTimeMillis();
      insertEntry(e, now);
      calcActualRenews(now);
  }
    }

    /**
     * Removes a given lease from the managed set, and cancels it.
     * <p>
     * Note that even if an exception is thrown as a result of the
     * cancel operation, the lease will still have been removed from the
     * set of leases managed by this class. Additionally, any exception
     * thrown by the <code>cancel</code> method of the lease object
     * itself may also be thrown by this method.
     *
     * @param lease the lease to remove and cancel
     * @throws UnknownLeaseException if the lease passed to this method
     *         is not in the set of managed leases
     * @throws RemoteException typically, this exception occurs when
     *         there is a communication failure between the client and
     *         the server. When this exception does occur, the lease may
     *         or may not have been successfully cancelled, (but the
     *         lease is guaranteed to have been removed from the managed
     *         set).
     * @see Lease#cancel
     * @see UnknownLeaseException
     */
    public void cancel(Lease lease)
  throws UnknownLeaseException, RemoteException
    {
  remove(lease);
  lease.cancel();
    }

    /**
     * Removes a given lease from the managed set of leases; but does
     * not cancel the given lease.
     *
     * @param lease the lease to remove from the managed set
     * @throws UnknownLeaseException if the lease passed to this method
     *         is not in the set of managed leases
     * @see UnknownLeaseException
     */
    public synchronized void remove(Lease lease) throws UnknownLeaseException {
  Entry e = findEntry(lease);
  if (!removeLeaseInRenew(e))
      leases.remove(e);
  calcActualRenews();
  logger.log(Level.FINE, "Removed lease {0}", lease);
    }

    /**
     * Removes all leases from the managed set of leases. This method
     * does not request the cancellation of the removed leases.
     */
    public synchronized void clear() {
  leases.clear();
  leaseInRenew.clear();
  calcActualRenews();
  logger.log(Level.FINE, "Removed all leases");
    }

    /** Calculate the actual renew times, and poke/restart the queuer */
    private void calcActualRenews() {
  calcActualRenews(System.currentTimeMillis());
    }

    /** Calculate the actual renew times, and poke/restart the queuer */
    private void calcActualRenews(long now) {
  /*
   * Subtract one to account for the queuer thread, which should not be
   * counted.
   */
  int maxThreads = taskManager.getMaxThreads() - 1;
  if (calcList == null) {
      calcList = new ArrayList(maxThreads);
  }
  for (Iterator iter = leases.values().iterator(); iter.hasNext(); ) {
      Entry e = (Entry) iter.next();

      // Start by assuming we can renew the lease when we want
      e.actualRenew = e.renew;

      if (e.renewalsDone()) {
    /*
     * The lease's actual expiration is >= desired
     * expiration, drop the lease if the desired expiration
     * has been reached and we don't have to tell anyone
     * about it
     */
    if (now >= e.expiration &&
        e.desiredExpirationListener() == null)
    {
        logExpiration(e);
        iter.remove();
    }

    /*
     * Even if we have to send an event we assume that it
     * won't consume a slot in our schedule
     */
    continue;
      }

      if (e.endTime <= now && e.listener == null) {
    // Lease has expired and no listener, just remove it.
    logExpiration(e);
    iter.remove();
    continue;
      }

      /*
       * Make sure there aren't too many lease renewal threads
       * operating at the same time.
       */
      if (!canBatch(e)) {
    /*
     * Find all renewals that start before we expect ours to
     * be done.
     */
    for (Iterator listIter = calcList.iterator();
         listIter.hasNext(); )
    {
        if (e.renew >=
      ((Entry) listIter.next()).actualRenew - renewalRTT)
        {
      /*
       * This renewal starts after we expect ours to
       * be done.
       */
      break;
        }
        listIter.remove();
    }
    if (calcList.size() == maxThreads) {
        /*
         * Too many renewals.  Move our actual renewal time
         * earlier so we'll probably be done before the last
         * one needs to start.  Remove that one, since it
         * won't overlap any earlier renewals.
         */
        Entry e1 = (Entry) calcList.remove(0);
        e.actualRenew = e1.actualRenew - renewalRTT;
    }
    calcList.add(e);
      }
  }
  calcList.clear();
  long newWakeup = wakeupTime();
  if (queuer == null) {
      if (newWakeup < Long.MAX_VALUE) {
    queuer = new QueuerTask(newWakeup);
    taskManager.add(queuer);
      }
  } else if (newWakeup < queuer.wakeup ||
       (newWakeup == Long.MAX_VALUE && leaseInRenew.isEmpty()))
  {
      notifyAll();
  }
    }

    /**
     * Return true if e can be batched with another entry that expires
     * between e.renew - renewBatchTimeWindow and e.renew.
     */
    private boolean canBatch(Entry e) {
  Iterator iter = leases.tailMap(e).values().iterator();
  iter.next(); // skip e itself
  while (iter.hasNext()) {
      Entry be = (Entry) iter.next();
      if (e.renew - be.renew > renewBatchTimeWindow)
    break;
      if (e.canBatch(be))
    return true;
  }
  return false;
    }

    /**
     * Find a lease entry, throw exception if not found or expired
     * normally
     */
    private Entry findEntry(Lease lease) throws UnknownLeaseException {
  Entry e = findEntryDo(lease);
  if (e != null &&
      (e.renew < e.endTime || System.currentTimeMillis() < e.endTime))
  {
      return e;
  }
  throw new UnknownLeaseException();
    }

    /** Find a lease entry, or null */
    private Entry findEntryDo(Lease lease) {
  Entry e = findLeaseFromIterator(leases.values().iterator(), lease);
  if (e == null)
      e = findLeaseFromIterator(leaseInRenew.iterator(), lease);
  return e;
    }

    /** Find a lease entry, or null */
    private static Entry findLeaseFromIterator(Iterator iter, Lease lease) {
  while (iter.hasNext()) {
      Entry e = (Entry) iter.next();
      if (e.lease.equals(lease))
    return e;
  }
  return null;
    }

    /** Notify the listener for each lease */
    private void tell(List bad) {
  for (Iterator iter = bad.iterator(); iter.hasNext(); ) {
      Entry e = (Entry) iter.next();
      if (e.renewalsDone()) {
    final DesiredExpirationListener del =
        e.desiredExpirationListener();
    if (del != null) {
        del.expirationReached(new LeaseRenewalEvent(this, e.lease,
            e.expiration, null));
    }
    continue;
      }
      e.listener.notify(new LeaseRenewalEvent(this, e.lease,
                e.expiration, e.ex));
  }
    }

    /**
     * Logs a lease expiration, distinguishing between expected
     * and premature expirations.
     *
     * @param e the <code>Entry</code> holding the lease
     */
    private void logExpiration(Entry e) {
  if (e.renewalsDone()) {
      logger.log(Level.FINE,
           "Reached desired expiration for lease {0}",
           e.lease);
  } else {
      logger.log(Levels.FAILED,
           "Lease {0} expired before reaching "
           + "desired expiration of "
           + e.expiration);
  }
    }
   
    /**
     * Logs a throw. Use this method to log a throw when the log message needs
     * parameters.
     *
     * @param level the log level
     * @param sourceMethod name of the method where throw occurred
     * @param msg log message
     * @param params log message parameters
     * @param e exception thrown
     */
    private static void logThrow(Level level,
         String sourceMethod,
         String msg,
         Object[] params,
         Throwable e)
    {
  LogRecord r = new LogRecord(level, msg);
  r.setLoggerName(logger.getName());
  r.setSourceClassName(LeaseRenewalManager.class.getName());
  r.setSourceMethodName(sourceMethod);
  r.setParameters(params);
  r.setThrown(e);
  logger.log(r);
    }

    /** Renew all of the leases (if multiple, all can be batched) */
    private void renewAll(List bList, long now) {
        Map lmeMap = null;
  Throwable t = null;
  List bad = null;

  try {
      if (bList.size() == 1) {
    Entry e = (Entry) bList.get(0);
    logger.log(Level.FINE, "Renewing lease {0}", e.lease);
    e.lease.renew(e.getRenewDuration(now));
      } else {
    LeaseMap batchLeaseMap = createBatchLeaseMap(bList, now);
    logger.log(Level.FINE, "Renewing leases {0}", batchLeaseMap);
    batchLeaseMap.renewAll();
      }
  } catch (LeaseMapException ex) {
      lmeMap = ex.exceptionMap;
      bad = new ArrayList(lmeMap.size());
  } catch (Throwable ex) {
      t = ex;
      bad = new ArrayList(bList.size())// They may all be bad
  }

  /*
   * For each lease we tried to renew determine the associated
   * exception (if any), and then ether add the lease back to
   * leases (if the renewal was successful), schedule a retry and
   * add back to leases (if the renewal was indefinite), or drop
   * the lease (by not adding it back to leases) and notify any
   * interested listeners.  In any event remove lease from the
   * list of leases being renewed.
   */

  now = System.currentTimeMillis();
  synchronized (this) {
      for (Iterator iter = bList.iterator(); iter.hasNext(); ) {
    Entry e = (Entry) iter.next();

    if (!removeLeaseInRenew(e))
        continue;

    // Update the entries exception field
    if (bad == null) {
        e.ex = null;
    } else {
        e.ex = (t != null) ? t : (Throwable) lmeMap.get(e.lease);
    }

    if (e.ex == null) {
        // No problems just put back in list
        insertEntry(e, now);
        continue;
    }

    /*
     * Some sort of problem.  If definite don't put back
     * into leases and setup to notify the appropriate
     * listener, if indefinite schedule for a retry and put
     * back into leases
     */
    final int cat = ThrowableConstants.retryable(e.ex);
    if (cat == ThrowableConstants.INDEFINITE) {
        e.delayRenew();
        leases.put(e, e);
        if (logger.isLoggable(Levels.HANDLED)) {
      logThrow(
          Levels.HANDLED, "renewAll",
          "Indefinite exception while renewing lease {0}",
          new Object[] { e.lease }, e.ex);
        }
    } else {
        if (logger.isLoggable(Levels.FAILED)) {
      logThrow(Levels.FAILED, "renewAll",
         "Lease renewal failed for lease {0}",
         new Object[] { e.lease }, e.ex);
        }
        if (e.listener != null) {
      /*
       * Note: For us ThrowableConstants.UNCATEGORIZED ==
       * definite
       */
      bad.add(e);
       
    } 
      }
      calcActualRenews(now);
  }

  if (bad != null)
      tell(bad)
    }

    /** Create a LeaseMap for batch renewal */
    private static LeaseMap createBatchLeaseMap(List bList, long now) {
  Iterator iter = bList.iterator();
  Entry e = (Entry) iter.next();
  LeaseMap batchLeaseMap =
      e.lease.createLeaseMap(e.getRenewDuration(now));
  if (e.lease instanceof RemoteMethodControl &&
      batchLeaseMap instanceof RemoteMethodControl)
  {
      batchLeaseMap = (LeaseMap)
    ((RemoteMethodControl) batchLeaseMap).setConstraints(
        ConstrainableProxyUtil.translateConstraints(
      ((RemoteMethodControl) e.lease).getConstraints(),
      leaseToLeaseMapMethods));
  }
  while (iter.hasNext()) {
      e = (Entry) iter.next();
      batchLeaseMap.put(e.lease, new Long(e.getRenewDuration(now)));
  }
  return batchLeaseMap;
    }

    /** Remove from leaseInRenew, return true if removed */
    private boolean removeLeaseInRenew(Entry e) {
  int index = leaseInRenew.indexOf(e); // avoid iterator cons
  if (index < 0)
      return false;
  leaseInRenew.remove(index);
  return true;
    }

    /** Return the soonest actual renewal time */
    private long wakeupTime() {
  if (leases.isEmpty())
      return Long.MAX_VALUE;
  return ((Entry) leases.lastKey()).actualRenew;
    }

    private class QueuerTask implements TaskManager.Task {

  /** When to next wake up and queue a new renew task */
  long wakeup;

  QueuerTask(long wakeup) {
      this.wakeup = wakeup;
  }

  /** No ordering */
  public boolean runAfter(List tasks, int size) {
      return false;
  }

        public void run() {
      synchronized (LeaseRenewalManager.this) {
    try {
        while (true) {
      wakeup = wakeupTime();
      if (wakeup == Long.MAX_VALUE && leaseInRenew.isEmpty())
          break;
      final long now = System.currentTimeMillis();
      long delta = wakeup - now;
      if (delta <= 0) {
          taskManager.add(new RenewTask(now));
      } else {
          LeaseRenewalManager.this.wait(delta);
      }
        }
    } catch (InterruptedException ex) {
    }
    queuer = null;
      }
  }
    }
}
TOP

Related Classes of net.jini.lease.LeaseRenewalManager$RenewTask

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.