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

Fabio Pistolesi
02.59.2015 8b806159078ce1308b27e676c0e8a6340f05e6e0
opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PersistItStorage.java
@@ -30,13 +30,19 @@
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.ServerConstants.ALERT_DESCRIPTION_DISK_FULL;
import static org.opends.server.util.ServerConstants.ALERT_DESCRIPTION_DISK_SPACE_LOW;
import static org.opends.server.util.ServerConstants.ALERT_TYPE_DISK_FULL;
import static org.opends.server.util.ServerConstants.ALERT_TYPE_DISK_SPACE_LOW;
import static org.opends.server.util.StaticUtils.*;
import java.io.File;
import java.io.FilenameFilter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
@@ -46,17 +52,22 @@
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.AlertGenerator;
import org.opends.server.api.DiskSpaceMonitorHandler;
import org.opends.server.backends.pluggable.spi.Cursor;
import org.opends.server.backends.pluggable.spi.Importer;
import org.opends.server.backends.pluggable.spi.ReadOperation;
import org.opends.server.backends.pluggable.spi.Storage;
import org.opends.server.backends.pluggable.spi.StorageRuntimeException;
import org.opends.server.backends.pluggable.spi.StorageStatus;
import org.opends.server.backends.pluggable.spi.TreeName;
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.extensions.DiskSpaceMonitor;
import org.opends.server.types.DN;
import org.opends.server.types.FilePermission;
import com.persistit.Configuration;
@@ -74,7 +85,8 @@
import com.persistit.exception.RollbackException;
/** PersistIt database implementation of the {@link Storage} engine. */
public final class PersistItStorage implements Storage, ConfigurationChangeListener<PersistitBackendCfg>
public final class PersistItStorage implements Storage, ConfigurationChangeListener<PersistitBackendCfg>,
  DiskSpaceMonitorHandler, AlertGenerator
{
  private static final String VOLUME_NAME = "dj";
  /** The buffer / page size used by the PersistIt storage. */
@@ -501,6 +513,7 @@
  private Volume volume;
  private Configuration dbCfg;
  private PersistitBackendCfg config;
  private DiskSpaceMonitor diskMonitor;
  /** {@inheritDoc} */
  @Override
@@ -519,6 +532,8 @@
      }
    }
    config.removePersistitChangeListener(this);
    DirectoryServer.deregisterMonitorProvider(diskMonitor);
    DirectoryServer.deregisterAlertGenerator(this);
  }
  /** {@inheritDoc} */
@@ -562,13 +577,6 @@
  /** {@inheritDoc} */
  @Override
  public boolean isValid()
  {
    return !db.isFatal();
  }
  /** {@inheritDoc} */
  @Override
  public void open() throws Exception
  {
    setupStorageFiles();
@@ -588,6 +596,11 @@
    {
      throw new StorageRuntimeException(e);
    }
    // Register as disk space monitor handler
    diskMonitor = newDiskMonitor(config);
    DirectoryServer.registerMonitorProvider(diskMonitor);
    //Register as an AlertGenerator.
    DirectoryServer.registerAlertGenerator(this);
  }
  /** {@inheritDoc} */
@@ -900,6 +913,8 @@
        setDBDirPermissions(cfg, newBackendDirectory);
      }
      diskMonitor.setFullThreshold(cfg.getDiskFullThreshold());
      diskMonitor.setLowThreshold(cfg.getDiskLowThreshold());
      config = cfg;
    }
@@ -957,5 +972,78 @@
      throw new StorageRuntimeException(message.toString(), e);
    }
  }
  @Override
  public StorageStatus getStorageStatus()
  {
    if (diskMonitor.isFullThresholdReached())
    {
      return StorageStatus.unusable(WARN_JEB_OUT_OF_DISK_SPACE.get());
    }
    if (diskMonitor.isLowThresholdReached())
    {
      return StorageStatus.lockedDown(WARN_JEB_OUT_OF_DISK_SPACE.get());
    }
    return StorageStatus.working();
  }
  /** {@inheritDoc} */
  @Override
  public void diskFullThresholdReached(DiskSpaceMonitor monitor) {
    LocalizableMessage msg = ERR_JEB_DISK_FULL_THRESHOLD_REACHED.get(
        monitor.getDirectory().getPath(), config.getBackendId(), monitor.getFreeSpace(),
        Math.max(monitor.getLowThreshold(), monitor.getFullThreshold()));
    DirectoryServer.sendAlertNotification(this, ALERT_TYPE_DISK_FULL, msg);
  }
  /** {@inheritDoc} */
  @Override
  public void diskLowThresholdReached(DiskSpaceMonitor monitor) {
    LocalizableMessage msg = ERR_JEB_DISK_LOW_THRESHOLD_REACHED.get(
        monitor.getDirectory().getPath(), config.getBackendId(), monitor.getFreeSpace(),
        Math.max(monitor.getLowThreshold(), monitor.getFullThreshold()));
    DirectoryServer.sendAlertNotification(this, ALERT_TYPE_DISK_SPACE_LOW, msg);
  }
  /** {@inheritDoc} */
  @Override
  public void diskSpaceRestored(DiskSpaceMonitor monitor) {
    logger.error(NOTE_JEB_DISK_SPACE_RESTORED, monitor.getFreeSpace(),
        monitor.getDirectory().getPath(), config.getBackendId(),
        Math.max(monitor.getLowThreshold(), monitor.getFullThreshold()));
  }
  private DiskSpaceMonitor newDiskMonitor(PersistitBackendCfg config) throws Exception
  {
    File parentDirectory = getFileForPath(config.getDBDirectory());
    File backendDirectory = new File(parentDirectory, config.getBackendId());
    DiskSpaceMonitor dm = new DiskSpaceMonitor(config.getBackendId() + " backend",
        backendDirectory, config.getDiskLowThreshold(), config.getDiskFullThreshold(),
        5, TimeUnit.SECONDS, this);
    dm.initializeMonitorProvider(null);
    return dm;
  }
  /** {@inheritDoc} */
  @Override
  public DN getComponentEntryDN() {
    return config.dn();
  }
  /** {@inheritDoc} */
  @Override
  public String getClassName() {
    return PersistItStorage.class.getName();
  }
  /** {@inheritDoc} */
  @Override
  public Map<String, String> getAlerts()
  {
    Map<String, String> alerts = new LinkedHashMap<String, String>();
    alerts.put(ALERT_TYPE_DISK_SPACE_LOW, ALERT_DESCRIPTION_DISK_SPACE_LOW);
    alerts.put(ALERT_TYPE_DISK_FULL, ALERT_DESCRIPTION_DISK_FULL);
    return alerts;
  }
}
opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PitBackend.java
@@ -29,15 +29,11 @@
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.
@@ -52,30 +48,6 @@
  }
  /** {@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;
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/BackendImpl.java
@@ -49,9 +49,7 @@
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;
import org.opends.server.api.MonitorProvider;
import org.opends.server.backends.RebuildConfig;
import org.opends.server.backends.VerifyConfig;
@@ -60,7 +58,6 @@
import org.opends.server.backends.pluggable.spi.WriteOperation;
import org.opends.server.backends.pluggable.spi.WriteableStorage;
import org.opends.server.core.*;
import org.opends.server.extensions.DiskSpaceMonitor;
import org.opends.server.types.*;
/**
@@ -68,8 +65,7 @@
 * locally in a Berkeley DB JE database.
 */
public abstract class BackendImpl extends Backend<PluggableBackendCfg> implements
    ConfigurationChangeListener<PluggableBackendCfg>, AlertGenerator,
    DiskSpaceMonitorHandler
    ConfigurationChangeListener<PluggableBackendCfg>
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
@@ -85,9 +81,6 @@
  private DN[] baseDNs;
  private MonitorProvider<?> rootContainerMonitor;
  /** Disk space monitoring if the storage supports it. */
  private DiskSpaceMonitor diskMonitor;
  /** The controls supported by this backend. */
  private static final Set<String> supportedControls = new HashSet<String>(Arrays.asList(
      OID_SUBTREE_DELETE_CONTROL,
@@ -96,10 +89,24 @@
      OID_SERVER_SIDE_SORT_REQUEST_CONTROL,
      OID_VLV_REQUEST_CONTROL));
  /** Begin a Backend API method that accesses the database. */
  private void accessBegin()
  /**
   * Begin a Backend API method that accesses the database and returns the <code>EntryContainer</code> for
   * <code>entryDN</code>.
   * @param operation requesting the storage
   * @param entryDN the target DN for the operation
   * @return <code>EntryContainer</code> where <code>entryDN</code> resides
   */
  private EntryContainer accessBegin(Operation operation, DN entryDN) throws DirectoryException
  {
    checkRootContainerInitialized();
    rootContainer.checkForEnoughResources(operation);
    EntryContainer ec = rootContainer.getEntryContainer(entryDN);
    if (ec == null)
    {
      throw new DirectoryException(ResultCode.UNDEFINED, ERR_BACKEND_ENTRY_DOESNT_EXIST.get(entryDN, getBackendID()));
    }
    threadTotalCount.getAndIncrement();
    return ec;
  }
  /** End a Backend API method that accesses the database. */
@@ -181,39 +188,10 @@
    rootContainerMonitor = rootContainer.getMonitorProvider();
    DirectoryServer.registerMonitorProvider(rootContainerMonitor);
    // Register as disk space monitor handler
    diskMonitor = newDiskMonitor(cfg);
    if (diskMonitor != null)
    {
      DirectoryServer.registerMonitorProvider(diskMonitor);
    }
    //Register as an AlertGenerator.
    DirectoryServer.registerAlertGenerator(this);
    // Register this backend as a change listener.
    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()
@@ -235,7 +213,6 @@
    }
    DirectoryServer.deregisterMonitorProvider(rootContainerMonitor);
    DirectoryServer.deregisterMonitorProvider(diskMonitor);
    // We presume the server will prevent more operations coming into this
    // backend, but there may be existing operations already in the
@@ -254,8 +231,6 @@
      logger.error(ERR_JEB_DATABASE_EXCEPTION, e.getMessage());
    }
    DirectoryServer.deregisterAlertGenerator(this);
    // Make sure the thread counts are zero for next initialization.
    threadTotalCount.set(0);
@@ -364,8 +339,7 @@
  /** {@inheritDoc} */
  @Override
  public ConditionResult hasSubordinates(DN entryDN)
         throws DirectoryException
  public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException
  {
    long ret = numSubordinates(entryDN, false);
    if(ret < 0)
@@ -379,17 +353,26 @@
  /** {@inheritDoc} */
  @Override
  public long numSubordinates(DN entryDN, boolean subtree)
      throws DirectoryException
  public long numSubordinates(DN entryDN, boolean subtree) throws DirectoryException
  {
    checkRootContainerInitialized();
    EntryContainer ec = rootContainer.getEntryContainer(entryDN);
    if(ec == null)
    EntryContainer ec;
    /*
     * Only place where we need special handling. Should return -1 instead of an
     * error if the EntryContainer is null...
     */
    try {
      ec = accessBegin(null, entryDN);
    }
    catch (DirectoryException de)
    {
      return -1;
      if (de.getResultCode() == ResultCode.UNDEFINED)
      {
        return -1;
      }
      throw de;
    }
    accessBegin();
    ec.sharedLock.lock();
    try
    {
@@ -419,10 +402,8 @@
  @Override
  public Entry getEntry(DN entryDN) throws DirectoryException
  {
    accessBegin();
    EntryContainer ec = accessBegin(null, entryDN);
    checkRootContainerInitialized();
    EntryContainer ec = rootContainer.getEntryContainer(entryDN);
    ec.sharedLock.lock();
    Entry entry;
    try
@@ -447,14 +428,10 @@
  /** {@inheritDoc} */
  @Override
  public void addEntry(Entry entry, AddOperation addOperation)
      throws DirectoryException, CanceledOperationException
  public void addEntry(Entry entry, AddOperation addOperation) throws DirectoryException, CanceledOperationException
  {
    checkDiskSpace(addOperation);
    accessBegin();
    EntryContainer ec = accessBegin(addOperation, entry.getName());
    checkRootContainerInitialized();
    EntryContainer ec = rootContainer.getEntryContainer(entry.getName());
    ec.sharedLock.lock();
    try
    {
@@ -479,11 +456,8 @@
  public void deleteEntry(DN entryDN, DeleteOperation deleteOperation)
      throws DirectoryException, CanceledOperationException
  {
    checkDiskSpace(deleteOperation);
    accessBegin();
    EntryContainer ec = accessBegin(deleteOperation, entryDN);
    checkRootContainerInitialized();
    EntryContainer ec = rootContainer.getEntryContainer(entryDN);
    ec.sharedLock.lock();
    try
    {
@@ -505,15 +479,11 @@
  /** {@inheritDoc} */
  @Override
  public void replaceEntry(Entry oldEntry, Entry newEntry,
      ModifyOperation modifyOperation) throws DirectoryException,
      CanceledOperationException
  public void replaceEntry(Entry oldEntry, Entry newEntry, ModifyOperation modifyOperation)
      throws DirectoryException, CanceledOperationException
  {
    checkDiskSpace(modifyOperation);
    accessBegin();
    EntryContainer ec = accessBegin(modifyOperation, newEntry.getName());
    checkRootContainerInitialized();
    EntryContainer ec = rootContainer.getEntryContainer(newEntry.getName());
    ec.sharedLock.lock();
    try
@@ -536,23 +506,18 @@
  /** {@inheritDoc} */
  @Override
  public void renameEntry(DN currentDN, Entry entry,
                          ModifyDNOperation modifyDNOperation)
  public void renameEntry(DN currentDN, Entry entry, ModifyDNOperation modifyDNOperation)
      throws DirectoryException, CanceledOperationException
  {
    checkDiskSpace(modifyDNOperation);
    accessBegin();
    checkRootContainerInitialized();
    EntryContainer currentContainer = rootContainer.getEntryContainer(currentDN);
    EntryContainer currentContainer = accessBegin(modifyDNOperation, currentDN);
    EntryContainer container = rootContainer.getEntryContainer(entry.getName());
    if (currentContainer != container)
    {
      accessEnd();
      // FIXME: No reason why we cannot implement a move between containers
      // since the containers share the same database environment.
      LocalizableMessage msg = WARN_JEB_FUNCTION_NOT_SUPPORTED.get();
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, msg);
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, WARN_JEB_FUNCTION_NOT_SUPPORTED.get());
    }
    currentContainer.sharedLock.lock();
@@ -576,13 +541,10 @@
  /** {@inheritDoc} */
  @Override
  public void search(SearchOperation searchOperation)
      throws DirectoryException, CanceledOperationException
  public void search(SearchOperation searchOperation) throws DirectoryException, CanceledOperationException
  {
    accessBegin();
    EntryContainer ec = accessBegin(searchOperation, searchOperation.getBaseDN());
    checkRootContainerInitialized();
    EntryContainer ec = rootContainer.getEntryContainer(searchOperation.getBaseDN());
    ec.sharedLock.lock();
    try
@@ -876,11 +838,6 @@
            baseDNs = newBaseDNsArray;
            if (diskMonitor != null)
            {
              updateDiskMonitor(diskMonitor, newCfg);
            }
            // Put the new configuration in place.
            cfg = newCfg;
          }
@@ -975,62 +932,18 @@
   */
  private DirectoryException createDirectoryException(StorageRuntimeException e)
  {
    if (true) // FIXME JNR
    Throwable cause = e.getCause();
    if (cause instanceof OpenDsException)
    {
      Throwable cause = e.getCause();
      if (cause instanceof OpenDsException)
      {
        return new DirectoryException(
            DirectoryServer.getServerErrorResultCode(), (OpenDsException) cause);
      }
      else
      {
        return new DirectoryException(
            DirectoryServer.getServerErrorResultCode(),
            LocalizableMessage.raw(e.getMessage()), e);
      }
      return new DirectoryException(
          DirectoryServer.getServerErrorResultCode(), (OpenDsException) cause);
    }
    if (/*e instanceof EnvironmentFailureException && */ !rootContainer.isValid()) {
      LocalizableMessage message = NOTE_BACKEND_ENVIRONMENT_UNUSABLE.get(getBackendID());
      logger.info(message);
      DirectoryServer.sendAlertNotification(DirectoryServer.getInstance(),
              ALERT_TYPE_BACKEND_ENVIRONMENT_UNUSABLE, message);
    else
    {
      return new DirectoryException(
          DirectoryServer.getServerErrorResultCode(),
          LocalizableMessage.raw(e.getMessage()), e);
    }
    String jeMessage = e.getMessage();
    if (jeMessage == null) {
      jeMessage = stackTraceToSingleLineString(e);
    }
    LocalizableMessage message = ERR_JEB_DATABASE_EXCEPTION.get(jeMessage);
    return new DirectoryException(
        DirectoryServer.getServerErrorResultCode(), message, e);
  }
  /** {@inheritDoc} */
  @Override
  public String getClassName() {
    return BackendImpl.class.getName();
  }
  /** {@inheritDoc} */
  @Override
  public Map<String, String> getAlerts()
  {
    Map<String, String> alerts = new LinkedHashMap<String, String>();
    alerts.put(ALERT_TYPE_BACKEND_ENVIRONMENT_UNUSABLE,
            ALERT_DESCRIPTION_BACKEND_ENVIRONMENT_UNUSABLE);
    alerts.put(ALERT_TYPE_DISK_SPACE_LOW,
            ALERT_DESCRIPTION_DISK_SPACE_LOW);
    alerts.put(ALERT_TYPE_DISK_FULL,
            ALERT_DESCRIPTION_DISK_FULL);
    return alerts;
  }
  /** {@inheritDoc} */
  @Override
  public DN getComponentEntryDN() {
    return cfg.dn();
  }
  private RootContainer initializeRootContainer()
@@ -1056,43 +969,4 @@
    EntryCachePreloader preloader = new EntryCachePreloader(this);
    preloader.preload();
  }
  /** {@inheritDoc} */
  @Override
  public void diskLowThresholdReached(DiskSpaceMonitor monitor) {
    LocalizableMessage msg = ERR_JEB_DISK_LOW_THRESHOLD_REACHED.get(
        monitor.getDirectory().getPath(), cfg.getBackendId(), monitor.getFreeSpace(),
        Math.max(monitor.getLowThreshold(), monitor.getFullThreshold()));
    DirectoryServer.sendAlertNotification(this, ALERT_TYPE_DISK_SPACE_LOW, msg);
  }
  /** {@inheritDoc} */
  @Override
  public void diskFullThresholdReached(DiskSpaceMonitor monitor) {
    LocalizableMessage msg = ERR_JEB_DISK_FULL_THRESHOLD_REACHED.get(
        monitor.getDirectory().getPath(), cfg.getBackendId(), monitor.getFreeSpace(),
        Math.max(monitor.getLowThreshold(), monitor.getFullThreshold()));
    DirectoryServer.sendAlertNotification(this, ALERT_TYPE_DISK_FULL, msg);
  }
  /** {@inheritDoc} */
  @Override
  public void diskSpaceRestored(DiskSpaceMonitor monitor) {
    logger.error(NOTE_JEB_DISK_SPACE_RESTORED, monitor.getFreeSpace(),
        monitor.getDirectory().getPath(), cfg.getBackendId(),
        Math.max(monitor.getLowThreshold(), monitor.getFullThreshold()));
  }
  private void checkDiskSpace(Operation operation) throws DirectoryException
  {
    if(diskMonitor.isFullThresholdReached() ||
        (diskMonitor.isLowThresholdReached()
            && operation != null
            && !operation.getClientConnection().hasPrivilege(
                Privilege.BYPASS_LOCKDOWN, operation)))
    {
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
          WARN_JEB_OUT_OF_DISK_SPACE.get());
    }
  }
}
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/RootContainer.java
@@ -53,6 +53,7 @@
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.config.server.ConfigChangeResult;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.PluggableBackendCfg;
import org.opends.server.api.CompressedSchema;
@@ -60,9 +61,11 @@
import org.opends.server.backends.pluggable.spi.ReadableStorage;
import org.opends.server.backends.pluggable.spi.Storage;
import org.opends.server.backends.pluggable.spi.StorageRuntimeException;
import org.opends.server.backends.pluggable.spi.StorageStatus;
import org.opends.server.backends.pluggable.spi.WriteOperation;
import org.opends.server.backends.pluggable.spi.WriteableStorage;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.SearchOperation;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
@@ -70,6 +73,8 @@
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.LDIFImportResult;
import org.opends.server.types.OpenDsException;
import org.opends.server.types.Operation;
import org.opends.server.types.Privilege;
import org.opends.server.util.LDIFException;
import org.opends.server.util.LDIFReader;
import org.opends.server.util.RuntimeInformation;
@@ -690,12 +695,27 @@
  }
  /**
   * Returns whether this container JE database environment is open, valid and
   * can be used.
   * Checks the storage has enough resources for an operation.
   *
   * @return {@code true} if valid, or {@code false} otherwise.
   * @param operation the current operation
   * @throws DirectoryException if resources are in short supply
   */
  public boolean isValid() {
    return storage.isValid();
  public void checkForEnoughResources(Operation operation) throws DirectoryException
  {
    StorageStatus status = storage.getStorageStatus();
    if (status.isUnusable()
        || (status.isLockedDown() && hasBypassLockdownPrivileges(operation)))
    {
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, status.getReason());
    }
  }
  private boolean hasBypassLockdownPrivileges(Operation operation)
  {
    return operation != null
          // Read operations are always allowed in lock down mode
          && !(operation instanceof SearchOperation)
          && !operation.getClientConnection().hasPrivilege(
              Privilege.BYPASS_LOCKDOWN, operation);
  }
}
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/spi/Storage.java
@@ -100,13 +100,6 @@
  void closeTree(TreeName treeName);
  /**
   * Returns whether the storage engine is in a valid state, i.e. whether it can be used for processing.
   *
   * @return {@code true} if the storage engine is in a valid state, {@code false} otherwise
   */
  boolean isValid();
  /**
   * Returns a filename filter which selects the files to be included in a backup.
   * @return a filename filter which selects the files to be included in a backup
   */
@@ -122,4 +115,11 @@
   * @throws StorageRuntimeException if removal fails
   */
  void removeStorageFiles() throws StorageRuntimeException;
  /**
   * Returns the current status of the storage.
   *
   * @return the current status of the storage
   */
  StorageStatus getStorageStatus();
}
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/spi/StorageStatus.java
New file
@@ -0,0 +1,132 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2015 ForgeRock AS
 */
package org.opends.server.backends.pluggable.spi;
import org.forgerock.i18n.LocalizableMessage;
/**
 * Represents the current status of a storage with respect to its resources.
 */
public final class StorageStatus
{
  /** Internal States. */
  private enum Code
  {
    /** Storage is working normally. */
    WORKING,
    /** Storage resources start getting scarce. */
    LOCKED_DOWN,
    /** Storage has no resources to execute operations. */
    UNUSABLE
  };
  /** Hopefully resources are always in this state. */
  private static final StorageStatus WORKING = new StorageStatus(Code.WORKING, null);
  /** Current status. */
  private final Code code;
  /** Current warning/error message. */
  private final LocalizableMessage reason;
  /**
   * Returns normal state.
   *
   * @return normal state
   */
  public static StorageStatus working()
  {
    return WORKING;
  }
  /**
   * Returns state for resources getting scarce.
   *
   * @param reason the message to forward
   * @return state for resources getting scarce
   */
  public static StorageStatus lockedDown(LocalizableMessage reason)
  {
    return new StorageStatus(Code.LOCKED_DOWN, reason);
  }
  /**
   * Returns state for no more resources.
   *
   * @param reason the message to forward
   * @return state for no more resources
   */
  public static StorageStatus unusable(LocalizableMessage reason)
  {
    return new StorageStatus(Code.UNUSABLE, reason);
  }
  private StorageStatus(Code code, LocalizableMessage reason)
  {
    this.code = code;
    this.reason = reason;
  }
  /**
   * Returns true if resources are getting scarce.
   *
   * @return true if resources are getting scarce
   */
  public boolean isLockedDown()
  {
    return code == Code.LOCKED_DOWN;
  }
  /**
   * Returns true if state is normal.
   *
   * @return true if state is normal
   */
  public boolean isWorking()
  {
    return code == Code.WORKING;
  }
  /**
   * Returns true if no more resources are available.
   *
   * @return true if no more resources are available
   */
  public boolean isUnusable()
  {
    return code == Code.UNUSABLE;
  }
  /**
   * Returns the error message for non working states.
   *
   * @return the error message for non working states
   */
  public LocalizableMessage getReason()
  {
    return reason;
  }
}
opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/persistit/PersistitTestCase.java
@@ -28,7 +28,6 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertTrue;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.TestCaseUtils;
@@ -46,10 +45,17 @@
 */
public class PersistitTestCase extends PluggableBackendImplTestCase
{
  /**
   * Tests the storage API for resource checking.
   * The tested method has no return value, but an exception, while not systematic, may be thrown,
   * in which case the test must fail.
   *
   * @throws Exception if resources are low.
   */
  @Test
  public void testPersistitCfg() throws Exception
  {
    assertTrue(backend.getRootContainer().isValid());
    backend.getRootContainer().checkForEnoughResources(null);
  }
  @Override