/*
* 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.outrigger;
import java.util.Hashtable;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.jini.core.entry.UnusableEntryException;
import net.jini.core.transaction.CannotJoinException;
import net.jini.core.transaction.server.TransactionConstants;
/**
* <code>EntryHolder</code>s hold all the entries of a exact given
* class. <code>OutriggerServerImpl</code> has one
* <code>EntryHolder</code> for each entry class it knows about. A
* simple implementation is used that simply stores the entries in a
* list.
*
* @author Sun Microsystems, Inc.
*/
class EntryHolder implements TransactionConstants {
/** The list that holds the handles */
private final FastList contents = new FastList();
/**
* The map of cookies to handles, shared with the
* <code>EntryHolderSet</code> and every other
* <code>EntryHolder</code>.
*/
private final Hashtable idMap;
/** The server we are working for */
private final OutriggerServerImpl space;
/** Logger for logging information about entry matching */
private static final Logger matchingLogger =
Logger.getLogger(OutriggerServerImpl.matchingLoggerName);
/** Logger for logging information about iterators */
private static final Logger iteratorLogger =
Logger.getLogger(OutriggerServerImpl.iteratorLoggerName);
/**
* Create a new <code>EntryHolder</code> with the shared
* <code>idMap</code>, and which will hold classes of the given
* <code>className</code>. The <code>idMap</code> is shared with
* <code>EntryHolderSet</code> so that there is one table that can
* map ID to <code>EntryRep</code>
*/
EntryHolder(OutriggerServerImpl space, Hashtable idMap) {
this.space = space;
this.idMap = idMap;
}
/**
* Return an <code>EntryHandle</code> object that matches the given
* template, or <code>null</code> if none does. Optionally
* removes (perhaps provisionally) the found entry.
*
* @param tmplRep The template to match against
* @param txnMgr If non-null the transaction (represented as
* a <code>TransactableMgr</code> to perform
* the operation under. May be <code>null</code>
* if the operation is not to be done under
* a transaction.
* @param takeIt <code>true</code> if <code>hasMatch</code> should
* remove the matching entry.
* @param conflictSet If non-null the <code>TransactableMgr</code>
* objects of any transactions that prevent
* a non-null value from being retured will
* be added to <code>conflictSet</code>. May
* be <code>null</code> in which case
* conflicting transaction will not be recorded.
* This method assumes that any concurrent access
* is being arbitrated by the set or by the caller.
* @param lockedEntrySet If non-null the ID of any entries that
* can't be retured because of conflicting
* transaction will be added to
* <code>lockedEntrySet</code>. May be
* <code>null</code> in which case unavailable
* entries will not be recorded. This method
* assumes that any concurrent access is being
* arbitrated by the set or by the caller.
* @param provisionallyRemovedEntrySet If the entry can not be
* read/taken because it has been provisionally
* removed then its handle will be placed in the
* passed <code>WeakHashMap</code> as a key (with
* null as the value). May be <code>null</code> in
* which case provisionally removed entries will not
* be recorded. This method assumes that any
* concurrent access is being arbitrated by the set
* or by the caller.
* @throws CannotJoinException if a match is found and
* the operation is to be performed under a transaction,
* but the transaction is no longer active.
* @see #attemptCapture
*/
EntryHandle hasMatch(EntryRep tmpl, TransactableMgr txn, boolean takeIt,
Set conflictSet, Set lockedEntrySet,
WeakHashMap provisionallyRemovedEntrySet)
throws CannotJoinException
{
matchingLogger.entering("EntryHolder", "hasMatch");
EntryHandle handle = (EntryHandle) contents.head();
if (handle == null)
return null; // Nothing here
final EntryHandleTmplDesc desc =
EntryHandle.descFor(tmpl, handle.rep().numFields());
final long startTime = System.currentTimeMillis();
for (;handle != null; handle = (EntryHandle) handle.next()) {
if (handle.removed())
continue;
// Quick reject -- see the if handle mask is incompatible
if ((handle.hash() & desc.mask) != desc.hash)
continue;
final EntryRep rep = handle.rep();
if (!tmpl.matches(rep))
continue;
final boolean available =
confirmAvailabilityWithTxn(rep, handle, txn,
takeIt, startTime, conflictSet, lockedEntrySet,
provisionallyRemovedEntrySet);
if (available)
return handle;
}
return null;
}
/**
* Debug method: Dump out the state of this holder, printing out
* the name of the dump first.
*/
void dump(String name) {
try {
System.out.println(name);
for (EntryHandle handle = (EntryHandle) contents.head();
handle != null;
handle = (EntryHandle) handle.next())
{
EntryRep rep = handle.rep();
System.out.println(" " + rep + ", " + rep.entry());
}
} catch (UnusableEntryException e) {
e.printStackTrace();
}
}
/**
* Atomically check to see if the passed entry can be read/taken by
* the specified operation using the specified transaction and if
* it can read/take it and return <code>true</code>, otherwise
* return <code>false</code>. If the entry is removed. Note,
* if the entry is removed, removal is logged.
* @param entry The <code>EntryHandle</code> of the entry
* the caller wants to read/take.
* @param txn If non-null the transaction to perform
* this operation under. Note, if non-null and
* <code>txn</code> is not active <code>false</code>
* will be returned.
* @param takeIt <code>true</code> if the caller is trying
* take the passed entry, <code>false</code>
* otherwise.
* @param conflictSet If non-null and the entry can not be
* read/taken because of transaction conflicts the
* conflicting transaction(s) will be added to this set.
* This method assumes that any concurrent access is
* being arbitrated by the set or by the caller.
* @param lockedEntrySet If the entry can not be read/taken
* because of a transaction conflict, the ID of the
* entry will be added to this set. This method
* assumes that any concurrent access is being arbitrated
* by the set or by the caller.
* @param provisionallyRemovedEntrySet If the entry can not be
* read/taken because it has been provisionally
* removed then its handle will be placed in the
* passed <code>WeakHashMap</code> as a key (with
* null as the value). May be <code>null</code> in
* which case provisionally removed entries will not
* be recorded. This method assumes that any
* concurrent access is being arbitrated by the set
* or by the caller.
* @param now an estimate of the current time in milliseconds
* since the beginning of the epoch.
* @return <code>true</code> if the entry could be read/taken and
* <code>false</code> otherwise.
* @throws NullPointerException if entry is <code>null</code>.
*/
boolean attemptCapture(EntryHandle handle, TransactableMgr txn,
boolean takeIt, Set conflictSet, Set lockedEntrySet,
WeakHashMap provisionallyRemovedEntrySet, long now)
{
try {
return confirmAvailabilityWithTxn(handle.rep(), handle,
txn, takeIt, now, conflictSet, lockedEntrySet,
provisionallyRemovedEntrySet);
} catch (CannotJoinException e) {
return false;
}
}
/* Now that we know we have a match, make sure that the the
* item in question is still in the space and hasn't been
* subject to other transactional-interference...
*/
private boolean confirmAvailabilityWithTxn(EntryRep rep,
EntryHandle handle, TransactableMgr txnMgr, boolean takeIt,
long time, Set conflictSet, Set lockedEntrySet,
WeakHashMap provisionallyRemovedEntrySet)
throws CannotJoinException
{
// Now that we know we have a match, make sure that the the
// item in question is still in the space and hasn't been
// subject to other transactional-interference...
// Lock the txn first (since that what everybody else does that)
// to ensure that the locking always happens in A cannonical
// order. We lock the transaction (txn) object to ensure that the
// transaction hasn't been altered behind our backs. Other, cheaper
// actions with the space (write, etc.) are handled nicely in
// OutriggerServerImpl. But, this one (reading/taking) was expensive
// enough that we wanted to do it here, where the window is
// smallest.
// Bug 4394263...
final Txn txn = (Txn)txnMgr;
try {
if (txn != null)
txn.ensureActive();
return confirmAvailability(rep, handle, txn,
takeIt, time, conflictSet, lockedEntrySet,
provisionallyRemovedEntrySet);
} finally {
if (txn != null)
txn.allowStateChange();
}
}
/**
* With the EntryRep <code>rep</code> passed in, verify that the
* entry hasn't been taken by someone else, hasn't expired, etc.
* Also, verify that the entry is really (legally) visible to this
* transaction at this time. If this is a <code>take</code>, it it
* is removed or provisionally removed. If this operation is under
* a transaction, the entry is locked appropriately.
* @see grab -- a helper routine
*/
private boolean
confirmAvailability(EntryRep rep, EntryHandle handle,
TransactableMgr txn, boolean takeIt, long time,
Set conflictSet, Set lockedEntrySet,
WeakHashMap provisionallyRemovedEntrySet)
{
if (handle.removed())
return false;
synchronized (handle) {
// get rid of stale entries
if (isExpired(time, handle))
return false;
if (handle.removed()) // oh, well -- someone got it first
return false;
if (handle.isProvisionallyRemoved()) {
if (provisionallyRemovedEntrySet != null)
provisionallyRemovedEntrySet.put(handle, null);
return false;
}
int op = (takeIt ? TransactableMgr.TAKE : TransactableMgr.READ);
if (!handle.canPerform(txn, op)) {
// Before deciding this is a conflict, we have to
// make sure we are not conflicting with ourselves.
if (handle.onlyMgr(txn))
// We are just conflicting with ourselves, this entry
// must have been taken by this txn already ... oh well
return false;
// Must be a real conflict, log if we were told too
if (matchingLogger.isLoggable(Level.FINER)) {
matchingLogger.log(Level.FINER, "match, but can''t " +
"perform {0}; handle.knownMgr(txn) == {1}",
new Object[] {new Integer(op),
new Boolean(handle.knownMgr(txn))});
}
if (conflictSet != null) {
handle.addTxns(conflictSet);
}
if (lockedEntrySet != null) {
lockedEntrySet.add(rep.id());
}
return false;
}
if (grab(handle, txn, op, takeIt, false))
return true;
else
throw
new AssertionError("entry became non-available while locked");
}
}
/**
* Given an entry that we want to return as the result of a query
* and we have confirmed we can return it, make the results of the
* query visible to the rest of the service. We must either [a]
* remove it from the holder (if the operation is a take under the
* same transaction the entry was written under), [b] mark it as
* provisionally removed pending commiting the operation to disk
* (if the operation is a take without a transaction), [c]
* lock it under the transaction passed into this method (if the
* operation is a read or take under a transaction), or [d] do
* nothing (read not under a transaction) as appropriate.
* <p>
* Also used during log recovery to recover takes.
*
* @param handle The handle attached to the particular EntryRep
* @param txn The Txn object
* @param op TAKE or READ (as a TransactableMgr constant)
* @param takeIt Is this a TAKE (or is it [false] a READ)
* @param recovery <code>true</code> if being called as
* part of store recovery.
* @return <code>true</code> if the entry could be grabbed.
* @throws NullPointerException if <code>handle</code> is
* <code>null</code>.
*/
private boolean grab(EntryHandle handle, TransactableMgr txn, int op,
boolean takeIt, boolean recovery)
{
assert op==(takeIt?TransactableMgr.TAKE:TransactableMgr.READ);
assert !recovery || takeIt;
/*
* If this manager (txn) is known to the handle, then txn
* is a transaction which can operate directly on the
* existing handle. If so, then we can operate on it
* directly. Note that if txn is null and the handle is
* not managed, then txn is "known" in this sense.
*
* Else, this operation is new relative to the
* known transactions (possibly null) and txn.
*/
if (handle.knownMgr(txn)) { // added or read in this txn if taking
// take it if we have to (otherwise it must be a read
// there is no house keeping to be done since must already
// be read locked)
if (takeIt) {
// is this an entry that was written under this
// transaction or another?
if (handle.managed()) {
// It is locked by a transaction and it must
// be the one this take is under.
if (!handle.promoteToTakeIfNeeded()) {
// this transaction wrote the entry (note txn
// must be non-null, otherwise either
// handle.managed or handle.knownMgr(txn)
// would have returned null), just need to try
// and remove it
assert txn != null;
// It is ok to call remove before committing this
// op to disk since a crash will cause the original
// write to be undone and the entry will still
// end up being removed.
if (!remove(handle, recovery))
// Someone got to it first
return false;
}
// Otherwise it was read locked (now take
// locked and needs to still be semi-visible
// to ifExists queries outside of the
// transaction, leave it in place with its new
// lock type (aka "state")
} else {
// Nether the entry nor this take are under a
// transaction (txn must be null because the
// that is the only way knownMgr return true, and
// managed() false)
assert txn == null;
if (handle.removed() || handle.isProvisionallyRemoved())
// Someone got to it first
return false;
handle.provisionallyRemove();
}
}
} else {
if (txn != null) {
// We need to add this txn to this handle and
// add this handle to this txn.
handle.add(txn, op, this); // add manager to handle's list
txn.add(handle); // add handle to mgr's list
}
}
return true;
}
/**
* Recover a logged take.
* @param handle The <code>EntryHandle</code> of the entry who's
* take is being logged.
* @param txn If non-null the transaction the take was performed
* under.
* @throws NullPointerException if <code>handle</code> is
* <code>null</code>.
*/
void recoverTake(EntryHandle handle, Txn txn) {
if (!grab(handle, txn, TransactableMgr.TAKE, true, true))
throw
new AssertionError("match not found while recovering take");
}
/**
* Return <code>true</code> if the entry held by the given
* <code>handle</code> has expired by the time in <code>now</code>.
*/
private boolean isExpired(long now, EntryHandle handle) {
/* Some callers already own the lock on handle when
* they call us, but the general feeling is that
* re-synchronizing is not costly and it is important
* to only call remove if we own the lock.
*/
synchronized (handle) {
final EntryRep rep = handle.rep();
synchronized (rep) {
if (rep.getExpiration() > now) {
// Not expired
return false;
}
/* Expired, set expiration to before the beginning of
* time so renew() (which does not lock on the handle,
* but the rep...) can't renew the lease once we leave
* this block, even it already has a ref to the
* rep. Everyone else locks on the handle so
* and makes sure the handle has not been removed
* before doing anything important.
*/
rep.setExpiration(Long.MIN_VALUE);
}
// If we are here the lease must have expired
if (matchingLogger.isLoggable(Level.FINER)) {
matchingLogger.log(Level.FINER, "expired {0} at {1} (now {2})",
new Object[] {rep.id(),
new Long(rep.getExpiration()),
new Long(now)});
}
if (!handle.isProvisionallyRemoved() && remove(handle, false)) {
/* If we got to do the remove, schedule
* logging of the removal, otherwise
* someone else already did it, or will do it
*/
space.scheduleCancelOp(rep.id());
}
return true;
}
}
/**
* Add new new entry to the holder. Assumes the lock
* on the handle is held if there is a possibility
* of concurrent access.
* @param handle The <code>EntryHandle</code> for the
* entry being added.
* @param txn If the add is being done under a
* transaction the <code>TransactableMgr</code> for
* that transaction.
* @throws NullPointerException if <code>handle</code> is
* <code>null</code>.
*/
void add(EntryHandle handle, TransactableMgr txn) {
final EntryRep rep = handle.rep();
/* Make sure info duplicated across all the handles in this
* holder is shared.
*
* Because this thread will usually hold the lock on handle,
* we must call contents.head before calling contents.add
* otherwise during the head call this thread will attempt to
* obtain a lock on a 2nd FastList node in the same FastList,
* and make that attempt in the wrong order. In particular
* this could lead to deadlocks when SimpleRepEnum or
* ContinuingQuery call into FastList.Node.restart ( restart
* locks the node its called on (which can be the same node
* head locks) and then tries to lock the tail (which can be
* handle if add has already been called)). By calling head
* before calling add we have locks on two FastList.Node
* objects, but they are not in the same list so we are ok.
*
* We make the head call before calling txn.add because
* it seems like better hygiene to fix reps state before
* exposing it to others (even though shareWith will
* not change handle's state materially).
*/
final EntryHandle head = (EntryHandle) contents.head();
if (head != null && head != handle)
rep.shareWith(head.rep());
if (txn != null)
txn.add(handle);
contents.add(handle);
idMap.put(rep.getCookie(), handle);
}
/**
* Return an array of the class names of the super classes of the
* entries in this holder, or <code>null</code> if the holder is
* empty.
*/
String[] supertypes() {
final EntryHandle head = (EntryHandle)contents.head();
if (head == null)
return null;
return head.rep().superclasses();
}
/**
* Return an enumerator over the contents of this space that are visible
* from the given mgr.
*/
RepEnum contents(TransactableMgr mgr) {
return new SimpleRepEnum(mgr);
}
/**
* The class that implements <code>RepEnum</code> for this class.
*/
private class SimpleRepEnum implements RepEnum {
/** The last node we saw */
private EntryHandle handle;
private TransactableMgr mgr;
private long startTime;
int count = 0;
SimpleRepEnum(TransactableMgr mgr) {
this.mgr = mgr;
handle = (EntryHandle) contents.head();
startTime = System.currentTimeMillis();
}
// inherit doc comment from superclass
public EntryRep nextRep() {
iteratorLogger.entering("SimpleRepEnum", "nextRep");
/* Since we might be accessing our iteration from a
* different thread than the one that began the traversal,
* we need to inform the FastList that this thread should
* be allowed to assume that traversal without throwing
* an exception. FastList must therefore update its data
* structures to accommodate the traversal from this new
* thread. [It would be better if did this once per a thread...]
*/
if (handle != null)
handle.restart();
/* Skip over handles which are either removed or unable
* to perform a READ operation.
*/
while (handle != null &&
(!handle.canPerform(mgr, TransactableMgr.READ) ||
isExpired(startTime, handle) ||
handle.removed()
))
{
handle = (EntryHandle) handle.next();
iteratorLogger.log(Level.FINEST,
"advanced current handle to {0}", handle);
}
if (handle == null)
return null;
EntryHandle h = handle;
handle = (EntryHandle) h.next();
return h.rep();
}
}
/**
* Return an object that can be used to perform a query that can
* return multiple matches and be restarted in another thread.
*
* @param tmpls An array of templates. Query will yield any
* entry that matches one or more of the templates.
* @param txnMgr Transaction that should be used with the query.
* May be <code>null</code>. If
* non-<code>null</code> any entries yielded by the
* query will be locked under the transaction.
* @param takeThem If <code>true</code> any entries yielded by
* the query should be removed.
* @param now Estimate of current time used to weed out
* expired entries, ok if old
* @return a new ContinuingQuery object.
*/
ContinuingQuery continuingQuery(EntryRep[] tmpls, TransactableMgr txn,
boolean takeThem, long now)
{
return new ContinuingQuery(tmpls, txn, takeThem, now);
}
/**
* Object that can be used to perform a query that can
* return multiple matches and be restarted in another thread.
* Assumes that is being invoked from only one thread
* at a time, but does handle synchronization between concurrent
* queries on the parent <code>EntryHolder</code>.
*/
class ContinuingQuery {
/** Templates being used for the query */
final private EntryRep[] tmpls;
/** Transaction (if any) being used for the query */
final private TransactableMgr txn;
/**
* <code>true</code> if entries yielded by the query should
* removed.
*/
final private boolean takeThem;
/** <code>EntryHandleTmplDesc</code> for the templates */
final private EntryHandleTmplDesc[] descs;
/** Time used to weed out expired entries, ok if old */
long now;
/**
* Current position in parent <code>EntryHolder</code>'s
* <code>contents</code>
*/
private EntryHandle handle;
/**
* Create a new <code>ContinuingQuery</code> object.
*
* @param tmpls An array of templates. Query will yield any
* entry that matches one or more of the templates.
* @param txnMgr Transaction that should be used with the query.
* May be <code>null</code>. If
* non-<code>null</code> any entries yielded by the
* query will be locked under the transaction.
* @param takeThem If <code>true</code> any entries yielded by
* the query should be removed.
* @param now Estimate of current time used to weed out
* expired entries, ok if old
* @return a new ContinuingQuery object.
*/
private ContinuingQuery(EntryRep[] tmpls, TransactableMgr txn,
boolean takeThem, long now)
{
this.tmpls = tmpls;
this.txn = txn;
this.takeThem = takeThem;
this.now = now;
handle = (EntryHandle)contents.head();
if (handle == null) {
// nothing to search - first next will return null
descs = null; // keep compiler happy
return;
}
descs = new EntryHandleTmplDesc[tmpls.length];
for (int i=0; i<tmpls.length; i++) {
descs[i] = EntryHandle.descFor(this.tmpls[i],
handle.rep().numFields());
}
}
/**
* <code>EntryHolder</code> queries have thread local state
* that get clobbered if another query (on a different or same
* <code>EntryHolder</code>, including <code>hasMatch</code>
* calls) is started in the same thread. The state also needs
* to be restored if it the query is continued in another
* thread. The <code>restart</code> method must be called to
* restore the thread local state.
* @param now Estimate of current time used to weed out
* expired entries, ok if old
*/
void restart(long now) {
if (handle == null)
return;
this.now = now;
handle.restart();
}
/**
* Return the next matching entry. Returns <code>null</code>
* if there are no matches remaining. Call {@link #restart
* restart} first if query is being used in a different thread
* from the last <code>next</code> call and/or the current
* thread has done any form of <code>EntryHolder</code> query
* on any holder since the last next call.
*
* @param conflictSet If non-null the <code>TransactableMgr</code>
* objects of any transactions that prevent
* a entry from being retured will
* be added to <code>conflictSet</code>. May
* be <code>null</code> in which case
* conflicting transaction will not be recorded.
* This method assumes that any concurrent access
* is being arbitrated by the set or by the caller.
* @param lockedEntrySet If non-null the ID of any entries that
* can't be retured because of conflicting
* transaction will be added to
* <code>lockedEntrySet</code>. May be
* <code>null</code> in which case unavailable
* entries will not be recorded. This method
* assumes that any concurrent access is being
* arbitrated by the set or by the caller.
* @param provisionallyRemovedEntrySet If the entry can not be
* read/taken because it has been provisionally
* removed then its handle will be placed in the
* passed <code>WeakHashMap</code> as a key (with
* null as the value). May be <code>null</code> in
* which case provisionally removed entries will not
* be recorded. This method assumes that any
* concurrent access is being arbitrated by the set
* or by the caller.
* @return a matching entry or <code>null</code>.
* @throws CannotJoinException if a match is found and
* the operation is to be performed under a transaction,
* but the transaction is no longer active.
*/
EntryHandle next(Set conflictSet, Set lockedEntrySet,
WeakHashMap provisionallyRemovedEntrySet)
throws CannotJoinException
{
matchingLogger.entering("ContinuingQuery", "next");
if (handle == null)
return null; // done
for (; handle != null; handle = (EntryHandle)handle.next()) {
if (handleMatch()) {
final boolean available =
confirmAvailabilityWithTxn(handle.rep(), handle, txn,
takeThem, now, conflictSet, lockedEntrySet,
provisionallyRemovedEntrySet);
if (available) {
// Don't want to return this guy twice.
final EntryHandle rslt = handle;
handle = (EntryHandle)handle.next();
return rslt;
}
}
}
return null;
}
/**
* Returns <code>true</code> if handle has not been removed
* and matches one or more of the templates
*/
private boolean handleMatch() {
if (handle.removed())
return false;
for (int i=0; i<tmpls.length; i++) {
final EntryRep tmpl = tmpls[i];
final EntryHandleTmplDesc desc = descs[i];
// Quick reject
if ((handle.hash() & desc.mask) != desc.hash)
continue;
if (!tmpl.matches(handle.rep()))
continue;
return true;
}
return false;
}
}
/**
* Remove the given handle from this holder and the <code>idMap</code>.
* If the handle isn't in this holder, this does nothing.
* @param h the <code>EntryHandle</code> to remove.
* @param recovery <code>true</code> if being called as part
* of log recovery.
* @return <code>true</code> if this call removed <code>h</code> and
* <code>false</code> otherwise.
*/
boolean remove(EntryHandle h, boolean recovery) {
assert (recovery || Thread.holdsLock(h));
final boolean ok = contents.remove(h);
h.removalComplete();
if (ok) {
idMap.remove(h.rep().getCookie());
/* This may cause an ifExists query to be resolved,
* even if this entry was not locked under a transaction
*/
if (!recovery) {
space.recordTransition(
new EntryTransition(h, null, false, false, false));
}
}
return ok;
}
/**
* Return the handle for the given <code>EntryRep</code> object.
* This is done via lookup in the <code>idMap</code>. The
* <code>idMap</code> is doing double duty here. The
* <code>LeaseDesc</code> associated with an <code>EntryRep</code>
* is also the rep's <code>EntryHandle</code>.
*/
private EntryHandle handleFor(EntryRep rep) {
return (EntryHandle) idMap.get(rep.getCookie());
}
/**
* Reap the expired elements (and the underlying FastList)
*/
void reap() {
// Examine each of the elements within the FastList to determine if
// any of them have expired. If they have, ensure that they are
// removed ("reaped") from the collection.
long now = System.currentTimeMillis();
for (EntryHandle handle = (EntryHandle) contents.head();
handle != null;
handle = (EntryHandle) handle.next())
{
// Don't try to remove things twice
if (handle.removed()) {
continue;
}
// Calling isExpired() will both make the check and remove it
// if necessary.
isExpired(now, handle);
}
// This provides the FastList with an opportunity to actually
// excise the items identified as "removed" from the list.
contents.reap();
}
}