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

Fabio Pistolesi
13.24.2015 d161a11bcc21f7a035f069a99477df23198adfd1
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);
    DirectoryServer.registerMonitorProvider(diskMonitor);
    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);
    DirectoryServer.registerMonitorProvider(diskMonitor);
    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())
@@ -1447,7 +1443,6 @@
              break;
            }
          }
        }
      }
      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,24 +426,27 @@
  /**
   * 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)
    for (DN baseDN : baseDNs)
    {
      EntryContainer ec = openEntryContainer(baseDN, null, txn);
      EntryID id = ec.getHighestEntryID(txn);
      registerEntryContainer(baseDN, ec);
      if(highestID == null || id.compareTo(highestID) > 0)
      if (highestID == null || id.compareTo(highestID) > 0)
      {
        highestID = id;
      }
@@ -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.
   *         container for the base DN was not registered.
   */
  public EntryContainer unregisterEntryContainer(DN baseDN)
  EntryContainer unregisterEntryContainer(DN baseDN)
  {
    return entryContainers.remove(baseDN);
  }
@@ -530,7 +471,7 @@
  /**
   * Retrieves the compressed schema manager for this backend.
   *
   * @return  The compressed schema manager for this backend.
   * @return The compressed schema manager for this backend.
   */
  public CompressedSchema getCompressedSchema()
  {
@@ -541,11 +482,11 @@
   * Get the DatabaseEnvironmentMonitor object for JE environment used by this
   * root container.
   *
   * @return The DatabaseEnvironmentMonito object.
   * @return The DatabaseEnvironmentMonitor object.
   */
  public DatabaseEnvironmentMonitor getMonitorProvider()
  {
    if(monitor == null)
    if (monitor == null)
    {
      String monitorName = backend.getBackendID() + " Database Storage";
      monitor = new DatabaseEnvironmentMonitor(monitorName, this);
@@ -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,12 +543,12 @@
  /**
   * 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())
    for (DN baseDN : entryContainers.keySet())
    {
      EntryContainer ec = unregisterEntryContainer(baseDN);
      ec.exclusiveLock.lock();
@@ -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
  {
@@ -745,7 +686,7 @@
  }
  /**
   * Resets the next entry ID counter to zero.  This should only be used after
   * Resets the next entry ID counter to zero. This should only be used after
   * clearing all databases.
   */
  public void resetNextEntryID()
@@ -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;
      }
      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;
    // Storage has also registered a change listener, delegate to it.
    return true;
  }
  /** {@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);
}