From 72a49cdc969eed0d2a46c6ec37390a351e360015 Mon Sep 17 00:00:00 2001
From: kenneth_suter <kenneth_suter@localhost>
Date: Thu, 28 Jun 2007 13:39:36 +0000
Subject: [PATCH] This commit address issue 1674 <https://opends.dev.java.net/issues/show_bug.cgi?id=1674> to make the webstart and offline installers cancelable.  The plumbing for cancelability is already in place since the upgrader makes use of it.  This commit for the most part implements the actions that the installers must perform when the operation is canceled.

---
 opends/src/quicksetup/org/opends/quicksetup/resources/Resources.properties            |   14 +
 opends/src/quicksetup/org/opends/quicksetup/installer/InstallerHelper.java            |   13 +
 opends/src/quicksetup/org/opends/quicksetup/installer/offline/OfflineInstaller.java   |  123 ++++++++++++++
 opends/src/quicksetup/org/opends/quicksetup/installer/InstallProgressStep.java        |   17 ++
 opends/src/quicksetup/org/opends/quicksetup/installer/webstart/WebStartInstaller.java |   71 ++++++++
 opends/src/quicksetup/org/opends/quicksetup/util/FileManager.java                     |   78 +++++++++
 opends/src/quicksetup/org/opends/quicksetup/ui/ProgressPanel.java                     |    4 
 opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java                  |  106 +++++++++++-
 opends/src/quicksetup/org/opends/quicksetup/Application.java                          |    2 
 9 files changed, 391 insertions(+), 37 deletions(-)

diff --git a/opends/src/quicksetup/org/opends/quicksetup/Application.java b/opends/src/quicksetup/org/opends/quicksetup/Application.java
index f6a5d27..ed9eaaa 100644
--- a/opends/src/quicksetup/org/opends/quicksetup/Application.java
+++ b/opends/src/quicksetup/org/opends/quicksetup/Application.java
@@ -169,7 +169,7 @@
    * Sets the application's installation.
    * @param installation describing the application's OpenDS installation
    */
-  protected void setInstallation(Installation installation) {
+  public void setInstallation(Installation installation) {
     this.installation = installation;
   }
 
diff --git a/opends/src/quicksetup/org/opends/quicksetup/installer/InstallProgressStep.java b/opends/src/quicksetup/org/opends/quicksetup/installer/InstallProgressStep.java
index e4771ed..6c76c83 100644
--- a/opends/src/quicksetup/org/opends/quicksetup/installer/InstallProgressStep.java
+++ b/opends/src/quicksetup/org/opends/quicksetup/installer/InstallProgressStep.java
@@ -102,11 +102,27 @@
   ENABLING_WINDOWS_SERVICE,
 
   /**
+   * User is waiting for current task to finish
+   * so that the operation can be canceled.
+   */
+  WAITING_TO_CANCEL,
+
+  /**
+   * Canceling install.
+   */
+  CANCELING,
+
+  /**
    * Installation finished successfully.
    */
   FINISHED_SUCCESSFULLY,
 
   /**
+   * User canceled installation.
+   */
+  FINISHED_CANCELED,
+
+  /**
    * Installation finished with an error.
    */
   FINISHED_WITH_ERROR;
@@ -116,6 +132,7 @@
    */
   public boolean isLast() {
     return this == FINISHED_SUCCESSFULLY ||
+            this == FINISHED_CANCELED ||
     this == FINISHED_WITH_ERROR;
   }
 
diff --git a/opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java b/opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java
index 374232c..03b0f9a 100644
--- a/opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java
+++ b/opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java
@@ -100,9 +100,12 @@
 public abstract class Installer extends GuiApplication {
   private TopologyCache lastLoadedCache;
 
-  /* Indicates that we've detected that there is something installed */
+  /** Indicates that we've detected that there is something installed. */
   boolean forceToDisplaySetup = false;
 
+  /** When true indicates that the user has canceled this operation. */
+  protected boolean canceled = false;
+
   // Constants used to do checks
   private static final int MIN_DIRECTORY_MANAGER_PWD = 1;
 
@@ -122,6 +125,10 @@
 
   private static final int MAX_NUMBER_ENTRIES = 10000;
 
+  /** Set of progress steps that have been completed. */
+  protected Set<InstallProgressStep>
+          completedProgress = new HashSet<InstallProgressStep>();
+
   private List<WizardStep> lstSteps = new ArrayList<WizardStep>();
 
   private final HashSet<WizardStep> SUBSTEPS = new HashSet<WizardStep>();
@@ -137,12 +144,17 @@
   private HashMap<WizardStep, WizardStep> hmPreviousSteps =
     new HashMap<WizardStep, WizardStep>();
 
+  private char[] selfSignedCertPw = null;
+
   /**
    * An static String that contains the class name of ConfigFileHandler.
    */
   protected static final String CONFIG_CLASS_NAME =
       "org.opends.server.extensions.ConfigFileHandler";
 
+  /** Alias of a self-signed certificate. */
+  protected static final String SELF_SIGNED_CERT_ALIAS = "server-cert";
+
   /**
    * Creates a default instance.
    */
@@ -175,7 +187,7 @@
    * {@inheritDoc}
    */
   public boolean isCancellable() {
-    return false; // TODO: have installer delete installed files upon cancel
+    return true;
   }
 
   /**
@@ -338,6 +350,7 @@
   public boolean isFinished()
   {
     return getCurrentProgressStep() == InstallProgressStep.FINISHED_SUCCESSFULLY
+            || getCurrentProgressStep() == InstallProgressStep.FINISHED_CANCELED
         || getCurrentProgressStep() == InstallProgressStep.FINISHED_WITH_ERROR;
   }
 
@@ -345,7 +358,9 @@
    * {@inheritDoc}
    */
   public void cancel() {
-    // do nothing; not cancellable
+    setStatus(InstallProgressStep.WAITING_TO_CANCEL);
+    notifyListeners(null);
+    this.canceled = true;
   }
 
   /**
@@ -644,6 +659,26 @@
   }
 
   /**
+   * Uninstalls installed services.  This is to be used when the user
+   * has elected to cancel an installation.
+   */
+  protected void uninstallServices() {
+    if (completedProgress.contains(
+            InstallProgressStep.ENABLING_WINDOWS_SERVICE)) {
+      try {
+        new InstallerHelper().disableWindowsService();
+      } catch (ApplicationException ae) {
+        LOG.log(Level.INFO, "Error disabling Windows service", ae);
+      }
+    }
+
+    if (completedProgress.contains(
+            InstallProgressStep.CONFIGURING_REPLICATION)) {
+      // TODO:  undo replication
+    }
+  }
+
+  /**
    * Creates a template file based in the contents of the UserData object.
    * This template file is used to generate automatically data.  To generate
    * the template file the code will basically take into account the value of
@@ -815,17 +850,17 @@
             getSelfSignedKeystorePath(),
             CertificateManager.KEY_STORE_TYPE_JKS,
             pwd);
-        certManager.generateSelfSignedCertificate("server-cert",
+        certManager.generateSelfSignedCertificate(SELF_SIGNED_CERT_ALIAS,
             getSelfSignedCertificateSubjectDN(),
             getSelfSignedCertificateValidity());
-        exportCertificate(certManager, "server-cert",
+        exportCertificate(certManager, SELF_SIGNED_CERT_ALIAS,
             getTemporaryCertificatePath());
 
         trustManager = new CertificateManager(
             getTrustManagerPath(),
             CertificateManager.KEY_STORE_TYPE_JKS,
             pwd);
-        trustManager.addCertificate("server-cert",
+        trustManager.addCertificate(SELF_SIGNED_CERT_ALIAS,
             new File(getTemporaryCertificatePath()));
         Utils.createFile(getKeystorePinPath(), pwd);
         f = new File(getTemporaryCertificatePath());
@@ -1034,16 +1069,18 @@
 
       if (result != 0)
       {
-        String[] msgArgs = { Utils.stringArrayToString(args, " ") };
         throw new ApplicationException(
             ApplicationException.Type.CONFIGURATION_ERROR,
-            getMsg("error-import-automatically-generated", msgArgs), null);
+            getMsg("error-import-ldif-tool-return-code",
+                    Integer.toString(result)), null);
       }
     } catch (Throwable t)
     {
       throw new ApplicationException(
           ApplicationException.Type.CONFIGURATION_ERROR,
-          getThrowableMsg("error-import-automatically-generated", null, t), t);
+          getThrowableMsg("error-import-automatically-generated",
+                  new String[] { Utils.listToString(argList, " "),
+                          t.getLocalizedMessage()}, t), t);
     }
   }
 
@@ -1278,6 +1315,10 @@
         getFormattedSummary(getMsg("summary-initialize-replicated-suffixes")));
     hmSummary.put(InstallProgressStep.ENABLING_WINDOWS_SERVICE,
         getFormattedSummary(getMsg("summary-enabling-windows-service")));
+    hmSummary.put(InstallProgressStep.WAITING_TO_CANCEL,
+        getFormattedSummary(getMsg("summary-waiting-to-cancel")));
+    hmSummary.put(InstallProgressStep.CANCELING,
+        getFormattedSummary(getMsg("summary-canceling")));
 
     Installation installation = getInstallation();
     String cmd = Utils.getPath(installation.getStatusPanelCommandFile());
@@ -1286,11 +1327,32 @@
     hmSummary.put(InstallProgressStep.FINISHED_SUCCESSFULLY,
         getFormattedSuccess(
             getMsg("summary-install-finished-successfully", args)));
+    hmSummary.put(InstallProgressStep.FINISHED_CANCELED,
+        getFormattedSuccess(
+            getMsg("summary-install-finished-canceled", args)));
     hmSummary.put(InstallProgressStep.FINISHED_WITH_ERROR,
         getFormattedError(getMsg("summary-install-finished-with-error", args)));
   }
 
   /**
+   * Checks the value of <code>canceled</code> field and throws an
+   * ApplicationException if true.  This indicates that the user has
+   * canceled this operation and the process of aborting should begin
+   * as soon as possible.
+   *
+   * @throws ApplicationException thrown if <code>canceled</code>
+   */
+  protected void checkAbort() throws ApplicationException {
+    if (canceled) {
+      setStatus(InstallProgressStep.CANCELING);
+      notifyListeners(null);
+      throw new ApplicationException(
+            ApplicationException.Type.CANCEL,
+            getMsg("upgrade-canceled"), null);
+    }
+  }
+
+  /**
    * Writes the java home that we are using for the setup in a file.
    * This way we can use this java home even if the user has not set JAVA_HOME
    * when running the different scripts.
@@ -1365,6 +1427,9 @@
    */
   protected void setStatus(InstallProgressStep status)
   {
+    if (status != null) {
+      this.completedProgress.add(status);
+    }
     this.status = status;
   }
 
@@ -3339,7 +3404,7 @@
    * @return the keystore path to be used for generating a self-signed
    * certificate.
    */
-  private String getSelfSignedKeystorePath()
+  protected String getSelfSignedKeystorePath()
   {
     String parentFile = Utils.getPath(getInstallationPath(),
         Installation.CONFIG_PATH_RELATIVE);
@@ -3405,13 +3470,26 @@
   }
 
   /**
+   * Returns the self-signed certificate password used for this session.  This
+   * method calls <code>createSelfSignedCertificatePwd()</code> the first time
+   * this method is called.
+   * @return the self-signed certificate password used for this session.
+   */
+  protected String getSelfSignedCertificatePwd()
+  {
+    if (selfSignedCertPw == null) {
+      selfSignedCertPw = createSelfSignedCertificatePwd();
+    }
+    return new String(selfSignedCertPw);
+  }
+
+  /**
    * Returns a randomly generated password for the self-signed certificate
    * keystore.
    * @return a randomly generated password for the self-signed certificate
    * keystore.
    */
-  private String getSelfSignedCertificatePwd()
-  {
+  private char[] createSelfSignedCertificatePwd() {
     int pwdLength = 50;
     char[] pwd = new char[pwdLength];
     Random random = new Random();
@@ -3420,9 +3498,7 @@
         char nextChar = getRandomChar(random,type);
         pwd[pos] = nextChar;
     }
-
-    String pwdString = new String(pwd);
-    return pwdString;
+    return pwd;
   }
 
   private void exportCertificate(CertificateManager certManager, String alias,
diff --git a/opends/src/quicksetup/org/opends/quicksetup/installer/InstallerHelper.java b/opends/src/quicksetup/org/opends/quicksetup/installer/InstallerHelper.java
index 7ee27c9..bb57cc2 100644
--- a/opends/src/quicksetup/org/opends/quicksetup/installer/InstallerHelper.java
+++ b/opends/src/quicksetup/org/opends/quicksetup/installer/InstallerHelper.java
@@ -145,6 +145,19 @@
     }
   }
 
+  /**
+   * This method disables this server as a Windows service.
+   * @throws ApplicationException if something goes worong.
+   */
+  public void disableWindowsService() throws ApplicationException
+  {
+    int code = ConfigureWindowsService.disableService(System.out, System.err);
+    if (code == ConfigureWindowsService.SERVICE_DISABLE_ERROR) {
+      throw new ApplicationException(
+              ApplicationException.Type.WINDOWS_SERVICE_ERROR,
+              getMsg("error-disabling-windows-service"), null);
+    }
+  }
 
   private String getThrowableMsg(String key, Throwable t)
   {
diff --git a/opends/src/quicksetup/org/opends/quicksetup/installer/offline/OfflineInstaller.java b/opends/src/quicksetup/org/opends/quicksetup/installer/offline/OfflineInstaller.java
index ccf0198..48d84d2 100644
--- a/opends/src/quicksetup/org/opends/quicksetup/installer/offline/OfflineInstaller.java
+++ b/opends/src/quicksetup/org/opends/quicksetup/installer/offline/OfflineInstaller.java
@@ -28,17 +28,23 @@
 package org.opends.quicksetup.installer.offline;
 
 import java.io.PrintStream;
+import java.io.File;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import java.security.KeyStoreException;
 
 import org.opends.quicksetup.ApplicationException;
 import org.opends.quicksetup.ProgressStep;
+import org.opends.quicksetup.Installation;
+import org.opends.quicksetup.SecurityOptions;
 import org.opends.quicksetup.installer.Installer;
 import org.opends.quicksetup.installer.InstallProgressStep;
 import org.opends.quicksetup.util.Utils;
 import org.opends.quicksetup.util.ServerController;
+import org.opends.quicksetup.util.FileManager;
+import org.opends.server.util.CertificateManager;
 
 /**
  * This is an implementation of the Installer class that is used to install
@@ -83,11 +89,17 @@
       System.setErr(err);
       System.setOut(out);
 
+      checkAbort();
+
       setStatus(InstallProgressStep.CONFIGURING_SERVER);
       configureServer();
 
+      checkAbort();
+
       createData();
 
+      checkAbort();
+
       writeJavaHome();
 
       if (Utils.isWindows() && getUserData().getEnableWindowsService())
@@ -95,6 +107,7 @@
           notifyListeners(getTaskSeparator());
           setStatus(InstallProgressStep.ENABLING_WINDOWS_SERVICE);
           enableWindowsService();
+          checkAbort();
       }
 
       if (mustStart())
@@ -102,6 +115,7 @@
         notifyListeners(getTaskSeparator());
         setStatus(InstallProgressStep.STARTING_SERVER);
         new ServerController(this).startServer();
+        checkAbort();
       }
 
       if (mustConfigureReplication())
@@ -110,6 +124,7 @@
         notifyListeners(getTaskSeparator());
 
         configureReplication();
+        checkAbort();
       }
 
       if (mustInitializeSuffixes())
@@ -117,6 +132,7 @@
         notifyListeners(getTaskSeparator());
         setStatus(InstallProgressStep.INITIALIZE_REPLICATED_SUFFIXES);
         initializeSuffixes();
+        checkAbort();
       }
 
       if (mustCreateAds())
@@ -124,6 +140,7 @@
         notifyListeners(getTaskSeparator());
         setStatus(InstallProgressStep.CONFIGURING_ADS);
         updateADS();
+        checkAbort();
       }
 
       if (mustStop())
@@ -133,17 +150,24 @@
         new ServerController(this).stopServer();
       }
 
+      checkAbort();
       setStatus(InstallProgressStep.FINISHED_SUCCESSFULLY);
       notifyListeners(null);
 
     } catch (ApplicationException ex)
     {
-      notifyListeners(getLineBreak());
-      notifyListenersOfLog();
-      setStatus(InstallProgressStep.FINISHED_WITH_ERROR);
-      String html = getFormattedError(ex, true);
-      notifyListeners(html);
-      LOG.log(Level.SEVERE, "Error installing.", ex);
+      if (ApplicationException.Type.CANCEL.equals(ex.getType())) {
+        uninstall();
+        setStatus(InstallProgressStep.FINISHED_CANCELED);
+        notifyListeners(null);
+      } else {
+        notifyListeners(getLineBreak());
+        notifyListenersOfLog();
+        setStatus(InstallProgressStep.FINISHED_WITH_ERROR);
+        String html = getFormattedError(ex, true);
+        notifyListeners(html);
+        LOG.log(Level.SEVERE, "Error installing.", ex);
+      }
     }
     catch (Throwable t)
     {
@@ -177,6 +201,92 @@
   }
 
   /**
+   * Called when the user elects to cancel this operation.
+   */
+  protected void uninstall() {
+
+    Installation installation = getInstallation();
+    FileManager fm = new FileManager(this);
+
+    // Stop the server if necessary
+    if (installation.getStatus().isServerRunning()) {
+      try {
+        new ServerController(installation).stopServer(true);
+      } catch (ApplicationException e) {
+        LOG.log(Level.INFO, "error stopping server", e);
+      }
+    }
+
+    uninstallServices();
+
+    // Revert to the base configuration
+    try {
+      File newConfig = fm.copy(installation.getBaseConfigurationFile(),
+                               installation.getConfigurationDirectory(),
+                               /*overwrite=*/true);
+      fm.rename(newConfig, installation.getCurrentConfigurationFile());
+
+    } catch (ApplicationException ae) {
+      LOG.log(Level.INFO, "failed to restore base configuration", ae);
+    }
+
+    // Cleanup SSL if necessary
+    SecurityOptions sec = getUserData().getSecurityOptions();
+    if (sec.getEnableSSL() || sec.getEnableStartTLS()) {
+      if (SecurityOptions.CertificateType.SELF_SIGNED_CERTIFICATE.equals(
+              sec.getCertificateType())) {
+        CertificateManager cm = new CertificateManager(
+            getSelfSignedKeystorePath(),
+            CertificateManager.KEY_STORE_TYPE_JKS,
+            getSelfSignedCertificatePwd());
+        try {
+          cm.removeCertificate(SELF_SIGNED_CERT_ALIAS);
+        } catch (KeyStoreException e) {
+          LOG.log(Level.INFO, "Error deleting self signed certification", e);
+        }
+      }
+
+      File keystore = new File(installation.getConfigurationDirectory(),
+              "keystore");
+      if (keystore.exists()) {
+        try {
+          fm.delete(keystore);
+        } catch (ApplicationException e) {
+          LOG.log(Level.INFO, "Failed to delete keystore", e);
+        }
+      }
+
+      File keystorePin = new File(installation.getConfigurationDirectory(),
+              "keystore.pin");
+      if (keystorePin.exists()) {
+        try {
+          fm.delete(keystorePin);
+        } catch (ApplicationException e) {
+          LOG.log(Level.INFO, "Failed to delete keystore.pin", e);
+        }
+      }
+
+      File truststore = new File(installation.getConfigurationDirectory(),
+              "truststore");
+      if (truststore.exists()) {
+        try {
+          fm.delete(truststore);
+        } catch (ApplicationException e) {
+          LOG.log(Level.INFO, "Failed to delete truststore", e);
+        }
+      }
+    }
+
+    // Remove the databases
+    try {
+      fm.deleteChildren(installation.getDatabasesDirectory());
+    } catch (ApplicationException e) {
+      LOG.log(Level.INFO, "Error deleting databases", e);
+    }
+
+  }
+
+  /**
    * Initialize the different map used in this class.
    *
    */
@@ -277,6 +387,7 @@
     }
     hmRatio.put(InstallProgressStep.FINISHED_SUCCESSFULLY, 100);
     hmRatio.put(InstallProgressStep.FINISHED_WITH_ERROR, 100);
+    hmRatio.put(InstallProgressStep.FINISHED_CANCELED, 100);
   }
 
   /**
diff --git a/opends/src/quicksetup/org/opends/quicksetup/installer/webstart/WebStartInstaller.java b/opends/src/quicksetup/org/opends/quicksetup/installer/webstart/WebStartInstaller.java
index 1e798dd..022dd14 100644
--- a/opends/src/quicksetup/org/opends/quicksetup/installer/webstart/WebStartInstaller.java
+++ b/opends/src/quicksetup/org/opends/quicksetup/installer/webstart/WebStartInstaller.java
@@ -45,6 +45,7 @@
 import org.opends.quicksetup.util.Utils;
 import org.opends.quicksetup.util.ZipExtractor;
 import org.opends.quicksetup.util.ServerController;
+import org.opends.quicksetup.util.FileManager;
 
 /**
  * This is an implementation of the Installer class that is used to install
@@ -109,12 +110,16 @@
 
       setStatus(InstallProgressStep.DOWNLOADING);
 
+      checkAbort();
+
       InputStream in =
           getZipInputStream(getRatio(InstallProgressStep.EXTRACTING));
 
       setStatus(InstallProgressStep.EXTRACTING);
       notifyListeners(getTaskSeparator());
 
+      checkAbort();
+
       createParentDirectoryIfRequired();
       extractZipFiles(in, getRatio(InstallProgressStep.EXTRACTING),
           getRatio(InstallProgressStep.CONFIGURING_SERVER));
@@ -127,6 +132,9 @@
       {
         LOG.log(Level.INFO, "Error closing zip input stream: "+t, t);
       }
+
+      checkAbort();
+
       setStatus(InstallProgressStep.CONFIGURING_SERVER);
       notifyListeners(getTaskSeparator());
 
@@ -135,15 +143,24 @@
       writeJavaHome();
       setInstallation(new Installation(getUserData().getServerLocation()));
 
+      checkAbort();
+
       setStatus(InstallProgressStep.CONFIGURING_SERVER);
       configureServer();
+
+      checkAbort();
+
       createData();
 
+      checkAbort();
+
       if (Utils.isWindows() && getUserData().getEnableWindowsService())
       {
           notifyListeners(getTaskSeparator());
           setStatus(InstallProgressStep.ENABLING_WINDOWS_SERVICE);
           enableWindowsService();
+
+          checkAbort();
       }
 
       if (mustStart())
@@ -151,6 +168,8 @@
         notifyListeners(getTaskSeparator());
         setStatus(InstallProgressStep.STARTING_SERVER);
         new ServerController(this).startServer();
+
+        checkAbort();
       }
 
       if (mustConfigureReplication())
@@ -159,6 +178,8 @@
         notifyListeners(getTaskSeparator());
 
         configureReplication();
+
+        checkAbort();
       }
 
       if (mustInitializeSuffixes())
@@ -166,6 +187,8 @@
         notifyListeners(getTaskSeparator());
         setStatus(InstallProgressStep.INITIALIZE_REPLICATED_SUFFIXES);
         initializeSuffixes();
+
+        checkAbort();
       }
 
       if (mustCreateAds())
@@ -173,6 +196,8 @@
         notifyListeners(getTaskSeparator());
         setStatus(InstallProgressStep.CONFIGURING_ADS);
         updateADS();
+
+        checkAbort();
       }
 
       if (mustStop())
@@ -182,17 +207,24 @@
         new ServerController(this).stopServer();
       }
 
+      checkAbort();
       setStatus(InstallProgressStep.FINISHED_SUCCESSFULLY);
       notifyListeners(null);
 
     } catch (ApplicationException ex)
     {
-      notifyListeners(getLineBreak());
-      notifyListenersOfLog();
-      setStatus(InstallProgressStep.FINISHED_WITH_ERROR);
-      String html = getFormattedError(ex, true);
-      notifyListeners(html);
-      LOG.log(Level.SEVERE, "Error installing.", ex);
+      if (ApplicationException.Type.CANCEL.equals(ex.getType())) {
+        uninstall();
+        setStatus(InstallProgressStep.FINISHED_CANCELED);
+        notifyListeners(null);
+      } else {
+        notifyListeners(getLineBreak());
+        notifyListenersOfLog();
+        setStatus(InstallProgressStep.FINISHED_WITH_ERROR);
+        String html = getFormattedError(ex, true);
+        notifyListeners(html);
+        LOG.log(Level.SEVERE, "Error installing.", ex);
+      }
     }
     catch (Throwable t)
     {
@@ -338,6 +370,7 @@
     }
 
     hmRatio.put(InstallProgressStep.FINISHED_SUCCESSFULLY, 100);
+    hmRatio.put(InstallProgressStep.FINISHED_CANCELED, 100);
     hmRatio.put(InstallProgressStep.FINISHED_WITH_ERROR, 100);
   }
 
@@ -462,6 +495,32 @@
   }
 
   /**
+   * Uninstall what has already been installed.
+   */
+  private void uninstall() {
+    Installation installation = getInstallation();
+    FileManager fm = new FileManager(this);
+
+    // Stop the server if necessary
+    if (installation.getStatus().isServerRunning()) {
+      try {
+        new ServerController(installation).stopServer(true);
+      } catch (ApplicationException e) {
+        LOG.log(Level.INFO, "error stopping server", e);
+      }
+    }
+
+    uninstallServices();
+
+    try {
+      fm.deleteRecursively(installation.getRootDirectory(), null,
+              FileManager.DeletionPolicy.DELETE_ON_EXIT_IF_UNSUCCESSFUL);
+    } catch (ApplicationException e) {
+      LOG.log(Level.INFO, "error deleting files", e);
+    }
+  }
+
+  /**
    * {@inheritDoc}
    */
   protected String getInstallationPath()
diff --git a/opends/src/quicksetup/org/opends/quicksetup/resources/Resources.properties b/opends/src/quicksetup/org/opends/quicksetup/resources/Resources.properties
index 359794a..8e933bd 100644
--- a/opends/src/quicksetup/org/opends/quicksetup/resources/Resources.properties
+++ b/opends/src/quicksetup/org/opends/quicksetup/resources/Resources.properties
@@ -918,6 +918,8 @@
 summary-configuring-replication=Configuring Replication...
 summary-starting=Starting Directory Server...
 summary-enabling-windows-service=Enabling Windows Service...
+summary-waiting-to-cancel=Waiting to Cancel...
+summary-canceling=Canceling...
 summary-configuring-ads=Creating Registration Configuration...
 summary-initialize-replicated-suffixes=Initializing Contents of Replicated \
 Suffixes...
@@ -928,6 +930,9 @@
 configuration.<br>To see basic server configuration status and to start/stop \
 the server, click Launch Status Panel.  Note that you can launch this tool \
 later using {1}.<br><INPUT type="submit" value="Launch Status Panel"></INPUT>
+summary-install-finished-canceled=<b>OpenDS QuickSetup Canceled.</b> \
+  <br>The upgrade operation was canceled and any files installed to your system \
+  during this operation have been removed.
 summary-install-finished-with-error=An error occurred.  Check 'Details' text \
 area for more information.<br>To see basic server configuration status, click \
 Launch Status Panel.  Note that you can launch this tool \
@@ -1022,6 +1027,7 @@
 progress-creating-base-entry=Creating Base Entry {0}
 progress-importing-ldif=Importing LDIF file {0}:
 progress-configuring-replication=Configuring Replication
+progress-cancel=Waiting to cancel operation.
 
 progress-configuring-replication-remote=Configuring Replication on {0}
 progress-import-automatically-generated=Importing Automatically-Generated Data \
@@ -1037,7 +1043,7 @@
 progress-deleting-installation-files=Deleting Files under the Installation Path:
 progress-deleting-file=Deleting file {0}
 progress-deleting-directory=Deleting directory {0}
-progress-deleting-file-does-not-exist=Ignoring file {0} since it does not exist. 
+progress-deleting-file-does-not-exist=Ignoring file {0} since it does not exist.
 
 progress-copying-file=Copying file {0} to {1}
 progress-server-already-stopped=The Directory Server is already stopped.
@@ -1080,8 +1086,8 @@
 {0}\\bat\\windows-service.bat -d command-line to disable the service manually. 
 error-creating-base-entry=Error Creating Base Entry.
 error-importing-ldif=Error Importing LDIF File.
-error-import-automatically-generated=Error Importing Automatically- Generated \
-Data when invoked with arguments: {0}.
+error-import-automatically-generated=Error Importing Automatically-Generated \
+Data when invoked with arguments {0}:  {1}.
 error-starting-server-with-no-connection-handlers=Error Starting Server with \
 no connection handlers: {0}.
 error-starting-server=Error Starting Directory Server.
@@ -1098,6 +1104,7 @@
 exception-root-cause=Root Cause:
 error-deleting-file=Error deleting file {0}.  Check that you have the rights \
 to delete this file and that there is no other application using it.
+error-renaming-file=Error renaming file {0} to {1}.
 error-deleting-directory=Error deleting directory {0}.  Check that you have \
 the rights to delete this directory and that there is no other application \
 using it.
@@ -1155,6 +1162,7 @@
 error-determining-server-state=Failed to determine the server's state.
 error-backup-db-tool-return-code=The backup tool returned error code {0}.
 error-ldif-diff-tool-return-code=The LDIF diff tool returned error code {0}.
+error-import-ldif-tool-return-code=The import LDIF tool returned error code {0}.
 error-apply-ldif-modify=Error processing modification operation of {0}: {1}
 error-apply-ldif-add=Error processing add operation of {0}: {1}
 error-apply-ldif-delete=Error processing delete operation of {0}: {1}
diff --git a/opends/src/quicksetup/org/opends/quicksetup/ui/ProgressPanel.java b/opends/src/quicksetup/org/opends/quicksetup/ui/ProgressPanel.java
index 72e35eb..dbcb5da 100644
--- a/opends/src/quicksetup/org/opends/quicksetup/ui/ProgressPanel.java
+++ b/opends/src/quicksetup/org/opends/quicksetup/ui/ProgressPanel.java
@@ -202,8 +202,8 @@
     }
     progressBarLabel.setText(summaryText);
 
-    int v = descriptor.getProgressBarRatio();
-    if (v > 0)
+    Integer v = descriptor.getProgressBarRatio();
+    if (v != null && v > 0)
     {
       progressBar.setIndeterminate(false);
       progressBar.setValue(v);
diff --git a/opends/src/quicksetup/org/opends/quicksetup/util/FileManager.java b/opends/src/quicksetup/org/opends/quicksetup/util/FileManager.java
index f578716..8b3c1f9 100644
--- a/opends/src/quicksetup/org/opends/quicksetup/util/FileManager.java
+++ b/opends/src/quicksetup/org/opends/quicksetup/util/FileManager.java
@@ -112,6 +112,44 @@
   }
 
   /**
+   * Renames the source file to the target file.  If the target file exists
+   * it is first deleted.  The rename and delete operation return values
+   * are checked for success and if unsuccessful, this method throws an
+   * exception.
+   *
+   * @param fileToRename The file to rename.
+   * @param target       The file to which <code>fileToRename</code> will be
+   *                     moved.
+   * @throws ApplicationException If a problem occurs while attempting to rename
+   *                     the file.  On the Windows platform, this typically
+   *                     indicates that the file is in use by this or another
+   *                     application.
+   */
+  public void rename(File fileToRename, File target)
+          throws ApplicationException {
+    if (fileToRename != null && target != null) {
+      synchronized (target) {
+        if (target.exists()) {
+          if (!target.delete()) {
+            throw new ApplicationException(
+                    ApplicationException.Type.FILE_SYSTEM_ERROR,
+                    getMsg("error-deleting-file",
+                            Utils.getPath(target)), null);
+          }
+        }
+      }
+      if (!fileToRename.renameTo(target)) {
+        throw new ApplicationException(
+                ApplicationException.Type.FILE_SYSTEM_ERROR,
+                getMsg("error-renaming-file",
+                        Utils.getPath(fileToRename),
+                        Utils.getPath(target)), null);
+      }
+    }
+  }
+
+
+  /**
    * Move a file.
    * @param object File to move
    * @param newParent File representing new parent directory
@@ -167,6 +205,23 @@
   }
 
   /**
+   * Deletes the children of a directory.
+   *
+   * @param parentDir the directory whose children is deleted
+   * @throws ApplicationException if there is a problem deleting children
+   */
+  public void deleteChildren(File parentDir) throws ApplicationException {
+    if (parentDir != null && parentDir.exists() && parentDir.isDirectory()) {
+      File[] children = parentDir.listFiles();
+      if (children != null) {
+        for (File child : children) {
+          delete(child);
+        }
+      }
+    }
+  }
+
+  /**
    * Deletes everything below the specified file.
    *
    * @param file the path to be deleted.
@@ -198,12 +253,15 @@
    *
    * @param objectFile   the file to be copied.
    * @param destDir      the directory to copy the file to
+   * @return File representing the destination
    * @throws ApplicationException if something goes wrong.
    */
-  public void copy(File objectFile, File destDir)
+  public File copy(File objectFile, File destDir)
           throws ApplicationException
   {
-    new CopyOperation(objectFile, destDir, false).apply();
+    CopyOperation co = new CopyOperation(objectFile, destDir, false);
+    co.apply();
+    return co.getDestination();
   }
 
   /**
@@ -211,13 +269,16 @@
    *
    * @param objectFile   the file to be copied.
    * @param destDir      the directory to copy the file to
+   * @return File representing the destination
    * @param overwrite    overwrite destination files.
    * @throws ApplicationException if something goes wrong.
    */
-  public void copy(File objectFile, File destDir, boolean overwrite)
+  public File copy(File objectFile, File destDir, boolean overwrite)
           throws ApplicationException
   {
-    new CopyOperation(objectFile, destDir, overwrite).apply();
+    CopyOperation co = new CopyOperation(objectFile, destDir, overwrite);
+    co.apply();
+    return co.getDestination();
   }
 
   /**
@@ -372,6 +433,15 @@
     }
 
     /**
+     * Returns the destination file that is the result of copying
+     * <code>objectFile</code> to <code>destDir</code>.
+     * @return
+     */
+    public File getDestination() {
+      return this.destination;
+    }
+
+    /**
      * {@inheritDoc}
      */
     public void apply() throws ApplicationException {

--
Gitblit v1.10.0