From 43e8cc1e0b7576d94c96f07f64b25affb5b07af0 Mon Sep 17 00:00:00 2001
From: kenneth_suter <kenneth_suter@localhost>
Date: Fri, 06 Apr 2007 19:09:13 +0000
Subject: [PATCH] - There are 3 new classes for describing an instance of OpenDS:  Installation which describes the filesystem, Status which can answer questions like 'is the server running' and Configuration which represents an instance of config.ldif.  Part of the implementations of these classes were pulled from CurrentInstallStatus, Installer and Utils with some new methods to support upgrader needs.  There is still some overlap in functionality that I don't like but my attempt at converting the exiting applications to use the new classes ran into problems so I leave that for a future exercise.

---
 opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java |  633 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 604 insertions(+), 29 deletions(-)

diff --git a/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java b/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java
index d4f05a8..0fe630e 100644
--- a/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java
+++ b/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java
@@ -28,12 +28,25 @@
 package org.opends.quicksetup.upgrader;
 
 import org.opends.quicksetup.*;
+import org.opends.quicksetup.util.Utils;
+import org.opends.quicksetup.util.ZipExtractor;
+import org.opends.quicksetup.util.FileManager;
+import org.opends.quicksetup.util.ServerController;
 import org.opends.quicksetup.ui.QuickSetupDialog;
 import org.opends.quicksetup.ui.QuickSetupStepPanel;
+import org.opends.server.tools.BackUpDB;
+import org.opends.server.tools.LDIFDiff;
 
-import javax.swing.*;
 import java.awt.event.WindowEvent;
-import java.util.Set;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.io.IOException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileFilter;
+
+import static org.opends.quicksetup.Installation.*;
 
 /**
  * QuickSetup application of ugrading the bits of an installation of
@@ -41,18 +54,135 @@
  */
 public class Upgrader extends Application implements CliApplication {
 
+  /** Steps in the Upgrade wizard. */
+  enum UpgradeWizardStep implements WizardStep {
+
+    WELCOME("welcome-step"),
+
+    CHOOSE_VERSION("step-upgrade-choose-version"),
+
+    REVIEW("review-step"),
+
+    PROGRESS("progress-step");
+
+    private String msgKey;
+
+    private UpgradeWizardStep(String msgKey) {
+      this.msgKey = msgKey;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getMessageKey() {
+      return msgKey;
+    }
+  }
+
+  /** Steps during the upgrade process. */
+  enum UpgradeProgressStep implements ProgressStep {
+
+    NOT_STARTED("summary-upgrade-not-started"),
+
+    INITIALIZING("summary-upgrade-initializing"),
+
+    STOPPING_SERVER("summary-stopping"),
+
+    BACKING_UP_DATABASES("summary-upgrade-backing-up-db"),
+
+    BACKING_UP_FILESYSTEM("summary-upgrade-backing-up-files"),
+
+    CALCULATING_SCHEMA_CUSTOMIZATIONS(
+            "summary-upgrade-calculating-schema-customization"),
+
+    CALCULATING_CONFIGURATION_CUSTOMIZATIONS(
+            "summary-upgrade-calculating-config-customization"),
+
+    UPGRADING_COMPONENTS("summary-upgrade-upgrading-components"),
+
+    APPLYING_SCHEMA_CUSTOMIZATIONS(
+            "summary-upgrade-applying-schema-customization"),
+
+    APPLYING_CONFIGURATION_CUSTOMIZATIONS(
+            "summary-upgrade-applying-config-customization"),
+
+    VERIFYING("summary-upgrade-verifying"),
+
+    RECORDING_HISTORY("summary-upgrade-history"),
+
+    CLEANUP("summary-upgrade-cleanup"),
+
+    FINISHED_WITH_ERRORS("summary-upgrade-finished-with-errors"),
+
+    FINISHED("summary-upgrade-finished-successfully");
+
+    private String summaryMsgKey;
+
+    private UpgradeProgressStep(String summaryMsgKey) {
+      this.summaryMsgKey = summaryMsgKey;
+    }
+
+    /**
+     * Return a key for access a summary message.
+     * @return String representing key for access summary in resource bundle
+     */
+    public String getSummaryMesssageKey() {
+      return summaryMsgKey;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isLast() {
+      return this == FINISHED ||
+              this == FINISHED_WITH_ERRORS;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isError() {
+      return this == FINISHED_WITH_ERRORS;
+    }
+  }
+
+  static private final Logger LOG = Logger.getLogger(Upgrader.class.getName());
+
+  // Root files that will be ignored during backup
+  static private final String[] ROOT_FILES_TO_IGNORE_DURING_BACKUP = {
+    CHANGELOG_PATH_RELATIVE, // changelogDb
+    DATABASES_PATH_RELATIVE, // db
+    LOGS_PATH_RELATIVE, // logs
+    LOCKS_PATH_RELATIVE, // locks
+    HISTORY_PATH_RELATIVE // history; TODO: should we do this?
+  };
+
+  private ProgressStep currentProgressStep = UpgradeProgressStep.NOT_STARTED;
+
+  /** Assigned if an exception occurs during run(). */
+  private ApplicationException runException = null;
+
+  /** Helps with CLI specific tasks. */
+  private UpgraderCliHelper cliHelper = null;
+
+  /** Directory where we keep files temporarily. */
+  private File stagingDirectory = null;
+
+  /** Directory where backup is kept in case the upgrade needs reversion. */
+  private File backupDirectory = null;
+
   /**
    * {@inheritDoc}
    */
   public String getFrameTitle() {
-    return null;
+    return getMsg("frame-upgrade-title");
   }
 
   /**
    * {@inheritDoc}
    */
   public WizardStep getFirstWizardStep() {
-    return null;
+    return UpgradeWizardStep.WELCOME;
   }
 
   /**
@@ -61,68 +191,58 @@
   protected void setWizardDialogState(QuickSetupDialog dlg,
                                       UserData userData,
                                       WizardStep step) {
+    // TODO
   }
 
   /**
    * {@inheritDoc}
    */
   protected String getInstallationPath() {
-    return null;
+    return Utils.getInstallPathFromClasspath();
   }
 
   /**
    * {@inheritDoc}
    */
-  protected String getBinariesPath() {
-    return null;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public ProgressStep getStatus() {
-    return null;
+  public ProgressStep getCurrentProgressStep() {
+    return currentProgressStep;
   }
 
   /**
    * {@inheritDoc}
    */
   public Integer getRatio(ProgressStep step) {
-    return null;
+    return 100 * ((UpgradeProgressStep)step).ordinal() /
+            EnumSet.allOf(UpgradeWizardStep.class).size();
   }
 
   /**
    * {@inheritDoc}
    */
   public String getSummary(ProgressStep step) {
-    return null;
+    return getMsg(((UpgradeProgressStep)step).getSummaryMesssageKey());
   }
 
   /**
    * {@inheritDoc}
    */
   public void windowClosing(QuickSetupDialog dlg, WindowEvent evt) {
+    // TODO
   }
 
   /**
    * {@inheritDoc}
    */
   public ButtonName getInitialFocusButtonName() {
+    // TODO
     return null;
   }
 
   /**
    * {@inheritDoc}
    */
-  public JPanel createFramePanel(QuickSetupDialog dlg) {
-    return null;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public Set<WizardStep> getWizardSteps() {
-    return null;
+  public Set<? extends WizardStep> getWizardSteps() {
+    return Collections.unmodifiableSet(EnumSet.allOf(UpgradeWizardStep.class));
   }
 
   /**
@@ -194,23 +314,478 @@
    * {@inheritDoc}
    */
   public void run() {
+    // Reset exception just in case this application is rerun
+    // for some reason
+    runException = null;
+
+    try {
+      try {
+        setCurrentProgressStep(UpgradeProgressStep.INITIALIZING);
+        initialize();
+      } catch (ApplicationException e) {
+        LOG.log(Level.INFO, "error initializing upgrader", e);
+        throw e;
+      }
+
+      if (getInstallation().getStatus().isServerRunning()) {
+        try {
+          setCurrentProgressStep(UpgradeProgressStep.STOPPING_SERVER);
+          new ServerController(this).stopServer();
+        } catch (ApplicationException e) {
+          LOG.log(Level.INFO, "error stopping server", e);
+          throw e;
+        }
+      }
+
+      try {
+        setCurrentProgressStep(UpgradeProgressStep.BACKING_UP_DATABASES);
+        backupDatabases();
+      } catch (ApplicationException e) {
+        LOG.log(Level.INFO, "error backing up databases", e);
+        throw e;
+      }
+
+      try {
+        setCurrentProgressStep(UpgradeProgressStep.BACKING_UP_FILESYSTEM);
+        backupFilesytem();
+      } catch (ApplicationException e) {
+        LOG.log(Level.INFO, "error backing up files", e);
+        throw e;
+      }
+
+        try {
+          setCurrentProgressStep(
+              UpgradeProgressStep.CALCULATING_SCHEMA_CUSTOMIZATIONS);
+          calculateSchemaCustomizations();
+        } catch (ApplicationException e) {
+          LOG.log(Level.INFO, "error calculating schema customizations", e);
+          throw e;
+        }
+
+        try {
+          setCurrentProgressStep(
+              UpgradeProgressStep.CALCULATING_CONFIGURATION_CUSTOMIZATIONS);
+          calculateConfigCustomizations();
+        } catch (ApplicationException e) {
+          LOG.log(Level.INFO,
+                  "error calculating config customizations", e);
+          throw e;
+        }
+
+      try {
+        setCurrentProgressStep(
+            UpgradeProgressStep.UPGRADING_COMPONENTS);
+        upgradeComponents();
+      } catch (ApplicationException e) {
+        LOG.log(Level.INFO,
+                "error upgrading components", e);
+        throw e;
+      }
+
+//      setCurrentProgressStep(
+//              UpgradeProgressStep.APPLYING_SCHEMA_CUSTOMIZATIONS);
+//      sleepFor1();
+//      setCurrentProgressStep(
+//              UpgradeProgressStep.APPLYING_CONFIGURATION_CUSTOMIZATIONS);
+//      sleepFor1();
+//      setCurrentProgressStep(UpgradeProgressStep.VERIFYING);
+//      sleepFor1();
+//      setCurrentProgressStep(UpgradeProgressStep.RECORDING_HISTORY);
+//      sleepFor1();
+
+    } catch (ApplicationException ae) {
+      this.runException = ae;
+    } catch (Throwable t) {
+      this.runException =
+              new ApplicationException(ApplicationException.Type.BUG,
+                      t.getLocalizedMessage(),
+                      t);
+    } finally {
+      try {
+        setCurrentProgressStep(UpgradeProgressStep.CLEANUP);
+        cleanup();
+      } catch (ApplicationException e) {
+        System.err.print("error cleaning up after upgrade: " +
+                e.getLocalizedMessage());
+      }
+    }
+
+    // Decide final status based on presense of error
+    if (runException == null) {
+      setCurrentProgressStep(UpgradeProgressStep.FINISHED);
+    } else {
+      setCurrentProgressStep(UpgradeProgressStep.FINISHED_WITH_ERRORS);
+    }
+
+  }
+
+  private void upgradeComponents() throws ApplicationException {
+    try {
+      File stageDir = getStageDirectory();
+      File root = getInstallation().getRootDirectory();
+      FileManager fm = new FileManager(this);
+      for (String fileName : stageDir.list()) {
+        File f = new File(stageDir, fileName);
+        fm.copyRecursively(f, root, new UpgradeFileFilter(stageDir));
+      }
+    } catch (IOException e) {
+      throw ApplicationException.createFileSystemException(
+              "I/0 error upgrading components", e);
+    }
+  }
+
+  private void calculateConfigCustomizations() throws ApplicationException {
+    try {
+    if (getInstallation().getCurrentConfiguration().hasBeenModified()) {
+
+      try {
+        List<String> args = new ArrayList<String>();
+
+        args.add("-s"); // source LDIF
+        args.add(getInstallation().getCurrentConfigurationFile().
+                getCanonicalPath());
+
+        args.add("-t"); // target LDIF
+        args.add(getInstallation().getBaseConfigurationFile().
+                getCanonicalPath());
+
+        args.add("-o"); // output LDIF
+        args.add(getCustomConfigDiffFile().
+                getCanonicalPath());
+
+        // TODO i18n
+        notifyListeners("Diff'ing configuration with base configuration...");
+
+        int ret = LDIFDiff.mainDiff(args.toArray(new String[]{}), false);
+        if (ret != 0) {
+          StringBuffer msg = new StringBuffer()
+                  .append("'ldif-diff' tool returned error code ")
+                  .append(ret)
+                  .append(" when invoked with args :")
+                  .append(Utils.listToString(args, " "));
+          throw ApplicationException.createFileSystemException(
+                  msg.toString(), null);
+        } else {
+          notifyListeners(formatter.getFormattedDone());
+        }
+      } catch (Exception e) {
+        throw ApplicationException.createFileSystemException(
+                "error determining configuration customizations", e);
+      }
+    } else {
+      // TODO i18n
+      notifyListeners("No configuration customizations to migrate" +
+                      formatter.getLineBreak());
+    }
+    } catch (IOException e) {
+      // TODO i18n
+      throw ApplicationException.createFileSystemException(
+              "could not determine configuration modifications", e);
+    }
+  }
+
+  private void calculateSchemaCustomizations() throws ApplicationException {
+    if (getInstallation().getStatus().schemaHasBeenModified()) {
+
+      // TODO i18n
+      notifyListeners(
+              "Schema contains customizations and needs to be migrated");
+
+      try {
+        List<String> args = new ArrayList<String>();
+
+        args.add("-s"); // source LDIF
+        args.add(getInstallation().getSchemaConcatFile().
+                getCanonicalPath());
+
+        args.add("-t"); // target LDIF
+        args.add(getInstallation().getBaseSchemaFile().
+                getCanonicalPath());
+
+        args.add("-o"); // output LDIF
+        args.add(getCustomSchemaDiffFile().
+                getCanonicalPath());
+
+        // TODO i18n
+        notifyListeners("Diff'ing schema with base schema...");
+
+        int ret = LDIFDiff.mainDiff(args.toArray(new String[]{}), false);
+        if (ret != 0) {
+          StringBuffer sb = new StringBuffer()
+                  .append("'ldif-diff' tool returned error code ")
+                  .append(ret)
+                  .append(" when invoked with args: ")
+                  .append(Utils.listToString(args, " "));
+          throw ApplicationException.createFileSystemException(sb.toString(),
+                  null);
+        } else {
+          notifyListeners(formatter.getFormattedDone());
+        }
+
+      } catch (Exception e) {
+        throw ApplicationException.createFileSystemException(
+                "error determining schema customizations", e);
+      }
+    } else {
+      // TODO i18n
+      notifyListeners("No schema customizations to migrate" +
+          formatter.getLineBreak());
+    }
+  }
+
+  private void backupFilesytem() throws ApplicationException {
+    try {
+      File filesBackupDirectory = getFilesBackupDirectory();
+      FileManager fm = new FileManager(this);
+      File root = getInstallation().getRootDirectory();
+      for (String fileName : root.list()) {
+        File f = new File(root, fileName);
+        fm.copyRecursively(f, filesBackupDirectory,
+                new UpgradeFileFilter(root));
+      }
+    } catch (Exception e) {
+      throw new ApplicationException(
+              ApplicationException.Type.FILE_SYSTEM_ERROR,
+              e.getLocalizedMessage(),
+              e);
+    }
+  }
+
+  private void backupDatabases() throws ApplicationException {
+    List<String> args = new ArrayList<String>();
+    args.add("--configClass");
+    args.add("org.opends.server.extensions.ConfigFileHandler");
+    args.add("--configFile");
+    args.add(getInstallation().getCurrentConfigurationFile().getPath());
+    args.add("-a"); // backup all
+    args.add("-d"); // backup to directory
+    try {
+      args.add(getUpgradeBackupDirectory().getCanonicalPath());
+    } catch (IOException e) {
+      // TODO i18n
+      throw new ApplicationException(
+              ApplicationException.Type.FILE_SYSTEM_ERROR,
+              "error backup up databases", e);
+    }
+    int ret = BackUpDB.mainBackUpDB(args.toArray(new String[0]));
+    if (ret != 0) {
+      StringBuffer sb = new StringBuffer()
+              .append("'backup utility returned error code ")
+              .append(ret)
+              .append(" when invoked with args: ")
+              .append(Utils.listToString(args, " "));
+      throw new ApplicationException(
+              ApplicationException.Type.FILE_SYSTEM_ERROR,
+              sb.toString(), null);
+
+    }
+  }
+
+  private void cleanup() throws ApplicationException {
+    deleteStagingDirectory();
+  }
+
+  private void deleteStagingDirectory() throws ApplicationException {
+    File stagingDir = null;
+    try {
+      stagingDir = getStageDirectory();
+      FileManager fm = new FileManager(this);
+      fm.deleteRecursively(stagingDir);
+    } catch (IOException e) {
+      // TODO i18n
+      throw ApplicationException.createFileSystemException(
+              "error attempting to clean up tmp directory " +
+              stagingDir != null ? stagingDir.getName() : "null",
+              e);
+    }
+  }
+
+  private void initialize() throws ApplicationException {
+    try {
+      expandZipFile();
+      insureUpgradability();
+    } catch (Exception e) {
+      throw new ApplicationException(
+              ApplicationException.Type.FILE_SYSTEM_ERROR,
+              e.getMessage(), e);
+    }
+  }
+
+  /**
+   * Given the current information, determines whether or not
+   * an upgrade from the current version to the next version
+   * is possible.  Upgrading may not be possible due to 'flag
+   * day' types of changes to the codebase.
+   */
+  private void insureUpgradability() throws ApplicationException {
+    Integer currentVersion;
+    Integer newVersion;
+
+    try {
+      currentVersion = getInstallation().getSvnRev();
+    } catch (QuickSetupException e) {
+      LOG.log(Level.INFO, "error", e);
+      throw ApplicationException.createFileSystemException(
+              "could not determine current version number", e);
+    }
+
+    try {
+      newVersion = getStagedInstallation().getSvnRev();
+    } catch (Exception e) {
+      LOG.log(Level.INFO, "error", e);
+      throw ApplicationException.createFileSystemException(
+              "could not determine upgrade version number", e);
+    }
+
+    UpgradeOracle uo = new UpgradeOracle(currentVersion, newVersion);
+    if (!uo.isSupported()) {
+      throw new ApplicationException(ApplicationException.Type.APPLICATION,
+              uo.getSummaryMessage(), null);
+    }
+
+  }
+
+  private Installation getStagedInstallation()
+          throws IOException, ApplicationException
+  {
+    return new Installation(getStageDirectory());
+  }
+
+  private void expandZipFile()
+          throws ApplicationException, IOException, QuickSetupException
+  {
+    File installPackage = getUpgradeUserData().getInstallPackage();
+    FileInputStream fis = new FileInputStream(installPackage);
+    ZipExtractor extractor = new ZipExtractor(fis,
+            1, 10, // TODO figure out these values
+            getStageDirectory().getCanonicalPath(),
+            Utils.getNumberZipEntries(),
+            installPackage.getName(), this);
+    extractor.extract();
+  }
+
+  /**
+   * Delays for a time FOR TESTING ONLY.
+   */
+  private void sleepFor1() {
+    try {
+      Thread.sleep(1000);
+    } catch (InterruptedException e) {
+
+    }
   }
 
   /**
    * {@inheritDoc}
    */
-  public UserData createUserData(String[] args,
-                                 CurrentInstallStatus status)
+  public UserData createUserData(String[] args, CurrentInstallStatus cis)
           throws UserDataException
   {
-    return null;
+    return getCliHelper().createUserData(args, cis);
   }
 
   /**
    * {@inheritDoc}
    */
   public ApplicationException getException() {
-    return null;
+    return runException;
+  }
+
+  private void setCurrentProgressStep(UpgradeProgressStep step) {
+    this.currentProgressStep = step;
+    String msg = getMsg(step.getSummaryMesssageKey());
+    notifyListeners(getFormattedProgress(msg) + getLineBreak());
+  }
+
+  private UpgraderCliHelper getCliHelper() {
+    if (cliHelper == null) {
+      cliHelper = new UpgraderCliHelper();
+    }
+    return cliHelper;
+  }
+
+  private File getTempDirectory() {
+    return new File(System.getProperty("java.io.tmpdir"));
+  }
+
+  private File getStageDirectory()
+          throws ApplicationException, IOException
+  {
+    if (stagingDirectory == null) {
+      File tmpDir = getTempDirectory();
+      stagingDirectory =
+              new File(tmpDir, "opends-upgrade-tmp-" +
+                      System.currentTimeMillis());
+      if (stagingDirectory.exists()) {
+        FileManager fm = new FileManager(this);
+        fm.deleteRecursively(stagingDirectory);
+      }
+      stagingDirectory.mkdirs();
+    }
+    return stagingDirectory;
+  }
+
+  private UpgradeUserData getUpgradeUserData() {
+    return (UpgradeUserData)getUserData();
+  }
+
+  private File getFilesBackupDirectory() throws IOException {
+    File files = new File(getUpgradeBackupDirectory(), "files");
+    if (!files.exists()) {
+      if (!files.mkdirs()) {
+        throw new IOException("error creating files backup directory");
+      }
+    }
+    return files;
+  }
+
+  private File getUpgradeBackupDirectory() throws IOException {
+    if (backupDirectory == null) {
+      backupDirectory = getInstallation().createHistoryBackupDirectory();
+    }
+    return backupDirectory;
+  }
+
+  private File getCustomConfigDiffFile() throws IOException {
+    return new File(getUpgradeBackupDirectory(), "config.custom.diff");
+  }
+
+  private File getCustomSchemaDiffFile() throws IOException {
+    return new File(getUpgradeBackupDirectory(), "schema.custom.diff");
+  }
+
+  /**
+   * Filter defining files we want to manage in the upgrade
+   * process.
+   */
+  private class UpgradeFileFilter implements FileFilter {
+
+    Set<File> filesToIgnore;
+
+    public UpgradeFileFilter(File root) throws IOException {
+      this.filesToIgnore = new HashSet<File>();
+      for (String rootFileNamesToIgnore : ROOT_FILES_TO_IGNORE_DURING_BACKUP) {
+        filesToIgnore.add(new File(root, rootFileNamesToIgnore));
+      }
+
+      // Definitely want to not back this up since it would create
+      // infinite recursion.  This may not be necessary if we are
+      // ignoring the entire history directory but its added here for
+      // safe measure.
+      filesToIgnore.add(getUpgradeBackupDirectory());
+    }
+
+    public boolean accept(File file) {
+      boolean accept = true;
+      for (File ignoreFile : filesToIgnore) {
+        if (ignoreFile.equals(file) ||
+                Utils.isParentOf(ignoreFile, file)) {
+          accept = false;
+          break;
+        }
+      }
+      return accept;
+    }
   }
 
 }

--
Gitblit v1.10.0