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

Violette Roche-Montane
11.36.2014 edb7b32eaa2db4a171496d30ed3293bfa06b30c8
Checkpoint commit for OPENDJ-1303
- added DEFAULT_LDAP_CONNECT_TIMEOUT to constants.
- added ClientException, merge it with CLIException. Replaced CLIException calls. (CLIException will disappear soon)
- Console application : errors,warnings should be displayed in stdout if interactive mode is enabled.
- added unit tests.
- removed closeIfNotNull / replaced it by Utils.closeSilently

1 files added
5 files modified
313 ■■■■ changed files
opendj-cli/src/main/java/com/forgerock/opendj/cli/CliConstants.java 3 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/ClientException.java 108 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/ConsoleApplication.java 131 ●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java 7 ●●●●● patch | view | raw | blame | history
opendj-cli/src/test/java/com/forgerock/opendj/cli/ConsoleApplicationTestCase.java 58 ●●●●● patch | view | raw | blame | history
opendj-cli/src/test/java/com/forgerock/opendj/cli/UtilsTestCase.java 6 ●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/CliConstants.java
@@ -31,6 +31,9 @@
 */
public final class CliConstants {
    /** Default value for LDAP connection timeout. */
    public static final int DEFAULT_LDAP_CONNECT_TIMEOUT = 30000;
    /** Default value for incrementing port number. */
    public static final int PORT_INCREMENT = 1000;
opendj-cli/src/main/java/com/forgerock/opendj/cli/ClientException.java
New file
@@ -0,0 +1,108 @@
/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions Copyright 2014 ForgeRock AS
 */
package com.forgerock.opendj.cli;
import static com.forgerock.opendj.cli.CliMessages.ERR_CONSOLE_INPUT_ERROR;
import org.forgerock.i18n.LocalizableException;
import org.forgerock.i18n.LocalizableMessage;
/**
 * This class defines an exception that may be thrown if a local problem occurs in a Directory Server client.
 */
public class ClientException extends Exception implements LocalizableException {
    /**
     * The serial version identifier required to satisfy the compiler because this class extends
     * <CODE>java.lang.Exception</CODE>, which implements the <CODE>java.io.Serializable</CODE> interface. This value
     * was generated using the <CODE>serialver</CODE> command-line utility included with the Java SDK.
     */
    private static final long serialVersionUID = 1384120263337669664L;
    /** The return code. */
    private ReturnCode returnCode;
    /** The message linked to that exception. */
    private final LocalizableMessage message;
    /**
     * Adapts any exception that may have occurred whilst reading input from the
     * console.
     *
     * @param cause
     *            The exception that occurred whilst reading input from the
     *            console.
     * @return Returns a new CLI exception describing a problem that occurred
     *         whilst reading input from the console.
     */
    public static ClientException adaptInputException(final Throwable cause) {
        return new ClientException(ReturnCode.ERROR_USER_DATA, ERR_CONSOLE_INPUT_ERROR.get(cause.getMessage()), cause);
    }
    /**
     * Creates a new client exception with the provided message.
     *
     * @param exitCode
     *            The exit code that may be used if the client considers this to be a fatal problem.
     * @param message
     *            The message that explains the problem that occurred.
     */
    public ClientException(ReturnCode exitCode, LocalizableMessage message) {
        super(message.toString());
        this.returnCode = exitCode;
        this.message = message;
    }
    /**
     * Creates a new client exception with the provided message and root cause.
     *
     * @param exitCode
     *            The exit code that may be used if the client considers this to be a fatal problem.
     * @param message
     *            The message that explains the problem that occurred.
     * @param cause
     *            The exception that was caught to trigger this exception.
     */
    public ClientException(ReturnCode exitCode, LocalizableMessage message, Throwable cause) {
        super(message.toString(), cause);
        this.returnCode = exitCode;
        this.message = message;
    }
    /**
     * Retrieves the exit code that the client may use if it considers this to be a fatal problem.
     *
     * @return The exit code that the client may use if it considers this to be a fatal problem.
     */
    public int getReturnCode() {
        return returnCode.get();
    }
    @Override
    public LocalizableMessage getMessageObject() {
        return message;
    }
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/ConsoleApplication.java
@@ -34,7 +34,6 @@
import static com.forgerock.opendj.cli.Utils.wrapText;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.Console;
import java.io.EOFException;
import java.io.IOException;
@@ -45,11 +44,9 @@
import org.forgerock.i18n.LocalizableMessage;
/**
 * 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 {
    private final PrintStream err;
    private final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
@@ -57,6 +54,8 @@
    private final PrintStream out;
    private final PrintStream err;
    private final Console console = System.console();
    /**
@@ -80,27 +79,6 @@
    }
    /**
     * Closes the provided {@code Closeable}s if they are not {@code null}.
     *
     * @param closeables
     *          The closeables to be closed.
     */
    public final void closeIfNotNull(Closeable... closeables) {
        if (closeables == null) {
            return;
        }
        for (Closeable closeable : closeables) {
            if (closeable != null) {
                try {
                    closeable.close();
                } catch (Exception ignored) {
                    // Do nothing.
                }
            }
        }
    }
    /**
     * Returns the application error stream.
     *
     * @return The application error stream.
@@ -128,8 +106,8 @@
    }
    /**
     * Indicates whether or not the user has requested interactive behavior. The
     * default implementation returns {@code true}.
     * Indicates whether or not the user has requested interactive behavior. The default implementation returns
     * {@code true}.
     *
     * @return {@code true} if the user has requested interactive behavior.
     */
@@ -138,8 +116,7 @@
    }
    /**
     * Indicates whether or not the user has requested quiet output. The default
     * implementation returns {@code false}.
     * Indicates whether or not the user has requested quiet output. The default implementation returns {@code false}.
     *
     * @return {@code true} if the user has requested quiet output.
     */
@@ -148,8 +125,8 @@
    }
    /**
     * Indicates whether or not the user has requested script-friendly output.
     * The default implementation returns {@code false}.
     * Indicates whether or not the user has requested script-friendly output. The default implementation returns
     * {@code false}.
     *
     * @return {@code true} if the user has requested script-friendly output.
     */
@@ -158,8 +135,7 @@
    }
    /**
     * Indicates whether or not the user has requested verbose output. The
     * default implementation returns {@code false}.
     * Indicates whether or not the user has requested verbose output. The default implementation returns {@code false}.
     *
     * @return {@code true} if the user has requested verbose output.
     */
@@ -168,16 +144,15 @@
    }
    /**
     * 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
     * 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() {
        final LocalizableMessage msg = INFO_MENU_PROMPT_RETURN_TO_CONTINUE.get();
        try {
            readLineOfInput(msg);
        } catch (final CLIException e) {
        } catch (final ClientException e) {
            // Ignore the exception - applications don't care.
        }
    }
@@ -189,14 +164,14 @@
     *            The message.
     */
    public final void errPrint(final LocalizableMessage msg) {
        err.print(wrapText(msg, MAX_LINE_WIDTH));
        getErrStream().print(wrap(msg));
    }
    /**
     * Displays a blank line to the error stream.
     */
    public final void errPrintln() {
        err.println();
        getErrStream().println();
    }
    /**
@@ -206,12 +181,11 @@
     *            The message.
     */
    public final void errPrintln(final LocalizableMessage msg) {
        err.println(wrapText(msg, MAX_LINE_WIDTH));
        getErrStream().println(wrap(msg));
    }
    /**
     * 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.
@@ -219,7 +193,7 @@
     *            The number of columns to indent.
     */
    public final void errPrintln(final LocalizableMessage msg, final int indent) {
        err.println(wrapText(msg, MAX_LINE_WIDTH, indent));
        getErrStream().println(wrapText(msg, MAX_LINE_WIDTH, indent));
    }
    /**
@@ -229,8 +203,8 @@
     *            The verbose message.
     */
    public final void errPrintVerboseMessage(final LocalizableMessage msg) {
        if (isVerbose() || isInteractive()) {
            err.println(wrapText(msg, MAX_LINE_WIDTH));
        if (isVerbose()) {
            getErrStream().println(wrap(msg));
        }
    }
@@ -241,7 +215,7 @@
     *            The message.
     */
    public final void print(final LocalizableMessage msg) {
        out.print(wrapText(msg, MAX_LINE_WIDTH));
        out.print(wrap(msg));
    }
    /**
@@ -258,12 +232,11 @@
     *            The message.
     */
    public final void println(final LocalizableMessage msg) {
        out.println(wrapText(msg, MAX_LINE_WIDTH));
        out.println(wrap(msg));
    }
    /**
     * Displays a message to the output stream indented by the specified number
     * of columns.
     * Displays a message to the output stream indented by the specified number of columns.
     *
     * @param msg
     *            The message.
@@ -282,27 +255,24 @@
     */
    public final void printVerboseMessage(final LocalizableMessage msg) {
        if (isVerbose() || isInteractive()) {
            out.println(wrapText(msg, MAX_LINE_WIDTH));
            out.println(wrap(msg));
        }
    }
    /**
     * 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).
     * 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).
     *
     * @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} if there should not be a
     *            default and the user must explicitly provide a value.
     * @throws CLIException
     *            The default value to assume if the user presses ENTER without typing anything, or {@code null} if
     *            there should not be a default and the user must explicitly provide a value.
     * @throws ClientException
     *             If the line of input could not be retrieved for some reason.
     * @return The string value read from the user.
     */
    public final String readInput(LocalizableMessage prompt, final String defaultValue)
            throws CLIException {
    public final String readInput(LocalizableMessage prompt, final String defaultValue) throws ClientException {
        while (true) {
            if (defaultValue != null) {
                prompt = INFO_PROMPT_SINGLE_DEFAULT.get(prompt.toString(), defaultValue);
@@ -327,13 +297,13 @@
     * @param prompt
     *            The password prompt.
     * @return The password.
     * @throws CLIException
     * @throws ClientException
     *             If the password could not be retrieved for some reason.
     */
    public final char[] readPassword(final LocalizableMessage prompt) throws CLIException {
    public final char[] readPassword(final LocalizableMessage prompt) throws ClientException {
        if (console != null) {
            if (prompt != null) {
                err.print(wrapText(prompt, MAX_LINE_WIDTH));
                err.print(wrap(prompt));
                err.print(" ");
            }
            try {
@@ -343,7 +313,7 @@
                }
                return password;
            } catch (final Throwable e) {
                throw CLIException.adaptInputException(e);
                throw ClientException.adaptInputException(e);
            }
        } else {
            // FIXME: should go direct to char[] and avoid the String.
@@ -357,23 +327,48 @@
     * @param prompt
     *            The prompt.
     * @return The line of input.
     * @throws CLIException
     * @throws ClientException
     *             If the line of input could not be retrieved for some reason.
     */
    private final String readLineOfInput(final LocalizableMessage prompt) throws CLIException {
    private final String readLineOfInput(final LocalizableMessage prompt) throws ClientException {
        if (prompt != null) {
            err.print(wrapText(prompt, MAX_LINE_WIDTH));
            err.print(wrap(prompt));
            err.print(" ");
        }
        try {
            final String s = reader.readLine();
            if (s == null) {
                throw CLIException.adaptInputException(new EOFException("End of input"));
                throw ClientException.adaptInputException(new EOFException("End of input"));
            } else {
                return s;
            }
        } catch (final IOException e) {
            throw CLIException.adaptInputException(e);
            throw ClientException.adaptInputException(e);
        }
    }
    /**
     * Inserts line breaks into the provided buffer to wrap text at no more than the specified column width (80).
     *
     * @param msg
     *            The message to wrap.
     * @return The wrapped message.
     */
    private String wrap(final LocalizableMessage msg) {
        return wrapText(msg, MAX_LINE_WIDTH);
    }
    /**
     * Returns the error stream. Effectively, when an application is in "interactive mode" all the informations should
     * be written in the stdout.
     *
     * @return The error stream that should be used with this application.
     */
    private PrintStream getErrStream() {
        if (isInteractive()) {
            return out;
        } else {
            return err;
        }
    }
opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java
@@ -332,14 +332,15 @@
    /**
     * Checks that the java version.
     *
     * @throws CLIException
     * @throws ClientException
     *             If the java version we are running on is not compatible.
     */
    public static void checkJavaVersion() throws CLIException {
    public static void checkJavaVersion() throws ClientException {
        final String version = System.getProperty("java.specification.version");
        if (!(Float.valueOf(version) >= 1.6)) {
            final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
            throw new CLIException(ERR_INCOMPATIBLE_JAVA_VERSION.get("1.6", version, javaBin), null);
            throw new ClientException(ReturnCode.JAVA_VERSION_INCOMPATIBLE, ERR_INCOMPATIBLE_JAVA_VERSION.get("1.6",
                    version, javaBin), null);
        }
    }
opendj-cli/src/test/java/com/forgerock/opendj/cli/ConsoleApplicationTestCase.java
@@ -33,6 +33,8 @@
import org.testng.annotations.Test;
import static org.fest.assertions.Assertions.assertThat;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
/**
 * Unit tests for the console application class.
@@ -80,8 +82,12 @@
            return interactive;
        }
        public void setVerbose(boolean verbose) {
            this.verbose = verbose;
        public void setVerbose(boolean v) {
            verbose = v;
        }
        public void setInteractive(boolean inter) {
            interactive = inter;
        }
    }
@@ -99,7 +105,7 @@
    public void testWriteLineInErrorStream() throws UnsupportedEncodingException {
        final LocalizableMessage msg = LocalizableMessage.raw("Language is the source of misunderstandings.");
        final MockConsoleApplication ca = MockConsoleApplication.getDefault();
        ca.errPrint(msg);
        ca.errPrintln(msg);
        assertThat(ca.getOut()).isEmpty();
        assertThat(ca.getErr()).contains(msg.toString());
    }
@@ -134,4 +140,50 @@
        assertThat(ca.getOut()).isEmpty();
        assertThat(ca.getErr()).contains(msg.toString());
    }
    /**
     * In non interactive applications, standard messages should be displayed in the stdout(info) and errors to the
     * stderr (warnings, errors).
     *
     * @throws UnsupportedEncodingException
     */
    @Test()
    public void testNonInteractiveApplicationShouldNotStdoutErrors() throws UnsupportedEncodingException {
        final LocalizableMessage msg = LocalizableMessage.raw("Language is the source of misunderstandings.");
        final LocalizableMessage msg2 = LocalizableMessage
                .raw("If somebody wants a sheep, that is a proof that one exists.");
        final MockConsoleApplication ca = MockConsoleApplication.getDefault();
        assertFalse(ca.isInteractive());
        ca.errPrintln(msg);
        assertThat(ca.getOut()).isEmpty();
        assertThat(ca.getErr()).contains(msg.toString());
        ca.println(msg2);
        assertThat(ca.getOut()).contains(msg2.toString());
        assertThat(ca.getErr()).doesNotContain(msg2.toString());
    }
    /**
     * If an application is interactive, all messages should be redirect to the stdout. (info, warnings, errors).
     *
     * @throws UnsupportedEncodingException
     */
    @Test()
    public void testInteractiveApplicationShouldStdoutErrors() throws UnsupportedEncodingException {
        final LocalizableMessage msg = LocalizableMessage.raw("Language is the source of misunderstandings.");
        final LocalizableMessage msg2 = LocalizableMessage
                .raw("If somebody wants a sheep, that is a proof that one exists.");
        final MockConsoleApplication ca = MockConsoleApplication.getDefault();
        assertFalse(ca.isInteractive());
        ca.setInteractive(true);
        assertTrue(ca.isInteractive());
        ca.errPrintln(msg);
        assertThat(ca.getOut()).contains(msg.toString());
        assertThat(ca.getErr()).isEmpty();
        ca.println(msg2);
        assertThat(ca.getOut()).contains(msg2.toString());
        assertThat(ca.getErr()).isEmpty();
    }
}
opendj-cli/src/test/java/com/forgerock/opendj/cli/UtilsTestCase.java
@@ -29,8 +29,8 @@
public class UtilsTestCase extends CliTestCase {
    @Test(expectedExceptions = CLIException.class)
    public void testInvalidJavaVersion() throws CLIException {
    @Test(expectedExceptions = ClientException.class)
    public void testInvalidJavaVersion() throws ClientException {
        final String original = System.getProperty("java.specification.version");
        System.setProperty("java.specification.version", "1.5");
        try {
@@ -41,7 +41,7 @@
    }
    @Test()
    public void testValidJavaVersion() throws CLIException {
    public void testValidJavaVersion() throws ClientException {
        Utils.checkJavaVersion();
    }
}