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

Fabio Pistolesi
06.51.2015 8b8c3e9140e20dcbd7c12911d366d7b714fd0cb1
OPENDJ-1750 CR-5916 Backup of a Persistit backend generates empty backup set 
Fixes the "Empty backup" problem when creating a backup of a pluggable backend. Uses CR-5915 preparatory work to propagate storage information to the backupConfig, to correctly decide which files should belong to a backup.
Thanks JN!
5 files modified
135 ■■■■■ changed files
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistItStorage.java 16 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/BackendImpl.java 4 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/BackupManager.java 43 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/spi/Storage.java 7 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/types/BackupConfig.java 65 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/persistit/PersistItStorage.java
@@ -27,11 +27,11 @@
import static com.persistit.Transaction.CommitPolicy.*;
import static java.util.Arrays.*;
import static org.opends.messages.JebMessages.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.File;
import java.io.FilenameFilter;
import java.util.HashMap;
import java.util.Map;
@@ -686,6 +686,20 @@
    }
  }
  /** {@inheritDoc} */
  @Override
  public FilenameFilter getFilesToBackupFilter()
  {
    return new FilenameFilter()
    {
      @Override
      public boolean accept(File d, String name)
      {
        return name.startsWith(VOLUME_NAME) && !name.endsWith(".lck");
      }
    };
  }
  /*
   * TODO: it would be nice to use the low-level key/value APIs. They seem quite
   * inefficient at the moment for simple byte arrays.
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/BackendImpl.java
@@ -759,8 +759,8 @@
    BackupManager backupManager = new BackupManager(getBackendID());
    File parentDir = getFileForPath(cfg.getDBDirectory());
    File backendDir = new File(parentDir, cfg.getBackendId());
    // Storage storage = newStorageInstance();
    // backupConfig.setFilesToBackupFilter(storage.getFilesToBackupFilter());
    Storage storage = newStorageInstance();
    backupConfig.setFilesToBackupFilter(storage.getFilesToBackupFilter());
    backupManager.createBackup(backendDir, backupConfig);
  }
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/BackupManager.java
@@ -61,7 +61,7 @@
import org.opends.server.util.StaticUtils;
/**
 * A backup manager for JE backends.
 * A backup manager for backends.
 */
public class BackupManager
{
@@ -106,7 +106,7 @@
  /**
   * Construct a backup manager for a JE backend.
   * Construct a backup manager for a backend.
   * @param backendID The ID of the backend instance for which a backup
   * manager is required.
   */
@@ -116,11 +116,11 @@
  }
  /**
   * Create a backup of the JE backend.  The backup is stored in a single zip
   * Create a backup of the backend.  The backup is stored in a single zip
   * file in the backup directory.  If the backup is incremental, then the
   * first entry in the zip is a text file containing a list of all the JE
   * first entry in the zip is a text file containing a list of all the
   * log files that are unchanged since the previous backup.  The remaining
   * zip entries are the JE log files themselves, which, for an incremental,
   * zip entries are the log files themselves, which, for an incremental,
   * only include those files that have changed.
   * @param backendDir The directory of the backend instance for
   * which the backup is required.
@@ -139,6 +139,7 @@
    boolean         encrypt         = backupConfig.encryptData();
    boolean         hash            = backupConfig.hashData();
    boolean         signHash        = backupConfig.signHash();
    FilenameFilter  filenameFilter  = backupConfig.getFilesToBackupFilter();
    HashMap<String,String> backupProperties = new HashMap<String,String>();
@@ -321,17 +322,8 @@
      zipStream.setLevel(Deflater.NO_COMPRESSION);
    }
    // Get a list of all the log files comprising the database.
    FilenameFilter filenameFilter = new FilenameFilter()
    {
      @Override
      public boolean accept(File d, String name)
      {
        return name.endsWith(".jdb");
      }
    };
    File[] logFiles;
    try
    {
      logFiles = backendDir.listFiles(filenameFilter);
@@ -367,7 +359,7 @@
    // Sort the log files from oldest to youngest since this is the order
    // in which they must be copied.
    // This is easy since the files are created in alphabetical order by JE.
    // This is easy since the files are created in alphabetical order.
    Arrays.sort(logFiles);
    try
@@ -464,8 +456,7 @@
          been written to new log files, so we must include those new files.
          */
          final String latest = logFiles[logFiles.length-1].getName();
          FilenameFilter filter = new JELatestFileFilter(latest,
              latestFileSize);
          FilenameFilter filter = new DBLatestFileFilter(latest, latestFileSize, filenameFilter);
          try
          {
@@ -581,7 +572,7 @@
  /**
   * Restore a JE backend from backup, or verify the backup.
   * Restore a backend from backup, or verify the backup.
   * @param backendDir The configuration of the backend instance to be
   * restored.
   * @param  restoreConfig The configuration to use when performing the restore.
@@ -1236,22 +1227,26 @@
  }
  /**
   * This class implements a FilenameFilter to detect the last file
   * from a JE database.
   * This class implements a FilenameFilter to detect the last file from a database.
   */
  private static class JELatestFileFilter implements FilenameFilter {
  private static class DBLatestFileFilter implements FilenameFilter {
    private final String latest;
    private final long latestSize;
    private FilenameFilter filenameFilter;
    public JELatestFileFilter(String latest, long latestSize) {
    public DBLatestFileFilter(String latest, long latestSize, FilenameFilter filenameFilter) {
      this.latest = latest;
      this.latestSize = latestSize;
      this.filenameFilter = filenameFilter;
    }
    @Override
    public boolean accept(File d, String name)
    {
      if (!name.endsWith(".jdb")) return false;
      if (!filenameFilter.accept(d, name))
      {
        return false;
      }
      int compareTo = name.compareTo(latest);
      return compareTo > 0 || compareTo == 0 && d.length() > latestSize;
    }
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/spi/Storage.java
@@ -26,6 +26,7 @@
package org.opends.server.backends.pluggable.spi;
import java.io.Closeable;
import java.io.FilenameFilter;
import org.opends.server.admin.std.server.PluggableBackendCfg;
@@ -105,6 +106,12 @@
   */
  boolean isValid();
  /**
   * Returns a filename filter which selects the files to be included in a backup.
   * @return a filename filter which selects the files to be included in a backup
   */
  FilenameFilter getFilesToBackupFilter();
  /** {@inheritDoc} */
  @Override
  void close();
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/types/BackupConfig.java
@@ -22,9 +22,11 @@
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions Copyright 2015 Forgerock AS
 */
package org.opends.server.types;
import java.io.FilenameFilter;
/**
 * This class defines a data structure for holding configuration
@@ -50,36 +52,51 @@
     mayInvoke=true)
public final class BackupConfig extends OperationConfig
{
  // The path to the directory in which the backup file(s) should be
  // created.
  /**
   * The path to the directory in which the backup file(s) should be
   * created.
   */
  private BackupDirectory backupDirectory;
  // Indicates whether the data should be compressed as it is written.
  /** Indicates whether the data should be compressed as it is written. */
  private boolean compressData;
  // Indicates whether the data should be encrypted as it is written.
  /** Indicates whether the data should be encrypted as it is written. */
  private boolean encryptData;
  // Indicates whether to generate a cryptographic hash of the data as
  // it is written.
  /**
   * Indicates whether to generate a cryptographic hash of the data as
   * it is written.
   */
  private boolean hashData;
  // Indicates whether to attempt an incremental backup.
  /** Indicates whether to attempt an incremental backup. */
  private boolean isIncremental;
  // Indicates whether to digitally sign the hash when the backup is
  // complete.
  /**
   * Indicates whether to digitally sign the hash when the backup is
   * complete.
   */
  private boolean signHash;
  // The unique identifier assigned to this backup operation (which
  // may be used to indicate which version to restore if multiple
  // backups are in the same directory).
  /**
   * The unique identifier assigned to this backup operation (which
   * may be used to indicate which version to restore if multiple
   * backups are in the same directory).
   */
  private String backupID;
  // The unique ID for the existing full or incremental backup against
  // which the incremental backup should be based.
  /**
   * The unique ID for the existing full or incremental backup against
   * which the incremental backup should be based.
   */
  private String incrementalBaseID;
  /**
   * The filename filter to decide which files should be included as defined
   * by the storage.
   */
  private FilenameFilter filesToBackupFilter;
  /**
@@ -298,5 +315,25 @@
  {
    this.signHash = signHash;
  }
  /**
   * Returns the storage-defined filename filter deciding which files should go into a backup.
   *
   * @return the storage-defined filename filter deciding which files should go into a backup
   */
  public FilenameFilter getFilesToBackupFilter()
  {
    return filesToBackupFilter;
  }
  /**
   * Sets the storage-defined filter for files belonging to the backend.
   *
   * @param filenameFilter the filenameFilter to set
   */
  public void setFilesToBackupFilter(FilenameFilter filenameFilter)
  {
    this.filesToBackupFilter = filenameFilter;
  }
}