mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

abobrov
12.03.2007 281a429e6f526e356353103ae3b60d45d75791c9
- [Issue 1588] consolidate cache entry locking code:
getEntry(lck) locking code logic is now consolidated in a single method
inside the EntryCache abstract class effectively moving it out of entry
cache implementing subclasses.

- [Issue 1589] entry cache implementations can lock and return stale entry:
Prevent stale entries by check [checking that given entry does indeed
exist in the cache], lock [acquiring a lock] and load [loading the entry].
5 files modified
957 ■■■■ changed files
opends/src/server/org/opends/server/api/EntryCache.java 221 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/DefaultEntryCache.java 18 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/FIFOEntryCache.java 235 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/FileSystemEntryCache.java 240 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/SoftReferenceEntryCache.java 243 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/api/EntryCache.java
@@ -36,7 +36,11 @@
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LockType;
import org.opends.server.types.LockManager;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.admin.std.server.EntryCacheCfg;
import org.opends.server.loggers.debug.DebugTracer;
import static org.opends.server.loggers.debug.DebugLogger.*;
@@ -70,6 +74,21 @@
       <T extends EntryCacheCfg>
{
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
  /**
   * The maximum length of time to try to obtain a lock
   * before giving up.
   */
  protected long lockTimeout;
  /**
   * Initializes this entry cache implementation so that it will be
   * available for storing and retrieving entries.
   *
@@ -136,6 +155,8 @@
   * Indicates whether the entry cache currently contains the entry
   * with the specified DN.  This method may be called without holding
   * any locks if a point-in-time check is all that is required.
   * Note that this method is called from @see #getEntry(DN entryDN,
   * LockType lockType, List lockList)
   *
   * @param  entryDN  The DN for which to make the determination.
   *
@@ -151,6 +172,8 @@
   * Retrieves the entry with the specified DN from the cache.  The
   * caller should have already acquired a read or write lock for the
   * entry if such protection is needed.
   * Note that this method is called from @see #getEntry(DN entryDN,
   * LockType lockType, List lockList)
   *
   * @param  entryDN  The DN of the entry to retrieve.
   *
@@ -162,27 +185,13 @@
  /**
   * Retrieves the entry ID for the entry with the specified DN from
   * the cache.  The caller should have already acquired a read or
   * write lock for the entry if such protection is needed.
   *
   * @param  entryDN  The DN of the entry for which to retrieve the
   *                  entry ID.
   *
   * @return  The entry ID for the requested entry, or -1 if it is not
   *          present in the cache.
   */
  public abstract long getEntryID(DN entryDN);
  /**
   * Retrieves the entry with the specified DN from the cache,
   * obtaining a lock on the entry before it is returned.  If the
   * entry is present in the cache, then a lock will be obtained for
   * that entry and appended to the provided list before the entry is
   * returned.  If the entry is not present, then no lock will be
   * obtained.
   * obtained.  Note that although this method is declared non-final
   * it is not recommended for subclasses to implement this method.
   *
   * @param  entryDN   The DN of the entry to retrieve.
   * @param  lockType  The type of lock to obtain (it may be
@@ -194,8 +203,131 @@
   * @return  The requested entry if it is present in the cache, or
   *          <CODE>null</CODE> if it is not present.
   */
  public abstract Entry getEntry(DN entryDN, LockType lockType,
                                 List<Lock> lockList);
  public Entry getEntry(DN entryDN,
                        LockType lockType,
                        List<Lock> lockList) {
    if (!containsEntry(entryDN)) {
      return null;
    }
    // Obtain a lock for the entry before actually retrieving the
    // entry itself thus preventing any stale entries being returned,
    // see Issue #1589 for more details.  If an error occurs, then
    // make sure no lock is held and return null. Otherwise, return
    // the entry.
    switch (lockType)
    {
      case READ:
        // Try to obtain a read lock for the entry.
        Lock readLock = LockManager.lockRead(entryDN, lockTimeout);
        if (readLock == null)
        {
          // We couldn't get the lock, so we have to return null.
          return null;
        }
        else
        {
          try
          {
            lockList.add(readLock);
            // and load.
            Entry entry = getEntry(entryDN);
            if (entry == null)
            {
              lockList.remove(readLock);
              LockManager.unlock(entryDN, readLock);
              return null;
            }
            return entry;
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            // The attempt to add the lock to the list failed,
            // so we need to release the lock and return null.
            try
            {
              LockManager.unlock(entryDN, readLock);
            }
            catch (Exception e2)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, e2);
              }
            }
            return null;
          }
        }
      case WRITE:
        // Try to obtain a write lock for the entry.
        Lock writeLock = LockManager.lockWrite(entryDN, lockTimeout);
        if (writeLock == null)
        {
          // We couldn't get the lock, so we have to return null.
          return null;
        }
        else
        {
          try
          {
            lockList.add(writeLock);
            // and load.
            Entry entry = getEntry(entryDN);
            if (entry == null)
            {
              lockList.remove(writeLock);
              LockManager.unlock(entryDN, writeLock);
              return null;
            }
            return entry;
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            // The attempt to add the lock to the list failed,
            // so we need to release the lock and return null.
            try
            {
              LockManager.unlock(entryDN, writeLock);
            }
            catch (Exception e2)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, e2);
              }
            }
            return null;
          }
        }
      case NONE:
        // We don't need to obtain a lock, so just return the entry.
        Entry entry = getEntry(entryDN);
        if (entry == null)
        {
          return null;
        }
        return entry;
      default:
        // This is an unknown type of lock, so we'll return null.
        return null;
    }
  }
@@ -205,7 +337,8 @@
   * entry is present in the cache, then a lock  will be obtained for
   * that entry and appended to the provided list before the entry is
   * returned.  If the entry is not present, then no lock will be
   * obtained.
   * obtained.  Note that although this method is declared non-final
   * it is not recommended for subclasses to implement this method.
   *
   * @param  backend   The backend associated with the entry to
   *                   retrieve.
@@ -220,9 +353,54 @@
   * @return  The requested entry if it is present in the cache, or
   *          <CODE>null</CODE> if it is not present.
   */
  public abstract Entry getEntry(Backend backend, long entryID,
  public Entry getEntry(Backend backend, long entryID,
                                 LockType lockType,
                                 List<Lock> lockList);
                                 List<Lock> lockList) {
    // Translate given backend/entryID pair to entryDN.
    DN entryDN = getEntryDN(backend, entryID);
    if (entryDN == null) {
      return null;
    }
    // Delegate to by DN lock and load method.
    return getEntry(entryDN, lockType, lockList);
  }
  /**
   * Retrieves the entry ID for the entry with the specified DN from
   * the cache.  The caller should have already acquired a read or
   * write lock for the entry if such protection is needed.
   *
   * @param  entryDN  The DN of the entry for which to retrieve the
   *                  entry ID.
   *
   * @return  The entry ID for the requested entry, or -1 if it is
   *          not present in the cache.
   */
  public abstract long getEntryID(DN entryDN);
  /**
   * Retrieves the entry DN for the entry with the specified ID on
   * the specific backend from the cache.  The caller should have
   * already acquired a read or write lock for the entry if such
   * protection is needed.
   * Note that this method is called from @see #getEntry(Backend
   * backend, long entryID, LockType lockType, List lockList)
   *
   * @param  backend  The backend associated with the entry for
   *                  which to retrieve the entry DN.
   * @param  entryID  The entry ID within the provided backend
   *                  for which to retrieve the entry DN.
   *
   * @return  The entry DN for the requested entry, or
   *          <CODE>null</CODE> if it is not present in the cache.
   */
  protected abstract DN getEntryDN(Backend backend, long entryID);
@@ -315,4 +493,3 @@
   */
  public abstract void handleLowMemory();
}
opends/src/server/org/opends/server/extensions/DefaultEntryCache.java
@@ -30,7 +30,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.EntryCacheCfg;
@@ -41,7 +40,6 @@
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LockType;
import org.opends.server.types.ResultCode;
@@ -128,21 +126,9 @@
  /**
   * {@inheritDoc}
   */
  public Entry getEntry(DN entryDN, LockType lockType, List<Lock> lockList)
  protected DN getEntryDN(Backend backend, long entryID)
  {
    // This implementation does not store entries.
    return null;
  }
  /**
   * {@inheritDoc}
   */
  public Entry getEntry(Backend backend, long entryID, LockType lockType,
                        List<Lock> lockList)
  {
    // This implementation does not store entries.
    // This implementation does not store any entries.
    return null;
  }
opends/src/server/org/opends/server/extensions/FIFOEntryCache.java
@@ -53,8 +53,6 @@
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LockManager;
import org.opends.server.types.LockType;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
@@ -144,9 +142,6 @@
  // the cache.
  private Lock cacheLock;
  // The maximum length of time to try to obtain a lock before giving up.
  private long lockTimeout;
  // The maximum amount of memory in bytes that the JVM will be allowed to use
  // before we need to start purging entries.
  private long maxAllowedMemory;
@@ -288,229 +283,17 @@
  /**
   * {@inheritDoc}
   */
  public Entry getEntry(DN entryDN, LockType lockType, List<Lock> lockList)
  protected DN getEntryDN(Backend backend, long entryID)
  {
    // Get the entry from the DN map if it is present.  If not, then return
    // null.
    CacheEntry entry = dnMap.get(entryDN);
    if (entry == null)
    {
      return null;
    // Locate specific backend map and return the entry DN by ID.
    HashMap<Long,CacheEntry> backendMap = idMap.get(backend);
    if (backendMap != null) {
      CacheEntry e = backendMap.get(entryID);
      if (e != null) {
        return e.getDN();
      }
    }
    // Obtain a lock for the entry as appropriate.  If an error occurs, then
    // make sure no lock is held and return null.  Otherwise, return the entry.
    switch (lockType)
    {
      case READ:
        // Try to obtain a read lock for the entry.
        Lock readLock = LockManager.lockRead(entryDN, lockTimeout);
        if (readLock == null)
        {
          // We couldn't get the lock, so we have to return null.
          return null;
        }
        else
        {
          try
          {
            lockList.add(readLock);
            return entry.getEntry();
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            // The attempt to add the lock to the list failed, so we need to
            // release the lock and return null.
            try
            {
              LockManager.unlock(entryDN, readLock);
            }
            catch (Exception e2)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, e2);
              }
            }
            return null;
          }
        }
      case WRITE:
        // Try to obtain a write lock for the entry.
        Lock writeLock = LockManager.lockWrite(entryDN, lockTimeout);
        if (writeLock == null)
        {
          // We couldn't get the lock, so we have to return null.
          return null;
        }
        else
        {
          try
          {
            lockList.add(writeLock);
            return entry.getEntry();
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            // The attempt to add the lock to the list failed, so we need to
            // release the lock and return null.
            try
            {
              LockManager.unlock(entryDN, writeLock);
            }
            catch (Exception e2)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, e2);
              }
            }
            return null;
          }
        }
      case NONE:
        // We don't need to obtain a lock, so just return the entry.
        return entry.getEntry();
      default:
        // This is an unknown type of lock, so we'll return null.
        return null;
    }
  }
  /**
   * {@inheritDoc}
   */
  public Entry getEntry(Backend backend, long entryID, LockType lockType,
                        List<Lock> lockList)
  {
    // Get the hash map for the provided backend.  If it isn't present, then
    // return null.
    HashMap<Long,CacheEntry> map = idMap.get(backend);
    if (map == null)
    {
      return null;
    }
    // Get the entry from the map by its ID.  If it isn't present, then return
    // null.
    CacheEntry cacheEntry = map.get(entryID);
    if (cacheEntry == null)
    {
      return null;
    }
    // Obtain a lock for the entry as appropriate.  If an error occurs, then
    // make sure no lock is held and return null.  Otherwise, return the entry.
    Entry entry = cacheEntry.getEntry();
    switch (lockType)
    {
      case READ:
        // Try to obtain a read lock for the entry.
        Lock readLock = LockManager.lockRead(entry.getDN(), lockTimeout);
        if (readLock == null)
        {
          // We couldn't get the lock, so we have to return null.
          return null;
        }
        else
        {
          try
          {
            lockList.add(readLock);
            return entry;
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            // The attempt to add the lock to the list failed, so we need to
            // release the lock and return null.
            try
            {
              LockManager.unlock(entry.getDN(), readLock);
            }
            catch (Exception e2)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, e2);
              }
            }
            return null;
          }
        }
      case WRITE:
        // Try to obtain a write lock for the entry.
        Lock writeLock = LockManager.lockWrite(entry.getDN(), lockTimeout);
        if (writeLock == null)
        {
          // We couldn't get the lock, so we have to return null.
          return null;
        }
        else
        {
          try
          {
            lockList.add(writeLock);
            return entry;
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            // The attempt to add the lock to the list failed, so we need to
            // release the lock and return null.
            try
            {
              LockManager.unlock(entry.getDN(), writeLock);
            }
            catch (Exception e2)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, e2);
              }
            }
            return null;
          }
        }
      case NONE:
        // We don't need to obtain a lock, so just return the entry.
        return entry;
      default:
        // This is an unknown type of lock, so we'll return null.
        return null;
    }
    return null;
  }
opends/src/server/org/opends/server/extensions/FileSystemEntryCache.java
@@ -67,11 +67,9 @@
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LockType;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.FilePermission;
import org.opends.server.types.LockManager;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.loggers.debug.DebugTracer;
import static org.opends.server.loggers.debug.DebugLogger.*;
@@ -167,9 +165,6 @@
  private Lock cacheReadLock;
  private Lock cacheWriteLock;
  // The maximum length of time to try to obtain a lock before giving up.
  private long lockTimeout;
  // The mapping between DNs and IDs. This is the main index map for this
  // cache, keyed to the underlying JE database where entries are stored.
  private Map<DN,Long> dnMap;
@@ -729,246 +724,23 @@
  /**
   * {@inheritDoc}
   */
  public Entry getEntry(DN entryDN, LockType lockType, List<Lock> lockList) {
  protected DN getEntryDN(Backend backend, long entryID) {
    Entry entry = getEntry(entryDN);
    if (entry == null)
    {
      return null;
    }
    // Obtain a lock for the entry as appropriate.  If an error occurs, then
    // make sure no lock is held and return null.  Otherwise, return the entry.
    switch (lockType)
    {
      case READ:
        // Try to obtain a read lock for the entry.
        Lock readLock = LockManager.lockRead(entryDN, lockTimeout);
        if (readLock == null)
        {
          // We couldn't get the lock, so we have to return null.
          return null;
        }
        else
        {
          try
          {
            lockList.add(readLock);
            return entry;
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            // The attempt to add the lock to the list failed, so we need to
            // release the lock and return null.
            try
            {
              LockManager.unlock(entryDN, readLock);
            }
            catch (Exception e2)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, e2);
              }
            }
            return null;
          }
        }
      case WRITE:
        // Try to obtain a write lock for the entry.
        Lock writeLock = LockManager.lockWrite(entryDN, lockTimeout);
        if (writeLock == null)
        {
          // We couldn't get the lock, so we have to return null.
          return null;
        }
        else
        {
          try
          {
            lockList.add(writeLock);
            return entry;
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            // The attempt to add the lock to the list failed, so we need to
            // release the lock and return null.
            try
            {
              LockManager.unlock(entryDN, writeLock);
            }
            catch (Exception e2)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, e2);
              }
            }
            return null;
          }
        }
      case NONE:
        // We don't need to obtain a lock, so just return the entry.
        return entry;
      default:
        // This is an unknown type of lock, so we'll return null.
        return null;
    }
  }
  /**
   * Retrieves the requested entry if it is present in the cache.
   *
   * @param  backend   The backend associated with the entry to retrieve.
   * @param  entryID   The entry ID within the provided backend for the
   *                   specified entry.
   *
   * @return  The requested entry if it is present in the cache, or
   *          <CODE>null</CODE> if it is not present.
   */
  public Entry getEntry(Backend backend, long entryID) {
    Entry entry = null;
    DN entryDN = null;
    cacheReadLock.lock();
    try {
      // Get the map for the provided backend.  If it isn't present, then
      // return null.
      Map map = backendMap.get(backend);
      if ( !(map == null) ) {
        // Get the entry from the map by its ID.  If it isn't present, then
        // return null.
        DN dn = (DN) map.get(entryID);
        if ( !(dn == null) ) {
          if (dnMap.containsKey(dn)) {
            entry = getEntryFromDB(dn);
          }
        }
        // Get the entry DN from the map by its ID.  If it isn't present,
        // then return null.
        entryDN = (DN) map.get(entryID);
      }
    } finally {
      cacheReadLock.unlock();
    }
    return entry;
  }
  /**
   * {@inheritDoc}
   */
  public Entry getEntry(Backend backend, long entryID, LockType lockType,
          List<Lock> lockList) {
    Entry entry = getEntry(backend, entryID);
    if (entry == null)
    {
      return null;
    }
    // Obtain a lock for the entry as appropriate.  If an error occurs, then
    // make sure no lock is held and return null.  Otherwise, return the entry.
    switch (lockType)
    {
      case READ:
        // Try to obtain a read lock for the entry.
        Lock readLock = LockManager.lockRead(entry.getDN(), lockTimeout);
        if (readLock == null)
        {
          // We couldn't get the lock, so we have to return null.
          return null;
        }
        else
        {
          try
          {
            lockList.add(readLock);
            return entry;
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            // The attempt to add the lock to the list failed, so we need to
            // release the lock and return null.
            try
            {
              LockManager.unlock(entry.getDN(), readLock);
            }
            catch (Exception e2)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, e2);
              }
            }
            return null;
          }
        }
      case WRITE:
        // Try to obtain a write lock for the entry.
        Lock writeLock = LockManager.lockWrite(entry.getDN(), lockTimeout);
        if (writeLock == null)
        {
          // We couldn't get the lock, so we have to return null.
          return null;
        }
        else
        {
          try
          {
            lockList.add(writeLock);
            return entry;
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            // The attempt to add the lock to the list failed, so we need to
            // release the lock and return null.
            try
            {
              LockManager.unlock(entry.getDN(), writeLock);
            }
            catch (Exception e2)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, e2);
              }
            }
            return null;
          }
        }
      case NONE:
        // We don't need to obtain a lock, so just return the entry.
        return entry;
      default:
        // This is an unknown type of lock, so we'll return null.
        return null;
    }
    return entryDN;
  }
  /**
opends/src/server/org/opends/server/extensions/SoftReferenceEntryCache.java
@@ -35,7 +35,6 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.EntryCacheCfg;
@@ -52,7 +51,6 @@
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LockManager;
import org.opends.server.types.LockType;
import org.opends.server.types.SearchFilter;
@@ -105,10 +103,6 @@
  // cache.
  private HashSet<SearchFilter> includeFilters;
  // The maximum length of time that we will wait while trying to obtain a lock
  // on an entry.
  private long lockTimeout;
  // The reference queue that will be used to notify us whenever a soft
  // reference is freed.
  private ReferenceQueue<CacheEntry> referenceQueue;
@@ -254,237 +248,18 @@
  /**
   * {@inheritDoc}
   */
  public Entry getEntry(DN entryDN, LockType lockType,
                        List<Lock> lockList)
  protected DN getEntryDN(Backend backend, long entryID)
  {
    SoftReference<CacheEntry> ref = dnMap.get(entryDN);
    if (ref == null)
    {
      return null;
    }
    else
    {
      CacheEntry cacheEntry = ref.get();
      if (cacheEntry == null)
      {
        return null;
      }
      else
      {
        switch (lockType)
        {
          case READ:
            // Try to obtain a read lock for the entry, but don't wait too long
            // so only try once.
            Lock readLock = LockManager.lockRead(entryDN);
            if (readLock == null)
            {
              // We couldn't get the lock, so we have to return null.
              return null;
            }
            else
            {
              try
              {
                lockList.add(readLock);
                return cacheEntry.getEntry();
              }
              catch (Exception e)
              {
                if (debugEnabled())
                {
                  TRACER.debugCaught(DebugLogLevel.ERROR, e);
                }
                // The attempt to add the lock to the list failed, so we need to
                // release the lock and return null.
                try
                {
                  LockManager.unlock(entryDN, readLock);
                }
                catch (Exception e2)
                {
                  if (debugEnabled())
                  {
                    TRACER.debugCaught(DebugLogLevel.ERROR, e2);
                  }
                }
                return null;
              }
            }
          case WRITE:
            // Try to obtain a write lock for the entry, but don't wait too long
            // so only try once.
            Lock writeLock = LockManager.lockWrite(entryDN);
            if (writeLock == null)
            {
              // We couldn't get the lock, so we have to return null.
              return null;
            }
            else
            {
              try
              {
                lockList.add(writeLock);
                return cacheEntry.getEntry();
              }
              catch (Exception e)
              {
                if (debugEnabled())
                {
                  TRACER.debugCaught(DebugLogLevel.ERROR, e);
                }
                // The attempt to add the lock to the list failed, so we need to
                // release the lock and return null.
                try
                {
                  LockManager.unlock(entryDN, writeLock);
                }
                catch (Exception e2)
                {
                  if (debugEnabled())
                  {
                    TRACER.debugCaught(DebugLogLevel.ERROR, e2);
                  }
                }
                return null;
              }
            }
          case NONE:
            // There is no lock required, so we can just return the entry.
            return cacheEntry.getEntry();
          default:
            // This is an unknown type of lock, so we can't provide it.
            return null;
        }
    // Locate specific backend map and return the entry DN by ID.
    ConcurrentHashMap<Long,SoftReference<CacheEntry>>
      backendMap = idMap.get(backend);
    if (backendMap != null) {
      SoftReference<CacheEntry> ref = backendMap.get(entryID);
      if ((ref != null) && (ref.get() != null)) {
        return ref.get().getDN();
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  public Entry getEntry(Backend backend, long entryID,
                        LockType lockType, List<Lock> lockList)
  {
    ConcurrentHashMap<Long,SoftReference<CacheEntry>> map = idMap.get(backend);
    if (map == null)
    {
      return null;
    }
    SoftReference<CacheEntry> ref = map.get(entryID);
    if (ref == null)
    {
      return null;
    }
    CacheEntry cacheEntry = ref.get();
    if (cacheEntry == null)
    {
      return null;
    }
    switch (lockType)
    {
      case READ:
        // Try to obtain a read lock for the entry, but don't wait too long so
        // only try once.
        Lock readLock = LockManager.lockRead(cacheEntry.getDN());
        if (readLock == null)
        {
          // We couldn't get the lock, so we have to return null.
          return null;
        }
        else
        {
          try
          {
            lockList.add(readLock);
            return cacheEntry.getEntry();
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            // The attempt to add the lock to the list failed, so we need to
            // release the lock and return null.
            try
            {
              LockManager.unlock(cacheEntry.getDN(), readLock);
            }
            catch (Exception e2)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, e2);
              }
            }
            return null;
          }
        }
      case WRITE:
        // Try to obtain a write lock for the entry, but don't wait too long so
        // only try once.
        Lock writeLock = LockManager.lockWrite(cacheEntry.getDN());
        if (writeLock == null)
        {
          // We couldn't get the lock, so we have to return null.
          return null;
        }
        else
        {
          try
          {
            lockList.add(writeLock);
            return cacheEntry.getEntry();
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            // The attempt to add the lock to the list failed, so we need to
            // release the lock and return null.
            try
            {
              LockManager.unlock(cacheEntry.getDN(), writeLock);
            }
            catch (Exception e2)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, e2);
              }
            }
            return null;
          }
        }
      case NONE:
        // There is no lock required, so we can just return the entry.
        return cacheEntry.getEntry();
      default:
        // This is an unknown type of lock, so we can't provide it.
        return null;
    }
    return null;
  }