From 281a429e6f526e356353103ae3b60d45d75791c9 Mon Sep 17 00:00:00 2001
From: abobrov <abobrov@localhost>
Date: Wed, 11 Jul 2007 23:03:46 +0000
Subject: [PATCH] - [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.

---
 opends/src/server/org/opends/server/api/EntryCache.java |  221 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 199 insertions(+), 22 deletions(-)

diff --git a/opends/src/server/org/opends/server/api/EntryCache.java b/opends/src/server/org/opends/server/api/EntryCache.java
index 57c01f6..0907459 100644
--- a/opends/src/server/org/opends/server/api/EntryCache.java
+++ b/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();
 }
-

--
Gitblit v1.10.0