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

Fabio Pistolesi
02.59.2015 8b806159078ce1308b27e676c0e8a6340f05e6e0
OPENDJ-1826 CR-6180 BackendImpl current DiskSpaceMonitoring should be done entirely at the Storage layer

Moving Disk Monitoring to the storage level, a new class has been introduced,
implementing an abstract representation of the storage state. It can be queried
by the backend layer regardless of the kind of media used by the storage.
6 files modified
1 files added
536 ■■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PersistItStorage.java 104 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PitBackend.java 28 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/BackendImpl.java 218 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/RootContainer.java 30 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/spi/Storage.java 14 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/spi/StorageStatus.java 132 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/persistit/PersistitTestCase.java 10 ●●●● patch | view | raw | blame | history
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)
    {
      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,8 +932,6 @@
   */
  private DirectoryException createDirectoryException(StorageRuntimeException e)
  {
    if (true) // FIXME JNR
    {
      Throwable cause = e.getCause();
      if (cause instanceof OpenDsException)
      {
@@ -990,48 +945,6 @@
            LocalizableMessage.raw(e.getMessage()), e);
      }
    }
    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);
    }
    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()
          throws ConfigException, InitializationException {
@@ -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