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

Jean-Noël Rouvignac
07.58.2015 933f25dffbc74ce15a0b1b680af6f441d9dcf4c5
JEStorage.java, PDBStorage.java: Removed duplicated code and extracted it in StorageUtils.java
1 files added
2 files modified
650 ■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java 201 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pdb/PDBStorage.java 199 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/spi/StorageUtils.java 250 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java
@@ -30,8 +30,8 @@
import static org.forgerock.util.Utils.*;
import static org.opends.messages.BackendMessages.*;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.messages.UtilityMessages.*;
import static org.opends.server.backends.pluggable.spi.StorageUtils.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.File;
@@ -73,6 +73,7 @@
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.StorageUtils;
import org.opends.server.backends.pluggable.spi.TreeName;
import org.opends.server.backends.pluggable.spi.UpdateFunction;
import org.opends.server.backends.pluggable.spi.WriteOperation;
@@ -84,7 +85,6 @@
import org.opends.server.types.BackupConfig;
import org.opends.server.types.BackupDirectory;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.FilePermission;
import org.opends.server.types.RestoreConfig;
import org.opends.server.util.BackupManager;
@@ -594,7 +594,7 @@
  JEStorage(final JEBackendCfg cfg, ServerContext serverContext) throws ConfigException
  {
    this.serverContext = serverContext;
    backendDirectory = new File(getFileForPath(cfg.getDBDirectory()), cfg.getBackendId());
    backendDirectory = getBackendDirectory(cfg);
    config = cfg;
    cfg.addJEChangeListener(this);
  }
@@ -706,7 +706,7 @@
  private void open0() throws ConfigException
  {
    setupStorageFiles();
    setupStorageFiles(backendDirectory, config.getDBDirectoryPermissions(), config.dn());
    try
    {
      if (env != null)
@@ -722,12 +722,7 @@
    {
      throw new StorageRuntimeException(e);
    }
    diskMonitor.registerMonitoredDirectory(
        config.getBackendId() + " backend",
        getDirectory(),
        config.getDiskLowThreshold(),
        config.getDiskFullThreshold(),
        this);
    registerMonitoredDirectory(config);
  }
  @Override
@@ -840,8 +835,12 @@
  @Override
  public File getDirectory()
  {
    File parentDir = getFileForPath(config.getDBDirectory());
    return new File(parentDir, config.getBackendId());
    return getBackendDirectory(config);
  }
  private static File getBackendDirectory(JEBackendCfg cfg)
  {
    return getDBDirectory(cfg.getDBDirectory(), cfg.getBackendId());
  }
  @Override
@@ -988,6 +987,8 @@
    /**
     * Creates the filter for log files that are newer than provided file name
     * or equal to provided file name and of larger size.
     * @param latestFilename the latest file name
     * @param latestFileSize the latest file size
     */
    JELogFileFilter(String latestFilename, long latestFileSize)
    {
@@ -1097,7 +1098,7 @@
   * Checks newly created backend has a valid configuration.
   * @param cfg the new configuration
   * @param unacceptableReasons the list of accumulated errors and their messages
   * @param context TODO
   * @param context the server context
   * @return true if newly created backend has a valid configuration
   */
  static boolean isConfigurationAcceptable(JEBackendCfg cfg, List<LocalizableMessage> unacceptableReasons,
@@ -1126,11 +1127,10 @@
    List<LocalizableMessage> unacceptableReasons)
  {
    final ConfigChangeResult ccr = new ConfigChangeResult();
    File parentDirectory = getFileForPath(cfg.getDBDirectory());
    File newBackendDirectory = new File(parentDirectory, cfg.getBackendId());
    File newBackendDirectory = getBackendDirectory(cfg);
    checkDBDirExistsOrCanCreate(newBackendDirectory, ccr, true);
    checkDBDirPermissions(cfg, ccr);
    checkDBDirPermissions(cfg.getDBDirectoryPermissions(), cfg.dn(), ccr);
    if (!ccr.getMessages().isEmpty())
    {
      unacceptableReasons.addAll(ccr.getMessages());
@@ -1139,96 +1139,6 @@
    return true;
  }
  /**
   * Checks a directory exists or can actually be created.
   *
   * @param backendDir the directory to check for
   * @param ccr the list of reasons to return upstream or null if called from setupStorage()
   * @param cleanup true if the directory should be deleted after creation
   */
  private static void checkDBDirExistsOrCanCreate(File backendDir, ConfigChangeResult ccr, boolean cleanup)
  {
    if (!backendDir.exists())
    {
      if(!backendDir.mkdirs())
      {
        addErrorMessage(ccr, ERR_CREATE_FAIL.get(backendDir.getPath()));
      }
      if (cleanup)
      {
        backendDir.delete();
      }
    }
    else if (!backendDir.isDirectory())
    {
      addErrorMessage(ccr, ERR_DIRECTORY_INVALID.get(backendDir.getPath()));
    }
  }
  /**
   * Returns false if directory permissions in the configuration are invalid. Otherwise returns the
   * same value as it was passed in.
   *
   * @param cfg a (possibly new) backend configuration
   * @param ccr the current list of change results
   * @throws forwards a file exception
   */
  private static void checkDBDirPermissions(JEBackendCfg cfg, ConfigChangeResult ccr)
  {
    try
    {
      FilePermission backendPermission = decodeDBDirPermissions(cfg);
      // Make sure the mode will allow the server itself access to the database
      if(!backendPermission.isOwnerWritable() ||
          !backendPermission.isOwnerReadable() ||
          !backendPermission.isOwnerExecutable())
      {
        addErrorMessage(ccr, ERR_CONFIG_BACKEND_INSANE_MODE.get(cfg.getDBDirectoryPermissions()));
      }
    }
    catch(ConfigException ce)
    {
      addErrorMessage(ccr, ce.getMessageObject());
    }
  }
  /**
   * Sets files permissions on the backend directory.
   *
   * @param backendDir the directory to setup
   * @param curCfg a backend configuration
   */
  private void setDBDirPermissions(JEBackendCfg curCfg, File backendDir) throws ConfigException
  {
    FilePermission backendPermission = decodeDBDirPermissions(curCfg);
    // Get the backend database backendDirectory permissions and apply
    try
    {
      if(!FilePermission.setPermissions(backendDir, backendPermission))
      {
        logger.warn(WARN_UNABLE_SET_PERMISSIONS, backendPermission, backendDir);
      }
    }
    catch(Exception e)
    {
      // Log an warning that the permissions were not set.
      logger.warn(WARN_SET_PERMISSIONS_FAILED, backendDir, e);
    }
  }
  private static FilePermission decodeDBDirPermissions(JEBackendCfg curCfg) throws ConfigException
  {
    try
    {
      return FilePermission.decodeUNIXMode(curCfg.getDBDirectoryPermissions());
    }
    catch (Exception e)
    {
      throw new ConfigException(ERR_CONFIG_BACKEND_MODE_INVALID.get(curCfg.dn()));
    }
  }
  @Override
  public ConfigChangeResult applyConfigurationChange(JEBackendCfg cfg)
  {
@@ -1236,11 +1146,10 @@
    try
    {
      File parentDirectory = getFileForPath(cfg.getDBDirectory());
      File newBackendDirectory = new File(parentDirectory, cfg.getBackendId());
      File newBackendDirectory = getBackendDirectory(cfg);
      // Create the directory if it doesn't exist.
      if(!cfg.getDBDirectory().equals(config.getDBDirectory()))
      if (!cfg.getDBDirectory().equals(config.getDBDirectory()))
      {
        checkDBDirExistsOrCanCreate(newBackendDirectory, ccr, false);
        if (!ccr.getMessages().isEmpty())
@@ -1255,20 +1164,19 @@
      if (!cfg.getDBDirectoryPermissions().equalsIgnoreCase(config.getDBDirectoryPermissions())
          || !cfg.getDBDirectory().equals(config.getDBDirectory()))
      {
        checkDBDirPermissions(cfg, ccr);
        checkDBDirPermissions(cfg.getDBDirectoryPermissions(), cfg.dn(), ccr);
        if (!ccr.getMessages().isEmpty())
        {
          return ccr;
        }
        setDBDirPermissions(cfg, newBackendDirectory);
        setDBDirPermissions(newBackendDirectory, cfg.getDBDirectoryPermissions(), cfg.dn(), ccr);
        if (!ccr.getMessages().isEmpty())
        {
          return ccr;
        }
      }
      diskMonitor.registerMonitoredDirectory(
        config.getBackendId() + " backend",
        getDirectory(),
        cfg.getDiskLowThreshold(),
        cfg.getDiskFullThreshold(),
        this);
      registerMonitoredDirectory(cfg);
      config = cfg;
    }
    catch (Exception e)
@@ -1278,55 +1186,20 @@
    return ccr;
  }
  private static void addErrorMessage(final ConfigChangeResult ccr, LocalizableMessage message)
  private void registerMonitoredDirectory(JEBackendCfg cfg)
  {
    ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
    ccr.addMessage(message);
  }
  private void setupStorageFiles() throws ConfigException
  {
    ConfigChangeResult ccr = new ConfigChangeResult();
    checkDBDirExistsOrCanCreate(backendDirectory, ccr, false);
    if (!ccr.getMessages().isEmpty())
    {
      throw new ConfigException(ccr.getMessages().get(0));
    }
    checkDBDirPermissions(config, ccr);
    if (!ccr.getMessages().isEmpty())
    {
      throw new ConfigException(ccr.getMessages().get(0));
    }
    setDBDirPermissions(config, backendDirectory);
    diskMonitor.registerMonitoredDirectory(
      cfg.getBackendId() + " backend",
      getDirectory(),
      cfg.getDiskLowThreshold(),
      cfg.getDiskFullThreshold(),
      this);
  }
  @Override
  public void removeStorageFiles() throws StorageRuntimeException
  {
    if (!backendDirectory.exists())
    {
      return;
    }
    if (!backendDirectory.isDirectory())
    {
      throw new StorageRuntimeException(ERR_DIRECTORY_INVALID.get(backendDirectory.getPath()).toString());
    }
    try
    {
      File[] files = backendDirectory.listFiles();
      for (File f : files)
      {
        f.delete();
      }
    }
    catch (Exception e)
    {
      logger.traceException(e);
      throw new StorageRuntimeException(ERR_REMOVE_FAIL.get(e.getMessage()).toString(), e);
    }
    StorageUtils.removeStorageFiles(backendDirectory);
  }
  @Override
@@ -1337,16 +1210,12 @@
  @Override
  public void diskFullThresholdReached(File directory, long thresholdInBytes) {
    storageStatus = StorageStatus.unusable(
        WARN_DISK_SPACE_FULL_THRESHOLD_CROSSED.get(directory.getFreeSpace(), directory.getAbsolutePath(),
        thresholdInBytes, config.getBackendId()));
    storageStatus = statusWhenDiskSpaceFull(directory, thresholdInBytes, config.getBackendId());
  }
  @Override
  public void diskLowThresholdReached(File directory, long thresholdInBytes) {
    storageStatus = StorageStatus.lockedDown(
        WARN_DISK_SPACE_LOW_THRESHOLD_CROSSED.get(directory.getFreeSpace(), directory.getAbsolutePath(),
        thresholdInBytes, config.getBackendId()));
    storageStatus = statusWhenDiskSpaceLow(directory, thresholdInBytes, config.getBackendId());
  }
  @Override
opendj-server-legacy/src/main/java/org/opends/server/backends/pdb/PDBStorage.java
@@ -29,8 +29,8 @@
import static java.util.Arrays.*;
import static org.opends.messages.BackendMessages.*;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.messages.UtilityMessages.*;
import static org.opends.server.backends.pluggable.spi.StorageUtils.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.Closeable;
@@ -74,6 +74,7 @@
import org.opends.server.backends.pluggable.spi.StorageInUseException;
import org.opends.server.backends.pluggable.spi.StorageRuntimeException;
import org.opends.server.backends.pluggable.spi.StorageStatus;
import org.opends.server.backends.pluggable.spi.StorageUtils;
import org.opends.server.backends.pluggable.spi.TreeName;
import org.opends.server.backends.pluggable.spi.UpdateFunction;
import org.opends.server.backends.pluggable.spi.WriteOperation;
@@ -85,7 +86,6 @@
import org.opends.server.types.BackupConfig;
import org.opends.server.types.BackupDirectory;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.FilePermission;
import org.opends.server.types.RestoreConfig;
import org.opends.server.util.BackupManager;
@@ -730,7 +730,7 @@
  public PDBStorage(final PDBBackendCfg cfg, ServerContext serverContext) throws ConfigException
  {
    this.serverContext = serverContext;
    backendDirectory = new File(getFileForPath(cfg.getDBDirectory()), cfg.getBackendId());
    backendDirectory = getBackendDirectory(cfg);
    config = cfg;
    cfg.addPDBChangeListener(this);
  }
@@ -818,7 +818,7 @@
  private void open0(final Configuration dbCfg) throws ConfigException
  {
    setupStorageFiles();
    setupStorageFiles(backendDirectory, config.getDBDirectoryPermissions(), config.dn());
    try
    {
      if (db != null)
@@ -844,12 +844,7 @@
    {
      throw new StorageRuntimeException(e);
    }
    diskMonitor.registerMonitoredDirectory(
        config.getBackendId() + " backend",
        getDirectory(),
        config.getDiskLowThreshold(),
        config.getDiskFullThreshold(),
        this);
    registerMonitoredDirectory(config);
  }
  @Override
@@ -901,9 +896,9 @@
  private static String mangleTreeName(final TreeName treeName)
  {
    StringBuilder mangled = new StringBuilder();
    String name = treeName.toString();
    StringBuilder mangled = new StringBuilder(name.length());
    for (int idx = 0; idx < name.length(); idx++)
    {
      char ch = name.charAt(idx);
@@ -966,8 +961,12 @@
  @Override
  public File getDirectory()
  {
    File parentDir = getFileForPath(config.getDBDirectory());
    return new File(parentDir, config.getBackendId());
    return getBackendDirectory(config);
  }
  private static File getBackendDirectory(PDBBackendCfg cfg)
  {
    return getDBDirectory(cfg.getDBDirectory(), cfg.getBackendId());
  }
  @Override
@@ -1184,7 +1183,7 @@
   * Checks newly created backend has a valid configuration.
   * @param cfg the new configuration
   * @param unacceptableReasons the list of accumulated errors and their messages
   * @param context TODO
   * @param context the server context
   * @return true if newly created backend has a valid configuration
   */
  static boolean isConfigurationAcceptable(PDBBackendCfg cfg, List<LocalizableMessage> unacceptableReasons,
@@ -1213,11 +1212,10 @@
    List<LocalizableMessage> unacceptableReasons)
  {
    final ConfigChangeResult ccr = new ConfigChangeResult();
    File parentDirectory = getFileForPath(cfg.getDBDirectory());
    File newBackendDirectory = new File(parentDirectory, cfg.getBackendId());
    File newBackendDirectory = getBackendDirectory(cfg);
    checkDBDirExistsOrCanCreate(newBackendDirectory, ccr, true);
    checkDBDirPermissions(cfg, ccr);
    checkDBDirPermissions(cfg.getDBDirectoryPermissions(), cfg.dn(), ccr);
    if (!ccr.getMessages().isEmpty())
    {
      unacceptableReasons.addAll(ccr.getMessages());
@@ -1226,96 +1224,6 @@
    return true;
  }
  /**
   * Checks a directory exists or can actually be created.
   *
   * @param backendDir the directory to check for
   * @param ccr the list of reasons to return upstream or null if called from setupStorage()
   * @param cleanup true if the directory should be deleted after creation
   */
  private static void checkDBDirExistsOrCanCreate(File backendDir, ConfigChangeResult ccr, boolean cleanup)
  {
    if (!backendDir.exists())
    {
      if(!backendDir.mkdirs())
      {
        addErrorMessage(ccr, ERR_CREATE_FAIL.get(backendDir.getPath()));
      }
      if (cleanup)
      {
        backendDir.delete();
      }
    }
    else if (!backendDir.isDirectory())
    {
      addErrorMessage(ccr, ERR_DIRECTORY_INVALID.get(backendDir.getPath()));
    }
  }
  /**
   * Returns false if directory permissions in the configuration are invalid. Otherwise returns the
   * same value as it was passed in.
   *
   * @param cfg a (possibly new) backend configuration
   * @param ccr the current list of change results
   * @throws forwards a file exception
   */
  private static void checkDBDirPermissions(PDBBackendCfg cfg, ConfigChangeResult ccr)
  {
    try
    {
      FilePermission backendPermission = decodeDBDirPermissions(cfg);
      // Make sure the mode will allow the server itself access to the database
      if(!backendPermission.isOwnerWritable() ||
          !backendPermission.isOwnerReadable() ||
          !backendPermission.isOwnerExecutable())
      {
        addErrorMessage(ccr, ERR_CONFIG_BACKEND_INSANE_MODE.get(cfg.getDBDirectoryPermissions()));
      }
    }
    catch(ConfigException ce)
    {
      addErrorMessage(ccr, ce.getMessageObject());
    }
  }
  /**
   * Sets files permissions on the backend directory.
   *
   * @param backendDir the directory to setup
   * @param curCfg a backend configuration
   */
  private void setDBDirPermissions(PDBBackendCfg curCfg, File backendDir) throws ConfigException
  {
    FilePermission backendPermission = decodeDBDirPermissions(curCfg);
    // Get the backend database backendDirectory permissions and apply
    try
    {
      if(!FilePermission.setPermissions(backendDir, backendPermission))
      {
        logger.warn(WARN_UNABLE_SET_PERMISSIONS, backendPermission, backendDir);
      }
    }
    catch(Exception e)
    {
      // Log an warning that the permissions were not set.
      logger.warn(WARN_SET_PERMISSIONS_FAILED, backendDir, e);
    }
  }
  private static FilePermission decodeDBDirPermissions(PDBBackendCfg curCfg) throws ConfigException
  {
    try
    {
      return FilePermission.decodeUNIXMode(curCfg.getDBDirectoryPermissions());
    }
    catch (Exception e)
    {
      throw new ConfigException(ERR_CONFIG_BACKEND_MODE_INVALID.get(curCfg.dn()));
    }
  }
  @Override
  public ConfigChangeResult applyConfigurationChange(PDBBackendCfg cfg)
  {
@@ -1323,8 +1231,7 @@
    try
    {
      File parentDirectory = getFileForPath(cfg.getDBDirectory());
      File newBackendDirectory = new File(parentDirectory, cfg.getBackendId());
      File newBackendDirectory = getBackendDirectory(cfg);
      // Create the directory if it doesn't exist.
      if(!cfg.getDBDirectory().equals(config.getDBDirectory()))
@@ -1342,20 +1249,19 @@
      if (!cfg.getDBDirectoryPermissions().equalsIgnoreCase(config.getDBDirectoryPermissions())
          || !cfg.getDBDirectory().equals(config.getDBDirectory()))
      {
        checkDBDirPermissions(cfg, ccr);
        checkDBDirPermissions(cfg.getDBDirectoryPermissions(), cfg.dn(), ccr);
        if (!ccr.getMessages().isEmpty())
        {
          return ccr;
        }
        setDBDirPermissions(cfg, newBackendDirectory);
        setDBDirPermissions(newBackendDirectory, cfg.getDBDirectoryPermissions(), cfg.dn(), ccr);
        if (!ccr.getMessages().isEmpty())
        {
          return ccr;
        }
      }
      diskMonitor.registerMonitoredDirectory(
        config.getBackendId() + " backend",
        getDirectory(),
        cfg.getDiskLowThreshold(),
        cfg.getDiskFullThreshold(),
        this);
      registerMonitoredDirectory(cfg);
      config = cfg;
    }
    catch (Exception e)
@@ -1365,55 +1271,20 @@
    return ccr;
  }
  private static void addErrorMessage(final ConfigChangeResult ccr, LocalizableMessage message)
  private void registerMonitoredDirectory(PDBBackendCfg cfg)
  {
    ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
    ccr.addMessage(message);
  }
  private void setupStorageFiles() throws ConfigException
  {
    ConfigChangeResult ccr = new ConfigChangeResult();
    checkDBDirExistsOrCanCreate(backendDirectory, ccr, false);
    if (!ccr.getMessages().isEmpty())
    {
      throw new ConfigException(ccr.getMessages().get(0));
    }
    checkDBDirPermissions(config, ccr);
    if (!ccr.getMessages().isEmpty())
    {
      throw new ConfigException(ccr.getMessages().get(0));
    }
    setDBDirPermissions(config, backendDirectory);
    diskMonitor.registerMonitoredDirectory(
      cfg.getBackendId() + " backend",
      getDirectory(),
      cfg.getDiskLowThreshold(),
      cfg.getDiskFullThreshold(),
      this);
  }
  @Override
  public void removeStorageFiles() throws StorageRuntimeException
  {
    if (!backendDirectory.exists())
    {
      return;
    }
    if (!backendDirectory.isDirectory())
    {
      throw new StorageRuntimeException(ERR_DIRECTORY_INVALID.get(backendDirectory.getPath()).toString());
    }
    try
    {
      File[] files = backendDirectory.listFiles();
      for (File f : files)
      {
        f.delete();
      }
    }
    catch (Exception e)
    {
      logger.traceException(e);
      throw new StorageRuntimeException(ERR_REMOVE_FAIL.get(e.getMessage()).toString(), e);
    }
    StorageUtils.removeStorageFiles(backendDirectory);
  }
  @Override
@@ -1424,16 +1295,12 @@
  @Override
  public void diskFullThresholdReached(File directory, long thresholdInBytes) {
    storageStatus = StorageStatus.unusable(
        WARN_DISK_SPACE_FULL_THRESHOLD_CROSSED.get(directory.getFreeSpace(), directory.getAbsolutePath(),
        thresholdInBytes, config.getBackendId()));
    storageStatus = statusWhenDiskSpaceFull(directory, thresholdInBytes, config.getBackendId());
  }
  @Override
  public void diskLowThresholdReached(File directory, long thresholdInBytes) {
    storageStatus = StorageStatus.lockedDown(
        WARN_DISK_SPACE_LOW_THRESHOLD_CROSSED.get(directory.getFreeSpace(), directory.getAbsolutePath(),
        thresholdInBytes, config.getBackendId()));
    storageStatus = statusWhenDiskSpaceLow(directory, thresholdInBytes, config.getBackendId());
  }
  @Override
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/spi/StorageUtils.java
New file
@@ -0,0 +1,250 @@
/*
 * 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 static org.opends.messages.BackendMessages.*;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.File;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.config.server.ConfigChangeResult;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.DN;
import org.opends.server.types.FilePermission;
/** Utility class for implementations of {@link Storage}. */
public final class StorageUtils
{
  private StorageUtils()
  {
    // do not instantiate utility classes
  }
  /**
   * Returns a database directory file from the provided parent database directory and backendId.
   *
   * @param parentDbDirectory the parent database directory
   * @param backendId the backend id
   * @return a database directory file where to store data for the provided backendId
   */
  public static File getDBDirectory(String parentDbDirectory, String backendId)
  {
    return new File(getFileForPath(parentDbDirectory), backendId);
  }
  /**
   * Ensure backendDir exists (creating it if not) and has the specified dbDirPermissions.
   *
   * @param backendDir the backend directory where to set the storage files
   * @param dbDirPermissions the permissions to set for the database directory
   * @param configDN the backend configuration DN
   * @throws ConfigException if configuration fails
   */
  public static void setupStorageFiles(File backendDir, String dbDirPermissions, DN configDN) throws ConfigException
  {
    ConfigChangeResult ccr = new ConfigChangeResult();
    checkDBDirExistsOrCanCreate(backendDir, ccr, false);
    if (!ccr.getMessages().isEmpty())
    {
      throw new ConfigException(ccr.getMessages().get(0));
    }
    checkDBDirPermissions(dbDirPermissions, configDN, ccr);
    if (!ccr.getMessages().isEmpty())
    {
      throw new ConfigException(ccr.getMessages().get(0));
    }
    setDBDirPermissions(backendDir, dbDirPermissions, configDN, ccr);
    if (!ccr.getMessages().isEmpty())
    {
      throw new ConfigException(ccr.getMessages().get(0));
    }
  }
  /**
   * Checks a directory exists or can actually be created.
   *
   * @param backendDir the directory to check for
   * @param ccr the list of reasons to return upstream or null if called from setupStorage()
   * @param cleanup true if the directory should be deleted after creation
   */
  public static void checkDBDirExistsOrCanCreate(File backendDir, ConfigChangeResult ccr, boolean cleanup)
  {
    if (!backendDir.exists())
    {
      if (!backendDir.mkdirs())
      {
        addErrorMessage(ccr, ERR_CREATE_FAIL.get(backendDir.getPath()));
      }
      if (cleanup)
      {
        backendDir.delete();
      }
    }
    else if (!backendDir.isDirectory())
    {
      addErrorMessage(ccr, ERR_DIRECTORY_INVALID.get(backendDir.getPath()));
    }
  }
  /**
   * Returns false if directory permissions in the configuration are invalid.
   * Otherwise returns the same value as it was passed in.
   *
   * @param dbDirPermissions the permissions to set for the database directory
   * @param configDN the backend configuration DN
   * @param ccr the current list of change results
   */
  public static void checkDBDirPermissions(String dbDirPermissions, DN configDN, ConfigChangeResult ccr)
  {
    try
    {
      FilePermission backendPermission = decodeDBDirPermissions(dbDirPermissions, configDN);
      // Make sure the mode will allow the server itself access to the database
      if (!backendPermission.isOwnerWritable()
          || !backendPermission.isOwnerReadable()
          || !backendPermission.isOwnerExecutable())
      {
        addErrorMessage(ccr, ERR_CONFIG_BACKEND_INSANE_MODE.get(dbDirPermissions));
      }
    }
    catch (ConfigException ce)
    {
      addErrorMessage(ccr, ce.getMessageObject());
    }
  }
  /**
   * Sets files permissions on the backend directory.
   *
   * @param backendDir the directory to setup
   * @param dbDirPermissions the permissions to set for the database directory
   * @param configDN the backend configuration DN
   * @param ccr the current list of change results
   * @throws ConfigException if configuration fails
   */
  public static void setDBDirPermissions(File backendDir, String dbDirPermissions, DN configDN, ConfigChangeResult ccr)
      throws ConfigException
  {
    try
    {
      FilePermission backendPermission = decodeDBDirPermissions(dbDirPermissions, configDN);
      if (!FilePermission.setPermissions(backendDir, backendPermission))
      {
        addErrorMessage(ccr, WARN_UNABLE_SET_PERMISSIONS.get(backendPermission, backendDir));
      }
    }
    catch (Exception e)
    {
      addErrorMessage(ccr, WARN_SET_PERMISSIONS_FAILED.get(backendDir, stackTraceToSingleLineString(e)));
    }
  }
  private static FilePermission decodeDBDirPermissions(String dbDirPermissions, DN configDN) throws ConfigException
  {
    try
    {
      return FilePermission.decodeUNIXMode(dbDirPermissions);
    }
    catch (Exception e)
    {
      throw new ConfigException(ERR_CONFIG_BACKEND_MODE_INVALID.get(configDN));
    }
  }
  /**
   * Adds the provided message to the provided config change result.
   *
   * @param ccr the config change result
   * @param message the message to add
   */
  public static void addErrorMessage(ConfigChangeResult ccr, LocalizableMessage message)
  {
    ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
    ccr.addMessage(message);
  }
  /**
   * Removes the storage files from the provided backend directory.
   *
   * @param backendDir
   *          the backend directory where to remove storage files
   */
  public static void removeStorageFiles(File backendDir)
  {
    if (!backendDir.exists())
    {
      return;
    }
    if (!backendDir.isDirectory())
    {
      throw new StorageRuntimeException(ERR_DIRECTORY_INVALID.get(backendDir.getPath()).toString());
    }
    try
    {
      File[] files = backendDir.listFiles();
      for (File f : files)
      {
        f.delete();
      }
    }
    catch (Exception e)
    {
      throw new StorageRuntimeException(ERR_REMOVE_FAIL.get(e.getMessage()).toString(), e);
    }
  }
  /**
   * Creates a new unusable {@link StorageStatus} for the disk full threshold.
   *
   * @param directory the directory which reached the disk full threshold
   * @param thresholdInBytes the threshold in bytes
   * @param backendId the backend id
   * @return a new unusable {@link StorageStatus}
   */
  public static StorageStatus statusWhenDiskSpaceFull(File directory, long thresholdInBytes, String backendId)
  {
    return StorageStatus.unusable(WARN_DISK_SPACE_FULL_THRESHOLD_CROSSED.get(
        directory.getFreeSpace(), directory.getAbsolutePath(), thresholdInBytes, backendId));
  }
  /**
   * Creates a new locked down {@link StorageStatus} for the disk low threshold.
   *
   * @param directory the directory which reached the disk low threshold
   * @param thresholdInBytes the threshold in bytes
   * @param backendId the backend id
   * @return a new locked down {@link StorageStatus}
   */
  public static StorageStatus statusWhenDiskSpaceLow(File directory, long thresholdInBytes, String backendId)
  {
    return StorageStatus.lockedDown(WARN_DISK_SPACE_LOW_THRESHOLD_CROSSED.get(
        directory.getFreeSpace(), directory.getAbsolutePath(), thresholdInBytes, backendId));
  }
}