From 8ff034142142a3103f6e84e0c50fb6d77328c854 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Sat, 01 Sep 2007 22:41:49 +0000
Subject: [PATCH] Provide new directory environment properties that can be used to indicate whether the server should maintain a configuration archvie, and if so the maximum number of archived configurations that it should keep.  By default, the archive will be enabled and will be configured to keep an unlimited number of previous configurations.

---
 opendj-sdk/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java     |  194 ++++++++++++++++++---------
 opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java             |   22 +++
 opendj-sdk/opends/src/server/org/opends/server/types/DirectoryEnvironmentConfig.java |  162 +++++++++++++++++++++++
 3 files changed, 313 insertions(+), 65 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
index 0712d7a..54bfa16 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
@@ -44,6 +44,7 @@
 import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.TreeSet;
 import java.util.TreeMap;
 import java.util.zip.Deflater;
 import java.util.zip.GZIPInputStream;
@@ -76,6 +77,7 @@
 import org.opends.server.protocols.asn1.ASN1OctetString;
 import org.opends.server.schema.GeneralizedTimeSyntax;
 import org.opends.server.tools.LDIFModify;
+import org.opends.server.types.DirectoryEnvironmentConfig;
 
 
 import org.opends.server.types.*;
@@ -130,6 +132,9 @@
 
 
 
+  // Indicates whether to maintain a configuration archive.
+  private boolean maintainConfigArchive;
+
   // A SHA-1 digest of the last known configuration.  This should only be
   // incorrect if the server configuration file has been manually edited with
   // the server online, which is a bad thing.
@@ -145,6 +150,9 @@
   // The set of base DNs for this config handler backend.
   private DN[] baseDNs;
 
+  // The maximum config archive size to maintain.
+  private int maxConfigArchiveSize;
+
   // The write lock used to ensure that only one thread can apply a
   // configuration update at any given time.
   private ReentrantLock configLock;
@@ -228,30 +236,37 @@
     // Check to see if a configuration archive exists.  If not, then create one.
     // If so, then check whether the current configuration matches the last
     // configuration in the archive.  If it doesn't, then archive it.
-    try
-    {
-      configurationDigest = calculateConfigDigest();
-    }
-    catch (DirectoryException de)
-    {
-      throw new InitializationException(de.getMessageObject(), de.getCause());
-    }
-
-    File archiveDirectory = new File(f.getParent(), CONFIG_ARCHIVE_DIR_NAME);
-    if (archiveDirectory.exists())
+    DirectoryEnvironmentConfig envConfig =
+         DirectoryServer.getEnvironmentConfig();
+    maintainConfigArchive = envConfig.maintainConfigArchive();
+    maxConfigArchiveSize  = envConfig.getMaxConfigArchiveSize();
+    if (maintainConfigArchive)
     {
       try
       {
-        byte[] lastDigest = getLastConfigDigest(archiveDirectory);
-        if (! Arrays.equals(configurationDigest, lastDigest))
+        configurationDigest = calculateConfigDigest();
+      }
+      catch (DirectoryException de)
+      {
+        throw new InitializationException(de.getMessageObject(), de.getCause());
+      }
+
+      File archiveDirectory = new File(f.getParent(), CONFIG_ARCHIVE_DIR_NAME);
+      if (archiveDirectory.exists())
+      {
+        try
         {
-          writeConfigArchive();
-        }
-      } catch (Exception e) {}
-    }
-    else
-    {
-      writeConfigArchive();
+          byte[] lastDigest = getLastConfigDigest(archiveDirectory);
+          if (! Arrays.equals(configurationDigest, lastDigest))
+          {
+            writeConfigArchive();
+          }
+        } catch (Exception e) {}
+      }
+      else
+      {
+        writeConfigArchive();
+      }
     }
 
 
@@ -267,8 +282,11 @@
       if (changesFile.exists())
       {
         applyChangesFile(f, changesFile);
-        configurationDigest = calculateConfigDigest();
-        writeConfigArchive();
+        if (maintainConfigArchive)
+        {
+          configurationDigest = calculateConfigDigest();
+          writeConfigArchive();
+        }
       }
     }
     catch (Exception e)
@@ -628,7 +646,7 @@
     // Determine the appropriate server root.  If it's not defined in the
     // environment config, then try to figure it out from the location of the
     // configuration file.
-    File rootFile = DirectoryServer.getEnvironmentConfig().getServerRoot();
+    File rootFile = envConfig.getServerRoot();
     if (rootFile == null)
     {
       try
@@ -1952,61 +1970,64 @@
     // copy the current config off to the side before writing the new config
     // so that the manual changes don't get lost but also don't get applied.
     // Also, send an admin alert notifying administrators about the problem.
-    try
+    if (maintainConfigArchive)
     {
-      byte[] currentDigest = calculateConfigDigest();
-      if (! Arrays.equals(configurationDigest, currentDigest))
+      try
       {
-        File existingCfg   = new File(configFile);
-        File newConfigFile = new File(existingCfg.getParent(),
-                                      "config.manualedit-" +
-                                           TimeThread.getGMTTime() + ".ldif");
-        int counter = 2;
-        while (newConfigFile.exists())
+        byte[] currentDigest = calculateConfigDigest();
+        if (! Arrays.equals(configurationDigest, currentDigest))
         {
-          newConfigFile = new File(newConfigFile.getAbsolutePath() + "." +
-                                   counter++);
-        }
-
-        FileInputStream  inputStream  = new FileInputStream(existingCfg);
-        FileOutputStream outputStream = new FileOutputStream(newConfigFile);
-        byte[] buffer = new byte[8192];
-        while (true)
-        {
-          int bytesRead = inputStream.read(buffer);
-          if (bytesRead < 0)
+          File existingCfg   = new File(configFile);
+          File newConfigFile = new File(existingCfg.getParent(),
+                                        "config.manualedit-" +
+                                             TimeThread.getGMTTime() + ".ldif");
+          int counter = 2;
+          while (newConfigFile.exists())
           {
-            break;
+            newConfigFile = new File(newConfigFile.getAbsolutePath() + "." +
+                                     counter++);
           }
 
-          outputStream.write(buffer, 0, bytesRead);
+          FileInputStream  inputStream  = new FileInputStream(existingCfg);
+          FileOutputStream outputStream = new FileOutputStream(newConfigFile);
+          byte[] buffer = new byte[8192];
+          while (true)
+          {
+            int bytesRead = inputStream.read(buffer);
+            if (bytesRead < 0)
+            {
+              break;
+            }
+
+            outputStream.write(buffer, 0, bytesRead);
+          }
+
+          inputStream.close();
+          outputStream.close();
+
+          Message message = WARN_CONFIG_MANUAL_CHANGES_DETECTED.get(
+              configFile, newConfigFile.getAbsolutePath());
+          logError(message);
+
+          DirectoryServer.sendAlertNotification(this,
+               ALERT_TYPE_MANUAL_CONFIG_EDIT_HANDLED, message);
+        }
+      }
+      catch (Exception e)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
         }
 
-        inputStream.close();
-        outputStream.close();
-
-        Message message = WARN_CONFIG_MANUAL_CHANGES_DETECTED.get(
-            configFile, newConfigFile.getAbsolutePath());
+        Message message = ERR_CONFIG_MANUAL_CHANGES_LOST.get(
+            configFile, stackTraceToSingleLineString(e));
         logError(message);
 
         DirectoryServer.sendAlertNotification(this,
              ALERT_TYPE_MANUAL_CONFIG_EDIT_HANDLED, message);
       }
     }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      Message message = ERR_CONFIG_MANUAL_CHANGES_LOST.get(
-          configFile, stackTraceToSingleLineString(e));
-      logError(message);
-
-      DirectoryServer.sendAlertNotification(this,
-           ALERT_TYPE_MANUAL_CONFIG_EDIT_HANDLED, message);
-    }
 
 
     // Write the new configuration to a temporary file.
@@ -2064,7 +2085,10 @@
 
 
     // Try to write the archive for the new configuration.
-    writeConfigArchive();
+    if (maintainConfigArchive)
+    {
+      writeConfigArchive();
+    }
   }
 
 
@@ -2075,6 +2099,11 @@
    */
   private void writeConfigArchive()
   {
+    if (! maintainConfigArchive)
+    {
+      return;
+    }
+
     // Determine the path to the directory that will hold the archived
     // configuration files.
     File configDirectory  = new File(configFile).getParentFile();
@@ -2196,6 +2225,41 @@
         outputStream.close();
       } catch (Exception e) {}
     }
+
+
+    // If we should enforce a maximum number of archived configurations, then
+    // see if there are any old ones that we need to delete.
+    if (maxConfigArchiveSize > 0)
+    {
+      String[] archivedFileList = archiveDirectory.list();
+      int numToDelete = archivedFileList.length - maxConfigArchiveSize;
+      if (numToDelete > 0)
+      {
+        TreeSet<String> archiveSet = new TreeSet<String>();
+        for (String name : archivedFileList)
+        {
+          if (! name.startsWith("config-"))
+          {
+            continue;
+          }
+
+          // Simply ordering by filename should work, even when there are
+          // timestamp conflicts, because the dash comes before the period in
+          // the ASCII character set.
+          archiveSet.add(name);
+        }
+
+        Iterator<String> iterator = archiveSet.iterator();
+        for (int i=0; ((i < numToDelete) && iterator.hasNext()); i++)
+        {
+          File f = new File(archiveDirectory, iterator.next());
+          try
+          {
+            f.delete();
+          } catch (Exception e) {}
+        }
+      }
+    }
   }
 
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/DirectoryEnvironmentConfig.java b/opendj-sdk/opends/src/server/org/opends/server/types/DirectoryEnvironmentConfig.java
index 38374d4..dcf9c6b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/DirectoryEnvironmentConfig.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/DirectoryEnvironmentConfig.java
@@ -470,6 +470,168 @@
 
 
   /**
+   * Indicates whether the Directory Server should maintain an archive
+   * of previous configurations.  If no explicit value is defined,
+   * then a default result of {@code true} will be returned.
+   *
+   * @return  {@code true} if the Directory Server should maintain an
+   *          archive of previous configurations, or {@code false} if
+   *          not.
+   */
+  public boolean maintainConfigArchive()
+  {
+    String maintainArchiveStr =
+         getProperty(PROPERTY_MAINTAIN_CONFIG_ARCHIVE);
+    if (maintainArchiveStr == null)
+    {
+      return true;
+    }
+
+    return (! maintainArchiveStr.equalsIgnoreCase("false"));
+  }
+
+
+
+  /**
+   * Specifies whether the Directory Server should maintain an archive
+   * of previous configurations.
+   *
+   * @param  maintainConfigArchive  Indicates whether the Directory
+   *                                Server should maintain an archive
+   *                                of previous configurations.
+   *
+   * @return  The previous setting for this configuration option.  If
+   *          no previous value was specified, then {@code true} will
+   *          be returned.
+   *
+   * @throws  InitializationException  If the Directory Server is
+   *                                   already running.
+   */
+  public boolean setMaintainConfigArchive(
+                      boolean maintainConfigArchive)
+         throws InitializationException
+  {
+    if (DirectoryServer.isRunning())
+    {
+      throw new InitializationException(
+              ERR_DIRCFG_SERVER_ALREADY_RUNNING.get());
+    }
+
+    String oldMaintainStr =
+         setProperty(PROPERTY_MAINTAIN_CONFIG_ARCHIVE,
+                     String.valueOf(maintainConfigArchive));
+    if (oldMaintainStr == null)
+    {
+      return true;
+    }
+    else
+    {
+      return (! oldMaintainStr.equalsIgnoreCase("false"));
+    }
+  }
+
+
+
+  /**
+   * Retrieves the maximum number of archived configurations that the
+   * Directory Server should maintain.  If no value is defined, then a
+   * value of zero will be returned.
+   *
+   * @return  The maximum number of archived configurations that the
+   *          Directory Server should maintain, or zero if there
+   *          should not be any limit.
+   */
+  public int getMaxConfigArchiveSize()
+  {
+    String maxSizeStr =
+         getProperty(PROPERTY_MAX_CONFIG_ARCHIVE_SIZE);
+    if (maxSizeStr == null)
+    {
+      return 0;
+    }
+
+    try
+    {
+      int maxSize = Integer.parseInt(maxSizeStr);
+      if (maxSize > 0)
+      {
+        return maxSize;
+      }
+      else
+      {
+        return 0;
+      }
+    }
+    catch (Exception e)
+    {
+      return 0;
+    }
+  }
+
+
+
+  /**
+   * Specifies the maximum number of archived configurations that the
+   * Directory Server should maintain.  A value that is less than or
+   * equal to zero may be used to indicate that there should not be
+   * any limit to the number of archived configurations.
+   *
+   * @param  maxConfigArchiveSize  The maximum number of archived
+   *                               configurations that the Directory
+   *                               Server should maintain.
+   *
+   * @return  The previous setting for this configuration option.  If
+   *          no previous value was specified, then zero will be
+   *          returned.
+   *
+   * @throws  InitializationException  If the Directory Server is
+   *                                   already running.
+   */
+  public int setMaxConfigArchiveSize(int maxConfigArchiveSize)
+         throws InitializationException
+  {
+    if (DirectoryServer.isRunning())
+    {
+      throw new InitializationException(
+              ERR_DIRCFG_SERVER_ALREADY_RUNNING.get());
+    }
+
+    if (maxConfigArchiveSize < 0)
+    {
+      maxConfigArchiveSize = 0;
+    }
+
+    String oldMaxSizeStr =
+         setProperty(PROPERTY_MAX_CONFIG_ARCHIVE_SIZE,
+                     String.valueOf(maxConfigArchiveSize));
+    if (oldMaxSizeStr == null)
+    {
+      return 0;
+    }
+    else
+    {
+      try
+      {
+        int oldMaxSize = Integer.parseInt(oldMaxSizeStr);
+        if (oldMaxSize > 0)
+        {
+          return oldMaxSize;
+        }
+        else
+        {
+          return 0;
+        }
+      }
+      catch (Exception e)
+      {
+        return 0;
+      }
+    }
+  }
+
+
+
+  /**
    * Retrieves the directory that contains the server schema
    * configuration files.  If no value is defined, but a default
    * directory of "config/schema" exists below the server root, then
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java b/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
index b1cab30..23fab11 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -2531,6 +2531,28 @@
 
   /**
    * The name of the system property that can be used to determine whether the
+   * server should maintain an archive of previous configurations.  If this is
+   * not set, or if the value is anything other than "false", then the server
+   * will maintain a configuration archive.
+   */
+  public static final String PROPERTY_MAINTAIN_CONFIG_ARCHIVE =
+       "org.opends.server.MaintainConfigArchive";
+
+
+
+  /**
+   * The name of the system property that can be used to specify the maximum
+   * number of archived configurations to maintain.  If this is not set, or if
+   * it set to a zero or negative value, then there will be no limit on the
+   * number of archived configurations.
+   */
+  public static final String PROPERTY_MAX_CONFIG_ARCHIVE_SIZE =
+       "org.opends.server.MaxConfigArchiveSize";
+
+
+
+  /**
+   * The name of the system property that can be used to determine whether the
    * Directory Server is starting up for the purpose of running the unit tests.
    */
   public static final String PROPERTY_RUNNING_UNIT_TESTS =

--
Gitblit v1.10.0