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

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

- ServerController now holds the functionality for starting/stopping a server.

- FileManager supports file management operations for copy and delete while handling event notification for applications.

- Most of the functionality for the upgrader's file management tasks are here. Still left to do are functions related to configuration and schema migration.
8 files added
12 files modified
3730 ■■■■ changed files
opends/src/quicksetup/org/opends/quicksetup/Application.java 340 ●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/ApplicationException.java 16 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/Configuration.java 224 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/Installation.java 572 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/Launcher.java 1 ●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/QuickSetup.java 32 ●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/Status.java 147 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java 27 ●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/installer/offline/OfflineInstaller.java 3 ●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/installer/webstart/WebStartInstaller.java 16 ●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/resources/Resources.properties 42 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/ui/CurrentStepPanel.java 2 ●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/uninstaller/Uninstaller.java 432 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/upgrader/UpgradeOracle.java 120 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/upgrader/UpgradeUserData.java 59 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java 633 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/upgrader/UpgraderCliHelper.java 130 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/util/FileManager.java 391 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/util/ServerController.java 478 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/util/Utils.java 65 ●●●●● patch | view | raw | blame | history
opends/src/quicksetup/org/opends/quicksetup/Application.java
@@ -35,8 +35,7 @@
import org.opends.quicksetup.util.ProgressMessageFormatter;
import org.opends.quicksetup.ui.QuickSetupDialog;
import org.opends.quicksetup.ui.QuickSetupStepPanel;
import javax.naming.NamingException;
import org.opends.quicksetup.ui.FramePanel;
import javax.swing.*;
import java.io.*;
import java.util.*;
@@ -51,12 +50,6 @@
 */
public abstract class Application implements ProgressNotifier, Runnable {
  /**
   * The path to the Configuration LDIF file.
   */
  static protected final String CONFIG_PATH_RELATIVE =
      "config" + File.separator + "config.ldif";
  static private final Logger LOG =
          Logger.getLogger(Application.class.getName());
@@ -115,6 +108,8 @@
  private UserData userData;
  private Installation installation;
  /** Formats progress messages. */
  protected ProgressMessageFormatter formatter;
@@ -176,7 +171,22 @@
   */
  public boolean isFinished()
  {
    return getStatus().isLast();
    return getCurrentProgressStep().isLast();
  }
  /**
   * Gets the OpenDS installation associated with the execution of this
   * command.
   * @return Installation object representing the current OpenDS installation
   */
  public Installation getInstallation() {
    if (installation == null) {
      String installPath = getInstallationPath();
      if (installPath != null) {
        installation = new Installation(installPath);
      }
    }
    return installation;
  }
  /**
@@ -186,7 +196,7 @@
   * @return the UserData object representing the parameters provided
   * by the user to do the installation.
   */
  protected UserData getUserData()
  public UserData getUserData()
  {
    if (userData == null) {
      userData = createUserData();
@@ -202,7 +212,7 @@
   */
  public void notifyListenersDone(Integer ratioWhenCompleted) {
    notifyListeners(ratioWhenCompleted,
            getSummary(getStatus()),
            getSummary(getCurrentProgressStep()),
            formatter.getFormattedDone() + formatter.getLineBreak());
  }
@@ -220,8 +230,8 @@
      String newLogDetail)
  {
    ProgressUpdateEvent ev =
        new ProgressUpdateEvent(getStatus(), ratio, currentPhaseSummary,
            newLogDetail);
        new ProgressUpdateEvent(getCurrentProgressStep(), ratio,
                currentPhaseSummary, newLogDetail);
    for (ProgressUpdateListener l : listeners)
    {
      l.progressUpdate(ev);
@@ -237,7 +247,7 @@
   * current installation progress in formatted form.
   */
  public void notifyListeners(Integer ratio, String currentPhaseSummary) {
    notifyListeners(ratio, getSummary(getStatus()),
    notifyListeners(ratio, getSummary(getCurrentProgressStep()),
        formatter.getFormattedWithPoints(currentPhaseSummary));
  }
@@ -251,7 +261,7 @@
   * @return the value associated to the key in the properties file.
   * properties file.
   */
  protected String getMsg(String key)
  public String getMsg(String key)
  {
    return getI18n().getMsg(key);
  }
@@ -271,7 +281,7 @@
   * @param args the arguments to be passed to generate the resulting value.
   * @return the value associated to the key in the properties file.
   */
  protected String getMsg(String key, String[] args)
  public String getMsg(String key, String[] args)
  {
    return getI18n().getMsg(key, args);
  }
@@ -291,7 +301,7 @@
   * @param t the throwable for which we want to get a message.
   * @return a localized message for a given properties key and throwable.
   */
  protected String getThrowableMsg(String key, Throwable t)
  public String getThrowableMsg(String key, Throwable t)
  {
    return getThrowableMsg(key, null, t);
  }
@@ -358,7 +368,7 @@
   * representation
   * @return the formatted representation of an warning for the given text.
   */
  protected String getFormattedWarning(String text)
  public String getFormattedWarning(String text)
  {
    return formatter.getFormattedWarning(text, false);
  }
@@ -383,7 +393,7 @@
   * @return the formatted representation of a log error message for the given
   * text.
   */
  protected String getFormattedLogError(String text)
  public String getFormattedLogError(String text)
  {
    return formatter.getFormattedLogError(text);
  }
@@ -394,7 +404,7 @@
   * representation
   * @return the formatted representation of a log message for the given text.
   */
  protected String getFormattedLog(String text)
  public String getFormattedLog(String text)
  {
    return formatter.getFormattedLog(text);
  }
@@ -403,7 +413,7 @@
   * Returns the formatted representation of the 'Done' text string.
   * @return the formatted representation of the 'Done' text string.
   */
  protected String getFormattedDone()
  public String getFormattedDone()
  {
    return formatter.getFormattedDone();
  }
@@ -415,7 +425,7 @@
   * @param text the String to which add points.
   * @return the formatted representation of the '.....' text string.
   */
  protected String getFormattedWithPoints(String text)
  public String getFormattedWithPoints(String text)
  {
    return formatter.getFormattedWithPoints(text);
  }
@@ -428,7 +438,7 @@
   * @return the formatted representation of a progress message for the given
   * text.
   */
  protected String getFormattedProgress(String text)
  public String getFormattedProgress(String text)
  {
    return formatter.getFormattedProgress(text);
  }
@@ -453,7 +463,7 @@
   * Returns the line break formatted.
   * @return the line break formatted.
   */
  protected String getLineBreak()
  public String getLineBreak()
  {
    return formatter.getLineBreak();
  }
@@ -472,139 +482,14 @@
   * notify the ProgressUpdateListeners of this fact.
   * @param newLogDetail the new log detail.
   */
  protected void notifyListeners(String newLogDetail)
  public void notifyListeners(String newLogDetail)
  {
    Integer ratio = getRatio(getStatus());
    String currentPhaseSummary = getSummary(getStatus());
    Integer ratio = getRatio(getCurrentProgressStep());
    String currentPhaseSummary = getSummary(getCurrentProgressStep());
    notifyListeners(ratio, currentPhaseSummary, newLogDetail);
  }
  /**
   * This methods starts the server.
   * @throws org.opends.quicksetup.QuickSetupException if something goes wrong.
   */
  protected void startServer() throws QuickSetupException {
    notifyListeners(getFormattedProgress(getMsg("progress-starting")) +
        getLineBreak());
    ArrayList<String> argList = new ArrayList<String>();
    if (Utils.isWindows())
    {
      argList.add(Utils.getPath(getBinariesPath(),
              Utils.getWindowsStartFileName()));
    } else
    {
      argList.add(Utils.getPath(getBinariesPath(),
              Utils.getUnixStartFileName()));
    }
    String[] args = new String[argList.size()];
    argList.toArray(args);
    ProcessBuilder pb = new ProcessBuilder(args);
    Map<String, String> env = pb.environment();
    env.put("JAVA_HOME", System.getProperty("java.home"));
    /* Remove JAVA_BIN to be sure that we use the JVM running the installer
     * JVM to start the server.
     */
    env.remove("JAVA_BIN");
    try
    {
      String startedId = getStartedId();
      Process process = pb.start();
      BufferedReader err =
          new BufferedReader(new InputStreamReader(process.getErrorStream()));
      BufferedReader out =
          new BufferedReader(new InputStreamReader(process.getInputStream()));
      StartReader errReader = new StartReader(err, startedId, true);
      StartReader outputReader = new StartReader(out, startedId, false);
      while (!errReader.isFinished() && !outputReader.isFinished())
      {
        try
        {
          Thread.sleep(100);
        } catch (InterruptedException ie)
        {
        }
      }
      // Check if something wrong occurred reading the starting of the server
      QuickSetupException ex = errReader.getException();
      if (ex == null)
      {
        ex = outputReader.getException();
      }
      if (ex != null)
      {
        throw ex;
      } else
      {
        /*
         * There are no exceptions from the readers and they are marked as
         * finished. This means that the server has written in its output the
         * message id informing that it started. So it seems that everything
         * went fine.
         *
         * However we can have issues with the firewalls or do not have rights
         * to connect.  Just check if we can connect to the server.
         * Try 5 times with an interval of 1 second between try.
         */
        boolean connected = false;
        for (int i=0; i<5 && !connected; i++)
        {
          String ldapUrl = "ldap://localhost:"+userData.getServerPort();
          try
          {
            Utils.createLdapContext(
                ldapUrl,
                userData.getDirectoryManagerDn(),
                userData.getDirectoryManagerPwd(), 3000, null);
            connected = true;
          }
          catch (NamingException ne)
          {
          }
          if (!connected)
          {
            try
            {
              Thread.sleep(1000);
            }
            catch (Throwable t)
            {
            }
          }
        }
        if (!connected)
        {
          if (Utils.isWindows())
          {
            String[] arg = {String.valueOf(userData.getServerPort())};
            throw new QuickSetupException(QuickSetupException.Type.START_ERROR,
                getMsg("error-starting-server-in-windows", arg), null);
          }
          else
          {
            String[] arg = {String.valueOf(userData.getServerPort())};
            throw new QuickSetupException(QuickSetupException.Type.START_ERROR,
                getMsg("error-starting-server-in-unix", arg), null);
          }
        }
      }
    } catch (IOException ioe)
    {
      throw new QuickSetupException(QuickSetupException.Type.START_ERROR,
          getThrowableMsg("error-starting-server", ioe), ioe);
    }
  }
  /**
   * Returns the initial wizard step.
   * @return Step representing the first step to show in the wizard
   */
@@ -648,31 +533,6 @@
  protected abstract String getInstallationPath();
  /**
   * Returns the config file path.
   * @return the config file path.
   */
  protected String getConfigFilePath()
  {
    return Utils.getPath(getInstallationPath(), CONFIG_PATH_RELATIVE);
  }
  /**
   * Returns the path to the binaries.
   * @return the path to the binaries.
   */
  protected abstract String getBinariesPath();
  /**
   * Returns the Message ID indicating that the server has started.
   * @return the Message ID indicating that the server has started.
   */
  private String getStartedId()
  {
    InstallerHelper helper = new InstallerHelper();
    return helper.getStartedId();
  }
  /**
   * Returns the tab formatted.
   * @return the tab formatted.
   */
@@ -682,20 +542,10 @@
  }
  /**
   * Returns the path to the libraries.
   * @return the path to the libraries.
   */
  protected String getLibrariesPath()
  {
    return Utils.getPath(Utils.getInstallPathFromClasspath(),
        Utils.getLibrariesRelativePath());
  }
  /**
   * Gets the current step.
   * @return ProgressStep representing the current step
   */
  public abstract ProgressStep getStatus();
  public abstract ProgressStep getCurrentProgressStep();
  /**
   * Gets an integer representing the amount of processing
@@ -754,13 +604,17 @@
   * @param dlg QuickSetupDialog used
   * @return JPanel frame panel
   */
  abstract public JPanel createFramePanel(QuickSetupDialog dlg);
  public JPanel createFramePanel(QuickSetupDialog dlg) {
    return new FramePanel(dlg.getStepsPanel(),
            dlg.getCurrentStepPanel(),
            dlg.getButtonsPanel());
  }
  /**
   * Returns the set of wizard steps used in this application's wizard.
   * @return Set of Step objects representing wizard steps
   */
  abstract public Set<WizardStep> getWizardSteps();
  abstract public Set<? extends WizardStep> getWizardSteps();
  /**
   * Creates a wizard panel given a specific step.
@@ -929,111 +783,6 @@
  }
  /**
   * This class is used to read the standard error and standard output of the
   * Start process.
   *
   * When a new log message is found notifies the ProgressUpdateListeners
   * of it. If an error occurs it also notifies the listeners.
   *
   */
  private class StartReader
  {
    private QuickSetupException ex;
    private boolean isFinished;
    private boolean isFirstLine;
    /**
     * The protected constructor.
     * @param reader the BufferedReader of the start process.
     * @param startedId the message ID that this class can use to know whether
     * the start is over or not.
     * @param isError a boolean indicating whether the BufferedReader
     * corresponds to the standard error or to the standard output.
     */
    public StartReader(final BufferedReader reader, final String startedId,
        final boolean isError)
    {
      final String errorTag =
          isError ? "error-reading-erroroutput" : "error-reading-output";
      isFirstLine = true;
      Thread t = new Thread(new Runnable()
      {
        public void run()
        {
          try
          {
            String line = reader.readLine();
            while (line != null)
            {
              StringBuffer buf = new StringBuffer();
              if (!isFirstLine)
              {
                buf.append(formatter.getLineBreak());
              }
              if (isError)
              {
                buf.append(getFormattedLogError(line));
              } else
              {
                buf.append(getFormattedLog(line));
              }
              notifyListeners(buf.toString());
              isFirstLine = false;
              if (line.indexOf("id=" + startedId) != -1)
              {
                isFinished = true;
              }
              line = reader.readLine();
            }
          } catch (IOException ioe)
          {
            String errorMsg = getThrowableMsg(errorTag, ioe);
            ex =
                new QuickSetupException(QuickSetupException.Type.START_ERROR,
                    errorMsg, ioe);
          } catch (Throwable t)
          {
            String errorMsg = getThrowableMsg(errorTag, t);
            ex =
                new QuickSetupException(QuickSetupException.Type.START_ERROR,
                    errorMsg, t);
          }
          isFinished = true;
        }
      });
      t.start();
    }
    /**
     * Returns the QuickSetupException that occurred reading the Start error and
     * output or <CODE>null</CODE> if no exception occurred.
     * @return the exception that occurred reading or <CODE>null</CODE> if
     * no exception occurred.
     */
    public QuickSetupException getException()
    {
      return ex;
    }
    /**
     * Returns <CODE>true</CODE> if the server starting process finished
     * (successfully or not) and <CODE>false</CODE> otherwise.
     * @return <CODE>true</CODE> if the server starting process finished
     * (successfully or not) and <CODE>false</CODE> otherwise.
     */
    public boolean isFinished()
    {
      return isFinished;
    }
  }
  /**
   * This class is used to notify the ProgressUpdateListeners of events
   * that are written to the standard error.  It is used in WebStartInstaller
   * and in OfflineInstaller.  These classes just create a ErrorPrintStream and
@@ -1150,4 +899,5 @@
      println(new String(b, off, len));
    }
  }
}
opends/src/quicksetup/org/opends/quicksetup/ApplicationException.java
@@ -84,12 +84,28 @@
    WINDOWS_SERVICE_ERROR,
    /**
     * Application specific error.
     */
    APPLICATION,
    /**
     * A bug (for instance when we throw an IllegalStateException).
     */
    BUG
  }
  /**
   * Creates a new ApplicationException of type FILE_SYSTEM_ERROR.
   * @param msg localized exception message
   * @param e Exception cause
   * @return ApplicationException with Type property being FILE_SYSTEM_ERROR
   */
  public static ApplicationException createFileSystemException(String msg,
                                                               Exception e) {
    return new ApplicationException(Type.FILE_SYSTEM_ERROR, msg, e);
  }
  /**
   * The constructor of the ApplicationException.
   * @param type the type of error we have.
   * @param localizedMsg a localized string describing the problem.
opends/src/quicksetup/org/opends/quicksetup/Configuration.java
New file
@@ -0,0 +1,224 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.quicksetup;
import java.io.File;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Set;
import java.util.HashSet;
/**
 * Represents the contents of an OpenDS configuration file.
 */
public class Configuration {
  private String contents = null;
  private File file = null;
  /**
   * Create a Configuration from a file.
   * @param file config.ldif file
   */
  public Configuration(File file) {
    if (file == null) {
      throw new NullPointerException("config file cannot be null");
    } else if (
            // Leave open the possibility that the file might be
            // config.ldif.<svn rev>
            !file.getName().startsWith("config.ldif")) {
      throw new IllegalArgumentException("file must be a config.ldif file");
    }
    this.file = file;
  }
  /**
   * Returns the list of directory manager dns as they appear in the
   * configuration file.
   *
   * @return the list of directory manager dns as they appear in the
   *         configuration file.
   * @throws IOException if there were problems reading the information from
   * the configuration file.
   */
  public Set<String> getDirectoryManagerDns() throws IOException {
    return getConfigurationValues("ds-cfg-alternate-bind-dn");
  }
  /**
   * Provides the LDAP port as is specified in the config.ldif file.
   *
   * @return the LDAP port specified in the config.ldif file.
   * @throws IOException if there were problems reading the information from
   * the configuration file.
   */
  public int getPort() throws IOException {
    return getPort("ds-cfg-listen-port");
  }
  /**
   * Provides the LDAP secure port as is specified in the config.ldif file.
   *
   * @return the LDAP secure port specified in the config.ldif file.
   * @throws IOException if there were problems reading the information from
   * the configuration file.
   */
  public int getSecurePort() throws IOException {
    // TODO find out which is the attribute for this port.
    return getPort("ds-cfg-listen-secure-port");
  }
  /**
   * Returns the list of paths where the logs files are located as they appear
   * in the configuration file.
   *
   * @return the list of paths where the logs files are located as they appear
   *         in the configuration file.
   * @throws IOException if there were problems reading the information from
   * the configuration file.
   */
  public Set<String> getLogPaths() throws IOException {
    return getConfigurationValues("ds-cfg-log-file");
  }
  private int getPort(String portAttr) throws IOException {
    int port = -1;
    int index = getContents().indexOf("cn=ldap connection handler");
    if (index != -1) {
      String attrWithPoints = portAttr + ":";
      int index1 = getContents().indexOf(attrWithPoints, index);
      if (index1 != -1) {
        int index2 =
                getContents().indexOf(
                        System.getProperty("line.separator"), index1);
        if (index2 != -1) {
          String sPort =
                  getContents().substring(attrWithPoints.length() +
                          index1,
                          index2).trim();
          try {
            port = Integer.parseInt(sPort);
          } catch (NumberFormatException nfe) {
            // do nothing;
          }
        }
      }
    }
    return port;
  }
  /**
   * Indicates whether the config.ldif file has been modified (compared to what
   * we had in the zip file). This is used to know if we have configured the
   * current binaries or not.
   *
   * @return <CODE>true</CODE> if the config.ldif file has been modified, or
   *         <CODE>false</CODE> if not.
   * @throws IOException if there were problems reading the information from
   * the configuration file.
   */
  public boolean hasBeenModified() throws IOException {
    boolean isConfigFileModified = getPort() != 389;
    if (!isConfigFileModified) {
      // TODO: this is not really stable
      // Note: a better way might be to diff this file with
      // /config/ldif/upgrade/config.ldif.<svn rev>
      isConfigFileModified =
              getContents().indexOf("# cddl header start") == -1;
    }
    return isConfigFileModified;
  }
  /**
   * Provides the contents of the config.ldif file in a String.
   *
   * @return a String representing the contents of the config.ldif file.
   * @throws IOException if there was a problem reading the file
   */
  private String getContents() throws IOException {
    if (contents == null) {
      load();
    }
    return contents;
  }
  /**
   * Loads the contents of the configuration file into memory.
   * @throws IOException if there were problems loading the file
   */
  public void load() throws IOException {
    StringBuffer buf = new StringBuffer();
    FileReader reader = new FileReader(file);
    BufferedReader in = new BufferedReader(reader);
    String line;
    // We do not care about encoding: we are just interested in the ports
    while ((line = in.readLine()) != null) {
      buf.append(line).append(System.getProperty("line.separator"));
    }
    reader.close();
    contents = buf.toString().toLowerCase();
  }
  private Set<String> getConfigurationValues(String attrName)
          throws IOException
  {
    Set<String> set = new HashSet<String>();
    attrName += ":";
    int index1 = getContents().indexOf(attrName);
    while (index1 != -1) {
      int index2 = getContents().indexOf(
              System.getProperty("line.separator"), index1);
      String value;
      if (index2 > (index1 + attrName.length())) {
        value = getContents().substring(attrName.length() + index1,
                index2).trim();
      } else if (getContents().length() > (index1 + attrName.length())) {
        // Assume end of file
        value = getContents().substring(
                attrName.length() + index1).trim();
      } else {
        value = null;
      }
      if ((value != null) && (value.length() > 0)) {
        set.add(value);
      }
      index1 = getContents().indexOf(attrName,
              index1 + attrName.length());
    }
    return set;
  }
}
opends/src/quicksetup/org/opends/quicksetup/Installation.java
New file
@@ -0,0 +1,572 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.quicksetup;
import java.io.File;
import java.io.IOException;
import org.opends.quicksetup.util.Utils;
/**
 * This class represents the physical state of an OpenDS installation.
 * All the operations are dependent upon the root directory that is
 * specified in the constructor.
 */
public class Installation {
  /**
   * Relative path to OpenDS jar files.
   */
  public static final String[] OPEN_DS_JAR_RELATIVE_PATHS =
          {"lib/quicksetup.jar", "lib/OpenDS.jar", "lib/je.jar"};
  /**
   * The relative path where all the Windows binaries (batch files) are.
   */
  public static final String WINDOWS_BINARIES_PATH_RELATIVE = "bat";
  /**
   * The relative path where all the UNIX binaries (scripts) are.
   */
  public static final String UNIX_BINARIES_PATH_RELATIVE = "bin";
  /**
   * The relative path where all the libraries (jar files) are.
   */
  public static final String LIBRARIES_PATH_RELATIVE = "lib";
  /**
   * The relative path where the database files are.
   */
  public static final String DATABASES_PATH_RELATIVE = "db";
  /**
   * The relative path where the log files are.
   */
  public static final String LOGS_PATH_RELATIVE = "logs";
  /**
   * The relative path where the LDIF files are.
   */
  public static final String LDIFS_PATH_RELATIVE = "ldif";
  /**
   * The relative path where the backup files are.
   */
  public static final String BACKUPS_PATH_RELATIVE = "bak";
  /**
   * The relative path where the config files are.
   */
  public static final String CONFIG_PATH_RELATIVE = "config";
  /**
   * The relative path where the config files are.
   */
  public static final String HISTORY_PATH_RELATIVE = "history";
  /**
   * Path to the config/upgrade directory where upgrade base files are stored.
   */
  public static final String CONFIG_UPGRADE_PATH = "upgrade";
  /**
   * Relative path to the change log database directory.
   */
  public static final String CHANGELOG_PATH_RELATIVE = "changelogDb";
  /**
   * Relative path to the locks directory.
   */
  public static final String LOCKS_PATH_RELATIVE = "locks";
  /**
   * The relative path to the current Configuration LDIF file.
   */
  public static final String CURRENT_CONFIG_FILE_NAME = "config.ldif";
  /**
   * The relative path to the current Configuration LDIF file.
   */
  public static final String BASE_CONFIG_FILE_PREFIX ="config.ldif.";
  /**
   * The UNIX setup script file name.
   */
  public static final String UNIX_SETUP_FILE_NAME = "setup";
  /**
   * The Windows setup batch file name.
   */
  public static final String WINDOWS_SETUP_FILE_NAME = "setup.bat";
  /**
   * The UNIX uninstall script file name.
   */
  public static final String UNIX_UNINSTALL_FILE_NAME = "uninstall";
  /**
   * The Windows uninstall batch file name.
   */
  public static final String WINDOWS_UNINSTALL_FILE_NAME = "uninstall.bat";
  /**
   * The UNIX uninstall script file name.
   */
  public static final String UNIX_UPGRADE_FILE_NAME = "upgrade";
  /**
   * The Windows uninstall batch file name.
   */
  public static final String WINDOWS_UPGRADE_FILE_NAME = "upgrade.bat";
  /**
   * The UNIX start script file name.
   */
  public static final String UNIX_START_FILE_NAME = "start-ds";
  /**
   * The Windows start batch file name.
   */
  public static final String WINDOWS_START_FILE_NAME = "start-ds.bat";
  /**
   * The UNIX stop script file name.
   */
  public static final String UNIX_STOP_FILE_NAME = "stop-ds";
  /**
   * The Windows stop batch file name.
   */
  public static final String WINDOWS_STOP_FILE_NAME = "stop-ds.bat";
  /**
   * The UNIX status panel script file name.
   */
  public static final String UNIX_STATUSPANEL_FILE_NAME = "statuspanel";
  /**
   * The Windows status panel batch file name.
   */
  public static final String WINDOWS_STATUSPANEL_FILE_NAME = "statuspanel.bat";
  /**
   * The UNIX status command line script file name.
   */
  public static final String UNIX_STATUSCLI_FILE_NAME = "status";
  /**
   * The Windows status command line batch file name.
   */
  public static final String WINDOWS_STATUSCLI_FILE_NAME = "status.bat";
  /**
   * Name of the file kept in the histoy directory containing logs
   * of upgrade and reversions.
   */
  public static final String HISTORY_LOG_FILE_NAME = "log";
  private File rootDirectory;
  private Status status;
  private Configuration configuration;
  private Configuration baseConfiguration;
  /**
   * Creates a new instance from a root directory specified as a string.
   *
   * @param rootDirectory of this installation
   */
  public Installation(String rootDirectory) {
    this(new File(rootDirectory));
  }
  /**
   * Creates a new instance from a root directory specified as a File.
   *
   * @param rootDirectory of this installation
   */
  public Installation(File rootDirectory) {
    setRootDirectory(rootDirectory);
  }
  /**
   * Gets the top level directory of an OpenDS installation.
   *
   * @return File object representing the top level directory of
   *         and OpenDS installation
   */
  public File getRootDirectory() {
    return this.rootDirectory;
  }
  /**
   * Sets the root directory of this installation.
   *
   * @param rootDirectory File of this installation
   * @throws NullPointerException if root directory is null
   */
  public void setRootDirectory(File rootDirectory) throws NullPointerException {
    if (rootDirectory == null) {
      throw new NullPointerException("install root cannot be null");
    }
    this.rootDirectory = rootDirectory;
  }
  /**
   * Gets the Configuration object representing this file.  The
   * current configuration is stored in config/config.ldif.
   *
   * @return Configuration representing the current configuration.
   */
  public Configuration getCurrentConfiguration() {
    if (configuration == null) {
      configuration = new Configuration(getCurrentConfigurationFile());
    }
    return configuration;
  }
  /**
   * Gets the Configuration object representing this file.  The base
   * configuration is stored in config/upgrade/config.ldif.[svn rev].
   *
   * @return Configuration object representing the base configuration.
   * @throws QuickSetupException if there was a problem determining the
   * svn rev number.
   */
  public Configuration getBaseConfiguration() throws QuickSetupException {
    if (baseConfiguration == null) {
      baseConfiguration = new Configuration(getBaseConfigurationFile());
    }
    return baseConfiguration;
  }
  /**
   * Gets the current status of this installation.
   * @return Status object representing the state of this installation.
   */
  public Status getStatus() {
    if (status == null) {
      status = new Status(this);
    }
    return status;
  }
  /**
   * Returns the path to the libraries.
   *
   * @return the path to the libraries.
   */
  public File getLibrariesDirectory() {
    return new File(getRootDirectory(), LIBRARIES_PATH_RELATIVE);
  }
  /**
   * Creates a File object representing config/upgrade/schema.ldif.current
   * which the server creates the first time it starts if there are schema
   * customizations.
   *
   * @return File object with no
   */
  public File getSchemaConcatFile() {
    return new File(getConfigurationUpgradeDirectory(),
                    "schema.ldif.current");
  }
  /**
   * Creates a File object representing config/upgrade/schema.ldif.current
   * which the server creates the first time it starts if there are schema
   * customizations.
   *
   * @return File object with no
   * @throws QuickSetupException if there was a problem determining the
   *                             svn revision number
   */
  public File getBaseSchemaFile() throws QuickSetupException {
    return new File(getConfigurationUpgradeDirectory(),
                  "config/upgrade/schema.ldif." + getSvnRev().toString());
  }
  /**
   * Creates a File object representing config/upgrade/schema.ldif.current
   * which the server creates the first time it starts if there are schema
   * customizations.
   *
   * @return File object with no
   * @throws QuickSetupException if there was a problem determining the
   *                             svn revision number
   */
  public File getBaseConfigurationFile() throws QuickSetupException {
    return new File(getConfigurationUpgradeDirectory(),
            BASE_CONFIG_FILE_PREFIX + getSvnRev().toString());
  }
  /**
   * Gets the SVN revision number of the build by looking for the 'base'
   * configuration file config/upgrade/config.ldif.[svn rev #].
   *
   * @return Integer representing the svn number
   * @throws QuickSetupException if for some reason the number could not
   *                             be determined
   */
  public Integer getSvnRev() throws QuickSetupException {
    Integer rev = null;
    File configUpgradeDir = getConfigurationUpgradeDirectory();
    if (configUpgradeDir.exists()) {
      String[] upgradeFileNames = configUpgradeDir.list();
      for (String upgradeFileName : upgradeFileNames) {
        // This code assumes that there will only be one file
        // in the config/upgrade directory having the prefix
        // config.ldif. and that it will end with the SVN
        // revision number of the build of the current
        // installation.
        if (upgradeFileName.startsWith(BASE_CONFIG_FILE_PREFIX)) {
          String svnRefString = upgradeFileName.substring(
                  BASE_CONFIG_FILE_PREFIX.length());
          try {
            rev = new Integer(svnRefString);
          } catch (NumberFormatException nfe) {
            // ignore for now; try other files
          }
          if (rev != null) {
            break;
          }
        }
      }
    }
    if (rev == null) {
      // TODO: i18n
      throw new QuickSetupException(QuickSetupException.Type.FILE_SYSTEM_ERROR,
              "Could not determine SVN rev", null);
    }
    return rev;
  }
  /**
   * Returns the path to the configuration file of the directory server.  Note
   * that this method assumes that this code is being run locally.
   *
   * @return the path of the configuration file of the directory server.
   */
  public File getCurrentConfigurationFile() {
    return new File(getConfigurationDirectory(), CURRENT_CONFIG_FILE_NAME);
  }
  /**
   * Returns the relative path of the directory containing the binaries/scripts
   * of the Open DS installation.  The path is relative to the installation
   * path.
   *
   * @return the relative path of the directory containing the binaries/scripts
   *         of the Open DS installation.
   */
  public File getBinariesDirectory() {
    File binPath;
    if (Utils.isWindows()) {
      binPath = new File(getRootDirectory(), WINDOWS_BINARIES_PATH_RELATIVE);
    } else {
      binPath = new File(getRootDirectory(), UNIX_BINARIES_PATH_RELATIVE);
    }
    return binPath;
  }
  /**
   * Returns the path to the database files under the install path.
   *
   * @return the path to the database files under the install path.
   */
  public File getDatabasesDirectory() {
    return new File(getRootDirectory(), DATABASES_PATH_RELATIVE);
  }
  /**
   * Returns the path to the backup files under the install path.
   *
   * @return the path to the backup files under the install path.
   */
  public File getBackupDirectory() {
    return new File(getRootDirectory(), BACKUPS_PATH_RELATIVE);
  }
  /**
   * Returns the path to the LDIF files under the install path.
   *
   * @return the path to the LDIF files under the install path.
   */
  public File geLdifDirectory() {
    return new File(getRootDirectory(), LDIFS_PATH_RELATIVE);
  }
  /**
   * Returns the path to the config files under the install path.
   *
   * @return the path to the config files under the install path.
   */
  public File getConfigurationDirectory() {
    return new File(getRootDirectory(), CONFIG_PATH_RELATIVE);
  }
  /**
   * Returns the path to the log files under the install path.
   *
   * @return the path to the log files under the install path.
   */
  public File getLogsDirectory() {
    return new File(getRootDirectory(), LOGS_PATH_RELATIVE);
  }
  /**
   * Returns the directory where the lock files are stored.
   *
   * @return the path to the lock files.
   */
  public File getLocksDirectory() {
    return new File(getRootDirectory(), LOCKS_PATH_RELATIVE);
  }
  /**
   * Returns the directory where the lock files are stored.
   *
   * @return the path to the lock files.
   */
  public File getHistoryDirectory() {
    return new File(getRootDirectory(), HISTORY_PATH_RELATIVE);
  }
  /**
   * Creates a new directory in the history directory appropriate
   * for backing up an installation during an upgrade.
   * @return File representing a new backup directory.  The directory
   * can be assumed to exist if this method returns cleanly.
   * @throws IOException if an error occured creating the directory.
   */
  public File createHistoryBackupDirectory() throws IOException {
    File backupDirectory =
            new File(getHistoryDirectory(),
                    "upgrade-" + System.currentTimeMillis());
    if (backupDirectory.exists()) {
      backupDirectory.delete();
    }
    if (!backupDirectory.mkdirs()) {
      // TODO: i18n
      throw new IOException("failed to create history backup directory");
    }
    return backupDirectory;
  }
  /**
   * Gets the log file where the history of upgrades and reversions is kept.
   * @return File containing upgrade/reversion history.
   */
  public File getHistoryLogFile() {
    return new File(getHistoryDirectory(), HISTORY_LOG_FILE_NAME);
  }
  /**
   * Gets the directory config/upgrade.
   * @return File representing the config/upgrade directory
   */
  public File getConfigurationUpgradeDirectory() {
    return new File(getConfigurationDirectory(), CONFIG_UPGRADE_PATH);
  }
  /**
   * Gets the file responsible for stopping the server appropriate
   * for the current operating system.
   * @return File representing the stop command
   */
  public File getServerStartCommandFile() {
    File startCommandFile;
    if (Utils.isWindows()) {
      startCommandFile = new File(getBinariesDirectory(),
              WINDOWS_START_FILE_NAME);
    } else {
      startCommandFile = new File(getBinariesDirectory(),
              UNIX_START_FILE_NAME);
    }
    return startCommandFile;
  }
  /**
   * Gets the file responsible for stopping the server appropriate
   * for the current operating system.
   * @return File representing the stop command
   */
  public File getServerStopCommandFile() {
    File stopCommandFile;
    if (Utils.isWindows()) {
      stopCommandFile = new File(getBinariesDirectory(),
              WINDOWS_STOP_FILE_NAME);
    } else {
      stopCommandFile = new File(getBinariesDirectory(),
              UNIX_STOP_FILE_NAME);
    }
    return stopCommandFile;
  }
  /**
   * Returns the 'ldif' directory.
   *
   * @return the 'ldif' directory.
   */
  public File getLdifDirectory() {
    return new File(getRootDirectory(), LDIFS_PATH_RELATIVE);
  }
  /**
   * Returns the path to the quicksetup jar file.
   *
   * @return the path to the quicksetup jar file.
   */
  public File getQuicksetupJarFile() {
    return new File(getLibrariesDirectory(), "quicksetup.jar");
  }
  /**
   * Returns the path to the opends jar file.
   *
   * @return the path to the opends jar file.
   */
  public File getOpenDSJarFile() {
    return new File(getLibrariesDirectory(), "OpenDS.jar");
  }
  /**
   * Returns the path to the uninstall.bat file.
   *
   * @return the path to the uninstall.bat file.
   */
  public File getUninstallBatFile() {
    return new File(getRootDirectory(), "uninstall.bat");
  }
}
opends/src/quicksetup/org/opends/quicksetup/Launcher.java
@@ -183,7 +183,6 @@
   *         occurred.
   */
  protected int launchCli(String[] args, CliApplication cliApp) {
    System.out.println(getMsg("uninstall-launcher-launching-cli"));
    System.setProperty("org.opends.quicksetup.cli", "true");
    QuickSetupCli cli = new QuickSetupCli(cliApp, args);
opends/src/quicksetup/org/opends/quicksetup/QuickSetup.java
@@ -425,31 +425,9 @@
  }
  /**
   * Launch the installation of Open DS. Depending on whether we are running a
   * web start or not it will use on Installer object or other.
   *
   * Launch the QuickSetup application Open DS.
   */
  public void launchInstallation()
  {
    ProgressMessageFormatter formatter = getDialog().getFormatter();
    application.addProgressUpdateListener(this);
    new Thread(application).start();
    Thread t = new Thread(new Runnable()
    {
      public void run()
      {
        runDisplayUpdater();
      }
    });
    t.start();
  }
  /**
   * Launch the uninstallation of Open DS.
   *
   */
  public void launchUninstallation()
  public void launch()
  {
    application.addProgressUpdateListener(this);
    new Thread(application).start();
@@ -557,7 +535,11 @@
   */
  public void displayError(String msg, String title)
  {
    getDialog().displayError(msg, title);
    if (Utils.isCli()) {
      System.err.println(msg);
    } else {
      getDialog().displayError(msg, title);
    }
  }
  /**
opends/src/quicksetup/org/opends/quicksetup/Status.java
New file
@@ -0,0 +1,147 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.quicksetup;
import java.io.File;
import java.io.IOException;
/**
 * This class represents the current state of a particular installation.
 */
public class Status {
  private static boolean lockPathInitialized;
  private Installation installation;
  /**
   * Creates a status instance of the installation indicated by the
   * input parameter.
   * @param installation physical installation
   */
  public Status(Installation installation) {
    this.installation = installation;
  }
  /**
   * Indicates whether there is something installed or not.
   *
   * @return <CODE>true</CODE> if there is something installed under the
   *         binaries that we are running, or <CODE>false</CODE> if not.
   */
  public boolean isInstalled() {
    File rootDirectory = installation.getRootDirectory();
    return rootDirectory == null || !rootDirectory.exists() ||
            !rootDirectory.isDirectory();
  }
  /**
   * Determines whether or not the configuration has been modified for this
   * installation.
   * @return boolean where true means the configuration has been modified
   */
  public boolean configurationHasBeenModified() {
    // TODO: a better way might be to diff config.ldif with configuration
    // base in config/upgrade/config.ldif.<svn rev>
    boolean mod = false;
    try {
      mod = installation.getCurrentConfiguration().hasBeenModified();
    } catch (IOException e) {
      // do nothing for now;
    }
    return mod;
  }
  /**
   * Determines whether or not the schema has been modified for this
   * installation.
   * @return boolean where true means the schema has been modified
   */
  public boolean schemaHasBeenModified() {
    File f = installation.getSchemaConcatFile();
    return f.exists();
  }
  /**
   * Returns if the server is running on the given path.
   * NOTE: this method is to be called only when the OpenDS.jar class has
   * already been loaded as it uses classes in that jar.
   *
   * @return <CODE>true</CODE> if the server is running and <CODE>false</CODE>
   *         otherwise.
   * @throws java.io.IOException if there was a problem reading required
   * configuration information
   */
  public boolean isServerRunning() throws IOException {
    boolean isServerRunning;
    if (!lockPathInitialized) {
      File lockDirectory = installation.getLocksDirectory();
      System.setProperty(
              org.opends.server.util.ServerConstants.PROPERTY_LOCK_DIRECTORY,
              lockDirectory.getCanonicalPath());
      lockPathInitialized = true;
    }
    String lockFile =
            org.opends.server.core.LockFileManager.getServerLockFileName();
    StringBuilder failureReason = new StringBuilder();
    try {
      if (org.opends.server.core.LockFileManager.acquireExclusiveLock(lockFile,
              failureReason)) {
        org.opends.server.core.LockFileManager.releaseLock(lockFile,
                failureReason);
        isServerRunning = false;
      } else {
        isServerRunning = true;
      }
    }
    catch (Throwable t) {
      // Assume that if we cannot acquire the lock file the server is
      // running.
      isServerRunning = true;
    }
    return isServerRunning;
  }
  /**
   * Indicates whether there are database files under this installation.
   *
   * @return <CODE>true</CODE> if there are database files, or
   *         <CODE>false</CODE> if not.
   */
  public boolean dbFilesExist() {
    boolean dbFilesExist = false;
    File dbDir = installation.getDatabasesDirectory();
    File[] children = dbDir.listFiles();
    if ((children != null) && (children.length > 0)) {
      dbFilesExist = true;
    }
    return dbFilesExist;
  }
}
opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java
@@ -162,7 +162,7 @@
  public void finishClicked(final WizardStep cStep, final QuickSetup qs) {
    if (cStep == Step.REVIEW) {
        updateUserDataForReviewPanel(qs);
        qs.launchInstallation();
        qs.launch();
        qs.setCurrentStep(Step.PROGRESS);
    } else {
        throw new IllegalStateException(
@@ -250,9 +250,7 @@
    if (installStatus.isInstalled() && !forceToDisplaySetup) {
      p = dlg.getInstalledPanel();
    } else {
      p = new FramePanel(dlg.getStepsPanel(),
              dlg.getCurrentStepPanel(),
              dlg.getButtonsPanel());
      p = super.createFramePanel(dlg);
    }
    return p;
  }
@@ -260,7 +258,7 @@
  /**
   * {@inheritDoc}
   */
  public Set<WizardStep> getWizardSteps() {
  public Set<? extends WizardStep> getWizardSteps() {
    return Collections.unmodifiableSet(new HashSet<WizardStep>(lstSteps));
  }
@@ -365,7 +363,7 @@
  /**
   * {@inheritDoc}
   */
  public ProgressStep getStatus()
  public ProgressStep getCurrentProgressStep()
  {
    return status;
  }
@@ -438,7 +436,7 @@
    argList.add(CONFIG_CLASS_NAME);
    argList.add("-c");
    argList.add(getConfigFilePath());
    argList.add(Utils.getPath(getInstallation().getCurrentConfigurationFile()));
    argList.add("-p");
    argList.add(String.valueOf(getUserData().getServerPort()));
    argList.add("-j");
@@ -494,7 +492,7 @@
    argList.add(CONFIG_CLASS_NAME);
    argList.add("-f");
    argList.add(getConfigFilePath());
    argList.add(Utils.getPath(getInstallation().getCurrentConfigurationFile()));
    argList.add("-n");
    argList.add(getBackendName());
@@ -543,7 +541,7 @@
    argList.add(CONFIG_CLASS_NAME);
    argList.add("-f");
    argList.add(getConfigFilePath());
    argList.add(Utils.getPath(getInstallation().getCurrentConfigurationFile()));
    argList.add("-n");
    argList.add(getBackendName());
    argList.add("-l");
@@ -590,7 +588,7 @@
    argList.add(CONFIG_CLASS_NAME);
    argList.add("-f");
    argList.add(getConfigFilePath());
    argList.add(Utils.getPath(getInstallation().getCurrentConfigurationFile()));
    argList.add("-n");
    argList.add(getBackendName());
    argList.add("-t");
@@ -729,15 +727,6 @@
  }
  /**
   * {@inheritDoc}
   */
  protected String getBinariesPath()
  {
    return Utils.getPath(getInstallationPath(),
        Utils.getBinariesRelativePath());
  }
  /**
   * Validate the data provided by the user in the server settings panel and
   * update the userData object according to that content.
   *
opends/src/quicksetup/org/opends/quicksetup/installer/offline/OfflineInstaller.java
@@ -36,6 +36,7 @@
import org.opends.quicksetup.installer.Installer;
import org.opends.quicksetup.installer.InstallProgressStep;
import org.opends.quicksetup.util.Utils;
import org.opends.quicksetup.util.ServerController;
/**
 * This is an implementation of the Installer class that is used to install
@@ -112,7 +113,7 @@
      {
        notifyListeners(getTaskSeparator());
        status = InstallProgressStep.STARTING_SERVER;
        startServer();
        new ServerController(this).startServer();
      }
      status = InstallProgressStep.FINISHED_SUCCESSFULLY;
opends/src/quicksetup/org/opends/quicksetup/installer/webstart/WebStartInstaller.java
@@ -40,6 +40,7 @@
import org.opends.quicksetup.installer.InstallProgressStep;
import org.opends.quicksetup.util.Utils;
import org.opends.quicksetup.util.ZipExtractor;
import org.opends.quicksetup.util.ServerController;
/**
 * This is an implementation of the Installer class that is used to install
@@ -141,7 +142,7 @@
      {
        notifyListeners(getTaskSeparator());
        status = InstallProgressStep.STARTING_SERVER;
        startServer();
        new ServerController(this).startServer();
      }
      if (Utils.isWindows())
@@ -371,7 +372,7 @@
    ZipExtractor extractor =
            new ZipExtractor(is, minRatio, maxRatio,
            getUserData().getServerLocation(),
            getNumberZipEntries(),
            Utils.getNumberZipEntries(),
            getZipFileName(),
            this);
    extractor.extract();
@@ -423,17 +424,6 @@
  }
  /**
   * Returns the number of entries contained in the zip file.  This is used to
   * update properly the progress bar ratio.
   * @return the number of entries contained in the zip file.
   */
  private int getNumberZipEntries()
  {
    // TODO  we should get this dynamically during build
    return 83;
  }
  /**
   * {@inheritDoc}
   */
  protected String getInstallationPath()
opends/src/quicksetup/org/opends/quicksetup/resources/Resources.properties
@@ -157,6 +157,8 @@
#
# Upgrade command line messages
#
upgrade-launcher-description=This utility may be used to upgrade the Directory \
  Server to a newer version.
upgrade-launcher-usage=This utility may be used to upgrade the \
Directory Server to a newer version.\n\
Usage:  {0} {options}\n        where {options} include:\n\
@@ -332,6 +334,7 @@
review-step=Review
progress-step=Progress
confirm-uninstall-step=Uninstall Options
step-upgrade-choose-version=Choose Version
#
# Icon descriptions.  Used for accessibility.
@@ -579,6 +582,25 @@
summary-uninstall-finished-with-error=An error occurred.  Check 'Details' text \
area for more information.
summary-upgrade-not-started=Starting Upgrade...
summary-upgrade-initializing=Initializing Upgrade...
summary-upgrade-backing-up-db=Backing Up Data...
summary-upgrade-backing-up-files=Backing Up Files...
summary-upgrade-calculating-schema-customization=Calculating Schema \
  Customizations...
summary-upgrade-calculating-config-customization=Calculating Configuration\
  Customizations...
summary-upgrade-upgrading-components=Upgrading Components...
summary-upgrade-applying-schema-customization=Applying Schema \
  Customizations...
summary-upgrade-applying-config-customization=Applying Configuration\
  Customizations...
summary-upgrade-verifying=Verifying Upgrade...
summary-upgrade-history=Recording Upgrade History...
summary-upgrade-cleanup=Cleanup...
summary-upgrade-finished-successfully=Upgrade Finished Successfully
summary-upgrade-finished-with-errors=Upgrade Finished with Errors
#
# Progress messages
#
@@ -605,10 +627,11 @@
progress-deleting-installation-files=Deleting Files under the Installation Path:
progress-deleting-file=Deleting file {0}
progress-deleting-directory=Deleting directory {0}
progress-copying-file=Copying file {0} to {1}
progress-server-already-stopped=The Directory Server is already stopped.
progress-server-waiting-to-stop=Waiting for Server to stop...
progress-server-stopped=Server stopped.
deleting-file-does-not-exist=Path {0} does not exist.
file-does-not-exist=Path {0} does not exist.
#
# Progress errors
@@ -622,7 +645,6 @@
error-zipinputstreamnull=Could not retrieve zip file {0}.  The input stream \
is null.
bug-msg=An unexpected error occurred.
error-configuring=An error occurred configuring the server.
error-reflection=An unexpected error occurred while loading classes.
error-creating-temp-file=An error occurred creating the temporary file.
error-writing-to-temp-file=An error occurred writing to temporary file {0}.
@@ -650,7 +672,8 @@
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.
error-copying-file=Error copying file {0} to {1}.
info-ignoring-file=Ignoring {0} since {1} exists.
#
# Install Status: messages displayed in the offline quick setup
# if server is already installed.
@@ -665,3 +688,16 @@
installstatus-canoverwritecurrentinstall-msg=The Directory Server contains \
some database files.<br>If you continue with the setup the contents of these \
database files will be deleted.
upgrade-hypothetical-upgrade-success=Upgrade from version {0} to version {1} \
  is supported.
upgrade-hypothetical-reversion-success=Reversion from version {0} to version \
  {1} is supported.
upgrade-hypothetical-upgrade-failure=Upgrade from version {0} to version {1} \
  is not supported.  To upgrade You must uninstall the current server, install \
  the new server, and manually migrate your data.
upgrade-hypothetical-reversion-failure=Reversion from version {0} to version \
  {1} is not supported.  To revert versions you must uninstall the current \
  server, install the new server, and manually migrate your data.
upgrade-hypothetical-versions-the-same=This operation is unnecessary.  Both \
  new current and proposed version numbers are the same: {0}
opends/src/quicksetup/org/opends/quicksetup/ui/CurrentStepPanel.java
@@ -122,7 +122,7 @@
  private void createLayout(Application app)
  {
    Set<WizardStep> steps = app.getWizardSteps();
    Set<? extends WizardStep> steps = app.getWizardSteps();
    if (steps != null) {
      for (WizardStep step : steps) {
        QuickSetupStepPanel panel = app.createWizardStepPanel(step);
opends/src/quicksetup/org/opends/quicksetup/uninstaller/Uninstaller.java
@@ -34,10 +34,9 @@
import org.opends.quicksetup.ui.*;
import org.opends.quicksetup.util.Utils;
import org.opends.quicksetup.util.BackgroundTask;
import org.opends.quicksetup.util.ServerController;
import org.opends.server.tools.ConfigureWindowsService;
import org.opends.server.protocols.ldap.LDAPResultCode;
import javax.swing.*;
import java.io.*;
import java.util.*;
import java.awt.event.WindowEvent;
@@ -311,7 +310,7 @@
              if (qs.displayConfirmation(
                      getMsg("confirm-uninstall-server-not-running-msg"),
                      getMsg("confirm-uninstall-server-not-running-title"))) {
                qs.launchUninstallation();
                qs.launch();
                qs.setCurrentStep(getNextWizardStep(cStep));
              }
            } else {
@@ -319,7 +318,7 @@
                      getMsg("confirm-uninstall-server-running-msg"),
                      getMsg("confirm-uninstall-server-running-title"))) {
                getUserData().setStopServer(true);
                qs.launchUninstallation();
                qs.launch();
                qs.setCurrentStep(getNextWizardStep(cStep));
              } else {
                getUserData().setStopServer(false);
@@ -368,7 +367,7 @@
   * {@inheritDoc}
   */
  protected String getInstallationPath() {
    return null;
    return Utils.getInstallPathFromClasspath();
  }
  /**
@@ -403,14 +402,16 @@
            getFormattedSummary(getMsg("summary-deleting-installation-files")));
    String successMsg;
    Installation installation = getInstallation();
    String libPath = Utils.getPath(installation.getLibrariesDirectory());
    if (Utils.isCli()) {
      if (getUninstallUserData().getRemoveLibrariesAndTools()) {
        String[] arg = new String[1];
        if (Utils.isWindows()) {
          arg[0] = getUninstallBatFile() + getLineBreak() +
                  getTab() + getLibrariesPath();
          arg[0] = installation.getUninstallBatFile() + getLineBreak() +
                  getTab() + libPath;
        } else {
          arg[0] = getLibrariesPath();
          arg[0] = libPath;
        }
        successMsg = getMsg(
                "summary-uninstall-finished-successfully-remove-jarfiles-cli",
@@ -420,7 +421,7 @@
      }
    } else {
      if (getUninstallUserData().getRemoveLibrariesAndTools()) {
        String[] arg = {getLibrariesPath()};
        String[] arg = {libPath};
        successMsg = getMsg(
                "summary-uninstall-finished-successfully-remove-jarfiles", arg);
      } else {
@@ -504,7 +505,7 @@
      boolean displaySeparator = false;
      if (getUserData().getStopServer()) {
        status = UninstallProgressStep.STOPPING_SERVER;
        stopServer();
        new ServerController(this).stopServer();
        displaySeparator = true;
      }
      if (isWindowsServiceEnabled()) {
@@ -586,7 +587,7 @@
  /**
   * {@inheritDoc}
   */
  public ProgressStep getStatus() {
  public ProgressStep getCurrentProgressStep() {
    return status;
  }
@@ -637,16 +638,7 @@
  /**
   * {@inheritDoc}
   */
  public JPanel createFramePanel(QuickSetupDialog dlg) {
    return new FramePanel(dlg.getStepsPanel(),
            dlg.getCurrentStepPanel(),
            dlg.getButtonsPanel());
  }
  /**
   * {@inheritDoc}
   */
  public Set<WizardStep> getWizardSteps() {
  public Set<? extends WizardStep> getWizardSteps() {
    Set<WizardStep> setSteps = new HashSet<WizardStep>();
    setSteps.add(Step.CONFIRM_UNINSTALL);
    setSteps.add(Step.PROGRESS);
@@ -667,116 +659,6 @@
  }
  /**
   * This methods stops the server.
   *
   * @throws ApplicationException if something goes wrong.
   */
  private void stopServer() throws ApplicationException {
    notifyListeners(getFormattedProgress(getMsg("progress-stopping")) +
            getLineBreak());
    ArrayList<String> argList = new ArrayList<String>();
    if (Utils.isWindows()) {
      argList.add(Utils.getPath(getBinariesPath(),
              Utils.getWindowsStopFileName()));
    } else {
      argList.add(Utils.getPath(getBinariesPath(),
              Utils.getUnixStopFileName()));
    }
    String[] args = new String[argList.size()];
    argList.toArray(args);
    ProcessBuilder pb = new ProcessBuilder(args);
    Map<String, String> env = pb.environment();
    env.put("JAVA_HOME", System.getProperty("java.home"));
    /* Remove JAVA_BIN to be sure that we use the JVM running the uninstaller
     * JVM to stop the server.
     */
    env.remove("JAVA_BIN");
    try {
      Process process = pb.start();
      BufferedReader err =
              new BufferedReader(
                      new InputStreamReader(process.getErrorStream()));
      BufferedReader out =
              new BufferedReader(
                      new InputStreamReader(process.getInputStream()));
      /* Create these objects to resend the stop process output to the details
       * area.
       */
      new StopReader(err, true);
      new StopReader(out, false);
      int returnValue = process.waitFor();
      int clientSideError = LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR;
      if ((returnValue == clientSideError) || (returnValue == 0)) {
        if (Utils.isWindows()) {
          /*
           * Sometimes the server keeps some locks on the files.
           * TODO: remove this code once stop-ds returns properly when server
           * is stopped.
           */
          int nTries = 10;
          boolean stopped = false;
          for (int i = 0; i < nTries && !stopped; i++) {
            stopped = !CurrentInstallStatus.isServerRunning();
            if (!stopped) {
              String msg =
                   getFormattedLog(getMsg("progress-server-waiting-to-stop")) +
                           getLineBreak();
              notifyListeners(msg);
              try {
                Thread.sleep(5000);
              }
              catch (Exception ex) {
              }
            }
          }
          if (!stopped) {
            returnValue = -1;
          }
        }
      }
      if (returnValue == clientSideError) {
        String msg = getLineBreak() +
                getFormattedLog(getMsg("progress-server-already-stopped")) +
                getLineBreak();
        notifyListeners(msg);
      } else if (returnValue != 0) {
        String[] arg = {String.valueOf(returnValue)};
        String msg = getMsg("error-stopping-server-code", arg);
        /*
         * The return code is not the one expected, assume the server could
         * not be stopped.
         */
        throw new ApplicationException(ApplicationException.Type.STOP_ERROR,
                msg,
                null);
      } else {
        String msg = getFormattedLog(getMsg("progress-server-stopped"));
        notifyListeners(msg);
      }
    } catch (IOException ioe) {
      throw new ApplicationException(ApplicationException.Type.STOP_ERROR,
              getThrowableMsg("error-stopping-server", ioe), ioe);
    }
    catch (InterruptedException ie) {
      throw new ApplicationException(ApplicationException.Type.BUG,
              getThrowableMsg("error-stopping-server", ie), ie);
    }
  }
  /**
   * Deletes the external database files specified in the provided Set.
   *
   * @param dbFiles the database directories to be deleted.
@@ -830,26 +712,33 @@
      ArrayList<Integer> cumulatedRatio = new ArrayList<Integer>();
      for (int i = 0; i < rootFiles.length; i++) {
        if (filter.accept(rootFiles[i])) {
          Installation installation = getInstallation();
          int relativeRatio;
          if (equalsOrDescendant(rootFiles[i], new File(getLibrariesPath()))) {
          if (equalsOrDescendant(rootFiles[i],
                  installation.getLibrariesDirectory())) {
            relativeRatio = 10;
          } else
          if (equalsOrDescendant(rootFiles[i], new File(getBinariesPath()))) {
          if (equalsOrDescendant(rootFiles[i],
                  installation.getBinariesDirectory())) {
            relativeRatio = 5;
          } else
          if (equalsOrDescendant(rootFiles[i], new File(getConfigPath()))) {
          if (equalsOrDescendant(rootFiles[i],
                  installation.getConfigurationDirectory())) {
            relativeRatio = 5;
          } else
          if (equalsOrDescendant(rootFiles[i], new File(getBackupsPath()))) {
          if (equalsOrDescendant(rootFiles[i],
                  installation.getBackupDirectory())) {
            relativeRatio = 20;
          } else
          if (equalsOrDescendant(rootFiles[i], new File(getLDIFsPath()))) {
          if (equalsOrDescendant(rootFiles[i],
                  installation.getLdifDirectory())) {
            relativeRatio = 20;
          } else if (equalsOrDescendant(rootFiles[i],
                  new File(getDatabasesPath()))) {
                  installation.getDatabasesDirectory())) {
            relativeRatio = 50;
          } else
          if (equalsOrDescendant(rootFiles[i], new File(getLogsPath()))) {
          if (equalsOrDescendant(rootFiles[i],
                  installation.getLogsDirectory())) {
            relativeRatio = 30;
          } else {
            relativeRatio = 2;
@@ -873,84 +762,6 @@
  }
  /**
   * Returns the path to the quicksetup jar file.
   *
   * @return the path to the quicksetup jar file.
   */
  private String getQuicksetupJarPath() {
    return Utils.getPath(getLibrariesPath(), "quicksetup.jar");
  }
  /**
   * Returns the path to the opends jar file.
   *
   * @return the path to the opends jar file.
   */
  private String getOpenDSJarPath() {
    return Utils.getPath(getLibrariesPath(), "OpenDS.jar");
  }
  /**
   * Returns the path to the uninstall.bat file.
   *
   * @return the path to the uninstall.bat file.
   */
  private String getUninstallBatFile() {
    return Utils.getPath(Utils.getInstallPathFromClasspath(), "uninstall.bat");
  }
  /**
   * Returns the path to the backup files under the install path.
   *
   * @return the path to the backup files under the install path.
   */
  private String getBackupsPath() {
    return Utils.getPath(Utils.getInstallPathFromClasspath(),
            Utils.getBackupsRelativePath());
  }
  /**
   * Returns the path to the LDIF files under the install path.
   *
   * @return the path to the LDIF files under the install path.
   */
  private String getLDIFsPath() {
    return Utils.getPath(Utils.getInstallPathFromClasspath(),
            Utils.getLDIFsRelativePath());
  }
  /**
   * Returns the path to the config files under the install path.
   *
   * @return the path to the config files under the install path.
   */
  private String getConfigPath() {
    return Utils.getPath(Utils.getInstallPathFromClasspath(),
            Utils.getConfigRelativePath());
  }
  /**
   * Returns the path to the log files under the install path.
   *
   * @return the path to the log files under the install path.
   */
  private String getLogsPath() {
    return Utils.getPath(Utils.getInstallPathFromClasspath(),
            Utils.getLogsRelativePath());
  }
  /**
   * Returns the path to the database files under the install path.
   *
   * @return the path to the database files under the install path.
   */
  private String getDatabasesPath() {
    return Utils.getPath(Utils.getInstallPathFromClasspath(),
            Utils.getDatabasesRelativePath());
  }
  /**
   * Deletes everything below the specified file.
   *
   * @param file the path to be deleted.
@@ -1058,84 +869,19 @@
  }
  /**
   * {@inheritDoc}
   */
  protected String getBinariesPath() {
    return Utils.getPath(Utils.getInstallPathFromClasspath(),
            Utils.getBinariesRelativePath());
  }
  /**
   * This class is used to read the standard error and standard output of the
   * Stop process.
   * <p/>
   * When a new log message is found notifies the
   * UninstallProgressUpdateListeners of it. If an error occurs it also
   * notifies the listeners.
   */
  private class StopReader {
    private boolean isFirstLine;
    /**
     * The protected constructor.
     *
     * @param reader  the BufferedReader of the stop process.
     * @param isError a boolean indicating whether the BufferedReader
     *        corresponds to the standard error or to the standard output.
     */
    public StopReader(final BufferedReader reader, final boolean isError) {
      final String errorTag =
              isError ? "error-reading-erroroutput" : "error-reading-output";
      isFirstLine = true;
      Thread t = new Thread(new Runnable() {
        public void run() {
          try {
            String line = reader.readLine();
            while (line != null) {
              StringBuilder buf = new StringBuilder();
              if (!isFirstLine) {
                buf.append(formatter.getLineBreak());
              }
              if (isError) {
                buf.append(getFormattedLogError(line));
              } else {
                buf.append(getFormattedLog(line));
              }
              notifyListeners(buf.toString());
              isFirstLine = false;
              line = reader.readLine();
            }
          } catch (IOException ioe) {
            String errorMsg = getThrowableMsg(errorTag, ioe);
            notifyListeners(errorMsg);
          } catch (Throwable t) {
            String errorMsg = getThrowableMsg(errorTag, t);
            notifyListeners(errorMsg);
          }
        }
      });
      t.start();
    }
  }
  /**
   * This class is used to get the files that are not binaries.  This is
   * required to know which are the files that can be deleted directly and which
   * not.
   */
  class InstallationFilesToDeleteFilter implements FileFilter {
    File quicksetupFile = new File(getQuicksetupJarPath());
    File openDSFile = new File(getOpenDSJarPath());
    File librariesFile = new File(getLibrariesPath());
    Installation installation = getInstallation();
    File quicksetupFile = installation.getQuicksetupJarFile();
    File openDSFile = installation.getOpenDSJarFile();
    File librariesFile = installation.getLibrariesDirectory();
    File uninstallBatFile = new File(getUninstallBatFile());
    File uninstallBatFile = installation.getUninstallBatFile();
    File installationPath = new File(Utils.getInstallPathFromClasspath());
    File installationPath = installation.getRootDirectory();
    /**
     * {@inheritDoc}
@@ -1152,14 +898,15 @@
              userData.getRemoveLDIFs()
      };
      Installation installation = getInstallation();
      String[] parentFiles = {
              getLibrariesPath(),
              getBinariesPath(),
              getDatabasesPath(),
              getLogsPath(),
              getConfigPath(),
              getBackupsPath(),
              getLDIFsPath()
              Utils.getPath(installation.getLibrariesDirectory()),
              Utils.getPath(installation.getBinariesDirectory()),
              Utils.getPath(installation.getDatabasesDirectory()),
              Utils.getPath(installation.getLogsDirectory()),
              Utils.getPath(installation.getConfigurationDirectory()),
              Utils.getPath(installation.getBackupDirectory()),
              Utils.getPath(installation.getLdifDirectory())
      };
      boolean accept =
@@ -1217,103 +964,6 @@
    }
  }
  /**
   * This class is used to notify the UninstallProgressUpdateListeners of events
   * that are written to the standard error.  These classes just create an
   * ErrorPrintStream and then they do a call to System.err with it.
   * <p/>
   * The class just reads what is written to the standard error, obtains an
   * formatted representation of it and then notifies the
   * UninstallProgressUpdateListeners with the formatted messages.
   */
  protected class ErrorPrintStream extends PrintStream {
    private boolean isFirstLine;
    /**
     * Default constructor.
     */
    public ErrorPrintStream() {
      super(new ByteArrayOutputStream(), true);
      isFirstLine = true;
    }
    /**
     * {@inheritDoc}
     */
    public void println(String msg) {
      if (isFirstLine) {
        notifyListeners(getFormattedLogError(msg));
      } else {
        notifyListeners(formatter.getLineBreak() + getFormattedLogError(msg));
      }
      isFirstLine = false;
    }
    /**
     * {@inheritDoc}
     */
    public void write(byte[] b, int off, int len) {
      if (b == null) {
        throw new NullPointerException("b is null");
      }
      if (off + len > b.length) {
        throw new IndexOutOfBoundsException(
                "len + off are bigger than the length of the byte array");
      }
      println(new String(b, off, len));
    }
  }
  /**
   * This class is used to notify the UninstallProgressUpdateListeners of events
   * that are written to the standard output.  These classes just create an
   * OutputPrintStream and then they do a call to System.out with it.
   * <p/>
   * The class just reads what is written to the standard output, obtains an
   * formatted representation of it and then notifies the
   * UninstallProgressUpdateListeners with the formatted messages.
   */
  protected class OutputPrintStream extends PrintStream {
    private boolean isFirstLine;
    /**
     * Default constructor.
     */
    public OutputPrintStream() {
      super(new ByteArrayOutputStream(), true);
      isFirstLine = true;
    }
    /**
     * {@inheritDoc}
     */
    public void println(String msg) {
      if (isFirstLine) {
        notifyListeners(getFormattedLog(msg));
      } else {
        notifyListeners(formatter.getLineBreak() + getFormattedLog(msg));
      }
      isFirstLine = false;
    }
    /**
     * {@inheritDoc}
     */
    public void write(byte[] b, int off, int len) {
      if (b == null) {
        throw new NullPointerException("b is null");
      }
      if (off + len > b.length) {
        throw new IndexOutOfBoundsException(
                "len + off are bigger than the length of the byte array");
      }
      println(new String(b, off, len));
    }
  }
  private UninstallUserData getUninstallUserData() {
    return (UninstallUserData) getUserData();
  }
opends/src/quicksetup/org/opends/quicksetup/upgrader/UpgradeOracle.java
New file
@@ -0,0 +1,120 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.quicksetup.upgrader;
import org.opends.quicksetup.i18n.ResourceProvider;
/**
 * This class can answer questions important upgrade/reversion questions
 * like 'can I upgrade from verion X to version Y?' and 'if not then why?'.
 */
public class UpgradeOracle {
  private Integer currentVersion;
  private Integer newVersion;
  /**
   * Creates a new instance that can analyze a hypothetical upgrade/reversion
   * operation from one version to another.
   * @param currentVersion Integer representing the current version
   * @param newVersion Integer representing the proposed next version
   */
  public UpgradeOracle(Integer currentVersion, Integer newVersion) {
    this.currentVersion = currentVersion;
    this.newVersion = newVersion;
  }
  /**
   * Indicates whether or not this operation would be considered an
   * upgrade (as opposed to a reversion).
   * @return boolean where true indicates that this would be an upgrade;
   *         false indicates that this would be a reversion.
   */
  public boolean isUpgrade() {
    return newVersion > currentVersion;
  }
  /**
   * Indicates whether or not this operation would be considered an
   * reversion (as opposed to an upgrade).
   * @return boolean where true indicates that this would be a reversion;
   *         false indicates that this would be an upgrade.
   */
  public boolean isReversion() {
    return newVersion < currentVersion;
  }
  /**
   * Indicates whether or not this hypothetical operation should be allowed
   * to happen.
   * @return boolean where true indicates that we are confident that such
   * an operation will succeed
   */
  public boolean isSupported() {
    boolean supported;
    if (newVersion == currentVersion) {
      supported = false;
    } else {
      supported = true;
    }
    return supported;
  }
  /**
   * Creates a string summarizing a hypothetical upgrade/reversion
   * from <code>currentVersion</code> to <code>newVersion</code> giving
   * reasons why such an attempt would not be successful.
   * @return String representing a localized message giving a summary of
   * this hypothetical operation.
   */
  public String getSummaryMessage() {
    String msg;
    String[] args = { currentVersion.toString(),
            newVersion.toString() };
    ResourceProvider rp = ResourceProvider.getInstance();
    if (isSupported()) {
      if (isUpgrade()) {
        msg = rp.getMsg("upgrade-hypothetical-upgrade-success", args);
      } else if (isReversion()) {
        msg = rp.getMsg("upgrade-hypothetical-reversion-success", args);
      } else {
        msg = rp.getMsg("upgrade-hypothetical-versions-the-same", args);
      }
    } else {
      if (isUpgrade()) {
        msg = rp.getMsg("upgrade-hypothetical-upgrade-failure", args);
      } else {
        msg = rp.getMsg("upgrade-hypothetical-reversion-failure", args);
      }
    }
    return msg;
  }
}
opends/src/quicksetup/org/opends/quicksetup/upgrader/UpgradeUserData.java
New file
@@ -0,0 +1,59 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.quicksetup.upgrader;
import org.opends.quicksetup.UserData;
import java.io.File;
/**
 * User data specific to the upgrade process.
 */
public class UpgradeUserData extends UserData {
  File installPackage;
  /**
   * Gets the OpenDS package (.zip) file whose contents will
   * be used in the upgrade/reversion.
   * @return File object representing the OpenDS package
   */
  public File getInstallPackage() {
    return installPackage;
  }
  /**
   * Sets the OpenDS package (.zip) file whose contents will
   * be used in the upgrade/reversion.
   * @param installPackage File object representing the OpenDS package
   */
  public void setInstallPackage(File installPackage) {
    this.installPackage = installPackage;
  }
}
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;
    }
  }
}
opends/src/quicksetup/org/opends/quicksetup/upgrader/UpgraderCliHelper.java
New file
@@ -0,0 +1,130 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.quicksetup.upgrader;
import org.opends.quicksetup.CliApplicationHelper;
import org.opends.quicksetup.UserDataException;
import org.opends.quicksetup.CurrentInstallStatus;
import org.opends.server.util.args.ArgumentParser;
import org.opends.server.util.args.BooleanArgument;
import org.opends.server.util.args.StringArgument;
import org.opends.server.util.args.ArgumentException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.File;
/**
 * Assists Upgrader utility in CLI drudgery.
 */
public class UpgraderCliHelper extends CliApplicationHelper {
  static private final Logger LOG =
          Logger.getLogger(UpgraderCliHelper.class.getName());
  BooleanArgument cliArg = null;
  BooleanArgument dryRunArg = null;
  StringArgument localInstallPackFileNameArg = null;
  StringArgument remoteBuildNameArg = null;
  /**
   * Creates a set of user data from command line arguments and installation
   * status.
   * @param args String[] of arguments passed in from the command line
   * @param cis the current installation status
   * @return UserData object populated to reflect the input args and status
   * @throws UserDataException if something is wrong
   */
  public UpgradeUserData createUserData(String[] args, CurrentInstallStatus cis)
    throws UserDataException {
    UpgradeUserData uud = new UpgradeUserData();
    ArgumentParser ap = createArgumentParser();
    try {
      ap.parseArguments(args);
      if (localInstallPackFileNameArg.isPresent()) {
        String localInstallPackFileName =
                localInstallPackFileNameArg.getValue();
        File installPackFile = new File(localInstallPackFileName);
        if (!installPackFile.exists()) {
          throw new UserDataException(null, "File '" +
                  localInstallPackFileName +
                  "' does not exist");
        } else {
          uud.setInstallPackage(installPackFile);
        }
      } else if (remoteBuildNameArg.isPresent()) {
        // TODO download build
      } else {
        // TODO i18N
        throw new UserDataException(null,
                "either -F or -B option must be present");
      }
    } catch (ArgumentException e) {
      e.printStackTrace(); // TODO remove
      throw new UserDataException(null, "error parsing arguments");
    }
    return uud;
  }
  private ArgumentParser createArgumentParser() {
    // Create the command-line argument parser for use with this program.
    String toolDescription = getMsg("upgrade-launcher-description");
    ArgumentParser argParser =
         new ArgumentParser("org.opends.quicksetup.upgrader.Upgrader",
                 toolDescription,
                 false);
    // Initialize all the command-line argument types and register them with the
    // parser.
    // try {
    try {
      cliArg =
           new BooleanArgument("cli", 'c', "cli", 0);
      argParser.addArgument(cliArg);
      localInstallPackFileNameArg =
           new StringArgument("install package file", 'F', "package file",
                   false, true, "{install package file}", 0);
      argParser.addArgument(localInstallPackFileNameArg);
      remoteBuildNameArg =
           new StringArgument("build name", 'B', "build name",
                   false, false, "{build name}", 1);
      argParser.addArgument(remoteBuildNameArg);
    } catch (ArgumentException e) {
      LOG.log(Level.INFO, "error", e);
    }
    return argParser;
  }
}
opends/src/quicksetup/org/opends/quicksetup/util/FileManager.java
New file
@@ -0,0 +1,391 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.quicksetup.util;
import org.opends.quicksetup.*;
import org.opends.quicksetup.i18n.ResourceProvider;
import java.io.*;
/**
 * Utility class for use by applications containing methods for managing
 * file system files.  This class handles application notifications for
 * interesting events.
 */
public class FileManager {
  private Application application = null;
  /**
   * Creates a new file manager.
   * @param app Application managing files.
   */
  public FileManager(Application app) {
    this.application = app;
  }
  /**
   * Deletes everything below the specified file.
   *
   * @param file the path to be deleted.
   * @throws org.opends.quicksetup.ApplicationException if something goes wrong.
   */
  public void deleteRecursively(File file) throws ApplicationException {
    deleteRecursively(file, null);
  }
  /**
   * Deletes everything below the specified file.
   *
   * @param file   the path to be deleted.
   * @param filter the filter of the files to know if the file can be deleted
   *               directly or not.
   * @throws ApplicationException if something goes wrong.
   */
  public void deleteRecursively(File file, FileFilter filter)
          throws ApplicationException {
    operateRecursively(new DeleteOperation(file), filter);
  }
  /**
   * Copies everything below the specified file.
   *
   * @param objectFile   the file to be copied.
   * @param destDir      the directory to copy the file to
   * @throws ApplicationException if something goes wrong.
   */
  public void copy(File objectFile, File destDir)
          throws ApplicationException
  {
    new CopyOperation(objectFile, destDir, false).apply();
  }
  /**
   * Copies everything below the specified file.
   *
   * @param objectFile   the file to be copied.
   * @param destDir      the directory to copy the file to
   * @param overwrite    overwrite destination files.
   * @throws ApplicationException if something goes wrong.
   */
  public void copy(File objectFile, File destDir, boolean overwrite)
          throws ApplicationException
  {
    new CopyOperation(objectFile, destDir, overwrite).apply();
  }
  /**
   * Copies everything below the specified file.
   *
   * @param objectFile   the file to be copied.
   * @param destDir      the directory to copy the file to
   * @throws ApplicationException if something goes wrong.
   */
  public void copyRecursively(File objectFile, File destDir)
          throws ApplicationException
  {
    copyRecursively(objectFile, destDir, null);
  }
  /**
   * Copies everything below the specified file.
   *
   * @param objectFile   the file to be copied.
   * @param destDir      the directory to copy the file to
   * @param filter the filter of the files to know if the file can be copied
   *               directly or not.
   * @throws ApplicationException if something goes wrong.
   */
  public void copyRecursively(File objectFile, File destDir, FileFilter filter)
          throws ApplicationException {
    copyRecursively(objectFile, destDir, filter, false);
  }
  /**
   * Copies everything below the specified file.
   *
   * @param objectFile   the file to be copied.
   * @param destDir      the directory to copy the file to
   * @param filter the filter of the files to know if the file can be copied
   *               directly or not.
   * @param overwrite    overwrite destination files.
   * @throws ApplicationException if something goes wrong.
   */
  public void copyRecursively(File objectFile, File destDir,
                              FileFilter filter, boolean overwrite)
          throws ApplicationException {
    operateRecursively(new CopyOperation(objectFile, destDir, overwrite),
            filter);
  }
  private void operateRecursively(FileOperation op, FileFilter filter)
          throws ApplicationException {
    File file = op.getObjectFile();
    if (file.exists()) {
      if (file.isFile()) {
        if (filter != null) {
          if (filter.accept(file)) {
            op.apply();
          }
        } else {
          op.apply();
        }
      } else {
        File[] children = file.listFiles();
        if (children != null) {
          for (File aChildren : children) {
            FileOperation newOp = op.copyForChild(aChildren);
            operateRecursively(newOp, filter);
          }
        }
        if (filter != null) {
          if (filter.accept(file)) {
            op.apply();
          }
        } else {
          op.apply();
        }
      }
    } else {
      // Just tell that the file/directory does not exist.
      String[] arg = {file.toString()};
      application.notifyListeners(application.getFormattedWarning(
              getMsg("file-does-not-exist", arg)));
    }
  }
  /**
   * A file operation.
   */
  private abstract class FileOperation {
    private File objectFile = null;
    /**
     * Creates a new file operation.
     * @param objectFile to be operated on
     */
    public FileOperation(File objectFile) {
      this.objectFile = objectFile;
    }
    /**
     * Gets the file to be operated on.
     * @return File to be operated on
     */
    protected File getObjectFile() {
      return objectFile;
    }
    /**
     * Make a copy of this class for the child file.
     * @param child to act as the new file object
     * @return FileOperation as the same type as this class
     */
    abstract public FileOperation copyForChild(File child);
    /**
     * Execute this operation.
     * @throws ApplicationException if there is a problem.
     */
    abstract public void apply() throws ApplicationException;
  }
  /**
   * A copy operation.
   */
  private class CopyOperation extends FileOperation {
    private File destination;
    private boolean overwrite;
    /**
     * Create a new copy operation.
     * @param objectFile to copy
     * @param destDir to copy to
     */
    public CopyOperation(File objectFile, File destDir, boolean overwrite) {
      super(objectFile);
      this.destination = new File(destDir, objectFile.getName());
      this.overwrite = overwrite;
    }
    /**
     * {@inheritDoc}
     */
    public FileOperation copyForChild(File child) {
      return new CopyOperation(child, destination, overwrite);
    }
    /**
     * {@inheritDoc}
     */
    public void apply() throws ApplicationException {
      File objectFile = getObjectFile();
      String[] args = {objectFile.getAbsolutePath(),
              destination.getAbsolutePath()};
      // If overwriting and the destination exists then kill it
      if (destination.exists() && overwrite) {
        deleteRecursively(destination);
      }
      if (objectFile.isDirectory()) {
        if (!destination.exists()) {
            destination.mkdirs();
        }
      } else {
        if (!destination.exists()) {
          if (insureParentsExist(destination)) {
            application.notifyListeners(application.getFormattedWithPoints(
                    getMsg("progress-copying-file", args)));
            try {
              FileInputStream fis = new FileInputStream(objectFile);
              FileOutputStream fos = new FileOutputStream(destination);
              byte[] buf = new byte[1024];
              int i;
              while ((i = fis.read(buf)) != -1) {
                fos.write(buf, 0, i);
              }
              fis.close();
              fos.close();
              application.notifyListeners(application.getFormattedDone() +
                      application.getLineBreak());
            } catch (Exception e) {
              String errMsg = getMsg("error-copying-file", args);
              throw new ApplicationException(
                      ApplicationException.Type.FILE_SYSTEM_ERROR,
                      errMsg, null);
            }
          } else {
            String errMsg = getMsg("error-copying-file", args);
            throw new ApplicationException(
                    ApplicationException.Type.FILE_SYSTEM_ERROR, errMsg, null);
          }
        } else {
          application.notifyListeners(getMsg("info-ignoring-file", args) +
                  application.getLineBreak());
        }
      }
    }
    private boolean insureParentsExist(File f) {
      File parent = f.getParentFile();
      boolean b = parent.exists();
      if (!b) {
        b = parent.mkdirs();
      }
      return b;
    }
  }
  /**
   * A delete operation.
   */
  private class DeleteOperation extends FileOperation {
    /**
     * Creates a delete operation.
     * @param objectFile to delete
     */
    public DeleteOperation(File objectFile) {
      super(objectFile);
    }
    /**
     * {@inheritDoc}
     */
    public FileOperation copyForChild(File child) {
      return new DeleteOperation(child);
    }
    /**
     * {@inheritDoc}
     */
    public void apply() throws ApplicationException {
      File file = getObjectFile();
      String[] arg = {file.getAbsolutePath()};
      boolean isFile = file.isFile();
      if (isFile) {
        application.notifyListeners(application.getFormattedWithPoints(
                getMsg("progress-deleting-file", arg)));
      } else {
        application.notifyListeners(application.getFormattedWithPoints(
                getMsg("progress-deleting-directory", arg)));
      }
      boolean delete = false;
      /*
       * Sometimes the server keeps some locks on the files.
       * TODO: remove this code once stop-ds returns properly when server
       * is stopped.
       */
      int nTries = 5;
      for (int i = 0; i < nTries && !delete; i++) {
        delete = file.delete();
        if (!delete) {
          try {
            Thread.sleep(1000);
          }
          catch (Exception ex) {
            // do nothing;
          }
        }
      }
      if (!delete) {
        String errMsg;
        if (isFile) {
          errMsg = getMsg("error-deleting-file", arg);
        } else {
          errMsg = getMsg("error-deleting-directory", arg);
        }
        throw new ApplicationException(
                ApplicationException.Type.FILE_SYSTEM_ERROR, errMsg, null);
      }
      application.notifyListeners(application.getFormattedDone() +
              application.getLineBreak());
    }
  }
  private String getMsg(String key) {
    return ResourceProvider.getInstance().getMsg(key);
  }
  private String getMsg(String key, String... args) {
    return ResourceProvider.getInstance().getMsg(key, args);
  }
}
opends/src/quicksetup/org/opends/quicksetup/util/ServerController.java
New file
@@ -0,0 +1,478 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.quicksetup.util;
import org.opends.quicksetup.*;
import org.opends.server.protocols.ldap.LDAPResultCode;
import javax.naming.NamingException;
import java.util.ArrayList;
import java.util.Map;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
/**
 * Class used to manipulate an OpenDS server.
 */
public class ServerController {
  private Application application;
  private Installation installation;
  /**
   * Creates a new instance that will operate on <code>application</code>'s
   * installation.
   * @param application to use for notifications
   */
  public ServerController(Application application) {
    this(application, application.getInstallation());
  }
  /**
   * Creates a new instance that will operate on <code>installation</code>
   * and use <code>application</code> for notifications.
   * @param application to use for notifications
   * @param installation representing the server instance to control
   */
  public ServerController(Application application, Installation installation) {
    if (application == null) {
      throw new NullPointerException("application cannot be null");
    }
    if (installation == null) {
      throw new NullPointerException("installation cannot be null");
    }
    this.application = application;
    this.installation = installation;
  }
  /**
   * This methods stops the server.
   *
   * @throws org.opends.quicksetup.ApplicationException if something goes wrong.
   */
  public void stopServer() throws ApplicationException {
    application.notifyListeners(
            application.getFormattedProgress(
                    application.getMsg("progress-stopping")) +
                    application.getLineBreak());
    ArrayList<String> argList = new ArrayList<String>();
    argList.add(Utils.getPath(installation.getServerStopCommandFile()));
    String[] args = new String[argList.size()];
    argList.toArray(args);
    ProcessBuilder pb = new ProcessBuilder(args);
    Map<String, String> env = pb.environment();
    env.put("JAVA_HOME", System.getProperty("java.home"));
    /* Remove JAVA_BIN to be sure that we use the JVM running the uninstaller
     * JVM to stop the server.
     */
    env.remove("JAVA_BIN");
    try {
      Process process = pb.start();
      BufferedReader err =
              new BufferedReader(
                      new InputStreamReader(process.getErrorStream()));
      BufferedReader out =
              new BufferedReader(
                      new InputStreamReader(process.getInputStream()));
      /* Create these objects to resend the stop process output to the details
       * area.
       */
      new StopReader(err, true);
      new StopReader(out, false);
      int returnValue = process.waitFor();
      int clientSideError = LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR;
      if ((returnValue == clientSideError) || (returnValue == 0)) {
        if (Utils.isWindows()) {
          /*
           * Sometimes the server keeps some locks on the files.
           * TODO: remove this code once stop-ds returns properly when server
           * is stopped.
           */
          int nTries = 10;
          boolean stopped = false;
          for (int i = 0; i < nTries && !stopped; i++) {
            stopped = !CurrentInstallStatus.isServerRunning();
            if (!stopped) {
              String msg =
                      application.getFormattedLog(
                        application.getMsg("progress-server-waiting-to-stop")) +
                        application.getLineBreak();
              application.notifyListeners(msg);
              try {
                Thread.sleep(5000);
              }
              catch (Exception ex) {
              }
            }
          }
          if (!stopped) {
            returnValue = -1;
          }
        }
      }
      if (returnValue == clientSideError) {
        String msg = application.getLineBreak() +
                application.getFormattedLog(
                        application.getMsg("progress-server-already-stopped")) +
                    application.getLineBreak();
        application.notifyListeners(msg);
      } else if (returnValue != 0) {
        String[] arg = {String.valueOf(returnValue)};
        String msg = application.getMsg("error-stopping-server-code", arg);
        /*
         * The return code is not the one expected, assume the server could
         * not be stopped.
         */
        throw new ApplicationException(ApplicationException.Type.STOP_ERROR,
                msg,
                null);
      } else {
        String msg = application.getFormattedLog(
                application.getMsg("progress-server-stopped"));
        application.notifyListeners(msg);
      }
    } catch (IOException ioe) {
      throw new ApplicationException(ApplicationException.Type.STOP_ERROR,
              application.getThrowableMsg("error-stopping-server", ioe), ioe);
    }
    catch (InterruptedException ie) {
      throw new ApplicationException(ApplicationException.Type.BUG,
              application.getThrowableMsg("error-stopping-server", ie), ie);
    }
  }
  /**
   * This methods starts the server.
   * @throws org.opends.quicksetup.QuickSetupException if something goes wrong.
   */
  public void startServer() throws QuickSetupException {
    application.notifyListeners(
            application.getFormattedProgress(
                    application.getMsg("progress-starting")) +
        application.getLineBreak());
    ArrayList<String> argList = new ArrayList<String>();
    argList.add(Utils.getPath(installation.getServerStartCommandFile()));
    String[] args = new String[argList.size()];
    argList.toArray(args);
    ProcessBuilder pb = new ProcessBuilder(args);
    Map<String, String> env = pb.environment();
    env.put("JAVA_HOME", System.getProperty("java.home"));
    /* Remove JAVA_BIN to be sure that we use the JVM running the installer
     * JVM to start the server.
     */
    env.remove("JAVA_BIN");
    try
    {
      String startedId = getStartedId();
      Process process = pb.start();
      BufferedReader err =
          new BufferedReader(new InputStreamReader(process.getErrorStream()));
      BufferedReader out =
          new BufferedReader(new InputStreamReader(process.getInputStream()));
      StartReader errReader = new StartReader(err, startedId, true);
      StartReader outputReader = new StartReader(out, startedId, false);
      while (!errReader.isFinished() && !outputReader.isFinished())
      {
        try
        {
          Thread.sleep(100);
        } catch (InterruptedException ie)
        {
        }
      }
      // Check if something wrong occurred reading the starting of the server
      QuickSetupException ex = errReader.getException();
      if (ex == null)
      {
        ex = outputReader.getException();
      }
      if (ex != null)
      {
        throw ex;
      } else
      {
        /*
         * There are no exceptions from the readers and they are marked as
         * finished. This means that the server has written in its output the
         * message id informing that it started. So it seems that everything
         * went fine.
         *
         * However we can have issues with the firewalls or do not have rights
         * to connect.  Just check if we can connect to the server.
         * Try 5 times with an interval of 1 second between try.
         */
        boolean connected = false;
        for (int i=0; i<5 && !connected; i++)
        {
          // TODO: get this information from the installation instead
          UserData userData = application.getUserData();
          String ldapUrl = "ldap://localhost:" + userData.getServerPort();
          try
          {
            Utils.createLdapContext(
                ldapUrl,
                userData.getDirectoryManagerDn(),
                userData.getDirectoryManagerPwd(), 3000, null);
            connected = true;
          }
          catch (NamingException ne)
          {
          }
          if (!connected)
          {
            try
            {
              Thread.sleep(1000);
            }
            catch (Throwable t)
            {
            }
          }
        }
        if (!connected)
        {
          String[] arg = {String.valueOf(application.getUserData().
                  getServerPort())};
          if (Utils.isWindows())
          {
            throw new QuickSetupException(QuickSetupException.Type.START_ERROR,
                application.getMsg("error-starting-server-in-windows", arg),
                    null);
          }
          else
          {
            throw new QuickSetupException(QuickSetupException.Type.START_ERROR,
                application.getMsg("error-starting-server-in-unix", arg), null);
          }
        }
      }
    } catch (IOException ioe)
    {
      throw new QuickSetupException(QuickSetupException.Type.START_ERROR,
          application.getThrowableMsg("error-starting-server", ioe), ioe);
    }
  }
  /**
   * This class is used to read the standard error and standard output of the
   * Stop process.
   * <p/>
   * When a new log message is found notifies the
   * UninstallProgressUpdateListeners of it. If an error occurs it also
   * notifies the listeners.
   */
  private class StopReader {
    private boolean isFirstLine;
    /**
     * The protected constructor.
     *
     * @param reader  the BufferedReader of the stop process.
     * @param isError a boolean indicating whether the BufferedReader
     *        corresponds to the standard error or to the standard output.
     */
    public StopReader(final BufferedReader reader, final boolean isError) {
      final String errorTag =
              isError ? "error-reading-erroroutput" : "error-reading-output";
      isFirstLine = true;
      Thread t = new Thread(new Runnable() {
        public void run() {
          try {
            String line = reader.readLine();
            while (line != null) {
              StringBuilder buf = new StringBuilder();
              if (!isFirstLine) {
                buf.append(application.getProgressMessageFormatter().
                        getLineBreak());
              }
              if (isError) {
                buf.append(application.getFormattedLogError(line));
              } else {
                buf.append(application.getFormattedLog(line));
              }
              application.notifyListeners(buf.toString());
              isFirstLine = false;
              line = reader.readLine();
            }
          } catch (IOException ioe) {
            String errorMsg = application.getThrowableMsg(errorTag, ioe);
            application.notifyListeners(errorMsg);
          } catch (Throwable t) {
            String errorMsg = application.getThrowableMsg(errorTag, t);
            application.notifyListeners(errorMsg);
          }
        }
      });
      t.start();
    }
  }
  /**
   * Returns the Message ID indicating that the server has started.
   * @return the Message ID indicating that the server has started.
   */
  private String getStartedId()
  {
    InstallerHelper helper = new InstallerHelper();
    return helper.getStartedId();
  }
  /**
   * This class is used to read the standard error and standard output of the
   * Start process.
   *
   * When a new log message is found notifies the ProgressUpdateListeners
   * of it. If an error occurs it also notifies the listeners.
   *
   */
  private class StartReader
  {
    private QuickSetupException ex;
    private boolean isFinished;
    private boolean isFirstLine;
    /**
     * The protected constructor.
     * @param reader the BufferedReader of the start process.
     * @param startedId the message ID that this class can use to know whether
     * the start is over or not.
     * @param isError a boolean indicating whether the BufferedReader
     * corresponds to the standard error or to the standard output.
     */
    public StartReader(final BufferedReader reader, final String startedId,
        final boolean isError)
    {
      final String errorTag =
          isError ? "error-reading-erroroutput" : "error-reading-output";
      isFirstLine = true;
      Thread t = new Thread(new Runnable()
      {
        public void run()
        {
          try
          {
            String line = reader.readLine();
            while (line != null)
            {
              StringBuffer buf = new StringBuffer();
              if (!isFirstLine)
              {
                buf.append(application.getProgressMessageFormatter().
                        getLineBreak());
              }
              if (isError)
              {
                buf.append(application.getFormattedLogError(line));
              } else
              {
                buf.append(application.getFormattedLog(line));
              }
              application.notifyListeners(buf.toString());
              isFirstLine = false;
              if (line.indexOf("id=" + startedId) != -1)
              {
                isFinished = true;
              }
              line = reader.readLine();
            }
          } catch (IOException ioe)
          {
            String errorMsg = application.getThrowableMsg(errorTag, ioe);
            ex =
                new QuickSetupException(QuickSetupException.Type.START_ERROR,
                    errorMsg, ioe);
          } catch (Throwable t)
          {
            String errorMsg = application.getThrowableMsg(errorTag, t);
            ex =
                new QuickSetupException(QuickSetupException.Type.START_ERROR,
                    errorMsg, t);
          }
          isFinished = true;
        }
      });
      t.start();
    }
    /**
     * Returns the QuickSetupException that occurred reading the Start error and
     * output or <CODE>null</CODE> if no exception occurred.
     * @return the exception that occurred reading or <CODE>null</CODE> if
     * no exception occurred.
     */
    public QuickSetupException getException()
    {
      return ex;
    }
    /**
     * Returns <CODE>true</CODE> if the server starting process finished
     * (successfully or not) and <CODE>false</CODE> otherwise.
     * @return <CODE>true</CODE> if the server starting process finished
     * (successfully or not) and <CODE>false</CODE> otherwise.
     */
    public boolean isFinished()
    {
      return isFinished;
    }
  }
}
opends/src/quicksetup/org/opends/quicksetup/util/Utils.java
@@ -39,11 +39,7 @@
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.net.ConnectException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
import java.util.*;
import javax.naming.CommunicationException;
import javax.naming.Context;
@@ -264,7 +260,16 @@
   */
  public static String getPath(String parentPath, String relativePath)
  {
    File f = new File(new File(parentPath), relativePath);
    return getPath(new File(new File(parentPath), relativePath));
  }
  /**
   * Returns the absolute path for the given parentPath and relativePath.
   * @param f File to get the path
   * @return the absolute path for the given parentPath and relativePath.
   */
  public static String getPath(File f)
  {
    try
    {
      /*
@@ -1491,4 +1496,52 @@
    }
    return isOutOfMemory;
  }
  /**
   * Returns the number of entries contained in the zip file.  This is used to
   * update properly the progress bar ratio.
   * @return the number of entries contained in the zip file.
   */
  static public int getNumberZipEntries()
  {
    // TODO  we should get this dynamically during build
    return 83;
  }
  /**
   * Determines whether one file is the parent of another.
   * @param ancestor possible parent of <code>descendant</code>
   * @param descendant possible child 0f <code>ancestor</code>
   * @return return true if ancestor is a parent of descendant
   */
  static public boolean isParentOf(File ancestor, File descendant) {
    if (ancestor != null) {
      if (ancestor.equals(descendant)) {
        return false;
      }
      while ((descendant != null) && !ancestor.equals(descendant)) {
        descendant = descendant.getParentFile();
      }
    }
    return (ancestor != null) && (descendant != null);
  }
  /**
   * Creates a string consisting of the string representation of the
   * elements in the <code>list</code> separated by <code>separator</code>.
   * @param list the list to print
   * @param separator to use in separating elements
   * @return String representing the list
   */
  static public String listToString(List<?> list, String separator) {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < list.size(); i++) {
      sb.append(list.get(i));
      if (i < list.size() - 1) {
        sb.append(separator);
      }
    }
    return sb.toString();
  }
}