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

Violette Roche-Montane
07.50.2013 00d37e85955e374be365f1c34de5af1f69a6476c
opendj-sdk/opends/src/server/org/opends/server/util/cli/ConsoleApplication.java
@@ -23,12 +23,10 @@
 *
 *
 *      Copyright 2008-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2012 ForgeRock AS
 *      Portions Copyright 2012-2013 ForgeRock AS
 */
package org.opends.server.util.cli;
import static org.opends.messages.AdminToolMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import static org.opends.messages.QuickSetupMessages.*;
@@ -70,37 +68,65 @@
import org.opends.server.util.PasswordReader;
import org.opends.server.util.SetupUtils;
/**
 * This class provides an abstract base class which can be used as the
 * basis of a console-based application.
 * This class provides an abstract base class which can be used as the basis of
 * a console-based application.
 */
public abstract class ConsoleApplication {
public abstract class ConsoleApplication
{
  /**
   * A null reader.
   */
  private static final class NullReader extends Reader {
  private static final class NullReader extends Reader
  {
    /**
     * {@inheritDoc}
     */
    @Override
    public void close() throws IOException {
    public void close() throws IOException
    {
      // Do nothing.
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
    public int read(char[] cbuf, int off, int len) throws IOException
    {
      return -1;
    }
  }
  /**
   * Defines a title in the console application.
   */
  public final static int TITLE = 0;
  /**
   * Defines a subtitle in the console application.
   */
  public final static int SUBTITLE = 1;
  /**
   * Defines a notice in the console application.
   */
  public final static int NOTICE = 2;
  /**
   * Defines a normal line in the console application.
   */
  public final static int NORMAL = 3;
  /**
   * Defines an error line in the console application.
   */
  public final static int ERROR = 4;
  /**
   * Defines a break line in the console application.
   */
  public final static int BREAKLINE = 5;
  // The error stream which this application should use.
  private final PrintStream err;
@@ -111,7 +137,7 @@
  private final PrintStream out;
  /**
   *  The maximum number of times we try to confirm.
   * The maximum number of times we try to confirm.
   */
  protected final static int CONFIRMATION_MAX_TRIES = 5;
@@ -121,9 +147,8 @@
  /**
   * The String used to write comments in a shell (or batch) script.
   */
  protected static final String SHELL_COMMENT_SEPARATOR =
    SetupUtils.isWindows() ?
      COMMENT_BATCH_WINDOWS : COMMENT_SHELL_UNIX;
  protected static final String SHELL_COMMENT_SEPARATOR = SetupUtils
      .isWindows() ? COMMENT_BATCH_WINDOWS : COMMENT_SHELL_UNIX;
  /**
   * Creates a new console application instance.
@@ -136,28 +161,36 @@
   *          The application error stream.
   */
  protected ConsoleApplication(BufferedReader in, PrintStream out,
      PrintStream err) {
    if (in != null) {
      PrintStream err)
  {
    if (in != null)
    {
      this.in = in;
    } else {
    }
    else
    {
      this.in = new BufferedReader(new NullReader());
    }
    if (out != null) {
    if (out != null)
    {
      this.out = out;
    } else {
    }
    else
    {
      this.out = NullOutputStream.printStream();
    }
    if (err != null) {
    if (err != null)
    {
      this.err = out;
    } else {
    }
    else
    {
      this.err = NullOutputStream.printStream();
    }
  }
  /**
   * Creates a new console application instance.
   *
@@ -169,70 +202,89 @@
   *          The application error stream.
   */
  protected ConsoleApplication(InputStream in, OutputStream out,
      OutputStream err) {
    if (in != null) {
      OutputStream err)
  {
    if (in != null)
    {
      this.in = new BufferedReader(new InputStreamReader(in));
    } else {
    }
    else
    {
      this.in = new BufferedReader(new NullReader());
    }
    if (out != null) {
    if (out != null)
    {
      this.out = new PrintStream(out);
    } else {
    }
    else
    {
      this.out = NullOutputStream.printStream();
    }
    if (err != null) {
    if (err != null)
    {
      this.err = new PrintStream(err);
    } else {
    }
    else
    {
      this.err = NullOutputStream.printStream();
    }
  }
  /**
   * Interactively confirms whether a user wishes to perform an
   * action. If the application is non-interactive, then the provided
   * default is returned automatically.
   * Interactively confirms whether a user wishes to perform an action. If the
   * application is non-interactive, then the provided default is returned
   * automatically.
   *
   * @param prompt
   *          The prompt describing the action.
   * @param defaultValue
   *          The default value for the confirmation message. This
   *          will be returned if the application is non-interactive
   *          or if the user just presses return.
   * @return Returns <code>true</code> if the user wishes the action
   *         to be performed, or <code>false</code> if they refused,
   *         or if an exception occurred.
   *          The default value for the confirmation message. This will be
   *          returned if the application is non-interactive or if the user just
   *          presses return.
   * @return Returns <code>true</code> if the user wishes the action to be
   *         performed, or <code>false</code> if they refused, or if an
   *         exception occurred.
   * @throws CLIException
   *           If the user's response could not be read from the
   *           console for some reason.
   *           If the user's response could not be read from the console for
   *           some reason.
   */
  public final boolean confirmAction(Message prompt, final boolean defaultValue)
      throws CLIException {
    if (!isInteractive()) {
      throws CLIException
  {
    if (!isInteractive())
    {
      return defaultValue;
    }
    final Message yes = INFO_GENERAL_YES.get();
    final Message no = INFO_GENERAL_NO.get();
    final Message errMsg = ERR_CONSOLE_APP_CONFIRM.get(yes, no);
    prompt = INFO_MENU_PROMPT_CONFIRM.get(prompt, yes, no, defaultValue ? yes
        : no);
    prompt =
        INFO_MENU_PROMPT_CONFIRM.get(prompt, yes, no, defaultValue ? yes : no);
    ValidationCallback<Boolean> validator = new ValidationCallback<Boolean>() {
    ValidationCallback<Boolean> validator = new ValidationCallback<Boolean>()
    {
      @Override
      public Boolean validate(ConsoleApplication app, String input) {
      public Boolean validate(ConsoleApplication app, String input)
      {
        String ninput = input.toLowerCase().trim();
        if (ninput.length() == 0) {
        if (ninput.length() == 0)
        {
          return defaultValue;
        } else if (no.toString().toLowerCase().startsWith(ninput)) {
        }
        else if (no.toString().toLowerCase().startsWith(ninput))
        {
          return false;
        } else if (yes.toString().toLowerCase().startsWith(ninput)) {
        }
        else if (yes.toString().toLowerCase().startsWith(ninput))
        {
          return true;
        } else {
        }
        else
        {
          // Try again...
          app.println();
          app.println(errMsg);
@@ -246,165 +298,217 @@
    return readValidatedInput(prompt, validator, CONFIRMATION_MAX_TRIES);
  }
  /**
   * Gets the application error stream.
   *
   * @return Returns the application error stream.
   */
  public final PrintStream getErrorStream() {
  public final PrintStream getErrorStream()
  {
    return err;
  }
  /**
   * Gets the application input stream.
   *
   * @return Returns the application input stream.
   */
  public final BufferedReader getInputStream() {
  public final BufferedReader getInputStream()
  {
    return in;
  }
  /**
   * Gets the application output stream.
   *
   * @return Returns the application output stream.
   */
  public final PrintStream getOutputStream() {
  public final PrintStream getOutputStream()
  {
    return out;
  }
  /**
   * Indicates whether or not the user has requested advanced mode.
   *
   * @return Returns <code>true</code> if the user has requested
   *         advanced mode.
   * @return Returns <code>true</code> if the user has requested advanced mode.
   */
  public abstract boolean isAdvancedMode();
  /**
   * Indicates whether or not the user has requested interactive
   * behavior.
   * Indicates whether or not the user has requested interactive behavior.
   *
   * @return Returns <code>true</code> if the user has requested
   *         interactive behavior.
   * @return Returns <code>true</code> if the user has requested interactive
   *         behavior.
   */
  public abstract boolean isInteractive();
  /**
   * Indicates whether or not this console application is running in
   * its menu-driven mode. This can be used to dictate whether output
   * should go to the error stream or not. In addition, it may also
   * dictate whether or not sub-menus should display a cancel option
   * as well as a quit option.
   * Indicates whether or not this console application is running in its
   * menu-driven mode. This can be used to dictate whether output should go to
   * the error stream or not. In addition, it may also dictate whether or not
   * sub-menus should display a cancel option as well as a quit option.
   *
   * @return Returns <code>true</code> if this console application
   *         is running in its menu-driven mode.
   * @return Returns <code>true</code> if this console application is running in
   *         its menu-driven mode.
   */
  public abstract boolean isMenuDrivenMode();
  /**
   * Indicates whether or not the user has requested quiet output.
   *
   * @return Returns <code>true</code> if the user has requested
   *         quiet output.
   * @return Returns <code>true</code> if the user has requested quiet output.
   */
  public abstract boolean isQuiet();
  /**
   * Indicates whether or not the user has requested script-friendly
   * output.
   * Indicates whether or not the user has requested script-friendly output.
   *
   * @return Returns <code>true</code> if the user has requested
   *         script-friendly output.
   * @return Returns <code>true</code> if the user has requested script-friendly
   *         output.
   */
  public abstract boolean isScriptFriendly();
  /**
   * Indicates whether or not the user has requested verbose output.
   *
   * @return Returns <code>true</code> if the user has requested
   *         verbose output.
   * @return Returns <code>true</code> if the user has requested verbose output.
   */
  public abstract boolean isVerbose();
  /**
   * Interactively prompts the user to press return to continue. This
   * method should be called in situations where a user needs to be
   * given a chance to read some documentation before continuing
   * (continuing may cause the documentation to be scrolled out of
   * view).
   * Interactively prompts the user to press return to continue. This method
   * should be called in situations where a user needs to be given a chance to
   * read some documentation before continuing (continuing may cause the
   * documentation to be scrolled out of view).
   */
  public final void pressReturnToContinue() {
  public final void pressReturnToContinue()
  {
    Message msg = INFO_MENU_PROMPT_RETURN_TO_CONTINUE.get();
    try {
    try
    {
      readLineOfInput(msg);
    } catch (CLIException e) {
    }
    catch (CLIException e)
    {
      // Ignore the exception - applications don't care.
    }
  }
  /**
   * Displays a blank line to the error stream.
   */
  public final void println() {
  public final void println()
  {
    err.println();
  }
  /**
   * Displays a message to the error stream.
   *
   * @param msg
   *          The message.
   */
  public final void println(Message msg) {
  public final void println(Message msg)
  {
    err.println(wrapText(msg, MAX_LINE_WIDTH));
  }
  /**
   * Displays a message to the error stream.
   *
   * @param msg
   *          The message.
   */
  public final void print(Message msg) {
  public final void print(Message msg)
  {
    err.print(wrapText(msg, MAX_LINE_WIDTH));
  }
  /**
   * Print a line with EOL in the output stream.
   *
   * @param msg
   *          The message to display in normal mode.
   * @param indent
   *          The indentation.
   */
  public final void println(final Message msg, final int indent)
  {
    println(0, msg, indent);
  }
  /**
   * Print a line with EOL in the output stream.
   *
   * @param typeMessage
   *          The type of formatted output desired.
   * @param msg
   *          The message to display in normal mode.
   * @param indent
   *          The indentation.
   */
  public final void println(final int typeMessage, final Message msg,
      final int indent)
  {
    if (!isQuiet())
    {
      if (typeMessage == TITLE)
      {
        out.println();
        out.println(">>>> " + wrapText(msg, MAX_LINE_WIDTH, indent));
        out.println();
      }
      else if (typeMessage == SUBTITLE)
      {
        out.println(wrapText(msg, MAX_LINE_WIDTH, indent));
        out.println();
      }
      else if (typeMessage == NOTICE)
      {
        out.println(wrapText(" * " + msg, MAX_LINE_WIDTH, indent));
      }
      else if (typeMessage == ERROR)
      {
        out.println();
        out.println(wrapText("** " + msg, MAX_LINE_WIDTH, indent));
      }
      else if (typeMessage == BREAKLINE)
      {
        out.println();
      }
      else
      {
        out.println(wrapText(msg, MAX_LINE_WIDTH, indent));
      }
    }
  }
  /**
   * Displays a blank line to the output stream if we are not in quiet mode.
   */
  public final void printlnProgress() {
  public final void printlnProgress()
  {
    if (!isQuiet())
    {
      out.println();
    }
  }
  /**
   * Displays a message to the output stream if we are not in quiet mode.
   * Message is wrap to max line width.
   *
   * @param msg
   *          The message.
   */
  public final void printlnProgress(Message msg)
  {
    if (!isQuiet())
    {
      out.println(wrapText(msg, MAX_LINE_WIDTH));
    }
  }
  /**
   * Displays a message to the output stream if we are not in quiet mode.
@@ -412,7 +516,8 @@
   * @param msg
   *          The message.
   */
  public final void printProgress(Message msg) {
  public final void printProgress(final Message msg)
  {
    if (!isQuiet())
    {
      out.print(msg);
@@ -420,13 +525,67 @@
  }
  /**
   * Display the batch progress string to the error stream, if we are not
   * in quiet mode.
   * Prints a progress bar on the same output stream line if not in quiet mode.
   *
   * <pre>
   * Like
   *   msg......   50%
   *   if progress is up to 100 :
   *   msg.....................  100%
   *   if progress is < 0 :
   *   msg....  FAIL
   *   msg.....................  FAIL
   * </pre>
   *
   * @param linePos
   *          The progress bar starts at this position on the line.
   * @param progress
   *          The current percentage progress to print.
   */
  public final void printProgressBar(final int linePos, final int progress)
  {
    if (!isQuiet())
    {
      final int spacesLeft = MAX_LINE_WIDTH - linePos - 10;
      StringBuilder bar = new StringBuilder();
      if (progress != 0)
      {
        for (int i = 0; i < 50; i++)
        {
          if ((i < (Math.abs(progress) / 2)) && (bar.length() < spacesLeft))
          {
            bar.append(".");
          }
        }
      }
      bar.append(".   ");
      if(progress >= 0) {
        bar.append(progress).append("%     ");
      } else {
        bar.append("FAIL");
      }
      final int endBuilder = linePos + bar.length();
      for (int i = 0; i < endBuilder; i++)
      {
        bar.append("\b");
      }
      if (progress >= 100 || progress < 0)
      {
        bar.append(EOL);
      }
      out.print(bar.toString());
    }
  }
  /**
   * Display the batch progress string to the error stream, if we are not in
   * quiet mode.
   *
   * @param s
   *          The string to display
   */
  public final void printlnBatchProgress(String s) {
  public final void printlnBatchProgress(String s)
  {
    if (!isQuiet())
    {
      err.println(s);
@@ -434,98 +593,164 @@
  }
  /**
   * Displays a message to the error stream indented by the specified
   * number of columns.
   * Displays a message to the error stream indented by the specified number of
   * columns.
   *
   * @param msg
   *          The message.
   * @param indent
   *          The number of columns to indent.
   */
  public final void println(Message msg, int indent) {
  public final void printErrln(Message msg, int indent)
  {
    err.println(wrapText(msg, MAX_LINE_WIDTH, indent));
  }
  /**
   * Displays a message to the error stream if verbose mode is
   * enabled.
   * Displays a message to the error stream if verbose mode is enabled.
   *
   * @param msg
   *          The verbose message.
   */
  public final void printVerboseMessage(Message msg) {
    if (isVerbose() || isInteractive()) {
  public final void printVerboseMessage(Message msg)
  {
    if (isVerbose() || isInteractive())
    {
      err.println(wrapText(msg, MAX_LINE_WIDTH));
    }
  }
  /**
   * Interactively retrieves a line of input from the console.
   *
   * @param prompt
   *          The prompt.
   * @return Returns the line of input, or <code>null</code> if the
   *         end of input has been reached.
   * @return Returns the line of input, or <code>null</code> if the end of input
   *         has been reached.
   * @throws CLIException
   *           If the line of input could not be retrieved for some
   *           reason.
   *           If the line of input could not be retrieved for some reason.
   */
  public final String readLineOfInput(Message prompt) throws CLIException {
  public final String readLineOfInput(Message prompt) throws CLIException
  {
    if (prompt != null)
    {
      err.print(wrapText(prompt, MAX_LINE_WIDTH));
      err.print(" ");
    }
    try {
    try
    {
      String s = in.readLine();
      if (s == null) {
      if (s == null)
      {
        throw CLIException
            .adaptInputException(new EOFException("End of input"));
      } else {
      }
      else
      {
        return s;
      }
    } catch (IOException e) {
    }
    catch (IOException e)
    {
      throw CLIException.adaptInputException(e);
    }
  }
  /**
   * Displays a message and read the user's input from output.
   *
   * @param prompt
   *          The message to display.
   * @param defaultValue
   *          The default answer by default.
   * @param formattedOutput
   *          The formatted style chosen.
   * @return The user's input as a string.
   * @throws CLIException
   *           If an Exception occurs during the process.
   */
  public final String readInput(final Message prompt,
      final String defaultValue, final int formattedOutput) throws CLIException
  {
    String answer = null;
    final Message messageToDisplay =
        INFO_PROMPT_SINGLE_DEFAULT.get(prompt.toString(), defaultValue);
    if (formattedOutput == TITLE)
    {
      println();
    }
    print(messageToDisplay);
    out.print(" ");
    try
    {
      // Reads the user input.
      answer = in.readLine();
    }
    catch (IOException e)
    {
      throw CLIException.adaptInputException(e);
    }
    if (formattedOutput == TITLE || formattedOutput == SUBTITLE)
    {
      println();
    }
    if ("".equals(answer))
    {
      if (defaultValue == null)
      {
        println(INFO_ERROR_EMPTY_RESPONSE.get());
      }
      else
      {
        return defaultValue;
      }
    }
    return answer;
  }
  /**
   * Commodity method that interactively prompts (on error output) the user to
   * provide a string value.  Any non-empty string will be allowed (the empty
   * provide a string value. Any non-empty string will be allowed (the empty
   * string will indicate that the default should be used, if there is one).
   *
   * @param  prompt        The prompt to present to the user.
   * @param  defaultValue  The default value to assume if the user presses ENTER
   *                       without typing anything, or <CODE>null</CODE> if
   *                       there should not be a default and the user must
   *                       explicitly provide a value.
   *
   * @param prompt
   *          The prompt to present to the user.
   * @param defaultValue
   *          The default value to assume if the user presses ENTER without
   *          typing anything, or <CODE>null</CODE> if there should not be a
   *          default and the user must explicitly provide a value.
   * @throws CLIException
   *           If the line of input could not be retrieved for some
   *           reason.
   * @return  The string value read from the user.
   *           If the line of input could not be retrieved for some reason.
   * @return The string value read from the user.
   */
  public String readInput(Message prompt, String defaultValue)
  throws CLIException {
    while (true) {
      if (defaultValue != null) {
        prompt = INFO_PROMPT_SINGLE_DEFAULT.get(prompt.toString(),
            defaultValue);
      throws CLIException
  {
    while (true)
    {
      if (defaultValue != null)
      {
        prompt =
            INFO_PROMPT_SINGLE_DEFAULT.get(prompt.toString(), defaultValue);
      }
      String response = readLineOfInput(prompt);
      if ("".equals(response)) {
        if (defaultValue == null) {
      if ("".equals(response))
      {
        if (defaultValue == null)
        {
          println(INFO_ERROR_EMPTY_RESPONSE.get());
        } else {
        }
        else
        {
          return defaultValue;
        }
      } else {
      }
      else
      {
        return response;
      }
    }
@@ -533,18 +758,19 @@
  /**
   * Commodity method that interactively prompts (on error output) the user to
   * provide a string value.  Any non-empty string will be allowed (the empty
   * string will indicate that the default should be used, if there is one).
   * If an error occurs a message will be logged to the provided logger.
   * provide a string value. Any non-empty string will be allowed (the empty
   * string will indicate that the default should be used, if there is one). If
   * an error occurs a message will be logged to the provided logger.
   *
   * @param  prompt        The prompt to present to the user.
   * @param  defaultValue  The default value to assume if the user presses ENTER
   *                       without typing anything, or <CODE>null</CODE> if
   *                       there should not be a default and the user must
   *                       explicitly provide a value.
   *
   * @param logger the Logger to be used to log the error message.
   * @return  The string value read from the user.
   * @param prompt
   *          The prompt to present to the user.
   * @param defaultValue
   *          The default value to assume if the user presses ENTER without
   *          typing anything, or <CODE>null</CODE> if there should not be a
   *          default and the user must explicitly provide a value.
   * @param logger
   *          the Logger to be used to log the error message.
   * @return The string value read from the user.
   */
  public String readInput(Message prompt, String defaultValue, Logger logger)
  {
@@ -555,7 +781,7 @@
    }
    catch (CLIException ce)
    {
      logger.log(Level.WARNING, "Error reading input: "+ce, ce);
      logger.log(Level.WARNING, "Error reading input: " + ce, ce);
    }
    return s;
  }
@@ -569,25 +795,30 @@
   * @throws CLIException
   *           If the password could not be retrieved for some reason.
   */
  public final String readPassword(Message prompt) throws CLIException {
  public final String readPassword(Message prompt) throws CLIException
  {
    err.print(wrapText(prompt + " ", MAX_LINE_WIDTH));
    char[] pwChars;
    try {
    try
    {
      pwChars = PasswordReader.readPassword();
    } catch (Exception e) {
    }
    catch (Exception e)
    {
      throw CLIException.adaptInputException(e);
    }
    return new String(pwChars);
  }
  /**
   * Commodity method that interactively retrieves a password from the
   * console. If there is an error an error message is logged to the provided
   * Logger and <CODE>null</CODE> is returned.
   * Commodity method that interactively retrieves a password from the console.
   * If there is an error an error message is logged to the provided Logger and
   * <CODE>null</CODE> is returned.
   *
   * @param prompt
   *          The password prompt.
   * @param logger the Logger to be used to log the error message.
   * @param logger
   *          the Logger to be used to log the error message.
   * @return Returns the password.
   */
  protected final String readPassword(Message prompt, Logger logger)
@@ -599,7 +830,7 @@
    }
    catch (CLIException ce)
    {
      logger.log(Level.WARNING, "Error reading input: "+ce, ce);
      logger.log(Level.WARNING, "Error reading input: " + ce, ce);
    }
    return pwd;
  }
@@ -616,7 +847,7 @@
   *           If the port could not be retrieved for some reason.
   */
  public final int readPort(Message prompt, final int defaultValue)
  throws CLIException
      throws CLIException
  {
    ValidationCallback<Integer> callback = new ValidationCallback<Integer>()
    {
@@ -653,9 +884,11 @@
    };
    if (defaultValue != -1) {
      prompt = INFO_PROMPT_SINGLE_DEFAULT.get(prompt.toString(),
          String.valueOf(defaultValue));
    if (defaultValue != -1)
    {
      prompt =
          INFO_PROMPT_SINGLE_DEFAULT.get(prompt.toString(), String
              .valueOf(defaultValue));
    }
    return readValidatedInput(prompt, callback, CONFIRMATION_MAX_TRIES);
@@ -663,23 +896,28 @@
  /**
   * Returns a message object for the given NamingException.
   * @param ne the NamingException.
   * @param hostPort the hostPort representation of the server we were
   * contacting when the NamingException occurred.
   *
   * @param ne
   *          the NamingException.
   * @param hostPort
   *          the hostPort representation of the server we were contacting when
   *          the NamingException occurred.
   * @return a message object for the given NamingException.
   */
  protected Message getMessageForException(NamingException ne,
      String hostPort)
  protected Message getMessageForException(NamingException ne, String hostPort)
  {
    return Utils.getMessageForException(ne, hostPort);
  }
  /**
   * Commodity method used to repeatidly ask the user to provide a port value.
   * @param prompt the prompt message.
   * @param defaultValue the default value of the port to be proposed to the
   * user.
   * @param logger the logger where the errors will be written.
   *
   * @param prompt
   *          the prompt message.
   * @param defaultValue
   *          the default value of the port to be proposed to the user.
   * @param logger
   *          the logger where the errors will be written.
   * @return the port value provided by the user.
   */
  protected int askPort(Message prompt, int defaultValue, Logger logger)
@@ -694,66 +932,71 @@
      catch (CLIException ce)
      {
        port = -1;
        logger.log(Level.WARNING, "Error reading input: "+ce, ce);
        logger.log(Level.WARNING, "Error reading input: " + ce, ce);
      }
    }
    return port;
  }
  /**
   * Interactively prompts for user input and continues until valid
   * input is provided.
   * Interactively prompts for user input and continues until valid input is
   * provided.
   *
   * @param <T>
   *          The type of decoded user input.
   * @param prompt
   *          The interactive prompt which should be displayed on each
   *          input attempt.
   *          The interactive prompt which should be displayed on each input
   *          attempt.
   * @param validator
   *          An input validator responsible for validating and
   *          decoding the user's response.
   *          An input validator responsible for validating and decoding the
   *          user's response.
   * @return Returns the decoded user's response.
   * @throws CLIException
   *           If an unexpected error occurred which prevented
   *           validation.
   *           If an unexpected error occurred which prevented validation.
   */
  public final <T> T readValidatedInput(Message prompt,
      ValidationCallback<T> validator) throws CLIException {
    while (true) {
      ValidationCallback<T> validator) throws CLIException
  {
    while (true)
    {
      String response = readLineOfInput(prompt);
      T value = validator.validate(this, response);
      if (value != null) {
      if (value != null)
      {
        return value;
      }
    }
  }
  /**
   * Interactively prompts for user input and continues until valid
   * input is provided.
   * Interactively prompts for user input and continues until valid input is
   * provided.
   *
   * @param <T>
   *          The type of decoded user input.
   * @param prompt
   *          The interactive prompt which should be displayed on each
   *          input attempt.
   *          The interactive prompt which should be displayed on each input
   *          attempt.
   * @param validator
   *          An input validator responsible for validating and
   *          decoding the user's response.
   *          An input validator responsible for validating and decoding the
   *          user's response.
   * @param maxTries
   *          The maximum number of tries that we can make.
   * @return Returns the decoded user's response.
   * @throws CLIException
   *           If an unexpected error occurred which prevented
   *           validation or if the maximum number of tries was reached.
   *           If an unexpected error occurred which prevented validation or if
   *           the maximum number of tries was reached.
   */
  public final <T> T readValidatedInput(Message prompt,
      ValidationCallback<T> validator, int maxTries) throws CLIException {
      ValidationCallback<T> validator, int maxTries) throws CLIException
  {
    int nTries = 0;
    while (nTries < maxTries) {
    while (nTries < maxTries)
    {
      String response = readLineOfInput(prompt);
      T value = validator.validate(this, response);
      if (value != null) {
      if (value != null)
      {
        return value;
      }
      nTries++;
@@ -764,21 +1007,22 @@
  /**
   * Commodity method that interactively confirms whether a user wishes to
   * perform an action. If the application is non-interactive, then the provided
   * default is returned automatically.  If there is an error an error message
   * is logged to the provided Logger and the defaul value is returned.
   * default is returned automatically. If there is an error an error message is
   * logged to the provided Logger and the defaul value is returned.
   *
   * @param prompt
   *          The prompt describing the action.
   * @param defaultValue
   *          The default value for the confirmation message. This
   *          will be returned if the application is non-interactive
   *          or if the user just presses return.
   * @param logger the Logger to be used to log the error message.
   * @return Returns <code>true</code> if the user wishes the action
   *         to be performed, or <code>false</code> if they refused.
   * @throws CLIException if the user did not provide valid answer after
   *         a certain number of tries
   *         (ConsoleApplication.CONFIRMATION_MAX_TRIES)
   *          The default value for the confirmation message. This will be
   *          returned if the application is non-interactive or if the user just
   *          presses return.
   * @param logger
   *          the Logger to be used to log the error message.
   * @return Returns <code>true</code> if the user wishes the action to be
   *         performed, or <code>false</code> if they refused.
   * @throws CLIException
   *           if the user did not provide valid answer after a certain number
   *           of tries (ConsoleApplication.CONFIRMATION_MAX_TRIES)
   */
  protected final boolean askConfirmation(Message prompt, boolean defaultValue,
      Logger logger) throws CLIException
@@ -799,14 +1043,14 @@
      catch (CLIException ce)
      {
        if (ce.getMessageObject().getDescriptor().equals(
            ERR_CONFIRMATION_TRIES_LIMIT_REACHED) ||
            ce.getMessageObject().getDescriptor().equals(
            ERR_CONFIRMATION_TRIES_LIMIT_REACHED)
            || ce.getMessageObject().getDescriptor().equals(
                ERR_TRIES_LIMIT_REACHED))
        {
          throw ce;
        }
        logger.log(Level.WARNING, "Error reading input: "+ce, ce);
//      Try again...
        logger.log(Level.WARNING, "Error reading input: " + ce, ce);
        //      Try again...
        println();
      }
    }
@@ -814,53 +1058,63 @@
    if (!done)
    {
      // This means we reached the maximum number of tries
      throw new CLIException(ERR_CONFIRMATION_TRIES_LIMIT_REACHED.get(
          CONFIRMATION_MAX_TRIES));
      throw new CLIException(ERR_CONFIRMATION_TRIES_LIMIT_REACHED
          .get(CONFIRMATION_MAX_TRIES));
    }
    return v;
  }
  /**
   * Returns an InitialLdapContext using the provided parameters.  We try
   * to guarantee that the connection is able to read the configuration.
   * @param host the host name.
   * @param port the port to connect.
   * @param useSSL whether to use SSL or not.
   * @param useStartTLS whether to use StartTLS or not.
   * @param bindDn the bind dn to be used.
   * @param pwd the password.
   * @param connectTimeout the timeout in milliseconds to connect to the server.
   * @param trustManager the trust manager.
   * Returns an InitialLdapContext using the provided parameters. We try to
   * guarantee that the connection is able to read the configuration.
   *
   * @param host
   *          the host name.
   * @param port
   *          the port to connect.
   * @param useSSL
   *          whether to use SSL or not.
   * @param useStartTLS
   *          whether to use StartTLS or not.
   * @param bindDn
   *          the bind dn to be used.
   * @param pwd
   *          the password.
   * @param connectTimeout
   *          the timeout in milliseconds to connect to the server.
   * @param trustManager
   *          the trust manager.
   * @return an InitialLdapContext connected.
   * @throws NamingException if there was an error establishing the connection.
   * @throws NamingException
   *           if there was an error establishing the connection.
   */
  protected InitialLdapContext createAdministrativeContext(String host,
      int port, boolean useSSL, boolean useStartTLS, String bindDn, String pwd,
      int connectTimeout, ApplicationTrustManager trustManager)
  throws NamingException
      throws NamingException
  {
    InitialLdapContext ctx;
    String ldapUrl = ConnectionUtils.getLDAPUrl(host, port, useSSL);
    if (useSSL)
    {
      ctx = Utils.createLdapsContext(ldapUrl, bindDn, pwd,
          connectTimeout, null, trustManager);
      ctx =
          Utils.createLdapsContext(ldapUrl, bindDn, pwd, connectTimeout, null,
              trustManager);
    }
    else if (useStartTLS)
    {
      ctx = Utils.createStartTLSContext(ldapUrl, bindDn, pwd,
          connectTimeout, null, trustManager,
          null);
      ctx =
          Utils.createStartTLSContext(ldapUrl, bindDn, pwd, connectTimeout,
              null, trustManager, null);
    }
    else
    {
      ctx = Utils.createLdapContext(ldapUrl, bindDn, pwd,
          connectTimeout, null);
      ctx = Utils.createLdapContext(ldapUrl, bindDn, pwd, connectTimeout, null);
    }
    if (!ConnectionUtils.connectedAsAdministrativeUser(ctx))
    {
      throw new NoPermissionException(
          ERR_NOT_ADMINISTRATIVE_USER.get().toString());
      throw new NoPermissionException(ERR_NOT_ADMINISTRATIVE_USER.get()
          .toString());
    }
    return ctx;
  }
@@ -868,33 +1122,39 @@
  /**
   * Creates an Initial LDAP Context interacting with the user if the
   * application is interactive.
   * @param ci the LDAPConnectionConsoleInteraction object that is assumed
   * to have been already run.
   * @return the initial LDAP context or <CODE>null</CODE> if the user did
   * not accept to trust the certificates.
   * @throws ClientException if there was an error establishing the connection.
   *
   * @param ci
   *          the LDAPConnectionConsoleInteraction object that is assumed to
   *          have been already run.
   * @return the initial LDAP context or <CODE>null</CODE> if the user did not
   *         accept to trust the certificates.
   * @throws ClientException
   *           if there was an error establishing the connection.
   */
  protected InitialLdapContext createInitialLdapContextInteracting(
      LDAPConnectionConsoleInteraction ci) throws ClientException
  {
    return createInitialLdapContextInteracting(ci, isInteractive() &&
        ci.isTrustStoreInMemory());
    return createInitialLdapContextInteracting(ci, isInteractive()
        && ci.isTrustStoreInMemory());
  }
  /**
   * Creates an Initial LDAP Context interacting with the user if the
   * application is interactive.
   * @param ci the LDAPConnectionConsoleInteraction object that is assumed
   * to have been already run.
   * @param promptForCertificate whether we should prompt for the certificate
   * or not.
   * @return the initial LDAP context or <CODE>null</CODE> if the user did
   * not accept to trust the certificates.
   * @throws ClientException if there was an error establishing the connection.
   *
   * @param ci
   *          the LDAPConnectionConsoleInteraction object that is assumed to
   *          have been already run.
   * @param promptForCertificate
   *          whether we should prompt for the certificate or not.
   * @return the initial LDAP context or <CODE>null</CODE> if the user did not
   *         accept to trust the certificates.
   * @throws ClientException
   *           if there was an error establishing the connection.
   */
  protected InitialLdapContext createInitialLdapContextInteracting(
      LDAPConnectionConsoleInteraction ci,
      boolean promptForCertificate) throws ClientException
      LDAPConnectionConsoleInteraction ci, boolean promptForCertificate)
      throws ClientException
  {
    // Interact with the user though the console to get
    // LDAP connection information
@@ -914,9 +1174,10 @@
      {
        try
        {
          ctx = ConnectionUtils.createLdapsContext(ldapsUrl, bindDN,
              bindPassword, ci.getConnectTimeout(), null,
              trustManager, keyManager);
          ctx =
              ConnectionUtils.createLdapsContext(ldapsUrl, bindDN,
                  bindPassword, ci.getConnectTimeout(), null, trustManager,
                  keyManager);
          ctx.reconnect(null);
          break;
        }
@@ -931,11 +1192,10 @@
              if (trustManager instanceof ApplicationTrustManager)
              {
                ApplicationTrustManager appTrustManager =
                  (ApplicationTrustManager)trustManager;
                    (ApplicationTrustManager) trustManager;
                authType = appTrustManager.getLastRefusedAuthType();
              }
              if (ci.checkServerCertificate(oce.getChain(), authType,
                  hostName))
              if (ci.checkServerCertificate(oce.getChain(), authType, hostName))
              {
                // If the certificate is trusted, update the trust manager.
                trustManager = ci.getTrustManager();
@@ -952,33 +1212,32 @@
          }
          if (e.getCause() != null)
          {
            if (!isInteractive() &&
                !ci.isTrustAll())
            if (!isInteractive() && !ci.isTrustAll())
            {
              if (getCertificateRootException(e) != null ||
                (e.getCause() instanceof SSLHandshakeException))
              if (getCertificateRootException(e) != null
                  || (e.getCause() instanceof SSLHandshakeException))
              {
                Message message =
                  ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT_NOT_TRUSTED.get(
                  hostName, String.valueOf(portNumber));
                    ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT_NOT_TRUSTED.get(
                        hostName, String.valueOf(portNumber));
                throw new ClientException(
                  LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message);
                    LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message);
              }
            }
            if (e.getCause() instanceof SSLException)
            {
              Message message =
                ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT_WRONG_PORT.get(
                hostName, String.valueOf(portNumber));
                  ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT_WRONG_PORT.get(
                      hostName, String.valueOf(portNumber));
              throw new ClientException(
                LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message);
                  LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message);
            }
          }
          String hostPort =
            ServerDescriptor.getServerRepresentation(hostName, portNumber);
              ServerDescriptor.getServerRepresentation(hostName, portNumber);
          Message message = Utils.getMessageForException(e, hostPort);
          throw new ClientException(
              LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message);
          throw new ClientException(LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR,
              message);
        }
      }
    }
@@ -989,9 +1248,10 @@
      {
        try
        {
          ctx = ConnectionUtils.createStartTLSContext(ldapUrl, bindDN,
              bindPassword, ConnectionUtils.getDefaultLDAPTimeout(), null,
              trustManager, keyManager, null);
          ctx =
              ConnectionUtils.createStartTLSContext(ldapUrl, bindDN,
                  bindPassword, ConnectionUtils.getDefaultLDAPTimeout(), null,
                  trustManager, keyManager, null);
          ctx.reconnect(null);
          break;
        }
@@ -1006,18 +1266,17 @@
              if (trustManager instanceof ApplicationTrustManager)
              {
                ApplicationTrustManager appTrustManager =
                  (ApplicationTrustManager)trustManager;
                    (ApplicationTrustManager) trustManager;
                authType = appTrustManager.getLastRefusedAuthType();
              }
              if (ci.checkServerCertificate(oce.getChain(), authType,
                  hostName))
              if (ci.checkServerCertificate(oce.getChain(), authType, hostName))
              {
                // If the certificate is trusted, update the trust manager.
                trustManager = ci.getTrustManager();
                // Try to connect again.
                continue ;
                continue;
              }
              else
              {
@@ -1027,16 +1286,18 @@
            }
            else
            {
              Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get(
                  hostName, String.valueOf(portNumber));
              Message message =
                  ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get(hostName, String
                      .valueOf(portNumber));
              throw new ClientException(
                  LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message);
            }
          }
          Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get(
              hostName, String.valueOf(portNumber));
          throw new ClientException(
              LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message);
          Message message =
              ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get(hostName, String
                  .valueOf(portNumber));
          throw new ClientException(LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR,
              message);
        }
      }
    }
@@ -1047,17 +1308,19 @@
      {
        try
        {
          ctx = ConnectionUtils.createLdapContext(ldapUrl, bindDN,
              bindPassword, ConnectionUtils.getDefaultLDAPTimeout(), null);
          ctx =
              ConnectionUtils.createLdapContext(ldapUrl, bindDN, bindPassword,
                  ConnectionUtils.getDefaultLDAPTimeout(), null);
          ctx.reconnect(null);
          break;
        }
        catch (NamingException e)
        {
          Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get(
              hostName, String.valueOf(portNumber));
          throw new ClientException(
              LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message);
          Message message =
              ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get(hostName, String
                  .valueOf(portNumber));
          throw new ClientException(LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR,
              message);
        }
      }
    }
@@ -1067,20 +1330,21 @@
  /**
   * Returns the message to be displayed in the file with the equivalent
   * command-line with information about the current time.
   * @return  the message to be displayed in the file with the equivalent
   * command-line with information about the current time.
   *
   * @return the message to be displayed in the file with the equivalent
   *         command-line with information about the current time.
   */
  protected String getCurrentOperationDateMessage()
  {
    String date = formatDateTimeStringForEquivalentCommand(new Date());
    return INFO_OPERATION_START_TIME_MESSAGE.get(date).
    toString();
    return INFO_OPERATION_START_TIME_MESSAGE.get(date).toString();
  }
  /**
   * Formats a Date to String representation in "dd/MMM/yyyy:HH:mm:ss Z".
   *
   * @param date to format; null if <code>date</code> is null
   * @param date
   *          to format; null if <code>date</code> is null
   * @return string representation of the date
   */
  protected String formatDateTimeStringForEquivalentCommand(Date date)
@@ -1089,7 +1353,7 @@
    if (date != null)
    {
      SimpleDateFormat dateFormat =
        new SimpleDateFormat(DATE_FORMAT_LOCAL_TIME);
          new SimpleDateFormat(DATE_FORMAT_LOCAL_TIME);
      dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
      timeStr = dateFormat.format(date);
    }
@@ -1098,13 +1362,14 @@
  /**
   * Prompts the user to give the Global Administrator UID.
   * @param defaultValue the default value that will be proposed in the prompt
   * message.
   * @param logger the Logger to be used to log the error message.
   *
   * @param defaultValue
   *          the default value that will be proposed in the prompt message.
   * @param logger
   *          the Logger to be used to log the error message.
   * @return the Global Administrator UID as provided by the user.
   */
  protected String askForAdministratorUID(String defaultValue,
      Logger logger)
  protected String askForAdministratorUID(String defaultValue, Logger logger)
  {
    String s = defaultValue;
    try
@@ -1113,14 +1378,16 @@
    }
    catch (CLIException ce)
    {
      logger.log(Level.WARNING, "Error reading input: "+ce, ce);
      logger.log(Level.WARNING, "Error reading input: " + ce, ce);
    }
    return s;
  }
  /**
   * Prompts the user to give the Global Administrator password.
   * @param logger the Logger to be used to log the error message.
   *
   * @param logger
   *          the Logger to be used to log the error message.
   * @return the Global Administrator password as provided by the user.
   */
  protected String askForAdministratorPwd(Logger logger)
@@ -1137,23 +1404,25 @@
      t = t.getCause();
      if (t instanceof OpendsCertificateException)
      {
        oce = (OpendsCertificateException)t;
        oce = (OpendsCertificateException) t;
      }
    }
    return oce;
  }
  /**
   * Commodity method used to repeatidly ask the user to provide an
   * integer value.
   * @param prompt the prompt message.
   * @param defaultValue the default value to be proposed to the user.
   * @param logger the logger where the errors will be written.
   * Commodity method used to repeatidly ask the user to provide an integer
   * value.
   *
   * @param prompt
   *          the prompt message.
   * @param defaultValue
   *          the default value to be proposed to the user.
   * @param logger
   *          the logger where the errors will be written.
   * @return the value provided by the user.
   */
  protected int askInteger(Message prompt, int defaultValue,
      Logger logger)
  protected int askInteger(Message prompt, int defaultValue, Logger logger)
  {
    int newInt = -1;
    while (newInt == -1)
@@ -1165,7 +1434,7 @@
      catch (CLIException ce)
      {
        newInt = -1;
        logger.log(Level.WARNING, "Error reading input: "+ce, ce);
        logger.log(Level.WARNING, "Error reading input: " + ce, ce);
      }
    }
    return newInt;
@@ -1183,7 +1452,7 @@
   *           If the value could not be retrieved for some reason.
   */
  public final int readInteger(Message prompt, final int defaultValue)
  throws CLIException
      throws CLIException
  {
    ValidationCallback<Integer> callback = new ValidationCallback<Integer>()
    {
@@ -1220,12 +1489,13 @@
    };
    if (defaultValue != -1) {
      prompt = INFO_PROMPT_SINGLE_DEFAULT.get(prompt.toString(),
          String.valueOf(defaultValue));
    if (defaultValue != -1)
    {
      prompt =
          INFO_PROMPT_SINGLE_DEFAULT.get(prompt.toString(), String
              .valueOf(defaultValue));
    }
    return readValidatedInput(prompt, callback, CONFIRMATION_MAX_TRIES);
  }
}