[infinispan-commits] Infinispan SVN: r306 - in trunk/cachestore/bdbje/src: test/java/org/infinispan/loaders/bdbje and 1 other directory.

infinispan-commits at lists.jboss.org infinispan-commits at lists.jboss.org
Thu May 14 16:32:43 EDT 2009


Author: adriancole
Date: 2009-05-14 16:32:43 -0400 (Thu, 14 May 2009)
New Revision: 306

Modified:
   trunk/cachestore/bdbje/src/main/java/org/infinispan/loaders/bdbje/BdbjeCacheStore.java
   trunk/cachestore/bdbje/src/main/java/org/infinispan/loaders/bdbje/BdbjeCacheStoreConfig.java
   trunk/cachestore/bdbje/src/main/java/org/infinispan/loaders/bdbje/BdbjeResourceFactory.java
   trunk/cachestore/bdbje/src/test/java/org/infinispan/loaders/bdbje/BdbjeCacheStoreConfigTest.java
   trunk/cachestore/bdbje/src/test/java/org/infinispan/loaders/bdbje/BdbjeCacheStoreTest.java
Log:
ISPN-76: tuned expiry logic

Modified: trunk/cachestore/bdbje/src/main/java/org/infinispan/loaders/bdbje/BdbjeCacheStore.java
===================================================================
--- trunk/cachestore/bdbje/src/main/java/org/infinispan/loaders/bdbje/BdbjeCacheStore.java	2009-05-14 18:41:20 UTC (rev 305)
+++ trunk/cachestore/bdbje/src/main/java/org/infinispan/loaders/bdbje/BdbjeCacheStore.java	2009-05-14 20:32:43 UTC (rev 306)
@@ -3,33 +3,26 @@
 import com.sleepycat.bind.serial.StoredClassCatalog;
 import com.sleepycat.collections.CurrentTransaction;
 import com.sleepycat.collections.StoredMap;
-import com.sleepycat.je.Cursor;
-import com.sleepycat.je.Database;
-import com.sleepycat.je.DatabaseEntry;
-import com.sleepycat.je.DatabaseException;
-import com.sleepycat.je.Environment;
-import com.sleepycat.je.EnvironmentConfig;
-import com.sleepycat.je.JEVersion;
-import com.sleepycat.je.OperationStatus;
-import com.sleepycat.je.Transaction;
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.je.*;
 import com.sleepycat.util.ExceptionUnwrapper;
 import org.infinispan.Cache;
-import org.infinispan.transaction.xa.GlobalTransaction;
 import org.infinispan.container.entries.InternalCacheEntry;
 import org.infinispan.loaders.AbstractCacheStore;
 import org.infinispan.loaders.CacheLoaderConfig;
 import org.infinispan.loaders.CacheLoaderException;
 import org.infinispan.loaders.modifications.Modification;
 import org.infinispan.marshall.Marshaller;
+import org.infinispan.transaction.xa.GlobalTransaction;
 import org.infinispan.util.ReflectionUtil;
 import org.infinispan.util.logging.Log;
 import org.infinispan.util.logging.LogFactory;
 
 import java.io.File;
+import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -61,473 +54,503 @@
  */
 public class BdbjeCacheStore extends AbstractCacheStore {
 
-   private static final Log log = LogFactory.getLog(BdbjeCacheStore.class);
-   private static final boolean trace = log.isTraceEnabled();
+    private static final Log log = LogFactory.getLog(BdbjeCacheStore.class);
+    private static final boolean trace = log.isTraceEnabled();
 
-   private BdbjeCacheStoreConfig cfg;
-   private Cache cache;
+    private BdbjeCacheStoreConfig cfg;
+    private Cache cache;
 
-   private Environment env;
-   private StoredClassCatalog catalog;
-   private Database cacheDb;
-   private StoredMap<Object, InternalCacheEntry> cacheMap;
+    private Environment env;
+    private StoredClassCatalog catalog;
+    private Database cacheDb;
+    private Database expiryDb;
+    private StoredMap<Object, InternalCacheEntry> cacheMap;
+    private StoredSortedMap<Long, Object> expiryMap;
 
-   private PreparableTransactionRunner transactionRunner;
-   private Map<GlobalTransaction, Transaction> txnMap;
-   private CurrentTransaction currentTransaction;
-   private BdbjeResourceFactory factory;
 
-   /**
-    * {@inheritDoc} This implementation expects config to be an instance of {@link BdbjeCacheStoreConfig} <p /> note
-    * that the <code>m</code> is not currently used as SleepyCat has its own efficient solution.
-    *
-    * @see BdbjeCacheStoreConfig
-    */
-   public void init(CacheLoaderConfig config, Cache cache, Marshaller m) {
-      BdbjeCacheStoreConfig cfg = (BdbjeCacheStoreConfig) config;
-      init(cfg, new BdbjeResourceFactory(cfg), cache, m);
-   }
+    private PreparableTransactionRunner transactionRunner;
+    private Map<GlobalTransaction, Transaction> txnMap;
+    private CurrentTransaction currentTransaction;
+    private BdbjeResourceFactory factory;
 
-   void init(BdbjeCacheStoreConfig cfg, BdbjeResourceFactory factory, Cache cache, Marshaller m) {
-      if (trace) log.trace("initializing BdbjeCacheStore");
-      printLicense();
-      super.init(cfg, cache, m);
-      this.cfg = cfg;
-      this.factory = factory;
-      this.cache = cache;
-   }
+    /**
+     * {@inheritDoc} This implementation expects config to be an instance of {@link BdbjeCacheStoreConfig} <p /> note
+     * that the <code>m</code> is not currently used as SleepyCat has its own efficient solution.
+     *
+     * @see BdbjeCacheStoreConfig
+     */
+    public void init(CacheLoaderConfig config, Cache cache, Marshaller m) {
+        BdbjeCacheStoreConfig cfg = (BdbjeCacheStoreConfig) config;
+        init(cfg, new BdbjeResourceFactory(cfg), cache, m);
+    }
 
-   /**
-    * {@inheritDoc}
-    *
-    * @return {@link BdbjeCacheStoreConfig}
-    */
-   public Class<? extends CacheLoaderConfig> getConfigurationClass() {
-      return BdbjeCacheStoreConfig.class;
-   }
+    void init(BdbjeCacheStoreConfig cfg, BdbjeResourceFactory factory, Cache cache, Marshaller m) {
+        if (trace) log.trace("initializing BdbjeCacheStore");
+        printLicense();
+        super.init(cfg, cache, m);
+        this.cfg = cfg;
+        this.factory = factory;
+        this.cache = cache;
+    }
 
-   /**
-    * {@inheritDoc} Validates configuration, configures and opens the {@link Environment}, then {@link
-    * org.infinispan.loaders.bdbje.BdbjeCacheStore#openSleepyCatResources() opens the databases}.  When this is
-    * finished, transactional and purging services are instantiated.
-    */
-   public void start() throws CacheLoaderException {
-      if (trace) log.trace("starting BdbjeCacheStore");
+    /**
+     * {@inheritDoc}
+     *
+     * @return {@link BdbjeCacheStoreConfig}
+     */
+    public Class<? extends CacheLoaderConfig> getConfigurationClass() {
+        return BdbjeCacheStoreConfig.class;
+    }
 
-      openSleepyCatResources();
-      openTransactionServices();
-      super.start();
+    /**
+     * {@inheritDoc} Validates configuration, configures and opens the {@link Environment}, then {@link
+     * org.infinispan.loaders.bdbje.BdbjeCacheStore#openSleepyCatResources() opens the databases}.  When this is
+     * finished, transactional and purging services are instantiated.
+     */
+    public void start() throws CacheLoaderException {
+        if (trace) log.trace("starting BdbjeCacheStore");
 
-      log.debug("started cache store {1}", this);
-   }
+        openSleepyCatResources();
+        openTransactionServices();
+        super.start();
 
-   private void openTransactionServices() {
-      txnMap = new ConcurrentHashMap<GlobalTransaction, Transaction>();
-      currentTransaction = factory.createCurrentTransaction(env);
-      transactionRunner = factory.createPreparableTransactionRunner(env);
-   }
+        log.debug("started cache store {1}", this);
+    }
 
-   /**
-    * Opens the SleepyCat environment and all databases.  A {@link StoredMap} instance is provided which persists the
-    * CacheStore.
-    */
-   private void openSleepyCatResources() throws CacheLoaderException {
-      if (trace) log.trace("creating je environment with home dir {0}", cfg.getLocation());
+    private void openTransactionServices() {
+        txnMap = new ConcurrentHashMap<GlobalTransaction, Transaction>();
+        currentTransaction = factory.createCurrentTransaction(env);
+        transactionRunner = factory.createPreparableTransactionRunner(env);
+    }
 
-      if (cfg.getCacheDbName() == null) cfg.setCacheDbName(cache.getName());
-      if (cfg.getCatalogDbName() == null) cfg.setCatalogDbName(cfg.getCacheDbName() + "_class_catalog");
+    /**
+     * Opens the SleepyCat environment and all databases.  A {@link StoredMap} instance is provided which persists the
+     * CacheStore.
+     */
+    private void openSleepyCatResources() throws CacheLoaderException {
+        if (trace) log.trace("creating je environment with home dir {0}", cfg.getLocation());
 
-      File location = verifyOrCreateEnvironmentDirectory(new File(cfg.getLocation()));
-      try {
-         env = factory.createEnvironment(location);
-         cacheDb = factory.createDatabase(env, cfg.getCacheDbName());
-         Database catalogDb = factory.createDatabase(env, cfg.getCatalogDbName());
-         catalog = factory.createStoredClassCatalog(catalogDb);
-         cacheMap = factory.createStoredMapViewOfDatabase(cacheDb, catalog, marshaller);
-      } catch (DatabaseException e) {
-         throw convertToCacheLoaderException("could not open sleepycat je resource", e);
-      }
-   }
+        if (cfg.getCacheDbName() == null) cfg.setCacheDbName(cache.getName());
+        if (cfg.getCatalogDbName() == null) cfg.setCatalogDbName(cfg.getCacheDbName() + "_class_catalog");
+        if (cfg.getExpiryDbName() == null) cfg.setExpiryDbName(cfg.getCacheDbName() + "_expiry");
 
-   // not private so that this can be unit tested
-   File verifyOrCreateEnvironmentDirectory(File location) throws CacheLoaderException {
-      if (!location.exists()) {
-         boolean created = location.mkdirs();
-         if (!created) throw new CacheLoaderException("Unable to create cache loader location " + location);
+        File location = verifyOrCreateEnvironmentDirectory(new File(cfg.getLocation()));
+        try {
+            env = factory.createEnvironment(location);
+            cacheDb = factory.createDatabase(env, cfg.getCacheDbName());
+            Database catalogDb = factory.createDatabase(env, cfg.getCatalogDbName());
+            expiryDb = factory.createDatabase(env, cfg.getExpiryDbName());
+            catalog = factory.createStoredClassCatalog(catalogDb);
+            cacheMap = factory.createStoredMapViewOfDatabase(cacheDb, catalog, marshaller);
+            expiryMap = factory.createStoredSortedMapForKeyExpiry(expiryDb, catalog, marshaller);
+        } catch (DatabaseException e) {
+            throw convertToCacheLoaderException("could not open sleepycat je resource", e);
+        }
+    }
 
-      }
-      if (!location.isDirectory()) {
-         throw new CacheLoaderException("Cache loader location [" + location + "] is not a directory!");
-      }
-      return location;
-   }
+    // not private so that this can be unit tested
+    File verifyOrCreateEnvironmentDirectory(File location) throws CacheLoaderException {
+        if (!location.exists()) {
+            boolean created = location.mkdirs();
+            if (!created) throw new CacheLoaderException("Unable to create cache loader location " + location);
 
-   /**
-    * Stops transaction and purge processing and closes the SleepyCat environment.  The environment and databases are
-    * not removed from the file system. Exceptions during close of databases are ignored as closing the environment will
-    * ensure the databases are also.
-    */
-   public void stop() throws CacheLoaderException {
-      if (trace) log.trace("stopping BdbjeCacheStore");
-      super.stop();
-      closeTransactionServices();
-      closeSleepyCatResources();
-      log.debug("started cache store {1}", this);
-   }
+        }
+        if (!location.isDirectory()) {
+            throw new CacheLoaderException("Cache loader location [" + location + "] is not a directory!");
+        }
+        return location;
+    }
 
-   private void closeTransactionServices() {
-      transactionRunner = null;
-      currentTransaction = null;
-      txnMap = null;
-   }
+    /**
+     * Stops transaction and purge processing and closes the SleepyCat environment.  The environment and databases are
+     * not removed from the file system. Exceptions during close of databases are ignored as closing the environment will
+     * ensure the databases are also.
+     */
+    public void stop() throws CacheLoaderException {
+        if (trace) log.trace("stopping BdbjeCacheStore");
+        super.stop();
+        closeTransactionServices();
+        closeSleepyCatResources();
+        log.debug("started cache store {1}", this);
+    }
 
-   private void closeSleepyCatResources() throws CacheLoaderException {
-      cacheMap = null;
-      closeDatabases();
-      closeEnvironment();
-   }
+    private void closeTransactionServices() {
+        transactionRunner = null;
+        currentTransaction = null;
+        txnMap = null;
+    }
 
-   /**
-    * Exceptions are ignored so that {@link org.infinispan.loaders.bdbje.BdbjeCacheStore#closeEnvironment()}  will
-    * execute.
-    */
-   private void closeDatabases() {
-      if (trace) log.trace("closing databases");
-      try {
-         cacheDb.close();
-      } catch (Exception e) {
-         log.error("Error closing database", e);
-      }
-      try {
-         catalog.close();
-      } catch (Exception e) {
-         log.error("Error closing catalog", e);
-      }
-      cacheMap = null;
-      catalog = null;
-      cacheDb = null;
-   }
+    private void closeSleepyCatResources() throws CacheLoaderException {
+        cacheMap = null;
+        expiryMap = null;
+        closeDatabases();
+        closeEnvironment();
+    }
 
-   private void closeEnvironment() throws CacheLoaderException {
-      if (env != null) {
-         try {
-            env.close();
-         } catch (DatabaseException e) {
-            throw new CacheLoaderException("Unexpected exception closing cacheStore", e);
-         }
-      }
-      env = null;
-   }
+    /**
+     * Exceptions are ignored so that {@link org.infinispan.loaders.bdbje.BdbjeCacheStore#closeEnvironment()}  will
+     * execute.
+     */
+    private void closeDatabases() {
+        if (trace) log.trace("closing databases");
+        try {
+            cacheDb.close();
+        } catch (Exception e) {
+            log.error("Error closing database", e);
+        }
+        try {
+            expiryDb.close();
+        } catch (Exception e) {
+            log.error("Error closing database", e);
+        }
+        try {
+            catalog.close();
+        } catch (Exception e) {
+            log.error("Error closing catalog", e);
+        }
+        catalog = null;
+        cacheDb = null;
+        expiryDb = null;
+    }
 
-   /**
-    * {@inheritDoc} delegates to {@link BdbjeCacheStore#applyModifications(java.util.List)}, if <code>isOnePhase</code>.
-    * Otherwise, delegates to {@link BdbjeCacheStore#prepare(java.util.List, javax.transaction.Transaction) prepare}.
-    */
-   public void prepare(List<? extends Modification> mods, GlobalTransaction tx, boolean isOnePhase) throws CacheLoaderException {
-      if (isOnePhase) {
-         applyModifications(mods);
-      } else {
-         prepare(mods, tx);
-      }
-   }
+    private void closeEnvironment() throws CacheLoaderException {
+        if (env != null) {
+            try {
+                env.close();
+            } catch (DatabaseException e) {
+                throw new CacheLoaderException("Unexpected exception closing cacheStore", e);
+            }
+        }
+        env = null;
+    }
 
-   /**
-    * Perform the <code>mods</code> atomically by creating a {@link ModificationsTransactionWorker worker} and invoking
-    * them in {@link PreparableTransactionRunner#run(com.sleepycat.collections.TransactionWorker)}.
-    *
-    * @param mods actions to perform atomically
-    * @throws CacheLoaderException on problems during the transaction
-    */
-   @Override
-   protected void applyModifications(List<? extends Modification> mods) throws CacheLoaderException {
-      if (trace) log.trace("performing one phase transaction");
-      try {
-         transactionRunner.run(new ModificationsTransactionWorker(this, mods));
-      } catch (Exception caught) {
-         throw convertToCacheLoaderException("Problem committing modifications: " + mods, caught);
-      }
-   }
+    /**
+     * {@inheritDoc} delegates to {@link BdbjeCacheStore#applyModifications(java.util.List)}, if <code>isOnePhase</code>.
+     * Otherwise, delegates to {@link BdbjeCacheStore#prepare(java.util.List, javax.transaction.Transaction) prepare}.
+     */
+    public void prepare(List<? extends Modification> mods, GlobalTransaction tx, boolean isOnePhase) throws CacheLoaderException {
+        if (isOnePhase) {
+            applyModifications(mods);
+        } else {
+            prepare(mods, tx);
+        }
+    }
 
-   /**
-    * Looks up the {@link Transaction SleepyCat transaction} associated with <code>tx</code>.  Creates a {@link
-    * org.infinispan.loaders.bdbje.ModificationsTransactionWorker} instance from <code>mods</code>.  Then prepares the
-    * transaction via {@link PreparableTransactionRunner#prepare(com.sleepycat.collections.TransactionWorker)}. Finally,
-    * it invalidates {@link com.sleepycat.collections.CurrentTransaction#getTransaction()} so that no other thread can
-    * accidentally commit this.
-    *
-    * @param mods modifications to be applied
-    * @param tx   transaction identifier
-    * @throws CacheLoaderException in the event of problems writing to the store
-    */
-   protected void prepare(List<? extends Modification> mods, GlobalTransaction tx) throws CacheLoaderException {
-      if (trace) log.trace("preparing transaction {0}", tx);
-      try {
-         transactionRunner.prepare(new ModificationsTransactionWorker(this, mods));
-         Transaction txn = currentTransaction.getTransaction();
-         if (trace) log.trace("transaction {0} == sleepycat transaction {1}", tx, txn);
-         txnMap.put(tx, txn);
-         ReflectionUtil.setValue(currentTransaction, "localTrans", new ThreadLocal());
-      } catch (Exception e) {
-         throw convertToCacheLoaderException("Problem preparing transaction", e);
-      }
-   }
+    /**
+     * Perform the <code>mods</code> atomically by creating a {@link ModificationsTransactionWorker worker} and invoking
+     * them in {@link PreparableTransactionRunner#run(com.sleepycat.collections.TransactionWorker)}.
+     *
+     * @param mods actions to perform atomically
+     * @throws CacheLoaderException on problems during the transaction
+     */
+    @Override
+    protected void applyModifications(List<? extends Modification> mods) throws CacheLoaderException {
+        if (trace) log.trace("performing one phase transaction");
+        try {
+            transactionRunner.run(new ModificationsTransactionWorker(this, mods));
+        } catch (Exception caught) {
+            throw convertToCacheLoaderException("Problem committing modifications: " + mods, caught);
+        }
+    }
 
+    /**
+     * Looks up the {@link Transaction SleepyCat transaction} associated with <code>tx</code>.  Creates a {@link
+     * org.infinispan.loaders.bdbje.ModificationsTransactionWorker} instance from <code>mods</code>.  Then prepares the
+     * transaction via {@link PreparableTransactionRunner#prepare(com.sleepycat.collections.TransactionWorker)}. Finally,
+     * it invalidates {@link com.sleepycat.collections.CurrentTransaction#getTransaction()} so that no other thread can
+     * accidentally commit this.
+     *
+     * @param mods modifications to be applied
+     * @param tx   transaction identifier
+     * @throws CacheLoaderException in the event of problems writing to the store
+     */
+    protected void prepare(List<? extends Modification> mods, GlobalTransaction tx) throws CacheLoaderException {
+        if (trace) log.trace("preparing transaction {0}", tx);
+        try {
+            transactionRunner.prepare(new ModificationsTransactionWorker(this, mods));
+            Transaction txn = currentTransaction.getTransaction();
+            if (trace) log.trace("transaction {0} == sleepycat transaction {1}", tx, txn);
+            txnMap.put(tx, txn);
+            ReflectionUtil.setValue(currentTransaction, "localTrans", new ThreadLocal());
+        } catch (Exception e) {
+            throw convertToCacheLoaderException("Problem preparing transaction", e);
+        }
+    }
 
-   /**
-    * {@inheritDoc}
-    * <p/>
-    * This implementation calls {@link BdbjeCacheStore#completeTransaction(javax.transaction.Transaction, boolean)
-    * completeTransaction} with an argument of false.
-    */
-   public void rollback(GlobalTransaction tx) {
-      try {
-         completeTransaction(tx, false);
-      } catch (Exception e) {
-         log.error("Error rolling back transaction", e);
-      }
-   }
 
-   /**
-    * {@inheritDoc}
-    * <p/>
-    * This implementation calls {@link BdbjeCacheStore#completeTransaction(javax.transaction.Transaction, boolean)
-    * completeTransaction} with an argument of true.
-    */
-   public void commit(GlobalTransaction tx) throws CacheLoaderException {
-      completeTransaction(tx, true);
-   }
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This implementation calls {@link BdbjeCacheStore#completeTransaction(javax.transaction.Transaction, boolean)
+     * completeTransaction} with an argument of false.
+     */
+    public void rollback(GlobalTransaction tx) {
+        try {
+            completeTransaction(tx, false);
+        } catch (Exception e) {
+            log.error("Error rolling back transaction", e);
+        }
+    }
 
-   /**
-    * Looks up the SleepyCat transaction associated with the parameter <code>tx</code>.  If there is no associated
-    * sleepycat transaction, an error is logged.
-    *
-    * @param tx     java transaction used to lookup a SleepyCat transaction
-    * @param commit true to commit false to abort
-    * @throws CacheLoaderException if there are problems committing or aborting the transaction
-    */
-   protected void completeTransaction(GlobalTransaction tx, boolean commit) throws CacheLoaderException {
-      Transaction txn = txnMap.remove(tx);
-      if (txn != null) {
-         if (trace) log.trace("{0} sleepycat transaction {1}", commit ? "committing" : "aborting", txn);
-         try {
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This implementation calls {@link BdbjeCacheStore#completeTransaction(javax.transaction.Transaction, boolean)
+     * completeTransaction} with an argument of true.
+     */
+    public void commit(GlobalTransaction tx) throws CacheLoaderException {
+        completeTransaction(tx, true);
+    }
+
+    /**
+     * Looks up the SleepyCat transaction associated with the parameter <code>tx</code>.  If there is no associated
+     * sleepycat transaction, an error is logged.
+     *
+     * @param tx     java transaction used to lookup a SleepyCat transaction
+     * @param commit true to commit false to abort
+     * @throws CacheLoaderException if there are problems committing or aborting the transaction
+     */
+    protected void completeTransaction(GlobalTransaction tx, boolean commit) throws CacheLoaderException {
+        Transaction txn = txnMap.remove(tx);
+        if (txn != null) {
+            if (trace) log.trace("{0} sleepycat transaction {1}", commit ? "committing" : "aborting", txn);
+            try {
+                if (commit)
+                    txn.commit();
+                else
+                    txn.abort();
+            } catch (Exception caught) {
+                throw convertToCacheLoaderException("Problem completing transaction", caught);
+            }
+        } else {
+            if (trace) log.trace("no sleepycat transaction associated  transaction {0}", tx);
+        }
+    }
+
+    /**
+     * commits or aborts the {@link com.sleepycat.collections.CurrentTransaction#getTransaction() current transaction}
+     *
+     * @param commit true to commit, false to abort
+     * @throws CacheLoaderException if there was a problem completing the transaction
+     */
+    private void completeCurrentTransaction(boolean commit) throws CacheLoaderException {
+        try {
+            if (trace)
+                log.trace("{0} current sleepycat transaction {1}", commit ? "committing" : "aborting", currentTransaction.getTransaction());
             if (commit)
-               txn.commit();
+                currentTransaction.commitTransaction();
             else
-               txn.abort();
-         } catch (Exception caught) {
+                currentTransaction.abortTransaction();
+        } catch (Exception caught) {
             throw convertToCacheLoaderException("Problem completing transaction", caught);
-         }
-      } else {
-         if (trace) log.trace("no sleepycat transaction associated  transaction {0}", tx);
-      }
-   }
+        }
+    }
 
-   /**
-    * commits or aborts the {@link com.sleepycat.collections.CurrentTransaction#getTransaction() current transaction}
-    *
-    * @param commit true to commit, false to abort
-    * @throws CacheLoaderException if there was a problem completing the transaction
-    */
-   private void completeCurrentTransaction(boolean commit) throws CacheLoaderException {
-      try {
-         if (trace)
-            log.trace("{0} current sleepycat transaction {1}", commit ? "committing" : "aborting", currentTransaction.getTransaction());
-         if (commit)
-            currentTransaction.commitTransaction();
-         else
-            currentTransaction.abortTransaction();
-      } catch (Exception caught) {
-         throw convertToCacheLoaderException("Problem completing transaction", caught);
-      }
-   }
+    /**
+     * {@inheritDoc} This implementation delegates to {@link StoredMap#remove(Object)}
+     */
+    public boolean remove(Object key) throws CacheLoaderException {
+        try {
+            if (cacheMap.containsKey(key)) {
+                cacheMap.remove(key);
+                return true;
+            }
+            return false;
+        } catch (RuntimeException caught) {
+            throw convertToCacheLoaderException("error removing key " + key, caught);
+        }
+    }
 
-   /**
-    * {@inheritDoc} This implementation delegates to {@link StoredMap#remove(Object)}
-    */
-   public boolean remove(Object key) throws CacheLoaderException {
-      try {
-         if (cacheMap.containsKey(key)) {
-            cacheMap.remove(key);
-            return true;
-         }
-         return false;
-      } catch (RuntimeException caught) {
-         throw convertToCacheLoaderException("error removing key " + key, caught);
-      }
-   }
+    /**
+     * {@inheritDoc} This implementation delegates to {@link StoredMap#get(Object)}.  If the object is expired, it will
+     * not be returned.
+     */
+    public InternalCacheEntry load(Object key) throws CacheLoaderException {
+        try {
+            InternalCacheEntry s = cacheMap.get(key);
+            if (s != null && s.isExpired()) {
+                s = null;
+            }
+            return s;
+        } catch (RuntimeException caught) {
+            throw convertToCacheLoaderException("error loading key " + key, caught);
+        }
+    }
 
-   /**
-    * {@inheritDoc} This implementation delegates to {@link StoredMap#get(Object)}.  If the object is expired, it will
-    * not be returned.
-    */
-   public InternalCacheEntry load(Object key) throws CacheLoaderException {
-      try {
-         InternalCacheEntry s = cacheMap.get(key);
-         if (s != null && s.isExpired()) {
-            cacheMap.remove(key);
-            s = null;
-         }
-         return s;
-      } catch (RuntimeException caught) {
-         throw convertToCacheLoaderException("error loading key " + key, caught);
-      }
-   }
+    /**
+     * {@inheritDoc} This implementation delegates to {@link StoredMap#put(Object, Object)}
+     */
+    public void store(InternalCacheEntry ed) throws CacheLoaderException {
+        try {
+            cacheMap.put(ed.getKey(), ed);
+            if (ed.canExpire())
+                addNewExpiry(ed);
+        } catch (IOException caught) {
+            throw convertToCacheLoaderException("error storing entry " + ed, caught);
+        }
+    }
 
-   /**
-    * {@inheritDoc} This implementation delegates to {@link StoredMap#put(Object, Object)}
-    */
-   public void store(InternalCacheEntry ed) throws CacheLoaderException {
-      try {
-         cacheMap.put(ed.getKey(), ed);
-      } catch (RuntimeException caught) {
-         throw convertToCacheLoaderException("error storing entry " + ed, caught);
-      }
-   }
 
-   /**
-    * {@inheritDoc} This implementation delegates to {@link StoredMap#clear()}
-    */
-   public void clear() throws CacheLoaderException {
-      try {
-         cacheMap.clear();
-      } catch (RuntimeException caught) {
-         throw convertToCacheLoaderException("error clearing store", caught);
-      }
-   }
+    private void addNewExpiry(InternalCacheEntry entry) throws IOException {
+        long expiry = entry.getExpiryTime();
+        if (entry.getMaxIdle() > 0) {
+            // TODO do we need both?
+            expiry = entry.getMaxIdle() + System.currentTimeMillis();
+        }
+        Long at = new Long(expiry);
+        Object key = entry.getKey();
+        expiryMap.put(at, key);
+    }
 
-   /**
-    * {@inheritDoc} This implementation returns a Set from {@link StoredMap#values()}
-    */
-   public Set<InternalCacheEntry> loadAll() throws CacheLoaderException {
-      try {
-         return new HashSet<InternalCacheEntry>(cacheMap.values());
-      } catch (RuntimeException caught) {
-         throw convertToCacheLoaderException("error loading all entries", caught);
-      }
-   }
+    /**
+     * {@inheritDoc} This implementation delegates to {@link StoredMap#clear()}
+     */
+    public void clear() throws CacheLoaderException {
+        try {
+            cacheMap.clear();
+        } catch (RuntimeException caught) {
+            throw convertToCacheLoaderException("error clearing store", caught);
+        }
+    }
 
-   /**
-    * {@inheritDoc} This implementation reads the number of entries to load from the stream, then begins a transaction.
-    * During that tranasaction, the cachestore is cleared and replaced with entries from the stream.  If there are any
-    * errors during the process, the entire transaction is rolled back.  Deadlock handling is not addressed, as there is
-    * no means to rollback reads from the inputstream.
-    *
-    * @see BdbjeCacheStore#toStream(java.io.ObjectOutput)
-    */
-   public void fromStream(ObjectInput ois) throws CacheLoaderException {
-      try {
-         long recordCount = ois.readLong();
-         currentTransaction.beginTransaction(null);
-         log.debug("clearing and reading {0} records from stream", recordCount);
-         cacheMap.clear();
-         Cursor cursor = null;
-         try {
-            cursor = cacheDb.openCursor(currentTransaction.getTransaction(), null);
-            for (int i = 0; i < recordCount; i++) {
-               byte[] keyBytes = (byte[]) ois.readObject();
-               byte[] dataBytes = (byte[]) ois.readObject();
+    /**
+     * {@inheritDoc} This implementation returns a Set from {@link StoredMap#values()}
+     */
+    public Set<InternalCacheEntry> loadAll() throws CacheLoaderException {
+        try {
+            return new HashSet<InternalCacheEntry>(cacheMap.values());
+        } catch (RuntimeException caught) {
+            throw convertToCacheLoaderException("error loading all entries", caught);
+        }
+    }
 
-               DatabaseEntry key = new DatabaseEntry(keyBytes);
-               DatabaseEntry data = new DatabaseEntry(dataBytes);
-               cursor.put(key, data);
+    /**
+     * {@inheritDoc} This implementation reads the number of entries to load from the stream, then begins a transaction.
+     * During that tranasaction, the cachestore is cleared and replaced with entries from the stream.  If there are any
+     * errors during the process, the entire transaction is rolled back.  Deadlock handling is not addressed, as there is
+     * no means to rollback reads from the inputstream.
+     *
+     * @see BdbjeCacheStore#toStream(java.io.ObjectOutput)
+     */
+    public void fromStream(ObjectInput ois) throws CacheLoaderException {
+        try {
+            currentTransaction.beginTransaction(null);
+            cacheMap.clear();
+            expiryMap.clear();
+            for (Database db : new Database[]{cacheDb, expiryDb}) {
+                long recordCount = ois.readLong();
+                log.debug("clearing and reading {0} records from stream", recordCount);
+                Cursor cursor = null;
+                try {
+                    cursor = db.openCursor(currentTransaction.getTransaction(), null);
+                    for (int i = 0; i < recordCount; i++) {
+                        byte[] keyBytes = (byte[]) ois.readObject();
+                        byte[] dataBytes = (byte[]) ois.readObject();
+
+                        DatabaseEntry key = new DatabaseEntry(keyBytes);
+                        DatabaseEntry data = new DatabaseEntry(dataBytes);
+                        cursor.put(key, data);
+                    }
+                } finally {
+                    if (cursor != null) cursor.close();
+                }
             }
-         } finally {
-            if (cursor != null) cursor.close();
-         }
-         completeCurrentTransaction(true);
-      } catch (Exception caught) {
-         completeCurrentTransaction(false);
-         clear();
-         throw convertToCacheLoaderException("Problems reading from stream", caught);
-      }
-   }
+            completeCurrentTransaction(true);
+        } catch (Exception caught) {
+            completeCurrentTransaction(false);
+            clear();
+            throw convertToCacheLoaderException("Problems reading from stream", caught);
+        }
+    }
 
-   /**
-    * Writes the current count of cachestore entries followed by a pair of byte[]s corresponding to the SleepyCat binary
-    * representation of {@link InternalCacheEntry#getKey() key} {@link InternalCacheEntry value}.
-    * <p/>
-    * This implementation holds a transaction open to ensure that we see no new records added while iterating.
-    */
-   public void toStream(ObjectOutput oos) throws CacheLoaderException {
-      try {
-         currentTransaction.beginTransaction(null);
-         long recordCount = cacheDb.count();
-         if (trace) log.trace("writing {0} records to stream", recordCount);
-         oos.writeLong(recordCount);
-         Cursor cursor = null;
-         try {
-            cursor = cacheDb.openCursor(currentTransaction.getTransaction(), null);
-            DatabaseEntry key = new DatabaseEntry();
-            DatabaseEntry data = new DatabaseEntry();
-            int recordsWritten = 0;
-            while (cursor.getNext(key, data, null) ==
-                  OperationStatus.SUCCESS) {
-               oos.writeObject(key.getData());
-               oos.writeObject(data.getData());
-               recordsWritten++;
+    /**
+     * Writes the current count of cachestore entries followed by a pair of byte[]s corresponding to the SleepyCat binary
+     * representation of {@link InternalCacheEntry#getKey() key} {@link InternalCacheEntry value}.
+     * <p/>
+     * This implementation holds a transaction open to ensure that we see no new records added while iterating.
+     */
+    public void toStream(ObjectOutput oos) throws CacheLoaderException {
+        try {
+            currentTransaction.beginTransaction(null);
+            for (Database db : new Database[]{cacheDb, expiryDb}) {
+                long recordCount = db.count();
+                oos.writeLong(recordCount);
+                if (trace) log.trace("writing {0} records to stream", recordCount);
+                Cursor cursor = null;
+                try {
+                    cursor = db.openCursor(currentTransaction.getTransaction(), null);
+                    DatabaseEntry key = new DatabaseEntry();
+                    DatabaseEntry data = new DatabaseEntry();
+                    int recordsWritten = 0;
+                    while (cursor.getNext(key, data, null) ==
+                            OperationStatus.SUCCESS) {
+                        oos.writeObject(key.getData());
+                        oos.writeObject(data.getData());
+                        recordsWritten++;
+                    }
+                    if (trace) log.trace("wrote {0} records to stream", recordsWritten);
+                    if (recordsWritten != recordCount)
+                        log.warn("expected to write {0} records, but wrote {1}", recordCount, recordsWritten);
+                } finally {
+                    if (cursor != null) cursor.close();
+                }
             }
-            if (trace) log.trace("wrote {0} records to stream", recordsWritten);
-            if (recordsWritten != recordCount)
-               log.warn("expected to write {0} records, but wrote {1}", recordCount, recordsWritten);
-         } finally {
-            if (cursor != null) cursor.close();
-         }
-         completeCurrentTransaction(true);
-      } catch (Exception caught) {
-         completeCurrentTransaction(false);
-         throw convertToCacheLoaderException("Problems writing to stream", caught);
-      }
-   }
+            completeCurrentTransaction(true);
+        } catch (Exception caught) {
+            completeCurrentTransaction(false);
+            throw convertToCacheLoaderException("Problems writing to stream", caught);
+        }
+    }
 
-   /**
-    * In order to adhere to APIs which do not throw checked exceptions, BDBJE wraps IO and DatabaseExceptions inside
-    * RuntimeExceptions.  These special Exceptions implement {@link com.sleepycat.util.ExceptionWrapper}.  This method
-    * will look for any of that type of Exception and encapsulate it into a CacheLoaderException.  In doing so, the real
-    * root cause can be obtained.
-    *
-    * @param message what to attach to the CacheLoaderException
-    * @param caught  exception to parse
-    * @return CacheLoaderException with the correct cause
-    */
-   CacheLoaderException convertToCacheLoaderException(String message, Exception caught) {
-      caught = ExceptionUnwrapper.unwrap(caught);
-      return (caught instanceof CacheLoaderException) ? (CacheLoaderException) caught :
-            new CacheLoaderException(message, caught);
-   }
+    /**
+     * In order to adhere to APIs which do not throw checked exceptions, BDBJE wraps IO and DatabaseExceptions inside
+     * RuntimeExceptions.  These special Exceptions implement {@link com.sleepycat.util.ExceptionWrapper}.  This method
+     * will look for any of that type of Exception and encapsulate it into a CacheLoaderException.  In doing so, the real
+     * root cause can be obtained.
+     *
+     * @param message what to attach to the CacheLoaderException
+     * @param caught  exception to parse
+     * @return CacheLoaderException with the correct cause
+     */
+    CacheLoaderException convertToCacheLoaderException(String message, Exception caught) {
+        caught = ExceptionUnwrapper.unwrap(caught);
+        return (caught instanceof CacheLoaderException) ? (CacheLoaderException) caught :
+                new CacheLoaderException(message, caught);
+    }
 
-   /**
-    * Iterate through {@link com.sleepycat.collections.StoredMap#entrySet()} and remove, if expired.
-    */
-   @Override
-   protected void purgeInternal() throws CacheLoaderException {
-      try {
-         Iterator<Map.Entry<Object, InternalCacheEntry>> i = cacheMap.entrySet().iterator();
-         while (i.hasNext()) {
-            if (i.next().getValue().isExpired())
-               i.remove();
-         }
-      } catch (RuntimeException caught) {
-         throw convertToCacheLoaderException("error purging expired entries", caught);
-      }
-   }
+    /**
+     * Iterate through {@link com.sleepycat.collections.StoredMap#entrySet()} and remove, if expired.
+     */
+    @Override
+    protected void purgeInternal() throws CacheLoaderException {
+        try {
+            Map<Long, Object> expired = expiryMap.tailMap(System.currentTimeMillis(), true);
+            for (Map.Entry<Long, Object> entry : expired.entrySet()) {
+                expiryMap.remove(entry.getKey());
+                cacheMap.remove(entry.getValue());
+            }
+        } catch (RuntimeException caught) {
+            throw convertToCacheLoaderException("error purging expired entries", caught);
+        }
+    }
 
-   /**
-    * prints terms of use for Berkeley DB JE
-    */
-   public void printLicense() {
-      String license = "\n*************************************************************************************\n" +
-            "Berkeley DB Java Edition version: " + JEVersion.CURRENT_VERSION.toString() + "\n" +
-            "JBoss Cache can use Berkeley DB Java Edition from Oracle \n" +
-            "(http://www.oracle.com/database/berkeley-db/je/index.html)\n" +
-            "for persistent, reliable and transaction-protected data storage.\n" +
-            "If you choose to use Berkeley DB Java Edition with JBoss Cache, you must comply with the terms\n" +
-            "of Oracle's public license, included in the file LICENSE.txt.\n" +
-            "If you prefer not to release the source code for your own application in order to comply\n" +
-            "with the Oracle public license, you may purchase a different license for use of\n" +
-            "Berkeley DB Java Edition with JBoss Cache.\n" +
-            "See http://www.oracle.com/database/berkeley-db/je/index.html for pricing and license terms\n" +
-            "*************************************************************************************";
-      System.out.println(license);
-   }
+    /**
+     * prints terms of use for Berkeley DB JE
+     */
+    public void printLicense() {
+        String license = "\n*************************************************************************************\n" +
+                "Berkeley DB Java Edition version: " + JEVersion.CURRENT_VERSION.toString() + "\n" +
+                "JBoss Cache can use Berkeley DB Java Edition from Oracle \n" +
+                "(http://www.oracle.com/database/berkeley-db/je/index.html)\n" +
+                "for persistent, reliable and transaction-protected data storage.\n" +
+                "If you choose to use Berkeley DB Java Edition with JBoss Cache, you must comply with the terms\n" +
+                "of Oracle's public license, included in the file LICENSE.txt.\n" +
+                "If you prefer not to release the source code for your own application in order to comply\n" +
+                "with the Oracle public license, you may purchase a different license for use of\n" +
+                "Berkeley DB Java Edition with JBoss Cache.\n" +
+                "See http://www.oracle.com/database/berkeley-db/je/index.html for pricing and license terms\n" +
+                "*************************************************************************************";
+        System.out.println(license);
+    }
 
 }

Modified: trunk/cachestore/bdbje/src/main/java/org/infinispan/loaders/bdbje/BdbjeCacheStoreConfig.java
===================================================================
--- trunk/cachestore/bdbje/src/main/java/org/infinispan/loaders/bdbje/BdbjeCacheStoreConfig.java	2009-05-14 18:41:20 UTC (rev 305)
+++ trunk/cachestore/bdbje/src/main/java/org/infinispan/loaders/bdbje/BdbjeCacheStoreConfig.java	2009-05-14 20:32:43 UTC (rev 306)
@@ -26,57 +26,67 @@
  * @since 4.0
  */
 public class BdbjeCacheStoreConfig extends AbstractCacheStoreConfig {
-   private String location = "Infinispan-BdbjeCacheStore";
-   private long lockAcquistionTimeout = 60 * 1000;
-   private int maxTxRetries = 5;
-   private String cacheDbName;
-   private String catalogDbName;
+    private String location = "Infinispan-BdbjeCacheStore";
+    private long lockAcquistionTimeout = 60 * 1000;
+    private int maxTxRetries = 5;
+    private String cacheDbName;
+    private String catalogDbName;
+    private String expiryDbName;
 
-   public BdbjeCacheStoreConfig() {
-      setCacheLoaderClassName(BdbjeCacheStore.class.getName());
-   }
+    public String getExpiryDbName() {
+        return expiryDbName;
+    }
 
-   public int getMaxTxRetries() {
-      return maxTxRetries;
-   }
+    public void setExpiryDbName(String expiryDbName) {
+        this.expiryDbName = expiryDbName;
+    }
 
-   public void setMaxTxRetries(int maxTxRetries) {
-      this.maxTxRetries = maxTxRetries;
-   }
 
+    public BdbjeCacheStoreConfig() {
+        setCacheLoaderClassName(BdbjeCacheStore.class.getName());
+    }
 
-   public long getLockAcquistionTimeout() {
-      return lockAcquistionTimeout;
-   }
+    public int getMaxTxRetries() {
+        return maxTxRetries;
+    }
 
-   public void setLockAcquistionTimeout(long lockAcquistionTimeout) {
-      this.lockAcquistionTimeout = lockAcquistionTimeout;
-   }
+    public void setMaxTxRetries(int maxTxRetries) {
+        this.maxTxRetries = maxTxRetries;
+    }
 
-   public String getLocation() {
-      return location;
-   }
 
-   public void setLocation(String location) {
-      testImmutability("location");
-      this.location = location;
-   }
+    public long getLockAcquistionTimeout() {
+        return lockAcquistionTimeout;
+    }
 
+    public void setLockAcquistionTimeout(long lockAcquistionTimeout) {
+        this.lockAcquistionTimeout = lockAcquistionTimeout;
+    }
 
-   public String getCacheDbName() {
-      return cacheDbName;
-   }
+    public String getLocation() {
+        return location;
+    }
 
-   public void setCacheDbName(String cacheDbName) {
-      this.cacheDbName = cacheDbName;
-   }
+    public void setLocation(String location) {
+        testImmutability("location");
+        this.location = location;
+    }
 
-   public String getCatalogDbName() {
-      return catalogDbName;
-   }
 
-   public void setCatalogDbName(String catalogDbName) {
-      this.catalogDbName = catalogDbName;
-   }
+    public String getCacheDbName() {
+        return cacheDbName;
+    }
 
+    public void setCacheDbName(String cacheDbName) {
+        this.cacheDbName = cacheDbName;
+    }
+
+    public String getCatalogDbName() {
+        return catalogDbName;
+    }
+
+    public void setCatalogDbName(String catalogDbName) {
+        this.catalogDbName = catalogDbName;
+    }
+
 }

Modified: trunk/cachestore/bdbje/src/main/java/org/infinispan/loaders/bdbje/BdbjeResourceFactory.java
===================================================================
--- trunk/cachestore/bdbje/src/main/java/org/infinispan/loaders/bdbje/BdbjeResourceFactory.java	2009-05-14 18:41:20 UTC (rev 305)
+++ trunk/cachestore/bdbje/src/main/java/org/infinispan/loaders/bdbje/BdbjeResourceFactory.java	2009-05-14 20:32:43 UTC (rev 306)
@@ -5,11 +5,8 @@
 import com.sleepycat.bind.serial.StoredClassCatalog;
 import com.sleepycat.collections.CurrentTransaction;
 import com.sleepycat.collections.StoredMap;
-import com.sleepycat.je.Database;
-import com.sleepycat.je.DatabaseConfig;
-import com.sleepycat.je.DatabaseException;
-import com.sleepycat.je.Environment;
-import com.sleepycat.je.EnvironmentConfig;
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.je.*;
 import com.sleepycat.util.ExceptionUnwrapper;
 import org.infinispan.container.entries.InternalCacheEntry;
 import org.infinispan.marshall.Marshaller;
@@ -26,86 +23,110 @@
  * @since 4.0
  */
 public class BdbjeResourceFactory {
-   private static final Log log = LogFactory.getLog(BdbjeResourceFactory.class);
-   private static final boolean trace = log.isTraceEnabled();
+    private static final Log log = LogFactory.getLog(BdbjeResourceFactory.class);
+    private static final boolean trace = log.isTraceEnabled();
 
-   private BdbjeCacheStoreConfig config;
+    private BdbjeCacheStoreConfig config;
 
-   public BdbjeResourceFactory(BdbjeCacheStoreConfig config) {
-      this.config = config;
-   }
+    public BdbjeResourceFactory(BdbjeCacheStoreConfig config) {
+        this.config = config;
+    }
 
-   /**
-    * @return PreparableTransactionRunner that will try to resolve deadlocks maximum of {@link
-    *         BdbjeCacheStoreConfig#getMaxTxRetries()} times.
-    */
-   public PreparableTransactionRunner createPreparableTransactionRunner(Environment env) {
-      return new PreparableTransactionRunner(env, config.getMaxTxRetries(), null);
-   }
+    /**
+     * @return PreparableTransactionRunner that will try to resolve deadlocks maximum of {@link
+     *         BdbjeCacheStoreConfig#getMaxTxRetries()} times.
+     */
+    public PreparableTransactionRunner createPreparableTransactionRunner(Environment env) {
+        return new PreparableTransactionRunner(env, config.getMaxTxRetries(), null);
+    }
 
-   public CurrentTransaction createCurrentTransaction(Environment env) {
-      return CurrentTransaction.getInstance(env);
-   }
+    public CurrentTransaction createCurrentTransaction(Environment env) {
+        return CurrentTransaction.getInstance(env);
+    }
 
-   /**
-    * Open the environment, creating it if it doesn't exist.
-    *
-    * @param envLocation base directory where the Environment will write files
-    * @return open Environment with a lock timeout of {@link org.infinispan.loaders.bdbje.BdbjeCacheStoreConfig#getLockAcquistionTimeout()}
-    *         milliseconds.
-    */
-   public Environment createEnvironment(File envLocation) throws DatabaseException {
-      EnvironmentConfig envConfig = new EnvironmentConfig();
-      envConfig.setAllowCreate(true);
-      envConfig.setTransactional(true);
-      /* lock timeout is in microseconds */
-      envConfig.setLockTimeout(config.getLockAcquistionTimeout() * 1000);
-      if (trace) log.trace("opening or creating je environment at {0}", envLocation);
-      Environment env = new Environment(envLocation, envConfig);
-      log.debug("opened je environment at {0}", envLocation);
-      return env;
-   }
+    /**
+     * Open the environment, creating it if it doesn't exist.
+     *
+     * @param envLocation base directory where the Environment will write files
+     * @return open Environment with a lock timeout of {@link org.infinispan.loaders.bdbje.BdbjeCacheStoreConfig#getLockAcquistionTimeout()}
+     *         milliseconds.
+     */
+    public Environment createEnvironment(File envLocation) throws DatabaseException {
+        EnvironmentConfig envConfig = new EnvironmentConfig();
+        envConfig.setAllowCreate(true);
+        envConfig.setTransactional(true);
+        /* lock timeout is in microseconds */
+        envConfig.setLockTimeout(config.getLockAcquistionTimeout() * 1000);
+        if (trace) log.trace("opening or creating je environment at {0}", envLocation);
+        Environment env = new Environment(envLocation, envConfig);
+        log.debug("opened je environment at {0}", envLocation);
+        return env;
+    }
 
-   public StoredClassCatalog createStoredClassCatalog(Database catalogDb) throws DatabaseException {
-      StoredClassCatalog catalog = new StoredClassCatalog(catalogDb);
-      log.debug("created stored class catalog from database {0}", config.getCatalogDbName());
-      return catalog;
-   }
+    public StoredClassCatalog createStoredClassCatalog(Database catalogDb) throws DatabaseException {
+        StoredClassCatalog catalog = new StoredClassCatalog(catalogDb);
+        log.debug("created stored class catalog from database {0}", config.getCatalogDbName());
+        return catalog;
+    }
 
-   /**
-    * Open the database, creating it if it doesn't exist.
-    *
-    * @return open transactional Database
-    */
-   public Database createDatabase(Environment env, String name) throws DatabaseException {
-      DatabaseConfig dbConfig = new DatabaseConfig();
-      dbConfig.setTransactional(true);
-      dbConfig.setAllowCreate(true);
-      if (trace) log.trace("opening or creating database {0}", name);
-      Database db = env.openDatabase(null, name, dbConfig);
-      log.debug("opened database {0}", name);
-      return db;
-   }
+    /**
+     * Open the database, creating it if it doesn't exist.
+     *
+     * @return open transactional Database
+     */
+    public Database createDatabase(Environment env, String name) throws DatabaseException {
+        DatabaseConfig dbConfig = new DatabaseConfig();
+        dbConfig.setTransactional(true);
+        dbConfig.setAllowCreate(true);
+        if (trace) log.trace("opening or creating database {0}", name);
+        Database db = env.openDatabase(null, name, dbConfig);
+        log.debug("opened database {0}", name);
+        return db;
+    }
 
-   /**
-    * create a {@link com.sleepycat.collections.StoredMap} persisted by the <code>database</code>
-    *
-    * @param database     where entries in the StoredMap are persisted
-    * @param classCatalog location to store class descriptions
-    * @return StoredMap backed by the database and classCatalog
-    * @throws com.sleepycat.je.DatabaseException
-    *          if the StoredMap cannot be opened.
-    */
-   public StoredMap<Object, InternalCacheEntry> createStoredMapViewOfDatabase(Database database, StoredClassCatalog classCatalog, Marshaller m) throws DatabaseException {
-      EntryBinding<Object> storedEntryKeyBinding =
-            new SerialBinding<Object>(classCatalog, Object.class);
-      EntryBinding<InternalCacheEntry> storedEntryValueBinding = new InternalCacheEntryBinding(m);
-      try {
-         return new StoredMap<Object, InternalCacheEntry>(database,
-                                                          storedEntryKeyBinding, storedEntryValueBinding, true);
-      } catch (Exception caught) {
-         caught = ExceptionUnwrapper.unwrap(caught);
-         throw new DatabaseException("error opening stored map", caught);
-      }
-   }
+    /**
+     * create a {@link com.sleepycat.collections.StoredMap} persisted by the <code>database</code>
+     *
+     * @param database     where entries in the StoredMap are persisted
+     * @param classCatalog location to store class descriptions
+     * @return StoredMap backed by the database and classCatalog
+     * @throws com.sleepycat.je.DatabaseException
+     *          if the StoredMap cannot be opened.
+     */
+    public StoredMap<Object, InternalCacheEntry> createStoredMapViewOfDatabase(Database database, StoredClassCatalog classCatalog, Marshaller m) throws DatabaseException {
+        EntryBinding<Object> storedEntryKeyBinding =
+                new SerialBinding<Object>(classCatalog, Object.class);
+        EntryBinding<InternalCacheEntry> storedEntryValueBinding = new InternalCacheEntryBinding(m);
+        try {
+            return new StoredMap<Object, InternalCacheEntry>(database,
+                    storedEntryKeyBinding, storedEntryValueBinding, true);
+        } catch (Exception caught) {
+            caught = ExceptionUnwrapper.unwrap(caught);
+            throw new DatabaseException("error opening stored map", caught);
+        }
+    }
+
+    /**
+     * create a {@link com.sleepycat.collections.StoredMap} persisted by the <code>database</code>
+     *
+     * @param database     where entries in the StoredMap are persisted
+     * @param classCatalog location to store class descriptions
+     * @return StoredMap backed by the database and classCatalog
+     * @throws com.sleepycat.je.DatabaseException
+     *          if the StoredMap cannot be opened.
+     */
+    public StoredSortedMap<Long, Object> createStoredSortedMapForKeyExpiry(Database database, StoredClassCatalog classCatalog, Marshaller marshaller) throws DatabaseException {
+        EntryBinding<Long> expiryKeyBinding =
+                new SerialBinding<Long>(classCatalog, Long.class);
+        EntryBinding<Object> expiryValueBinding =
+                new SerialBinding<Object>(classCatalog, Object.class);
+        try {
+            return new StoredSortedMap<Long, Object>(database,
+                    expiryKeyBinding, expiryValueBinding, true);
+        } catch (Exception caught) {
+            caught = ExceptionUnwrapper.unwrap(caught);
+            throw new DatabaseException("error opening stored map", caught);
+        }
+
+    }
 }
\ No newline at end of file

Modified: trunk/cachestore/bdbje/src/test/java/org/infinispan/loaders/bdbje/BdbjeCacheStoreConfigTest.java
===================================================================
--- trunk/cachestore/bdbje/src/test/java/org/infinispan/loaders/bdbje/BdbjeCacheStoreConfigTest.java	2009-05-14 18:41:20 UTC (rev 305)
+++ trunk/cachestore/bdbje/src/test/java/org/infinispan/loaders/bdbje/BdbjeCacheStoreConfigTest.java	2009-05-14 20:32:43 UTC (rev 306)
@@ -15,67 +15,73 @@
 @Test(groups = "unit", enabled = true, testName = "loaders.bdbje.BdbjeCacheStoreConfigTest")
 public class BdbjeCacheStoreConfigTest {
 
-   private BdbjeCacheStoreConfig config;
+    private BdbjeCacheStoreConfig config;
 
-   @BeforeMethod
-   public void setUp() throws Exception {
-      config = new BdbjeCacheStoreConfig();
-   }
+    @BeforeMethod
+    public void setUp() throws Exception {
+        config = new BdbjeCacheStoreConfig();
+    }
 
-   @AfterMethod
-   public void tearDown() throws CacheLoaderException {
-      config = null;
-   }
+    @AfterMethod
+    public void tearDown() throws CacheLoaderException {
+        config = null;
+    }
 
 
-   @Test
-   public void testGetClassNameDefault() {
-      assert config.getCacheLoaderClassName().equals(BdbjeCacheStore.class.getName());
-   }
+    @Test
+    public void testGetClassNameDefault() {
+        assert config.getCacheLoaderClassName().equals(BdbjeCacheStore.class.getName());
+    }
 
-   @Test
-   public void testgetMaxTxRetries() {
-      assert config.getMaxTxRetries() == 5;
-   }
+    @Test
+    public void testgetMaxTxRetries() {
+        assert config.getMaxTxRetries() == 5;
+    }
 
-   @Test
-   public void testSetMaxTxRetries() {
-      config.setMaxTxRetries(1);
-      assert config.getMaxTxRetries() == 1;
-   }
+    @Test
+    public void testSetMaxTxRetries() {
+        config.setMaxTxRetries(1);
+        assert config.getMaxTxRetries() == 1;
+    }
 
-   @Test
-   public void testGetLockAcquistionTimeout() {
-      assert config.getLockAcquistionTimeout() == 60 * 1000;
-   }
+    @Test
+    public void testGetLockAcquistionTimeout() {
+        assert config.getLockAcquistionTimeout() == 60 * 1000;
+    }
 
-   @Test
-   public void testSetLockAcquistionTimeoutMicros() {
-      config.setLockAcquistionTimeout(1);
-      assert config.getLockAcquistionTimeout() == 1;
-   }
+    @Test
+    public void testSetLockAcquistionTimeoutMicros() {
+        config.setLockAcquistionTimeout(1);
+        assert config.getLockAcquistionTimeout() == 1;
+    }
 
-   @Test
-   public void testGetLocationDefault() {
-      assert config.getLocation().equals("Infinispan-BdbjeCacheStore");
-   }
+    @Test
+    public void testGetLocationDefault() {
+        assert config.getLocation().equals("Infinispan-BdbjeCacheStore");
+    }
 
-   @Test
-   public void testSetLocation() {
-      config.setLocation("foo");
-      assert config.getLocation().equals("foo");
-   }
+    @Test
+    public void testSetLocation() {
+        config.setLocation("foo");
+        assert config.getLocation().equals("foo");
+    }
 
-   @Test
-   public void testSetCacheDb() {
-      config.setCacheDbName("foo");
-      assert config.getCacheDbName().equals("foo");
-   }
+    @Test
+    public void testSetCacheDb() {
+        config.setCacheDbName("foo");
+        assert config.getCacheDbName().equals("foo");
+    }
 
-   @Test
-   public void testSetCatalogDb() {
-      config.setCatalogDbName("foo");
-      assert config.getCatalogDbName().equals("foo");
-   }
+    @Test
+    public void testSetCatalogDb() {
+        config.setCatalogDbName("foo");
+        assert config.getCatalogDbName().equals("foo");
+    }
 
+    @Test
+    public void testSetExpiryDb() {
+        config.setExpiryDbName("foo");
+        assert config.getExpiryDbName().equals("foo");
+    }
+
 }

Modified: trunk/cachestore/bdbje/src/test/java/org/infinispan/loaders/bdbje/BdbjeCacheStoreTest.java
===================================================================
--- trunk/cachestore/bdbje/src/test/java/org/infinispan/loaders/bdbje/BdbjeCacheStoreTest.java	2009-05-14 18:41:20 UTC (rev 305)
+++ trunk/cachestore/bdbje/src/test/java/org/infinispan/loaders/bdbje/BdbjeCacheStoreTest.java	2009-05-14 20:32:43 UTC (rev 306)
@@ -3,6 +3,7 @@
 import com.sleepycat.bind.serial.StoredClassCatalog;
 import com.sleepycat.collections.CurrentTransaction;
 import com.sleepycat.collections.StoredMap;
+import com.sleepycat.collections.StoredSortedMap;
 import com.sleepycat.collections.TransactionWorker;
 import com.sleepycat.je.Cursor;
 import com.sleepycat.je.Database;
@@ -36,327 +37,349 @@
  */
 @Test(groups = "unit", enabled = true, testName = "loaders.bdbje.BdbjeCacheStoreTest")
 public class BdbjeCacheStoreTest {
-   private BdbjeCacheStore cs;
-   private BdbjeCacheStoreConfig cfg;
-   private BdbjeResourceFactory factory;
-   private Cache cache;
-   private Environment env;
-   private Database cacheDb;
-   private Database catalogDb;
-   private StoredClassCatalog catalog;
-   private StoredMap cacheMap;
-   private PreparableTransactionRunner runner;
-   private CurrentTransaction currentTransaction;
+    private BdbjeCacheStore cs;
+    private BdbjeCacheStoreConfig cfg;
+    private BdbjeResourceFactory factory;
+    private Cache cache;
+    private Environment env;
+    private Database cacheDb;
+    private Database catalogDb;
+    private Database expiryDb;
 
-   private class MockBdbjeResourceFactory extends BdbjeResourceFactory {
+    private StoredClassCatalog catalog;
+    private StoredMap cacheMap;
+    private StoredSortedMap expiryMap;
 
-      @Override
-      public PreparableTransactionRunner createPreparableTransactionRunner(Environment env) {
-         return runner;
-      }
+    private PreparableTransactionRunner runner;
+    private CurrentTransaction currentTransaction;
 
-      @Override
-      public CurrentTransaction createCurrentTransaction(Environment env) {
-         return currentTransaction;
-      }
+    private class MockBdbjeResourceFactory extends BdbjeResourceFactory {
 
-      @Override
-      public Environment createEnvironment(File envLocation) throws DatabaseException {
-         return env;
-      }
+        @Override
+        public PreparableTransactionRunner createPreparableTransactionRunner(Environment env) {
+            return runner;
+        }
 
-      @Override
-      public StoredClassCatalog createStoredClassCatalog(Database catalogDb) throws DatabaseException {
-         return catalog;
-      }
+        @Override
+        public CurrentTransaction createCurrentTransaction(Environment env) {
+            return currentTransaction;
+        }
 
-      @Override
-      public Database createDatabase(Environment env, String name) throws DatabaseException {
-         if (name.equals(cfg.getCacheDbName()))
-            return cacheDb;
-         else
-            return catalogDb;
-      }
+        @Override
+        public Environment createEnvironment(File envLocation) throws DatabaseException {
+            return env;
+        }
 
-      @Override
-      public StoredMap createStoredMapViewOfDatabase(Database database, StoredClassCatalog classCatalog, Marshaller m) throws DatabaseException {
-         return cacheMap;
-      }
+        @Override
+        public StoredClassCatalog createStoredClassCatalog(Database catalogDb) throws DatabaseException {
+            return catalog;
+        }
 
-      public MockBdbjeResourceFactory(BdbjeCacheStoreConfig config) {
-         super(config);
-      }
-   }
+        @Override
+        public Database createDatabase(Environment env, String name) throws DatabaseException {
+            if (name.equals(cfg.getCacheDbName()))
+                return cacheDb;
+            else if (name.equals(cfg.getCatalogDbName()))
+                return catalogDb;
+            else
+                return expiryDb;
+        }
 
-   @BeforeMethod
-   public void setUp() throws Exception {
-      cfg = new BdbjeCacheStoreConfig();
-      factory = new MockBdbjeResourceFactory(cfg);
-      cache = createMock(Cache.class);
-      cs = new BdbjeCacheStore();
-      env = createMock(Environment.class);
-      cacheDb = createMock(Database.class);
-      catalogDb = createMock(Database.class);
-      catalog = createMock(StoredClassCatalog.class);
-      cacheMap = createMock(StoredMap.class);
-      currentTransaction = createMock(CurrentTransaction.class);
-      WeakReference<Environment> envRef = new WeakReference<Environment>(env);
-      ReflectionUtil.setValue(currentTransaction, "envRef", envRef);
-      ThreadLocal localTrans = new ThreadLocal();
-      ReflectionUtil.setValue(currentTransaction, "localTrans", localTrans);
-      runner = createMock(PreparableTransactionRunner.class);
-   }
+        @Override
+        public StoredMap createStoredMapViewOfDatabase(Database database, StoredClassCatalog classCatalog, Marshaller m) throws DatabaseException {
+            return cacheMap;
+        }
 
-   @AfterMethod
-   public void tearDown() throws CacheLoaderException {
-      runner = null;
-      currentTransaction = null;
-      cacheMap = null;
-      catalogDb = null;
-      cacheDb = null;
-      env = null;
-      factory = null;
-      cache = null;
-      cfg = null;
-      cs = null;
-   }
+        @Override
+        public StoredSortedMap<Long, Object> createStoredSortedMapForKeyExpiry(Database database, StoredClassCatalog classCatalog, Marshaller marshaller) throws DatabaseException {
+            return expiryMap;
+        }
 
-   void start() throws DatabaseException, CacheLoaderException {
-      cs.init(cfg, factory, cache, new TestObjectStreamMarshaller());
-      expect(cache.getName()).andReturn("cache");
-   }
+        public MockBdbjeResourceFactory(BdbjeCacheStoreConfig config) {
+            super(config);
+        }
+    }
 
-   @Test
-   public void testGetConfigurationClass() throws Exception {
-      replayAll();
-      assert cs.getConfigurationClass().equals(BdbjeCacheStoreConfig.class);
-      verifyAll();
-   }
+    @BeforeMethod
+    public void setUp() throws Exception {
+        cfg = new BdbjeCacheStoreConfig();
+        factory = new MockBdbjeResourceFactory(cfg);
+        cache = createMock(Cache.class);
+        cs = new BdbjeCacheStore();
+        env = createMock(Environment.class);
+        cacheDb = createMock(Database.class);
+        catalogDb = createMock(Database.class);
+        expiryDb = createMock(Database.class);
+        catalog = createMock(StoredClassCatalog.class);
+        cacheMap = createMock(StoredMap.class);
+        expiryMap = createMock(StoredSortedMap.class);
+        currentTransaction = createMock(CurrentTransaction.class);
+        WeakReference<Environment> envRef = new WeakReference<Environment>(env);
+        ReflectionUtil.setValue(currentTransaction, "envRef", envRef);
+        ThreadLocal localTrans = new ThreadLocal();
+        ReflectionUtil.setValue(currentTransaction, "localTrans", localTrans);
+        runner = createMock(PreparableTransactionRunner.class);
+    }
 
-   void replayAll() throws Exception {
-      replay(runner);
-      replay(currentTransaction);
-      replay(cacheMap);
-      replay(catalog);
-      replay(catalogDb);
-      replay(cacheDb);
-      replay(env);
-      replay(cache);
-   }
+    @AfterMethod
+    public void tearDown() throws CacheLoaderException {
+        runner = null;
+        currentTransaction = null;
+        cacheMap = null;
+        catalogDb = null;
+        expiryDb = null;
+        cacheDb = null;
+        env = null;
+        factory = null;
+        cache = null;
+        cfg = null;
+        cs = null;
+    }
 
-   void verifyAll() throws Exception {
-      verify(runner);
-      verify(currentTransaction);
-      verify(cacheMap);
-      verify(catalog);
-      verify(catalogDb);
-      verify(env);
-      verify(cache);
-   }
+    void start() throws DatabaseException, CacheLoaderException {
+        cs.init(cfg, factory, cache, new TestObjectStreamMarshaller());
+        expect(cache.getName()).andReturn("cache");
+    }
 
-   @Test
-   public void testInitNoMock() throws Exception {
-      replayAll();
-      cs.init(cfg, cache, null);
-      assert cfg.equals(ReflectionUtil.getValue(cs, "cfg"));
-      assert cache.equals(ReflectionUtil.getValue(cs, "cache"));
-      assert ReflectionUtil.getValue(cs, "factory") instanceof BdbjeResourceFactory;
-      verifyAll();
-   }
+    @Test
+    public void testGetConfigurationClass() throws Exception {
+        replayAll();
+        assert cs.getConfigurationClass().equals(BdbjeCacheStoreConfig.class);
+        verifyAll();
+    }
 
-   @Test
-   void testExceptionClosingCacheDatabaseDoesntPreventEnvironmentFromClosing() throws Exception {
-      start();
-      cacheDb.close();
-      expectLastCall().andThrow(new DatabaseException());
-      catalog.close();
-      env.close();
-      replayAll();
-      cs.start();
-      cs.stop();
+    void replayAll() throws Exception {
+        replay(runner);
+        replay(currentTransaction);
+        replay(cacheMap);
+        replay(expiryMap);
+        replay(catalog);
+        replay(catalogDb);
+        replay(expiryDb);
+        replay(cacheDb);
+        replay(env);
+        replay(cache);
+    }
 
-      verifyAll();
-   }
+    void verifyAll() throws Exception {
+        verify(runner);
+        verify(currentTransaction);
+        verify(cacheMap);
+        verify(expiryMap);
+        verify(catalog);
+        verify(catalogDb);
+        verify(expiryDb);
+        verify(env);
+        verify(cache);
+    }
 
-   @Test
-   void testExceptionClosingCatalogDoesntPreventEnvironmentFromClosing() throws Exception {
-      start();
-      cacheDb.close();
-      catalog.close();
-      expectLastCall().andThrow(new DatabaseException());
-      env.close();
-      replayAll();
-      cs.start();
-      cs.stop();
-      verifyAll();
-   }
+    @Test
+    public void testInitNoMock() throws Exception {
+        replayAll();
+        cs.init(cfg, cache, null);
+        assert cfg.equals(ReflectionUtil.getValue(cs, "cfg"));
+        assert cache.equals(ReflectionUtil.getValue(cs, "cache"));
+        assert ReflectionUtil.getValue(cs, "factory") instanceof BdbjeResourceFactory;
+        verifyAll();
+    }
 
-   @Test(expectedExceptions = CacheLoaderException.class)
-   void testExceptionClosingEnvironment() throws Exception {
-      start();
-      cacheDb.close();
-      catalog.close();
-      env.close();
-      expectLastCall().andThrow(new DatabaseException());
-      replayAll();
-      cs.start();
-      cs.stop();
-      verifyAll();
-   }
+    @Test
+    void testExceptionClosingCacheDatabaseDoesntPreventEnvironmentFromClosing() throws Exception {
+        start();
+        cacheDb.close();
+        expiryDb.close();
+        expectLastCall().andThrow(new DatabaseException());
+        catalog.close();
+        env.close();
+        replayAll();
+        cs.start();
+        cs.stop();
 
+        verifyAll();
+    }
 
-   @Test(expectedExceptions = CacheLoaderException.class)
-   void testThrowsCorrectExceptionOnStartForDatabaseException() throws Exception {
-      factory = new MockBdbjeResourceFactory(cfg) {
-         @Override
-         public StoredClassCatalog createStoredClassCatalog(Database catalogDb) throws DatabaseException {
-            throw new DatabaseException();
-         }
-      };
-      start();
-      replayAll();
-      cs.start();
+    @Test
+    void testExceptionClosingCatalogDoesntPreventEnvironmentFromClosing() throws Exception {
+        start();
+        cacheDb.close();
+        expiryDb.close();
+        catalog.close();
+        expectLastCall().andThrow(new DatabaseException());
+        env.close();
+        replayAll();
+        cs.start();
+        cs.stop();
+        verifyAll();
+    }
 
-   }
+    @Test(expectedExceptions = CacheLoaderException.class)
+    void testExceptionClosingEnvironment() throws Exception {
+        start();
+        cacheDb.close();
+        expiryDb.close();
+        catalog.close();
+        env.close();
+        expectLastCall().andThrow(new DatabaseException());
+        replayAll();
+        cs.start();
+        cs.stop();
+        verifyAll();
+    }
 
-   @Test(expectedExceptions = CacheLoaderException.class)
-   void testEnvironmentDirectoryExistsButNotAFile() throws Exception {
-      File file = createMock(File.class);
-      expect(file.exists()).andReturn(true);
-      expect(file.isDirectory()).andReturn(false);
-      replay(file);
-      cs.verifyOrCreateEnvironmentDirectory(file);
-   }
 
-   @Test(expectedExceptions = CacheLoaderException.class)
-   void testCantCreateEnvironmentDirectory() throws Exception {
-      File file = createMock(File.class);
-      expect(file.exists()).andReturn(false);
-      expect(file.mkdirs()).andReturn(false);
-      replay(file);
-      cs.verifyOrCreateEnvironmentDirectory(file);
-   }
+    @Test(expectedExceptions = CacheLoaderException.class)
+    void testThrowsCorrectExceptionOnStartForDatabaseException() throws Exception {
+        factory = new MockBdbjeResourceFactory(cfg) {
+            @Override
+            public StoredClassCatalog createStoredClassCatalog(Database catalogDb) throws DatabaseException {
+                throw new DatabaseException();
+            }
+        };
+        start();
+        replayAll();
+        cs.start();
 
-   @Test
-   void testCanCreateEnvironmentDirectory() throws Exception {
-      File file = createMock(File.class);
-      expect(file.exists()).andReturn(false);
-      expect(file.mkdirs()).andReturn(true);
-      expect(file.isDirectory()).andReturn(true);
-      replay(file);
-      assert file.equals(cs.verifyOrCreateEnvironmentDirectory(file));
-   }
+    }
 
-   @Test
-   public void testNoExceptionOnRollback() throws Exception {
-      start();
-      GlobalTransaction tx = new GlobalTransaction(false);
-      replayAll();
-      cs.start();
-      cs.rollback(tx);
-      verifyAll();
-   }
+    @Test(expectedExceptions = CacheLoaderException.class)
+    void testEnvironmentDirectoryExistsButNotAFile() throws Exception {
+        File file = createMock(File.class);
+        expect(file.exists()).andReturn(true);
+        expect(file.isDirectory()).andReturn(false);
+        replay(file);
+        cs.verifyOrCreateEnvironmentDirectory(file);
+    }
 
-   @Test
-   protected void testApplyModificationsThrowsOriginalDatabaseException() throws Exception {
-      start();
-      DatabaseException ex = new DatabaseException();
-      runner.run(isA(TransactionWorker.class));
-      expectLastCall().andThrow(new RuntimeExceptionWrapper(ex));
-      replayAll();
-      cs.start();
-      try {
-         cs.applyModifications(Collections.singletonList(new Store(InternalEntryFactory.create("k", "v"))));
-         assert false : "should have gotten an exception";
-      } catch (CacheLoaderException e) {
-         assert ex.equals(e.getCause());
-         verifyAll();
-         return;
-      }
-      assert false : "should have returned";
+    @Test(expectedExceptions = CacheLoaderException.class)
+    void testCantCreateEnvironmentDirectory() throws Exception {
+        File file = createMock(File.class);
+        expect(file.exists()).andReturn(false);
+        expect(file.mkdirs()).andReturn(false);
+        replay(file);
+        cs.verifyOrCreateEnvironmentDirectory(file);
+    }
 
-   }
+    @Test
+    void testCanCreateEnvironmentDirectory() throws Exception {
+        File file = createMock(File.class);
+        expect(file.exists()).andReturn(false);
+        expect(file.mkdirs()).andReturn(true);
+        expect(file.isDirectory()).andReturn(true);
+        replay(file);
+        assert file.equals(cs.verifyOrCreateEnvironmentDirectory(file));
+    }
 
-   @Test
-   protected void testCommitThrowsOriginalDatabaseException() throws Exception {
-      start();
-      DatabaseException ex = new DatabaseException();
-      com.sleepycat.je.Transaction txn = createMock(com.sleepycat.je.Transaction.class);
-      expect(currentTransaction.beginTransaction(null)).andReturn(txn);
-      runner.prepare(isA(TransactionWorker.class));
-      txn.commit();
-      expectLastCall().andThrow(new RuntimeExceptionWrapper(ex));
-      replayAll();
-      replay(txn);
-      cs.start();
-      try {
-         txn = currentTransaction.beginTransaction(null);
-         GlobalTransaction t = new GlobalTransaction(false);
-         cs.prepare(Collections.singletonList(new Store(InternalEntryFactory.create("k", "v"))), t, false);
-         cs.commit(t);
-         assert false : "should have gotten an exception";
-      } catch (CacheLoaderException e) {
-         assert ex.equals(e.getCause());
-         verifyAll();
-         return;
-      }
-      assert false : "should have returned";
+    @Test
+    public void testNoExceptionOnRollback() throws Exception {
+        start();
+        GlobalTransaction tx = new GlobalTransaction(false);
+        replayAll();
+        cs.start();
+        cs.rollback(tx);
+        verifyAll();
+    }
 
-   }
+    @Test
+    protected void testApplyModificationsThrowsOriginalDatabaseException() throws Exception {
+        start();
+        DatabaseException ex = new DatabaseException();
+        runner.run(isA(TransactionWorker.class));
+        expectLastCall().andThrow(new RuntimeExceptionWrapper(ex));
+        replayAll();
+        cs.start();
+        try {
+            cs.applyModifications(Collections.singletonList(new Store(InternalEntryFactory.create("k", "v"))));
+            assert false : "should have gotten an exception";
+        } catch (CacheLoaderException e) {
+            assert ex.equals(e.getCause());
+            verifyAll();
+            return;
+        }
+        assert false : "should have returned";
 
-   @Test
-   protected void testPrepareThrowsOriginalDatabaseException() throws Exception {
-      start();
-      DatabaseException ex = new DatabaseException();
-      runner.prepare(isA(TransactionWorker.class));
-      expectLastCall().andThrow(new RuntimeExceptionWrapper(ex));
-      replayAll();
-      cs.start();
-      try {
-         GlobalTransaction tx = new GlobalTransaction(false);
-         cs.prepare(Collections.singletonList(new Store(InternalEntryFactory.create("k", "v"))), tx, false);
-         assert false : "should have gotten an exception";
-      } catch (CacheLoaderException e) {
-         assert ex.equals(e.getCause());
-         verifyAll();
-         return;
-      }
-      assert false : "should have returned";
+    }
 
-   }
+    @Test
+    protected void testCommitThrowsOriginalDatabaseException() throws Exception {
+        start();
+        DatabaseException ex = new DatabaseException();
+        com.sleepycat.je.Transaction txn = createMock(com.sleepycat.je.Transaction.class);
+        expect(currentTransaction.beginTransaction(null)).andReturn(txn);
+        runner.prepare(isA(TransactionWorker.class));
+        txn.commit();
+        expectLastCall().andThrow(new RuntimeExceptionWrapper(ex));
+        replayAll();
+        replay(txn);
+        cs.start();
+        try {
+            txn = currentTransaction.beginTransaction(null);
+            GlobalTransaction t = new GlobalTransaction(false);
+            cs.prepare(Collections.singletonList(new Store(InternalEntryFactory.create("k", "v"))), t, false);
+            cs.commit(t);
+            assert false : "should have gotten an exception";
+        } catch (CacheLoaderException e) {
+            assert ex.equals(e.getCause());
+            verifyAll();
+            return;
+        }
+        assert false : "should have returned";
 
-   @Test
-   void testClearOnAbortFromStream() throws Exception {
-      start();
-      InternalCacheEntry entry = InternalEntryFactory.create("key", "value");
-      expect(cacheMap.put(entry.getKey(), entry)).andReturn(null);
-      ObjectInput ois = createMock(ObjectInput.class);
-      expect(ois.readLong()).andReturn(new Long(1));
-      com.sleepycat.je.Transaction txn = createMock(com.sleepycat.je.Transaction.class);
-      expect(currentTransaction.beginTransaction(null)).andReturn(txn);
-      cacheMap.clear();
-      Cursor cursor = createMock(Cursor.class);
-      expect(cacheDb.openCursor(txn, null)).andReturn(cursor);
-      IOException ex = new IOException();
-      expect(ois.readObject()).andReturn(new byte[0]);
-      expectLastCall().andThrow(ex);
-      txn.abort();
-      cacheMap.clear();
-      replay(ois);
-      replay(txn);
-      replayAll();
-      cs.start();
-      try {
-         cs.store(entry);
-         cs.fromStream(ois);
-         assert false : "should have gotten an exception";
-      } catch (CacheLoaderException e) {
-         assert ex.equals(e.getCause());
-         verifyAll();
-         verify(ois);
-         verify(txn);
-         return;
-      }
-      assert false : "should have returned";
-   }
+    }
+
+    @Test
+    protected void testPrepareThrowsOriginalDatabaseException() throws Exception {
+        start();
+        DatabaseException ex = new DatabaseException();
+        runner.prepare(isA(TransactionWorker.class));
+        expectLastCall().andThrow(new RuntimeExceptionWrapper(ex));
+        replayAll();
+        cs.start();
+        try {
+            GlobalTransaction tx = new GlobalTransaction(false);
+            cs.prepare(Collections.singletonList(new Store(InternalEntryFactory.create("k", "v"))), tx, false);
+            assert false : "should have gotten an exception";
+        } catch (CacheLoaderException e) {
+            assert ex.equals(e.getCause());
+            verifyAll();
+            return;
+        }
+        assert false : "should have returned";
+
+    }
+
+    @Test
+    void testClearOnAbortFromStream() throws Exception {
+        start();
+        InternalCacheEntry entry = InternalEntryFactory.create("key", "value");
+        expect(cacheMap.put(entry.getKey(), entry)).andReturn(null);
+        ObjectInput ois = createMock(ObjectInput.class);
+        expect(ois.readLong()).andReturn(new Long(1));
+        com.sleepycat.je.Transaction txn = createMock(com.sleepycat.je.Transaction.class);
+        expect(currentTransaction.beginTransaction(null)).andReturn(txn);
+        cacheMap.clear();
+        Cursor cursor = createMock(Cursor.class);
+        expect(cacheDb.openCursor(txn, null)).andReturn(cursor);
+        IOException ex = new IOException();
+        expect(ois.readObject()).andReturn(new byte[0]);
+        expectLastCall().andThrow(ex);
+        txn.abort();
+        cacheMap.clear();
+        expiryMap.clear();
+        replay(ois);
+        replay(txn);
+        replayAll();
+        cs.start();
+        try {
+            cs.store(entry);
+            cs.fromStream(ois);
+            assert false : "should have gotten an exception";
+        } catch (CacheLoaderException e) {
+            assert ex.equals(e.getCause());
+            verifyAll();
+            verify(ois);
+            verify(txn);
+            return;
+        }
+        assert false : "should have returned";
+    }
 }




More information about the infinispan-commits mailing list