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

kenneth_suter
05.02.2007 0f7b83578af3a66529590cf0516dc8bc1b79b8c1
This commit introduces several bits of quicksetup plumbing necessary to support the upgrader's interaction in asking the user whether they would like to cancel an upgrade if there are problems found:

- The notion of a 'UserInteraction' which can be used by applications to prompt the user regardless of whether or not they are running in CLI or GUI mode. There are two subclasses of this, one for the CLI and one for the GUI. Unlike existing code for prompting the user in the CLI UserInteraction is intended to be used during the coarse of the progress panel but can be used elsewhere as well. UserInteraction has two methods currently that take arguments similar to JOptionPane's showConfirmDialog. One of the methods allows an application to present the user a dialog having a 'Details' section which in the GUI shows the user a JOptionPane have a 'Show Details' button that when clicked expands the panel to show details. Care has been taken to extend JOptionPane rather than invent a new dialog whose look and feel may not be compatible with future Swing look and feels.

- Code for the upgrader to use the new functionality in asking the user if they would like to back out of an upgrade if there are errors found. The user is presented with an option pane in the GUI with a 'Show Details' button where they can get more information. The CLI has an option for viewing the details if in an interactive session. The previous behavior was to back out automatically. The upgrader CLI supports a noninteractive argument to support the current behavior.

- Common support for 'silent' and 'noninteractive' command line parsing. The uninstaller and installer still retain their separate method for determining this but they could easily adopt the common approach now present in CliApplicationHelper.
3 files added
10 files modified
901 ■■■■■ changed files
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/Application.java 15 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/CliApplicationHelper.java 108 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/CliUserInteraction.java 151 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/UserData.java 38 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/UserInteraction.java 97 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/resources/Resources.properties 4 ●●● patch | view | raw | blame | history
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/GuiApplication.java 27 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/GuiUserInteraction.java 288 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/QuickSetup.java 1 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java 74 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/UpgraderCliHelper.java 27 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/util/HtmlProgressMessageFormatter.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/util/Utils.java 62 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/Application.java
@@ -553,6 +553,21 @@
  abstract public void cancel();
  /**
   * Makes available a <code>UserInteraction</code> class that can be used
   * by the application to interact with the user.  If the user has requested
   * a noninteractive session this method returns null.
   * @return UserInteraction object
   */
  protected UserInteraction userInteraction() {
    // Note:  overridden in GuiApplication
    UserInteraction ui = null;
    if (!getUserData().isNoninteractive()) {
      ui = new CliUserInteraction();
    }
    return ui;
  }
  /**
   * 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
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/CliApplicationHelper.java
@@ -29,11 +29,17 @@
import org.opends.quicksetup.util.Utils;
import org.opends.quicksetup.i18n.ResourceProvider;
import org.opends.server.util.args.ArgumentParser;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.args.BooleanArgument;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Set;
import java.util.ArrayList;
import java.util.logging.Logger;
import java.util.logging.Level;
/**
 * Helper class containing useful methods for processing input and output
@@ -44,6 +50,22 @@
  static private final Logger LOG =
          Logger.getLogger(CliApplication.class.getName());
  /** Short form of the option for specifying a noninteractive session. */
  static public final Character SILENT_OPTION_SHORT = 's';
  /** Long form of the option for specifying a noninteractive session. */
  static public final String SILENT_OPTION_LONG = "silent";
  /** Short form of the option for specifying a noninteractive session. */
  static public final Character NONINTERACTIVE_OPTION_SHORT = 'n';
  /** Long form of the option for specifying a noninteractive session. */
  static public final String NONINTERACTIVE_OPTION_LONG = "noninteractive";
  private BooleanArgument noninteractiveArg = null;
  private BooleanArgument silentArg = null;
  /**
   * Interactively prompts (on standard output) the user to provide a string
   * value.  Any non-empty string will be allowed (the empty string will
@@ -90,12 +112,23 @@
  /**
   * Reads a line of text from standard input.
   *
   * @return  The line of text read from standard input, or <CODE>null</CODE>
   *          if the end of the stream is reached or an error occurs while
   *          attempting to read the response.
   */
  private String readLine()
  public String readLine() {
    return readLine(System.in, System.err);
  }
  /**
   * Reads a line of text from standard input.
   * @param   in InputSteam from which line will be read
   * @param   err PrintSteam where any errors will be printed
   * @return  The line of text read from standard input, or <CODE>null</CODE>
   *          if the end of the stream is reached or an error occurs while
   *          attempting to read the response.
   */
  public String readLine(InputStream in, PrintStream err)
  {
    try
    {
@@ -103,14 +136,14 @@
      while (true)
      {
        int b = System.in.read();
        int b = in.read();
        if ((b < 0) || (b == '\n'))
        {
          break;
        }
        else if (b == '\r')
        {
          int b2 = System.in.read();
          int b2 = in.read();
          if (b2 == '\n')
          {
            break;
@@ -131,7 +164,7 @@
    }
    catch (Exception e)
    {
      System.err.println(getMsg("cli-uninstall-error-reading-stdin"));
      err.println(getMsg("cli-uninstall-error-reading-stdin"));
      return null;
    }
@@ -191,6 +224,71 @@
  }
  /**
   * Returns <CODE>true</CODE> if this is a silent session and
   * <CODE>false</CODE> otherwise.  This method relies on the a previous
   * call to createArgumentParser having been made and the parser
   * having been used to parse the arguments.
   * @return <CODE>true</CODE> if this is a silent uninstall and
   * <CODE>false</CODE> otherwise.
   */
  protected boolean isSilent() {
    return silentArg != null && silentArg.isPresent();
  }
  /**
   * Returns <CODE>true</CODE> if this is a noninteractive sessions and
   * <CODE>false</CODE> otherwise.  This method relies on the a previous
   * call to createArgumentParser having been made and the parser
   * having been used to parse the arguments.
   * @return <CODE>true</CODE> if this is a noninteractive session and
   * <CODE>false</CODE> otherwise.
   */
  protected boolean isNoninteractive() {
    return noninteractiveArg != null && noninteractiveArg.isPresent();
  }
  /**
   * Creates an argument parser having common arguments.
   * @param mainClass class of the tool
   * @param description localized description of the tool
   * @param caseSensitive whether long args are case sensitive
   * @return ArgumentParser ready for app specific customization
   * @see org.opends.server.util.args.ArgumentParser#ArgumentParser(
          String, String, boolean)
   */
  protected ArgumentParser createArgumentParser(String mainClass,
                                                String description,
                                                boolean caseSensitive) {
    // Create the command-line argument parser for use with this program.
    ArgumentParser argParser =
         new ArgumentParser(mainClass, description, caseSensitive);
    // Initialize all the common command-line argument types and register
    // them with the parser.
    try {
      noninteractiveArg =
           new BooleanArgument("noninteractive session",
                   NONINTERACTIVE_OPTION_SHORT,
                   NONINTERACTIVE_OPTION_LONG,
                   0);
      argParser.addArgument(noninteractiveArg);
      silentArg =
           new BooleanArgument("silent session",
                   SILENT_OPTION_SHORT,
                   SILENT_OPTION_LONG,
                   0);
      argParser.addArgument(silentArg);
    } catch (ArgumentException e) {
      LOG.log(Level.INFO, "error", e);
    }
    return argParser;
  }
  /**
   * The following three methods are just commodity methods to get localized
   * messages.
   * @param key String key
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/CliUserInteraction.java
New file
@@ -0,0 +1,151 @@
/*
 * 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;
import org.opends.quicksetup.util.Utils;
import org.opends.server.util.StaticUtils;
import java.util.List;
import java.util.ArrayList;
import java.io.PrintStream;
import java.io.InputStream;
/**
 * Supports user interactions for a command line driven application.
 */
public class CliUserInteraction extends CliApplicationHelper
        implements UserInteraction {
  private PrintStream out;
  private PrintStream err;
  private InputStream in;
  /**
   * Creates an instance that will use standard streams for interaction.
   */
  public CliUserInteraction() {
    this.out = System.out;
    this.err = System.err;
    this.in = System.in;
  }
  /**
   * Creates an instance using specific streams.
   * @param out OutputStream where prompts will be written
   * @param err OutputStream where errors will be written
   * @param in InputStream from which information will be read
   */
  public CliUserInteraction(PrintStream out, PrintStream err, InputStream in) {
    this.out = out;
    this.err = err;
    this.in = in;
  }
  /**
   * {@inheritDoc}
   */
  public Object confirm(String summary, String details,
                        String title, MessageType type, String[] options,
                        String def) {
    return confirm(summary, details, null, title, type, options, def, null);
  }
  /**
   * {@inheritDoc}
   */
  public Object confirm(String summary, String details, String fineDetails,
                        String title, MessageType type, String[] options,
                        String def, String viewDetailsOption) {
    List<String> sOptions = new ArrayList<String>();
    int defInt = -1;
    for (int i = 0; i < options.length; i++) {
      sOptions.add(createOption(i + 1, options[i]));
      if (options[i].equals(def)) {
        defInt = i + 1;
      }
    }
    if (fineDetails != null) {
      sOptions.add(createOption(options.length + 1,
              viewDetailsOption != null ? viewDetailsOption : "View Details"));
    }
    println();
    println(summary);
    println();
    println(details);
    String returnValue = null;
    while (returnValue == null) {
      println();
      for (String o : sOptions) {
        println(o);
      }
      System.out.print(getMsg("cli-uninstall-confirm-prompt",
          new String[] {"Enter a number or press Enter to accept the default",
                  Integer.toString(defInt)}));
      System.out.flush();
      String response = readLine(in, err);
      int respInt = -1;
      if (response.equals("")) {
        respInt = defInt;
      } else {
        try {
          respInt = Integer.parseInt(response);
        } catch (Exception e) {
          // do nothing;
        }
      }
      if (fineDetails != null && respInt == options.length + 1) {
        println(fineDetails);
      } else if (respInt > 0 && respInt <= options.length) {
        returnValue = options[respInt - 1];
      } else {
        println("Illegal response " + response);
      }
    }
    return returnValue;
  }
  private String createOption(int index, String option) {
    return new StringBuilder().
            append(Integer.toString(index)).
            append(". ").
            append(option).toString();
  }
  private void println() {
    out.println();
  }
  private void println(String text) {
    out.println(StaticUtils.wrapText(text, Utils.getCommandLineMaxLineWidth()));
  }
}
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/UserData.java
@@ -72,6 +72,10 @@
  private SuffixesToReplicateOptions suffixesToReplicateOptions;
  private boolean silent;
  private boolean noninteractive;
  /**
   * Creates a user data object with default values.
   */
@@ -387,6 +391,40 @@
  }
  /**
   * Sets whether or not this session should print messages to the
   * console if in CLI mode.
   * @param silent where true indicates this sesssion should be silent
   */
  public void setSilent(boolean silent) {
    this.silent = silent;
  }
  /**
   * Indicates whether or not the user has requested silent mode.
   * @return boolean where true indicates this session should be silent.
   */
  public boolean isSilent() {
    return this.silent;
  }
  /**
   * Sets whether or not this session should solicite feedback from the user.
   * @param noninteractive boolean where true indicates this application
   *        should NOT solicite feedback from the user
   */
  public void setNoninteractive(boolean noninteractive) {
    this.noninteractive = noninteractive;
  }
  /**
   * Indicates whether or not the user has requested noninteractive mode.
   * @return boolean where true indicates this session should be noninteractive
   */
  public boolean isNoninteractive() {
    return this.noninteractive;
  }
  /**
   * Provides the port that will be proposed to the user in the second page of
   * the installation wizard. It will check whether we can use ports of type
   * X389 and if not it will return -1.
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/UserInteraction.java
New file
@@ -0,0 +1,97 @@
/*
 * 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;
/**
 * This class describes methods for supporting interaction with the user.
 */
public interface UserInteraction {
  /**
   * Type of message displayed to the user.  The type of message
   * may affect the presentation of the interaction.
   */
  public enum MessageType {
    /** A message with no context. */
    PLAIN,
    /** A message displayed as a result of an error. */
    ERROR,
    /** A message displayed informing the user of something. */
    INFORMATION,
    /** A message displayed to warn the user. */
    WARNING,
    /** A message displayed to ask the user a question. */
    QUESTION
  }
  /**
   * Present a list of choices to the user and wait for them to select one
   * of them.
   * @param summary text to present to the user.  This is usually just a
   *        string bug For GUI applications can be a component that will appear
   *        inside a dialog
   * @param detail more details of the message
   * @param title of the prompt if any
   * @param type of message
   * @param options set of options to give the user
   * @param def the default option from <code>options</code>
   * @return Object that is the same value as the user selection from the
   *         <code>options</code> parameter.
   */
  Object confirm(String summary, String detail,
                 String title, MessageType type, String[] options, String def);
  /**
   * Present a list of choices to the user and wait for them to select one
   * of them.
   * @param summary text to present to the user.  This is usually just a
   *        string bug For GUI applications can be a component that will appear
   *        inside a dialog
   * @param detail more details of the message
   * @param fineDetails even finer details.  This text may be rendered in
   *        such a way that the user needs to take some sort of action to
   *        see this text
   * @param title of the prompt if any
   * @param type of message
   * @param options set of options to give the user
   * @param def the default option from <code>options</code>
   * @param viewDetailsOption name of the option to be used for showing the
   *        details.  If null a default will be used.
   * @return Object that is the same value as the user selection from the
   *         <code>options</code> parameter.
   */
  Object confirm(String summary, String detail, String fineDetails,
                 String title, MessageType type, String[] options, String def,
                 String viewDetailsOption);
}
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/resources/Resources.properties
@@ -176,7 +176,9 @@
     -V, --version\n    Display Directory Server version information.\n\
     -f, --file\n    Specifies an existing OpenDS package (.zip) \
   file to which the\n    current build will be upgraded using the command line \
   \n    version of this tool\n-?, -H , --help\n    Display this usage information.\n
   \n    version of this tool\n-n, --noninteractive\n    If this tool requires more \
   information to complete successfully\n    it will fail instead of prompting.\
   \n-?, -H , --help\n    Display this usage information.\n
upgrade-launcher-launching-gui=Launching graphical upgrade...
upgrade-launcher-launching-cli=Launching command line upgrade...
upgrade-launcher-gui-launched-failed=\n\nThe graphical upgrade launch \
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/GuiApplication.java
@@ -30,6 +30,8 @@
import org.opends.quicksetup.*;
import org.opends.quicksetup.util.ServerController;
import org.opends.quicksetup.util.InProcessServerController;
import org.opends.quicksetup.util.Utils;
import org.opends.quicksetup.UserInteraction;
import org.opends.quicksetup.webstart.WebStartDownloader;
import javax.swing.*;
@@ -57,6 +59,9 @@
  /** Downloads .jar files for webstart application. */
  protected WebStartDownloader loader;
  /** The QuickSetupDialog in control. */
  private QuickSetupDialog qs;
  /**
   * Constructs an instance of an application.  Subclasses
   * of this application must have a default constructor.
@@ -512,4 +517,26 @@
    }
  }
  /**
   * {@inheritDoc}
   */
  protected UserInteraction userInteraction() {
    UserInteraction ui = null;
    if (!getUserData().isNoninteractive()) {
      if (Utils.isCli()) {
        ui = new CliUserInteraction();
      } else {
        ui = new GuiUserInteraction(qs.getFrame());
      }
    }
    return ui;
  }
  /**
   * Sets the QuickSetupDialog driving this application.
   * @param dialog QuickSetupDialog driving this application
   */
  public void setQuickSetupDialog(QuickSetupDialog dialog) {
    this.qs = dialog;
  }
}
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/GuiUserInteraction.java
New file
@@ -0,0 +1,288 @@
/*
 * 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.ui;
import org.opends.quicksetup.UserInteraction;
import org.opends.quicksetup.util.Utils;
import javax.swing.*;
import javax.swing.plaf.basic.BasicOptionPaneUI;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
/**
 * This class supports user interactions for a graphical application.
 */
public class GuiUserInteraction implements UserInteraction {
  static private final int MAX_CHARS_PER_LINE = 100;
  private Component parent = null;
  /**
   * Creates an instance.
   * @param parent Component acting as parent to dialogs supporting interaction.
   */
  public GuiUserInteraction(Component parent) {
    this.parent = parent;
  }
  /**
   * {@inheritDoc}
   */
  public Object confirm(String summary, String details,
                        String title, MessageType type, String[] options,
                        String def) {
    return confirm(summary, details, title, null, type, options, def, null);
  }
  /**
   * {@inheritDoc}
   */
  public Object confirm(String summary, String details, String fineDetails,
                        String title, MessageType type, String[] options,
                        String def, String viewDetailsOption) {
    int optionType;
    if (options != null) {
      if (options.length == 2) {
        optionType = JOptionPane.YES_NO_OPTION;
      } else if (options.length == 3) {
        optionType = JOptionPane.YES_NO_CANCEL_OPTION;
      } else {
        throw new IllegalArgumentException(
                "unsupported number of options: " + options.length);
      }
    } else {
      throw new NullPointerException("options cannot be null");
    }
    int msgType;
    switch(type) {
        case PLAIN: msgType = JOptionPane.PLAIN_MESSAGE; break;
        case ERROR: msgType = JOptionPane.ERROR_MESSAGE; break;
        case INFORMATION: msgType = JOptionPane.INFORMATION_MESSAGE; break;
        case WARNING: msgType = JOptionPane.WARNING_MESSAGE; break;
        case QUESTION: msgType = JOptionPane.QUESTION_MESSAGE; break;
        default: throw new IllegalArgumentException("unsupported MessageType");
    }
    JOptionPane op;
    if (fineDetails != null) {
      op = new DetailsOptionPane(MAX_CHARS_PER_LINE, fineDetails);
    } else {
      op = new MaxCharactersPerLineOptionPane(MAX_CHARS_PER_LINE);
    }
    // Create the main message using HTML formatting.  The max
    // characters per line functionality of the extends options
    // pane does not affect message that are components so we
    // have to format this ourselves.
    StringBuilder sb = new StringBuilder("<b>");
    sb.append(Utils.breakHtmlString(summary, MAX_CHARS_PER_LINE));
    sb.append("</b><br>");
    sb.append(Utils.breakHtmlString(details, MAX_CHARS_PER_LINE));
    JEditorPane ep = UIFactory.makeHtmlPane(
            sb.toString(),
            UIFactory.INSTRUCTIONS_FONT);
    ep.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 0));
    op.setMessage(ep);
    op.setOptionType(optionType);
    op.setMessageType(msgType);
    op.setOptions(options);
    op.setInitialValue(def);
    JDialog dlg = op.createDialog(parent, title);
    dlg.setVisible(true);
    return op.getValue();
  }
  /**
   * JOptionPane that controls the number of characters that are allowed
   * to appear on a single line in the input area of the dialog.
   */
  private class MaxCharactersPerLineOptionPane extends JOptionPane {
    /** Implements serializable. */
    static final long serialVersionUID = 8984664928623358120L;
    private int maxCharactersPerLineCount;
    /**
     * Creates an instance.
     * @param maxCharactersPerLine the maximum number of characters that
     *        are allowed on a single line in the dialog.
     */
    public MaxCharactersPerLineOptionPane(int maxCharactersPerLine) {
      this.maxCharactersPerLineCount = maxCharactersPerLine;
    }
    /**
     * {@inheritDoc}
     */
    public int getMaxCharactersPerLineCount() {
      return maxCharactersPerLineCount;
    }
  }
  /**
   * JOptionPane that controls the number of characters that are allowed
   * to appear on a single line in the input area of the dialog.
   */
  private class DetailsOptionPane extends MaxCharactersPerLineOptionPane {
    static final long serialVersionUID = -7813059467702205272L;
    private static final int MAX_DETAILS_COMPONENT_HEIGHT = 200;
    private boolean detailsShowing = false;
    private Component detailsComponent;
    private JDialog dialog;
    /**
     * Creates an instance.
     * @param maxCharactersPerLine the maximum number of characters that
     *        are allowed on a single line in the dialog.
     * @param details String of HTML representing the details section of the
     *        dialog.
     */
    public DetailsOptionPane(int maxCharactersPerLine,
                                         String details) {
      super(maxCharactersPerLine);
      detailsComponent = createDetailsComponent(details);
    }
    /**
     * {@inheritDoc}
     */
    public Component add(Component comp) {
      if ("OptionPane.buttonArea".equals(comp.getName())) {
        JPanel detailsButtonsPanel = new JPanel();
        detailsButtonsPanel.setLayout(
                new BoxLayout(detailsButtonsPanel,
                              BoxLayout.LINE_AXIS));
        final JButton btnDetails = new JButton("Show Details");
        btnDetails.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            Dimension current = dialog.getSize();
            if (!detailsShowing) {
              // detailsComponent.setVisible(true);
              dialog.setSize(current.width,
                      current.height + getExpansionHeight());
              btnDetails.setText("Hide Details");
            } else {
              // detailsComponent.setVisible(false);
              dialog.setSize(current.width,
                      current.height - getExpansionHeight());
              btnDetails.setText("Show Details");
            }
            detailsShowing = !detailsShowing;
          }
        });
        JPanel detailsBottom = new JPanel();
        Border border = UIManager.getBorder("OptionPane.buttonAreaBorder");
        if (border != null) {
          detailsBottom.setBorder(border);
        }
        detailsBottom.setLayout(
                new BasicOptionPaneUI.ButtonAreaLayout(
                        UIManager.getBoolean("OptionPane.sameSizeButtons"),
                        UIManager.getInt("OptionPane.buttonPadding")));
        detailsBottom.add(btnDetails);
        detailsButtonsPanel.add(detailsBottom);
        detailsButtonsPanel.add(Box.createHorizontalGlue());
        detailsButtonsPanel.add(comp);
        super.add(detailsButtonsPanel);
      } else {
        super.add(comp);
      }
      return comp;
    }
    /**
     * {@inheritDoc}
     */
    public JDialog createDialog(Component parentComponent, String title)
            throws HeadlessException
    {
      this.dialog = super.createDialog(parentComponent, title);
      Dimension d = dialog.getSize();
      add(detailsComponent);
      this.dialog.pack();
      dialog.setSize(d);
      return dialog;
    }
    private Component createDetailsComponent(String details) {
      JPanel detailsPanel = new JPanel();
      detailsPanel.setLayout(new GridBagLayout());
      GridBagConstraints gbc = new GridBagConstraints();
      gbc.insets = new Insets(15, 0, 0, 0);
      gbc.fill = GridBagConstraints.HORIZONTAL;
      detailsPanel.add(UIFactory.makeJLabel(null,
              "Details:",
              UIFactory.TextStyle.PRIMARY_FIELD_VALID), gbc);
      gbc.insets.top = UIFactory.TOP_INSET_PRIMARY_FIELD;
      gbc.gridx++;
      gbc.weightx = 1.0;
      gbc.weighty = 1.0;
      gbc.fill = GridBagConstraints.BOTH;
      JEditorPane ep;
      if (Utils.containsHtml(details)) {
        ep = UIFactory.makeHtmlPane(details, UIFactory.INSTRUCTIONS_FONT);
      } else {
        ep = UIFactory.makeTextPane(details, UIFactory.TextStyle.INSTRUCTIONS);
      }
      ep.setOpaque(false);
      detailsPanel.add(new JScrollPane(ep), gbc);
      return detailsPanel;
    }
    private int getExpansionHeight() {
      return (int) Math.min(detailsComponent.getPreferredSize().getHeight(),
              MAX_DETAILS_COMPONENT_HEIGHT);
    }
  }
//  public static void main(String[] args) {
//    new GuiUserInteraction(null).confirm(
//            "Summary",
//            "Details",
//            "Title",
//            MessageType.ERROR,
//            new String[]{"Yes","No"},"No");
//  }
}
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/ui/QuickSetup.java
@@ -521,6 +521,7 @@
      dialog = new QuickSetupDialog(application,
              installStatus, this);
      dialog.addButtonActionListener(this);
      application.setQuickSetupDialog(dialog);
    }
    return dialog;
  }
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java
@@ -40,6 +40,7 @@
import org.opends.quicksetup.Step;
import org.opends.quicksetup.BuildInformation;
import org.opends.quicksetup.CurrentInstallStatus;
import org.opends.quicksetup.UserInteraction;
import org.opends.quicksetup.webstart.WebStartDownloader;
import org.opends.quicksetup.util.Utils;
import org.opends.quicksetup.util.ZipExtractor;
@@ -573,7 +574,9 @@
            || getCurrentProgressStep() ==
            UpgradeProgressStep.FINISHED_WITH_ERRORS
            || getCurrentProgressStep() ==
            UpgradeProgressStep.FINISHED_WITH_WARNINGS;
            UpgradeProgressStep.FINISHED_WITH_WARNINGS
            || getCurrentProgressStep() ==
            UpgradeProgressStep.FINISHED_CANCELED;
  }
  /**
@@ -946,19 +949,46 @@
                null, "ARTIFICIAL ERROR FOR TESTING ABORT PROCESS", null);
      }
      try {
        LOG.log(Level.INFO, "verifying upgrade");
        setCurrentProgressStep(UpgradeProgressStep.VERIFYING);
        verifyUpgrade();
        notifyListeners(formatter.getFormattedDone() +
                formatter.getLineBreak());
        LOG.log(Level.INFO, "upgrade verification complete");
      } catch (ApplicationException e) {
      LOG.log(Level.INFO, "verifying upgrade");
      setCurrentProgressStep(UpgradeProgressStep.VERIFYING);
      Installation installation = getInstallation();
      ServerHealthChecker healthChecker = new ServerHealthChecker(installation);
      healthChecker.checkServer();
      List<String> errors = healthChecker.getProblemMessages();
      if (errors != null && errors.size() > 0) {
        notifyListeners(formatter.getFormattedError() +
                formatter.getLineBreak());
        LOG.log(Level.INFO, "Error verifying upgrade", e);
        throw e;
        String sep = System.getProperty("line.separator");
        String formattedDetails =
                Utils.listToString(errors, sep, /*bullet=*/"\u2022 ", "");
        runWarning = new ApplicationException(
                ApplicationException.Type.APPLICATION,
              "Upgraded server failed verification test by signaling " +
                      "errors during startup:" + sep +
                      formattedDetails, null);
        String cancel = "Cancel Upgrade";
        UserInteraction ui = userInteraction();
        if (ui == null || cancel.equals(ui.confirm(
                  "Upgrade Verification Failed",
                  "The upgraded server returned errors on startup.  Would " +
                          "you like to cancel the upgrade?  If you cancel, " +
                          "any changes made to the server by this upgrade " +
                          "will be backed out.",
                  formattedDetails,
                  "Upgrade Error",
                  UserInteraction.MessageType.ERROR,
                  new String[] { "Continue", cancel },
                  cancel, "View Error Details"))) {
            cancel();
            throw new ApplicationException(
              ApplicationException.Type.APPLICATION,
              "Upgrade canceled", null);
        }
      } else {
        notifyListeners(formatter.getFormattedDone() +
                formatter.getLineBreak());
      }
      LOG.log(Level.INFO, "upgrade verification complete");
      // Leave the server in the state requested by the user via the
      // checkbox on the review panel.  The upgrade has already been
@@ -1102,17 +1132,22 @@
      } else if (runWarning != null) {
        LOG.log(Level.INFO, "upgrade completed with warnings");
        String warningText = runWarning.getLocalizedMessage();
        // By design, the warnings are written as errors to the details section
        // as errors.  Warning markup is used surrounding the main message
        // at the end of progress.
        if (!Utils.isCli()) {
          notifyListenersOfLog();
          this.currentProgressStep = UpgradeProgressStep.FINISHED_WITH_WARNINGS;
          notifyListeners(formatter.getFormattedWarning(warningText, true));
          notifyListeners(formatter.getFormattedError(warningText, true));
        } else {
          notifyListeners(formatter.getFormattedWarning(warningText, true) +
          notifyListeners(formatter.getFormattedError(warningText, true) +
                          formatter.getLineBreak());
          notifyListeners(formatter.getLineBreak());
          setCurrentProgressStep(UpgradeProgressStep.FINISHED_WITH_WARNINGS);
          notifyListeners(formatter.getLineBreak());
        }
      } else {
        LOG.log(Level.INFO, "upgrade completed successfully");
        if (!Utils.isCli()) {
@@ -1199,19 +1234,6 @@
  }
  private void verifyUpgrade() throws ApplicationException {
    Installation installation = getInstallation();
    ServerHealthChecker healthChecker = new ServerHealthChecker(installation);
    healthChecker.checkServer();
    List<String> errors = healthChecker.getProblemMessages();
    if (errors != null && errors.size() > 0) {
      throw new ApplicationException(ApplicationException.Type.APPLICATION,
              "Upgraded server failed verification test by signaling " +
                      "errors during startup: " +
                      Utils.listToString(errors, " "), null);
    }
  }
  private void applyConfigurationCustomizations() throws ApplicationException {
    try {
      File configDiff = getCustomConfigDiffFile();
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/UpgraderCliHelper.java
@@ -68,7 +68,8 @@
    ArgumentParser ap = createArgumentParser();
    try {
      ap.parseArguments(args);
      uud.setSilent(isSilent());
      uud.setNoninteractive(isNoninteractive());
      if (localInstallPackFileNameArg.isPresent()) {
        String localInstallPackFileName =
                localInstallPackFileNameArg.getValue();
@@ -87,31 +88,27 @@
      }
    } catch (ArgumentException e) {
      e.printStackTrace(); // TODO remove
      throw new UserDataException(null, "error parsing arguments");
      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);
    ArgumentParser argParser = createArgumentParser(
            "org.opends.quicksetup.upgrader.Upgrader",
            toolDescription,
            false);
    // Initialize all the command-line argument types and register them with the
    // parser.
    // try {
    // Initialize all the app specific command-line argument types
    // and register them with the parser.
    try {
      localInstallPackFileNameArg =
           new StringArgument("install package file",
                   FILE_OPTION_SHORT, FILE_OPTION_LONG,
                   false, true, "{install package file}", 0);
              new StringArgument("install package file",
                      FILE_OPTION_SHORT, FILE_OPTION_LONG,
                      false, true, "{install package file}", 0);
      argParser.addArgument(localInstallPackFileNameArg);
    } catch (ArgumentException e) {
      LOG.log(Level.INFO, "error", e);
    }
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/util/HtmlProgressMessageFormatter.java
@@ -94,7 +94,7 @@
  public String getFormattedError(String text, boolean applyMargin)
  {
    String html;
    if (!containsHtml(text)) {
    if (!Utils.containsHtml(text)) {
      html = UIFactory.getIconHtml(UIFactory.IconType.ERROR_LARGE)
          + SPACE
          + SPACE
@@ -127,7 +127,7 @@
  public String getFormattedWarning(String text, boolean applyMargin)
  {
    String html;
    if (!containsHtml(text)) {
    if (!Utils.containsHtml(text)) {
      html =
        UIFactory.getIconHtml(UIFactory.IconType.WARNING_LARGE)
            + SPACE
@@ -620,10 +620,5 @@
    }
  }
  private boolean containsHtml(String text) {
    return (text != null &&
            text.indexOf('<') != -1 &&
            text.indexOf('>') != -1);
  }
}
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/util/Utils.java
@@ -1265,9 +1265,31 @@
   * @return String representing the list
   */
  static public String listToString(List<?> list, String separator) {
    return listToString(list, separator, null, 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
   * @param prefix prepended to each individual element in the list before
   *        adding to the returned string.
   * @param suffix appended to each individual element in the list before
   *        adding to the returned string.
   * @return String representing the list
   */
  static public String listToString(List<?> list, String separator,
                                    String prefix, String suffix) {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < list.size(); i++) {
      if (prefix != null) {
        sb.append(prefix);
      }
      sb.append(list.get(i));
      if (suffix != null) {
        sb.append(suffix);
      }
      if (i < list.size() - 1) {
        sb.append(separator);
      }
@@ -1365,4 +1387,44 @@
    return b;
  }
  /**
   * Inserts HTML break tags into <code>d</code> breaking it up
   * so that no line is longer than <code>maxll</code>.
   * @param d String to break
   * @param maxll int maximum line length
   * @return String representing <code>d</code> with HTML break
   *         tags inserted
   */
  static public String breakHtmlString(String d, int maxll) {
    // Primitive line wrapping
    int len = d.length();
    if (len <= 0)
      return d;
    if (len > maxll) {
      int p = d.lastIndexOf(' ', maxll);
      if (p <= 0)
        p = d.indexOf(' ', maxll);
      if (p > 0 && p < len) {
        return d.substring(0, p) +
                "<br>" +
               breakHtmlString(d.substring(p + 1), maxll);
      } else {
        return d;
      }
    } else {
      return d;
    }
  }
  /**
   * Tests a text string to see if it contains HTML.
   * @param text String to test
   * @return true if the string contains HTML
   */
  static public boolean containsHtml(String text) {
    return (text != null &&
            text.indexOf('<') != -1 &&
            text.indexOf('>') != -1);
  }
}