mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Fabio Pistolesi
13.24.2015 d161a11bcc21f7a035f069a99477df23198adfd1
OPENDJ-1793 Convert PersistItBackendCfg references to PluggableBackendCfg

Remove some of the persistit references in the common pluggable backend code, moving management of files and directories to the storage layer.
Some refactioring can also be applied to JE.
8 files modified
950 ■■■■■ changed files
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/BackendImpl.java 39 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PersistItStorage.java 261 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PitBackend.java 43 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/BackendImpl.java 83 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java 73 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/Importer.java 2 ●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/RootContainer.java 433 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/spi/Storage.java 16 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/BackendImpl.java
@@ -27,7 +27,6 @@
package org.opends.server.backends.jeb;
import static com.sleepycat.je.EnvironmentConfig.*;
import static org.opends.messages.BackendMessages.*;
import static org.opends.messages.JebMessages.*;
import static org.opends.server.backends.jeb.ConfigurableEnvironment.*;
@@ -206,14 +205,11 @@
    DirectoryServer.registerMonitorProvider(rootContainerMonitor);
    // Register as disk space monitor handler
    File parentDirectory = getFileForPath(cfg.getDBDirectory());
    File backendDirectory =
        new File(parentDirectory, cfg.getBackendId());
    diskMonitor = new DiskSpaceMonitor(getBackendID() + " backend",
        backendDirectory, cfg.getDiskLowThreshold(), cfg.getDiskFullThreshold(),
        5, TimeUnit.SECONDS, this);
    diskMonitor.initializeMonitorProvider(null);
    diskMonitor = newDiskMonitor(cfg);
    if (diskMonitor != null)
    {
    DirectoryServer.registerMonitorProvider(diskMonitor);
    }
    //Register as an AlertGenerator.
    DirectoryServer.registerAlertGenerator(this);
@@ -221,6 +217,18 @@
    cfg.addLocalDBChangeListener(this);
  }
  private DiskSpaceMonitor newDiskMonitor(LocalDBBackendCfg cfg) throws ConfigException, InitializationException
  {
    File parentDirectory = getFileForPath(cfg.getDBDirectory());
    File backendDirectory =
        new File(parentDirectory, cfg.getBackendId());
    DiskSpaceMonitor dm = new DiskSpaceMonitor(getBackendID() + " backend",
        backendDirectory, cfg.getDiskLowThreshold(), cfg.getDiskFullThreshold(),
        5, TimeUnit.SECONDS, this);
    dm.initializeMonitorProvider(null);
    return dm;
  }
  /** {@inheritDoc} */
  @Override
  public void finalizeBackend()
@@ -1000,11 +1008,9 @@
        baseDNs = newBaseDNsArray;
      }
      if(cfg.getDiskFullThreshold() != newCfg.getDiskFullThreshold() ||
          cfg.getDiskLowThreshold() != newCfg.getDiskLowThreshold())
      if (diskMonitor != null)
      {
        diskMonitor.setFullThreshold(newCfg.getDiskFullThreshold());
        diskMonitor.setLowThreshold(newCfg.getDiskLowThreshold());
        updateDiskMonitor(diskMonitor, newCfg);
      }
      // Put the new configuration in place.
@@ -1018,6 +1024,15 @@
    return ccr;
  }
  /**
   * @param newCfg
   */
  private void updateDiskMonitor(DiskSpaceMonitor dm, LocalDBBackendCfg newCfg)
  {
    dm.setFullThreshold(newCfg.getDiskFullThreshold());
    dm.setLowThreshold(newCfg.getDiskLowThreshold());
  }
  private void removeDeletedBaseDNs(SortedSet<DN> newBaseDNs) throws DirectoryException
  {
    for (DN baseDN : cfg.getBaseDN())
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PersistItStorage.java
@@ -27,17 +27,23 @@
import static com.persistit.Transaction.CommitPolicy.*;
import static java.util.Arrays.*;
import static org.opends.messages.ConfigMessages.ERR_CONFIG_BACKEND_INSANE_MODE;
import static org.opends.messages.ConfigMessages.ERR_CONFIG_BACKEND_MODE_INVALID;
import static org.opends.messages.JebMessages.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.File;
import java.io.FilenameFilter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.config.server.ConfigChangeResult;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.PersistitBackendCfg;
import org.opends.server.admin.std.server.PluggableBackendCfg;
import org.opends.server.backends.pluggable.spi.Cursor;
@@ -49,6 +55,9 @@
import org.opends.server.backends.pluggable.spi.UpdateFunction;
import org.opends.server.backends.pluggable.spi.WriteOperation;
import org.opends.server.backends.pluggable.spi.WriteableStorage;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.FilePermission;
import com.persistit.Configuration;
import com.persistit.Configuration.BufferPoolConfiguration;
@@ -65,7 +74,7 @@
import com.persistit.exception.RollbackException;
/** PersistIt database implementation of the {@link Storage} engine. */
public final class PersistItStorage implements Storage
public final class PersistItStorage implements Storage, ConfigurationChangeListener<PersistitBackendCfg>
{
  private static final String VOLUME_NAME = "dj";
  /** The buffer / page size used by the PersistIt storage. */
@@ -504,6 +513,7 @@
        throw new IllegalStateException(e);
      }
    }
    config.removePersistitChangeListener(this);
  }
  /** {@inheritDoc} */
@@ -537,6 +547,7 @@
      bufferPoolCfg.setFraction(cfg.getDBCachePercent() / 100.0f);
    }
    dbCfg.setCommitPolicy(cfg.isDBTxnNoSync() ? SOFT : GROUP);
    cfg.addPersistitChangeListener(this);
  }
  private BufferPoolConfiguration getBufferPoolCfg()
@@ -553,8 +564,9 @@
  /** {@inheritDoc} */
  @Override
  public void open()
  public void open() throws Exception
  {
    setupStorageFiles();
    try
    {
      db = new Persistit(dbCfg);
@@ -621,21 +633,15 @@
  /** {@inheritDoc} */
  @Override
  public Importer startImport()
  public Importer startImport() throws Exception
  {
    clearAndCreateDbDir(backendDirectory);
    open();
    return new ImporterImpl();
  }
  /**
   * Replace persistit reserved comma character with an underscore character.
   *
   * @param suffix
   *          the suffix name to convert
   * @return a new String suitable for use as a suffix name
   */
  public String toSuffixName(final String suffix)
  /** {@inheritDoc} */
  public String toSafeSuffixName(final String suffix)
  {
    return suffix.replaceAll("[,=]", "_");
  }
@@ -700,7 +706,7 @@
    };
  }
  /*
  /**
   * TODO: it would be nice to use the low-level key/value APIs. They seem quite
   * inefficient at the moment for simple byte arrays.
   */
@@ -735,4 +741,235 @@
    }
    return null;
  }
  /** {@inheritDoc} */
  @Override
  public boolean isConfigurationChangeAcceptable(PersistitBackendCfg cfg, List<LocalizableMessage> unacceptableReasons)
  {
    boolean acceptable = true;
    File parentDirectory = getFileForPath(config.getDBDirectory());
    File backendDirectory = new File(parentDirectory, config.getBackendId());
    //Make sure the directory either already exists or is able to create.
    if (!backendDirectory.exists())
    {
      if(!backendDirectory.mkdirs())
      {
        unacceptableReasons.add(ERR_JEB_CREATE_FAIL.get(backendDirectory.getPath()));
        acceptable = false;
}
      else
      {
        backendDirectory.delete();
      }
    }
    else if (!backendDirectory.isDirectory())
    {
      unacceptableReasons.add(ERR_JEB_DIRECTORY_INVALID.get(backendDirectory.getPath()));
      acceptable = false;
    }
    try
    {
      FilePermission newBackendPermission =
          FilePermission.decodeUNIXMode(cfg.getDBDirectoryPermissions());
      //Make sure the mode will allow the server itself access to the database
      if(!newBackendPermission.isOwnerWritable() ||
          !newBackendPermission.isOwnerReadable() ||
          !newBackendPermission.isOwnerExecutable())
      {
        LocalizableMessage message = ERR_CONFIG_BACKEND_INSANE_MODE.get(
            cfg.getDBDirectoryPermissions());
        unacceptableReasons.add(message);
        acceptable = false;
      }
    }
    catch(Exception e)
    {
      unacceptableReasons.add(ERR_CONFIG_BACKEND_MODE_INVALID.get(cfg.dn()));
      acceptable = false;
    }
    return acceptable;
  }
  /** {@inheritDoc} */
  @Override
  public ConfigChangeResult applyConfigurationChange(PersistitBackendCfg cfg)
  {
    final ConfigChangeResult ccr = new ConfigChangeResult();
    try
    {
      // Create the directory if it doesn't exist.
      if(!cfg.getDBDirectory().equals(this.config.getDBDirectory()))
      {
        File parentDirectory = getFileForPath(cfg.getDBDirectory());
        File backendDirectory =
          new File(parentDirectory, cfg.getBackendId());
        if (!backendDirectory.exists())
        {
          if (!backendDirectory.mkdirs())
          {
            ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
            ccr.addMessage(ERR_JEB_CREATE_FAIL.get(backendDirectory.getPath()));
            return ccr;
          }
        }
        //Make sure the directory is valid.
        else if (!backendDirectory.isDirectory())
        {
          ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
          ccr.addMessage(ERR_JEB_DIRECTORY_INVALID.get(backendDirectory.getPath()));
          return ccr;
        }
        ccr.setAdminActionRequired(true);
        ccr.addMessage(NOTE_JEB_CONFIG_DB_DIR_REQUIRES_RESTART.get(this.config.getDBDirectory(),
            cfg.getDBDirectory()));
      }
      if (!cfg.getDBDirectoryPermissions().equalsIgnoreCase(config.getDBDirectoryPermissions())
          || !cfg.getDBDirectory().equals(this.config.getDBDirectory()))
      {
        FilePermission backendPermission;
        try
        {
          backendPermission =
              FilePermission.decodeUNIXMode(cfg.getDBDirectoryPermissions());
        }
        catch(Exception e)
        {
          ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
          ccr.addMessage(ERR_CONFIG_BACKEND_MODE_INVALID.get(config.dn()));
          return ccr;
        }
        // Make sure the mode will allow the server itself access to the database
        if(!backendPermission.isOwnerWritable() ||
            !backendPermission.isOwnerReadable() ||
            !backendPermission.isOwnerExecutable())
        {
          ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
          ccr.addMessage(ERR_CONFIG_BACKEND_INSANE_MODE.get(cfg.getDBDirectoryPermissions()));
          return ccr;
        }
        // Get the backend database backendDirectory permissions and apply
        if(FilePermission.canSetPermissions())
        {
          File parentDirectory = getFileForPath(config.getDBDirectory());
          File backendDirectory = new File(parentDirectory, config.getBackendId());
          try
          {
            if (!FilePermission.setPermissions(backendDirectory, backendPermission))
            {
              logger.warn(WARN_JEB_UNABLE_SET_PERMISSIONS, backendPermission, backendDirectory);
            }
          }
          catch(Exception e)
          {
            // Log an warning that the permissions were not set.
            logger.warn(WARN_JEB_SET_PERMISSIONS_FAILED, backendDirectory, e);
          }
        }
      }
      this.config = cfg;
    }
    catch (Exception e)
    {
      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
      ccr.addMessage(LocalizableMessage.raw(stackTraceToSingleLineString(e)));
    }
    return ccr;
  }
  /** {@inheritDoc} */
  private void setupStorageFiles() throws Exception
  {
    // Create the directory if it doesn't exist.
    if (!backendDirectory.exists())
    {
      if(!backendDirectory.mkdirs())
      {
        LocalizableMessage message =
          ERR_JEB_CREATE_FAIL.get(backendDirectory.getPath());
        throw new ConfigException(message);
      }
    }
    //Make sure the directory is valid.
    else if (!backendDirectory.isDirectory())
    {
      throw new ConfigException(ERR_JEB_DIRECTORY_INVALID.get(backendDirectory.getPath()));
    }
    FilePermission backendPermission;
    try
    {
      backendPermission =
          FilePermission.decodeUNIXMode(config.getDBDirectoryPermissions());
    }
    catch(Exception e)
    {
      throw new ConfigException(ERR_CONFIG_BACKEND_MODE_INVALID.get(config.dn()));
    }
    //Make sure the mode will allow the server itself access to
    //the database
    if(!backendPermission.isOwnerWritable() ||
        !backendPermission.isOwnerReadable() ||
        !backendPermission.isOwnerExecutable())
    {
      LocalizableMessage message = ERR_CONFIG_BACKEND_INSANE_MODE.get(
          config.getDBDirectoryPermissions());
      throw new ConfigException(message);
    }
    // Get the backend database backendDirectory permissions and apply
    if(FilePermission.canSetPermissions())
    {
      try
      {
        if(!FilePermission.setPermissions(backendDirectory, backendPermission))
        {
          logger.warn(WARN_JEB_UNABLE_SET_PERMISSIONS, backendPermission, backendDirectory);
        }
      }
      catch(Exception e)
      {
        // Log an warning that the permissions were not set.
        logger.warn(WARN_JEB_SET_PERMISSIONS_FAILED, backendDirectory, e);
      }
    }
  }
  /** {@inheritDoc} */
  public void removeStorageFiles() throws StorageRuntimeException
  {
    if (!backendDirectory.isDirectory())
    {
      LocalizableMessage msg = ERR_JEB_DIRECTORY_INVALID.get(backendDirectory.getPath());
      throw new StorageRuntimeException(msg.toString());
    }
    try
    {
      File[] files = backendDirectory.listFiles();
      for (File f : files)
      {
        f.delete();
      }
    }
    catch (Exception e)
    {
      logger.traceException(e);
      LocalizableMessage message = ERR_JEB_REMOVE_FAIL.get(e.getMessage());
      throw new StorageRuntimeException(message.toString(), e);
    }
  }
}
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PitBackend.java
@@ -26,19 +26,60 @@
package org.opends.server.backends.persistit;
import static org.opends.server.util.StaticUtils.getFileForPath;
import java.io.File;
import java.util.concurrent.TimeUnit;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.admin.std.server.PersistitBackendCfg;
import org.opends.server.admin.std.server.PluggableBackendCfg;
import org.opends.server.backends.pluggable.BackendImpl;
import org.opends.server.backends.pluggable.spi.Storage;
import org.opends.server.extensions.DiskSpaceMonitor;
import org.opends.server.types.InitializationException;
/**
 * Class defined in the configuration for this backend type.
 */
public class PitBackend extends BackendImpl
{
  /** {@inheritDoc} */
  @Override
  protected Storage newStorageInstance()
  {
    return new PersistItStorage();
  }
  /** {@inheritDoc} */
  @Override
  public DiskSpaceMonitor newDiskMonitor(PluggableBackendCfg cfg) throws ConfigException, InitializationException
  {
    PersistitBackendCfg config = (PersistitBackendCfg) cfg;
    File parentDirectory = getFileForPath(config.getDBDirectory());
    File backendDirectory =
        new File(parentDirectory, config.getBackendId());
    DiskSpaceMonitor dm = new DiskSpaceMonitor(getBackendID() + " backend",
        backendDirectory, config.getDiskLowThreshold(), config.getDiskFullThreshold(),
        5, TimeUnit.SECONDS, this);
    dm.initializeMonitorProvider(null);
    return dm;
  }
  /** {@inheritDoc} */
  @Override
  public void updateDiskMonitor(DiskSpaceMonitor dm, PluggableBackendCfg newConfig)
  {
    PersistitBackendCfg newCfg = (PersistitBackendCfg) newConfig;
    dm.setFullThreshold(newCfg.getDiskFullThreshold());
    dm.setLowThreshold(newCfg.getDiskLowThreshold());
  }
  /** {@inheritDoc} */
  protected File getBackupDirectory(PluggableBackendCfg cfg)
  {
    PersistitBackendCfg config = (PersistitBackendCfg) cfg;
    File parentDir = getFileForPath(config.getDBDirectory());
    return new File(parentDir, config.getBackendId());
  }
}
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/BackendImpl.java
@@ -35,7 +35,6 @@
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.forgerock.i18n.LocalizableMessage;
@@ -47,7 +46,7 @@
import org.forgerock.util.Reject;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.meta.BackendIndexCfgDefn;
import org.opends.server.admin.std.server.PersistitBackendCfg;
import org.opends.server.admin.std.server.PluggableBackendCfg;
import org.opends.server.api.AlertGenerator;
import org.opends.server.api.Backend;
import org.opends.server.api.DiskSpaceMonitorHandler;
@@ -65,14 +64,14 @@
 * This is an implementation of a Directory Server Backend which stores entries
 * locally in a Berkeley DB JE database.
 */
public abstract class BackendImpl extends Backend<PersistitBackendCfg> implements
    ConfigurationChangeListener<PersistitBackendCfg>, AlertGenerator,
public abstract class BackendImpl extends Backend<PluggableBackendCfg> implements
    ConfigurationChangeListener<PluggableBackendCfg>, AlertGenerator,
    DiskSpaceMonitorHandler
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /** The configuration of this backend. */
  private PersistitBackendCfg cfg;
  private PluggableBackendCfg cfg;
  /** The root JE container to use for this backend. */
  private RootContainer rootContainer;
@@ -83,6 +82,7 @@
  private DN[] baseDNs;
  private MonitorProvider<?> rootContainerMonitor;
  /** Disk space monitoring if the storage supports it. */
  private DiskSpaceMonitor diskMonitor;
  /** The controls supported by this backend. */
@@ -128,7 +128,7 @@
  /** {@inheritDoc} */
  @Override
  public void configureBackend(PersistitBackendCfg cfg) throws ConfigException
  public void configureBackend(PluggableBackendCfg cfg) throws ConfigException
  {
    Reject.ifNull(cfg);
@@ -179,27 +179,44 @@
    DirectoryServer.registerMonitorProvider(rootContainerMonitor);
    // Register as disk space monitor handler
    File parentDirectory = getFileForPath(cfg.getDBDirectory());
    File backendDirectory =
        new File(parentDirectory, cfg.getBackendId());
    diskMonitor = new DiskSpaceMonitor(getBackendID() + " backend",
        backendDirectory, cfg.getDiskLowThreshold(), cfg.getDiskFullThreshold(),
        5, TimeUnit.SECONDS, this);
    diskMonitor.initializeMonitorProvider(null);
    diskMonitor = newDiskMonitor(cfg);
    if (diskMonitor != null)
    {
    DirectoryServer.registerMonitorProvider(diskMonitor);
    }
    //Register as an AlertGenerator.
    DirectoryServer.registerAlertGenerator(this);
    // Register this backend as a change listener.
    cfg.addPersistitChangeListener(this);
    cfg.addPluggableChangeListener(this);
  }
  /**
   * Let the storage create a new disk monitor if supported.
   *
   * @param cfg this storage current configuration
   * @return a new disk monitor if supported or null
   *
   * @throws ConfigException if configuration is incorrect
   * @throws InitializationException when disk monitor cannot be initialized
   */
  protected abstract DiskSpaceMonitor newDiskMonitor(PluggableBackendCfg cfg) throws
    ConfigException, InitializationException;
  /**
   * Updates the disk monitor when configuration changes.
   *
   * @param dm the disk monitor to update
   * @param newCfg the new configuration
   */
  protected abstract void updateDiskMonitor(DiskSpaceMonitor dm, PluggableBackendCfg newCfg);
  /** {@inheritDoc} */
  @Override
  public void finalizeBackend()
  {
    super.finalizeBackend();
    cfg.removePersistitChangeListener(this);
    cfg.removePluggableChangeListener(this);
    // Deregister our base DNs.
    for (DN dn : rootContainer.getBaseDNs())
@@ -722,8 +739,7 @@
  public void createBackup(BackupConfig backupConfig) throws DirectoryException
  {
    BackupManager backupManager = new BackupManager(getBackendID());
    File parentDir = getFileForPath(cfg.getDBDirectory());
    File backendDir = new File(parentDir, cfg.getBackendId());
    File backendDir = getBackupDirectory(cfg);
    Storage storage = newStorageInstance();
    backupConfig.setFilesToBackupFilter(storage.getFilesToBackupFilter());
    backupManager.createBackup(backendDir, backupConfig);
@@ -735,7 +751,6 @@
   */
  protected abstract Storage newStorageInstance();
  /** {@inheritDoc} */
  @Override
  public void removeBackup(BackupDirectory backupDirectory, String backupID)
@@ -745,25 +760,27 @@
    backupManager.removeBackup(backupDirectory, backupID);
  }
  /** {@inheritDoc} */
  @Override
  public void restoreBackup(RestoreConfig restoreConfig)
      throws DirectoryException
  {
    BackupManager backupManager = new BackupManager(getBackendID());
    File parentDir = getFileForPath(cfg.getDBDirectory());
    File backendDir = new File(parentDir, cfg.getBackendId());
    File backendDir = getBackupDirectory(cfg);
    backupManager.restoreBackup(backendDir, restoreConfig);
  }
  /**
   * Returns the backup directory.
   *
   * @param cfg the configuration for this backend
   * @return the backup directory
   */
  protected abstract File getBackupDirectory(PluggableBackendCfg cfg);
  /** {@inheritDoc} */
  @Override
  public boolean isConfigurationAcceptable(PersistitBackendCfg config,
                                           List<LocalizableMessage> unacceptableReasons)
  public boolean isConfigurationAcceptable(PluggableBackendCfg config, List<LocalizableMessage> unacceptableReasons)
  {
    return isConfigurationChangeAcceptable(config, unacceptableReasons);
  }
@@ -772,9 +789,7 @@
  /** {@inheritDoc} */
  @Override
  public boolean isConfigurationChangeAcceptable(
      PersistitBackendCfg cfg,
      List<LocalizableMessage> unacceptableReasons)
  public boolean isConfigurationChangeAcceptable(PluggableBackendCfg cfg, List<LocalizableMessage> unacceptableReasons)
  {
    return true;
  }
@@ -783,7 +798,7 @@
  /** {@inheritDoc} */
  @Override
  public ConfigChangeResult applyConfigurationChange(final PersistitBackendCfg newCfg)
  public ConfigChangeResult applyConfigurationChange(final PluggableBackendCfg newCfg)
  {
    final ConfigChangeResult ccr = new ConfigChangeResult();
    try
@@ -807,11 +822,9 @@
            baseDNs = newBaseDNsArray;
            if(cfg.getDiskFullThreshold() != newCfg.getDiskFullThreshold() ||
                cfg.getDiskLowThreshold() != newCfg.getDiskLowThreshold())
            if (diskMonitor != null)
            {
              diskMonitor.setFullThreshold(newCfg.getDiskFullThreshold());
              diskMonitor.setLowThreshold(newCfg.getDiskLowThreshold());
              updateDiskMonitor(diskMonitor, newCfg);
            }
            // Put the new configuration in place.
@@ -906,7 +919,7 @@
   *          The StorageRuntimeException to be converted.
   * @return DirectoryException created from exception.
   */
  DirectoryException createDirectoryException(StorageRuntimeException e)
  private DirectoryException createDirectoryException(StorageRuntimeException e)
  {
    if (true) // FIXME JNR
    {
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java
@@ -60,7 +60,7 @@
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.std.server.BackendIndexCfg;
import org.opends.server.admin.std.server.BackendVLVIndexCfg;
import org.opends.server.admin.std.server.PersistitBackendCfg;
import org.opends.server.admin.std.server.PluggableBackendCfg;
import org.opends.server.api.Backend;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.EntryCache;
@@ -109,14 +109,14 @@
 * the guts of the backend API methods for LDAP operations.
 */
public class EntryContainer
    implements SuffixContainer, ConfigurationChangeListener<PersistitBackendCfg>
    implements SuffixContainer, ConfigurationChangeListener<PluggableBackendCfg>
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /** The name of the entry database. */
  public static final String ID2ENTRY_DATABASE_NAME = ID2ENTRY_INDEX_NAME;
  private static final String ID2ENTRY_DATABASE_NAME = ID2ENTRY_INDEX_NAME;
  /** The name of the DN database. */
  public static final String DN2ID_DATABASE_NAME = DN2ID_INDEX_NAME;
  private static final String DN2ID_DATABASE_NAME = DN2ID_INDEX_NAME;
  /** The name of the children index database. */
  private static final String ID2CHILDREN_DATABASE_NAME = ID2CHILDREN_INDEX_NAME;
  /** The name of the subtree index database. */
@@ -141,7 +141,7 @@
  private final DN baseDN;
  /** The backend configuration. */
  private PersistitBackendCfg config;
  private PluggableBackendCfg config;
  /** The JE database environment. */
  private final Storage storage;
@@ -441,8 +441,8 @@
   * @param rootContainer The root container this entry container is in.
   * @throws ConfigException if a configuration related error occurs.
   */
  public EntryContainer(DN baseDN, String databasePrefix, Backend<?> backend,
      PersistitBackendCfg config, Storage env, RootContainer rootContainer)
  EntryContainer(DN baseDN, String databasePrefix, Backend<?> backend,
      PluggableBackendCfg config, Storage env, RootContainer rootContainer)
          throws ConfigException
  {
    this.backend = backend;
@@ -452,7 +452,7 @@
    this.rootContainer = rootContainer;
    this.databasePrefix = databasePrefix;
    config.addPersistitChangeListener(this);
    config.addPluggableChangeListener(this);
    attributeJEIndexCfgManager = new AttributeJEIndexCfgManager();
    config.addBackendIndexAddListener(attributeJEIndexCfgManager);
@@ -475,7 +475,7 @@
   * @throws StorageRuntimeException If an error occurs in the JE database.
   * @throws ConfigException if a configuration related error occurs.
   */
  public void open(WriteableStorage txn) throws StorageRuntimeException, ConfigException
  void open(WriteableStorage txn) throws StorageRuntimeException, ConfigException
  {
    try
    {
@@ -577,7 +577,7 @@
    }
    // Deregister any listeners.
    config.removePersistitChangeListener(this);
    config.removePluggableChangeListener(this);
    config.removeBackendIndexAddListener(attributeJEIndexCfgManager);
    config.removeBackendIndexDeleteListener(attributeJEIndexCfgManager);
    config.removeBackendVLVIndexAddListener(vlvJEIndexCfgManager);
@@ -668,7 +668,7 @@
   * @param attrType The attribute type for which an attribute index is needed.
   * @return The attribute index or null if there is none for that type.
   */
  public AttributeIndex getAttributeIndex(AttributeType attrType)
  AttributeIndex getAttributeIndex(AttributeType attrType)
  {
    return attrIndexMap.get(attrType);
  }
@@ -688,7 +688,7 @@
   * @param vlvIndexName The vlv index name for which an vlv index is needed.
   * @return The VLV index or null if there is none with that name.
   */
  public VLVIndex getVLVIndex(String vlvIndexName)
  VLVIndex getVLVIndex(String vlvIndexName)
  {
    return vlvIndexMap.get(vlvIndexName);
  }
@@ -721,7 +721,7 @@
   * @return The highest entry ID.
   * @throws StorageRuntimeException If an error occurs in the JE database.
   */
  public EntryID getHighestEntryID(ReadableStorage txn) throws StorageRuntimeException
  EntryID getHighestEntryID(ReadableStorage txn) throws StorageRuntimeException
  {
    Cursor cursor = txn.openCursor(id2entry.getName());
    try
@@ -750,7 +750,7 @@
   *         the entry does not exist.
   * @throws StorageRuntimeException If an error occurs in the JE database.
   */
  public long getNumSubordinates(final DN entryDN, final boolean subtree)
  long getNumSubordinates(final DN entryDN, final boolean subtree)
  throws StorageRuntimeException
  {
    try
@@ -801,7 +801,7 @@
   * @throws StorageRuntimeException If an error occurs in the JE database.
   * @throws CanceledOperationException if this operation should be cancelled.
   */
  public void search(final SearchOperation searchOperation)
  void search(final SearchOperation searchOperation)
  throws DirectoryException, StorageRuntimeException, CanceledOperationException
  {
    try
@@ -819,7 +819,7 @@
              searchOperation.getRequestControl(ServerSideSortRequestControl.DECODER);
          if (sortRequest != null && !sortRequest.containsSortKeys() && sortRequest.isCritical())
          {
            /**
            /*
             * If the control's criticality field is true then the server SHOULD
             * do the following: return unavailableCriticalExtension as a return
             * code in the searchResultDone message; include the
@@ -1316,7 +1316,7 @@
   * @throws DirectoryException
   *           If an error occurs retrieving the entry
   */
  public Entry getEntry(ReadableStorage txn, EntryID entryID) throws DirectoryException
  Entry getEntry(ReadableStorage txn, EntryID entryID) throws DirectoryException
  {
    // Try the entry cache first.
    final EntryCache entryCache = getEntryCache();
@@ -1421,12 +1421,8 @@
        }
        // Process the candidate entry.
        if (entry != null)
        {
          // Filter the entry if it is in scope.
          if (isInScope(candidatesAreInScope, searchScope, aBaseDN, entry)
              && (manageDsaIT || entry.getReferralURLs() == null)
              && searchOperation.getFilter().matchesEntry(entry))
        if (entry != null && isInScope(candidatesAreInScope, searchScope, aBaseDN, entry)
              && (manageDsaIT || entry.getReferralURLs() == null) && searchOperation.getFilter().matchesEntry(entry))
          {
            if (pageRequest != null
                && searchOperation.getEntriesSent() == pageRequest.getSize())
@@ -1448,7 +1444,6 @@
            }
          }
        }
      }
      searchOperation.checkIfCanceled(false);
    }
@@ -1521,7 +1516,7 @@
   * @throws StorageRuntimeException If an error occurs in the JE database.
   * @throws CanceledOperationException if this operation should be cancelled.
   */
  public void addEntry(final Entry entry, final AddOperation addOperation)
  void addEntry(final Entry entry, final AddOperation addOperation)
  throws StorageRuntimeException, DirectoryException, CanceledOperationException
  {
    try
@@ -1676,7 +1671,7 @@
   * @throws StorageRuntimeException If an error occurs in the JE database.
   * @throws CanceledOperationException if this operation should be cancelled.
   */
  public void deleteEntry(final DN entryDN, final DeleteOperation deleteOperation)
  void deleteEntry(final DN entryDN, final DeleteOperation deleteOperation)
  throws DirectoryException, StorageRuntimeException, CanceledOperationException
  {
    try
@@ -1934,7 +1929,7 @@
   * @throws  DirectoryException  If a problem occurs while trying to make the
   *                              determination.
   */
  public boolean entryExists(final DN entryDN) throws DirectoryException
  private boolean entryExists(final DN entryDN) throws DirectoryException
  {
    // Try the entry cache first.
    EntryCache<?> entryCache = DirectoryServer.getEntryCache();
@@ -1975,7 +1970,7 @@
   *                            the entry.
   * @throws StorageRuntimeException An error occurred during a database operation.
   */
  public Entry getEntry(final DN entryDN) throws StorageRuntimeException, DirectoryException
  Entry getEntry(final DN entryDN) throws StorageRuntimeException, DirectoryException
  {
    try
    {
@@ -2060,7 +2055,7 @@
   * @throws DirectoryException If a Directory Server error occurs.
   * @throws CanceledOperationException if this operation should be cancelled.
   */
  public void replaceEntry(final Entry oldEntry, final Entry newEntry, final ModifyOperation modifyOperation)
  void replaceEntry(final Entry oldEntry, final Entry newEntry, final ModifyOperation modifyOperation)
      throws StorageRuntimeException, DirectoryException, CanceledOperationException
  {
    try
@@ -2187,7 +2182,7 @@
   *          modify DN operation.
   * @throws StorageRuntimeException If an error occurs in the JE database.
   */
  public void renameEntry(final DN currentDN, final Entry entry, final ModifyDNOperation modifyDNOperation)
  void renameEntry(final DN currentDN, final Entry entry, final ModifyDNOperation modifyDNOperation)
      throws StorageRuntimeException, DirectoryException, CanceledOperationException
  {
    try
@@ -2640,7 +2635,7 @@
   * @param newSuffixDN The new DN of the renamed or moved entry.
   * @return The new DN of the subordinate entry.
   */
  public static DN modDN(DN oldDN, int oldSuffixLen, DN newSuffixDN)
  static DN modDN(DN oldDN, int oldSuffixLen, DN newSuffixDN)
  {
    int oldDNNumComponents    = oldDN.size();
    int oldDNKeepComponents   = oldDNNumComponents - oldSuffixLen;
@@ -2745,7 +2740,7 @@
   * @return The number of entries stored in this entry container.
   * @throws StorageRuntimeException If an error occurs in the JE database.
   */
  public long getEntryCount(ReadableStorage txn) throws StorageRuntimeException
  long getEntryCount(ReadableStorage txn) throws StorageRuntimeException
  {
    final EntryID entryID = dn2id.get(txn, baseDN, false);
    if (entryID != null)
@@ -2793,7 +2788,7 @@
   * Get a list of the databases opened by the entryContainer.
   * @param dbList A list of database containers.
   */
  public void listDatabases(List<DatabaseContainer> dbList)
  void listDatabases(List<DatabaseContainer> dbList)
  {
    dbList.add(dn2id);
    dbList.add(id2entry);
@@ -2847,7 +2842,7 @@
   * @throws StorageRuntimeException If an error occurs while removing the entry
   *                           container.
   */
  public void delete(WriteableStorage txn) throws StorageRuntimeException
  void delete(WriteableStorage txn) throws StorageRuntimeException
  {
    List<DatabaseContainer> databases = new ArrayList<DatabaseContainer>();
    listDatabases(databases);
@@ -2866,7 +2861,7 @@
   * @throws StorageRuntimeException If an error occurs while attempting to delete the
   * database.
   */
  public void deleteDatabase(WriteableStorage txn, DatabaseContainer database) throws StorageRuntimeException
  void deleteDatabase(WriteableStorage txn, DatabaseContainer database) throws StorageRuntimeException
  {
    if(database == state)
    {
@@ -3011,7 +3006,7 @@
   * @param dn A DN which is in the scope of the base DN.
   * @return The parent DN, or null if the given DN is the base DN.
   */
  public DN getParentWithinBase(DN dn)
  DN getParentWithinBase(DN dn)
  {
    if (dn.equals(baseDN))
    {
@@ -3023,7 +3018,7 @@
  /** {@inheritDoc} */
  @Override
  public boolean isConfigurationChangeAcceptable(
      PersistitBackendCfg cfg, List<LocalizableMessage> unacceptableReasons)
      PluggableBackendCfg cfg, List<LocalizableMessage> unacceptableReasons)
  {
    // This is always true because only all config attributes used
    // by the entry container should be validated by the admin framework.
@@ -3032,7 +3027,7 @@
  /** {@inheritDoc} */
  @Override
  public ConfigChangeResult applyConfigurationChange(final PersistitBackendCfg cfg)
  public ConfigChangeResult applyConfigurationChange(final PluggableBackendCfg cfg)
  {
    final ConfigChangeResult ccr = new ConfigChangeResult();
@@ -3345,6 +3340,6 @@
  /** {@inheritDoc} */
  @Override
  public String toString() {
    return databasePrefix.toString();
    return databasePrefix;
  }
}
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/Importer.java
@@ -4054,7 +4054,7 @@
      }
      finally
      {
        rootContainer.removeFiles();
        storage.removeStorageFiles();
      }
    }
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/RootContainer.java
@@ -26,15 +26,24 @@
 */
package org.opends.server.backends.pluggable;
import static org.opends.messages.BackendMessages.*;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.messages.JebMessages.*;
import static org.opends.messages.UtilityMessages.*;
import static org.opends.server.core.DirectoryServer.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.messages.BackendMessages.ERR_LDIF_BACKEND_CANNOT_CREATE_LDIF_READER;
import static org.opends.messages.BackendMessages.ERR_LDIF_BACKEND_ERROR_READING_LDIF;
import static org.opends.messages.JebMessages.ERR_JEB_CACHE_PRELOAD;
import static org.opends.messages.JebMessages.ERR_JEB_REMOVE_FAIL;
import static org.opends.messages.JebMessages.ERR_JEB_ENTRY_CONTAINER_ALREADY_REGISTERED;
import static org.opends.messages.JebMessages.ERR_JEB_IMPORT_PARENT_NOT_FOUND;
import static org.opends.messages.JebMessages.NOTE_JEB_IMPORT_FINAL_STATUS;
import static org.opends.messages.JebMessages.NOTE_JEB_IMPORT_PROGRESS_REPORT;
import static org.opends.messages.JebMessages.WARN_JEB_IMPORT_ENTRY_EXISTS;
import static org.opends.messages.UtilityMessages.ERR_LDIF_SKIP;
import static org.opends.server.core.DirectoryServer.getServerErrorResultCode;
import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
import java.io.File;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -45,9 +54,8 @@
import org.forgerock.opendj.config.server.ConfigChangeResult;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.PersistitBackendCfg;
import org.opends.server.admin.std.server.PluggableBackendCfg;
import org.opends.server.api.CompressedSchema;
import org.opends.server.backends.persistit.PersistItStorage;
import org.opends.server.backends.pluggable.spi.ReadOperation;
import org.opends.server.backends.pluggable.spi.ReadableStorage;
import org.opends.server.backends.pluggable.spi.Storage;
@@ -58,7 +66,6 @@
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.FilePermission;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.LDIFImportResult;
@@ -67,13 +74,13 @@
import org.opends.server.util.LDIFReader;
import org.opends.server.util.RuntimeInformation;
/**
 * 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
     implements ConfigurationChangeListener<PersistitBackendCfg>
public class RootContainer implements ConfigurationChangeListener<PluggableBackendCfg>
{
  /** Logs the progress of the import. */
  private static final class ImportProgress implements Runnable
@@ -112,16 +119,14 @@
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  private static final int IMPORT_PROGRESS_INTERVAL = 10000;
  private static final int KB = 1024;
  /** The JE database environment. */
  private PersistItStorage storage; // FIXME JNR do not hardcode here
  private Storage storage;
  private final File backendDirectory;
  /** The backend to which this entry root container belongs. */
  private final BackendImpl backend;
  /** The backend configuration. */
  private PersistitBackendCfg config;
  private PluggableBackendCfg config;
  /** The database environment monitor for this JE environment. */
  private DatabaseEnvironmentMonitor monitor;
@@ -134,26 +139,25 @@
  /** The compressed schema manager for this backend. */
  private JECompressedSchema compressedSchema;
  /**
   * 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.
   * @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(BackendImpl backend, PersistitBackendCfg config)
  RootContainer(BackendImpl backend, PluggableBackendCfg config)
  {
    this.backend = backend;
    this.config = config;
    this.backendDirectory = new File(getFileForPath(config.getDBDirectory()),
        config.getBackendId());
    getMonitorProvider().enableFilterUseStats(config.isIndexFilterAnalyzerEnabled());
    getMonitorProvider().setMaxEntries(config.getIndexFilterAnalyzerMaxFilters());
    config.addPersistitChangeListener(this);
    config.addPluggableChangeListener(this);
  }
  /**
@@ -167,20 +171,33 @@
  }
  /**
   * Imports information from an LDIF file into this backend.
   * This method should only be called if {@code supportsLDIFImport} returns {@code true}.
   * Note that the server will not explicitly initialize this backend before calling this method.
   * Imports information from an LDIF file into this backend. This method should
   * only be called if {@code supportsLDIFImport} returns {@code true}. <p>Note
   * that the server will not explicitly initialize this backend before calling
   * this method.
   *
   * @param importConfig The configuration to use when performing the import.
   * @param importConfig
   *          The configuration to use when performing the import.
   * @return information about the result of the import processing.
   * @throws DirectoryException If a problem occurs while performing the LDIF import.
   * @throws DirectoryException
   *           If a problem occurs while performing the LDIF import.
   */
  LDIFImportResult importLDIF(LDIFImportConfig importConfig) throws DirectoryException
  {
    RuntimeInformation.logInfo();
    if (Importer.mustClearBackend(importConfig, config))
    {
      removeFiles();
      try
      {
        Storage storage = backend.newStorageInstance();
        storage.initialize(config);
        storage.removeStorageFiles();
      }
      catch (Exception e)
      {
        LocalizableMessage m = ERR_JEB_REMOVE_FAIL.get(e.getMessage());
        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), m, e);
      }
    }
    try
    {
@@ -275,8 +292,8 @@
        {
          rate = 1000f * reader.getEntriesRead() / importTime;
        }
        logger.info(NOTE_JEB_IMPORT_FINAL_STATUS, reader.getEntriesRead(), importCount,
            reader.getEntriesIgnored(), reader.getEntriesRejected(), 0, importTime / 1000, rate);
        logger.info(NOTE_JEB_IMPORT_FINAL_STATUS, reader.getEntriesRead(), importCount, reader.getEntriesIgnored(),
            reader.getEntriesRejected(), 0, importTime / 1000, rate);
        return new LDIFImportResult(reader.getEntriesRead(), reader.getEntriesRejected(), reader.getEntriesIgnored());
      }
      finally
@@ -311,35 +328,6 @@
  }
  /**
   * Removes all the files from the rootContainer's directory.
   *
   * @throws StorageRuntimeException If a problem occurred
   */
  void removeFiles() throws StorageRuntimeException
  {
    if (!backendDirectory.isDirectory())
    {
      LocalizableMessage msg = ERR_JEB_DIRECTORY_INVALID.get(backendDirectory.getPath());
      throw new StorageRuntimeException(msg.toString());
    }
    try
    {
      File[] jdbFiles = backendDirectory.listFiles();
      for (File f : jdbFiles)
      {
        f.delete();
      }
    }
    catch (Exception e)
    {
      logger.traceException(e);
      LocalizableMessage message = ERR_JEB_REMOVE_FAIL.get(e.getMessage());
      throw new StorageRuntimeException(message.toString(), e);
    }
  }
  /**
   * Opens the root container.
   *
   * @throws StorageRuntimeException
@@ -349,64 +337,9 @@
   */
  void open() throws StorageRuntimeException, ConfigException
  {
    // Create the directory if it doesn't exist.
    if (!backendDirectory.exists())
    {
      if(!backendDirectory.mkdirs())
      {
        LocalizableMessage message =
          ERR_JEB_CREATE_FAIL.get(backendDirectory.getPath());
        throw new ConfigException(message);
      }
    }
    //Make sure the directory is valid.
    else if (!backendDirectory.isDirectory())
    {
      throw new ConfigException(ERR_JEB_DIRECTORY_INVALID.get(backendDirectory.getPath()));
    }
    FilePermission backendPermission;
    try
    {
      backendPermission =
          FilePermission.decodeUNIXMode(config.getDBDirectoryPermissions());
    }
    catch(Exception e)
    {
      throw new ConfigException(ERR_CONFIG_BACKEND_MODE_INVALID.get(config.dn()));
    }
    //Make sure the mode will allow the server itself access to
    //the database
    if(!backendPermission.isOwnerWritable() ||
        !backendPermission.isOwnerReadable() ||
        !backendPermission.isOwnerExecutable())
    {
      LocalizableMessage message = ERR_CONFIG_BACKEND_INSANE_MODE.get(
          config.getDBDirectoryPermissions());
      throw new ConfigException(message);
    }
    // Get the backend database backendDirectory permissions and apply
    if(FilePermission.canSetPermissions())
    {
      try
      {
        if(!FilePermission.setPermissions(backendDirectory, backendPermission))
        {
          logger.warn(WARN_JEB_UNABLE_SET_PERMISSIONS, backendPermission, backendDirectory);
        }
      }
      catch(Exception e)
      {
        // Log an warning that the permissions were not set.
        logger.warn(WARN_JEB_SET_PERMISSIONS_FAILED, backendDirectory, e);
      }
    }
    try
    {
      storage = (PersistItStorage) backend.newStorageInstance();
      storage = backend.newStorageInstance();
      storage.initialize(config);
      storage.open();
      storage.write(new WriteOperation()
@@ -433,21 +366,24 @@
   * containers opened in a non transactional root container will also be non
   * transactional.
   *
   * @param baseDN The base DN of the entry container to open.
   * @param name The name of the entry container or <CODE>NULL</CODE> to open
   * the default entry container for the given base DN.
   * @param txn The database transaction
   * @param baseDN
   *          The base DN of the entry container to open.
   * @param name
   *          The name of the entry container or <CODE>NULL</CODE> to open the
   *          default entry container for the given base DN.
   * @param txn
   *          The database transaction
   * @return The opened entry container.
   * @throws StorageRuntimeException If an error occurs while opening the entry
   *                           container.
   * @throws ConfigException If an configuration error occurs while opening
   *                         the entry container.
   * @throws StorageRuntimeException
   *           If an error occurs while opening the entry container.
   * @throws ConfigException
   *           If an configuration error occurs while opening the entry container.
   */
  public EntryContainer openEntryContainer(DN baseDN, String name, WriteableStorage txn)
  EntryContainer openEntryContainer(DN baseDN, String name, WriteableStorage txn)
      throws StorageRuntimeException, ConfigException
  {
    String databasePrefix;
    if(name == null || name.equals(""))
    if (name == null || "".equals(name))
    {
      databasePrefix = baseDN.toIrreversibleReadableString();
    }
@@ -456,8 +392,8 @@
      databasePrefix = name;
    }
    EntryContainer ec = new EntryContainer(baseDN, storage.toSuffixName(databasePrefix),
                                           backend, config, storage, this);
    EntryContainer ec =
        new EntryContainer(baseDN, storage.toSafeSuffixName(databasePrefix), backend, config, storage, this);
    ec.open(txn);
    return ec;
  }
@@ -465,13 +401,14 @@
  /**
   * Registers the entry container for a base DN.
   *
   * @param baseDN The base DN of the entry container to close.
   * @param entryContainer The entry container to register for the baseDN.
   * @throws InitializationException If an error occurs while opening the
   *                                 entry container.
   * @param baseDN
   *          The base DN of the entry container to close.
   * @param entryContainer
   *          The entry container to register for the baseDN.
   * @throws InitializationException
   *           If an error occurs while opening the entry container.
   */
  public void registerEntryContainer(DN baseDN, EntryContainer entryContainer)
      throws InitializationException
  void registerEntryContainer(DN baseDN, EntryContainer entryContainer) throws InitializationException
  {
    EntryContainer ec1 = this.entryContainers.get(baseDN);
@@ -479,8 +416,8 @@
    // another to be opened.
    if (ec1 != null)
    {
      throw new InitializationException(ERR_JEB_ENTRY_CONTAINER_ALREADY_REGISTERED.get(
          ec1.getDatabasePrefix(), baseDN));
      throw new InitializationException(ERR_JEB_ENTRY_CONTAINER_ALREADY_REGISTERED.get(ec1.getDatabasePrefix(),
          baseDN));
    }
    this.entryContainers.put(baseDN, entryContainer);
@@ -489,16 +426,19 @@
  /**
   * Opens the entry containers for multiple base DNs.
   *
   * @param baseDNs The base DNs of the entry containers to open.
   * @throws StorageRuntimeException       If a database error occurs while opening
   *                                 the entry container.
   * @throws InitializationException If an initialization error occurs while
   *                                 opening the entry container.
   * @throws ConfigException         If a configuration error occurs while
   *                                 opening the entry container.
   * @param baseDNs
   *          The base DNs of the entry containers to open.
   * @throws StorageRuntimeException
   *           If a database error occurs while opening the entry container.
   * @throws InitializationException
   *           If an initialization error occurs while opening the entry
   *           container.
   * @throws ConfigException
   *           If a configuration error occurs while opening the entry
   *           container.
   */
  private void openAndRegisterEntryContainers(WriteableStorage txn, Set<DN> baseDNs)
      throws StorageRuntimeException, InitializationException, ConfigException
  private void openAndRegisterEntryContainers(WriteableStorage txn, Set<DN> baseDNs) throws StorageRuntimeException,
      InitializationException, ConfigException
  {
    EntryID highestID = null;
    for(DN baseDN : baseDNs)
@@ -518,11 +458,12 @@
  /**
   * Unregisters the entry container for a base DN.
   *
   * @param baseDN The base DN of the entry container to close.
   * @param baseDN
   *          The base DN of the entry container to close.
   * @return The entry container that was unregistered or NULL if a entry
   * container for the base DN was not registered.
   */
  public EntryContainer unregisterEntryContainer(DN baseDN)
  EntryContainer unregisterEntryContainer(DN baseDN)
  {
    return entryContainers.remove(baseDN);
  }
@@ -541,7 +482,7 @@
   * Get the DatabaseEnvironmentMonitor object for JE environment used by this
   * root container.
   *
   * @return The DatabaseEnvironmentMonito object.
   * @return The DatabaseEnvironmentMonitor object.
   */
  public DatabaseEnvironmentMonitor getMonitorProvider()
  {
@@ -558,9 +499,10 @@
   * Preload the database cache. There is no preload if the configured preload
   * time limit is zero.
   *
   * @param timeLimit The time limit for the preload process.
   * @param timeLimit
   *          The time limit for the preload process.
   */
  public void preload(long timeLimit)
  void preload(long timeLimit)
  {
    if (timeLimit > 0)
    {
@@ -601,10 +543,10 @@
  /**
   * Closes this root container.
   *
   * @throws StorageRuntimeException If an error occurs while attempting to close
   * the root container.
   * @throws StorageRuntimeException
   *           If an error occurs while attempting to close the root container.
   */
  public void close() throws StorageRuntimeException
  void close() throws StorageRuntimeException
  {
    for(DN baseDN : entryContainers.keySet())
    {
@@ -621,7 +563,7 @@
    }
    compressedSchema.close();
    config.removePersistitChangeListener(this);
    config.removePluggableChangeListener(this);
    if (storage != null)
    {
@@ -653,10 +595,11 @@
  /**
   * Return the entry container for a specific base DN.
   *
   * @param baseDN The base DN of the entry container to retrieve.
   * @param baseDN
   *          The base DN of the entry container to retrieve.
   * @return The entry container for the base DN.
   */
  public EntryContainer getEntryContainer(DN baseDN)
  EntryContainer getEntryContainer(DN baseDN)
  {
    EntryContainer ec = null;
    DN nodeDN = baseDN;
@@ -673,14 +616,12 @@
    return ec;
  }
  /**
   * Get the backend configuration used by this root container.
   *
   * @return The backend configuration used by this root container.
   */
  public PersistitBackendCfg getConfiguration()
  public PluggableBackendCfg getConfiguration()
  {
    return config;
  }
@@ -689,8 +630,8 @@
   * Get the total number of entries in this root container.
   *
   * @return The number of entries in this root container
   * @throws StorageRuntimeException If an error occurs while retrieving the entry
   *                           count.
   * @throws StorageRuntimeException
   *           If an error occurs while retrieving the entry count.
   */
  public long getEntryCount() throws StorageRuntimeException
  {
@@ -753,176 +694,28 @@
    nextid.set(1);
  }
  /** {@inheritDoc} */
  @Override
  public boolean isConfigurationChangeAcceptable(
      PersistitBackendCfg cfg,
  public boolean isConfigurationChangeAcceptable(PluggableBackendCfg configuration,
      List<LocalizableMessage> unacceptableReasons)
  {
    boolean acceptable = true;
    File parentDirectory = getFileForPath(config.getDBDirectory());
    File backendDirectory = new File(parentDirectory, config.getBackendId());
    //Make sure the directory either already exists or is able to create.
    if (!backendDirectory.exists())
    {
      if(!backendDirectory.mkdirs())
      {
        unacceptableReasons.add(ERR_JEB_CREATE_FAIL.get(backendDirectory.getPath()));
        acceptable = false;
    // Storage has also registered a change listener, delegate to it.
    return true;
      }
      else
      {
        backendDirectory.delete();
      }
    }
    //Make sure the directory is valid.
    else if (!backendDirectory.isDirectory())
    {
      unacceptableReasons.add(ERR_JEB_DIRECTORY_INVALID.get(backendDirectory.getPath()));
      acceptable = false;
    }
    try
    {
      FilePermission newBackendPermission =
          FilePermission.decodeUNIXMode(cfg.getDBDirectoryPermissions());
      //Make sure the mode will allow the server itself access to
      //the database
      if(!newBackendPermission.isOwnerWritable() ||
          !newBackendPermission.isOwnerReadable() ||
          !newBackendPermission.isOwnerExecutable())
      {
        LocalizableMessage message = ERR_CONFIG_BACKEND_INSANE_MODE.get(
            cfg.getDBDirectoryPermissions());
        unacceptableReasons.add(message);
        acceptable = false;
      }
    }
    catch(Exception e)
    {
      unacceptableReasons.add(ERR_CONFIG_BACKEND_MODE_INVALID.get(cfg.dn()));
      acceptable = false;
    }
    try
    {
      // FIXME JNR validate database specific configuration
    }
    catch (Exception e)
    {
      unacceptableReasons.add(LocalizableMessage.raw(e.getLocalizedMessage()));
      acceptable = false;
    }
    return acceptable;
  }
  /** {@inheritDoc} */
  @Override
  public ConfigChangeResult applyConfigurationChange(PersistitBackendCfg cfg)
  public ConfigChangeResult applyConfigurationChange(PluggableBackendCfg configuration)
  {
    final ConfigChangeResult ccr = new ConfigChangeResult();
    getMonitorProvider().enableFilterUseStats(configuration.isIndexFilterAnalyzerEnabled());
    getMonitorProvider().setMaxEntries(configuration.getIndexFilterAnalyzerMaxFilters());
    try
    {
      // Create the directory if it doesn't exist.
      if(!cfg.getDBDirectory().equals(this.config.getDBDirectory()))
      {
        File parentDirectory = getFileForPath(cfg.getDBDirectory());
        File backendDirectory =
          new File(parentDirectory, cfg.getBackendId());
        if (!backendDirectory.exists())
        {
          if(!backendDirectory.mkdirs())
          {
            ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
            ccr.addMessage(ERR_JEB_CREATE_FAIL.get(backendDirectory.getPath()));
            return ccr;
          }
        }
        //Make sure the directory is valid.
        else if (!backendDirectory.isDirectory())
        {
          ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
          ccr.addMessage(ERR_JEB_DIRECTORY_INVALID.get(backendDirectory.getPath()));
          return ccr;
        }
        ccr.setAdminActionRequired(true);
        ccr.addMessage(NOTE_JEB_CONFIG_DB_DIR_REQUIRES_RESTART.get(this.config.getDBDirectory(), cfg.getDBDirectory()));
      }
      if (!cfg.getDBDirectoryPermissions().equalsIgnoreCase(config.getDBDirectoryPermissions())
          || !cfg.getDBDirectory().equals(this.config.getDBDirectory()))
      {
        FilePermission backendPermission;
        try
        {
          backendPermission =
              FilePermission.decodeUNIXMode(cfg.getDBDirectoryPermissions());
        }
        catch(Exception e)
        {
          ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
          ccr.addMessage(ERR_CONFIG_BACKEND_MODE_INVALID.get(config.dn()));
          return ccr;
        }
        // Make sure the mode will allow the server itself access to the database
        if(!backendPermission.isOwnerWritable() ||
            !backendPermission.isOwnerReadable() ||
            !backendPermission.isOwnerExecutable())
        {
          ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
          ccr.addMessage(ERR_CONFIG_BACKEND_INSANE_MODE.get(cfg.getDBDirectoryPermissions()));
          return ccr;
        }
        // Get the backend database backendDirectory permissions and apply
        if(FilePermission.canSetPermissions())
        {
          File parentDirectory = getFileForPath(config.getDBDirectory());
          File backendDirectory = new File(parentDirectory, config.getBackendId());
          try
          {
            if (!FilePermission.setPermissions(backendDirectory, backendPermission))
            {
              logger.warn(WARN_JEB_UNABLE_SET_PERMISSIONS, backendPermission, backendDirectory);
            }
          }
          catch(Exception e)
          {
            // Log an warning that the permissions were not set.
            logger.warn(WARN_JEB_SET_PERMISSIONS_FAILED, backendDirectory, e);
          }
        }
      }
      getMonitorProvider().enableFilterUseStats(cfg.isIndexFilterAnalyzerEnabled());
      getMonitorProvider().setMaxEntries(cfg.getIndexFilterAnalyzerMaxFilters());
      this.config = cfg;
    }
    catch (Exception e)
    {
      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
      ccr.addMessage(LocalizableMessage.raw(stackTraceToSingleLineString(e)));
      return ccr;
    }
    return ccr;
    return new ConfigChangeResult();
  }
  /**
   * Returns whether this container JE database environment is
   * open, valid and can be used.
   * Returns whether this container JE database environment is open, valid and
   * can be used.
   *
   * @return {@code true} if valid, or {@code false} otherwise.
   */
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/spi/Storage.java
@@ -115,4 +115,20 @@
  /** {@inheritDoc} */
  @Override
  void close();
  /**
   * Remove all files for a backend of this storage.
   *
   * @throws StorageRuntimeException if removal fails
   */
  void removeStorageFiles() throws StorageRuntimeException;
  /**
   * Replace reserved characters with an underscore character.
   *
   * @param databasePrefix
   *          the suffix name to convert
   * @return a new String suitable for use as a suffix name
   */
  String toSafeSuffixName(String databasePrefix);
}