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

kenneth_suter
06.09.2007 43e8cc1e0b7576d94c96f07f64b25affb5b07af0
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;
    }
  }
}