From 83dd61651cb5d73c1a15dfcb7d217c0f272722d2 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). 

---
 opends/src/server/org/opends/server/backends/jeb/BackendImpl.java |  504 +++++++++++--------------------------------------------
 1 files changed, 106 insertions(+), 398 deletions(-)

diff --git a/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java b/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
index f838746..d2d54cf 100644
--- a/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
+++ b/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;
   }
 }

--
Gitblit v1.10.0