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

Fabio Pistolesi
10.55.2015 7ccaa46b4a749896b2daabda390d8ddd3ae7743f

OPENDJ-1809 CR-6564 Disk Space Monitoring should be a server wide service

Disk space monitoring is now a global service, per DirectoryServer instance.
Backends can get the service through a serverContext and will be notified of any trigger of the configured thresholds.
Instead of using raw directories, if the JVM supports it, try to gather the mountpoints or global root folders and monitor those
instead. Still messages logged by the backends will reference only their own directories.
Each backend can also log its own messages in addition to what already logged by the general service.

14 files modified
1135 ■■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/api/DiskSpaceMonitorHandler.java 23 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/BackendImpl.java 65 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/Importer.java 96 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PersistItStorage.java 99 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/Importer.java 52 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java 13 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/ServerContext.java 10 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/extensions/DiskSpaceMonitor.java 585 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/util/Platform.java 163 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/messages/org/opends/messages/backend.properties 8 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/messages/org/opends/messages/core.properties 10 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/messages/org/opends/messages/jeb.properties 6 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/messages/org/opends/messages/utility.properties 3 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/StateTest.java 2 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/api/DiskSpaceMonitorHandler.java
@@ -27,7 +27,8 @@
package org.opends.server.api;
import org.opends.server.extensions.DiskSpaceMonitor;
import java.io.File;
/**
 * This interface defines the set of methods that must be implemented
@@ -40,22 +41,26 @@
  /**
   * Notifies that the registered "low" threshold have been reached.
   *
   * @param monitor The DiskSpaceMonitor that detected this event.
   * @param directory the directory for which the threshold has been triggered
   * @param thresholdInBytes the threshold value in bytes
   */
  void diskLowThresholdReached(DiskSpaceMonitor monitor);
  void diskLowThresholdReached(File directory, long thresholdInBytes);
  /**
   * Notifies that the registered "full" threshold have been reached.
   *
   * @param monitor The DiskSpaceMonitor that detected this event.
   * @param directory the directory for which the threshold has been triggered
   * @param thresholdInBytes the threshold value in bytes
   */
  void diskFullThresholdReached(DiskSpaceMonitor monitor);
  void diskFullThresholdReached(File directory, long thresholdInBytes);
  /**
   * Notifies that the free disk space is now above both "low" and
   * "full" thresholds.
   * Notifies that the free disk space is now above both "low" and "full" thresholds.
   *
   * @param monitor The DiskSpaceMonitor that detected this event.
   * @param directory the directory for which the threshold has been triggeredTODO
   *
   * @param lowThresholdInBytes the low threshold value in bytes
   * @param fullThresholdInBytes the full threshold value in bytes
   */
  void diskSpaceRestored(DiskSpaceMonitor monitor);
  void diskSpaceRestored(File directory, long lowThresholdInBytes, long fullThresholdInBytes);
}
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.*;
@@ -58,6 +57,7 @@
import org.opends.server.api.MonitorProvider;
import org.opends.server.backends.RebuildConfig;
import org.opends.server.backends.VerifyConfig;
import org.opends.server.backends.pluggable.spi.StorageStatus;
import org.opends.server.core.*;
import org.opends.server.extensions.DiskSpaceMonitor;
import org.opends.server.types.*;
@@ -91,6 +91,7 @@
  private MonitorProvider<?> rootContainerMonitor;
  private DiskSpaceMonitor diskMonitor;
  private StorageStatus storageStatus = StorageStatus.working();
  /**
   * The controls supported by this backend.
@@ -159,6 +160,7 @@
    this.cfg = cfg;
    baseDNs = this.cfg.getBaseDN().toArray(new DN[0]);
    diskMonitor = serverContext.getDiskSpaceMonitor();
  }
  /** {@inheritDoc} */
@@ -206,11 +208,8 @@
    DirectoryServer.registerMonitorProvider(rootContainerMonitor);
    // Register as disk space monitor handler
    diskMonitor = newDiskMonitor(cfg);
    if (diskMonitor != null)
    {
      DirectoryServer.registerMonitorProvider(diskMonitor);
    }
    diskMonitor.registerMonitoredDirectory(getBackendID(), getDirectory(), cfg.getDiskLowThreshold(),
        cfg.getDiskFullThreshold(), this);
    //Register as an AlertGenerator.
    DirectoryServer.registerAlertGenerator(this);
@@ -218,16 +217,10 @@
    cfg.addLocalDBChangeListener(this);
  }
  private DiskSpaceMonitor newDiskMonitor(LocalDBBackendCfg cfg) throws ConfigException, InitializationException
  private File getDirectory()
  {
    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;
    return new File(parentDirectory, cfg.getBackendId());
  }
  /** {@inheritDoc} */
@@ -250,8 +243,7 @@
    }
    DirectoryServer.deregisterMonitorProvider(rootContainerMonitor);
    DirectoryServer.deregisterMonitorProvider(diskMonitor);
    diskMonitor.deregisterMonitoredDirectory(getDirectory(), this);
    // We presume the server will prevent more operations coming into this
    // backend, but there may be existing operations already in the
    // backend. We need to wait for them to finish.
@@ -717,7 +709,7 @@
      }
      final EnvironmentConfig envConfig = getEnvConfigForImport();
      final Importer importer = new Importer(importConfig, cfg, envConfig);
      final Importer importer = new Importer(importConfig, cfg, envConfig, serverContext);
      rootContainer = initializeRootContainer(envConfig);
      return importer.processImport(rootContainer);
    }
@@ -859,7 +851,7 @@
        envConfig = parseConfigEntry(cfg);
      }
      final Importer importer = new Importer(rebuildConfig, cfg, envConfig);
      final Importer importer = new Importer(rebuildConfig, cfg, envConfig, serverContext);
      importer.rebuildIndexes(rootContainer);
    }
    catch (ExecutionException execEx)
@@ -1002,10 +994,7 @@
        baseDNs = newBaseDNsArray;
      }
      if (diskMonitor != null)
      {
        updateDiskMonitor(diskMonitor, newCfg);
      }
      updateDiskMonitor(diskMonitor, newCfg);
      // Put the new configuration in place.
      this.cfg = newCfg;
@@ -1020,8 +1009,8 @@
  private void updateDiskMonitor(DiskSpaceMonitor dm, LocalDBBackendCfg newCfg)
  {
    dm.setFullThreshold(newCfg.getDiskFullThreshold());
    dm.setLowThreshold(newCfg.getDiskLowThreshold());
    diskMonitor.registerMonitoredDirectory(getBackendID(), getDirectory(), newCfg.getDiskLowThreshold(),
        newCfg.getDiskFullThreshold(), this);
  }
  private void removeDeletedBaseDNs(SortedSet<DN> newBaseDNs) throws DirectoryException
@@ -1196,34 +1185,30 @@
  /** {@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);
  public void diskLowThresholdReached(File directory, long thresholdInBytes) {
    storageStatus = StorageStatus.lockedDown(
        WARN_DISK_SPACE_LOW_THRESHOLD_CROSSED.get(directory.getFreeSpace(), directory.getAbsolutePath(),
        thresholdInBytes, getBackendID()));
  }
  /** {@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);
  public void diskFullThresholdReached(File directory, long thresholdInBytes) {
    storageStatus = StorageStatus.unusable(
        WARN_DISK_SPACE_FULL_THRESHOLD_CROSSED.get(directory.getFreeSpace(), directory.getAbsolutePath(),
        thresholdInBytes, getBackendID()));
  }
  /** {@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()));
  public void diskSpaceRestored(File directory, long lowThresholdInBytes, long fullThresholdInBytes) {
    storageStatus = StorageStatus.working();
  }
  private void checkDiskSpace(Operation operation) throws DirectoryException
  {
    if(diskMonitor.isFullThresholdReached() ||
        (diskMonitor.isLowThresholdReached()
    if(storageStatus.isUnusable() ||
        (storageStatus.isLockedDown()
            && operation != null
            && !operation.getClientConnection().hasPrivilege(
                Privilege.BYPASS_LOCKDOWN, operation)))
opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/Importer.java
@@ -27,7 +27,6 @@
package org.opends.server.backends.jeb;
import static com.sleepycat.je.EnvironmentConfig.*;
import static org.opends.messages.JebMessages.*;
import static org.opends.server.admin.std.meta.LocalDBIndexCfgDefn.IndexType.*;
import static org.opends.server.backends.jeb.IndexOutputBuffer.*;
@@ -84,7 +83,7 @@
import java.util.concurrent.atomic.AtomicLong;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageDescriptor.Arg3;
import org.forgerock.i18n.LocalizableMessageDescriptor.Arg2;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.ByteString;
@@ -98,6 +97,7 @@
import org.opends.server.backends.RebuildConfig;
import org.opends.server.backends.RebuildConfig.RebuildMode;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ServerContext;
import org.opends.server.extensions.DiskSpaceMonitor;
import org.opends.server.types.AttributeType;
import org.opends.server.types.DN;
@@ -162,6 +162,8 @@
  private static final int MIN_READ_AHEAD_CACHE_SIZE = 2 * KB;
  /** Small heap threshold used to give more memory to JVM to attempt OOM errors. */
  private static final int SMALL_HEAP_SIZE = 256 * MB;
  /** Minimum memory needed for import */
  private static final int MINIMUM_AVAILABLE_MEMORY = 32 * MB;
  /** The DN attribute type. */
  private static final AttributeType dnType;
@@ -267,6 +269,8 @@
  /** Number of phase one buffers. */
  private int phaseOneBufferCount;
  private final DiskSpaceMonitor diskSpaceMonitor;
  static
  {
    AttributeType attrType = DirectoryServer.getAttributeType("dn");
@@ -286,6 +290,8 @@
   *          The local DB back-end configuration.
   * @param envConfig
   *          The JEB environment config.
   * @param serverContext
   *          The ServerContext for this Directory Server instance
   * @throws InitializationException
   *           If a problem occurs during initialization.
   * @throws JebException
@@ -293,14 +299,15 @@
   * @throws ConfigException
   *           If a problem occurs during initialization.
   */
  public Importer(RebuildConfig rebuildConfig, LocalDBBackendCfg cfg,
      EnvironmentConfig envConfig) throws InitializationException,
  public Importer(RebuildConfig rebuildConfig, LocalDBBackendCfg cfg, EnvironmentConfig envConfig,
      ServerContext serverContext) throws InitializationException,
      JebException, ConfigException
  {
    this.importConfiguration = null;
    this.backendConfiguration = cfg;
    this.tmpEnv = null;
    this.threadCount = 1;
    this.diskSpaceMonitor = serverContext.getDiskSpaceMonitor();
    this.rebuildManager = new RebuildIndexManager(rebuildConfig, cfg);
    this.indexCount = rebuildManager.getIndexCount();
    this.clearedBackend = false;
@@ -327,6 +334,8 @@
   *          The local DB back-end configuration.
   * @param envConfig
   *          The JEB environment config.
   * @param serverContext
   *          The ServerContext for this Directory Server instance
   * @throws InitializationException
   *           If a problem occurs during initialization.
   * @throws ConfigException
@@ -334,13 +343,14 @@
   * @throws DatabaseException
   *           If an error occurred when opening the DB.
   */
  public Importer(LDIFImportConfig importConfiguration,
      LocalDBBackendCfg localDBBackendCfg, EnvironmentConfig envConfig)
  public Importer(LDIFImportConfig importConfiguration, LocalDBBackendCfg localDBBackendCfg,
      EnvironmentConfig envConfig, ServerContext serverContext)
      throws InitializationException, ConfigException, DatabaseException
  {
    this.rebuildManager = null;
    this.importConfiguration = importConfiguration;
    this.backendConfiguration = localDBBackendCfg;
    this.diskSpaceMonitor = serverContext.getDiskSpaceMonitor();
    if (importConfiguration.getThreadCount() == 0)
    {
@@ -625,8 +635,8 @@
        configuredMemory = backendConfiguration.getDBCachePercent() * Runtime.getRuntime().maxMemory() / 100;
      }
      // Round up to minimum of 16MB (e.g. unit tests only use 2% cache).
      totalAvailableMemory = Math.max(Math.min(usableMemory, configuredMemory), 16 * MB);
      // Round up to minimum of 32MB (e.g. unit tests only use a small cache).
      totalAvailableMemory = Math.max(Math.min(usableMemory, configuredMemory), MINIMUM_AVAILABLE_MEMORY);
    }
    else
    {
@@ -860,14 +870,10 @@
    this.rootContainer = rootContainer;
    long startTime = System.currentTimeMillis();
    DiskSpaceMonitor tmpMonitor = createDiskSpaceMonitor(tempDir, "backend index rebuild tmp directory");
    tmpMonitor.initializeMonitorProvider(null);
    DirectoryServer.registerMonitorProvider(tmpMonitor);
    updateDiskMonitor(tempDir, "backend index rebuild tmp directory");
    File parentDirectory = getFileForPath(backendConfiguration.getDBDirectory());
    File backendDirectory = new File(parentDirectory, backendConfiguration.getBackendId());
    DiskSpaceMonitor dbMonitor = createDiskSpaceMonitor(backendDirectory, "backend index rebuild DB directory");
    dbMonitor.initializeMonitorProvider(null);
    DirectoryServer.registerMonitorProvider(dbMonitor);
    updateDiskMonitor(backendDirectory, "backend index rebuild DB directory");
    try
    {
@@ -879,10 +885,8 @@
    }
    finally
    {
      DirectoryServer.deregisterMonitorProvider(tmpMonitor);
      DirectoryServer.deregisterMonitorProvider(dbMonitor);
      tmpMonitor.finalizeMonitorProvider();
      dbMonitor.finalizeMonitorProvider();
      diskSpaceMonitor.deregisterMonitoredDirectory(tempDir, this);
      diskSpaceMonitor.deregisterMonitoredDirectory(backendDirectory, this);
    }
  }
@@ -908,8 +912,8 @@
      InterruptedException, ExecutionException
  {
    this.rootContainer = rootContainer;
    DiskSpaceMonitor tmpMonitor = null;
    DiskSpaceMonitor dbMonitor = null;
    File parentDirectory = getFileForPath(backendConfiguration.getDBDirectory());
    File backendDirectory = new File(parentDirectory, backendConfiguration.getBackendId());
    try {
      try
      {
@@ -921,14 +925,8 @@
        throw new InitializationException(message, ioe);
      }
      tmpMonitor = createDiskSpaceMonitor(tempDir, "backend import tmp directory");
      tmpMonitor.initializeMonitorProvider(null);
      DirectoryServer.registerMonitorProvider(tmpMonitor);
      File parentDirectory = getFileForPath(backendConfiguration.getDBDirectory());
      File backendDirectory = new File(parentDirectory, backendConfiguration.getBackendId());
      dbMonitor = createDiskSpaceMonitor(backendDirectory, "backend import DB directory");
      dbMonitor.initializeMonitorProvider(null);
      DirectoryServer.registerMonitorProvider(dbMonitor);
      updateDiskMonitor(tempDir, "backend import tmp directory");
      updateDiskMonitor(backendDirectory, "backend import DB directory");
      logger.info(NOTE_JEB_IMPORT_STARTING, DirectoryServer.getVersionString(),
              BUILD_ID, REVISION_NUMBER);
@@ -991,24 +989,15 @@
          // Do nothing.
        }
      }
      if (tmpMonitor != null)
      {
        DirectoryServer.deregisterMonitorProvider(tmpMonitor);
        tmpMonitor.finalizeMonitorProvider();
      }
      if (dbMonitor != null)
      {
        DirectoryServer.deregisterMonitorProvider(dbMonitor);
        dbMonitor.finalizeMonitorProvider();
      }
      diskSpaceMonitor.deregisterMonitoredDirectory(tempDir, this);
      diskSpaceMonitor.deregisterMonitoredDirectory(backendDirectory, this);
    }
  }
  private DiskSpaceMonitor createDiskSpaceMonitor(File dir, String backendSuffix)
  private void updateDiskMonitor(File dir, String backendSuffix)
  {
    final LocalDBBackendCfg cfg = backendConfiguration;
    return new DiskSpaceMonitor(cfg.getBackendId() + " " + backendSuffix, dir,
        cfg.getDiskLowThreshold(), cfg.getDiskFullThreshold(), 5, TimeUnit.SECONDS, this);
    diskSpaceMonitor.registerMonitoredDirectory(backendConfiguration.getBackendId() + " " + backendSuffix, dir,
        backendConfiguration.getDiskLowThreshold(), backendConfiguration.getDiskFullThreshold(), this);
  }
  private void recursiveDelete(File dir)
@@ -3643,21 +3632,20 @@
    }
    @Override
    public void diskLowThresholdReached(DiskSpaceMonitor monitor)
    public void diskLowThresholdReached(File directory, long thresholdInBytes)
    {
      diskFullThresholdReached(monitor);
      diskFullThresholdReached(directory, thresholdInBytes);
    }
    @Override
    public void diskFullThresholdReached(DiskSpaceMonitor monitor)
    public void diskFullThresholdReached(File directory, long thresholdInBytes)
    {
      isCanceled = true;
      logger.error(ERR_REBUILD_INDEX_LACK_DISK, monitor.getDirectory().getPath(),
              monitor.getFreeSpace(), monitor.getLowThreshold());
      logger.error(ERR_REBUILD_INDEX_LACK_DISK, directory.getAbsolutePath(), thresholdInBytes);
    }
    @Override
    public void diskSpaceRestored(DiskSpaceMonitor monitor)
    public void diskSpaceRestored(File directory, long lowThresholdInBytes, long fullThresholdInBytes)
    {
      // Do nothing
    }
@@ -4406,25 +4394,25 @@
  /** {@inheritDoc} */
  @Override
  public void diskLowThresholdReached(DiskSpaceMonitor monitor)
  public void diskLowThresholdReached(File directory, long thresholdInBytes)
  {
    diskFullThresholdReached(monitor);
    diskFullThresholdReached(directory, thresholdInBytes);
  }
  /** {@inheritDoc} */
  @Override
  public void diskFullThresholdReached(DiskSpaceMonitor monitor)
  public void diskFullThresholdReached(File directory, long thresholdInBytes)
  {
    isCanceled = true;
    Arg3<Object, Number, Number> argMsg = !isPhaseOneDone
    Arg2<Object, Number> argMsg = !isPhaseOneDone
        ? ERR_IMPORT_LDIF_LACK_DISK_PHASE_ONE
        : ERR_IMPORT_LDIF_LACK_DISK_PHASE_TWO;
    logger.error(argMsg.get(monitor.getDirectory().getPath(), monitor.getFreeSpace(), monitor.getLowThreshold()));
    logger.error(argMsg.get(directory.getAbsolutePath(), thresholdInBytes));
  }
  /** {@inheritDoc} */
  @Override
  public void diskSpaceRestored(DiskSpaceMonitor monitor)
  public void diskSpaceRestored(File directory, long lowThresholdInBytes, long fullThresholdInBytes)
  {
    // Do nothing.
  }
opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PersistItStorage.java
@@ -27,7 +27,6 @@
import static com.persistit.Transaction.CommitPolicy.*;
import static java.util.Arrays.*;
import static org.opends.messages.BackendMessages.*;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.messages.JebMessages.*;
@@ -37,10 +36,8 @@
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;
@@ -50,7 +47,6 @@
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.api.AlertGenerator;
import org.opends.server.api.DiskSpaceMonitorHandler;
import org.opends.server.backends.pluggable.spi.Cursor;
import org.opends.server.backends.pluggable.spi.Importer;
@@ -66,7 +62,6 @@
import org.opends.server.core.MemoryQuota;
import org.opends.server.core.ServerContext;
import org.opends.server.extensions.DiskSpaceMonitor;
import org.opends.server.types.DN;
import org.opends.server.types.FilePermission;
import com.persistit.Configuration;
@@ -86,7 +81,7 @@
/** PersistIt database implementation of the {@link Storage} engine. */
@SuppressWarnings("javadoc")
public final class PersistItStorage implements Storage, ConfigurationChangeListener<PersistitBackendCfg>,
  DiskSpaceMonitorHandler, AlertGenerator
  DiskSpaceMonitorHandler
{
  private static final String VOLUME_NAME = "dj";
  /** The buffer / page size used by the PersistIt storage. */
@@ -505,6 +500,7 @@
  private PersistitBackendCfg config;
  private DiskSpaceMonitor diskMonitor;
  private MemoryQuota memQuota;
  private StorageStatus storageStatus = StorageStatus.working();
  /**
   * Creates a new persistit storage with the provided configuration.
@@ -528,6 +524,7 @@
    final BufferPoolConfiguration bufferPoolCfg = getBufferPoolCfg();
    bufferPoolCfg.setMaximumCount(Integer.MAX_VALUE);
    diskMonitor = serverContext.getDiskSpaceMonitor();
    memQuota = serverContext.getMemoryQuota();
    if (cfg.getDBCacheSize() > 0)
    {
@@ -568,8 +565,7 @@
      memQuota.releaseMemory(memQuota.memPercentToBytes(config.getDBCachePercent()));
    }
    config.removePersistitChangeListener(this);
    DirectoryServer.deregisterMonitorProvider(diskMonitor);
    DirectoryServer.deregisterAlertGenerator(this);
    diskMonitor.deregisterMonitoredDirectory(getDirectory(), this);
  }
  private BufferPoolConfiguration getBufferPoolCfg()
@@ -598,11 +594,12 @@
    {
      throw new StorageRuntimeException(e);
    }
    // Register as disk space monitor handler
    diskMonitor = newDiskMonitor(config);
    DirectoryServer.registerMonitorProvider(diskMonitor);
    //Register as an AlertGenerator.
    DirectoryServer.registerAlertGenerator(this);
    diskMonitor.registerMonitoredDirectory(
        config.getBackendId() + " backend",
        getDirectory(),
        config.getDiskLowThreshold(),
        config.getDiskFullThreshold(),
        this);
  }
  /** {@inheritDoc} */
@@ -968,9 +965,12 @@
        setDBDirPermissions(cfg, newBackendDirectory);
      }
      diskMonitor.setFullThreshold(cfg.getDiskFullThreshold());
      diskMonitor.setLowThreshold(cfg.getDiskLowThreshold());
      diskMonitor.registerMonitoredDirectory(
        config.getBackendId() + " backend",
        getDirectory(),
        cfg.getDiskLowThreshold(),
        cfg.getDiskFullThreshold(),
        this);
      config = cfg;
    }
    catch (Exception e)
@@ -1037,74 +1037,29 @@
  @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();
    return storageStatus;
  }
  /** {@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);
  public void diskFullThresholdReached(File directory, long thresholdInBytes) {
    storageStatus = StorageStatus.unusable(
        WARN_DISK_SPACE_FULL_THRESHOLD_CROSSED.get(directory.getFreeSpace(), directory.getAbsolutePath(),
        thresholdInBytes, config.getBackendId()));
  }
  /** {@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);
  public void diskLowThresholdReached(File directory, long thresholdInBytes) {
    storageStatus = StorageStatus.lockedDown(
        WARN_DISK_SPACE_LOW_THRESHOLD_CROSSED.get(directory.getFreeSpace(), directory.getAbsolutePath(),
        thresholdInBytes, config.getBackendId()));
  }
  /** {@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;
  public void diskSpaceRestored(File directory, long lowThresholdInBytes, long fullThresholdInBytes) {
    storageStatus = StorageStatus.working();
  }
}
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/Importer.java
@@ -84,7 +84,6 @@
import java.util.concurrent.atomic.AtomicLong;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageDescriptor.Arg3;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.ByteSequence;
@@ -98,7 +97,6 @@
import org.opends.server.admin.std.server.BackendIndexCfg;
import org.opends.server.admin.std.server.PersistitBackendCfg;
import org.opends.server.admin.std.server.PluggableBackendCfg;
import org.opends.server.api.DiskSpaceMonitorHandler;
import org.opends.server.backends.RebuildConfig;
import org.opends.server.backends.RebuildConfig.RebuildMode;
import org.opends.server.backends.persistit.PersistItStorage;
@@ -114,7 +112,6 @@
import org.opends.server.backends.pluggable.spi.WriteableTransaction;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ServerContext;
import org.opends.server.extensions.DiskSpaceMonitor;
import org.opends.server.types.AttributeType;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
@@ -128,7 +125,7 @@
 * This class provides the engine that performs both importing of LDIF files and
 * the rebuilding of indexes.
 */
final class Importer implements DiskSpaceMonitorHandler
final class Importer
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
@@ -2781,7 +2778,7 @@
  /**
   * The rebuild index manager handles all rebuild index related processing.
   */
  private class RebuildIndexManager extends ImportTask implements DiskSpaceMonitorHandler
  private class RebuildIndexManager extends ImportTask
  {
    /** Rebuild index configuration. */
@@ -3402,26 +3399,6 @@
    {
      return this.totalEntries;
    }
    @Override
    public void diskLowThresholdReached(DiskSpaceMonitor monitor)
    {
      diskFullThresholdReached(monitor);
    }
    @Override
    public void diskFullThresholdReached(DiskSpaceMonitor monitor)
    {
      isCanceled = true;
      logger.error(ERR_REBUILD_INDEX_LACK_DISK, monitor.getDirectory().getPath(),
              monitor.getFreeSpace(), monitor.getLowThreshold());
    }
    @Override
    public void diskSpaceRestored(DiskSpaceMonitor monitor)
    {
      // Do nothing
    }
  }
  /**
@@ -4035,29 +4012,4 @@
      }
    }
  }
  /** {@inheritDoc} */
  @Override
  public void diskLowThresholdReached(DiskSpaceMonitor monitor)
  {
    diskFullThresholdReached(monitor);
  }
  /** {@inheritDoc} */
  @Override
  public void diskFullThresholdReached(DiskSpaceMonitor monitor)
  {
    isCanceled = true;
    Arg3<Object, Number, Number> argMsg = !isPhaseOneDone
        ? ERR_IMPORT_LDIF_LACK_DISK_PHASE_ONE
        : ERR_IMPORT_LDIF_LACK_DISK_PHASE_TWO;
    logger.error(argMsg.get(monitor.getDirectory().getPath(), monitor.getFreeSpace(), monitor.getLowThreshold()));
  }
  /** {@inheritDoc} */
  @Override
  public void diskSpaceRestored(DiskSpaceMonitor monitor)
  {
    // Do nothing.
  }
}
opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
@@ -136,6 +136,7 @@
import org.opends.server.crypto.CryptoManagerImpl;
import org.opends.server.crypto.CryptoManagerSync;
import org.opends.server.extensions.ConfigFileHandler;
import org.opends.server.extensions.DiskSpaceMonitor;
import org.opends.server.extensions.JMXAlertHandler;
import org.opends.server.loggers.AccessLogger;
import org.opends.server.loggers.DebugLogPublisher;
@@ -797,6 +798,9 @@
  /** The memory reservation system */
  private MemoryQuota memoryQuota;
  /** The Disk Space Monitor */
  private DiskSpaceMonitor diskSpaceMonitor;
  /**
   * The maximum size that internal buffers will be allowed to grow to until
   * they are trimmed.
@@ -898,6 +902,12 @@
    {
      return directoryServer.memoryQuota;
    }
    @Override
    public DiskSpaceMonitor getDiskSpaceMonitor()
    {
      return directoryServer.diskSpaceMonitor;
    }
  }
@@ -930,6 +940,7 @@
    serverContext = new DirectoryServerContext();
    virtualAttributeConfigManager = new VirtualAttributeConfigManager(serverContext);
    memoryQuota = new MemoryQuota();
    diskSpaceMonitor = new DiskSpaceMonitor();
  }
@@ -1470,6 +1481,8 @@
      // Determine whether or not we should start the connection handlers.
      boolean startConnectionHandlers = !environmentConfig.disableConnectionHandlers();
      diskSpaceMonitor.startDiskSpaceMonitor();
      initializeSchema();
      // Allow internal plugins to be registered.
opendj-server-legacy/src/main/java/org/opends/server/core/ServerContext.java
@@ -26,6 +26,7 @@
package org.opends.server.core;
import org.forgerock.opendj.config.server.ServerManagementContext;
import org.opends.server.extensions.DiskSpaceMonitor;
import org.opends.server.schema.SchemaUpdater;
import org.opends.server.types.DirectoryEnvironmentConfig;
import org.opends.server.types.Schema;
@@ -86,4 +87,13 @@
   * @return the memory quota system
   */
  MemoryQuota getMemoryQuota();
  /**
   * Returns the Disk Space Monitoring service, for checking free disk space.
   * Configure a directory to be monitored and optionally get alerted when
   * disk space transitions from low to full to back to normal.
   *
   * @return the Disk Space Monioring service
   */
  DiskSpaceMonitor getDiskSpaceMonitor();
}
opendj-server-legacy/src/main/java/org/opends/server/extensions/DiskSpaceMonitor.java
@@ -28,23 +28,39 @@
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.core.DirectoryServer.*;
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 java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.admin.std.server.MonitorProviderCfg;
import org.opends.server.api.AlertGenerator;
import org.opends.server.api.AttributeSyntax;
import org.opends.server.api.DiskSpaceMonitorHandler;
import org.opends.server.api.MonitorProvider;
import org.opends.server.api.ServerShutdownListener;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.Attributes;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.InitializationException;
import org.opends.server.util.Platform;
/**
 * This class provides an application-wide disk space monitoring service.
@@ -56,243 +72,428 @@
 * have been reached, the handler will not be notified again until the
 * free space raises above the "low" threshold.
 */
public class DiskSpaceMonitor extends MonitorProvider<MonitorProviderCfg>
    implements Runnable
public class DiskSpaceMonitor extends MonitorProvider<MonitorProviderCfg> implements Runnable, AlertGenerator,
    ServerShutdownListener
{
  /**
   * Helper class for each requestor for use with cn=monitor reporting and users of a spcific mountpoint.
   */
  private class MonitoredDirectory extends MonitorProvider<MonitorProviderCfg>
  {
    private volatile File directory;
    private volatile long lowThreshold;
    private volatile long fullThreshold;
    private final DiskSpaceMonitorHandler handler;
    private final String instanceName;
    private final String baseName;
    private int lastState;
    private MonitoredDirectory(File directory, String instanceName, String baseName, DiskSpaceMonitorHandler handler)
    {
      this.directory = directory;
      this.instanceName = instanceName;
      this.baseName = baseName;
      this.handler = handler;
    }
    /** {@inheritDoc} */
    @Override
    public String getMonitorInstanceName() {
      return instanceName + "," + "cn=" + baseName;
    }
    /** {@inheritDoc} */
    @Override
    public void initializeMonitorProvider(MonitorProviderCfg configuration)
        throws ConfigException, InitializationException {
    }
    /** {@inheritDoc} */
    @Override
    public List<Attribute> getMonitorData() {
      final List<Attribute> monitorAttrs = new ArrayList<Attribute>();
      monitorAttrs.add(attr("disk-dir", getDefaultStringSyntax(), directory.getPath()));
      monitorAttrs.add(attr("disk-free", getDefaultIntegerSyntax(), getFreeSpace()));
      monitorAttrs.add(attr("disk-state", getDefaultStringSyntax(), getState()));
      return monitorAttrs;
    }
    private File getDirectory() {
      return directory;
    }
    private long getFreeSpace() {
      return directory.getUsableSpace();
    }
    private long getFullThreshold() {
      return fullThreshold;
    }
    private long getLowThreshold() {
      return lowThreshold;
    }
    private void setFullThreshold(long fullThreshold) {
      this.fullThreshold = fullThreshold;
    }
    private void setLowThreshold(long lowThreshold) {
      this.lowThreshold = lowThreshold;
    }
    private Attribute attr(String name, AttributeSyntax<?> syntax, Object value)
    {
      AttributeType attrType = DirectoryServer.getDefaultAttributeType(name, syntax);
      return Attributes.create(attrType, String.valueOf(value));
    }
    private String getState()
    {
      switch(lastState)
      {
      case NORMAL:
        return "normal";
      case LOW:
        return "low";
      case FULL:
        return "full";
      default:
        return null;
      }
    }
  }
  /**
   * Helper class for building temporary list of handlers to notify on threshold hits.
   * One object per directory per state will hold all the handlers matching directory and state.
   */
  private class HandlerNotifier {
    private File directory;
    private int state;
    /** printable list of handlers names, for reporting backend names in alert messages */
    private final StringBuilder diskNames = new StringBuilder();
    private final List<MonitoredDirectory> allHandlers = new ArrayList<MonitoredDirectory>();
    private HandlerNotifier(File directory, int state)
    {
      this.directory = directory;
      this.state = state;
    }
    private void notifyHandlers()
    {
      for (MonitoredDirectory mdElem : allHandlers)
      {
        switch (state)
        {
        case FULL:
          mdElem.handler.diskFullThresholdReached(mdElem.getDirectory(), mdElem.getFullThreshold());
          break;
        case LOW:
          mdElem.handler.diskLowThresholdReached(mdElem.getDirectory(), mdElem.getLowThreshold());
          break;
        case NORMAL:
          mdElem.handler.diskSpaceRestored(mdElem.getDirectory(), mdElem.getLowThreshold(),
              mdElem.getFullThreshold());
          break;
        }
      }
    }
    private boolean isEmpty()
    {
      return allHandlers.size() == 0;
    }
    private void addHandler(MonitoredDirectory handler)
    {
      logger.trace("State change: %d -> %d", handler.lastState, state);
      handler.lastState = state;
      if (handler.handler != null)
      {
        allHandlers.add(handler);
      }
      appendName(diskNames, handler.instanceName);
    }
    private void appendName(StringBuilder strNames, String strVal)
    {
      if (strNames.length() > 0)
      {
        strNames.append(", ");
      }
      strNames.append(strVal);
    }
  }
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  private static final int NORMAL = 0;
  private static final int LOW = 1;
  private static final int FULL = 2;
  private volatile File directory;
  private volatile long lowThreshold;
  private volatile long fullThreshold;
  private final DiskSpaceMonitorHandler handler;
  private final int interval;
  private final TimeUnit unit;
  private final String instanceName;
  private int lastState;
  private static final String INSTANCENAME = "Disk Space Monitor";
  private final HashMap<File, List<MonitoredDirectory>> monitoredDirs =
      new HashMap<File, List<MonitoredDirectory>>();
  /**
   * Constructs a new DiskSpaceMonitor that will notify the specified
   * DiskSpaceMonitorHandler when the specified disk
   * falls below the provided thresholds.
   *
   * @param instanceName A unique name for this monitor.
   * @param directory The directory to monitor.
   * @param lowThreshold The "low" threshold.
   * @param fullThreshold   The "full" threshold.
   * @param interval  The polling interval for checking free space.
   * @param unit the time unit of the interval parameter.
   * @param handler The handler to get notified when the provided thresholds are
   *                reached or <code>null</code> if no notification is needed;
   * Constructs a new DiskSpaceMonitor that will notify registered DiskSpaceMonitorHandler objects when filesystems
   * on which configured directories reside, fall below the provided thresholds.
   */
  public DiskSpaceMonitor(String instanceName, File directory,
                          long lowThreshold,
                          long fullThreshold, int interval, TimeUnit unit,
                          DiskSpaceMonitorHandler handler) {
    this.directory = directory;
    this.lowThreshold = lowThreshold;
    this.fullThreshold = fullThreshold;
    this.interval = interval;
    this.unit = unit;
    this.handler = handler;
    this.instanceName = instanceName+",cn=Disk Space Monitor";
  }
  /**
   * Retrieves the directory currently being monitored.
   *
   * @return The directory currently being monitored.
   */
  public File getDirectory() {
    return directory;
  }
  /**
   * Sets the directory to monitor.
   *
   * @param directory The directory to monitor.
   */
  public void setDirectory(File directory) {
    this.directory = directory;
  }
  /**
   * Retrieves the currently "low" space threshold currently being enforced.
   *
   * @return The currently "low" space threshold currently being enforced.
   */
  public long getLowThreshold() {
    return lowThreshold;
  }
  /**
   * Sets the "low" space threshold to enforce.
   *
   * @param lowThreshold The "low" space threshold to enforce.
   */
  public void setLowThreshold(long lowThreshold) {
    this.lowThreshold = lowThreshold;
  }
  /**
   * Retrieves the currently full threshold currently being enforced.
   *
   * @return The currently full space threshold currently being enforced.
   */
  public long getFullThreshold() {
    return fullThreshold;
  }
  /**
   * Sets the full threshold to enforce.
   *
   * @param fullThreshold The full space threshold to enforce.
   */
  public void setFullThreshold(long fullThreshold) {
    this.fullThreshold = fullThreshold;
  }
  /**
   * Retrieves the free space currently on the disk.
   *
   * @return The free space currently on the disk.
   */
  public long getFreeSpace() {
    return directory.getUsableSpace();
  }
  /**
   * Indicates if the "full" threshold is reached.
   *
   * @return <code>true</code> if the free space is lower than the "full"
   *         threshold or <code>false</code> otherwise.
   */
  public boolean isFullThresholdReached()
  public DiskSpaceMonitor()
  {
    return lastState >= FULL;
  }
  /**
   * Indicates if the "low" threshold is reached.
   *
   * @return <code>true</code> if the free space is lower than the "low"
   *         threshold or <code>false</code> otherwise.
   * Starts periodic monitoring of all registered directories.
   */
  public boolean isLowThresholdReached()
  public void startDiskSpaceMonitor()
  {
    return lastState >= LOW;
    DirectoryServer.registerMonitorProvider(this);
    DirectoryServer.registerShutdownListener(this);
    scheduleUpdate(this, 0, 5, TimeUnit.SECONDS);
  }
  /**
   * Registers or reconfigures a directory for monitoring.
   * If possible, we will try to get and use the mountpoint where the directory resides and monitor it instead.
   * If the directory is already registered for the same <code>handler</code>, simply change its configuration.
   * @param instanceName A name for the handler, as used by cn=monitor
   * @param directory The directory to monitor
   * @param lowThresholdBytes Disk slow threshold expressed in bytes
   * @param fullThresholdBytes Disk full threshold expressed in bytes
   * @param handler The class requesting to be called when a transition in disk space occurs
   */
  public void registerMonitoredDirectory(String instanceName, File directory, long lowThresholdBytes,
      long fullThresholdBytes, DiskSpaceMonitorHandler handler)
  {
    File fsMountPoint;
    try
    {
      fsMountPoint = Platform.getFilesystem(directory);
    }
    catch (IOException ioe)
    {
      logger.warn(ERR_DISK_SPACE_GET_MOUNT_POINT, directory.getAbsolutePath(), ioe.getLocalizedMessage());
      fsMountPoint = directory;
    }
    MonitoredDirectory newDSH = new MonitoredDirectory(directory, instanceName, INSTANCENAME, handler);
    newDSH.setFullThreshold(fullThresholdBytes);
    newDSH.setLowThreshold(lowThresholdBytes);
    synchronized (monitoredDirs)
    {
      List<MonitoredDirectory> diskHelpers = monitoredDirs.get(fsMountPoint);
      if (diskHelpers == null)
      {
        List<MonitoredDirectory> newList = new ArrayList<MonitoredDirectory>();
        newList.add(newDSH);
        monitoredDirs.put(fsMountPoint, newList);
      }
      else
      {
        for (MonitoredDirectory elem : diskHelpers)
        {
          if (elem.handler.equals(handler) && elem.getDirectory().equals(directory))
          {
            elem.setFullThreshold(fullThresholdBytes);
            elem.setLowThreshold(lowThresholdBytes);
            return;
          }
        }
        diskHelpers.add(newDSH);
      }
      DirectoryServer.registerMonitorProvider(newDSH);
    }
  }
  /**
   * Removes a directory from the set of monitored directories.
   *
   * @param directory The directory to stop monitoring on
   * @param handler The class that requested monitoring
   */
  public void deregisterMonitoredDirectory(File directory, DiskSpaceMonitorHandler handler)
  {
    synchronized (monitoredDirs)
    {
      List<MonitoredDirectory> directories = monitoredDirs.get(directory);
      if (directories != null)
      {
        Iterator<MonitoredDirectory> itr = directories.iterator();
        while (itr.hasNext())
        {
          MonitoredDirectory curDirectory = itr.next();
          if (curDirectory.handler.equals(handler))
          {
            DirectoryServer.deregisterMonitorProvider(curDirectory);
            itr.remove();
          }
        }
        if (directories.isEmpty())
        {
          monitoredDirs.remove(directory);
        }
      }
    }
  }
  /** {@inheritDoc} */
  @Override
  public void initializeMonitorProvider(MonitorProviderCfg configuration)
      throws ConfigException, InitializationException {
    scheduleUpdate(this, 0, interval, unit);
    // Not used...
  }
  /** {@inheritDoc} */
  @Override
  public String getMonitorInstanceName() {
    return instanceName;
    return INSTANCENAME;
  }
  /** {@inheritDoc} */
  @Override
  public List<Attribute> getMonitorData() {
    final ArrayList<Attribute> monitorAttrs = new ArrayList<Attribute>();
    monitorAttrs.add(attr("disk-dir", getDefaultStringSyntax(), directory.getPath()));
    monitorAttrs.add(attr("disk-free", getDefaultIntegerSyntax(), getFreeSpace()));
    monitorAttrs.add(attr("disk-state", getDefaultStringSyntax(), getState()));
    return monitorAttrs;
    return new ArrayList<Attribute>();
  }
  private Attribute attr(String name, AttributeSyntax<?> syntax, Object value)
  /** {@inheritDoc} */
  @Override
  public void run()
  {
    AttributeType attrType = DirectoryServer.getDefaultAttributeType(name, syntax);
    return Attributes.create(attrType, String.valueOf(value));
  }
    List<HandlerNotifier> diskFull = new ArrayList<HandlerNotifier>();
    List<HandlerNotifier> diskLow = new ArrayList<HandlerNotifier>();
    List<HandlerNotifier> diskRestored = new ArrayList<HandlerNotifier>();
  private String getState()
  {
    switch(lastState)
    synchronized (monitoredDirs)
    {
    case NORMAL:
      return "normal";
    case LOW:
      return "low";
    case FULL:
      return "full";
    default:
      return null;
      for (Entry<File, List<MonitoredDirectory>> dirElem : monitoredDirs.entrySet())
      {
        File directory = dirElem.getKey();
        HandlerNotifier diskFullClients = new HandlerNotifier(directory, FULL);
        HandlerNotifier diskLowClients = new HandlerNotifier(directory, LOW);
        HandlerNotifier diskRestoredClients = new HandlerNotifier(directory, NORMAL);
        try
        {
          long lastFreeSpace = directory.getUsableSpace();
          for (MonitoredDirectory handlerElem : dirElem.getValue())
          {
            if (lastFreeSpace < handlerElem.getFullThreshold() && handlerElem.lastState < FULL)
            {
              diskFullClients.addHandler(handlerElem);
            }
            else if (lastFreeSpace < handlerElem.getLowThreshold() && handlerElem.lastState < LOW)
            {
              diskLowClients.addHandler(handlerElem);
            }
            else if (handlerElem.lastState != NORMAL)
            {
              diskRestoredClients.addHandler(handlerElem);
            }
          }
          addToList(diskFull, diskFullClients);
          addToList(diskLow, diskLowClients);
          addToList(diskRestored, diskRestoredClients);
        }
        catch(Exception e)
        {
          logger.error(ERR_DISK_SPACE_MONITOR_UPDATE_FAILED, directory, e);
          logger.traceException(e);
        }
      }
    }
    // It is probably better to notify handlers outside of the synchronized section.
    sendNotification(diskFull, FULL, ALERT_DESCRIPTION_DISK_FULL);
    sendNotification(diskLow, LOW, ALERT_TYPE_DISK_SPACE_LOW);
    sendNotification(diskRestored, NORMAL, null);
  }
  private void addToList(List<HandlerNotifier> hnList, HandlerNotifier notifier)
  {
    if (!notifier.isEmpty())
    {
      hnList.add(notifier);
    }
  }
  private void sendNotification(List<HandlerNotifier> diskList, int state, String alert)
  {
    for (HandlerNotifier dirElem : diskList)
    {
      String dirPath = dirElem.directory.getAbsolutePath();
      String handlerNames = dirElem.diskNames.toString();
      long freeSpace = dirElem.directory.getFreeSpace();
      if (state == FULL)
      {
        DirectoryServer.sendAlertNotification(this, alert,
            ERR_DISK_SPACE_FULL_THRESHOLD_REACHED.get(dirPath, handlerNames, freeSpace));
      }
      else if (state == LOW)
      {
        DirectoryServer.sendAlertNotification(this, alert,
            ERR_DISK_SPACE_LOW_THRESHOLD_REACHED.get(dirPath, handlerNames, freeSpace));
      }
      else
      {
        logger.error(NOTE_DISK_SPACE_RESTORED.get(freeSpace, dirPath));
      }
      dirElem.notifyHandlers();
    }
  }
  /** {@inheritDoc} */
  @Override
  public void run() {
  public DN getComponentEntryDN()
  {
    try
    {
      long lastFreeSpace = directory.getUsableSpace();
      if(logger.isTraceEnabled())
      {
        logger.trace("Free space for %s: %d, " +
            "low threshold: %d, full threshold: %d, state: %d",
            directory.getPath(), lastFreeSpace, lowThreshold, fullThreshold,
            lastState);
      }
      if(lastFreeSpace < fullThreshold)
      {
        if (lastState < FULL)
        {
          if(logger.isTraceEnabled())
          {
            logger.trace("State change: %d -> %d", lastState, FULL);
          }
          lastState = FULL;
          if(handler != null)
          {
            handler.diskFullThresholdReached(this);
          }
        }
      }
      else if(lastFreeSpace < lowThreshold)
      {
        if (lastState < LOW)
        {
          if(logger.isTraceEnabled())
          {
            logger.trace("State change: %d -> %d", lastState, LOW);
          }
          lastState = LOW;
          if(handler != null)
          {
            handler.diskLowThresholdReached(this);
          }
        }
      }
      else if (lastState != NORMAL)
      {
        if(logger.isTraceEnabled())
        {
          logger.trace("State change: %d -> %d", lastState, NORMAL);
        }
        lastState = NORMAL;
        if(handler != null)
        {
          handler.diskSpaceRestored(this);
        }
      }
      return DN.valueOf(INSTANCENAME);
    }
    catch(Exception e)
    catch (DirectoryException de)
    {
      logger.error(ERR_DISK_SPACE_MONITOR_UPDATE_FAILED, directory.getPath(), e);
      logger.traceException(e);
      return DN.NULL_DN;
    }
  }
  /** {@inheritDoc} */
  @Override
  public String getClassName()
  {
    return DiskSpaceMonitor.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;
  }
  /** {@inheritDoc} */
  @Override
  public String getShutdownListenerName()
  {
    return INSTANCENAME;
  }
  /** {@inheritDoc} */
  @Override
  public void processServerShutdown(LocalizableMessage reason)
  {
    synchronized (monitoredDirs)
    {
      for (Entry<File, List<MonitoredDirectory>> dirElem : monitoredDirs.entrySet())
      {
        for (MonitoredDirectory handlerElem : dirElem.getValue())
        {
          DirectoryServer.deregisterMonitorProvider(handlerElem);
        }
      }
    }
    DirectoryServer.deregisterMonitorProvider(this);
  }
}
opendj-server-legacy/src/main/java/org/opends/server/util/Platform.java
@@ -36,15 +36,20 @@
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.List;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import static org.opends.messages.UtilityMessages.*;
@@ -108,8 +113,29 @@
    /** Constructors for each of the above classes. */
    private static Constructor<?> certKeyGenCons, X500NameCons;
    /** Filesystem APIs */
    private static Method FILESYSTEMS_GETSTORES;
    private static Method FILESYSTEMS_PATHSGET;
    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
    static
    {
      try
      {
        // FileSystem and FileStores APIs were introduced in JDK 7.
        FILESYSTEMS_PATHSGET = Class.forName("java.nio.file.Paths").getMethod("get", String.class, String[].class);
        FILESYSTEMS_GETSTORES = Class.forName("java.nio.file.Files").getMethod(
            "getFileStore", Class.forName("java.nio.file.Path"));
      }
      catch (Exception e)
      {
        FILESYSTEMS_GETSTORES = null;
        FILESYSTEMS_PATHSGET = null;
        logger.warn(WARN_UNABLE_TO_USE_FILESYSTEM_API.get());
      }
      String x509pkg = pkgPrefix + ".x509";
      String certAndKeyGen;
      if (pkgPrefix.equals(IBM_SEC)
@@ -155,21 +181,7 @@
    /**
     * Delete the specified alias from the specified keystore.
     *
     * @param ks
     *          The keystore to delete the alias from.
     * @param ksPath
     *          The path to the keystore.
     * @param alias
     *          The alias to use in the request generation.
     * @param pwd
     *          The keystore password to use.
     * @throws KeyStoreException
     *           If an error occurred deleting the alias.
     */
    public final void deleteAlias(KeyStore ks, String ksPath, String alias,
    private final void deleteAlias(KeyStore ks, String ksPath, String alias,
        char[] pwd) throws KeyStoreException
    {
      try
@@ -193,28 +205,7 @@
    /**
     * Add the certificate in the specified path to the specified keystore,
     * creating the keystore using the specified type and path if it the
     * keystore doesn't exist.
     *
     * @param ks
     *          The keystore to add the certificate to, may be null if it
     *          doesn't exist.
     * @param ksType
     *          The type to use if the keystore is created.
     * @param ksPath
     *          The path to the keystore if it is created.
     * @param alias
     *          The alias to store the certificate under.
     * @param pwd
     *          The password to use in saving the certificate.
     * @param certPath
     *          The path to the file containing the certificate.
     * @throws KeyStoreException
     *           If an error occurred adding the certificate to the keystore.
     */
    public final void addCertificate(KeyStore ks, String ksType, String ksPath,
    private final void addCertificate(KeyStore ks, String ksType, String ksPath,
        String alias, char[] pwd, String certPath) throws KeyStoreException
    {
      try
@@ -255,31 +246,7 @@
    /**
     * Generate a self-signed certificate using the specified alias, dn string
     * and validity period. If the keystore does not exist, create it using the
     * specified type and path.
     *
     * @param ks
     *          The keystore to save the certificate in. May be null if it does
     *          not exist.
     * @param ksType
     *          The keystore type to use if the keystore is created.
     * @param ksPath
     *          The path to the keystore if the keystore is created.
     * @param alias
     *          The alias to store the certificate under.
     * @param pwd
     *          The password to us in saving the certificate.
     * @param dn
     *          The dn string used as the certificate subject.
     * @param validity
     *          The validity of the certificate in days.
     * @return The keystore that the self-signed certificate was stored in.
     * @throws KeyStoreException
     *           If the self-signed certificate cannot be generated.
     */
    public final KeyStore generateSelfSignedCertificate(KeyStore ks,
    private final KeyStore generateSelfSignedCertificate(KeyStore ks,
        String ksType, String ksPath, String alias, char[] pwd, String dn,
        int validity) throws KeyStoreException
    {
@@ -330,19 +297,6 @@
    /**
     * Generate a x509 certificate from the input stream. Verification is done
     * only if it is self-signed.
     *
     * @param alias
     *          The alias to save the certificate under.
     * @param cf
     *          The x509 certificate factory.
     * @param ks
     *          The keystore to add the certificate in.
     * @param in
     *          The input stream to read the certificate from.
     * @throws KeyStoreException
     *           If the alias exists already in the keystore, if the self-signed
     *           certificate didn't verify, or the certificate could not be
     *           stored.
     */
    private void trustedCert(String alias, CertificateFactory cf, KeyStore ks,
        InputStream in) throws KeyStoreException
@@ -369,10 +323,6 @@
    /**
     * Check that the issuer and subject DNs match.
     *
     * @param cert
     *          The certificate to examine.
     * @return {@code true} if the certificate is self-signed.
     */
    private boolean isSelfSigned(X509Certificate cert)
    {
@@ -391,14 +341,7 @@
    /**
     * Calculates the usable memory which could potentially be used by the
     * application for caching objects.
     *
     * @return The usable memory which could potentially be used by the
     *         application for caching objects.
     */
    public long getUsableMemoryForCaching()
    private long getUsableMemoryForCaching()
    {
      long youngGenSize = 0;
      long oldGenSize = 0;
@@ -454,6 +397,40 @@
            .totalMemory())) * 40 / 100;
      }
    }
    private File getFilesystem(File directory) throws IOException
    {
      if (FILESYSTEMS_GETSTORES != null)
      {
        try
        {
          Object dirFStore = FILESYSTEMS_GETSTORES.invoke(null,
              FILESYSTEMS_PATHSGET.invoke(null, directory.getAbsolutePath(), new String[0]));
          File parentDir = directory.getParentFile();
          /*
           * Since there is no concept of mount point in the APIs, iterate on all parents of
           * the given directory until the FileSystem Store changes (hint of a different
           * device, hence a mount point) or we get to root, which works too.
           */
          while (parentDir != null)
          {
            Object parentFStore = FILESYSTEMS_GETSTORES.invoke(null,
                FILESYSTEMS_PATHSGET.invoke(null, parentDir.getAbsolutePath(), new String[0]));
            if (!parentFStore.equals(dirFStore))
            {
              return directory;
            }
            directory = directory.getParentFile();
            parentDir = directory.getParentFile();
          }
        }
        catch (Exception e)
        {
          throw new IOException(e);
        }
      }
      return directory;
    }
  }
@@ -648,4 +625,16 @@
  {
    return IMPL.getUsableMemoryForCaching();
  }
  /**
   * Returns the filesystem on which the given directory resides by its mountpoint.
   *
   * @param directory the directory whose filesystem is required
   * @return the filesystem on which the given directory resides
   * @throws IOException The exception in case information on filesystem/storage cannot be found
   */
  public static File getFilesystem(File directory) throws IOException
  {
    return IMPL.getFilesystem(directory);
  }
}
opendj-server-legacy/src/messages/org/opends/messages/backend.properties
@@ -1028,4 +1028,10 @@
 %d%% available
ERR_VLV_BAD_ASSERTION_430=Unable to process the virtual list view request \
 because the target assertion could not be decoded as a valid value for the \
 '%s' attribute type
 '%s' attribute type
WARN_DISK_SPACE_LOW_THRESHOLD_CROSSED_431=Disk free space of %d bytes for directory %s is now below low threshold of \
 %d bytes. Backend %s is now locked down and will no longer accept any operations from clients until sufficient disk \
 space is restored
WARN_DISK_SPACE_FULL_THRESHOLD_CROSSED_432=Disk free space of %d bytes for directory %s is now below disk low \
 threshold of %d bytes. Backend %s is now offline and will no longer accept any operations until sufficient \
 disk space is restored
opendj-server-legacy/src/messages/org/opends/messages/core.properties
@@ -1429,4 +1429,12 @@
ERR_PWPOLICY_REJECT_DUE_TO_UNKNOWN_VALIDATOR_LOG_747=The password for \
 user %s could not be validated because the password policy subentry %s is \
 referring to an unknown password validator (%s). Please make sure the password \
 policy subentry only refers to validators that exist on all replicas
 policy subentry only refers to validators that exist on all replicas
ERR_DISK_SPACE_GET_MOUNT_POINT_748=Could not get filesystem for directory %s: %s
ERR_DISK_SPACE_LOW_THRESHOLD_REACHED_749=The disk containing directory %s used by %s is low on free space \
 (%d bytes free). Write operations are only permitted by a user with the BYPASS_LOCKDOWN privilege until the free \
 space rises above the threshold. Replication updates are still allowed
ERR_DISK_SPACE_FULL_THRESHOLD_REACHED_750=The disk containing directory %s used by %s is full (%d bytes free). \
 Write operations to the backend, replication updates included, will fail until the free space rises above the threshold
NOTE_DISK_SPACE_RESTORED_751=The free space (%d bytes) on the disk containing directory %s is now above the \
 threshold
opendj-server-legacy/src/messages/org/opends/messages/jeb.properties
@@ -374,14 +374,14 @@
 perform import phase 2 in a single batch. Some indexes will be imported using \
 several batches which may result in reduced performance
ERR_IMPORT_LDIF_LACK_DISK_PHASE_ONE_222=The disk containing directory \
 %s is full (%d bytes free). After freeing more than %d bytes on the disk, \
 %s is full. After freeing more than %d bytes on the disk, \
 import can continue in append and replace mode to load the rest of the \
 entries
ERR_IMPORT_LDIF_LACK_DISK_PHASE_TWO_223=The disk containing directory \
 %s is full (%d bytes free). After freeing more than %d bytes on the disk, \
 %s is full. After freeing more than %d bytes on the disk, \
 a rebuild of all the indexes is needed to complete the import
ERR_REBUILD_INDEX_LACK_DISK_224=The disk containing directory \
 %s is full (%d bytes free). Rebuild index can not continue until the free \
 %s is full. Rebuild index can not continue until the free \
 space rises above the threshold (%d bytes)
INFO_JEB_INDEX_FILTER_INDEX_TYPE_DISABLED_225=%s index type is disabled for \
 the %s attribute
opendj-server-legacy/src/messages/org/opends/messages/utility.properties
@@ -571,4 +571,5 @@
was received when reading its attributes: %s
ERR_LDAP_CONN_BAD_INTEGER_302=Invalid integer number "%s". Please \
 enter a valid integer
ERR_ARG_SUBCOMMAND_INVALID_303=Invalid subcommand
ERR_ARG_SUBCOMMAND_INVALID_303=Invalid subcommand
WARN_UNABLE_TO_USE_FILESYSTEM_API_304=Unable to gather information on Filesystem APIs, disk monitoring will be verbose
opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/StateTest.java
@@ -47,6 +47,7 @@
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.MemoryQuota;
import org.opends.server.core.ServerContext;
import org.opends.server.extensions.DiskSpaceMonitor;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.testng.annotations.AfterMethod;
@@ -76,6 +77,7 @@
    ServerContext serverContext = mock(ServerContext.class);
    when(serverContext.getMemoryQuota()).thenReturn(new MemoryQuota());
    when(serverContext.getDiskSpaceMonitor()).thenReturn(new DiskSpaceMonitor());
    storage = new PersistItStorage(createBackendCfg(), serverContext);
    org.opends.server.backends.pluggable.spi.Importer importer = storage.startImport();