Package com.sun.jini.jeri.internal.runtime

Source Code of com.sun.jini.jeri.internal.runtime.AbstractDgcClient$EndpointEntry

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.sun.jini.jeri.internal.runtime;

import com.sun.jini.action.GetLongAction;
import com.sun.jini.thread.NewThreadAction;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.rmi.ConnectException;
import java.rmi.ConnectIOException;
import java.rmi.NoSuchObjectException;
import java.rmi.RemoteException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
* AbstractDgcClient implements the client-side behavior of RMI's
* distributed garbage collection system abstractly with respect to
* the types used to represent transport endpoints, object IDs, and
* live remote references.  The actual types used for endpoints,
* object IDs, and live references depends on the concrete subclass.
*
* The entry point into the machinery of AbstractDgcClient is the
* "registerRefs" method: when a live reference enters the scope of
* this AbstractDgcClient (the current virtual machine, for example),
* it should be registered with that method in order for it to
* participate in distributed garbage collection.
*
* When the first live reference to a particular remote object is
* registered, a "dirty" call is made to the server-side distributed
* garbage collector at the remote object's endpoint, which, if
* successful, returns a lease guaranteeing that the server-side DGC
* will not collect the remote object for a certain period of time.
* While live references to remote objects at a particular endpoint
* exist, this AbstractDgcClient will continue to make more "dirty"
* calls to renew its lease on the referenced remote objects.
*
* This AbstractDgcClient tracks the local reachability of registered
* live references (using phantom references).  When all of the live
* reference instances for a particular remote object become garbage
* collected locally, a "clean" call is made to the server-side
* distributed garbage collector, indicating that the server no longer
* needs to keep the remote object alive for this client.
*
* Internally, AbstractDgcClient holds and manipulates transport
* endpoints, object IDs, and live references with references of type
* java.lang.Object; it is assumed that their actual classes define
* "equals" and "hashCode" in a meaningful way.
*
* Concrete subclasses must provide additional behavior for the actual
* endpoint, object ID, and live reference types by implementing the
* abstract protected methods of this class (see below).  In
* particular, the "getDgcProxy" method should return an object that
* implements the actual protocol for DGC "dirty" and "clean" calls
* for the endpoint type being used, the "freeEndpoint" method may
* make use of the indication that a particular endpoint no longer has
* references, and the "getRefEndpoint" and "getRefObjectID" methods
* should return the endpoint and object ID contained in a particular
* live reference object.  A concrete subclass should also provide a
* type-safe equivalent of "registerRefs" that delegates to this
* class's "registerRefs" method.
*
* @author Sun Microsystems, Inc.
**/
abstract class AbstractDgcClient {

    /** lease duration to request (usually ignored by server) */
    private static final long leaseValue =    // default 10 minutes
  ((Long) AccessController.doPrivileged(new GetLongAction(
      "com.sun.jini.jeri.dgc.leaseValue", 600000)))
      .longValue();

    /** maximum interval between retries of failed clean calls */
    private static final long cleanInterval =    // default 3 minutes
  ((Long) AccessController.doPrivileged(new GetLongAction(
      "com.sun.jini.jeri.dgc.cleanInterval", 180000)))
      .longValue();

    /** minimum lease duration that we bother to honor */
    private static final long minimumDuration =    // default 5 seconds
  ((Long) AccessController.doPrivileged(new GetLongAction(
      "com.sun.jini.jeri.dgc.minimumDuration", 5000)))
      .longValue();

    /** minimum retry count for dirty calls that fail */
    private static final int dirtyFailureRetries = 5;

    /** retry count for clean calls that fail with ConnectException */
    private static final int cleanConnectRetries = 3;

    /** constant empty Object array for lease renewal optimization */
    private static final Object[] emptyObjectArray = new Object[0];

    /** next sequence number for DGC calls (access synchronized on class) */
    private static long nextSequenceNum = Long.MIN_VALUE;

    /**
     * endpoint table: maps generic endpoint to EndpointEntry
     * (lock guards endpointTable)
     */
    private final Map endpointTable = new HashMap(5);

    protected AbstractDgcClient() {
    }

    /**
     * A DgcProxy is a proxy for invoking DGC operations on a server-side
     * DGC implementation at a particular endpoint.  A DgcProxy instance
     * for a given endpoint is obtained from concrete class (using the
     * getDgcProxy method) and used by the abstract implementation.
     */
    protected interface DgcProxy {
  long dirty(long sequenceNum, Object[] ids, long duration)
      throws RemoteException;
  void clean(long sequenceNum, Object[] ids, boolean strong)
      throws RemoteException;
    }
    /** Returns a proxy for making DGC calls to the given endpoint. */
    protected abstract DgcProxy getDgcProxy(Object endpoint);
    /** Indicates that resources for the given endpoint may be freed. */
    protected abstract void freeEndpoint(Object endpoint);
    /** Returns the endpoint in the given live reference. */
    protected abstract Object getRefEndpoint(Object ref);
    /** Returns the object ID in the given live reference. */
    protected abstract Object getRefObjectID(Object ref);

    /**
     * Registers the live reference instances in the supplied collection to
     * participate in distributed garbage collection.
     *
     * All of the live references in the list must be for remote objects at
     * the given endpoint.
     */
    protected final void registerRefs(Object endpoint, Collection refs) {
  /*
   * Look up the given endpoint and register the refs with it.
   * The retrieved entry may get removed from the global endpoint
   * table before EndpointEntry.registerRefs() is able to acquire
   * its lock; in this event, it returns false, and we loop and
   * try again.
   */
  EndpointEntry epEntry;
  do {
      epEntry = getEndpointEntry(endpoint);
  } while (!epEntry.registerRefs(refs));
    }

    /**
     * Gets the next sequence number to be used for a dirty or clean
     * operation from this AbstractDgcClient.  This method should only be
     * called while synchronized on the EndpointEntry whose data structures
     * the operation affects.
     */
    private static synchronized long getNextSequenceNum() {
  return nextSequenceNum++;
    }

    /**
     * Given the length of a lease and the time that it was granted,
     * computes the absolute time at which it should be renewed, giving
     * room for reasonable computational and communication delays.
     */
    private static long computeRenewTime(long grantTime, long duration) {
  /*
   * REMIND: This algorithm should be more sophisticated, waiting
   * a longer fraction of the lease duration for longer leases.
   */
  return grantTime + (duration / 2);
    }

    /**
     * Looks up the EndpointEntry for the given endpoint.  An entry is
     * created if one does not already exist.
     */
    private EndpointEntry getEndpointEntry(Object endpoint) {
  synchronized (endpointTable) {
      EndpointEntry entry = (EndpointEntry) endpointTable.get(endpoint);
      if (entry == null) {
    entry = new EndpointEntry(endpoint);
    endpointTable.put(endpoint, entry);
    /*
     * If the endpoint table was previously empty, we are now
     * interested in special assistance from the local garbage
     * collector for aggressively discovering unreachable live
     * remote references (by notifying our phantom references),
     * so that DGC "clean" calls can be sent in a timely fashion.
     *
     * Without guaranteed access to something like the
     * sun.misc.GC API, however, we currently have no
     * practical way of getting such special assistance.
     */
      }
      return entry;
  }
    }

    /**
     * EndpointEntry encapsulates the client-side DGC information specific
     * to a particular endpoint.  Of most significance is the table that
     * maps live reference objects to RefEntry objects and the renew/clean
     * thread that handles asynchronous client-side DGC operations.
     */
    private final class EndpointEntry {

  /** the endpoint that this EndpointEntry is for */
  private final Object endpoint;
  /** synthesized reference to the remote server-side DGC */
  private final DgcProxy dgcProxy;
  /** renew/clean thread for handling lease renewals and clean calls */
  private final Thread renewCleanThread;
  /** reference queue for phantom references */
  private final ReferenceQueue refQueue = new ReferenceQueue();

  /* mutable instance state (below) is guarded by this object's lock */

  /** true if this entry has been removed from the global table */
  private boolean removed = false;

  /** table of refs held for endpoint: maps object ID to RefEntry */
  private final Map refTable = new HashMap(5);
  /** set of RefEntry instances from last (failed) dirty call */
  private Set invalidRefs = new HashSet(5);

  /** absolute time to renew current lease to this endpoint */
  private long renewTime = Long.MAX_VALUE;
  /** absolute time current lease to this endpoint will expire */
  private long expirationTime = Long.MIN_VALUE;
  /** count of recent dirty calls that have failed */
  private int dirtyFailures = 0;
  /** absolute time of first recent failed dirty call */
  private long dirtyFailureStartTime;
  /** (average) elapsed time for recent failed dirty calls */
  private long dirtyFailureDuration;

  /** true if renew/clean thread may be interrupted */
  private boolean interruptible = false;

  /** set of clean calls that need to be made */
  private final Set pendingCleans = new HashSet(5);

  private EndpointEntry(final Object endpoint) {
      this.endpoint = endpoint;
      dgcProxy = getDgcProxy(endpoint);
      renewCleanThread = (ThreadAccessController.doPrivileged(
    new NewThreadAction(new RenewCleanThread(),
            "RenewClean-" + endpoint, true));
      renewCleanThread.start();
  }

  /**
   * Registers the live reference instances in the supplied list to
   * participate in distributed garbage collection.
   *
   * This method returns false if this entry was removed from the
   * global endpoint table (because it was empty) before these refs
   * could be registered.  In that case, a new EndpointEntry needs
   * to be looked up.
   *
   * This method must NOT be invoked while synchronized on this
   * EndpointEntry.
   */
  boolean registerRefs(Collection refs) {
      assert !Thread.holdsLock(this);

      Set refsToDirty = null// entries for refs needing dirty
      long sequenceNum;    // sequence number for dirty call

      synchronized (this) {
    if (removed) {
        return false;
    }

    Iterator iter = refs.iterator();
    while (iter.hasNext()) {
        Object ref = iter.next();
        assert getRefEndpoint(ref).equals(endpoint);

        Object objectID = getRefObjectID(ref);
        RefEntry refEntry = (RefEntry) refTable.get(objectID);
        if (refEntry == null) {
      refEntry = new RefEntry(objectID);
      refTable.put(objectID, refEntry);
      if (refsToDirty == null) {
          refsToDirty = new HashSet(5);
      }
      refsToDirty.add(refEntry);
        }

        refEntry.addInstanceToRefSet(ref);
    }

    if (refsToDirty == null) {
        return true;
    }

    refsToDirty.addAll(invalidRefs);
    invalidRefs.clear();

    sequenceNum = getNextSequenceNum();
      }

      makeDirtyCall(refsToDirty, sequenceNum);
      return true;
  }

  /**
   * Removes the given RefEntry from the ref table.  If that makes
   * the ref table empty, remove this entry from the global endpoint
   * table.
   *
   * This method must ONLY be invoked while synchronized on this
   * EndpointEntry.
   */
  private void removeRefEntry(RefEntry refEntry) {
      assert Thread.holdsLock(this);
      assert !removed;
      assert refTable.containsKey(refEntry.getObjectID());

      refTable.remove(refEntry.getObjectID());
      invalidRefs.remove(refEntry);
      if (refTable.isEmpty()) {
    synchronized (endpointTable) {
        endpointTable.remove(endpoint);
        freeEndpoint(endpoint);
        /*
         * If the endpoint table is now empty, we are no longer
         * interested in special assistance from the local garbage
         * collector for aggressively discovering unreachable
         * live remote references, if we had been getting such
         * special assistance in the first place.
         */
    }
    removed = true;
      }
  }

  /**
   * Makes a DGC dirty call to this entry's endpoint, for the
   * object IDs corresponding to the given set of refs and with
   * the given sequence number.
   *
   * This method must NOT be invoked while synchronized on this
   * EndpointEntry.
   */
  private void makeDirtyCall(Set refEntries, long sequenceNum) {
      assert !Thread.holdsLock(this);

      Object[] ids;
      if (refEntries != null) {
    ids = createObjectIDArray(refEntries);
      } else {
    ids = emptyObjectArray;
      }

      long startTime = System.currentTimeMillis();
      try {
    long duration = dgcProxy.dirty(sequenceNum, ids, leaseValue);

    synchronized (this) {
        dirtyFailures = 0;

        if (duration < 0) {
      setRenewTime(Long.MAX_VALUE);
      invalidRefs.addAll(refTable.values());
        } else {
      setRenewTime(
          computeRenewTime(startTime,
               Math.max(duration,
                  minimumDuration)));
      expirationTime = startTime + duration;
        }
    }

      } catch (NoSuchObjectException e) {
    synchronized (this) {
        setRenewTime(Long.MAX_VALUE);
        invalidRefs.addAll(refTable.values());
    }
      } catch (Exception e) {
    long endTime = System.currentTimeMillis();

    synchronized (this) {
        dirtyFailures++;

        if (dirtyFailures == 1) {
      /*
       * If this was the first recent failed dirty call,
       * reschedule another one immediately, in case there
       * was just a transient network problem, and remember
       * the start time and duration of this attempt for
       * future calculations of the delays between retries.
       */
      dirtyFailureStartTime = startTime;
      dirtyFailureDuration = endTime - startTime;
      setRenewTime(endTime);
        } else {
      /*
       * For each successive failed dirty call, wait for a
       * (binary) exponentially increasing delay before
       * retrying, to avoid network congestion.
       */
      int n = dirtyFailures - 2;
      if (n == 0) {
          /*
           * Calculate the initial retry delay from the
           * average time elapsed for each of the first
           * two failed dirty calls.  The result must be
           * at least 1000ms, to prevent a tight loop.
           */
          dirtyFailureDuration =
        Math.max((dirtyFailureDuration +
            (endTime - startTime)) >> 1, 1000);
      }
      long newRenewTime =
          endTime + (dirtyFailureDuration << n);

      /*
       * Continue if the last known held lease has not
       * expired, or else at least a fixed number of times,
       * or at least until we've tried for a fixed amount
       * of time (the default lease value we request).
       */
      if (newRenewTime < expirationTime ||
          dirtyFailures < dirtyFailureRetries ||
          newRenewTime < dirtyFailureStartTime + leaseValue)
      {
          setRenewTime(newRenewTime);
      } else {
          /*
           * Give up: postpone lease renewals until next
           * ref is registered for this endpoint.
           */
          setRenewTime(Long.MAX_VALUE);
      }
        }

        if (refEntries != null) {
      /*
       * Add all of these refs to the set of refs for this
       * endpoint that may be invalid (this AbstractDgcClient
       * may not be in the server's referenced set), so that
       * we will attempt to explicitly dirty them again in
       * the future.
       */
      invalidRefs.addAll(refEntries);
     
      /*
       * Record that a dirty call has failed for all of these
       * refs, so that clean calls for them in the future
       * will be strong.
       */
      Iterator iter = refEntries.iterator();
      while (iter.hasNext()) {
          RefEntry refEntry = (RefEntry) iter.next();
          refEntry.markDirtyFailed();
      }
        }

        /*
         * If the last known held lease will have expired before
         * the next renewal, all refs might be invalid.
         */
        if (renewTime >= expirationTime) {
      invalidRefs.addAll(refTable.values());
        }
    }
      }
  }
 
  /**
   * Sets the absolute time at which the lease for this entry should
   * be renewed.
   *
   * This method must ONLY be invoked while synchronized on this
   * EndpointEntry.
   */
  private void setRenewTime(long newRenewTime) {
      assert Thread.holdsLock(this);

      if (newRenewTime < renewTime) {
    renewTime = newRenewTime;
    if (interruptible) {
        AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
          renewCleanThread.interrupt();
          return null;
      }
        });
    }
      } else {
    renewTime = newRenewTime;
      }
  }

  /**
   * RenewCleanThread handles the asynchronous client-side DGC activity
   * for this entry: renewing the leases and making clean calls.
   */
  private class RenewCleanThread implements Runnable {

      public void run() {
    do {
        long timeToWait;
        RefEntry.PhantomLiveRef phantom = null;
        boolean needRenewal = false;
        Set refsToDirty = null;
        long sequenceNum = Long.MIN_VALUE;

        synchronized (EndpointEntry.this) {
      /*
       * Calculate time to block (waiting for phantom
       * reference notifications).  It is the time until the
       * lease renewal should be done, bounded on the low
       * end by 1 ms so that the reference queue will always
       * get processed, and if there are pending clean
       * requests (remaining because some clean calls
       * failed), bounded on the high end by the maximum
       * clean call retry interval.
       */
      long timeUntilRenew =
          renewTime - System.currentTimeMillis();
      timeToWait = Math.max(timeUntilRenew, 1);
      if (!pendingCleans.isEmpty()) {
          timeToWait = Math.min(timeToWait, cleanInterval);
      }

      /*
       * Set flag indicating that it is OK to interrupt this
       * thread now, such as if a earlier lease renewal time
       * is set, because we are only going to be blocking
       * and can deal with interrupts.
       */
      interruptible = true;
        }

        try {
      /*
       * Wait for the duration calculated above for any of
       * our phantom references to be enqueued.
       */
      phantom = (RefEntry.PhantomLiveRef)
          refQueue.remove(timeToWait);
        } catch (InterruptedException e) {
        }

        synchronized (EndpointEntry.this) {
      /*
       * Set flag indicating that it is NOT OK to interrupt
       * this thread now, because we may be undertaking I/O
       * operations that should not be interrupted (and we
       * will not be blocking arbitrarily).
       */
      interruptible = false;
      Thread.interrupted()// clear interrupted state

      /*
       * If there was a phantom reference enqueued, process
       * it and all the rest on the queue, generating
       * clean requests as necessary.
       */
      if (phantom != null) {
          processPhantomRefs(phantom);
      }

      /*
       * Check if it is time to renew this entry's lease.
       */
      long currentTime = System.currentTimeMillis();
      if (currentTime > renewTime) {
          needRenewal = true;
          if (currentTime >= expirationTime) {
        invalidRefs.addAll(refTable.values());
          }
          if (!invalidRefs.isEmpty()) {
        refsToDirty = invalidRefs;
        invalidRefs = new HashSet(5);
          }
          sequenceNum = getNextSequenceNum();
      }
        }

        if (needRenewal) {
      makeDirtyCall(refsToDirty, sequenceNum);
        }

        if (!pendingCleans.isEmpty()) {
      makeCleanCalls();
        }
    } while (!removed || !pendingCleans.isEmpty());
      }
  }

  /**
   * Processes the notification of the given phantom reference and any
   * others that are on this entry's reference queue.  Each phantom
   * reference is removed from its RefEntry's ref set.  All ref
   * entries that have no more registered instances are collected
   * into up to two batched clean call requests: one for refs
   * requiring a "strong" clean call, and one for the rest.
   *
   * This method must ONLY be invoked while synchronized on this
   * EndpointEntry.
   */
  private void processPhantomRefs(RefEntry.PhantomLiveRef phantom) {
      assert Thread.holdsLock(this);

      Set strongCleans = null;
      Set normalCleans = null;

      do {
    RefEntry refEntry = phantom.getRefEntry();
    refEntry.removeInstanceFromRefSet(phantom);
    if (refEntry.isRefSetEmpty()) {
        if (refEntry.hasDirtyFailed()) {
      if (strongCleans == null) {
          strongCleans = new HashSet(5);
      }
      strongCleans.add(refEntry);
        } else {
      if (normalCleans == null) {
          normalCleans = new HashSet(5);
      }
      normalCleans.add(refEntry);
        }
        removeRefEntry(refEntry);
    }
      } while ((phantom =
    (RefEntry.PhantomLiveRef) refQueue.poll()) != null);

      if (strongCleans != null) {
    pendingCleans.add(
        new CleanRequest(getNextSequenceNum(),
             createObjectIDArray(strongCleans),
             true));
      }
      if (normalCleans != null) {
    pendingCleans.add(
        new CleanRequest(getNextSequenceNum(),
             createObjectIDArray(normalCleans),
             false));
      }
  }

  /**
   * Makes all of the clean calls described by the clean requests in
   * this entry's set of "pending cleans".  Clean requests for clean
   * calls that succeed are removed from the "pending cleans" set.
   *
   * This method must NOT be invoked while synchronized on this
   * EndpointEntry.
   */
  private void makeCleanCalls() {
      assert !Thread.holdsLock(this);

      Iterator iter = pendingCleans.iterator();
      while (iter.hasNext()) {
    CleanRequest request = (CleanRequest) iter.next();
    try {
        dgcProxy.clean(request.sequenceNum, request.objectIDs,
          request.strong);
        iter.remove();
    } catch (NoSuchObjectException e) {
        iter.remove();
    } catch (Exception e) {
        if (e instanceof ConnectException ||
      e instanceof ConnectIOException)
        {
      /*
       * If we get a ConnectException, the target DGC likely
       * has gone away, in which case we shouldn't bother
       * retrying this clean request forever.  Then again,
       * the server could just be heavily loaded, so we will
       * give a finite number of retry opportunities to
       * clean requests that fail this way.
       *
       * A similar (but different) argument can be made for
       * ConnectIOException.
       */
      if (++request.connectFailures >= cleanConnectRetries) {
          iter.remove();
      }
        } else {
      // possible transient failure, retain clean request
        }
    }
      }
  }

  /**
   * Creates an array of object IDs (needed for the DGC remote calls)
   * from the ids in the given set of refs.
   */
  private Object[] createObjectIDArray(Set refEntries) {
      Object[] ids = new Object[refEntries.size()];
      Iterator iter = refEntries.iterator();
      for (int i = 0; i < ids.length; i++) {
    ids[i] = ((RefEntry) iter.next()).getObjectID();
      }
      return ids;
  }

  /**
   * RefEntry encapsulates the client-side DGC information specific to
   * a particular object ID of an endpoint (a unique live reference
   * value).
   *
   * In particular, it contains a set of phantom references to all of
   * the live reference instances for the given object ID and endpoint
   * in this VM that have been registered with this AbstractDgcClient
   * (but not yet garbage collected locally).
   */
  private class RefEntry {

      /**
       * the object ID that this RefEntry is for (the endpoint is
       * implied by the outer EndpointEntry instance)
       */
      private final Object objectID;

      /*
       * mutable instance state (below) is guarded by outer
       * EndpointEntry's lock
       */

      /** set of phantom references to registered instances */
      private final Set refSet = new HashSet(5);
      /** true if a dirty call containing this ref has failed */
      private boolean dirtyFailed = false;

      RefEntry(Object objectID) {
    this.objectID = objectID;
      }

      /**
       * Returns the object ID that this entry is for.
       */
      Object getObjectID() {
    return objectID;
      }

      /**
       * Adds a live reference to the set of registered instances for
       * this entry.
       *
       * This method must ONLY be invoked while synchronized on this
       * RefEntry's EndpointEntry.
       */
      void addInstanceToRefSet(Object ref) {
    assert Thread.holdsLock(EndpointEntry.this);
    assert getRefObjectID(ref).equals(objectID);

    /*
     * Only keep a phantom reference to the registered instance,
     * so that it can be garbage collected normally (and we can be
     * notified when that happens).
     */
    refSet.add(new PhantomLiveRef(ref));
      }

      /**
       * Removes a PhantomLiveRef from the set of registered instances.
       *
       * This method must ONLY be invoked while synchronized on this
       * RefEntry's EndpointEntry.
       */
      void removeInstanceFromRefSet(PhantomLiveRef phantom) {
    assert Thread.holdsLock(EndpointEntry.this);
    assert refSet.contains(phantom);
    refSet.remove(phantom);
      }

      /**
       * Returns true if there are no registered live reference
       * instances for this entry still reachable in this VM.
       *
       * This method must ONLY be invoked while synchronized on this
       * RefEntry's EndpointEntry.
       */
      boolean isRefSetEmpty() {
    assert Thread.holdsLock(EndpointEntry.this);
    return refSet.size() == 0;
      }

      /**
       * Records that a dirty call that explicitly contained this
       * entry's ref value has failed.
       *
       * This method must ONLY be invoked while synchronized on this
       * RefEntry's EndpointEntry.
       */
      void markDirtyFailed() {
    assert Thread.holdsLock(EndpointEntry.this);
    dirtyFailed = true;
      }

      /**
       * Returns true if a dirty call that explicitly contained this
       * entry's ref value has failed (and therefore a clean call for
       * the ref value needs to be marked "strong").
       *
       * This method must ONLY be invoked while synchronized on this
       * RefEntry's EndpointEntry.
       */
      boolean hasDirtyFailed() {
    assert Thread.holdsLock(EndpointEntry.this);
    return dirtyFailed;
      }

      /**
       * PhantomLiveRef is a PhantomReference to a live reference
       * instance, used to detect when the particular live reference
       * becomes permanently unreachable in this VM.
       */
      class PhantomLiveRef extends PhantomReference {
 
    PhantomLiveRef(Object ref) {
        super(ref, EndpointEntry.this.refQueue);
    }

    RefEntry getRefEntry() {
        return RefEntry.this;
    }
      }
  }
    }

    /**
     * CleanRequest holds the data for the arguments of a clean call
     * that needs to be made.
     */
    private static class CleanRequest {

  long sequenceNum;
  Object[] objectIDs;
  boolean strong;

  /** how many times this request has failed with ConnectException */
  int connectFailures = 0;

  CleanRequest(long sequenceNum, Object[] objectIDs, boolean strong) {
      this.sequenceNum = sequenceNum;
      this.objectIDs = objectIDs;
      this.strong = strong;
  }
    }
}
TOP

Related Classes of com.sun.jini.jeri.internal.runtime.AbstractDgcClient$EndpointEntry

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.