/**
   * Purge least recently used object from the cache
   *
   * @return recyclable CacheEntry
   */
  protected CacheEntry purgeEntry() throws IOException {
    CacheEntry entry = _first;
    if (entry == null) return new CacheEntry(-1, null, null, false);

    if (entry._isDirty) _recman.update(entry._recid, entry._obj, entry._serializer);
    removeEntry(entry);
    _hash.remove(entry._recid);

    entry._obj = null;
    entry._serializer = null;
    entry._isDirty = false;
    return entry;
  }
  /**
   * Place an object in the cache.
   *
   * @throws IOException
   */
  protected void cachePut(long recid, Object value, Serializer serializer, boolean dirty)
      throws IOException {
    CacheEntry entry = _hash.get(recid);
    if (entry != null) {
      entry._obj = value;
      entry._serializer = serializer;
      touchEntry(entry);
    } else {

      if (_hash.size() == _max) {
        // purge and recycle entry
        entry = purgeEntry();
        entry._recid = recid;
        entry._obj = value;
        entry._isDirty = dirty;
        entry._serializer = serializer;
      } else {
        entry = new CacheEntry(recid, value, serializer, dirty);
      }
      addEntry(entry);
      _hash.put(entry._recid, entry);
    }
  }
 public synchronized <A> void update(long recid, A obj, Serializer<A> serializer)
     throws IOException {
   checkIfClosed();
   if (_softCache)
     synchronized (_softHash) {
       // soft cache can not contain dirty objects
       SoftCacheEntry e = _softHash.remove(recid);
       if (e != null) {
         e.clear();
       }
     }
   CacheEntry entry = cacheGet(recid);
   if (entry != null) {
     // reuse existing cache entry
     entry._obj = obj;
     entry._serializer = serializer;
     entry._isDirty = true;
   } else {
     cachePut(recid, obj, serializer, true);
   }
 }