- [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].
| | |
| | | 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.*; |
| | | |
| | | |
| | | |
| | |
| | | <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. |
| | | * |
| | |
| | | * 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. |
| | | * |
| | |
| | | * 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. |
| | | * |
| | |
| | | |
| | | |
| | | /** |
| | | * 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 |
| | |
| | | * @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; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | |
| | | * 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. |
| | |
| | | * @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); |
| | | |
| | | |
| | | |
| | |
| | | */ |
| | | public abstract void handleLowMemory(); |
| | | } |
| | | |
| | |
| | | |
| | | 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; |
| | |
| | | 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; |
| | | |
| | | |
| | |
| | | /** |
| | | * {@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; |
| | | } |
| | | |
| | |
| | | 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; |
| | | |
| | |
| | | // 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; |
| | |
| | | /** |
| | | * {@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; |
| | | } |
| | | |
| | | |
| | |
| | | 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.*; |
| | |
| | | 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; |
| | |
| | | /** |
| | | * {@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; |
| | | } |
| | | |
| | | /** |
| | |
| | | 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; |
| | |
| | | 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; |
| | | |
| | | |
| | |
| | | // 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; |
| | |
| | | /** |
| | | * {@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; |
| | | } |
| | | |
| | | |