From 723f96004511e826fd54ca9afc3ae8bba9565bc3 Mon Sep 17 00:00:00 2001
From: boli <boli@localhost>
Date: Tue, 03 Oct 2006 19:09:36 +0000
Subject: [PATCH] Refactoring of the JEB backend to simplify the container and entryContainer abstraction. This also elimates exposing the JE interface to backendImpl by creating a new RootContainer class. It provides a higher-level interface to access raw data in JE from anywhere in the server (ie. unit tests). 

---
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java                             |  667 +++++++++++--
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/VerifyJob.java                                  |   53 
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportJob.java                                  |   92 -
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/DN2ID.java                                      |   30 
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexFilter.java                                |    4 
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java                             |   28 
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/Config.java                                     |    7 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestEntryContainer.java |   21 
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java                                |  504 ++--------
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportThread.java                               |    6 
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ID2Entry.java                                   |   33 
 /dev/null                                                                                                   |  557 -----------
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/RootContainer.java                              |  622 +++++++++++++
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ExportJob.java                                  |   98 -
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/Index.java                                      |   38 
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/DN2URI.java                                     |   24 
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportContext.java                              |   20 
 17 files changed, 1,462 insertions(+), 1,342 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java
index 1de8247..8fd8930 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java
@@ -79,9 +79,9 @@
        new DatabaseEntry("+".getBytes());
 
   /**
-   * The container in which this attribute index resides.
+   * The entryContainer in which this attribute index resides.
    */
-  Container container;
+  EntryContainer entryContainer;
 
   /**
    * The attribute index configuration.
@@ -110,13 +110,13 @@
 
   /**
    * Create a new attribute index object.
+   * @param entryContainer The entryContainer of this attribute index.
    * @param indexConfig The attribute index configuration.
-   * @param container The container of this attribute index.
    */
-  public AttributeIndex(IndexConfig indexConfig, Container container)
+  public AttributeIndex(EntryContainer entryContainer, IndexConfig indexConfig)
   {
+    this.entryContainer = entryContainer;
     this.indexConfig = indexConfig;
-    this.container = container;
 
     AttributeType attrType = indexConfig.getAttributeType();
     String name = attrType.getNameOrOID();
@@ -124,7 +124,7 @@
     if (indexConfig.isEqualityIndex())
     {
       Indexer equalityIndexer = new EqualityIndexer(indexConfig);
-      this.equalityIndex = new Index(container, name + ".equality",
+      this.equalityIndex = new Index(this.entryContainer, name + ".equality",
                                      equalityIndexer,
                                      indexConfig.getEqualityEntryLimit(),
                                      indexConfig.getCursorEntryLimit());
@@ -133,7 +133,7 @@
     if (indexConfig.isPresenceIndex())
     {
       Indexer presenceIndexer = new PresenceIndexer(indexConfig);
-      this.presenceIndex = new Index(container, name + ".presence",
+      this.presenceIndex = new Index(this.entryContainer, name + ".presence",
                                      presenceIndexer,
                                      indexConfig.getPresenceEntryLimit(),
                                      indexConfig.getCursorEntryLimit());
@@ -142,7 +142,7 @@
     if (indexConfig.isSubstringIndex())
     {
       Indexer substringIndexer = new SubstringIndexer(indexConfig);
-      this.substringIndex = new Index(container, name + ".substring",
+      this.substringIndex = new Index(this.entryContainer, name + ".substring",
                                      substringIndexer,
                                      indexConfig.getSubstringEntryLimit(),
                                      indexConfig.getCursorEntryLimit());
@@ -151,7 +151,7 @@
     if (indexConfig.isOrderingIndex())
     {
       Indexer orderingIndexer = new OrderingIndexer(indexConfig);
-      this.orderingIndex = new Index(container, name + ".ordering",
+      this.orderingIndex = new Index(this.entryContainer, name + ".ordering",
                                      orderingIndexer,
                                      indexConfig.getEqualityEntryLimit(),
                                      indexConfig.getCursorEntryLimit());
@@ -195,7 +195,7 @@
    */
   public void close()
   {
-    // The container is responsible for closing the JE databases.
+    // The entryContainer is responsible for closing the JE databases.
   }
 
   /**
@@ -791,19 +791,19 @@
     String name = attrType.getNameOrOID();
     if (indexConfig.isEqualityIndex())
     {
-      container.removeDatabase(name + ".equality");
+      entryContainer.removeDatabase(name + ".equality");
     }
     if (indexConfig.isPresenceIndex())
     {
-      container.removeDatabase(name + ".presence");
+      entryContainer.removeDatabase(name + ".presence");
     }
     if (indexConfig.isSubstringIndex())
     {
-      container.removeDatabase(name + ".substring");
+      entryContainer.removeDatabase(name + ".substring");
     }
     if (indexConfig.isOrderingIndex())
     {
-      container.removeDatabase(name + ".ordering");
+      entryContainer.removeDatabase(name + ".ordering");
     }
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
index f838746..d2d54cf 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
@@ -26,29 +26,13 @@
  */
 package org.opends.server.backends.jeb;
 
-import java.io.File;
-import java.io.FilenameFilter;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import com.sleepycat.je.CheckpointConfig;
-import com.sleepycat.je.Database;
-import com.sleepycat.je.Environment;
-import com.sleepycat.je.EnvironmentConfig;
-import com.sleepycat.je.EnvironmentStats;
 import com.sleepycat.je.DatabaseException;
-import com.sleepycat.je.PreloadConfig;
-import com.sleepycat.je.PreloadStats;
-import com.sleepycat.je.PreloadStatus;
-import com.sleepycat.je.StatsConfig;
-import com.sleepycat.je.config.EnvironmentParams;
-import com.sleepycat.je.config.ConfigParam;
 
 import org.opends.server.api.Backend;
 import org.opends.server.api.ConfigurableComponent;
@@ -67,8 +51,6 @@
 import org.opends.server.types.BackupDirectory;
 import org.opends.server.types.CancelledOperationException;
 import org.opends.server.types.ConfigChangeResult;
-import org.opends.server.types.DebugLogCategory;
-import org.opends.server.types.DebugLogSeverity;
 import org.opends.server.types.DN;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
@@ -79,10 +61,7 @@
 import org.opends.server.types.LDIFExportConfig;
 import org.opends.server.types.RestoreConfig;
 import org.opends.server.types.ResultCode;
-import org.opends.server.types.FilePermission;
-import org.opends.server.monitors.DatabaseEnvironmentMonitor;
 import org.opends.server.util.LDIFException;
-import org.opends.server.loggers.Debug;
 
 import static org.opends.server.messages.MessageHandler.*;
 import static org.opends.server.messages.JebMessages.*;
@@ -117,26 +96,9 @@
   private Config config;
 
   /**
-   * The directory containing persistent storage for the backend.
+   * The root JE container to use for this backend.
    */
-  private File backendDirectory;
-
-  /**
-   * The permissions mode for the directory containing persistent storage for
-   * the backend.
-   */
-  private FilePermission backendPermission;
-
-  /**
-   * The base DNs contained in this backend.
-   */
-  private ConcurrentHashMap<DN, EntryContainer> baseDNs;
-
-  /**
-   * The JE environment for this backend instance.  Each instance has its own
-   * environment.
-   */
-  private com.sleepycat.je.Environment dbEnv;
+  private RootContainer rootContainer;
 
   /**
    * A configurable component to handle changes to the configuration of
@@ -273,46 +235,6 @@
     }
   }
 
-
-
-  /**
-   * Determine the container of an entry DN.
-   *
-   * @param dn The DN of an entry.
-   * @return The container of the entry.
-   * @throws DirectoryException If the entry DN does not match any of the base
-   *                            DNs.
-   */
-  private EntryContainer getContainer(DN dn) throws DirectoryException
-  {
-    assert debugEnter(CLASS_NAME, "getContainer");
-
-    EntryContainer ec = null;
-    DN nodeDN = dn;
-
-    while (ec == null && nodeDN != null)
-    {
-      ec = baseDNs.get(nodeDN);
-      if (ec == null)
-      {
-        nodeDN = nodeDN.getParent();
-      }
-    }
-
-    if (ec == null)
-    {
-      // The operation should not have been routed to this backend.
-      String message = getMessage(MSGID_JEB_INCORRECT_ROUTING,
-                                  dn.toString());
-      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message,
-                                   MSGID_JEB_INCORRECT_ROUTING);
-    }
-
-    return ec;
-  }
-
-
-
   /**
    * This method constructs a container name from a base DN. Only alphanumeric
    * characters are preserved, all other characters are replaced with an
@@ -371,34 +293,6 @@
     config = new Config();
     config.initializeConfig(configEntry, baseDNs);
 
-    // Get the backend database directory.
-    backendDirectory = config.getBackendDirectory();
-    if (!backendDirectory.isDirectory())
-    {
-      String message = getMessage(MSGID_JEB_DIRECTORY_INVALID,
-                                  backendDirectory.getPath());
-      throw new InitializationException(MSGID_JEB_DIRECTORY_INVALID,
-                                        message);
-    }
-
-    // Get the backend database directory permissions and apply
-    try
-    {
-      backendPermission = config.getBackendPermission();
-      if(!FilePermission.setPermissions(backendDirectory, backendPermission))
-      {
-        throw new Exception();
-      }
-    }
-    catch(Exception e)
-    {
-      // Log an warning that the permissions were not set.
-      int msgID = MSGID_JEB_SET_PERMISSIONS_FAILED;
-      String message = getMessage(msgID, backendDirectory.getPath());
-      logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.SEVERE_WARNING,
-               message, msgID);
-    }
-
     // FIXME: Currently assuming every base DN is also a suffix.
     for (DN dn : baseDNs)
     {
@@ -416,14 +310,8 @@
     // Open the database environment
     try
     {
-      dbEnv = new Environment(backendDirectory,
-                              config.getEnvironmentConfig());
-
-      Debug.debugMessage(DebugLogCategory.BACKEND, DebugLogSeverity.INFO,
-                         CLASS_NAME, "initializeBackend",
-                         dbEnv.getConfig().toString());
-
-//      cleanDatabase();
+      rootContainer = new RootContainer(config, this);
+      rootContainer.open();
     }
     catch (DatabaseException e)
     {
@@ -433,45 +321,33 @@
       throw new InitializationException(MSGID_JEB_OPEN_ENV_FAIL, message, e);
     }
 
-    // Create and register a monitor provider for the environment.
-    String monitorName = this.getBackendID() + " Database Environment";
+    // Register a monitor provider for the environment.
     MonitorProvider monitorProvider =
-         new DatabaseEnvironmentMonitor(monitorName, dbEnv);
+        rootContainer.getMonitorProvider();
     monitorProviders.add(monitorProvider);
     DirectoryServer.registerMonitorProvider(monitorProvider);
 
-    // Open all the databases.
-    this.baseDNs = new ConcurrentHashMap<DN, EntryContainer>(baseDNs.length);
-    for (DN dn : baseDNs)
+    try
     {
-      String containerName = getContainerName(dn);
-      Container container = new Container(dbEnv, containerName);
-      EntryContainer ec = new EntryContainer(this, config, container);
-
-      try
-      {
-        ec.open();
-      }
-      catch (DatabaseException databaseException)
-      {
-        assert debugException(CLASS_NAME, "initializeBackend",
-                              databaseException);
-        String message = getMessage(MSGID_JEB_OPEN_DATABASE_FAIL,
-                                    databaseException.getMessage());
-        throw new InitializationException(MSGID_JEB_OPEN_DATABASE_FAIL, message,
-                                          databaseException);
-      }
-
-      this.baseDNs.put(dn, ec);
+      rootContainer.openEntryContainers(baseDNs);
+    }
+    catch (DatabaseException databaseException)
+    {
+      assert debugException(CLASS_NAME, "initializeBackend",
+                            databaseException);
+      String message = getMessage(MSGID_JEB_OPEN_DATABASE_FAIL,
+                                  databaseException.getMessage());
+      throw new InitializationException(MSGID_JEB_OPEN_DATABASE_FAIL, message,
+                                        databaseException);
     }
 
     // Preload the database cache.
-    preload();
+    rootContainer.preload();
 
     // Determine the next entry ID and the total number of entries.
     EntryID highestID = null;
     long entryCount = 0;
-    for (EntryContainer ec : this.baseDNs.values())
+    for (EntryContainer ec : rootContainer.getEntryContainers())
     {
       try
       {
@@ -502,76 +378,14 @@
     DirectoryServer.registerConfigurableComponent(this);
 
     // Register the database environment as a configurable component.
-    DN envConfigDN = config.getEnvConfigDN();
-    if (envConfigDN != null)
+    ConfigurableEnvironment configurableEnv =
+        rootContainer.getConfigurableEnvironment();
+    if (configurableEnv != null)
     {
-      configurableEnv = new ConfigurableEnvironment(envConfigDN, dbEnv);
       DirectoryServer.registerConfigurableComponent(configurableEnv);
     }
   }
 
-
-
-  /**
-   * Synchronously invokes the cleaner on the database environment then forces a
-   * checkpoint to delete the log files that are no longer in use.
-   *
-   * @throws DatabaseException If an error occurs while cleaning the database
-   * environment.
-   */
-  private void cleanDatabase()
-       throws DatabaseException
-  {
-    assert debugEnter(CLASS_NAME, "cleanDatabase");
-
-    int msgID;
-    String message;
-
-    FilenameFilter filenameFilter = new FilenameFilter()
-    {
-      public boolean accept(File d, String name)
-      {
-        return name.endsWith(".jdb");
-      }
-    };
-
-    int beforeLogfileCount = backendDirectory.list(filenameFilter).length;
-
-    msgID = MSGID_JEB_CLEAN_DATABASE_START;
-    message = getMessage(msgID, beforeLogfileCount, backendDirectory.getPath());
-    logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message,
-             msgID);
-
-    int currentCleaned = 0;
-    int totalCleaned = 0;
-    while ((currentCleaned = dbEnv.cleanLog()) > 0)
-    {
-      totalCleaned += currentCleaned;
-    }
-
-    msgID = MSGID_JEB_CLEAN_DATABASE_MARKED;
-    message = getMessage(msgID, totalCleaned);
-    logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message,
-             msgID);
-
-    if (totalCleaned > 0)
-    {
-      CheckpointConfig force = new CheckpointConfig();
-      force.setForce(true);
-      dbEnv.checkpoint(force);
-    }
-
-    int afterLogfileCount = backendDirectory.list(filenameFilter).length;
-
-    msgID = MSGID_JEB_CLEAN_DATABASE_FINISH;
-    message = getMessage(msgID, afterLogfileCount);
-    logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message,
-             msgID);
-
-  }
-
-
-
   /**
    * Performs any necessary work to finalize this backend, including closing any
    * underlying databases or connections and deregistering any suffixes that it
@@ -595,7 +409,7 @@
 
     // Deregister our suffixes.
     // FIXME: Currently assuming every base DN is also a suffix.
-    for (DN dn : baseDNs.keySet())
+    for (DN dn : rootContainer.getBaseDNs())
     {
       try
       {
@@ -623,11 +437,7 @@
     // Close the database.
     try
     {
-      for (EntryContainer ec : baseDNs.values())
-      {
-        ec.close();
-      }
-      dbEnv.close();
+      rootContainer.close();
     }
     catch (DatabaseException e)
     {
@@ -850,7 +660,7 @@
     readerBegin();
     try
     {
-      EntryContainer ec = getContainer(entryDN);
+      EntryContainer ec = rootContainer.getEntryContainer(entryDN);
       Entry entry;
       try
       {
@@ -903,7 +713,7 @@
     try
     {
       DN entryDN = entry.getDN();
-      EntryContainer ec = getContainer(entryDN);
+      EntryContainer ec = rootContainer.getEntryContainer(entryDN);
 
       try
       {
@@ -954,7 +764,7 @@
     writerBegin();
     try
     {
-      EntryContainer ec = getContainer(entryDN);
+      EntryContainer ec = rootContainer.getEntryContainer(entryDN);
       try
       {
         ec.deleteEntry(entryDN, deleteOperation);
@@ -1005,7 +815,7 @@
     try
     {
       DN entryDN = entry.getDN();
-      EntryContainer ec = getContainer(entryDN);
+      EntryContainer ec = rootContainer.getEntryContainer(entryDN);
 
       try
       {
@@ -1061,12 +871,13 @@
     writerBegin();
     try
     {
-      EntryContainer currentContainer = getContainer(currentDN);
-      EntryContainer container = getContainer(entry.getDN());
+      EntryContainer currentContainer = rootContainer.getEntryContainer(
+          currentDN);
+      EntryContainer container = rootContainer.getEntryContainer(entry.getDN());
 
       if (currentContainer != container)
       {
-        // No reason why we cannot implement a move between containers
+        // FIXME: No reason why we cannot implement a move between containers
         // since the containers share the same database environment.
         int msgID = MSGID_JEB_FUNCTION_NOT_SUPPORTED;
         String msg = getMessage(MSGID_JEB_FUNCTION_NOT_SUPPORTED);
@@ -1115,7 +926,8 @@
     readerBegin();
     try
     {
-      EntryContainer ec = getContainer(searchOperation.getBaseDN());
+      EntryContainer ec = rootContainer.getEntryContainer(
+          searchOperation.getBaseDN());
       ec.search(searchOperation);
     }
     catch (DatabaseException e)
@@ -1166,48 +978,24 @@
                                    e.getMessageID());
     }
 
-    // If the backend already has the environment open, we must use the same
-    // underlying environment instance and its configuration.
-    Environment env;
-    try
-    {
-      EnvironmentConfig envConfig;
-      Environment existingEnv = dbEnv;
-      if (existingEnv == null)
-      {
-        // Open the environment read-only.
-        envConfig = config.getEnvironmentConfig();
-        envConfig.setReadOnly(true);
-        envConfig.setAllowCreate(false);
-        envConfig.setTransactional(false);
-      }
-      else
-      {
-        envConfig = existingEnv.getConfig();
-      }
-
-      // Open a new environment handle.
-      File backendDirectory = config.getBackendDirectory();
-      env = new Environment(backendDirectory, envConfig);
-
-      Debug.debugMessage(DebugLogCategory.BACKEND, DebugLogSeverity.INFO,
-                         CLASS_NAME, "exportLDIF",
-                         env.getConfig().toString());
-
-    }
-    catch (DatabaseException e)
-    {
-      assert debugException(CLASS_NAME, "exportLDIF", e);
-      String message = getMessage(MSGID_JEB_DATABASE_EXCEPTION,
-                                  e.getMessage());
-      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-                                   message, MSGID_JEB_DATABASE_EXCEPTION);
-    }
+    // If the backend already has the root container open, we must use the same
+    // underlying root container
+    boolean openRootContainer = rootContainer == null;
 
     try
     {
-      ExportJob exportJob = new ExportJob(this, config, exportConfig);
-      exportJob.exportLDIF(env);
+      if (openRootContainer)
+      {
+        // Open the database environment
+        rootContainer = new RootContainer(config, this);
+        rootContainer.open(config.getBackendDirectory(),
+                           config.getBackendPermission(),
+                           true, false, false, false, true, true);
+        rootContainer.openEntryContainers(baseDNs);
+      }
+
+      ExportJob exportJob = new ExportJob(exportConfig);
+      exportJob.exportLDIF(rootContainer);
     }
     catch (IOException ioe)
     {
@@ -1241,13 +1029,19 @@
     }
     finally
     {
-      try
+      //If a root container was opened in this method as read only, close it
+      //to leave the backend in the same state.
+      if (openRootContainer && rootContainer != null)
       {
-        env.close();
-      }
-      catch (DatabaseException e)
-      {
-        assert debugException(CLASS_NAME, "exportLDIF", e);
+        try
+        {
+          rootContainer.close();
+          rootContainer = null;
+        }
+        catch (DatabaseException e)
+        {
+          assert debugException(CLASS_NAME, "exportLDIF", e);
+        }
       }
     }
   }
@@ -1342,10 +1136,24 @@
     config = new Config();
     config.initializeConfig(configEntry, baseDNs);
 
-    VerifyJob verifyJob = new VerifyJob(this, config, verifyConfig);
+    // If the backend already has the root container open, we must use the same
+    // underlying root container
+    boolean openRootContainer = rootContainer == null;
+
     try
     {
-      verifyJob.verifyBackend();
+      if (openRootContainer)
+      {
+        // Open the database environment
+        rootContainer = new RootContainer(config, this);
+        rootContainer.open(config.getBackendDirectory(),
+                           config.getBackendPermission(),
+                           true, false, false, false, true, true);
+        rootContainer.openEntryContainers(baseDNs);
+      }
+
+      VerifyJob verifyJob = new VerifyJob(config, verifyConfig);
+      verifyJob.verifyBackend(rootContainer);
     }
     catch (DatabaseException e)
     {
@@ -1362,6 +1170,23 @@
                                    e.getMessage(),
                                    e.getMessageID());
     }
+    finally
+    {
+      //If a root container was opened in this method as read only, close it
+      //to leave the backend in the same state.
+      if (openRootContainer && rootContainer != null)
+      {
+        try
+        {
+          rootContainer.close();
+          rootContainer = null;
+        }
+        catch (DatabaseException e)
+        {
+          assert debugException(CLASS_NAME, "verifyBackend", e);
+        }
+      }
+    }
   }
 
 
@@ -1582,78 +1407,22 @@
           // operation threads with a handle on the entry container being
           // closed.
           DirectoryServer.deregisterSuffix(baseDN);
-          EntryContainer ec = this.baseDNs.remove(baseDN);
-          ec.close();
-          ec.removeContainer();
+          rootContainer.removeEntryContainer(baseDN);
         }
       }
 
       for (DN baseDN : newConfig.getBaseDNs())
       {
-        if (!this.baseDNs.containsKey(baseDN))
+        if (!rootContainer.getBaseDNs().contains(baseDN))
         {
           // The base DN was added.
-          String containerName = getContainerName(baseDN);
-          Container container = new Container(dbEnv, containerName);
-          EntryContainer ec = new EntryContainer(this, config, container);
-          ec.open();
-          this.baseDNs.put(baseDN, ec);
+          rootContainer.openEntryContainer(baseDN);
           DirectoryServer.registerSuffix(baseDN, this);
         }
       }
 
-      // Check for changes to the database directory permissions
-      FilePermission oldPermission = config.getBackendPermission();
-      FilePermission newPermission = newConfig.getBackendPermission();
-
-      if(!FilePermission.toUNIXMode(oldPermission).equals(
-          FilePermission.toUNIXMode(newPermission)))
-      {
-        try
-        {
-          if(!FilePermission.setPermissions(newConfig.getBackendDirectory(),
-              newPermission))
-          {
-            throw new Exception();
-          }
-        }
-        catch(Exception e)
-        {
-          // Log an warning that the permissions were not set.
-          int msgID = MSGID_JEB_SET_PERMISSIONS_FAILED;
-          String message = getMessage(msgID, backendDirectory.getPath());
-          logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.SEVERE_WARNING,
-                   message, msgID);
-        }
-      }
-
-      // Check if any JE non-mutable properties were changed.
-      EnvironmentConfig oldEnvConfig = config.getEnvironmentConfig();
-      EnvironmentConfig newEnvConfig = newConfig.getEnvironmentConfig();
-      Map paramsMap = EnvironmentParams.SUPPORTED_PARAMS;
-      for (Object o : paramsMap.values())
-      {
-        ConfigParam param = (ConfigParam)o;
-        if (!param.isMutable())
-        {
-          String oldValue = oldEnvConfig.getConfigParam(param.getName());
-          String newValue = newEnvConfig.getConfigParam(param.getName());
-          if (!oldValue.equalsIgnoreCase(newValue))
-          {
-            System.out.println("The change to the following property will " +
-                               "take effect when the backend is restarted: " +
-                               param.getName());
-          }
-        }
-      }
-
-      // This takes care of changes to the JE environment for those
-      // properties that are mutable at runtime.
-      dbEnv.setMutableConfig(newConfig.getEnvironmentConfig());
-
-      Debug.debugMessage(DebugLogCategory.BACKEND, DebugLogSeverity.INFO,
-                         CLASS_NAME, "applyNewConfiguration",
-                         dbEnv.getConfig().toString());
+      // Apply new JE configuration
+      rootContainer.applyNewConfig(config);
 
       // Put the new configuration in place.
       config = newConfig;
@@ -1671,76 +1440,15 @@
     return ccr;
   }
 
-
-
   /**
-   * Preload the database cache. There is no preload if the configured preload
-   * time limit is zero.
+   * Returns a handle to the JE root container currently used by this backend.
+   * The rootContainer could be NULL if the backend is not initialized.
+   *
+   * @return The RootContainer object currently used by this backend.
    */
-  private void preload()
+  public RootContainer getRootContainer()
   {
-    assert debugEnter(CLASS_NAME, "preload");
-
-    long timeLimit = config.getPreloadTimeLimit();
-
-    if (timeLimit > 0)
-    {
-      // Get a list of all the databases used by the backend.
-      ArrayList<Database> dbList = new ArrayList<Database>();
-      for (EntryContainer ec : baseDNs.values())
-      {
-        ec.listDatabases(dbList);
-      }
-
-      // Sort the list in order of priority.
-      Collections.sort(dbList, new DbPreloadComparator());
-
-      // Preload each database until we reach the time limit or the cache
-      // is filled.
-      try
-      {
-        long timeEnd = System.currentTimeMillis() + timeLimit;
-
-        // Configure preload of Leaf Nodes (LNs) containing the data values.
-        PreloadConfig preloadConfig = new PreloadConfig();
-        preloadConfig.setLoadLNs(true);
-
-        for (Database db : dbList)
-        {
-          // Calculate the remaining time.
-          long timeRemaining = timeEnd - System.currentTimeMillis();
-          if (timeRemaining <= 0)
-          {
-            break;
-          }
-
-          preloadConfig.setMaxMillisecs(timeRemaining);
-          PreloadStats preloadStats = db.preload(preloadConfig);
-/*
-          System.out.println("file=" + db.getDatabaseName() +
-                             " LNs=" + preloadStats.getNLNsLoaded());
-*/
-
-          // Stop if the cache is full or the time limit has been exceeded.
-          if (preloadStats.getStatus() != PreloadStatus.SUCCESS)
-          {
-            break;
-          }
-        }
-
-        // Log an informational message about the size of the cache.
-        EnvironmentStats stats = dbEnv.getStats(new StatsConfig());
-        long total = stats.getCacheTotalBytes();
-
-        int msgID = MSGID_JEB_CACHE_SIZE_AFTER_PRELOAD;
-        String message = getMessage(msgID, total / (1024 * 1024));
-        logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message,
-                 msgID);
-      }
-      catch (DatabaseException e)
-      {
-        assert debugException(CLASS_NAME, "preload", e);
-      }
-    }
+    assert debugEnter(CLASS_NAME, "getRootContainer");
+    return rootContainer;
   }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/Config.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/Config.java
index d6d3a96..5a31abe 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/Config.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/Config.java
@@ -356,6 +356,13 @@
       throw new ConfigException(msgID, message);
     }
     backendDirectory = getFileForPath(backendDirectoryAttr.activeValue());
+    //Make sure the directory is valid.
+    if (!backendDirectory.isDirectory())
+    {
+      int msgID = MSGID_JEB_DIRECTORY_INVALID;
+      String message = getMessage(msgID, backendDirectory.getPath());
+      throw new ConfigException(MSGID_JEB_DIRECTORY_INVALID, message);
+    }
 
     // ds-cfg-backend-mode
     // Optional, single-valued config attribute requiring admin action on change
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/Container.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/Container.java
deleted file mode 100644
index 2966070..0000000
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/Container.java
+++ /dev/null
@@ -1,557 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying * information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
- */
-package org.opends.server.backends.jeb;
-
-import static org.opends.server.types.DebugLogCategory.*;
-import static org.opends.server.types.DebugLogSeverity.VERBOSE;
-
-import com.sleepycat.je.Cursor;
-import com.sleepycat.je.Database;
-import com.sleepycat.je.DatabaseConfig;
-import com.sleepycat.je.DatabaseEntry;
-import com.sleepycat.je.DatabaseException;
-import com.sleepycat.je.Environment;
-import com.sleepycat.je.LockMode;
-import com.sleepycat.je.OperationStatus;
-import com.sleepycat.je.Transaction;
-import com.sleepycat.je.TransactionConfig;
-
-import org.opends.server.loggers.Debug;
-import org.opends.server.types.DebugLogCategory;
-import org.opends.server.util.StaticUtils;
-import org.opends.server.util.ServerConstants;
-
-import java.util.List;
-import java.util.ArrayList;
-
-/**
- * A container is a logical portion of a JE environment.  Each base DN of a
- * JE backend is given its own container.  Multiple containers are implemented
- * by prefixing each JE database name with the container name, which is derived
- * from the base DN.  Static methods of this class are wrappers around the
- * JE database methods to add Directory Server debug logging for database
- * access.
- */
-public class Container
-{
-  /**
-   * The fully-qualified name of this class for debugging purposes.
-   */
-  private static final String CLASS_NAME =
-       "org.opends.server.backends.jeb.Container";
-
-  /**
-   * The JE database environment.
-   */
-  private Environment env;
-
-  /**
-   * The container name, which may be null.
-   */
-  private String containerName;
-
-  /**
-   * A list of JE database handles opened through this container.
-   * They will be closed by the container.
-   */
-  private ArrayList<Database> databases;
-
-  /**
-   * A list of JE cursor handles registered with this container.
-   * They will be closed by the container.
-   */
-  private ArrayList<Cursor> cursors;
-
-  /**
-   * Creates a new container object.  It does not actually create anything
-   * in the JE database environment.
-   *
-   * @param env           The JE database environment in which the container
-   * resides.
-   * @param containerName The container name, which may be null.
-   */
-  public Container(Environment env, String containerName)
-  {
-    this.env = env;
-    this.containerName = containerName;
-  }
-
-  /**
-   * Open the container.
-   */
-  public void open()
-  {
-    databases = new ArrayList<Database>();
-    cursors = new ArrayList<Cursor>();
-  }
-
-  /**
-   * Close the container.
-   *
-   * @throws DatabaseException If an error occurs while attempting to close
-   * the container.
-   */
-  public void close() throws DatabaseException
-  {
-    // Close each cursor that has been registered.
-    for (Cursor cursor : cursors)
-    {
-      cursor.close();
-    }
-
-    // Close each database handle that has been opened.
-    for (Database database : databases)
-    {
-      if (database.getConfig().getDeferredWrite())
-      {
-        database.sync();
-      }
-
-      database.close();
-    }
-  }
-
-  /**
-   * Constructs a full JE database name incorporating a container name.
-   *
-   * @param builder A string builder to which the full name will be appended.
-   * @param name    The short database name.
-   */
-  private void buildDatabaseName(StringBuilder builder, String name)
-  {
-    if (containerName != null)
-    {
-      builder.append(containerName);
-      builder.append('_');
-    }
-    builder.append(name);
-  }
-
-  /**
-   * Opens a JE database in this container. The resulting database handle
-   * must not be closed by the caller, as it will be closed by the container.
-   * If the provided database configuration is transactional, a transaction will
-   * be created and used to perform the open.
-   * <p>
-   * Note that a database can be opened multiple times and will result in
-   * multiple unique handles to the database.  This is used for example to
-   * give each server thread its own database handle to eliminate contention
-   * that could occur on a single handle.
-   *
-   * @param dbConfig The JE database configuration to be used to open the
-   * database.
-   * @param name     The short database name, to which the container name will
-   * be added.
-   * @return A new JE database handle.
-   * @throws DatabaseException If an error occurs while attempting to open the
-   * database.
-   */
-  public synchronized Database openDatabase(DatabaseConfig dbConfig,
-                                            String name)
-       throws DatabaseException
-  {
-    Database database;
-
-    StringBuilder builder = new StringBuilder();
-    buildDatabaseName(builder, name);
-    String fullName = builder.toString();
-
-    if (dbConfig.getTransactional())
-    {
-      // Open the database under a transaction.
-      Transaction txn = beginTransaction();
-      try
-      {
-        database = env.openDatabase(txn, fullName, dbConfig);
-        assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME,
-                                  "openDatabase",
-                                  "open db=" + database.getDatabaseName() +
-                                  " txnid=" + txn.getId());
-        transactionCommit(txn);
-      }
-      catch (DatabaseException e)
-      {
-        transactionAbort(txn);
-        throw e;
-      }
-    }
-    else
-    {
-      database = env.openDatabase(null, fullName, dbConfig);
-      assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME,
-                                "openDatabase",
-                                "open db=" + database.getDatabaseName() +
-                                " txnid=none");
-    }
-
-    // Insert into the list of database handles.
-    databases.add(database);
-
-    return database;
-  }
-
-  /**
-   * Register a cursor with the container. The container will then take care of
-   * closing the cursor when the container is closed.
-   * @param cursor A cursor to one of the databases in the container.
-   */
-  public synchronized void addCursor(Cursor cursor)
-  {
-    cursors.add(cursor);
-  }
-
-  /**
-   * Begin a leaf transaction using the default configuration.
-   * Provides assertion debug logging.
-   * @return A JE transaction handle.
-   * @throws DatabaseException If an error occurs while attempting to begin
-   * a new transaction.
-   */
-  public Transaction beginTransaction()
-       throws DatabaseException
-  {
-    Transaction parentTxn = null;
-    TransactionConfig txnConfig = null;
-    Transaction txn = env.beginTransaction(parentTxn, txnConfig);
-    assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME,
-                       "beginTransaction", "begin txnid=" + txn.getId());
-    return txn;
-  }
-
-  /**
-   * Commit a transaction.
-   * Provides assertion debug logging.
-   * @param txn The JE transaction handle.
-   * @throws DatabaseException If an error occurs while attempting to commit
-   * the transaction.
-   */
-  public static void transactionCommit(Transaction txn)
-       throws DatabaseException
-  {
-    if (txn != null)
-    {
-      txn.commit();
-      assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME,
-                                "transactionCommit", "commit txnid=" +
-                                                     txn.getId());
-    }
-  }
-
-  /**
-   * Abort a transaction.
-   * Provides assertion debug logging.
-   * @param txn The JE transaction handle.
-   * @throws DatabaseException If an error occurs while attempting to abort the
-   * transaction.
-   */
-  public static void transactionAbort(Transaction txn)
-       throws DatabaseException
-  {
-    if (txn != null)
-    {
-      txn.abort();
-      assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME,
-                                "transactionAbort", "abort txnid=" +
-                                                    txn.getId());
-    }
-  }
-
-  /**
-   * Debug log a read or write access to the database.
-   * @param operation The operation label: "read", "put", "insert".
-   * @param category The log category for raw data value logging
-   * @param status The JE return status code of the operation.
-   * @param database The JE database handle operated on.
-   * @param txn The JE transaction handle used in the operation.
-   * @param key The database key operated on.
-   * @param data The database value read or written.
-   * @return true so that the method can be used in an assertion
-   * @throws DatabaseException If an error occurs while retrieving information
-   * about the JE objects provided to the method.
-   */
-  private static boolean debugAccess(String operation,
-                             DebugLogCategory category,
-                             OperationStatus status,
-                             Database database,
-                             Transaction txn,
-                             DatabaseEntry key, DatabaseEntry data)
-       throws DatabaseException
-  {
-    // Build the string that is common to category DATABASE_ACCESS and
-    // DATABASE_READ/DATABASE_WRITE
-    StringBuilder builder = new StringBuilder();
-    builder.append(operation);
-    if (status == OperationStatus.SUCCESS)
-    {
-      builder.append(" (ok)");
-    }
-    else
-    {
-      builder.append(" (");
-      builder.append(status.toString());
-      builder.append(")");
-    }
-    builder.append(" db=");
-    builder.append(database.getDatabaseName());
-    if (txn != null)
-    {
-      builder.append(" txnid=");
-      builder.append(txn.getId());
-    }
-    else
-    {
-      builder.append(" txnid=none");
-    }
-    Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME,
-                       "debugAccess", builder.toString());
-
-    // If the operation was successful we log the same common information
-    // plus the key and data under category DATABASE_READ or DATABASE_WRITE
-    if (status == OperationStatus.SUCCESS)
-    {
-      builder.append(" key:");
-      builder.append(ServerConstants.EOL);
-      StaticUtils.byteArrayToHexPlusAscii(builder, key.getData(), 0);
-      if (data != null)
-      {
-        builder.append("data(len=");
-        builder.append(data.getSize());
-        builder.append("):");
-        builder.append(ServerConstants.EOL);
-        StaticUtils.byteArrayToHexPlusAscii(builder, data.getData(), 0);
-      }
-      Debug.debugMessage(category, VERBOSE, CLASS_NAME,
-                         "debugAccess", builder.toString());
-/*
-      if (category == DATABASE_WRITE)
-      {
-        System.out.println(builder.toString());
-      }
-*/
-    }
-    return true;
-  }
-
-  /**
-   * Insert a record into a JE database, with optional debug logging. This is a
-   * simple wrapper around the JE Database.putNoOverwrite method.
-   * @param database The JE database handle.
-   * @param txn The JE transaction handle, or null if none.
-   * @param key The record key.
-   * @param data The record value.
-   * @return The operation status.
-   * @throws DatabaseException If an error occurs in the JE operation.
-   */
-  public static OperationStatus insert(Database database, Transaction txn,
-                                DatabaseEntry key, DatabaseEntry data)
-       throws DatabaseException
-  {
-    OperationStatus status = database.putNoOverwrite(txn, key, data);
-    assert debugAccess("insert", DATABASE_WRITE,
-                       status, database, txn, key, data);
-    return status;
-  }
-
-  /**
-   * Insert a record into a JE database through a cursor, with optional debug
-   * logging. This is a simple wrapper around the JE Cursor.putNoOverwrite
-   * method.
-   * @param cursor The JE cursor handle.
-   * @param key The record key.
-   * @param data The record value.
-   * @return The operation status.
-   * @throws DatabaseException If an error occurs in the JE operation.
-   */
-  public static OperationStatus cursorInsert(Cursor cursor,
-                                             DatabaseEntry key,
-                                             DatabaseEntry data)
-       throws DatabaseException
-  {
-    OperationStatus status = cursor.putNoOverwrite(key, data);
-    assert debugAccess("cursorInsert", DATABASE_WRITE,
-                       status, cursor.getDatabase(), null, key, data);
-    return status;
-  }
-
-  /**
-   * Replace or insert a record into a JE database, with optional debug logging.
-   * This is a simple wrapper around the JE Database.put method.
-   * @param database The JE database handle.
-   * @param txn The JE transaction handle, or null if none.
-   * @param key The record key.
-   * @param data The record value.
-   * @return The operation status.
-   * @throws DatabaseException If an error occurs in the JE operation.
-   */
-  public static OperationStatus put(Database database, Transaction txn,
-                                    DatabaseEntry key, DatabaseEntry data)
-       throws DatabaseException
-  {
-    OperationStatus status = database.put(txn, key, data);
-    assert debugAccess("put", DATABASE_WRITE,
-                       status, database, txn, key, data);
-    return status;
-  }
-
-  /**
-   * Replace or insert a record into a JE database through a cursor, with
-   * optional debug logging. This is a simple wrapper around the JE Cursor.put
-   * method.
-   * @param cursor The JE cursor handle.
-   * @param key The record key.
-   * @param data The record value.
-   * @return The operation status.
-   * @throws DatabaseException If an error occurs in the JE operation.
-   */
-  public static OperationStatus cursorPut(Cursor cursor,
-                                          DatabaseEntry key,
-                                          DatabaseEntry data)
-       throws DatabaseException
-  {
-    OperationStatus status = cursor.put(key, data);
-    assert debugAccess("cursorPut", DATABASE_WRITE,
-                       status, cursor.getDatabase(), null, key, data);
-    return status;
-  }
-
-  /**
-   * Read a record from a JE database, with optional debug logging. This is a
-   * simple wrapper around the JE Database.get method.
-   * @param database The JE database handle.
-   * @param txn The JE transaction handle, or null if none.
-   * @param key The key of the record to be read.
-   * @param data The record value returned as output. Its byte array does not
-   * need to be initialized by the caller.
-   * @param lockMode The JE locking mode to be used for the read.
-   * @return The operation status.
-   * @throws DatabaseException If an error occurs in the JE operation.
-   */
-  public static OperationStatus read(Database database, Transaction txn,
-                              DatabaseEntry key, DatabaseEntry data,
-                              LockMode lockMode)
-       throws DatabaseException
-  {
-    OperationStatus status = database.get(txn, key, data, lockMode);
-    assert debugAccess("read", DATABASE_READ,
-                       status, database, txn, key, data);
-    return status;
-  }
-
-  /**
-   * Read a record from a JE database through a cursor, with optional debug
-   * logging. This is a simple wrapper around the JE Cursor.getSearchKey method.
-   * @param cursor The JE cursor handle.
-   * @param key The key of the record to be read.
-   * @param data The record value returned as output. Its byte array does not
-   * need to be initialized by the caller.
-   * @param lockMode The JE locking mode to be used for the read.
-   * @return The operation status.
-   * @throws DatabaseException If an error occurs in the JE operation.
-   */
-  public static OperationStatus cursorRead(Cursor cursor,
-                                           DatabaseEntry key,
-                                           DatabaseEntry data,
-                                           LockMode lockMode)
-       throws DatabaseException
-  {
-    OperationStatus status = cursor.getSearchKey(key, data, lockMode);
-    assert debugAccess("cursorRead", DATABASE_READ,
-                       status, cursor.getDatabase(), null, key, data);
-    return status;
-  }
-
-  /**
-   * Delete a record from a JE database, with optional debug logging. This is a
-   * simple wrapper around the JE Database.delete method.
-   * @param database The JE database handle.
-   * @param txn The JE transaction handle, or null if none.
-   * @param key The key of the record to be read.
-   * @return The operation status.
-   * @throws DatabaseException If an error occurs in the JE operation.
-   */
-  public static OperationStatus delete(Database database, Transaction txn,
-                                       DatabaseEntry key)
-       throws DatabaseException
-  {
-    OperationStatus status = database.delete(txn, key);
-    assert debugAccess("delete", DATABASE_WRITE,
-                       status, database, txn, key, null);
-    return status;
-  }
-
-  /**
-   * Remove a database from disk.
-   *
-   * @param name The short database name, to which the container name will be
-   * added.
-   * @throws DatabaseException If an error occurs while attempting to delete the
-   * database.
-   */
-  public void removeDatabase(String name) throws DatabaseException
-  {
-    StringBuilder builder = new StringBuilder();
-    buildDatabaseName(builder, name);
-    String fullName = builder.toString();
-    env.removeDatabase(null, fullName);
-  }
-
-  /**
-   * Remove from disk all the databases in this container.
-   *
-   * @throws DatabaseException If an error occurs while attempting to delete any
-   * database.
-   */
-  public void removeAllDatabases() throws DatabaseException
-  {
-    StringBuilder builder = new StringBuilder();
-    buildDatabaseName(builder, "");
-    String prefix = builder.toString();
-    List nameList = env.getDatabaseNames();
-    if (nameList != null)
-    {
-      for (Object o : nameList)
-      {
-        String name = (String)o;
-        if (name.startsWith(prefix))
-        {
-          env.removeDatabase(null, name);
-        }
-      }
-    }
-  }
-
-  /**
-   * Get the list of database handles opened by this container.  Note that
-   * there can be multiple handles referring to the same database.
-   * @return The list of open database handles.
-   */
-  public synchronized ArrayList<Database> getDatabaseList()
-  {
-    return new ArrayList<Database>(databases);
-  }
-}
-
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/DN2ID.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/DN2ID.java
index c238ba9..929277d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/DN2ID.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/DN2ID.java
@@ -49,9 +49,9 @@
 public class DN2ID
 {
   /**
-   * The database container.
+   * The database entryContainer.
    */
-  private Container container;
+  private EntryContainer entryContainer;
 
   /**
    * The JE database configuration.
@@ -59,7 +59,7 @@
   private DatabaseConfig dbConfig;
 
   /**
-   * The name of the database within the container.
+   * The name of the database within the entryContainer.
    */
   private String name;
 
@@ -70,16 +70,17 @@
        new ThreadLocal<Database>();
 
   /**
-   * Create a DN2ID instance for the DN database in a given container.
+   * Create a DN2ID instance for the DN database in a given entryContainer.
    *
-   * @param container The container of the DN database.
+   * @param entryContainer The entryContainer of the DN database.
    * @param dbConfig The JE database configuration which will be used to
    * open the database.
    * @param name The name of the DN database ("dn2id").
    */
-  public DN2ID(Container container, DatabaseConfig dbConfig, String name)
+  public DN2ID(EntryContainer entryContainer, DatabaseConfig dbConfig,
+               String name)
   {
-    this.container = container;
+    this.entryContainer = entryContainer;
     this.dbConfig = dbConfig;
     this.name = name;
   }
@@ -96,7 +97,7 @@
 
   /**
    * Get a handle to the database. It returns a per-thread handle to avoid
-   * any thread contention on the database handle. The container is
+   * any thread contention on the database handle. The entryContainer is
    * responsible for closing all handles.
    *
    * @return A database handle.
@@ -108,7 +109,7 @@
     Database database = threadLocalDatabase.get();
     if (database == null)
     {
-      database = container.openDatabase(dbConfig, name);
+      database = entryContainer.openDatabase(dbConfig, name);
       threadLocalDatabase.set(database);
     }
     return database;
@@ -144,7 +145,7 @@
 
     OperationStatus status;
 
-    status = Container.insert(getDatabase(), txn, key, data);
+    status = EntryContainer.insert(getDatabase(), txn, key, data);
     if (status != OperationStatus.SUCCESS)
     {
       return false;
@@ -171,7 +172,7 @@
     DatabaseEntry data = id.getDatabaseEntry();
 
     OperationStatus status;
-    status = Container.put(getDatabase(), txn, key, data);
+    status = EntryContainer.put(getDatabase(), txn, key, data);
     if (status != OperationStatus.SUCCESS)
     {
       return false;
@@ -194,7 +195,7 @@
        throws DatabaseException
   {
     OperationStatus status;
-    status = Container.put(getDatabase(), txn, key, data);
+    status = EntryContainer.put(getDatabase(), txn, key, data);
     if (status != OperationStatus.SUCCESS)
     {
       return false;
@@ -216,7 +217,7 @@
   {
     DatabaseEntry key = DNdata(dn);
 
-    OperationStatus status = Container.delete(getDatabase(), txn, key);
+    OperationStatus status = EntryContainer.delete(getDatabase(), txn, key);
     if (status != OperationStatus.SUCCESS)
     {
       return false;
@@ -239,7 +240,8 @@
     DatabaseEntry data = new DatabaseEntry();
 
     OperationStatus status;
-    status = Container.read(getDatabase(), txn, key, data, LockMode.DEFAULT);
+    status = EntryContainer.read(getDatabase(), txn, key, data,
+                                 LockMode.DEFAULT);
     if (status != OperationStatus.SUCCESS)
     {
       return null;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/DN2URI.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/DN2URI.java
index 8f3c3c1..790df5f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/DN2URI.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/DN2URI.java
@@ -90,9 +90,9 @@
        DirectoryServer.getAttributeType(ATTR_REFERRAL_URL);
 
   /**
-   * The database container.
+   * The database entryContainer.
    */
-  private Container container;
+  private EntryContainer entryContainer;
 
   /**
    * The JE database configuration.
@@ -100,7 +100,7 @@
   private DatabaseConfig dbConfig;
 
   /**
-   * The name of the database within the container.
+   * The name of the database within the entryContainer.
    */
   private String name;
 
@@ -116,16 +116,18 @@
        new ThreadLocal<Database>();
 
   /**
-   * Create a new object representing a referral database in a given container.
+   * Create a new object representing a referral database in a given
+   * entryContainer.
    *
-   * @param container The container of the referral database.
+   * @param entryContainer The entryContainer of the referral database.
    * @param dbConfig The JE database configuration which will be used to
    * open the database.
    * @param name The name of the referral database.
    */
-  public DN2URI(Container container, DatabaseConfig dbConfig, String name)
+  public DN2URI(EntryContainer entryContainer, DatabaseConfig dbConfig,
+                String name)
   {
-    this.container = container;
+    this.entryContainer = entryContainer;
     this.dbConfig = dbConfig;
     this.name = name;
   }
@@ -142,7 +144,7 @@
 
   /**
    * Get a handle to the database. It returns a per-thread handle to avoid
-   * any thread contention on the database handle. The container is
+   * any thread contention on the database handle. The entryContainer is
    * responsible for closing all handles.
    *
    * @return A database handle.
@@ -154,7 +156,7 @@
     Database database = threadLocalDatabase.get();
     if (database == null)
     {
-      database = container.openDatabase(dbConfig, name);
+      database = entryContainer.openDatabase(dbConfig, name);
       threadLocalDatabase.set(database);
     }
     return database;
@@ -181,7 +183,7 @@
 
     // The JE insert method does not permit duplicate keys so we must use the
     // put method.
-    status = Container.put(getDatabase(), txn, key, data);
+    status = EntryContainer.put(getDatabase(), txn, key, data);
     if (status != OperationStatus.SUCCESS)
     {
       return false;
@@ -206,7 +208,7 @@
     DatabaseEntry key = new DatabaseEntry(normDN);
     OperationStatus status;
 
-    status = Container.delete(getDatabase(), txn, key);
+    status = EntryContainer.delete(getDatabase(), txn, key);
     if (status != OperationStatus.SUCCESS)
     {
       return false;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
index 050782e..13a1f4c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
@@ -26,16 +26,7 @@
  */
 package org.opends.server.backends.jeb;
 
-import com.sleepycat.je.Cursor;
-import com.sleepycat.je.Database;
-import com.sleepycat.je.DatabaseConfig;
-import com.sleepycat.je.DatabaseEntry;
-import com.sleepycat.je.DatabaseException;
-import com.sleepycat.je.DatabaseNotFoundException;
-import com.sleepycat.je.DeadlockException;
-import com.sleepycat.je.LockMode;
-import com.sleepycat.je.OperationStatus;
-import com.sleepycat.je.Transaction;
+import com.sleepycat.je.*;
 
 import org.opends.server.api.AttributeSyntax;
 import org.opends.server.api.Backend;
@@ -47,6 +38,7 @@
 import org.opends.server.core.ModifyDNOperation;
 import org.opends.server.core.Operation;
 import org.opends.server.core.SearchOperation;
+import org.opends.server.loggers.Debug;
 import org.opends.server.protocols.asn1.ASN1OctetString;
 import org.opends.server.protocols.ldap.LDAPException;
 import org.opends.server.controls.PagedResultsControl;
@@ -55,6 +47,7 @@
 import org.opends.server.types.AttributeValue;
 import org.opends.server.types.CancelledOperationException;
 import org.opends.server.types.Control;
+import org.opends.server.types.DebugLogCategory;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
@@ -77,6 +70,10 @@
 import static org.opends.server.messages.MessageHandler.getMessage;
 import static org.opends.server.messages.JebMessages.*;
 import static org.opends.server.loggers.Debug.debugException;
+import static org.opends.server.types.DebugLogSeverity.VERBOSE;
+import static org.opends.server.types.DebugLogCategory.DATABASE_ACCESS;
+import static org.opends.server.types.DebugLogCategory.DATABASE_WRITE;
+import static org.opends.server.types.DebugLogCategory.DATABASE_READ;
 import static org.opends.server.util.ServerConstants.OID_SUBTREE_DELETE_CONTROL;
 import static org.opends.server.util.ServerConstants.OID_PAGED_RESULTS_CONTROL;
 
@@ -93,6 +90,16 @@
   private static final String CLASS_NAME =
        "org.opends.server.backends.jeb.EntryContainer";
 
+ /**
+   * The JE database environment.
+   */
+  private static Environment env;
+
+  /**
+   * The backend configuration.
+   */
+  private static Config config;
+
   /**
    * The name of the entry database.
    */
@@ -124,19 +131,26 @@
   public static final String ATTR_DEBUG_SEARCH_INDEX = "debugsearchindex";
 
   /**
-   * The backend to which this entry container belongs.
+   * The backend to which this entry entryContainer belongs.
    */
   private Backend backend;
 
   /**
-   * The database container.
+   * The baseDN this entry container is responsible for.
    */
-  private Container container;
+  private DN baseDN;
 
   /**
-   * The backend configuration.
+   * A list of JE database handles opened through this entryContainer.
+   * They will be closed by the entryContainer.
    */
-  private Config config;
+  private ArrayList<Database> databases;
+
+  /**
+   * A list of JE cursor handles registered with this entryContainer.
+   * They will be closed by the entryContainer.
+   */
+  private ArrayList<Cursor> cursors;
 
   /**
    * The DN database maps a normalized DN string to an entry ID (8 bytes).
@@ -179,25 +193,34 @@
   private HashMap<AttributeType, AttributeIndex> attrIndexMap;
 
   /**
-   * Create a new entry container object.  This method does not actually create
-   * anything in the JE database environment.
+   * Create a new entry entryContainer object.
+   *
+   * @param baseDN  The baseDN this entry container will be responsible for
+   *                storing on disk.
    * @param backend A reference to the JE backend that is creating this entry
-   * container.  It is needed by the Directory Server entry cache methods.
+   *                container. It is needed by the Directory Server entry cache
+   *                methods.
    * @param config The configuration of the JE backend.
-   * @param container The databases reside in this container.
+   * @param env The JE environment to create this entryContainer in
    */
-  public EntryContainer(Backend backend, Config config, Container container)
+  public EntryContainer(DN baseDN, Backend backend, Config config,
+                        Environment env)
   {
     this.backend = backend;
+    this.baseDN = baseDN;
     this.config = config;
-    this.container = container;
+    this.env = env;
+
+    // Instantiate database and cursor lists
+    databases = new ArrayList<Database>();
+    cursors = new ArrayList<Cursor>();
 
     // Instantiate indexes for id2children and id2subtree.
-    id2children = new Index(container, ID2CHILDREN_DATABASE_NAME,
+    id2children = new Index(this, ID2CHILDREN_DATABASE_NAME,
                             new ID2CIndexer(),
                             config.getBackendIndexEntryLimit(),
                             0);
-    id2subtree = new Index(container, ID2SUBTREE_DATABASE_NAME,
+    id2subtree = new Index(this, ID2SUBTREE_DATABASE_NAME,
                            new ID2SIndexer(),
                            config.getBackendIndexEntryLimit(),
                            0);
@@ -208,7 +231,7 @@
     {
       for (IndexConfig indexConfig : config.getIndexConfigMap().values())
       {
-        AttributeIndex index = new AttributeIndex(indexConfig, container);
+        AttributeIndex index = new AttributeIndex(this, indexConfig);
         attrIndexMap.put(indexConfig.getAttributeType(), index);
       }
     }
@@ -218,7 +241,7 @@
   }
 
   /**
-   * Opens the container for reading and writing.
+   * Opens the entryContainer for reading and writing.
    *
    * @throws DatabaseException If an error occurs in the JE database.
    */
@@ -229,10 +252,10 @@
     DatabaseConfig dbNodupsConfig = new DatabaseConfig();
     dbNodupsConfig.setAllowCreate(true);
     dbNodupsConfig.setTransactional(true);
-    container.open();
+
     try
     {
-      id2entry = new ID2Entry(container, dbNodupsConfig, entryDataConfig,
+      id2entry = new ID2Entry(this, dbNodupsConfig, entryDataConfig,
                               ID2ENTRY_DATABASE_NAME);
       id2entry.open();
 
@@ -242,7 +265,7 @@
       dn2idConfig.setAllowCreate(true);
       dn2idConfig.setTransactional(true);
       dn2idConfig.setBtreeComparator(dn2idComparator.getClass());
-      dn2id = new DN2ID(container, dn2idConfig, DN2ID_DATABASE_NAME);
+      dn2id = new DN2ID(this, dn2idConfig, DN2ID_DATABASE_NAME);
       dn2id.open();
 
       id2children.open(dbNodupsConfig);
@@ -253,7 +276,7 @@
       dn2uriConfig.setAllowCreate(true);
       dn2uriConfig.setTransactional(true);
       dn2uriConfig.setBtreeComparator(dn2idComparator.getClass());
-      dn2uri = new DN2URI(container, dn2uriConfig, REFERRAL_DATABASE_NAME);
+      dn2uri = new DN2URI(this, dn2uriConfig, REFERRAL_DATABASE_NAME);
       dn2uri.open();
 
       for (AttributeIndex index : attrIndexMap.values())
@@ -264,16 +287,16 @@
     catch (DatabaseException e)
     {
       assert debugException(CLASS_NAME, "open", e);
-      container.close();
+      close();
       throw e;
     }
   }
 
   /**
-   * Opens the container for reading and writing without transactions.
+   * Opens the entryContainer for reading and writing without transactions.
    *
-   * @param  deferredWrite  Indicates whether to open the container using the
-   *                        deferred write mode.
+   * @param  deferredWrite  Indicates whether to open the entryContainer using
+   *                        the deferred write mode.
    *
    * @throws DatabaseException If an error occurs in the JE database.
    */
@@ -286,10 +309,9 @@
     dbNodupsConfig.setTransactional(false);
     dbNodupsConfig.setDeferredWrite(deferredWrite);
 
-    container.open();
     try
     {
-      id2entry = new ID2Entry(container, dbNodupsConfig, entryDataConfig,
+      id2entry = new ID2Entry(this, dbNodupsConfig, entryDataConfig,
                               ID2ENTRY_DATABASE_NAME);
       id2entry.open();
 
@@ -300,7 +322,7 @@
       dn2idConfig.setTransactional(false);
       dn2idConfig.setBtreeComparator(dn2idComparator.getClass());
       dn2idConfig.setDeferredWrite(deferredWrite);
-      dn2id = new DN2ID(container, dn2idConfig, DN2ID_DATABASE_NAME);
+      dn2id = new DN2ID(this, dn2idConfig, DN2ID_DATABASE_NAME);
       dn2id.open();
 
       id2children.open(dbNodupsConfig);
@@ -312,7 +334,7 @@
       dn2uriConfig.setTransactional(false);
       dn2uriConfig.setBtreeComparator(dn2idComparator.getClass());
       dn2uriConfig.setDeferredWrite(deferredWrite);
-      dn2uri = new DN2URI(container, dn2uriConfig, REFERRAL_DATABASE_NAME);
+      dn2uri = new DN2URI(this, dn2uriConfig, REFERRAL_DATABASE_NAME);
       dn2uri.open();
 
       for (AttributeIndex index : attrIndexMap.values())
@@ -323,13 +345,13 @@
     catch (DatabaseException e)
     {
       assert debugException(CLASS_NAME, "open", e);
-      container.close();
+      close();
       throw e;
     }
   }
 
   /**
-   * Opens the container for reading only.
+   * Opens the entryContainer for reading only.
    *
    * @throws DatabaseException If an error occurs in the JE database.
    */
@@ -342,10 +364,9 @@
     dbNodupsConfig.setAllowCreate(false);
     dbNodupsConfig.setTransactional(false);
 
-    container.open();
     try
     {
-      id2entry = new ID2Entry(container, dbNodupsConfig, entryDataConfig,
+      id2entry = new ID2Entry(this, dbNodupsConfig, entryDataConfig,
                               ID2ENTRY_DATABASE_NAME);
       id2entry.open();
 
@@ -356,7 +377,7 @@
       dn2idConfig.setAllowCreate(false);
       dn2idConfig.setTransactional(false);
       dn2idConfig.setBtreeComparator(dn2idComparator.getClass());
-      dn2id = new DN2ID(container, dn2idConfig, DN2ID_DATABASE_NAME);
+      dn2id = new DN2ID(this, dn2idConfig, DN2ID_DATABASE_NAME);
       dn2id.open();
 
       id2children.open(dbNodupsConfig);
@@ -368,7 +389,7 @@
       dn2uriConfig.setAllowCreate(false);
       dn2uriConfig.setTransactional(false);
       dn2uriConfig.setBtreeComparator(dn2idComparator.getClass());
-      dn2uri = new DN2URI(container, dn2uriConfig, REFERRAL_DATABASE_NAME);
+      dn2uri = new DN2URI(this, dn2uriConfig, REFERRAL_DATABASE_NAME);
       dn2uri.open();
 
       for (AttributeIndex index : attrIndexMap.values())
@@ -379,21 +400,36 @@
     catch (DatabaseException e)
     {
       assert debugException(CLASS_NAME, "openReadOnly", e);
-      container.close();
+      close();
       throw e;
     }
   }
 
   /**
-   * Closes the entry container.
+   * Closes the entry entryContainer.
    *
    * @throws DatabaseException If an error occurs in the JE database.
    */
   public void close()
        throws DatabaseException
   {
-    // The database container is responsible for closing the JE databases.
-    container.close();
+    // Close each cursor that has been registered.
+    for (Cursor cursor : cursors)
+    {
+      cursor.close();
+    }
+
+    // Close each database handle that has been opened.
+    for (Database database : databases)
+    {
+      if (database.getConfig().getDeferredWrite())
+      {
+        database.sync();
+      }
+
+      database.close();
+    }
+
     for (AttributeIndex index : attrIndexMap.values())
     {
       index.close();
@@ -401,8 +437,8 @@
   }
 
   /**
-   * Get the DN database used by this entry container. The container must
-   * have been opened.
+   * Get the DN database used by this entry entryContainer. The entryContainer
+   * must have been opened.
    *
    * @return The DN database.
    */
@@ -412,8 +448,8 @@
   }
 
   /**
-   * Get the entry database used by this entry container. The container must
-   * have been opened.
+   * Get the entry database used by this entry entryContainer. The
+   * entryContainer must have been opened.
    *
    * @return The entry database.
    */
@@ -423,8 +459,8 @@
   }
 
   /**
-   * Get the referral database used by this entry container. The container must
-   * have been opened.
+   * Get the referral database used by this entry entryContainer. The
+   * entryContainer must have been opened.
    *
    * @return The referral database.
    */
@@ -434,8 +470,8 @@
   }
 
   /**
-   * Get the children database used by this entry container.
-   * The container must have been opened.
+   * Get the children database used by this entry entryContainer.
+   * The entryContainer must have been opened.
    *
    * @return The children database.
    */
@@ -445,8 +481,8 @@
   }
 
   /**
-   * Get the subtree database used by this entry container.
-   * The container must have been opened.
+   * Get the subtree database used by this entry entryContainer.
+   * The entryContainer must have been opened.
    *
    * @return The subtree database.
    */
@@ -467,8 +503,8 @@
   }
 
   /**
-   * Determine the highest entryID in the container.
-   * The container must already be open.
+   * Determine the highest entryID in the entryContainer.
+   * The entryContainer must already be open.
    *
    * @return The highest entry ID.
    * @throws JebException If an error occurs in the JE backend.
@@ -499,7 +535,7 @@
   }
 
   /**
-   * Processes the specified search in this container.
+   * Processes the specified search in this entryContainer.
    * Matching entries should be provided back to the core server using the
    * <CODE>SearchOperation.returnEntry</CODE> method.
    *
@@ -1269,12 +1305,12 @@
         operation.invokeOperation(txn);
 
         // Commit the transaction.
-        Container.transactionCommit(txn);
+        EntryContainer.transactionCommit(txn);
         completed = true;
       }
       catch (DeadlockException deadlockException)
       {
-        Container.transactionAbort(txn);
+        EntryContainer.transactionAbort(txn);
         if (retryRemaining-- <= 0)
         {
           throw deadlockException;
@@ -1284,22 +1320,22 @@
       }
       catch (DatabaseException databaseException)
       {
-        Container.transactionAbort(txn);
+        EntryContainer.transactionAbort(txn);
         throw databaseException;
       }
       catch (DirectoryException directoryException)
       {
-        Container.transactionAbort(txn);
+        EntryContainer.transactionAbort(txn);
         throw directoryException;
       }
       catch (JebException jebException)
       {
-        Container.transactionAbort(txn);
+        EntryContainer.transactionAbort(txn);
         throw jebException;
       }
       catch (Exception e)
       {
-        Container.transactionAbort(txn);
+        EntryContainer.transactionAbort(txn);
 
         int messageID = MSGID_JEB_UNCHECKED_EXCEPTION;
         String message = getMessage(messageID);
@@ -1378,7 +1414,7 @@
      */
     public Transaction beginTransaction() throws DatabaseException
     {
-      return container.beginTransaction();
+      return EntryContainer.beginTransaction();
     }
 
     /**
@@ -1827,7 +1863,7 @@
      */
     public Transaction beginTransaction() throws DatabaseException
     {
-      return container.beginTransaction();
+      return EntryContainer.beginTransaction();
     }
 
     /**
@@ -2356,7 +2392,7 @@
      */
     public Transaction beginTransaction() throws DatabaseException
     {
-      return container.beginTransaction();
+      return EntryContainer.beginTransaction();
     }
 
     /**
@@ -2718,7 +2754,7 @@
      */
     public Transaction beginTransaction() throws DatabaseException
     {
-      return container.beginTransaction();
+      return EntryContainer.beginTransaction();
     }
 
     /**
@@ -3196,9 +3232,9 @@
   }
 
   /**
-   * Get a count of the number of entries stored in this entry container.
+   * Get a count of the number of entries stored in this entry entryContainer.
    *
-   * @return The number of entries stored in this entry container.
+   * @return The number of entries stored in this entry entryContainer.
    * @throws DatabaseException If an error occurs in the JE database.
    */
   public long getEntryCount() throws DatabaseException
@@ -3207,7 +3243,8 @@
   }
 
   /**
-   * Remove the entry container from disk. The container must not be open.
+   * Remove the entry entryContainer from disk. The entryContainer must not be
+   * open.
    *
    * @throws DatabaseException If an error occurs in the JE database.
    */
@@ -3215,7 +3252,7 @@
   {
     try
     {
-      container.removeDatabase(DN2ID_DATABASE_NAME);
+      removeDatabase(DN2ID_DATABASE_NAME);
     }
     catch (DatabaseNotFoundException e)
     {
@@ -3223,7 +3260,7 @@
     }
     try
     {
-      container.removeDatabase(ID2ENTRY_DATABASE_NAME);
+      removeDatabase(ID2ENTRY_DATABASE_NAME);
     }
     catch (DatabaseNotFoundException e)
     {
@@ -3231,7 +3268,7 @@
     }
     try
     {
-      container.removeDatabase(ID2CHILDREN_DATABASE_NAME);
+      removeDatabase(ID2CHILDREN_DATABASE_NAME);
     }
     catch (DatabaseNotFoundException e)
     {
@@ -3239,7 +3276,7 @@
     }
     try
     {
-      container.removeDatabase(ID2SUBTREE_DATABASE_NAME);
+      removeDatabase(ID2SUBTREE_DATABASE_NAME);
     }
     catch (DatabaseNotFoundException e)
     {
@@ -3260,7 +3297,7 @@
 
   /**
    * Get the number of values for which the entry limit has been exceeded
-   * since the entry container was opened.
+   * since the entry entryContainer was opened.
    * @return The number of values for which the entry limit has been exceeded.
    */
   public int getEntryLimitExceededCount()
@@ -3276,53 +3313,18 @@
   }
 
   /**
-   * Begin a leaf transaction.
-   * @return A JE transaction handle.
-   * @throws DatabaseException If an error occurs in the JE database.
-   */
-  protected Transaction beginTransaction()
-       throws DatabaseException
-  {
-    return container.beginTransaction();
-  }
-
-  /**
-   * Commit a transaction.
-   * @param txn The JE transaction handle.
-   * @throws DatabaseException If an error occurs in the JE database.
-   */
-  protected void transactionCommit(Transaction txn)
-       throws DatabaseException
-  {
-    Container.transactionCommit(txn);
-  }
-
-  /**
-   * Abort a transaction.
-   * @param txn The JE transaction handle.
-   * @throws DatabaseException If an error occurs in the JE database.
-   */
-  protected void transactionAbort(Transaction txn)
-       throws DatabaseException
-  {
-    Container.transactionAbort(txn);
-  }
-
-  /**
-   * Get a list of the databases opened by this container.  There will be
+   * Get a list of the databases opened by this entryContainer.  There will be
    * only one handle in the list for each database, regardless of the number
    * of handles open for a given database.
    * @param dbList A list of JE database handles.
    */
   public void listDatabases(List<Database> dbList)
   {
-    // The container has a list of all handles opened.
-    List<Database> dbCompleteList = container.getDatabaseList();
 
     // There may be more than one handle open for a given database
     // so we eliminate duplicates here.
     HashSet<String> set = new HashSet<String>();
-    for (Database db : dbCompleteList)
+    for (Database db : databases)
     {
       try
       {
@@ -3362,4 +3364,437 @@
     return false;
   }
 
-}
+  /**
+   * Constructs a full JE database name incorporating a entryContainer name.
+   *
+   * @param builder A string builder to which the full name will be appended.
+   * @param name    The short database name.
+   */
+  private void buildDatabaseName(StringBuilder builder, String name)
+  {
+    builder.append(getContainerName());
+    builder.append('_');
+    builder.append(name);
+  }
+
+  /**
+   * Opens a JE database in this entryContainer. The resulting database handle
+   * must not be closed by the caller, as it will be closed by the
+   * entryContainer. If the provided database configuration is transactional,
+   * a transaction will be created and used to perform the open.
+   * <p>
+   * Note that a database can be opened multiple times and will result in
+   * multiple unique handles to the database.  This is used for example to
+   * give each server thread its own database handle to eliminate contention
+   * that could occur on a single handle.
+   *
+   * @param dbConfig The JE database configuration to be used to open the
+   * database.
+   * @param name     The short database name, to which the entryContainer name
+   *                 will be added.
+   * @return A new JE database handle.
+   * @throws DatabaseException If an error occurs while attempting to open the
+   * database.
+   */
+  public synchronized Database openDatabase(DatabaseConfig dbConfig,
+                                            String name)
+       throws DatabaseException
+  {
+    Database database;
+
+    StringBuilder builder = new StringBuilder();
+    buildDatabaseName(builder, name);
+    String fullName = builder.toString();
+
+    if (dbConfig.getTransactional())
+    {
+      // Open the database under a transaction.
+      Transaction txn = beginTransaction();
+      try
+      {
+        database = env.openDatabase(txn, fullName, dbConfig);
+        assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME,
+                                  "openDatabase",
+                                  "open db=" + database.getDatabaseName() +
+                                  " txnid=" + txn.getId());
+        transactionCommit(txn);
+      }
+      catch (DatabaseException e)
+      {
+        transactionAbort(txn);
+        throw e;
+      }
+    }
+    else
+    {
+      database = env.openDatabase(null, fullName, dbConfig);
+      assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME,
+                                "openDatabase",
+                                "open db=" + database.getDatabaseName() +
+                                " txnid=none");
+    }
+
+    // Insert into the list of database handles.
+    databases.add(database);
+
+    return database;
+  }
+
+  /**
+   * Register a cursor with the entryContainer. The entryContainer will then
+   * take care of closing the cursor when the entryContainer is closed.
+   *
+   * @param cursor A cursor to one of the databases in the entryContainer.
+   */
+  public synchronized void addCursor(Cursor cursor)
+  {
+    cursors.add(cursor);
+  }
+
+  /**
+   * Begin a leaf transaction using the default configuration.
+   * Provides assertion debug logging.
+   * @return A JE transaction handle.
+   * @throws DatabaseException If an error occurs while attempting to begin
+   * a new transaction.
+   */
+  public static Transaction beginTransaction()
+       throws DatabaseException
+  {
+    Transaction parentTxn = null;
+    TransactionConfig txnConfig = null;
+    Transaction txn = env.beginTransaction(parentTxn, txnConfig);
+    assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME,
+                       "beginTransaction", "begin txnid=" + txn.getId());
+    return txn;
+  }
+
+  /**
+   * Commit a transaction.
+   * Provides assertion debug logging.
+   * @param txn The JE transaction handle.
+   * @throws DatabaseException If an error occurs while attempting to commit
+   * the transaction.
+   */
+  public static void transactionCommit(Transaction txn)
+       throws DatabaseException
+  {
+    if (txn != null)
+    {
+      txn.commit();
+      assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME,
+                                "transactionCommit", "commit txnid=" +
+                                                     txn.getId());
+    }
+  }
+
+  /**
+   * Abort a transaction.
+   * Provides assertion debug logging.
+   * @param txn The JE transaction handle.
+   * @throws DatabaseException If an error occurs while attempting to abort the
+   * transaction.
+   */
+  public static void transactionAbort(Transaction txn)
+       throws DatabaseException
+  {
+    if (txn != null)
+    {
+      txn.abort();
+      assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME,
+                                "transactionAbort", "abort txnid=" +
+                                                    txn.getId());
+    }
+  }
+
+  /**
+   * Debug log a read or write access to the database.
+   * @param operation The operation label: "read", "put", "insert".
+   * @param category The log category for raw data value logging
+   * @param status The JE return status code of the operation.
+   * @param database The JE database handle operated on.
+   * @param txn The JE transaction handle used in the operation.
+   * @param key The database key operated on.
+   * @param data The database value read or written.
+   * @return true so that the method can be used in an assertion
+   * @throws DatabaseException If an error occurs while retrieving information
+   * about the JE objects provided to the method.
+   */
+  private static boolean debugAccess(String operation,
+                             DebugLogCategory category,
+                             OperationStatus status,
+                             Database database,
+                             Transaction txn,
+                             DatabaseEntry key, DatabaseEntry data)
+       throws DatabaseException
+  {
+    // Build the string that is common to category DATABASE_ACCESS and
+    // DATABASE_READ/DATABASE_WRITE
+    StringBuilder builder = new StringBuilder();
+    builder.append(operation);
+    if (status == OperationStatus.SUCCESS)
+    {
+      builder.append(" (ok)");
+    }
+    else
+    {
+      builder.append(" (");
+      builder.append(status.toString());
+      builder.append(")");
+    }
+    builder.append(" db=");
+    builder.append(database.getDatabaseName());
+    if (txn != null)
+    {
+      builder.append(" txnid=");
+      builder.append(txn.getId());
+    }
+    else
+    {
+      builder.append(" txnid=none");
+    }
+    Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME,
+                       "debugAccess", builder.toString());
+
+    // If the operation was successful we log the same common information
+    // plus the key and data under category DATABASE_READ or DATABASE_WRITE
+    if (status == OperationStatus.SUCCESS)
+    {
+      builder.append(" key:");
+      builder.append(ServerConstants.EOL);
+      StaticUtils.byteArrayToHexPlusAscii(builder, key.getData(), 0);
+      if (data != null)
+      {
+        builder.append("data(len=");
+        builder.append(data.getSize());
+        builder.append("):");
+        builder.append(ServerConstants.EOL);
+        StaticUtils.byteArrayToHexPlusAscii(builder, data.getData(), 0);
+      }
+      Debug.debugMessage(category, VERBOSE, CLASS_NAME,
+                         "debugAccess", builder.toString());
+/*
+      if (category == DATABASE_WRITE)
+      {
+        System.out.println(builder.toString());
+      }
+*/
+    }
+    return true;
+  }
+
+  /**
+   * Insert a record into a JE database, with optional debug logging. This is a
+   * simple wrapper around the JE Database.putNoOverwrite method.
+   * @param database The JE database handle.
+   * @param txn The JE transaction handle, or null if none.
+   * @param key The record key.
+   * @param data The record value.
+   * @return The operation status.
+   * @throws DatabaseException If an error occurs in the JE operation.
+   */
+  public static OperationStatus insert(Database database, Transaction txn,
+                                DatabaseEntry key, DatabaseEntry data)
+       throws DatabaseException
+  {
+    OperationStatus status = database.putNoOverwrite(txn, key, data);
+    assert debugAccess("insert", DATABASE_WRITE,
+                       status, database, txn, key, data);
+    return status;
+  }
+
+  /**
+   * Insert a record into a JE database through a cursor, with optional debug
+   * logging. This is a simple wrapper around the JE Cursor.putNoOverwrite
+   * method.
+   * @param cursor The JE cursor handle.
+   * @param key The record key.
+   * @param data The record value.
+   * @return The operation status.
+   * @throws DatabaseException If an error occurs in the JE operation.
+   */
+  public static OperationStatus cursorInsert(Cursor cursor,
+                                             DatabaseEntry key,
+                                             DatabaseEntry data)
+       throws DatabaseException
+  {
+    OperationStatus status = cursor.putNoOverwrite(key, data);
+    assert debugAccess("cursorInsert", DATABASE_WRITE,
+                       status, cursor.getDatabase(), null, key, data);
+    return status;
+  }
+
+  /**
+   * Replace or insert a record into a JE database, with optional debug logging.
+   * This is a simple wrapper around the JE Database.put method.
+   * @param database The JE database handle.
+   * @param txn The JE transaction handle, or null if none.
+   * @param key The record key.
+   * @param data The record value.
+   * @return The operation status.
+   * @throws DatabaseException If an error occurs in the JE operation.
+   */
+  public static OperationStatus put(Database database, Transaction txn,
+                                    DatabaseEntry key, DatabaseEntry data)
+       throws DatabaseException
+  {
+    OperationStatus status = database.put(txn, key, data);
+    assert debugAccess("put", DATABASE_WRITE,
+                       status, database, txn, key, data);
+    return status;
+  }
+
+  /**
+   * Replace or insert a record into a JE database through a cursor, with
+   * optional debug logging. This is a simple wrapper around the JE Cursor.put
+   * method.
+   * @param cursor The JE cursor handle.
+   * @param key The record key.
+   * @param data The record value.
+   * @return The operation status.
+   * @throws DatabaseException If an error occurs in the JE operation.
+   */
+  public static OperationStatus cursorPut(Cursor cursor,
+                                          DatabaseEntry key,
+                                          DatabaseEntry data)
+       throws DatabaseException
+  {
+    OperationStatus status = cursor.put(key, data);
+    assert debugAccess("cursorPut", DATABASE_WRITE,
+                       status, cursor.getDatabase(), null, key, data);
+    return status;
+  }
+
+  /**
+   * Read a record from a JE database, with optional debug logging. This is a
+   * simple wrapper around the JE Database.get method.
+   * @param database The JE database handle.
+   * @param txn The JE transaction handle, or null if none.
+   * @param key The key of the record to be read.
+   * @param data The record value returned as output. Its byte array does not
+   * need to be initialized by the caller.
+   * @param lockMode The JE locking mode to be used for the read.
+   * @return The operation status.
+   * @throws DatabaseException If an error occurs in the JE operation.
+   */
+  public static OperationStatus read(Database database, Transaction txn,
+                              DatabaseEntry key, DatabaseEntry data,
+                              LockMode lockMode)
+       throws DatabaseException
+  {
+    OperationStatus status = database.get(txn, key, data, lockMode);
+    assert debugAccess("read", DATABASE_READ,
+                       status, database, txn, key, data);
+    return status;
+  }
+
+  /**
+   * Read a record from a JE database through a cursor, with optional debug
+   * logging. This is a simple wrapper around the JE Cursor.getSearchKey method.
+   * @param cursor The JE cursor handle.
+   * @param key The key of the record to be read.
+   * @param data The record value returned as output. Its byte array does not
+   * need to be initialized by the caller.
+   * @param lockMode The JE locking mode to be used for the read.
+   * @return The operation status.
+   * @throws DatabaseException If an error occurs in the JE operation.
+   */
+  public static OperationStatus cursorRead(Cursor cursor,
+                                           DatabaseEntry key,
+                                           DatabaseEntry data,
+                                           LockMode lockMode)
+       throws DatabaseException
+  {
+    OperationStatus status = cursor.getSearchKey(key, data, lockMode);
+    assert debugAccess("cursorRead", DATABASE_READ,
+                       status, cursor.getDatabase(), null, key, data);
+    return status;
+  }
+
+  /**
+   * Delete a record from a JE database, with optional debug logging. This is a
+   * simple wrapper around the JE Database.delete method.
+   * @param database The JE database handle.
+   * @param txn The JE transaction handle, or null if none.
+   * @param key The key of the record to be read.
+   * @return The operation status.
+   * @throws DatabaseException If an error occurs in the JE operation.
+   */
+  public static OperationStatus delete(Database database, Transaction txn,
+                                       DatabaseEntry key)
+       throws DatabaseException
+  {
+    OperationStatus status = database.delete(txn, key);
+    assert debugAccess("delete", DATABASE_WRITE,
+                       status, database, txn, key, null);
+    return status;
+  }
+
+  /**
+   * Remove a database from disk.
+   *
+   * @param name The short database name, to which the entryContainer name will
+   * be added.
+   * @throws DatabaseException If an error occurs while attempting to delete the
+   * database.
+   */
+  public void removeDatabase(String name) throws DatabaseException
+  {
+    StringBuilder builder = new StringBuilder();
+    buildDatabaseName(builder, name);
+    String fullName = builder.toString();
+    env.removeDatabase(null, fullName);
+  }
+
+  /**
+   * Remove from disk all the databases in this entryContainer.
+   *
+   * @throws DatabaseException If an error occurs while attempting to delete any
+   * database.
+   */
+  private void removeAllDatabases() throws DatabaseException
+  {
+    for(Database database : databases)
+    {
+      String name = database.getDatabaseName();
+      env.removeDatabase(null, name);
+    }
+  }
+
+  /**
+   * This method constructs a container name from a base DN. Only alphanumeric
+   * characters are preserved, all other characters are replaced with an
+   * underscore.
+   *
+   * @return The container name for the base DN.
+   */
+  public String getContainerName()
+  {
+
+    String normStr = baseDN.toNormalizedString();
+    StringBuilder builder = new StringBuilder(normStr.length());
+    for (int i = 0; i < normStr.length(); i++)
+    {
+      char ch = normStr.charAt(i);
+      if (Character.isLetterOrDigit(ch))
+      {
+        builder.append(ch);
+      }
+      else
+      {
+        builder.append('_');
+      }
+    }
+    return builder.toString();
+  }
+
+  /**
+   * Get the baseDN this entry container is responsible for.
+   *
+   * @return The Base DN for this entry container.
+   */
+  public DN getBaseDN()
+  {
+    return baseDN;
+  }
+}
\ No newline at end of file
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ExportJob.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ExportJob.java
index 63d3b10..c0d13b1 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ExportJob.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ExportJob.java
@@ -30,11 +30,9 @@
 import com.sleepycat.je.CursorConfig;
 import com.sleepycat.je.DatabaseEntry;
 import com.sleepycat.je.DatabaseException;
-import com.sleepycat.je.Environment;
 import com.sleepycat.je.LockMode;
 import com.sleepycat.je.OperationStatus;
 
-import org.opends.server.api.Backend;
 import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
 import org.opends.server.types.ErrorLogCategory;
@@ -44,10 +42,7 @@
 import org.opends.server.util.StaticUtils;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
+import java.util.*;
 
 import static org.opends.server.loggers.Debug.debugException;
 import static org.opends.server.messages.MessageHandler.getMessage;
@@ -71,16 +66,6 @@
   private LDIFExportConfig exportConfig;
 
   /**
-   * The JE backend instance to be exported.
-   */
-  private Backend backend;
-
-  /**
-   * The configuration of the JE backend instance.
-   */
-  private Config config;
-
-  /**
    * The number of milliseconds between job progress reports.
    */
   private long progressInterval = 10000;
@@ -98,43 +83,38 @@
   /**
    * Create a new export job.
    *
-   * @param backend The JE backend performing the export job.
-   * @param config The JE backend configuration.
    * @param exportConfig The requested LDIF export configuration.
    */
-  public ExportJob(Backend backend, Config config,
-                   LDIFExportConfig exportConfig)
+  public ExportJob(LDIFExportConfig exportConfig)
   {
     this.exportConfig = exportConfig;
-    this.backend = backend;
-    this.config = config;
   }
 
   /**
    * Export entries from the backend to an LDIF file.
-   * @param env A handle to the JE database environment of the backend.
+   * @param rootContainer The root container to export.
    * @throws DatabaseException If an error occurs in the JE database.
    * @throws IOException If an I/O error occurs while writing an entry.
    * @throws JebException If an error occurs in the JE backend.
    * @throws LDIFException If an error occurs while trying to determine whether
    * to write an entry.
    */
-  public void exportLDIF(Environment env)
+  public void exportLDIF(RootContainer rootContainer)
        throws IOException, LDIFException, DatabaseException, JebException
   {
-    // Open the containers read-only.
     List<DN> includeBranches = exportConfig.getIncludeBranches();
-    DN baseDNs[] = config.getBaseDNs();
-    ArrayList<EntryContainer> containers =
-         new ArrayList<EntryContainer>(baseDNs.length);
-    for (DN baseDN : baseDNs)
+    DN baseDN;
+    ArrayList<EntryContainer> exportContainers =
+        new ArrayList<EntryContainer>();
+
+    for (EntryContainer entryContainer : rootContainer.getEntryContainers())
     {
       // Skip containers that are not covered by the include branches.
+      baseDN = entryContainer.getBaseDN();
 
-      boolean includeBase = false;
       if (includeBranches == null || includeBranches.isEmpty())
       {
-        includeBase = true;
+        exportContainers.add(entryContainer);
       }
       else
       {
@@ -143,62 +123,35 @@
           if (includeBranch.isDescendantOf(baseDN) ||
                includeBranch.isAncestorOf(baseDN))
           {
-            includeBase = true;
+            exportContainers.add(entryContainer);
           }
         }
       }
-
-      if (includeBase)
-      {
-        String containerName = BackendImpl.getContainerName(baseDN);
-        Container container = new Container(env, containerName);
-        EntryContainer entryContainer =
-             new EntryContainer(backend, config, container);
-        if (env.getConfig().getReadOnly())
-        {
-          entryContainer.openReadOnly();
-        }
-        else
-        {
-          entryContainer.open();
-        }
-        containers.add(entryContainer);
-      }
-
     }
 
     // Make a note of the time we started.
     long startTime = System.currentTimeMillis();
 
+    // Start a timer for the progress report.
+    Timer timer = new Timer();
+    TimerTask progressTask = new ProgressTask();
+    timer.scheduleAtFixedRate(progressTask, progressInterval,
+                              progressInterval);
+
+    // Iterate through the containers.
     try
     {
-      // Start a timer for the progress report.
-      Timer timer = new Timer();
-      TimerTask progressTask = new ProgressTask();
-      timer.scheduleAtFixedRate(progressTask, progressInterval,
-                                progressInterval);
-
-      // Iterate through the containers.
-      try
+      for (EntryContainer exportContainer : exportContainers)
       {
-        for (EntryContainer ec : containers)
-        {
-          exportContainer(ec);
-        }
-      }
-      finally
-      {
-        timer.cancel();
+        exportContainer(exportContainer);
       }
     }
     finally
     {
-      for (EntryContainer ec : containers)
-      {
-        ec.close();
-      }
+      timer.cancel();
     }
 
+
     long finishTime = System.currentTimeMillis();
     long totalTime = (finishTime - startTime);
 
@@ -217,9 +170,10 @@
   }
 
   /**
-   * Export the entries in a single entry container, in other words from
+   * Export the entries in a single entry entryContainer, in other words from
    * one of the base DNs.
-   * @param entryContainer The entry container of those entries to be exported.
+   * @param entryContainer The entry container that holds the entries to be
+   *                       exported.
    * @throws DatabaseException If an error occurs in the JE database.
    * @throws IOException If an error occurs while writing an entry.
    * @throws  LDIFException  If an error occurs while trying to determine
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ID2Entry.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ID2Entry.java
index ce9d480..f2439af 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ID2Entry.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ID2Entry.java
@@ -49,9 +49,9 @@
 public class ID2Entry
 {
   /**
-   * The database container.
+   * The database entryContainer.
    */
-  private Container container;
+  private EntryContainer entryContainer;
 
   /**
    * The JE database configuration.
@@ -64,7 +64,7 @@
   private DataConfig dataConfig;
 
   /**
-   * The name of the database within the container.
+   * The name of the database within the entryContainer.
    */
   private String name;
 
@@ -76,17 +76,17 @@
 
   /**
    * Create a new ID2Entry object.
-   * @param container The container of the entry database.
+   * @param entryContainer The entryContainer of the entry database.
    * @param dbConfig The JE database configuration to be used to open the
    * underlying JE database.
    * @param dataConfig The desired compression and encryption options for data
    * stored in the entry database.
    * @param name The name of the entry database.
    */
-  public ID2Entry(Container container, DatabaseConfig dbConfig,
+  public ID2Entry(EntryContainer entryContainer, DatabaseConfig dbConfig,
                   DataConfig dataConfig, String name)
   {
-    this.container = container;
+    this.entryContainer = entryContainer;
     this.dbConfig = dbConfig;
     this.name = name;
     this.dataConfig = dataConfig;
@@ -104,7 +104,7 @@
 
   /**
    * Get a handle to the database. It returns a per-thread handle to avoid
-   * any thread contention on the database handle. The container is
+   * any thread contention on the database handle. The entryContainer is
    * responsible for closing all handles.
    *
    * @return A database handle.
@@ -116,7 +116,7 @@
     Database database = threadLocalDatabase.get();
     if (database == null)
     {
-      database = container.openDatabase(dbConfig, name);
+      database = entryContainer.openDatabase(dbConfig, name);
       threadLocalDatabase.set(database);
     }
     return database;
@@ -152,7 +152,7 @@
     DatabaseEntry data = entryData(entry);
 
     OperationStatus status;
-    status = Container.insert(getDatabase(), txn, key, data);
+    status = EntryContainer.insert(getDatabase(), txn, key, data);
     if (status != OperationStatus.SUCCESS)
     {
       return false;
@@ -176,7 +176,7 @@
     DatabaseEntry data = entryData(entry);
 
     OperationStatus status;
-    status = Container.put(getDatabase(), txn, key, data);
+    status = EntryContainer.put(getDatabase(), txn, key, data);
     if (status != OperationStatus.SUCCESS)
     {
       return false;
@@ -197,7 +197,7 @@
        throws DatabaseException
   {
     OperationStatus status;
-    status = Container.put(getDatabase(), txn, key, data);
+    status = EntryContainer.put(getDatabase(), txn, key, data);
     if (status != OperationStatus.SUCCESS)
     {
       return false;
@@ -218,7 +218,7 @@
   {
     DatabaseEntry key = id.getDatabaseEntry();
 
-    OperationStatus status = Container.delete(getDatabase(), txn, key);
+    OperationStatus status = EntryContainer.delete(getDatabase(), txn, key);
     if (status != OperationStatus.SUCCESS)
     {
       return false;
@@ -242,7 +242,8 @@
     DatabaseEntry data = new DatabaseEntry();
 
     OperationStatus status;
-    status = Container.read(getDatabase(), txn, key, data, LockMode.DEFAULT);
+    status = EntryContainer.read(getDatabase(), txn, key, data,
+                                 LockMode.DEFAULT);
 
     if (status != OperationStatus.SUCCESS)
     {
@@ -311,7 +312,7 @@
     key = id.getDatabaseEntry();
 
     // Read the current count, if any.
-    OperationStatus status = Container.read(getDatabase(), txn,
+    OperationStatus status = EntryContainer.read(getDatabase(), txn,
                                             key, data, LockMode.DEFAULT);
 
     // Parse the current count.
@@ -343,7 +344,7 @@
     key = id.getDatabaseEntry();
 
     // Read the current count, if any.
-    OperationStatus status = Container.read(getDatabase(), txn,
+    OperationStatus status = EntryContainer.read(getDatabase(), txn,
                                             key, data, LockMode.RMW);
 
     // Parse the current count.
@@ -359,6 +360,6 @@
     // Write it.
     byte[] bytes = JebFormat.entryIDToDatabase(count);
     data.setData(bytes);
-    Container.put(getDatabase(), txn, key, data);
+    EntryContainer.put(getDatabase(), txn, key, data);
   }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportContext.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportContext.java
index 1f3522c..5a181cf 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportContext.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportContext.java
@@ -41,7 +41,7 @@
 public class ImportContext
 {
   /**
-   * The name of the container for the destination base DN.
+   * The name of the entryContainer for the destination base DN.
    */
   private String containerName;
 
@@ -66,7 +66,7 @@
   private LDIFReader ldifReader;
 
   /**
-   * The entry container for the destination base DN.
+   * The entry entryContainer for the destination base DN.
    */
   private EntryContainer entryContainer;
 
@@ -120,8 +120,8 @@
   }
 
   /**
-   * Set the name of the container for the destination base DN.
-   * @param containerName The container name.
+   * Set the name of the entryContainer for the destination base DN.
+   * @param containerName The entryContainer name.
    */
   public void setContainerName(String containerName)
   {
@@ -129,8 +129,8 @@
   }
 
   /**
-   * Get the name of the container for the destination base DN.
-   * @return The container name.
+   * Get the name of the entryContainer for the destination base DN.
+   * @return The entryContainer name.
    */
   public String getContainerName()
   {
@@ -210,8 +210,8 @@
   }
 
   /**
-   * Set the entry container for the destination base DN.
-   * @param entryContainer The entry container for the destination base DN.
+   * Set the entry entryContainer for the destination base DN.
+   * @param entryContainer The entry entryContainer for the destination base DN.
    */
   public void setEntryContainer(EntryContainer entryContainer)
   {
@@ -219,8 +219,8 @@
   }
 
   /**
-   * Get the entry container for the destination base DN.
-   * @return The entry container for the destination base DN.
+   * Get the entry entryContainer for the destination base DN.
+   * @return The entry entryContainer for the destination base DN.
    */
   public EntryContainer getEntryContainer()
   {
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportJob.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportJob.java
index 83ccd88..93143fc 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportJob.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportJob.java
@@ -27,8 +27,6 @@
 package org.opends.server.backends.jeb;
 
 import com.sleepycat.je.DatabaseException;
-import com.sleepycat.je.Environment;
-import com.sleepycat.je.EnvironmentConfig;
 import com.sleepycat.je.EnvironmentStats;
 import com.sleepycat.je.StatsConfig;
 import com.sleepycat.je.Transaction;
@@ -89,9 +87,9 @@
   private Config config;
 
   /**
-   * The database environment.
+   * The root container used for this import job.
    */
-  private Environment env;
+  private RootContainer rootContainer;
 
   /**
    * The LDIF import configuration.
@@ -151,12 +149,9 @@
    *                      reading, or while reading from the LDIF file.
    * @throws JebException If an error occurs in the JE backend.
    */
-  public void importLDIF() throws DatabaseException, IOException, JebException
+  public void importLDIF()
+      throws DatabaseException, IOException, JebException
   {
-    File backendDirectory = config.getBackendDirectory();
-
-    EnvironmentConfig envConfig = config.getEnvironmentConfig();
-    envConfig.setConfigParam("je.env.runCheckpointer", "false");
 /*
     envConfig.setConfigParam("je.env.runCleaner", "false");
     envConfig.setConfigParam("je.log.numBuffers", "2");
@@ -164,28 +159,30 @@
     envConfig.setConfigParam("je.log.totalBufferBytes", "30000000");
     envConfig.setConfigParam("je.log.fileMax", "100000000");
 */
-
+    rootContainer = new RootContainer(config, backend);
     if (ldifImportConfig.appendToExistingData())
     {
-      envConfig.setTransactional(true);
-      envConfig.setTxnNoSync(true);
+      rootContainer.open(config.getBackendDirectory(),
+                         config.getBackendPermission(),
+                         false, true, true, true, true, false);
     }
     else
     {
-      envConfig.setTransactional(false);
-      envConfig.setConfigParam("je.env.isLocking", "false");
+      rootContainer.open(config.getBackendDirectory(),
+                         config.getBackendPermission(),
+                         false, true, false, false, false, false);
     }
 
-    env = new Environment(backendDirectory, envConfig);
-
     if (!ldifImportConfig.appendToExistingData())
     {
       // We have the writer lock on the environment, now delete the
       // environment and re-open it. Only do this when we are
       // importing to all the base DNs in the backend.
-      env.close();
-      EnvManager.removeFiles(backendDirectory.getPath());
-      env = new Environment(backendDirectory, envConfig);
+      rootContainer.close();
+      EnvManager.removeFiles(config.getBackendDirectory().getPath());
+      rootContainer.open(config.getBackendDirectory(),
+                         config.getBackendPermission(),
+                         false, true, false, false, false, false);
     }
 
     // Divide the total buffer size by the number of threads
@@ -205,49 +202,25 @@
              message, msgID);
 
     msgID = MSGID_JEB_IMPORT_ENVIRONMENT_CONFIG;
-    message = getMessage(msgID, env.getConfig().toString());
+    message = getMessage(msgID,
+                         rootContainer.getEnvironmentConfig().toString());
     logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE,
              message, msgID);
 
     Debug.debugMessage(DebugLogCategory.BACKEND, DebugLogSeverity.INFO,
                        CLASS_NAME, "importLDIF",
-                       env.getConfig().toString());
+                       rootContainer.getEnvironmentConfig().toString());
 
 
-    // Create and open the containers for each base DN.
+    rootContainer.openEntryContainers(config.getBaseDNs());
+
+    // Create the import contextes for each base DN.
     EntryID highestID = null;
-    for (DN baseDN : config.getBaseDNs())
+    DN baseDN;
+
+    for (EntryContainer entryContainer : rootContainer.getEntryContainers())
     {
-      String containerName = BackendImpl.getContainerName(baseDN);
-      Container container = new Container(env, containerName);
-      EntryContainer entryContainer =
-           new EntryContainer(backend, config, container);
-
-      if (ldifImportConfig.appendToExistingData())
-      {
-        entryContainer.open();
-      }
-      else
-      {
-        // We will need this code when the import can specify a subset
-        // of the base DNs in the backend.
-/*
-        long t1, t2;
-
-        String msg = String.format("Removing existing data for base DN '%s'",
-                                   baseDN);
-        System.out.println(msg);
-
-        t1 = System.currentTimeMillis();
-        container.removeAllDatabases();
-        t2 = System.currentTimeMillis();
-
-        msg = String.format("Data removed in %d seconds", (t2-t1)/1000);
-        System.out.println(msg);
-*/
-
-        entryContainer.openNonTransactional(true);
-      }
+      baseDN = entryContainer.getBaseDN();
 
       // Keep track of the highest entry ID.
       EntryID id = entryContainer.getHighestEntryID();
@@ -263,7 +236,7 @@
       importContext.setLDIFImportConfig(this.ldifImportConfig);
 
       importContext.setBaseDN(baseDN);
-      importContext.setContainerName(containerName);
+      importContext.setContainerName(entryContainer.getContainerName());
       importContext.setEntryContainer(entryContainer);
       importContext.setBufferSize(bufferSize);
 
@@ -344,17 +317,13 @@
     }
     finally
     {
-      for (ImportContext ic : importMap.values())
-      {
-        ic.getEntryContainer().close();
-      }
+      rootContainer.close();
 
       // Sync the environment to disk.
       msgID = MSGID_JEB_IMPORT_CLOSING_DATABASE;
       message = getMessage(msgID);
       logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE,
                message, msgID);
-      env.close();
     }
 
     long finishTime = System.currentTimeMillis();
@@ -923,7 +892,7 @@
     public ProgressTask() throws DatabaseException
     {
       previousTime = System.currentTimeMillis();
-      prevEnvStats = env.getStats(new StatsConfig());
+      prevEnvStats = rootContainer.getEnvironmentStats(new StatsConfig());
     }
 
     /**
@@ -957,7 +926,8 @@
         Runtime runtime = Runtime.getRuntime();
         long freeMemory = runtime.freeMemory() / bytesPerMegabyte;
 
-        EnvironmentStats envStats = env.getStats(new StatsConfig());
+        EnvironmentStats envStats =
+            rootContainer.getEnvironmentStats(new StatsConfig());
         long nCacheMiss =
              envStats.getNCacheMiss() - prevEnvStats.getNCacheMiss();
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportThread.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportThread.java
index 5189397..a17fbf0 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportThread.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ImportThread.java
@@ -58,17 +58,17 @@
   private ImportContext importContext;
 
   /**
-   * The destination entry container for entries read from the queue.
+   * The destination entry entryContainer for entries read from the queue.
    */
   private EntryContainer entryContainer;
 
   /**
-   * The entry database of the destination entry container.
+   * The entry database of the destination entry entryContainer.
    */
   private ID2Entry id2entry;
 
   /**
-   * The referral database of the destination entry container.
+   * The referral database of the destination entry entryContainer.
    */
   private DN2URI dn2uri;
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/Index.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/Index.java
index e32e99f..ec11b0f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/Index.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/Index.java
@@ -65,9 +65,9 @@
 
 
   /**
-   * The database container holding this index database.
+   * The database entryContainer holding this index database.
    */
-  private Container container;
+  private EntryContainer entryContainer;
 
   /**
    * The JE database configuration.
@@ -75,7 +75,7 @@
   private DatabaseConfig dbConfig;
 
   /**
-   * The name of the database within the container.
+   * The name of the database within the entryContainer.
    */
   private String name;
 
@@ -114,8 +114,8 @@
 
   /**
    * Create a new index object.
-   * @param container The database container holding this index.
-   * @param name The name of the index database within the container.
+   * @param entryContainer The database entryContainer holding this index.
+   * @param name The name of the index database within the entryContainer.
    * @param indexer The indexer object to construct index keys from LDAP
    * attribute values.
    * @param indexEntryLimit The configured limit on the number of entry IDs
@@ -123,10 +123,10 @@
    * @param cursorEntryLimit The configured limit on the number of entry IDs
    * that may be retrieved by cursoring through an index.
    */
-  public Index(Container container, String name, Indexer indexer,
+  public Index(EntryContainer entryContainer, String name, Indexer indexer,
                int indexEntryLimit, int cursorEntryLimit)
   {
-    this.container = container;
+    this.entryContainer = entryContainer;
     this.name = name;
     this.indexer = indexer;
     this.comparator = indexer.getComparator();
@@ -148,7 +148,7 @@
 
   /**
    * Get a handle to the database. It returns a per-thread handle to avoid
-   * any thread contention on the database handle. The container is
+   * any thread contention on the database handle. The entryContainer is
    * responsible for closing all handles.
    *
    * @return A database handle.
@@ -160,7 +160,7 @@
     Database database = threadLocalDatabase.get();
     if (database == null)
     {
-      database = container.openDatabase(dbConfig, name);
+      database = entryContainer.openDatabase(dbConfig, name);
       threadLocalDatabase.set(database);
     }
     return database;
@@ -182,7 +182,7 @@
     DatabaseEntry entryIDData = entryID.getDatabaseEntry();
     DatabaseEntry data = new DatabaseEntry();
 
-    status = Container.read(getDatabase(), txn, key, data, lockMode);
+    status = EntryContainer.read(getDatabase(), txn, key, data, lockMode);
 
     if (status == OperationStatus.SUCCESS)
     {
@@ -202,12 +202,12 @@
 
         byte[] after = entryIDList.toDatabase();
         data.setData(after);
-        Container.put(getDatabase(), txn, key, data);
+        EntryContainer.put(getDatabase(), txn, key, data);
       }
     }
     else
     {
-      Container.put(getDatabase(), txn, key, entryIDData);
+      EntryContainer.put(getDatabase(), txn, key, entryIDData);
     }
   }
 
@@ -226,7 +226,7 @@
     LockMode lockMode = LockMode.RMW;
     DatabaseEntry data = new DatabaseEntry();
 
-    status = Container.read(getDatabase(), txn, key, data, lockMode);
+    status = EntryContainer.read(getDatabase(), txn, key, data, lockMode);
 
     if (status == OperationStatus.SUCCESS)
     {
@@ -244,12 +244,12 @@
           if (after == null)
           {
             // No more IDs, so remove the key
-            Container.delete(getDatabase(), txn, key);
+            EntryContainer.delete(getDatabase(), txn, key);
           }
           else
           {
             data.setData(after);
-            Container.put(getDatabase(), txn, key, data);
+            EntryContainer.put(getDatabase(), txn, key, data);
           }
         }
       }
@@ -280,7 +280,7 @@
     LockMode lockMode = LockMode.DEFAULT;
     DatabaseEntry data = new DatabaseEntry();
 
-    status = Container.read(getDatabase(), txn, key, data, lockMode);
+    status = EntryContainer.read(getDatabase(), txn, key, data, lockMode);
     if (status == OperationStatus.SUCCESS)
     {
       EntryIDSet entryIDList =
@@ -320,7 +320,7 @@
     {
       OperationStatus status;
       DatabaseEntry data = new DatabaseEntry();
-      status = Container.read(getDatabase(), txn, key, data, lockMode);
+      status = EntryContainer.read(getDatabase(), txn, key, data, lockMode);
       if (status != OperationStatus.SUCCESS)
       {
         return new EntryIDSet(key.getData(), null);
@@ -351,7 +351,7 @@
     if (after == null)
     {
       // No more IDs, so remove the key.
-      Container.delete(getDatabase(), txn, key);
+      EntryContainer.delete(getDatabase(), txn, key);
     }
     else
     {
@@ -360,7 +360,7 @@
         entryLimitExceededCount++;
       }
       data.setData(after);
-      Container.put(getDatabase(), txn, key, data);
+      EntryContainer.put(getDatabase(), txn, key, data);
     }
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexFilter.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexFilter.java
index 08eb7a1..6104931 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexFilter.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexFilter.java
@@ -48,7 +48,7 @@
   public static final int FILTER_CANDIDATE_THRESHOLD = 10;
 
   /**
-   * The entry container holding the attribute indexes.
+   * The entry entryContainer holding the attribute indexes.
    */
   private EntryContainer entryContainer;
 
@@ -67,7 +67,7 @@
   /**
    * Construct an index filter for a search operation.
    *
-   * @param entryContainer The entry container.
+   * @param entryContainer The entry entryContainer.
    * @param searchOp       The search operation to be evaluated.
    *
    * @param debugBuilder If not null, a diagnostic string will be written
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/RootContainer.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/RootContainer.java
new file mode 100644
index 0000000..35accf3
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/RootContainer.java
@@ -0,0 +1,622 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2006 Sun Microsystems, Inc.
+ */
+package org.opends.server.backends.jeb;
+
+import com.sleepycat.je.Database;
+import com.sleepycat.je.DatabaseException;
+import com.sleepycat.je.Environment;
+import com.sleepycat.je.EnvironmentConfig;
+import com.sleepycat.je.EnvironmentStats;
+import com.sleepycat.je.PreloadConfig;
+import com.sleepycat.je.PreloadStats;
+import com.sleepycat.je.PreloadStatus;
+import com.sleepycat.je.config.EnvironmentParams;
+import com.sleepycat.je.config.ConfigParam;
+import com.sleepycat.je.StatsConfig;
+import com.sleepycat.je.CheckpointConfig;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.*;
+import java.io.File;
+import java.io.FilenameFilter;
+
+import org.opends.server.monitors.DatabaseEnvironmentMonitor;
+import org.opends.server.types.*;
+import org.opends.server.loggers.Debug;
+import static org.opends.server.loggers.Error.logError;
+import static org.opends.server.loggers.Debug.debugException;
+import static org.opends.server.loggers.Debug.debugEnter;
+import static org.opends.server.messages.MessageHandler.getMessage;
+import static org.opends.server.messages.JebMessages.
+    MSGID_JEB_CACHE_SIZE_AFTER_PRELOAD;
+import static org.opends.server.messages.JebMessages.
+    MSGID_JEB_CLEAN_DATABASE_START;
+import static org.opends.server.messages.JebMessages.
+    MSGID_JEB_CLEAN_DATABASE_MARKED;
+import static org.opends.server.messages.JebMessages.
+    MSGID_JEB_CLEAN_DATABASE_FINISH;
+import static org.opends.server.messages.JebMessages.
+    MSGID_JEB_SET_PERMISSIONS_FAILED;
+import org.opends.server.api.Backend;
+
+/**
+ * Wrapper class for the JE environment. Root container holds all the entry
+ * containers for each base DN. It also maintains all the openings and closings
+ * of the entry containers.
+ */
+public class RootContainer
+{
+    /**
+   * The fully-qualified name of this class for debugging purposes.
+   */
+  private static final String CLASS_NAME =
+       "org.opends.server.backends.jeb.RootContainer";
+
+  /**
+   * The JE database environment.
+   */
+  private Environment env;
+
+  /**
+   * The backend configuration.
+   */
+  private Config config;
+
+  /**
+   * The backend to which this entry root container belongs.
+   */
+  private Backend backend;
+
+  /**
+   * The database environment monitor for this JE environment.
+   */
+  private DatabaseEnvironmentMonitor monitor;
+
+  /**
+   * A configurable component to handle changes to the configuration of
+   * the database environment.
+   */
+  private ConfigurableEnvironment configurableEnv;
+
+  /**
+   * The base DNs contained in this entryContainer.
+   */
+  private ConcurrentHashMap<DN, EntryContainer> entryContainers;
+
+  /**
+   * Creates a new RootContainer object. Each root container represents a JE
+   * environment.
+   *
+   * @param config The configuration of the JE backend.
+   * @param backend A reference to the JE back end that is creating this
+   *                root container.
+   */
+  public RootContainer(Config config, Backend backend)
+  {
+    this.env = null;
+    this.configurableEnv = null;
+    this.monitor = null;
+    this.entryContainers = new ConcurrentHashMap<DN, EntryContainer>();
+    this.backend = backend;
+    this.config = config;
+  }
+
+  /**
+   * Helper method to apply database directory permissions and create a new
+   * JE environment.
+   *
+   * @param backendDirectory The environment home directory for JE.
+   * @param backendPermission The file permissions for the environment home
+   *                          directory.
+   * @param envConfig The JE environment configuration.
+   * @throws DatabaseException If an error occurs when creating the environment.
+   */
+  private void open(File backendDirectory,
+                    FilePermission backendPermission,
+                    EnvironmentConfig envConfig) throws DatabaseException
+  {
+    // Get the backend database backendDirectory permissions and apply
+    try
+    {
+      if(!FilePermission.setPermissions(backendDirectory, backendPermission))
+      {
+        throw new Exception();
+      }
+    }
+    catch(Exception e)
+    {
+      // Log an warning that the permissions were not set.
+      int msgID = MSGID_JEB_SET_PERMISSIONS_FAILED;
+      String message = getMessage(msgID, backendDirectory.getPath());
+      logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.SEVERE_WARNING,
+               message, msgID);
+    }
+
+    // Open the database environment
+    env = new Environment(backendDirectory,
+                          envConfig);
+
+    Debug.debugMessage(DebugLogCategory.BACKEND, DebugLogSeverity.INFO,
+                         CLASS_NAME, "initializeBackend",
+                         env.getConfig().toString());
+  }
+
+  /**
+   * Opens the root container.
+   *
+   * @throws DatabaseException If an error occurs when opening the container.
+   */
+  public void open() throws DatabaseException
+  {
+    open(config.getBackendDirectory(),
+         config.getBackendPermission(),
+         config.getEnvironmentConfig());
+  }
+
+  /**
+   * Opens the root container using the configuration parameters provided. Any
+   * configuration parameters provided will override the parameters in the
+   * JE configuration object.
+   *
+   * @param backendDirectory The environment home directory for JE.
+   * @param backendPermission he file permissions for the environment home
+   *                          directory.
+   * @param readOnly Open the container in read only mode.
+   * @param allowCreate Allow creating new entries in the container.
+   * @param transactional Use transactions on operations.
+   * @param txnNoSync Use asynchronous transactions.
+   * @param isLocking Create the environment with locking.
+   * @param runCheckPointer Start the checkpointer.
+   * @throws DatabaseException If an error occurs when openinng the container.
+   */
+  public void open(File backendDirectory,
+                   FilePermission backendPermission,
+                   boolean readOnly,
+                   boolean allowCreate,
+                   boolean transactional,
+                   boolean txnNoSync,
+                   boolean isLocking,
+                   boolean runCheckPointer) throws DatabaseException
+  {
+
+    EnvironmentConfig envConfig;
+    if(config.getEnvironmentConfig() != null)
+    {
+      envConfig = config.getEnvironmentConfig();
+    }
+    else
+    {
+      envConfig = new EnvironmentConfig();
+    }
+    envConfig.setReadOnly(readOnly);
+    envConfig.setAllowCreate(allowCreate);
+    envConfig.setTransactional(transactional);
+    envConfig.setTxnNoSync(txnNoSync);
+    envConfig.setConfigParam("je.env.isLocking", String.valueOf(isLocking));
+    envConfig.setConfigParam("je.env.runCheckpointer",
+                             String.valueOf(runCheckPointer));
+
+    open(backendDirectory, backendPermission, envConfig);
+  }
+
+  /**
+   * Opens the entry container for a base DN. If the entry container does not
+   * exist for the base DN, it will be created. The entry container will be
+   * opened with the same mode as the root container. Any entry containers
+   * opened in a read only root container will also be read only. Any entry
+   * containers opened in a non transactional root container will also be non
+   * transactional.
+   *
+   * @param baseDN The base DN of the entry container to open.
+   * @return The opened entry container.
+   * @throws DatabaseException If an error occurs while opening the entry
+   *                           container.
+   */
+  public EntryContainer openEntryContainer(DN baseDN) throws DatabaseException
+  {
+    EntryContainer ec = new EntryContainer(baseDN, backend, config, env);
+    if(env.getConfig().getReadOnly())
+    {
+      ec.openReadOnly();
+    }
+    else if(!env.getConfig().getTransactional())
+    {
+      ec.openNonTransactional(true);
+    }
+    else
+    {
+      ec.open();
+    }
+    this.entryContainers.put(baseDN, ec);
+
+    return ec;
+  }
+
+  /**
+   * Opens the entry containers for multiple base DNs.
+   *
+   * @param baseDNs The base DNs of the entry containers to open.
+   * @throws DatabaseException If an error occurs while opening the entry
+   *                           container.
+   */
+  public void openEntryContainers(DN[] baseDNs) throws DatabaseException
+  {
+    for(DN baseDN : baseDNs)
+    {
+      openEntryContainer(baseDN);
+    }
+  }
+
+  /**
+   * Close the entry container for a base DN.
+   *
+   * @param baseDN The base DN of the entry container to close.
+   * @throws DatabaseException If an error occurs while closing the entry
+   *                           container.
+   */
+  public void closeEntryContainer(DN baseDN) throws DatabaseException
+  {
+    getEntryContainer(baseDN).close();
+    entryContainers.remove(baseDN);
+  }
+
+  /**
+   * Close and remove a entry container for a base DN from disk.
+   *
+   * @param baseDN The base DN of the entry container to remove.
+   * @throws DatabaseException If an error occurs while removing the entry
+   *                           container.
+   */
+  public void removeEntryContainer(DN baseDN) throws DatabaseException
+  {
+    getEntryContainer(baseDN).close();
+    getEntryContainer(baseDN).removeContainer();
+    entryContainers.remove(baseDN);
+  }
+
+  /**
+   * Get the ConfigurableEnvironment object for JE environment used by this
+   * root container.
+   *
+   * @return The ConfigurableEnvironment object.
+   */
+  public ConfigurableEnvironment getConfigurableEnvironment()
+  {
+    if(configurableEnv == null)
+    {
+      DN envConfigDN = config.getEnvConfigDN();
+      if (envConfigDN != null)
+      {
+        configurableEnv = new ConfigurableEnvironment(envConfigDN, env);
+      }
+    }
+
+    return configurableEnv;
+  }
+
+  /**
+   * Get the DatabaseEnvironmentMonitor object for JE environment used by this
+   * root container.
+   *
+   * @return The DatabaseEnvironmentMonito object.
+   */
+  public DatabaseEnvironmentMonitor getMonitorProvider()
+  {
+    if(monitor == null)
+    {
+      String monitorName = backend.getBackendID() + " Database Environment";
+      monitor = new DatabaseEnvironmentMonitor(monitorName, env);
+    }
+
+    return monitor;
+  }
+
+  /**
+   * Preload the database cache. There is no preload if the configured preload
+   * time limit is zero.
+   */
+  public void preload()
+  {
+    assert debugEnter(CLASS_NAME, "preload");
+
+    long timeLimit = config.getPreloadTimeLimit();
+
+    if (timeLimit > 0)
+    {
+      // Get a list of all the databases used by the backend.
+      ArrayList<Database> dbList = new ArrayList<Database>();
+      for (EntryContainer ec : entryContainers.values())
+      {
+        ec.listDatabases(dbList);
+      }
+
+      // Sort the list in order of priority.
+      Collections.sort(dbList, new DbPreloadComparator());
+
+      // Preload each database until we reach the time limit or the cache
+      // is filled.
+      try
+      {
+        long timeEnd = System.currentTimeMillis() + timeLimit;
+
+        // Configure preload of Leaf Nodes (LNs) containing the data values.
+        PreloadConfig preloadConfig = new PreloadConfig();
+        preloadConfig.setLoadLNs(true);
+
+        for (Database db : dbList)
+        {
+          // Calculate the remaining time.
+          long timeRemaining = timeEnd - System.currentTimeMillis();
+          if (timeRemaining <= 0)
+          {
+            break;
+          }
+
+          preloadConfig.setMaxMillisecs(timeRemaining);
+          PreloadStats preloadStats = db.preload(preloadConfig);
+/*
+          System.out.println("file=" + db.getDatabaseName() +
+                             " LNs=" + preloadStats.getNLNsLoaded());
+*/
+
+          // Stop if the cache is full or the time limit has been exceeded.
+          if (preloadStats.getStatus() != PreloadStatus.SUCCESS)
+          {
+            break;
+          }
+        }
+
+        // Log an informational message about the size of the cache.
+        EnvironmentStats stats = env.getStats(new StatsConfig());
+        long total = stats.getCacheTotalBytes();
+
+        int msgID = MSGID_JEB_CACHE_SIZE_AFTER_PRELOAD;
+        String message = getMessage(msgID, total / (1024 * 1024));
+        logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message,
+                 msgID);
+      }
+      catch (DatabaseException e)
+      {
+        assert debugException(CLASS_NAME, "preload", e);
+      }
+    }
+  }
+
+  /**
+   * Synchronously invokes the cleaner on the database environment then forces a
+   * checkpoint to delete the log files that are no longer in use.
+   *
+   * @throws DatabaseException If an error occurs while cleaning the database
+   * environment.
+   */
+  private void cleanDatabase()
+       throws DatabaseException
+  {
+    assert debugEnter(CLASS_NAME, "cleanDatabase");
+
+    int msgID;
+    String message;
+
+    FilenameFilter filenameFilter = new FilenameFilter()
+    {
+      public boolean accept(File d, String name)
+      {
+        return name.endsWith(".jdb");
+      }
+    };
+
+    File backendDirectory = env.getHome();
+    int beforeLogfileCount = backendDirectory.list(filenameFilter).length;
+
+    msgID = MSGID_JEB_CLEAN_DATABASE_START;
+    message = getMessage(msgID, beforeLogfileCount, backendDirectory.getPath());
+    logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message,
+             msgID);
+
+    int currentCleaned = 0;
+    int totalCleaned = 0;
+    while ((currentCleaned = env.cleanLog()) > 0)
+    {
+      totalCleaned += currentCleaned;
+    }
+
+    msgID = MSGID_JEB_CLEAN_DATABASE_MARKED;
+    message = getMessage(msgID, totalCleaned);
+    logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message,
+             msgID);
+
+    if (totalCleaned > 0)
+    {
+      CheckpointConfig force = new CheckpointConfig();
+      force.setForce(true);
+      env.checkpoint(force);
+    }
+
+    int afterLogfileCount = backendDirectory.list(filenameFilter).length;
+
+    msgID = MSGID_JEB_CLEAN_DATABASE_FINISH;
+    message = getMessage(msgID, afterLogfileCount);
+    logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message,
+             msgID);
+
+  }
+
+  /**
+   * Close the root entryContainer.
+   *
+   * @throws DatabaseException If an error occurs while attempting to close
+   * the entryContainer.
+   */
+  public void close() throws DatabaseException
+  {
+    for(DN baseDN : entryContainers.keySet())
+    {
+      entryContainers.get(baseDN).close();
+      entryContainers.remove(baseDN);
+    }
+
+    env.close();
+  }
+
+  /**
+   * Return all the entry containers in this root container.
+   *
+   * @return The entry containers in this root container.
+   */
+  public Collection<EntryContainer> getEntryContainers()
+  {
+    return entryContainers.values();
+  }
+
+  /**
+   * Returns all the baseDNs this root container stores.
+   *
+   * @return The set of DNs this root container stores.
+   */
+  public Set<DN> getBaseDNs()
+  {
+    return entryContainers.keySet();
+  }
+
+  /**
+   * Return the entry container for a specific base DN.
+   *
+   * @param baseDN The base DN of the entry container to retrive.
+   * @return The entry container for the base DN.
+   */
+  public EntryContainer getEntryContainer(DN baseDN)
+  {
+    EntryContainer ec = null;
+    DN nodeDN = baseDN;
+
+    while (ec == null && nodeDN != null)
+    {
+      ec = entryContainers.get(nodeDN);
+      if (ec == null)
+      {
+        nodeDN = nodeDN.getParent();
+      }
+    }
+
+    return ec;
+  }
+
+  /**
+   * Apply new configuration to the JE environment.
+   *
+   * @param newConfig The new configuration to apply.
+   * @throws DatabaseException If an error occurs while applying the new
+   *                           configuration.
+   */
+  public void applyNewConfig(Config newConfig) throws DatabaseException
+  {
+    // Check for changes to the database directory permissions
+    FilePermission oldPermission = config.getBackendPermission();
+    FilePermission newPermission = newConfig.getBackendPermission();
+
+    if(!FilePermission.toUNIXMode(oldPermission).equals(
+        FilePermission.toUNIXMode(newPermission)))
+    {
+      try
+      {
+        if(!FilePermission.setPermissions(newConfig.getBackendDirectory(),
+                                          newPermission))
+        {
+          throw new Exception();
+        }
+      }
+      catch(Exception e)
+      {
+        // Log an warning that the permissions were not set.
+        int msgID = MSGID_JEB_SET_PERMISSIONS_FAILED;
+        String message = getMessage(msgID,
+                                    config.getBackendDirectory().getPath());
+        logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.SEVERE_WARNING,
+                 message, msgID);
+      }
+    }
+
+    // Check if any JE non-mutable properties were changed.
+    EnvironmentConfig oldEnvConfig = this.config.getEnvironmentConfig();
+    EnvironmentConfig newEnvConfig = newConfig.getEnvironmentConfig();
+    Map paramsMap = EnvironmentParams.SUPPORTED_PARAMS;
+    for (Object o : paramsMap.values())
+    {
+      ConfigParam param = (ConfigParam)o;
+      if (!param.isMutable())
+      {
+        String oldValue = oldEnvConfig.getConfigParam(param.getName());
+        String newValue = newEnvConfig.getConfigParam(param.getName());
+        if (!oldValue.equalsIgnoreCase(newValue))
+        {
+          System.out.println("The change to the following property will " +
+                             "take effect when the backend is restarted: " +
+                             param.getName());
+        }
+      }
+    }
+
+    // This takes care of changes to the JE environment for those
+    // properties that are mutable at runtime.
+    env.setMutableConfig(newConfig.getEnvironmentConfig());
+
+    config = newConfig;
+
+    Debug.debugMessage(DebugLogCategory.BACKEND, DebugLogSeverity.INFO,
+                       CLASS_NAME, "applyNewConfiguration",
+                       env.getConfig().toString());
+  }
+
+  /**
+   * Get the environment stats of the JE environment used in this root
+   * container.
+   *
+   * @param statsConfig The configuration to use for the EnvironmentStats
+   *                    object.
+   * @return The environment status of the JE environment.
+   * @throws DatabaseException If an error occurs while retriving the stats
+   *                           object.
+   */
+  public EnvironmentStats getEnvironmentStats(StatsConfig statsConfig)
+      throws DatabaseException
+  {
+    return env.getStats(statsConfig);
+  }
+
+  /**
+   * Get the environment config of the JE environment used in this root
+   * container.
+   *
+   * @return The environment config of the JE environment.
+   * @throws DatabaseException If an error occurs while retriving the
+   *                           configuration object.
+   */
+  public EnvironmentConfig getEnvironmentConfig() throws DatabaseException
+  {
+    return env.getConfig();
+  }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/VerifyJob.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/VerifyJob.java
index bd62750..3fda5c2 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/VerifyJob.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/VerifyJob.java
@@ -32,26 +32,20 @@
 import com.sleepycat.je.CursorConfig;
 import com.sleepycat.je.DatabaseEntry;
 import com.sleepycat.je.DatabaseException;
-import com.sleepycat.je.Environment;
-import com.sleepycat.je.EnvironmentConfig;
 import com.sleepycat.je.EnvironmentStats;
 import com.sleepycat.je.LockMode;
 import com.sleepycat.je.OperationStatus;
 import com.sleepycat.je.StatsConfig;
 import com.sleepycat.je.Transaction;
 
-import org.opends.server.api.Backend;
 import org.opends.server.api.OrderingMatchingRule;
 import org.opends.server.core.DirectoryServer;
-import org.opends.server.loggers.Debug;
 import org.opends.server.protocols.asn1.ASN1OctetString;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeType;
 import org.opends.server.types.AttributeValue;
 import org.opends.server.types.ByteString;
 import org.opends.server.types.ConditionResult;
-import org.opends.server.types.DebugLogCategory;
-import org.opends.server.types.DebugLogSeverity;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
@@ -65,7 +59,6 @@
 import static org.opends.server.messages.MessageHandler.getMessage;
 import static org.opends.server.messages.JebMessages.*;
 
-import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -94,19 +87,14 @@
   private VerifyConfig verifyConfig;
 
   /**
-   * The JE backend to be verified.
-   */
-  private Backend backend;
-
-  /**
    * The configuration of the JE backend.
    */
   private Config config;
 
   /**
-   * A read-only JE database environment handle for the purpose of verification.
+   * The root container used for the verify job.
    */
-  private Environment env;
+  RootContainer rootContainer;
 
   /**
    * The number of milliseconds between job progress reports.
@@ -193,44 +181,29 @@
   /**
    * Construct a VerifyJob.
    *
-   * @param backend The backend performing the verify process.
    * @param config The backend configuration.
    * @param verifyConfig The verify configuration.
    */
-  public VerifyJob(Backend backend, Config config, VerifyConfig verifyConfig)
+  public VerifyJob(Config config, VerifyConfig verifyConfig)
   {
     this.verifyConfig = verifyConfig;
-    this.backend = backend;
     this.config = config;
   }
 
   /**
    * Verify the backend.
+   *
+   * @param rootContainer The root container that holds the entries to verify.
    * @throws DatabaseException If an error occurs in the JE database.
    * @throws JebException If an error occurs in the JE backend.
    */
-  public void verifyBackend() throws DatabaseException, JebException
+  public void verifyBackend(RootContainer rootContainer) throws
+      DatabaseException, JebException
   {
-    File backendDirectory = config.getBackendDirectory();
 
-    // Open the environment read-only.
-    EnvironmentConfig envConfig = config.getEnvironmentConfig();
-    envConfig.setReadOnly(true);
-    envConfig.setAllowCreate(false);
-    envConfig.setTransactional(false);
-    env = new Environment(backendDirectory, envConfig);
-
-    Debug.debugMessage(DebugLogCategory.BACKEND, DebugLogSeverity.INFO,
-                       CLASS_NAME, "verifyBackend",
-                       env.getConfig().toString());
-
-    // Open a container read-only.
-    String containerName =
-         BackendImpl.getContainerName(verifyConfig.getBaseDN());
-    Container container = new Container(env, containerName);
+    this.rootContainer = rootContainer;
     EntryContainer entryContainer =
-         new EntryContainer(backend, config, container);
-    entryContainer.openReadOnly();
+        rootContainer.getEntryContainer(verifyConfig.getBaseDN());
 
     ArrayList<String> completeList = verifyConfig.getCompleteList();
     ArrayList<String> cleanList = verifyConfig.getCleanList();
@@ -304,7 +277,7 @@
 
     // We will be updating these files independently of the indexes
     // so we need direct access to them rather than going through
-    // the entry container methods.
+    // the entry entryContainer methods.
     id2entry = entryContainer.getID2Entry();
     dn2id = entryContainer.getDN2ID();
     id2c = entryContainer.getID2Children();
@@ -1567,7 +1540,8 @@
     public ProgressTask() throws DatabaseException
     {
       previousTime = System.currentTimeMillis();
-      prevEnvStats = env.getStats(new StatsConfig());
+      prevEnvStats =
+          rootContainer.getEnvironmentStats(new StatsConfig());
     }
 
     /**
@@ -1597,7 +1571,8 @@
         Runtime runtime = Runtime.getRuntime();
         long freeMemory = runtime.freeMemory() / bytesPerMegabyte;
 
-        EnvironmentStats envStats = env.getStats(new StatsConfig());
+        EnvironmentStats envStats =
+            rootContainer.getEnvironmentStats(new StatsConfig());
         long nCacheMiss =
              envStats.getNCacheMiss() - prevEnvStats.getNCacheMiss();
 
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestEntryContainer.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestEntryContainer.java
index 9e0a429..1da950c 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestEntryContainer.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestEntryContainer.java
@@ -35,8 +35,11 @@
 import java.util.ArrayList;
 
 import org.opends.server.TestCaseUtils;
+import org.opends.server.core.DirectoryServer;
 import org.opends.server.types.Entry;
 import org.opends.server.types.LDIFImportConfig;
+import org.opends.server.types.FilePermission;
+import org.opends.server.types.DN;
 import org.opends.server.util.LDIFReader;
 import org.testng.annotations.Test;
 import org.testng.annotations.BeforeClass;
@@ -181,14 +184,14 @@
   @Test()
   public void test1() throws Exception {
     EnvManager.createHomeDir(homeDirName);
-    EnvironmentConfig envConfig = new EnvironmentConfig();
-    envConfig.setTransactional(true);
-    envConfig.setAllowCreate(true);
-    Environment env = new Environment(new File(homeDirName), envConfig);
-    EntryContainer entryContainer = new EntryContainer(null, new Config(),
-        new Container(env, null));
+    RootContainer rootContainer = new RootContainer(new Config(), null);
+    rootContainer.open(new File(homeDirName),
+                       new FilePermission(true, true, true),
+                       false, true, true, false, true, true);
 
-    entryContainer.open();
+    EntryContainer entryContainer =
+        rootContainer.openEntryContainer(DirectoryServer.getSchemaDN());
+
     EntryID actualHighestID = entryContainer.getHighestEntryID();
     assertTrue(actualHighestID.equals(new EntryID(0)));
 
@@ -201,9 +204,7 @@
     actualHighestID = entryContainer.getHighestEntryID();
     assertTrue(actualHighestID.equals(new EntryID(calculatedHighestID)));
 
-    entryContainer.close();
-
-    env.close();
+    rootContainer.close();
     EnvManager.removeFiles(homeDirName);
   }
 }

--
Gitblit v1.10.0