From 75fceea66e311b3de58d76a4c993af0fec13dd3d Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Mon, 27 Apr 2015 13:20:04 +0000
Subject: [PATCH] OPENDJ-1870 CR-6581 Update BackupManager to generic tool

---
 opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/BackendImpl.java |  245 +++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 225 insertions(+), 20 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/BackendImpl.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/BackendImpl.java
index b1906ab..6454e3e 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/BackendImpl.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/BackendImpl.java
@@ -27,21 +27,29 @@
 package org.opends.server.backends.jeb;
 
 import static com.sleepycat.je.EnvironmentConfig.*;
+
 import static org.forgerock.util.Reject.*;
 import static org.opends.messages.BackendMessages.*;
 import static org.opends.messages.JebMessages.*;
+import static org.opends.messages.UtilityMessages.*;
 import static org.opends.server.backends.jeb.ConfigurableEnvironment.*;
 import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.*;
 
 import java.io.File;
+import java.io.FileFilter;
 import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.concurrent.ExecutionException;
@@ -61,6 +69,7 @@
 import org.opends.server.admin.std.server.LocalDBBackendCfg;
 import org.opends.server.api.AlertGenerator;
 import org.opends.server.api.Backend;
+import org.opends.server.api.Backupable;
 import org.opends.server.api.DiskSpaceMonitorHandler;
 import org.opends.server.api.MonitorProvider;
 import org.opends.server.backends.RebuildConfig;
@@ -90,6 +99,7 @@
 import org.opends.server.types.Operation;
 import org.opends.server.types.Privilege;
 import org.opends.server.types.RestoreConfig;
+import org.opends.server.util.BackupManager;
 import org.opends.server.util.RuntimeInformation;
 
 import com.sleepycat.je.DatabaseException;
@@ -103,7 +113,7 @@
  */
 public class BackendImpl extends Backend<LocalDBBackendCfg>
     implements ConfigurationChangeListener<LocalDBBackendCfg>, AlertGenerator,
-    DiskSpaceMonitorHandler
+    DiskSpaceMonitorHandler, Backupable
 {
   private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
 
@@ -246,7 +256,9 @@
     cfg.addLocalDBChangeListener(this);
   }
 
-  private File getDirectory()
+  /** {@inheritDoc} */
+  @Override
+  public File getDirectory()
   {
     File parentDirectory = getFileForPath(cfg.getDBDirectory());
     return new File(parentDirectory, cfg.getBackendId());
@@ -949,37 +961,230 @@
   @Override
   public void createBackup(BackupConfig backupConfig) throws DirectoryException
   {
-    BackupManager backupManager = new BackupManager(getBackendID());
-    File parentDir = getFileForPath(cfg.getDBDirectory());
-    File backendDir = new File(parentDir, cfg.getBackendId());
-    backupManager.createBackup(backendDir, backupConfig);
+    new BackupManager(getBackendID()).createBackup(this, backupConfig);
   }
 
-
-
   /** {@inheritDoc} */
   @Override
-  public void removeBackup(BackupDirectory backupDirectory, String backupID)
-      throws DirectoryException
+  public void removeBackup(BackupDirectory backupDirectory, String backupID) throws DirectoryException
   {
-    BackupManager backupManager = new BackupManager(getBackendID());
-    backupManager.removeBackup(backupDirectory, backupID);
+    new BackupManager(getBackendID()).removeBackup(backupDirectory, backupID);
   }
 
-
-
   /** {@inheritDoc} */
   @Override
-  public void restoreBackup(RestoreConfig restoreConfig)
-      throws DirectoryException
+  public void restoreBackup(RestoreConfig restoreConfig) throws DirectoryException
   {
-    BackupManager backupManager = new BackupManager(getBackendID());
-    File parentDir = getFileForPath(cfg.getDBDirectory());
-    File backendDir = new File(parentDir, cfg.getBackendId());
-    backupManager.restoreBackup(backendDir, restoreConfig);
+    new BackupManager(getBackendID()).restoreBackup(this, restoreConfig);
   }
 
+  /** {@inheritDoc} */
+  @Override
+  public ListIterator<Path> getFilesToBackup() throws DirectoryException
+  {
+    return new JELogFilesIterator(getDirectory(), cfg.getBackendId());
+  }
 
+  /**
+   * Iterator on JE log files to backup.
+   * <p>
+   * The cleaner thread may delete some log files during the backup. The
+   * iterator is automatically renewed if at least one file has been deleted.
+   */
+  static class JELogFilesIterator implements ListIterator<Path>
+  {
+    /** Underlying iterator on files. */
+    private ListIterator<Path> iterator;
+
+    /** Root directory where all files are located. */
+    private final File rootDirectory;
+
+    private final String backendID;
+
+    /** Files to backup. Used to renew the iterator if necessary. */
+    private List<Path> files;
+
+    private String lastFileName = "";
+    private long lastFileSize;
+
+    JELogFilesIterator(File rootDirectory, String backendID) throws DirectoryException
+    {
+      this.rootDirectory = rootDirectory;
+      this.backendID = backendID;
+      setFiles(BackupManager.getFiles(rootDirectory, new JELogFileFilter(), backendID));
+    }
+
+    private void setFiles(List<Path> files) {
+      this.files = files;
+      Collections.sort(files);
+      if (!files.isEmpty())
+      {
+        Path lastFile = files.get(files.size() - 1);
+        lastFileName = lastFile.getFileName().toString();
+        lastFileSize = lastFile.toFile().length();
+      }
+      iterator = files.listIterator();
+  }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean hasNext()
+    {
+      boolean hasNext = iterator.hasNext();
+      if (!hasNext && !files.isEmpty())
+      {
+        try
+        {
+          List<Path> allFiles = BackupManager.getFiles(rootDirectory, new JELogFileFilter(), backendID);
+          List<Path> compare = new ArrayList<Path>(files);
+          compare.removeAll(allFiles);
+          if (!compare.isEmpty())
+          {
+            // at least one file was deleted, the iterator must be renewed based on last file previously available
+            List<Path> newFiles =
+                BackupManager.getFiles(rootDirectory, new JELogFileFilter(lastFileName, lastFileSize), backendID);
+            logger.info(NOTE_JEB_BACKUP_CLEANER_ACTIVITY.get(newFiles.size()));
+            if (!newFiles.isEmpty())
+            {
+              setFiles(newFiles);
+              hasNext = iterator.hasNext();
+            }
+          }
+        }
+        catch (DirectoryException e)
+        {
+          logger.error(ERR_BACKEND_LIST_FILES_TO_BACKUP.get(backendID, stackTraceToSingleLineString(e)));
+        }
+      }
+      return hasNext;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Path next()
+    {
+      if (hasNext()) {
+        return iterator.next();
+      }
+      throw new NoSuchElementException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean hasPrevious()
+    {
+      return iterator.hasPrevious();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Path previous()
+    {
+      return iterator.previous();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int nextIndex()
+    {
+      return iterator.nextIndex();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int previousIndex()
+    {
+      return iterator.previousIndex();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void remove()
+    {
+      throw new UnsupportedOperationException("remove() is not implemented");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void set(Path e)
+    {
+      throw new UnsupportedOperationException("set() is not implemented");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void add(Path e)
+    {
+      throw new UnsupportedOperationException("add() is not implemented");
+    }
+
+  }
+
+  /**
+   * This class implements a FilenameFilter to detect a JE log file, possibly with a constraint
+   * on the file name and file size.
+   */
+  private static class JELogFileFilter implements FileFilter {
+
+    private final String latestFilename;
+    private final long latestFileSize;
+
+    /**
+     * Creates the filter for log files that are newer than provided file name
+     * or equal to provided file name and of larger size.
+     */
+    JELogFileFilter(String latestFilename, long latestFileSize) {
+      this.latestFilename = latestFilename;
+      this.latestFileSize = latestFileSize;
+    }
+
+    /** Creates the filter for any JE log file. */
+    JELogFileFilter() {
+      this("", 0);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean accept(File file)
+    {
+      String name = file.getName();
+      int cmp = name.compareTo(latestFilename);
+      return name.endsWith(".jdb") && (cmp > 0 || (cmp == 0 && file.length() > latestFileSize));
+    }
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isDirectRestore()
+  {
+    // restore is done in an intermediate directory
+    return false;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Path beforeRestore() throws DirectoryException
+  {
+    return null;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void afterRestore(Path restoreDirectory, Path saveDirectory) throws DirectoryException
+  {
+    // intermediate directory content is moved to database directory
+    File targetDirectory = getDirectory();
+    recursiveDelete(targetDirectory);
+    try
+    {
+      Files.move(restoreDirectory, targetDirectory.toPath());
+    }
+    catch(IOException e)
+    {
+      LocalizableMessage msg = ERR_CANNOT_RENAME_RESTORE_DIRECTORY.get(restoreDirectory, targetDirectory.getPath());
+      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), msg);
+    }
+  }
 
   /** {@inheritDoc} */
   @Override

--
Gitblit v1.10.0