From 4f50cdb97c5bc421bd1dccada157d45c3bdb5fc1 Mon Sep 17 00:00:00 2001
From: Violette Roche-Montane <violette.roche-montane@forgerock.com>
Date: Wed, 29 Jan 2014 08:25:04 +0000
Subject: [PATCH] OPENDJ-1303 Split out CLI support from opendj-ldap-toolkit into a separate Maven module, "opendj-cli" Part I. Added required classes.

---
 opendj-cli/src/main/java/com/forgerock/opendj/cli/IntegerArgument.java     |  483 ++++
 opendj-cli/src/main/java/com/forgerock/opendj/cli/ConsoleApplication.java  |  328 +++
 opendj-cli/src/main/java/com/forgerock/opendj/cli/package-info.java        |   29 
 opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentGroup.java       |  170 +
 opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentException.java   |   73 
 opendj-cli/src/main/java/com/forgerock/opendj/cli/StringArgument.java      |  136 +
 opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiChoiceArgument.java |  239 ++
 opendj-cli/src/main/java/com/forgerock/opendj/cli/CliConstants.java        |  625 ++++++
 opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java               |  336 +++
 opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java            |  651 ++++++
 opendj-cli/src/main/java/com/forgerock/opendj/cli/FileBasedArgument.java   |  242 ++
 opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java     |  120 +
 opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java  |  483 ++++
 opendj-cli/src/main/java/com/forgerock/opendj/cli/BooleanArgument.java     |  108 +
 opendj-cli/src/main/java/com/forgerock/opendj/cli/CLIException.java        |    2 
 opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties      |  476 ++++
 opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java      | 1596 +++++++++++++++
 opendj-cli/src/main/java/org/forgerock/opendj/cli/package-info.java        |    1 
 18 files changed, 6,095 insertions(+), 3 deletions(-)

diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java
new file mode 100644
index 0000000..a15be6d
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java
@@ -0,0 +1,651 @@
+/*
+ * 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.*;
+import static com.forgerock.opendj.util.StaticUtils.toLowerCase;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.LocalizableMessageBuilder;
+
+/**
+ * This class defines a generic argument that may be used in the argument list
+ * for an application. This is an abstract class that must be subclassed in
+ * order to provide specific functionality.
+ */
+abstract class Argument {
+    // Indicates whether this argument should be hidden in the usage
+    // information.
+    private boolean isHidden;
+
+    // Indicates whether this argument may be specified more than once for
+    // multiple values.
+    private boolean isMultiValued;
+
+    // Indicates whether this argument was provided in the set of
+    // command-line
+    // arguments.
+    private boolean isPresent;
+
+    // Indicates whether this argument is required to have a value.
+    private boolean isRequired;
+
+    // Indicates whether this argument requires a value.
+    private boolean needsValue;
+
+    // The single-character identifier for this argument.
+    private final Character shortIdentifier;
+
+    // The unique ID of the description for this argument.
+    private final LocalizableMessage description;
+
+    // The set of values for this argument.
+    private final LinkedList<String> values;
+
+    // The default value for the argument if none other is provided.
+    private String defaultValue;
+
+    // The long identifier for this argument.
+    private final String longIdentifier;
+
+    // The generic name that will be used to refer to this argument.
+    private final String name;
+
+    // The name of the property that can be used to set the default value.
+    private String propertyName;
+
+    // The value placeholder for this argument, which will be used in
+    // usage
+    // information.
+    private LocalizableMessage valuePlaceholder;
+
+    // Indicates whether this argument was provided in the set of
+    // properties
+    // found is a properties file.
+    private boolean isValueSetByProperty;
+
+    /**
+     * Creates a new argument with the provided information.
+     *
+     * @param name
+     *            The generic name that should be used to refer to this
+     *            argument.
+     * @param shortIdentifier
+     *            The single-character identifier for this argument, or
+     *            <CODE>null</CODE> if there is none.
+     * @param longIdentifier
+     *            The long identifier for this argument, or <CODE>null</CODE> if
+     *            there is none.
+     * @param isRequired
+     *            Indicates whether this argument must be specified on the
+     *            command line.
+     * @param isMultiValued
+     *            Indicates whether this argument may be specified more than
+     *            once to provide multiple values.
+     * @param needsValue
+     *            Indicates whether this argument requires a value.
+     * @param valuePlaceholder
+     *            The placeholder for the argument value that will be displayed
+     *            in usage information, or <CODE>null</CODE> if this argument
+     *            does not require a value.
+     * @param defaultValue
+     *            The default value that should be used for this argument if
+     *            none is provided in a properties file or on the command line.
+     *            This may be <CODE>null</CODE> if there is no generic default.
+     * @param propertyName
+     *            The name of the property in a property file that may be used
+     *            to override the default value but will be overridden by a
+     *            command-line argument.
+     * @param description
+     *            LocalizableMessage for the description of this argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to
+     *             create this argument.
+     */
+    protected Argument(final String name, final Character shortIdentifier,
+            final String longIdentifier, final boolean isRequired, final boolean isMultiValued,
+            final boolean needsValue, final LocalizableMessage valuePlaceholder,
+            final String defaultValue, final String propertyName,
+            final LocalizableMessage description) throws ArgumentException {
+        this.name = name;
+        this.shortIdentifier = shortIdentifier;
+        this.longIdentifier = longIdentifier;
+        this.isRequired = isRequired;
+        this.isMultiValued = isMultiValued;
+        this.needsValue = needsValue;
+        this.valuePlaceholder = valuePlaceholder;
+        this.defaultValue = defaultValue;
+        this.propertyName = propertyName;
+        this.description = description;
+        this.isValueSetByProperty = false;
+
+        if ((shortIdentifier == null) && (longIdentifier == null)) {
+            final LocalizableMessage message = ERR_ARG_NO_IDENTIFIER.get(name);
+            throw new ArgumentException(message);
+        }
+
+        if (needsValue && (valuePlaceholder == null)) {
+            final LocalizableMessage message = ERR_ARG_NO_VALUE_PLACEHOLDER.get(name);
+            throw new ArgumentException(message);
+        }
+
+        values = new LinkedList<String>();
+        isPresent = false;
+        isHidden = false;
+    }
+
+    /**
+     * Adds a value to the set of values for this argument. This should only be
+     * called if the value is allowed by the <CODE>valueIsAcceptable</CODE>
+     * method.
+     *
+     * @param valueString
+     *            The string representation of the value to add to this
+     *            argument.
+     */
+    public void addValue(final String valueString) {
+        values.add(valueString);
+    }
+
+    /**
+     * Clears the set of values assigned to this argument.
+     */
+    public void clearValues() {
+        values.clear();
+    }
+
+    /**
+     * Retrieves the value of this argument as a <CODE>Boolean</CODE>.
+     *
+     * @return The value of this argument as a <CODE>Boolean</CODE>.
+     * @throws ArgumentException
+     *             If this argument cannot be interpreted as a Boolean value.
+     */
+    public boolean getBooleanValue() throws ArgumentException {
+        if (values.isEmpty()) {
+            final LocalizableMessage message = ERR_ARG_NO_BOOLEAN_VALUE.get(name);
+            throw new ArgumentException(message);
+        }
+
+        final Iterator<String> iterator = values.iterator();
+        final String valueString = toLowerCase(iterator.next());
+
+        boolean booleanValue;
+        if (valueString.equals("true") || valueString.equals("yes") || valueString.equals("on")
+                || valueString.equals("1")) {
+            booleanValue = true;
+        } else if (valueString.equals("false") || valueString.equals("no")
+                || valueString.equals("off") || valueString.equals("0")) {
+            booleanValue = false;
+        } else {
+            final LocalizableMessage message =
+                    ERR_ARG_CANNOT_DECODE_AS_BOOLEAN.get(valueString, name);
+            throw new ArgumentException(message);
+        }
+
+        if (iterator.hasNext()) {
+            final LocalizableMessage message = ERR_ARG_BOOLEAN_MULTIPLE_VALUES.get(name);
+            throw new ArgumentException(message);
+        } else {
+            return booleanValue;
+        }
+    }
+
+    /**
+     * Retrieves the default value that will be used for this argument if it is
+     * not specified on the command line and it is not set from a properties
+     * file.
+     *
+     * @return The default value that will be used for this argument if it is
+     *         not specified on the command line and it is not set from a
+     *         properties file, or <CODE>null</CODE> if there is no default
+     *         value.
+     */
+    public String getDefaultValue() {
+        return defaultValue;
+    }
+
+    /**
+     * Retrieves the human-readable description for this argument.
+     *
+     * @return The human-readable description for this argument.
+     */
+    public LocalizableMessage getDescription() {
+        return description != null ? description : LocalizableMessage.EMPTY;
+    }
+
+    /**
+     * Retrieves the value of this argument as an integer.
+     *
+     * @return The value of this argument as an integer.
+     * @throws ArgumentException
+     *             If there are multiple values, or the value cannot be parsed
+     *             as an integer.
+     */
+    public double getDoubleValue() throws ArgumentException {
+        if (values.isEmpty()) {
+            final LocalizableMessage message = ERR_ARG_NO_INT_VALUE.get(name);
+            throw new ArgumentException(message);
+        }
+
+        final Iterator<String> iterator = values.iterator();
+        final String valueString = iterator.next();
+
+        double intValue;
+        try {
+            intValue = Double.parseDouble(valueString);
+        } catch (final Exception e) {
+            final LocalizableMessage message = ERR_ARG_CANNOT_DECODE_AS_INT.get(valueString, name);
+            throw new ArgumentException(message, e);
+        }
+
+        if (iterator.hasNext()) {
+            final LocalizableMessage message = ERR_ARG_INT_MULTIPLE_VALUES.get(name);
+            throw new ArgumentException(message);
+        } else {
+            return intValue;
+        }
+    }
+
+    /**
+     * Retrieves the set of values for this argument as a list of integers.
+     *
+     * @return A list of the integer representations of the values for this
+     *         argument.
+     * @throws ArgumentException
+     *             If any of the values cannot be parsed as an integer.
+     */
+    public LinkedList<Double> getDoubleValues() throws ArgumentException {
+        final LinkedList<Double> intList = new LinkedList<Double>();
+
+        final Iterator<String> iterator = values.iterator();
+        while (iterator.hasNext()) {
+            final String valueString = iterator.next();
+
+            try {
+                intList.add(Double.valueOf(valueString));
+            } catch (final Exception e) {
+                final LocalizableMessage message =
+                        ERR_ARG_CANNOT_DECODE_AS_INT.get(valueString, name);
+                throw new ArgumentException(message, e);
+            }
+        }
+
+        return intList;
+    }
+
+    /**
+     * Retrieves the value of this argument as an integer.
+     *
+     * @return The value of this argument as an integer.
+     * @throws ArgumentException
+     *             If there are multiple values, or the value cannot be parsed
+     *             as an integer.
+     */
+    public int getIntValue() throws ArgumentException {
+        if (values.isEmpty()) {
+            final LocalizableMessage message = ERR_ARG_NO_INT_VALUE.get(name);
+            throw new ArgumentException(message);
+        }
+
+        final Iterator<String> iterator = values.iterator();
+        final String valueString = iterator.next();
+
+        int intValue;
+        try {
+            intValue = Integer.parseInt(valueString);
+        } catch (final Exception e) {
+            final LocalizableMessage message = ERR_ARG_CANNOT_DECODE_AS_INT.get(valueString, name);
+            throw new ArgumentException(message, e);
+        }
+
+        if (iterator.hasNext()) {
+            final LocalizableMessage message = ERR_ARG_INT_MULTIPLE_VALUES.get(name);
+            throw new ArgumentException(message);
+        } else {
+            return intValue;
+        }
+    }
+
+    /**
+     * Retrieves the set of values for this argument as a list of integers.
+     *
+     * @return A list of the integer representations of the values for this
+     *         argument.
+     * @throws ArgumentException
+     *             If any of the values cannot be parsed as an integer.
+     */
+    public LinkedList<Integer> getIntValues() throws ArgumentException {
+        final LinkedList<Integer> intList = new LinkedList<Integer>();
+
+        final Iterator<String> iterator = values.iterator();
+        while (iterator.hasNext()) {
+            final String valueString = iterator.next();
+
+            try {
+                intList.add(Integer.valueOf(valueString));
+            } catch (final Exception e) {
+                final LocalizableMessage message =
+                        ERR_ARG_CANNOT_DECODE_AS_INT.get(valueString, name);
+                throw new ArgumentException(message, e);
+            }
+        }
+
+        return intList;
+    }
+
+    /**
+     * Retrieves the long (multi-character) identifier that may be used to
+     * specify the value of this argument.
+     *
+     * @return The long (multi-character) identifier that may be used to specify
+     *         the value of this argument.
+     */
+    public String getLongIdentifier() {
+        return longIdentifier;
+    }
+
+    /**
+     * Retrieves the generic name that will be used to refer to this argument.
+     *
+     * @return The generic name that will be used to refer to this argument.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Retrieves the name of a property in a properties file that may be used to
+     * set the default value for this argument if it is present. A value read
+     * from a properties file will override the default value returned from the
+     * <CODE>getDefaultValue</CODE>, but the properties file value will be
+     * overridden by a value supplied on the command line.
+     *
+     * @return The name of a property in a properties file that may be used to
+     *         set the default value for this argument if it is present.
+     */
+    public String getPropertyName() {
+        return propertyName;
+    }
+
+    /**
+     * Retrieves the single-character identifier that may be used to specify the
+     * value of this argument.
+     *
+     * @return The single-character identifier that may be used to specify the
+     *         value of this argument, or <CODE>null</CODE> if there is none.
+     */
+    public Character getShortIdentifier() {
+        return shortIdentifier;
+    }
+
+    /**
+     * Retrieves the string vale for this argument. If it has multiple values,
+     * then the first will be returned. If it does not have any values, then the
+     * default value will be returned.
+     *
+     * @return The string value for this argument, or <CODE>null</CODE> if there
+     *         are no values and no default value has been given.
+     */
+    public String getValue() {
+        if (values.isEmpty()) {
+            return defaultValue;
+        }
+
+        return values.getFirst();
+    }
+
+    /**
+     * Retrieves the value placeholder that will be displayed for this argument
+     * in the generated usage information.
+     *
+     * @return The value placeholder that will be displayed for this argument in
+     *         the generated usage information, or <CODE>null</CODE> if there is
+     *         none.
+     */
+    public LocalizableMessage getValuePlaceholder() {
+        return valuePlaceholder;
+    }
+
+    /**
+     * Retrieves the set of string values for this argument.
+     *
+     * @return The set of string values for this argument.
+     */
+    public LinkedList<String> getValues() {
+        return values;
+    }
+
+    /**
+     * Indicates whether this argument has at least one value.
+     *
+     * @return <CODE>true</CODE> if this argument has at least one value, or
+     *         <CODE>false</CODE> if it does not have any values.
+     */
+    public boolean hasValue() {
+        return (!values.isEmpty());
+    }
+
+    /**
+     * Indicates whether this argument should be hidden from the usage
+     * information.
+     *
+     * @return <CODE>true</CODE> if this argument should be hidden from the
+     *         usage information, or <CODE>false</CODE> if not.
+     */
+    public boolean isHidden() {
+        return isHidden;
+    }
+
+    /**
+     * Indicates whether this argument may be provided more than once on the
+     * command line to specify multiple values.
+     *
+     * @return <CODE>true</CODE> if this argument may be provided more than once
+     *         on the command line to specify multiple values, or
+     *         <CODE>false</CODE> if it may have at most one value.
+     */
+    public boolean isMultiValued() {
+        return isMultiValued;
+    }
+
+    /**
+     * Indicates whether this argument is present in the parsed set of
+     * command-line arguments.
+     *
+     * @return <CODE>true</CODE> if this argument is present in the parsed set
+     *         of command-line arguments, or <CODE>false</CODE> if not.
+     */
+    public boolean isPresent() {
+        return isPresent;
+    }
+
+    /**
+     * Indicates whether this argument is required to have at least one value.
+     *
+     * @return <CODE>true</CODE> if this argument is required to have at least
+     *         one value, or <CODE>false</CODE> if it does not need to have a
+     *         value.
+     */
+    public boolean isRequired() {
+        return isRequired;
+    }
+
+    /**
+     * Indicates whether this argument was provided in the set of properties
+     * found is a properties file.
+     *
+     * @return <CODE>true</CODE> if this argument was provided in the set of
+     *         properties found is a properties file, or <CODE>false</CODE> if
+     *         not.
+     */
+    public boolean isValueSetByProperty() {
+        return isValueSetByProperty;
+    }
+
+    /**
+     * Indicates whether a value must be provided with this argument if it is
+     * present.
+     *
+     * @return <CODE>true</CODE> if a value must be provided with the argument
+     *         if it is present, or <CODE>false</CODE> if the argument does not
+     *         take a value and the presence of the argument identifier itself
+     *         is sufficient to convey the necessary information.
+     */
+    public boolean needsValue() {
+        return needsValue;
+    }
+
+    /**
+     * Specifies the default value that will be used for this argument if it is
+     * not specified on the command line and it is not set from a properties
+     * file.
+     *
+     * @param defaultValue
+     *            The default value that will be used for this argument if it is
+     *            not specified on the command line and it is not set from a
+     *            properties file.
+     */
+    public void setDefaultValue(final String defaultValue) {
+        this.defaultValue = defaultValue;
+    }
+
+    /**
+     * Specifies whether this argument should be hidden from the usage
+     * information.
+     *
+     * @param isHidden
+     *            Indicates whether this argument should be hidden from the
+     *            usage information.
+     */
+    public void setHidden(final boolean isHidden) {
+        this.isHidden = isHidden;
+    }
+
+    /**
+     * Specifies whether this argument may be provided more than once on the
+     * command line to specify multiple values.
+     *
+     * @param isMultiValued
+     *            Indicates whether this argument may be provided more than once
+     *            on the command line to specify multiple values.
+     */
+    public void setMultiValued(final boolean isMultiValued) {
+        this.isMultiValued = isMultiValued;
+    }
+
+    /**
+     * Specifies whether a value must be provided with this argument if it is
+     * present. If this is changed from <CODE>false</CODE> to <CODE>true</CODE>,
+     * then a value placeholder must also be provided.
+     *
+     * @param needsValue
+     *            Indicates whether a value must be provided with this argument
+     *            if it is present.
+     */
+    public void setNeedsValue(final boolean needsValue) {
+        this.needsValue = needsValue;
+    }
+
+    /**
+     * Specifies whether this argument is present in the parsed set of
+     * command-line arguments.
+     *
+     * @param isPresent
+     *            Indicates whether this argument is present in the set of
+     *            command-line arguments.
+     */
+    public void setPresent(final boolean isPresent) {
+        this.isPresent = isPresent;
+    }
+
+    /**
+     * Specifies the name of a property in a properties file that may be used to
+     * set the default value for this argument if it is present.
+     *
+     * @param propertyName
+     *            The name of a property in a properties file that may be used
+     *            to set the default value for this argument if it is present.
+     */
+    public void setPropertyName(final String propertyName) {
+        this.propertyName = propertyName;
+    }
+
+    /**
+     * Specifies whether this argument is required to have at least one value.
+     *
+     * @param isRequired
+     *            Indicates whether this argument is required to have at least
+     *            one value.
+     */
+    public void setRequired(final boolean isRequired) {
+        this.isRequired = isRequired;
+    }
+
+    /**
+     * Specifies the value placeholder that will be displayed for this argument
+     * in the generated usage information. It may be <CODE>null</CODE> only if
+     * <CODE>needsValue()</CODE> returns <CODE>false</CODE>.
+     *
+     * @param valuePlaceholder
+     *            The value placeholder that will be displayed for this argument
+     *            in the generated usage information.
+     */
+    public void setValuePlaceholder(final LocalizableMessage valuePlaceholder) {
+        this.valuePlaceholder = valuePlaceholder;
+    }
+
+    /**
+     * Specifies whether this argument was provided in the set of properties
+     * found is a properties file.
+     *
+     * @param isValueSetByProperty
+     *            Specify whether this argument was provided in the set of
+     *            properties found is a properties file.
+     */
+    public void setValueSetByProperty(final boolean isValueSetByProperty) {
+        this.isValueSetByProperty = isValueSetByProperty;
+    }
+
+    /**
+     * Indicates whether the provided value is acceptable for use in this
+     * argument.
+     *
+     * @param valueString
+     *            The value for which to make the determination.
+     * @param invalidReason
+     *            A buffer into which the invalid reason may be written if the
+     *            value is not acceptable.
+     * @return <CODE>true</CODE> if the value is acceptable, or
+     *         <CODE>false</CODE> if it is not.
+     */
+    public abstract boolean valueIsAcceptable(String valueString,
+            LocalizableMessageBuilder invalidReason);
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentException.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentException.java
new file mode 100644
index 0000000..61ba1f8
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentException.java
@@ -0,0 +1,73 @@
+/*
+ * 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 org.forgerock.i18n.LocalizableException;
+import org.forgerock.i18n.LocalizableMessage;
+
+/**
+ * This class defines an exception that may be thrown if there is a problem with
+ * an argument definition.
+ */
+@SuppressWarnings("serial")
+public final class ArgumentException extends Exception implements LocalizableException {
+    // The I18N message associated with this exception.
+    private final LocalizableMessage message;
+
+    /**
+     * Creates a new argument exception with the provided message.
+     *
+     * @param message
+     *            The message that explains the problem that occurred.
+     */
+    public ArgumentException(final LocalizableMessage message) {
+        super(String.valueOf(message));
+        this.message = message;
+    }
+
+    /**
+     * Creates a new argument exception with the provided message and root
+     * cause.
+     *
+     * @param message
+     *            The message that explains the problem that occurred.
+     * @param cause
+     *            The exception that was caught to trigger this exception.
+     */
+    public ArgumentException(final LocalizableMessage message, final Throwable cause) {
+        super(String.valueOf(message), cause);
+        this.message = message;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public LocalizableMessage getMessageObject() {
+        return this.message;
+    }
+
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentGroup.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentGroup.java
new file mode 100644
index 0000000..d3d8c7c
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentGroup.java
@@ -0,0 +1,170 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ *      Portions copyright 2012-2014 ForgeRock AS.
+ */
+package com.forgerock.opendj.cli;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.forgerock.i18n.LocalizableMessage;
+
+/**
+ * Class for organizing options into logical groups when argument usage is
+ * printed. To use an argument group, create an instance and use
+ * {@code ArgumentParser#addArgument(Argument, ArgumentGroup)}
+ * when adding arguments for to the parser.
+ */
+final class ArgumentGroup implements Comparable<ArgumentGroup> {
+
+    // Description for this group of arguments
+    private LocalizableMessage description = null;
+
+    // List of arguments belonging to this group
+    private List<Argument> args = null;
+
+    // Governs groups position within usage statement
+    private final Integer priority;
+
+    /**
+     * Creates a parameterized instance.
+     *
+     * @param description
+     *            for options in this group that is printed before argument
+     *            descriptions in usage output
+     * @param priority
+     *            number governing the position of this group within the usage
+     *            statement. Groups with higher priority values appear before
+     *            groups with lower priority.
+     */
+    ArgumentGroup(final LocalizableMessage description, final int priority) {
+        this.description = description;
+        this.priority = priority;
+        this.args = new LinkedList<Argument>();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int compareTo(final ArgumentGroup o) {
+        // Groups with higher priority numbers appear before
+        // those with lower priority in the usage output
+        return -1 * priority.compareTo(o.priority);
+    }
+
+    /**
+     * Adds an argument to this group.
+     *
+     * @param arg
+     *            to add
+     * @return boolean where true indicates the add was successful
+     */
+    boolean addArgument(final Argument arg) {
+        boolean success = false;
+        if (arg != null) {
+            final Character newShort = arg.getShortIdentifier();
+            final String newLong = arg.getLongIdentifier();
+
+            // See if there is already an argument in this group that the
+            // new argument should replace
+            for (final Iterator<Argument> it = this.args.iterator(); it.hasNext();) {
+                final Argument a = it.next();
+                if (newShort != null && newShort.equals(a.getShortIdentifier()) || newLong != null
+                        && newLong.equals(a.getLongIdentifier())) {
+                    it.remove();
+                    break;
+                }
+            }
+
+            success = this.args.add(arg);
+        }
+        return success;
+    }
+
+    /**
+     * Indicates whether this group contains any members.
+     *
+     * @return boolean where true means this group contains members
+     */
+    boolean containsArguments() {
+        return this.args.size() > 0;
+    }
+
+    /**
+     * Indicates whether this group contains any non-hidden members.
+     *
+     * @return boolean where true means this group contains non-hidden members
+     */
+    boolean containsNonHiddenArguments() {
+        for (final Argument arg : args) {
+            if (!arg.isHidden()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Gets the list of arguments associated with this group.
+     *
+     * @return list of associated arguments
+     */
+    List<Argument> getArguments() {
+        return Collections.unmodifiableList(args);
+    }
+
+    /**
+     * Gets the description for this group of arguments.
+     *
+     * @return description for this argument group
+     */
+    LocalizableMessage getDescription() {
+        return this.description;
+    }
+
+    /**
+     * Removes an argument from this group.
+     *
+     * @param arg
+     *            to remove
+     * @return boolean where true indicates the remove was successful
+     */
+    boolean removeArgument(final Argument arg) {
+        return this.args.remove(arg);
+    }
+
+    /**
+     * Sets the description for this group of arguments.
+     *
+     * @param description
+     *            for this argument group
+     */
+    void setDescription(final LocalizableMessage description) {
+        this.description = description;
+    }
+
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java
new file mode 100644
index 0000000..0b1a392
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java
@@ -0,0 +1,1596 @@
+/*
+ * 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-2010 Sun Microsystems, Inc.
+ *      Portions copyright 2012-2014 ForgeRock AS.
+ */
+package com.forgerock.opendj.cli;
+
+import static com.forgerock.opendj.cli.CliMessages.*;
+import static com.forgerock.opendj.cli.CliConstants.*;
+import static com.forgerock.opendj.cli.Utils.PROPERTY_SCRIPT_NAME;
+import static com.forgerock.opendj.cli.Utils.wrapText;
+import static com.forgerock.opendj.util.StaticUtils.EOL;
+import static com.forgerock.opendj.util.StaticUtils.getBytes;
+import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage;
+import static com.forgerock.opendj.util.StaticUtils.toLowerCase;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Properties;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.LocalizableMessageBuilder;
+
+/**
+ * This class defines a utility that can be used to deal with command-line
+ * arguments for applications in a CLIP-compliant manner using either short
+ * one-character or longer word-based arguments. It is also integrated with the
+ * Directory Server message catalog so that it can display messages in an
+ * internationalizable format, can automatically generate usage information,
+ * can detect conflicts between arguments, and can interact with a properties
+ * file to obtain default values for arguments there if they are not specified
+ * on the command-line.
+ */
+public final class ArgumentParser {
+    /**
+     * The argument that will be used to indicate the file properties.
+     */
+    private StringArgument filePropertiesPathArgument;
+
+    /**
+     * The argument that will be used to indicate that we'll not look for
+     * default properties file.
+     */
+    private BooleanArgument noPropertiesFileArgument;
+
+    // The argument that will be used to trigger the display of usage
+    // information.
+    private Argument usageArgument;
+
+    // The argument that will be used to trigger the display of the OpenDJ
+    // version.
+    private Argument versionArgument;
+
+    // The set of unnamed trailing arguments that were provided for this
+    // parser.
+    private final ArrayList<String> trailingArguments;
+
+    // Indicates whether this parser will allow additional unnamed
+    // arguments at the end of the list.
+    private final boolean allowsTrailingArguments;
+
+    // Indicates whether long arguments should be treated in a
+    // case-sensitive manner.
+    private final boolean longArgumentsCaseSensitive;
+
+    // Indicates whether the usage or version information has been
+    // displayed.
+    private boolean usageOrVersionDisplayed;
+
+    // Indicates whether the version argument was provided.
+    private boolean versionPresent;
+
+    // The set of arguments defined for this parser, referenced by short
+    // ID.
+    private final HashMap<Character, Argument> shortIDMap;
+
+    // The set of arguments defined for this parser, referenced by
+    // argument name.
+    private final HashMap<String, Argument> argumentMap;
+
+    // The set of arguments defined for this parser, referenced by long
+    // ID.
+    private final HashMap<String, Argument> longIDMap;
+
+    // The maximum number of unnamed trailing arguments that may be
+    // provided.
+    private final int maxTrailingArguments;
+
+    // The minimum number of unnamed trailing arguments that may be
+    // provided.
+    private final int minTrailingArguments;
+
+    // The total set of arguments defined for this parser.
+    private final LinkedList<Argument> argumentList;
+
+    // The output stream to which usage information should be printed.
+    private OutputStream usageOutputStream;
+
+    // The fully-qualified name of the Java class that should be invoked
+    // to launch the program with which this argument parser is associated.
+    private final String mainClassName;
+
+    // A human-readable description for the tool, which will be included
+    // when displaying usage information.
+    private final LocalizableMessage toolDescription;
+
+    // The display name that will be used for the trailing arguments in
+    // the usage information.
+    private final String trailingArgsDisplayName;
+
+    // The raw set of command-line arguments that were provided.
+    private String[] rawArguments;
+
+    /** Set of argument groups. */
+    private Set<ArgumentGroup> argumentGroups;
+
+    /**
+     * Group for arguments that have not been explicitly grouped. These will
+     * appear at the top of the usage statement without a header.
+     */
+    private final ArgumentGroup defaultArgGroup = new ArgumentGroup(LocalizableMessage.EMPTY,
+            Integer.MAX_VALUE);
+
+    /**
+     * Group for arguments that are related to connection through LDAP. This
+     * includes options like the bind DN, the port, etc.
+     */
+    private final ArgumentGroup ldapArgGroup = new ArgumentGroup(
+            INFO_DESCRIPTION_LDAP_CONNECTION_ARGS.get(), Integer.MIN_VALUE + 2);
+
+    /**
+     * Group for arguments that are related to utility input/output like
+     * properties file, no-prompt etc. These will appear toward the bottom of
+     * the usage statement.
+     */
+    private final ArgumentGroup ioArgGroup = new ArgumentGroup(INFO_DESCRIPTION_IO_ARGS.get(),
+            Integer.MIN_VALUE + 1);
+
+    /**
+     * Group for arguments that are general like help, version etc. These will
+     * appear at the end of the usage statement.
+     */
+    private final ArgumentGroup generalArgGroup = new ArgumentGroup(INFO_DESCRIPTION_GENERAL_ARGS
+            .get(), Integer.MIN_VALUE);
+
+    private final static String INDENT = "    ";
+
+    private final static int MAX_LENGTH = 80;
+
+    /**
+     * Creates a new instance of this argument parser with no arguments. Unnamed
+     * trailing arguments will not be allowed.
+     *
+     * @param mainClassName
+     *            The fully-qualified name of the Java class that should be
+     *            invoked to launch the program with which this argument parser
+     *            is associated.
+     * @param toolDescription
+     *            A human-readable description for the tool, which will be
+     *            included when displaying usage information.
+     * @param longArgumentsCaseSensitive
+     *            Indicates whether long arguments should be treated in a
+     *            case-sensitive manner.
+     */
+    public ArgumentParser(final String mainClassName, final LocalizableMessage toolDescription,
+            final boolean longArgumentsCaseSensitive) {
+        this.mainClassName = mainClassName;
+        this.toolDescription = toolDescription;
+        this.longArgumentsCaseSensitive = longArgumentsCaseSensitive;
+
+        argumentList = new LinkedList<Argument>();
+        argumentMap = new HashMap<String, Argument>();
+        shortIDMap = new HashMap<Character, Argument>();
+        longIDMap = new HashMap<String, Argument>();
+        allowsTrailingArguments = false;
+        usageOrVersionDisplayed = false;
+        versionPresent = false;
+        trailingArgsDisplayName = null;
+        maxTrailingArguments = 0;
+        minTrailingArguments = 0;
+        trailingArguments = new ArrayList<String>();
+        rawArguments = null;
+        usageArgument = null;
+        filePropertiesPathArgument = null;
+        noPropertiesFileArgument = null;
+        usageOutputStream = System.out;
+        initGroups();
+    }
+
+    /**
+     * Creates a new instance of this argument parser with no arguments that may
+     * or may not be allowed to have unnamed trailing arguments.
+     *
+     * @param mainClassName
+     *            The fully-qualified name of the Java class that should be
+     *            invoked to launch the program with which this argument parser
+     *            is associated.
+     * @param toolDescription
+     *            A human-readable description for the tool, which will be
+     *            included when displaying usage information.
+     * @param longArgumentsCaseSensitive
+     *            Indicates whether long arguments should be treated in a
+     *            case-sensitive manner.
+     * @param allowsTrailingArguments
+     *            Indicates whether this parser allows unnamed trailing
+     *            arguments to be provided.
+     * @param minTrailingArguments
+     *            The minimum number of unnamed trailing arguments that must be
+     *            provided. A value less than or equal to zero indicates that no
+     *            minimum will be enforced.
+     * @param maxTrailingArguments
+     *            The maximum number of unnamed trailing arguments that may be
+     *            provided. A value less than or equal to zero indicates that no
+     *            maximum will be enforced.
+     * @param trailingArgsDisplayName
+     *            The display name that should be used as a placeholder for
+     *            unnamed trailing arguments in the generated usage information.
+     */
+    public ArgumentParser(final String mainClassName, final LocalizableMessage toolDescription,
+            final boolean longArgumentsCaseSensitive, final boolean allowsTrailingArguments,
+            final int minTrailingArguments, final int maxTrailingArguments,
+            final String trailingArgsDisplayName) {
+        this.mainClassName = mainClassName;
+        this.toolDescription = toolDescription;
+        this.longArgumentsCaseSensitive = longArgumentsCaseSensitive;
+        this.allowsTrailingArguments = allowsTrailingArguments;
+        this.minTrailingArguments = minTrailingArguments;
+        this.maxTrailingArguments = maxTrailingArguments;
+        this.trailingArgsDisplayName = trailingArgsDisplayName;
+
+        argumentList = new LinkedList<Argument>();
+        argumentMap = new HashMap<String, Argument>();
+        shortIDMap = new HashMap<Character, Argument>();
+        longIDMap = new HashMap<String, Argument>();
+        trailingArguments = new ArrayList<String>();
+        usageOrVersionDisplayed = false;
+        versionPresent = false;
+        rawArguments = null;
+        usageArgument = null;
+        usageOutputStream = System.out;
+        initGroups();
+    }
+
+    /**
+     * Adds the provided argument to the set of arguments handled by this
+     * parser.
+     *
+     * @param argument
+     *            The argument to be added.
+     * @throws ArgumentException
+     *             If the provided argument conflicts with another argument that
+     *             has already been defined.
+     */
+    public void addArgument(final Argument argument) throws ArgumentException {
+        addArgument(argument, null);
+    }
+
+    /**
+     * Adds the provided argument to the set of arguments handled by this
+     * parser.
+     *
+     * @param argument
+     *            The argument to be added.
+     * @param group
+     *            The argument group to which the argument belongs.
+     * @throws ArgumentException
+     *             If the provided argument conflicts with another argument that
+     *             has already been defined.
+     */
+    public void addArgument(final Argument argument, ArgumentGroup group) throws ArgumentException {
+
+        final Character shortID = argument.getShortIdentifier();
+        if ((shortID != null) && shortIDMap.containsKey(shortID)) {
+            final String conflictingName = shortIDMap.get(shortID).getName();
+
+            final LocalizableMessage message =
+                    ERR_ARGPARSER_DUPLICATE_SHORT_ID.get(argument.getName(), String
+                            .valueOf(shortID), conflictingName);
+            throw new ArgumentException(message);
+        }
+
+        if (versionArgument != null) {
+            if (shortID != null && shortID.equals(versionArgument.getShortIdentifier())) {
+                // Update the version argument to not display its short
+                // identifier.
+                try {
+                    versionArgument =
+                            new BooleanArgument(OPTION_LONG_PRODUCT_VERSION, null,
+                                    OPTION_LONG_PRODUCT_VERSION, INFO_DESCRIPTION_PRODUCT_VERSION
+                                            .get());
+                    this.generalArgGroup.addArgument(versionArgument);
+                } catch (final ArgumentException e) {
+                    // ignore
+                }
+            }
+        }
+
+        String longID = argument.getLongIdentifier();
+        if (longID != null) {
+            if (!longArgumentsCaseSensitive) {
+                longID = toLowerCase(longID);
+            }
+            if (longIDMap.containsKey(longID)) {
+                final String conflictingName = longIDMap.get(longID).getName();
+
+                final LocalizableMessage message =
+                        ERR_ARGPARSER_DUPLICATE_LONG_ID.get(argument.getName(), argument
+                                .getLongIdentifier(), conflictingName);
+                throw new ArgumentException(message);
+            }
+        }
+
+        if (shortID != null) {
+            shortIDMap.put(shortID, argument);
+        }
+
+        if (longID != null) {
+            longIDMap.put(longID, argument);
+        }
+
+        argumentList.add(argument);
+
+        if (group == null) {
+            group = getStandardGroup(argument);
+        }
+        group.addArgument(argument);
+        argumentGroups.add(group);
+    }
+
+    /**
+     * Adds the provided argument to the set of arguments handled by this parser
+     * and puts the arguement in the default group.
+     *
+     * @param argument
+     *            The argument to be added.
+     * @throws ArgumentException
+     *             If the provided argument conflicts with another argument that
+     *             has already been defined.
+     */
+    void addDefaultArgument(final Argument argument) throws ArgumentException {
+        addArgument(argument, defaultArgGroup);
+    }
+
+    /**
+     * Adds the provided argument to the set of arguments handled by this parser
+     * and puts the arguement in the general group.
+     *
+     * @param argument
+     *            The argument to be added.
+     * @throws ArgumentException
+     *             If the provided argument conflicts with another argument that
+     *             has already been defined.
+     */
+    void addGeneralArgument(final Argument argument) throws ArgumentException {
+        addArgument(argument, generalArgGroup);
+    }
+
+    /**
+     * Adds the provided argument to the set of arguments handled by this parser
+     * and puts the argument in the input/output group.
+     *
+     * @param argument
+     *            The argument to be added.
+     * @throws ArgumentException
+     *             If the provided argument conflicts with another argument that
+     *             has already been defined.
+     */
+    void addInputOutputArgument(final Argument argument) throws ArgumentException {
+        addArgument(argument, ioArgGroup);
+    }
+
+    /**
+     * Adds the provided argument to the set of arguments handled by this parser
+     * and puts the argument in the LDAP connection group.
+     *
+     * @param argument
+     *            The argument to be added.
+     * @throws ArgumentException
+     *             If the provided argument conflicts with another argument that
+     *             has already been defined.
+     */
+    public void addLdapConnectionArgument(final Argument argument) throws ArgumentException {
+        addArgument(argument, ldapArgGroup);
+    }
+
+    /**
+     * Indicates whether this parser will allow unnamed trailing arguments.
+     * These will be arguments at the end of the list that are not preceded by
+     * either a long or short identifier and will need to be manually parsed by
+     * the application using this parser. Note that once an unnamed trailing
+     * argument has been identified, all remaining arguments will be classified
+     * as such.
+     *
+     * @return <CODE>true</CODE> if this parser allows unnamed trailing
+     *         arguments, or <CODE>false</CODE> if it does not.
+     */
+    boolean allowsTrailingArguments() {
+        return allowsTrailingArguments;
+    }
+
+    /**
+     * Check if we have a properties file.
+     *
+     * @return The properties found in the properties file or null.
+     * @throws ArgumentException
+     *             If a problem was encountered while parsing the provided
+     *             arguments.
+     */
+    Properties checkExternalProperties() throws ArgumentException {
+        // We don't look for properties file.
+        if ((noPropertiesFileArgument != null) && (noPropertiesFileArgument.isPresent())) {
+            return null;
+        }
+
+        // Check if we have a properties file argument
+        if (filePropertiesPathArgument == null) {
+            return null;
+        }
+
+        // check if the properties file argument has been set. If not
+        // look for default location.
+        String propertiesFilePath = null;
+        if (filePropertiesPathArgument.isPresent()) {
+            propertiesFilePath = filePropertiesPathArgument.getValue();
+        } else {
+            // Check in "user home"/.opendj directory
+            final String userDir = System.getProperty("user.home");
+            propertiesFilePath =
+                    findPropertiesFile(userDir + File.separator + DEFAULT_OPENDJ_CONFIG_DIR);
+        }
+
+        // We don't have a properties file location
+        if (propertiesFilePath == null) {
+            return null;
+        }
+
+        // We have a location for the properties file.
+        final Properties argumentProperties = new Properties();
+        final String scriptName = System.getProperty(PROPERTY_SCRIPT_NAME);
+        try {
+            final Properties p = new Properties();
+            final FileInputStream fis = new FileInputStream(propertiesFilePath);
+            p.load(fis);
+            fis.close();
+
+            for (final Enumeration<?> e = p.propertyNames(); e.hasMoreElements();) {
+                final String currentPropertyName = (String) e.nextElement();
+                String propertyName = currentPropertyName;
+
+                // Property name form <script name>.<property name> has the
+                // precedence to <property name>
+                if (scriptName != null) {
+                    if (currentPropertyName.startsWith(scriptName)) {
+                        propertyName = currentPropertyName.substring(scriptName.length() + 1);
+                    } else {
+                        if (p.containsKey(scriptName + "." + currentPropertyName)) {
+                            continue;
+                        }
+                    }
+                }
+                argumentProperties.setProperty(propertyName.toLowerCase(), p
+                        .getProperty(currentPropertyName));
+            }
+        } catch (final Exception e) {
+            final LocalizableMessage message =
+                    ERR_ARGPARSER_CANNOT_READ_PROPERTIES_FILE.get(String
+                            .valueOf(propertiesFilePath), getExceptionMessage(e));
+            throw new ArgumentException(message, e);
+        }
+        return argumentProperties;
+    }
+
+    /**
+     * Retrieves the argument with the specified name.
+     *
+     * @param name
+     *            The name of the argument to retrieve.
+     * @return The argument with the specified name, or <CODE>null</CODE> if
+     *         there is no such argument.
+     */
+    Argument getArgument(final String name) {
+        return argumentMap.get(name);
+    }
+
+    /**
+     * Retrieves the argument with the specified long identifier.
+     *
+     * @param longID
+     *            The long identifier of the argument to retrieve.
+     * @return The argument with the specified long identifier, or
+     *         <CODE>null</CODE> if there is no such argument.
+     */
+    Argument getArgumentForLongID(final String longID) {
+        return longIDMap.get(longID);
+    }
+
+    /**
+     * Retrieves the argument with the specified short identifier.
+     *
+     * @param shortID
+     *            The short ID for the argument to retrieve.
+     * @return The argument with the specified short identifier, or
+     *         <CODE>null</CODE> if there is no such argument.
+     */
+    Argument getArgumentForShortID(final Character shortID) {
+        return shortIDMap.get(shortID);
+    }
+
+    /**
+     * Retrieves the list of all arguments that have been defined for this
+     * argument parser.
+     *
+     * @return The list of all arguments that have been defined for this
+     *         argument parser.
+     */
+    LinkedList<Argument> getArgumentList() {
+        return argumentList;
+    }
+
+    /**
+     * Retrieves the set of arguments mapped by the long identifier that may be
+     * used to reference them. Note that arguments that do not have a long
+     * identifier will not be present in this list.
+     *
+     * @return The set of arguments mapped by the long identifier that may be
+     *         used to reference them.
+     */
+    HashMap<String, Argument> getArgumentsByLongID() {
+        return longIDMap;
+    }
+
+    /**
+     * Retrieves the set of arguments mapped by the short identifier that may be
+     * used to reference them. Note that arguments that do not have a short
+     * identifier will not be present in this list.
+     *
+     * @return The set of arguments mapped by the short identifier that may be
+     *         used to reference them.
+     */
+    HashMap<Character, Argument> getArgumentsByShortID() {
+        return shortIDMap;
+    }
+
+    /**
+     * Retrieves the fully-qualified name of the Java class that should be
+     * invoked to launch the program with which this argument parser is
+     * associated.
+     *
+     * @return The fully-qualified name of the Java class that should be invoked
+     *         to launch the program with which this argument parser is
+     *         associated.
+     */
+    String getMainClassName() {
+        return mainClassName;
+    }
+
+    /**
+     * Retrieves the maximum number of unnamed trailing arguments that may be
+     * provided.
+     *
+     * @return The maximum number of unnamed trailing arguments that may be
+     *         provided, or a value less than or equal to zero if no maximum
+     *         will be enforced.
+     */
+    int getMaxTrailingArguments() {
+        return maxTrailingArguments;
+    }
+
+    /**
+     * Retrieves the minimum number of unnamed trailing arguments that must be
+     * provided.
+     *
+     * @return The minimum number of unnamed trailing arguments that must be
+     *         provided, or a value less than or equal to zero if no minimum
+     *         will be enforced.
+     */
+    int getMinTrailingArguments() {
+        return minTrailingArguments;
+    }
+
+    /**
+     * Retrieves the raw set of arguments that were provided.
+     *
+     * @return The raw set of arguments that were provided, or <CODE>null</CODE>
+     *         if the argument list has not yet been parsed.
+     */
+    String[] getRawArguments() {
+        return rawArguments;
+    }
+
+    /**
+     * Given an argument, returns an appropriate group. Arguments may be part of
+     * one of the special groups or the default group.
+     *
+     * @param argument
+     *            for which a group is requested
+     * @return argument group appropriate for <code>argument</code>
+     */
+    ArgumentGroup getStandardGroup(final Argument argument) {
+        ArgumentGroup group;
+        if (isInputOutputArgument(argument)) {
+            group = ioArgGroup;
+        } else if (isGeneralArgument(argument)) {
+            group = generalArgGroup;
+        } else if (isLdapConnectionArgument(argument)) {
+            group = ldapArgGroup;
+        } else {
+            group = defaultArgGroup;
+        }
+        return group;
+    }
+
+    /**
+     * Retrieves a human-readable description for this tool, which should be
+     * included at the top of the command-line usage information.
+     *
+     * @return A human-readable description for this tool, or {@code null} if
+     *         none is available.
+     */
+    LocalizableMessage getToolDescription() {
+        return toolDescription;
+    }
+
+    /**
+     * Retrieves the set of unnamed trailing arguments that were provided on the
+     * command line.
+     *
+     * @return The set of unnamed trailing arguments that were provided on the
+     *         command line.
+     */
+    public ArrayList<String> getTrailingArguments() {
+        return trailingArguments;
+    }
+
+    /**
+     * Retrieves a string containing usage information based on the defined
+     * arguments.
+     *
+     * @return A string containing usage information based on the defined
+     *         arguments.
+     */
+    String getUsage() {
+        final StringBuilder buffer = new StringBuilder();
+        getUsage(buffer);
+
+        return buffer.toString();
+    }
+
+    /**
+     * Writes usage information based on the defined arguments to the provided
+     * output stream.
+     *
+     * @param outputStream
+     *            The output stream to which the usage information should be
+     *            written.
+     * @throws IOException
+     *             If a problem occurs while attempting to write the usage
+     *             information to the provided output stream.
+     */
+    void getUsage(final OutputStream outputStream) throws IOException {
+        final StringBuilder buffer = new StringBuilder();
+        getUsage(buffer);
+
+        outputStream.write(getBytes(buffer.toString()));
+    }
+
+    /**
+     * Appends usage information based on the defined arguments to the provided
+     * buffer.
+     *
+     * @param buffer
+     *            The buffer to which the usage information should be appended.
+     */
+    void getUsage(final StringBuilder buffer) {
+        usageOrVersionDisplayed = true;
+        final String scriptName = System.getProperty(PROPERTY_SCRIPT_NAME);
+        if ((scriptName == null) || (scriptName.length() == 0)) {
+            buffer.append(INFO_ARGPARSER_USAGE_JAVA_CLASSNAME.get(mainClassName));
+        } else {
+            buffer.append(INFO_ARGPARSER_USAGE_JAVA_SCRIPTNAME.get(scriptName));
+        }
+
+        if (allowsTrailingArguments) {
+            if (trailingArgsDisplayName == null) {
+                buffer.append(" " + INFO_ARGPARSER_USAGE_TRAILINGARGS.get());
+            } else {
+                buffer.append(" ");
+                buffer.append(trailingArgsDisplayName);
+            }
+        }
+        buffer.append(EOL);
+        buffer.append(EOL);
+        if ((toolDescription != null) && (toolDescription.length() > 0)) {
+            buffer.append(wrapText(toolDescription.toString(), MAX_LENGTH - 1));
+            buffer.append(EOL);
+            buffer.append(EOL);
+        }
+        buffer.append(INFO_SUBCMDPARSER_WHERE_OPTIONS_INCLUDE.get());
+        buffer.append(EOL);
+        buffer.append(EOL);
+
+        Argument helpArgument = null;
+
+        final boolean printHeaders = printUsageGroupHeaders();
+        for (final ArgumentGroup argGroup : argumentGroups) {
+            if (argGroup.containsArguments() && printHeaders) {
+                // Print the groups description if any
+                final LocalizableMessage groupDesc = argGroup.getDescription();
+                if (groupDesc != null && !LocalizableMessage.EMPTY.equals(groupDesc)) {
+                    buffer.append(EOL);
+                    buffer.append(wrapText(groupDesc.toString(), MAX_LENGTH - 1));
+                    buffer.append(EOL);
+                    buffer.append(EOL);
+                }
+            }
+
+            final SortedSet<Argument> args = new TreeSet<Argument>(new Comparator<Argument>() {
+
+                /**
+                 * {@inheritDoc}
+                 */
+                public int compare(final Argument o1, final Argument o2) {
+                    final String s1;
+                    final String s2;
+
+                    if (o1.getShortIdentifier() != null) {
+                        s1 = o1.getShortIdentifier().toString();
+                    } else {
+                        s1 = o1.getLongIdentifier();
+                    }
+
+                    if (o2.getShortIdentifier() != null) {
+                        s2 = o2.getShortIdentifier().toString();
+                    } else {
+                        s2 = o2.getLongIdentifier();
+                    }
+
+                    final int res = s1.compareToIgnoreCase(s2);
+                    if (res != 0) {
+                        return res;
+                    } else {
+                        // Lowercase options first then uppercase.
+                        return -s1.compareTo(s2);
+                    }
+                }
+
+            });
+            args.addAll(argGroup.getArguments());
+
+            for (final Argument a : args) {
+                // If this argument is hidden, then skip it.
+                if (a.isHidden()) {
+                    continue;
+                }
+
+                // Help argument should be printed at the end
+                if ((usageArgument != null) && usageArgument.getName().equals(a.getName())) {
+                    helpArgument = a;
+                    continue;
+                }
+                printArgumentUsage(a, buffer);
+            }
+        }
+        if (helpArgument != null) {
+            printArgumentUsage(helpArgument, buffer);
+        } else {
+            buffer.append(EOL);
+            buffer.append("-?");
+            buffer.append(EOL);
+        }
+    }
+
+    /**
+     * Retrieves a message containing usage information based on the defined
+     * arguments.
+     *
+     * @return A string containing usage information based on the defined
+     *         arguments.
+     */
+    public LocalizableMessage getUsageMessage() {
+        final StringBuilder buffer = new StringBuilder();
+        getUsage(buffer);
+
+        // TODO: rework getUsage(OutputStream) to work with messages
+        // framework
+        return LocalizableMessage.raw(buffer.toString());
+    }
+
+    /**
+     * Returns whether the usage argument was provided or not. This method
+     * should be called after a call to parseArguments.
+     *
+     * @return <CODE>true</CODE> if the usage argument was provided and
+     *         <CODE>false</CODE> otherwise.
+     */
+    boolean isUsageArgumentPresent() {
+        boolean isUsageArgumentPresent = false;
+        if (usageArgument != null) {
+            isUsageArgumentPresent = usageArgument.isPresent();
+        }
+        return isUsageArgumentPresent;
+    }
+
+    /**
+     * Returns whether the version argument was provided or not. This method
+     * should be called after a call to parseArguments.
+     *
+     * @return <CODE>true</CODE> if the version argument was provided and
+     *         <CODE>false</CODE> otherwise.
+     */
+    boolean isVersionArgumentPresent() {
+        return versionPresent;
+    }
+
+    /**
+     * Parses the provided set of arguments and updates the information
+     * associated with this parser accordingly.
+     *
+     * @param rawArguments
+     *            The raw set of arguments to parse.
+     * @throws ArgumentException
+     *             If a problem was encountered while parsing the provided
+     *             arguments.
+     */
+    public void parseArguments(final String[] rawArguments) throws ArgumentException {
+        parseArguments(rawArguments, null);
+    }
+
+    /**
+     * Parses the provided set of arguments and updates the information
+     * associated with this parser accordingly. Default values for unspecified
+     * arguments may be read from the specified properties if any are provided.
+     *
+     * @param rawArguments
+     *            The set of raw arguments to parse.
+     * @param argumentProperties
+     *            A set of properties that may be used to provide default values
+     *            for arguments not included in the given raw arguments.
+     * @throws ArgumentException
+     *             If a problem was encountered while parsing the provided
+     *             arguments.
+     */
+    public void parseArguments(final String[] rawArguments, Properties argumentProperties)
+            throws ArgumentException {
+        this.rawArguments = rawArguments;
+
+        boolean inTrailingArgs = false;
+
+        final int numArguments = rawArguments.length;
+        for (int i = 0; i < numArguments; i++) {
+            final String arg = rawArguments[i];
+
+            if (inTrailingArgs) {
+                trailingArguments.add(arg);
+                if ((maxTrailingArguments > 0) && (trailingArguments.size() > maxTrailingArguments)) {
+                    final LocalizableMessage message =
+                            ERR_ARGPARSER_TOO_MANY_TRAILING_ARGS.get(maxTrailingArguments);
+                    throw new ArgumentException(message);
+                }
+
+                continue;
+            }
+
+            if (arg.equals("--")) {
+                // This is a special indicator that we have reached the end of
+                // the named arguments and that everything that follows after
+                // this
+                // should be considered trailing arguments.
+                inTrailingArgs = true;
+            } else if (arg.startsWith("--")) {
+                // This indicates that we are using the long name to reference
+                // the argument. It may be in any of the following forms:
+                // --name
+                // --name value
+                // --name=value
+
+                String argName = arg.substring(2);
+                String argValue = null;
+                final int equalPos = argName.indexOf('=');
+                if (equalPos < 0) {
+                    // This is fine. The value is not part of the argument name
+                    // token.
+                } else if (equalPos == 0) {
+                    // The argument starts with "--=", which is not acceptable.
+                    final LocalizableMessage message = ERR_ARGPARSER_LONG_ARG_WITHOUT_NAME.get(arg);
+                    throw new ArgumentException(message);
+                } else {
+                    // The argument is in the form --name=value, so parse them
+                    // both out.
+                    argValue = argName.substring(equalPos + 1);
+                    argName = argName.substring(0, equalPos);
+                }
+
+                // If we're not case-sensitive, then convert the name to
+                // lowercase.
+                final String origArgName = argName;
+                if (!longArgumentsCaseSensitive) {
+                    argName = toLowerCase(argName);
+                }
+
+                // Get the argument with the specified name.
+                final Argument a = longIDMap.get(argName);
+                if (a == null) {
+                    if (argName.equals(OPTION_LONG_HELP)) {
+                        // "--help" will always be interpreted as requesting
+                        // usage
+                        // information.
+                        try {
+                            getUsage(usageOutputStream);
+                        } catch (final Exception e) {
+                            // Ignored.
+                        }
+
+                        return;
+                    } else if (argName.equals(OPTION_LONG_PRODUCT_VERSION)) {
+                        // "--version" will always be interpreted as requesting
+                        // version information.
+                        usageOrVersionDisplayed = true;
+                        versionPresent = true;
+                        try {
+                            // TODO
+                            // DirectoryServer.printVersion(usageOutputStream);
+                        } catch (final Exception e) {
+                            // Ignored.
+                        }
+
+                        return;
+                    } else {
+                        // There is no such argument registered.
+                        final LocalizableMessage message =
+                                ERR_ARGPARSER_NO_ARGUMENT_WITH_LONG_ID.get(origArgName);
+                        throw new ArgumentException(message);
+                    }
+                } else {
+                    a.setPresent(true);
+
+                    // If this is the usage argument, then immediately stop and
+                    // print usage information.
+                    if ((usageArgument != null) && usageArgument.getName().equals(a.getName())) {
+                        try {
+                            getUsage(usageOutputStream);
+                        } catch (final Exception e) {
+                            // Ignored.
+                        }
+
+                        return;
+                    }
+                }
+
+                // See if the argument takes a value. If so, then make sure one
+                // was provided. If not, then make sure none was provided.
+                if (a.needsValue()) {
+                    if (argValue == null) {
+                        if ((i + 1) == numArguments) {
+                            final LocalizableMessage message =
+                                    ERR_ARGPARSER_NO_VALUE_FOR_ARGUMENT_WITH_LONG_ID
+                                            .get(origArgName);
+                            throw new ArgumentException(message);
+                        }
+
+                        argValue = rawArguments[++i];
+                    }
+
+                    final LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder();
+                    if (!a.valueIsAcceptable(argValue, invalidReason)) {
+                        final LocalizableMessage message =
+                                ERR_ARGPARSER_VALUE_UNACCEPTABLE_FOR_LONG_ID.get(argValue,
+                                        origArgName, invalidReason.toString());
+                        throw new ArgumentException(message);
+                    }
+
+                    // If the argument already has a value, then make sure it is
+                    // acceptable to have more than one.
+                    if (a.hasValue() && (!a.isMultiValued())) {
+                        final LocalizableMessage message =
+                                ERR_ARGPARSER_NOT_MULTIVALUED_FOR_LONG_ID.get(origArgName);
+                        throw new ArgumentException(message);
+                    }
+
+                    a.addValue(argValue);
+                } else {
+                    if (argValue != null) {
+                        final LocalizableMessage message =
+                                ERR_ARGPARSER_ARG_FOR_LONG_ID_DOESNT_TAKE_VALUE.get(origArgName);
+                        throw new ArgumentException(message);
+                    }
+                }
+            } else if (arg.startsWith("-")) {
+                // This indicates that we are using the 1-character name to
+                // reference the argument. It may be in any of the following
+                // forms:
+                // -n
+                // -nvalue
+                // -n value
+                if (arg.equals("-")) {
+                    final LocalizableMessage message = ERR_ARGPARSER_INVALID_DASH_AS_ARGUMENT.get();
+                    throw new ArgumentException(message);
+                }
+
+                final char argCharacter = arg.charAt(1);
+                String argValue;
+                if (arg.length() > 2) {
+                    argValue = arg.substring(2);
+                } else {
+                    argValue = null;
+                }
+
+                // Get the argument with the specified short ID.
+                final Argument a = shortIDMap.get(argCharacter);
+                if (a == null) {
+                    if (argCharacter == '?') {
+                        // "-?" will always be interpreted as requesting usage
+                        // information.
+                        try {
+                            getUsage(usageOutputStream);
+                        } catch (final Exception e) {
+                            // Ignored.
+                        }
+
+                        return;
+                    } else if ((argCharacter == OPTION_SHORT_PRODUCT_VERSION)
+                            && (!shortIDMap.containsKey(OPTION_SHORT_PRODUCT_VERSION))) {
+                        // "-V" will always be interpreted as requesting
+                        // version information except if it's already defined
+                        // (e.g
+                        // in ldap tools).
+                        usageOrVersionDisplayed = true;
+                        versionPresent = true;
+                        try {
+                            // TODO
+                            // DirectoryServer.printVersion(usageOutputStream);
+                        } catch (final Exception e) {
+                            // Ignored.
+                        }
+                        return;
+                    } else {
+                        // There is no such argument registered.
+                        final LocalizableMessage message =
+                                ERR_ARGPARSER_NO_ARGUMENT_WITH_SHORT_ID.get(String
+                                        .valueOf(argCharacter));
+                        throw new ArgumentException(message);
+                    }
+                } else {
+                    a.setPresent(true);
+
+                    // If this is the usage argument, then immediately stop and
+                    // print usage information.
+                    if ((usageArgument != null) && usageArgument.getName().equals(a.getName())) {
+                        try {
+                            getUsage(usageOutputStream);
+                        } catch (final Exception e) {
+                            // Ignored.
+                        }
+
+                        return;
+                    }
+                }
+
+                // See if the argument takes a value. If so, then make sure one
+                // was provided. If not, then make sure none was provided.
+                if (a.needsValue()) {
+                    if (argValue == null) {
+                        if ((i + 1) == numArguments) {
+                            final LocalizableMessage message =
+                                    ERR_ARGPARSER_NO_VALUE_FOR_ARGUMENT_WITH_SHORT_ID.get(String
+                                            .valueOf(argCharacter));
+                            throw new ArgumentException(message);
+                        }
+
+                        argValue = rawArguments[++i];
+                    }
+
+                    final LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder();
+                    if (!a.valueIsAcceptable(argValue, invalidReason)) {
+                        final LocalizableMessage message =
+                                ERR_ARGPARSER_VALUE_UNACCEPTABLE_FOR_SHORT_ID.get(argValue, String
+                                        .valueOf(argCharacter), invalidReason.toString());
+                        throw new ArgumentException(message);
+                    }
+
+                    // If the argument already has a value, then make sure it is
+                    // acceptable to have more than one.
+                    if (a.hasValue() && (!a.isMultiValued())) {
+                        final LocalizableMessage message =
+                                ERR_ARGPARSER_NOT_MULTIVALUED_FOR_SHORT_ID.get(String
+                                        .valueOf(argCharacter));
+                        throw new ArgumentException(message);
+                    }
+
+                    a.addValue(argValue);
+                } else {
+                    if (argValue != null) {
+                        // If we've gotten here, then it means that we're in a
+                        // scenario like
+                        // "-abc" where "a" is a valid argument that doesn't
+                        // take a
+                        // value. However, this could still be valid if all
+                        // remaining
+                        // characters in the value are also valid argument
+                        // characters that
+                        // don't take values.
+                        final int valueLength = argValue.length();
+                        for (int j = 0; j < valueLength; j++) {
+                            final char c = argValue.charAt(j);
+                            final Argument b = shortIDMap.get(c);
+                            if (b == null) {
+                                // There is no such argument registered.
+                                final LocalizableMessage message =
+                                        ERR_ARGPARSER_NO_ARGUMENT_WITH_SHORT_ID.get(String
+                                                .valueOf(argCharacter));
+                                throw new ArgumentException(message);
+                            } else if (b.needsValue()) {
+                                // This means we're in a scenario like "-abc"
+                                // where b is
+                                // a valid argument that takes a value. We don't
+                                // support
+                                // that.
+                                final LocalizableMessage message =
+                                        ERR_ARGPARSER_CANT_MIX_ARGS_WITH_VALUES
+                                                .get(String.valueOf(argCharacter), argValue, String
+                                                        .valueOf(c));
+                                throw new ArgumentException(message);
+                            } else {
+                                b.setPresent(true);
+
+                                // If this is the usage argument, then
+                                // immediately stop
+                                // and print usage information.
+                                if ((usageArgument != null)
+                                        && usageArgument.getName().equals(b.getName())) {
+                                    try {
+                                        getUsage(usageOutputStream);
+                                    } catch (final Exception e) {
+                                        // Ignored.
+                                    }
+
+                                    return;
+                                }
+                            }
+                        }
+                    }
+                }
+            } else if (allowsTrailingArguments) {
+                // It doesn't start with a dash, so it must be a trailing
+                // argument if that is acceptable.
+                inTrailingArgs = true;
+                trailingArguments.add(arg);
+            } else {
+                // It doesn't start with a dash and we don't allow trailing
+                // arguments, so this is illegal.
+                final LocalizableMessage message =
+                        ERR_ARGPARSER_DISALLOWED_TRAILING_ARGUMENT.get(arg);
+                throw new ArgumentException(message);
+            }
+        }
+
+        // If we allow trailing arguments and there is a minimum number,
+        // then make sure at least that many were provided.
+        if (allowsTrailingArguments && (minTrailingArguments > 0)) {
+            if (trailingArguments.size() < minTrailingArguments) {
+                final LocalizableMessage message =
+                        ERR_ARGPARSER_TOO_FEW_TRAILING_ARGUMENTS.get(minTrailingArguments);
+                throw new ArgumentException(message);
+            }
+        }
+
+        // If we don't have the argumentProperties, try to load a properties
+        // file.
+        if (argumentProperties == null) {
+            argumentProperties = checkExternalProperties();
+        }
+
+        // Iterate through all of the arguments. For any that were not
+        // provided on the command line, see if there is an alternate default
+        // that
+        // can be used. For cases where there is not, see that argument is
+        // required.
+        for (final Argument a : argumentList) {
+            if (!a.isPresent()) {
+                // See if there is a value in the properties that can be used
+                if ((argumentProperties != null) && (a.getPropertyName() != null)) {
+                    final String value =
+                            argumentProperties.getProperty(a.getPropertyName().toLowerCase());
+                    final LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder();
+                    if (value != null) {
+                        Boolean addValue = true;
+                        if (!(a instanceof BooleanArgument)) {
+                            addValue = a.valueIsAcceptable(value, invalidReason);
+                        }
+                        if (addValue) {
+                            a.addValue(value);
+                            if (a.needsValue()) {
+                                a.setPresent(true);
+                            }
+                            a.setValueSetByProperty(true);
+                        }
+                    }
+                }
+            }
+
+            if ((!a.isPresent()) && a.needsValue()) {
+                // See if the argument defines a default.
+                if (a.getDefaultValue() != null) {
+                    a.addValue(a.getDefaultValue());
+                }
+
+                // If there is still no value and the argument is required, then
+                // that's a problem.
+                if ((!a.hasValue()) && a.isRequired()) {
+                    final LocalizableMessage message =
+                            ERR_ARGPARSER_NO_VALUE_FOR_REQUIRED_ARG.get(a.getName());
+                    throw new ArgumentException(message);
+                }
+            }
+        }
+    }
+
+    /**
+     * Parses the provided set of arguments and updates the information
+     * associated with this parser accordingly. Default values for unspecified
+     * arguments may be read from the specified properties file.
+     *
+     * @param rawArguments
+     *            The set of raw arguments to parse.
+     * @param propertiesFile
+     *            The path to the properties file to use to obtain default
+     *            values for unspecified properties.
+     * @param requirePropertiesFile
+     *            Indicates whether the parsing should fail if the provided
+     *            properties file does not exist or is not accessible.
+     * @throws ArgumentException
+     *             If a problem was encountered while parsing the provided
+     *             arguments or interacting with the properties file.
+     */
+    public void parseArguments(final String[] rawArguments, final String propertiesFile,
+            final boolean requirePropertiesFile) throws ArgumentException {
+        this.rawArguments = rawArguments;
+
+        Properties argumentProperties = null;
+
+        try {
+            final Properties p = new Properties();
+            final FileInputStream fis = new FileInputStream(propertiesFile);
+            p.load(fis);
+            fis.close();
+            argumentProperties = p;
+        } catch (final Exception e) {
+            if (requirePropertiesFile) {
+                final LocalizableMessage message =
+                        ERR_ARGPARSER_CANNOT_READ_PROPERTIES_FILE.get(String
+                                .valueOf(propertiesFile), getExceptionMessage(e));
+                throw new ArgumentException(message, e);
+            }
+        }
+
+        parseArguments(rawArguments, argumentProperties);
+    }
+
+    /**
+     * Indicates whether or not argument group description headers should be
+     * printed.
+     *
+     * @return boolean where true means print the descriptions
+     */
+    boolean printUsageGroupHeaders() {
+        // If there is only a single group then we won't print them.
+        int groupsContainingArgs = 0;
+        for (final ArgumentGroup argGroup : argumentGroups) {
+            if (argGroup.containsNonHiddenArguments()) {
+                groupsContainingArgs++;
+            }
+        }
+        return groupsContainingArgs > 1;
+    }
+
+    /**
+     * Sets the usage group description for the default argument group.
+     *
+     * @param description
+     *            for the default group
+     */
+    void setDefaultArgumentGroupDescription(final LocalizableMessage description) {
+        this.defaultArgGroup.setDescription(description);
+    }
+
+    /**
+     * Sets the provided argument which will be used to identify the file
+     * properties.
+     *
+     * @param argument
+     *            The argument which will be used to identify the file
+     *            properties.
+     */
+    public void setFilePropertiesArgument(final StringArgument argument) {
+        filePropertiesPathArgument = argument;
+    }
+
+    /**
+     * Sets the usage group description for the general argument group.
+     *
+     * @param description
+     *            for the general group
+     */
+    void setGeneralArgumentGroupDescription(final LocalizableMessage description) {
+        this.generalArgGroup.setDescription(description);
+    }
+
+    /**
+     * Sets the usage group description for the input/output argument group.
+     *
+     * @param description
+     *            for the input/output group
+     */
+    void setInputOutputArgumentGroupDescription(final LocalizableMessage description) {
+        this.ioArgGroup.setDescription(description);
+    }
+
+    /**
+     * Sets the usage group description for the LDAP argument group.
+     *
+     * @param description
+     *            for the LDAP group
+     */
+    void setLdapArgumentGroupDescription(final LocalizableMessage description) {
+        this.ldapArgGroup.setDescription(description);
+    }
+
+    /**
+     * Sets the provided argument which will be used to identify the file
+     * properties.
+     *
+     * @param argument
+     *            The argument which will be used to indicate if we have to look
+     *            for properties file.
+     */
+    public void setNoPropertiesFileArgument(final BooleanArgument argument) {
+        noPropertiesFileArgument = argument;
+    }
+
+    /**
+     * Sets the provided argument as one which will automatically trigger the
+     * output of usage information if it is provided on the command line and no
+     * further argument validation will be performed. Note that the caller will
+     * still need to add this argument to the parser with the
+     * <CODE>addArgument</CODE> method, and the argument should not be required
+     * and should not take a value. Also, the caller will still need to check
+     * for the presence of the usage argument after calling
+     * <CODE>parseArguments</CODE> to know that no further processing will be
+     * required.
+     *
+     * @param argument
+     *            The argument whose presence should automatically trigger the
+     *            display of usage information.
+     */
+    public void setUsageArgument(final Argument argument) {
+        usageArgument = argument;
+        usageOutputStream = System.out;
+    }
+
+    /**
+     * Sets the provided argument as one which will automatically trigger the
+     * output of usage information if it is provided on the command line and no
+     * further argument validation will be performed. Note that the caller will
+     * still need to add this argument to the parser with the
+     * <CODE>addArgument</CODE> method, and the argument should not be required
+     * and should not take a value. Also, the caller will still need to check
+     * for the presence of the usage argument after calling
+     * <CODE>parseArguments</CODE> to know that no further processing will be
+     * required.
+     *
+     * @param argument
+     *            The argument whose presence should automatically trigger the
+     *            display of usage information.
+     * @param outputStream
+     *            The output stream to which the usage information should be
+     *            written.
+     */
+    public void setUsageArgument(final Argument argument, final OutputStream outputStream) {
+        usageArgument = argument;
+        usageOutputStream = outputStream;
+    }
+
+    /**
+     * Indicates whether the version or the usage information has been displayed
+     * to the end user either by an explicit argument like "-H" or "--help", or
+     * by a built-in argument like "-?".
+     *
+     * @return {@code true} if the usage information has been displayed, or
+     *         {@code false} if not.
+     */
+    public boolean usageOrVersionDisplayed() {
+        return usageOrVersionDisplayed;
+    }
+
+    /**
+     * Get the absolute path of the properties file.
+     *
+     * @param directory
+     *            The location in which we should look for properties file
+     * @return The absolute path of the properties file or null
+     */
+    private String findPropertiesFile(final String directory) {
+        // Look for the tools properties file
+        final File f =
+                new File(directory, DEFAULT_OPENDJ_PROPERTIES_FILE_NAME
+                        + DEFAULT_OPENDJ_PROPERTIES_FILE_EXTENSION);
+        if (f.exists() && f.canRead()) {
+            return f.getAbsolutePath();
+        } else {
+            return null;
+        }
+    }
+
+    private void initGroups() {
+        this.argumentGroups = new TreeSet<ArgumentGroup>();
+        this.argumentGroups.add(defaultArgGroup);
+        this.argumentGroups.add(ldapArgGroup);
+        this.argumentGroups.add(generalArgGroup);
+        this.argumentGroups.add(ioArgGroup);
+
+        try {
+            versionArgument =
+                    new BooleanArgument(OPTION_LONG_PRODUCT_VERSION, OPTION_SHORT_PRODUCT_VERSION,
+                            OPTION_LONG_PRODUCT_VERSION, INFO_DESCRIPTION_PRODUCT_VERSION.get());
+            this.generalArgGroup.addArgument(versionArgument);
+        } catch (final ArgumentException e) {
+            // ignore
+        }
+    }
+
+    private boolean isGeneralArgument(final Argument arg) {
+        boolean general = false;
+        if (arg != null) {
+            final String longId = arg.getLongIdentifier();
+            general = OPTION_LONG_HELP.equals(longId) || OPTION_LONG_PRODUCT_VERSION.equals(longId);
+        }
+        return general;
+    }
+
+    private boolean isInputOutputArgument(final Argument arg) {
+        boolean io = false;
+        if (arg != null) {
+            final String longId = arg.getLongIdentifier();
+            io =
+                    OPTION_LONG_VERBOSE.equals(longId) || OPTION_LONG_QUIET.equals(longId)
+                            || OPTION_LONG_NO_PROMPT.equals(longId)
+                            || OPTION_LONG_PROP_FILE_PATH.equals(longId)
+                            || OPTION_LONG_NO_PROP_FILE.equals(longId)
+                            || OPTION_LONG_SCRIPT_FRIENDLY.equals(longId)
+                            || OPTION_LONG_DONT_WRAP.equals(longId)
+                            || OPTION_LONG_ENCODING.equals(longId)
+                            || OPTION_LONG_BATCH_FILE_PATH.equals(longId);
+        }
+        return io;
+    }
+
+    private boolean isLdapConnectionArgument(final Argument arg) {
+        boolean ldap = false;
+        if (arg != null) {
+            final String longId = arg.getLongIdentifier();
+            ldap =
+                    OPTION_LONG_USE_SSL.equals(longId) || OPTION_LONG_START_TLS.equals(longId)
+                            || OPTION_LONG_HOST.equals(longId) || OPTION_LONG_PORT.equals(longId)
+                            || OPTION_LONG_BINDDN.equals(longId)
+                            || OPTION_LONG_BINDPWD.equals(longId)
+                            || OPTION_LONG_BINDPWD_FILE.equals(longId)
+                            || OPTION_LONG_SASLOPTION.equals(longId)
+                            || OPTION_LONG_TRUSTALL.equals(longId)
+                            || OPTION_LONG_TRUSTSTOREPATH.equals(longId)
+                            || OPTION_LONG_TRUSTSTORE_PWD.equals(longId)
+                            || OPTION_LONG_TRUSTSTORE_PWD_FILE.equals(longId)
+                            || OPTION_LONG_KEYSTOREPATH.equals(longId)
+                            || OPTION_LONG_KEYSTORE_PWD.equals(longId)
+                            || OPTION_LONG_KEYSTORE_PWD_FILE.equals(longId)
+                            || OPTION_LONG_CERT_NICKNAME.equals(longId)
+                            || OPTION_LONG_REFERENCED_HOST_NAME.equals(longId)
+                            || OPTION_LONG_ADMIN_UID.equals(longId)
+                            || OPTION_LONG_REPORT_AUTHZ_ID.equals(longId)
+                            || OPTION_LONG_USE_PW_POLICY_CTL.equals(longId)
+                            || OPTION_LONG_USE_SASL_EXTERNAL.equals(longId)
+                            || OPTION_LONG_PROTOCOL_VERSION.equals(longId);
+        }
+        return ldap;
+    }
+
+    /**
+     * Appends argument usage information to the provided buffer.
+     *
+     * @param a
+     *            The argument to handle.
+     * @param buffer
+     *            The buffer to which the usage information should be appended.
+     */
+    private void printArgumentUsage(final Argument a, final StringBuilder buffer) {
+        // Write a line with the short and/or long identifiers that may be
+        // used
+        // for the argument.
+        final int indentLength = INDENT.length();
+        final Character shortID = a.getShortIdentifier();
+        final String longID = a.getLongIdentifier();
+        if (shortID != null) {
+            final int currentLength = buffer.length();
+
+            if (usageArgument.getName().equals(a.getName())) {
+                buffer.append("-?, ");
+            }
+
+            buffer.append("-");
+            buffer.append(shortID.charValue());
+
+            if (a.needsValue() && longID == null) {
+                buffer.append(" ");
+                buffer.append(a.getValuePlaceholder());
+            }
+
+            if (longID != null) {
+                final StringBuilder newBuffer = new StringBuilder();
+                newBuffer.append(", --");
+                newBuffer.append(longID);
+
+                if (a.needsValue()) {
+                    newBuffer.append(" ");
+                    newBuffer.append(a.getValuePlaceholder());
+                }
+
+                final int lineLength = (buffer.length() - currentLength) + newBuffer.length();
+                if (lineLength > MAX_LENGTH) {
+                    buffer.append(EOL);
+                    buffer.append(newBuffer.toString());
+                } else {
+                    buffer.append(newBuffer.toString());
+                }
+            }
+
+            buffer.append(EOL);
+        } else {
+            if (longID != null) {
+                if (usageArgument.getName().equals(a.getName())) {
+                    buffer.append("-?, ");
+                }
+                buffer.append("--");
+                buffer.append(longID);
+
+                if (a.needsValue()) {
+                    buffer.append(" ");
+                    buffer.append(a.getValuePlaceholder());
+                }
+
+                buffer.append(EOL);
+            }
+        }
+
+        // Write one or more lines with the description of the argument.
+        // We will indent the description five characters and try our best to wrap
+        // at or before column 79 so it will be friendly to 80-column displays.
+        buffer.append(wrapText(a.getDescription(), MAX_LENGTH, indentLength));
+        buffer.append(EOL);
+
+        if (a.needsValue() && (a.getDefaultValue() != null) && (a.getDefaultValue().length() > 0)) {
+            buffer.append(INDENT);
+            buffer.append(INFO_ARGPARSER_USAGE_DEFAULT_VALUE.get(a.getDefaultValue()).toString());
+            buffer.append(EOL);
+        }
+    }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/BooleanArgument.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/BooleanArgument.java
new file mode 100644
index 0000000..c40d7bc
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/BooleanArgument.java
@@ -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_BOOLEANARG_NO_VALUE_ALLOWED;
+
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.LocalizableMessageBuilder;
+
+/**
+ * This class defines an argument type that will be used to represent Boolean
+ * values. These arguments will never take values from the command line but and
+ * will never be required. If the argument is provided, then it will be
+ * considered true, and if not then it will be considered false. As such, the
+ * default value will always be "false".
+ */
+public final class BooleanArgument extends Argument {
+    /**
+     * Creates a new Boolean argument with the provided information.
+     *
+     * @param name
+     *            The generic name that should be used to refer to this
+     *            argument.
+     * @param shortIdentifier
+     *            The single-character identifier for this argument, or
+     *            <CODE>null</CODE> if there is none.
+     * @param longIdentifier
+     *            The long identifier for this argument, or <CODE>null</CODE> if
+     *            there is none.
+     * @param description
+     *            LocalizableMessage for the description of this argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to
+     *             create this argument.
+     */
+    public BooleanArgument(final String name, final Character shortIdentifier,
+            final String longIdentifier, final LocalizableMessage description)
+            throws ArgumentException {
+        super(name, shortIdentifier, longIdentifier, false, false, false, null, String
+                .valueOf(false), null, description);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    final public void addValue(final String valueString) {
+        if (valueString != null) {
+            clearValues();
+            super.addValue(valueString);
+            super.setPresent(Boolean.valueOf(valueString));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    final public void setPresent(final boolean isPresent) {
+        addValue(String.valueOf(isPresent));
+    }
+
+    /**
+     * Indicates whether the provided value is acceptable for use in this
+     * argument.
+     *
+     * @param valueString
+     *            The value for which to make the determination.
+     * @param invalidReason
+     *            A buffer into which the invalid reason may be written if the
+     *            value is not acceptable.
+     * @return <CODE>true</CODE> if the value is acceptable, or
+     *         <CODE>false</CODE> if it is not.
+     */
+    @Override
+    public boolean valueIsAcceptable(final String valueString,
+            final LocalizableMessageBuilder invalidReason) {
+        // This argument type should never have a value, so any value
+        // provided will be unacceptable.
+        invalidReason.append(ERR_BOOLEANARG_NO_VALUE_ALLOWED.get(getName()));
+
+        return false;
+    }
+}
diff --git a/opendj-cli/src/main/java/org/forgerock/opendj/cli/CLIException.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/CLIException.java
similarity index 98%
rename from opendj-cli/src/main/java/org/forgerock/opendj/cli/CLIException.java
rename to opendj-cli/src/main/java/com/forgerock/opendj/cli/CLIException.java
index 3cd4fdb..b1f5344 100755
--- a/opendj-cli/src/main/java/org/forgerock/opendj/cli/CLIException.java
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/CLIException.java
@@ -24,7 +24,7 @@
  *      Copyright 2008 Sun Microsystems, Inc.
  *      Portions copyright 2014 ForgeRock AS
  */
-package org.forgerock.opendj.cli;
+package com.forgerock.opendj.cli;
 
 import static com.forgerock.opendj.cli.CliMessages.ERR_CONSOLE_INPUT_ERROR;
 
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/CliConstants.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/CliConstants.java
new file mode 100755
index 0000000..92b3ff0
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/CliConstants.java
@@ -0,0 +1,625 @@
+/*
+ * 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-2010 Sun Microsystems, Inc.
+ *      Portions copyright 2012-2014 ForgeRock AS.
+ */
+package com.forgerock.opendj.cli;
+
+/**
+ * This class defines a number of constants used in one or more Directory Server
+ * tools.
+ */
+public final class CliConstants {
+    /**
+     * The name of the SASL property that can be used to provide the
+     * authentication ID for the bind.
+     */
+    public static final String SASL_PROPERTY_AUTHID = "authid";
+
+    /**
+     * The name of the SASL property that can be used to provide the
+     * authorization ID for the bind.
+     */
+    public static final String SASL_PROPERTY_AUTHZID = "authzid";
+
+    /**
+     * The name of the SASL property that can be used to provide the digest URI
+     * for the bind.
+     */
+    public static final String SASL_PROPERTY_DIGEST_URI = "digest-uri";
+
+    /**
+     * The name of the SASL property that can be used to provide the KDC for use
+     * in Kerberos authentication.
+     */
+    public static final String SASL_PROPERTY_KDC = "kdc";
+
+    /**
+     * The name of the SASL property that can be used to provide the quality of
+     * protection for the bind.
+     */
+    public static final String SASL_PROPERTY_QOP = "qop";
+
+    /**
+     * The name of the SASL property that can be used to provide the realm for
+     * the bind.
+     */
+    public static final String SASL_PROPERTY_REALM = "realm";
+
+    /**
+     * The name of the SASL property that can be used to provide trace
+     * information for a SASL ANONYMOUS request.
+     */
+    public static final String SASL_PROPERTY_TRACE = "trace";
+
+    /**
+     * The name of the SASL property that can be used to provide the SASL
+     * mechanism to use.
+     */
+    public static final String SASL_PROPERTY_MECH = "mech";
+
+    /**
+     * The name of the opendj configuration direction in the user home
+     * directory.
+     */
+    public static final String DEFAULT_OPENDJ_CONFIG_DIR = ".opendj";
+
+    /**
+     * The default properties file name.
+     */
+    public static final String DEFAULT_OPENDJ_PROPERTIES_FILE_NAME = "tools";
+
+    /**
+     * The default properties file extension.
+     */
+    public static final String DEFAULT_OPENDJ_PROPERTIES_FILE_EXTENSION = ".properties";
+
+    /**
+     * The value for the short option batchFilePath.
+     */
+    public static final char OPTION_SHORT_BATCH_FILE_PATH = 'F';
+
+    /**
+     * The value for the long option batchFilePath .
+     */
+    public static final String OPTION_LONG_BATCH_FILE_PATH = "batchFilePath";
+
+    /**
+     * The value for the short option hostname.
+     */
+    public static final char OPTION_SHORT_HOST = 'h';
+
+    /**
+     * The value for the long option hostname.
+     */
+    public static final String OPTION_LONG_HOST = "hostname";
+
+    /**
+     * The value for the short option port.
+     */
+    public static final char OPTION_SHORT_PORT = 'p';
+
+    /**
+     * The value for the long option port.
+     */
+    public static final String OPTION_LONG_PORT = "port";
+
+    /**
+     * The value for the short option useSSL.
+     */
+    public static final char OPTION_SHORT_USE_SSL = 'Z';
+
+    /**
+     * The value for the long option useSSL.
+     */
+    public static final String OPTION_LONG_USE_SSL = "useSSL";
+
+    /**
+     * The value for the short option baseDN.
+     */
+    public static final char OPTION_SHORT_BASEDN = 'b';
+
+    /**
+     * The value for the long option baseDN.
+     */
+    public static final String OPTION_LONG_BASEDN = "baseDN";
+
+    /**
+     * The value for the short option bindDN.
+     */
+    public static final char OPTION_SHORT_BINDDN = 'D';
+
+    /**
+     * The value for the long option bindDN.
+     */
+    public static final String OPTION_LONG_BINDDN = "bindDN";
+
+    /**
+     * The value for the short option bindPassword.
+     */
+    public static final char OPTION_SHORT_BINDPWD = 'w';
+
+    /**
+     * The value for the long option bindPassword.
+     */
+    public static final String OPTION_LONG_BINDPWD = "bindPassword";
+
+    /**
+     * The value for the short option bindPasswordFile.
+     */
+    public static final char OPTION_SHORT_BINDPWD_FILE = 'j';
+
+    /**
+     * The value for the long option bindPasswordFile.
+     */
+    public static final String OPTION_LONG_BINDPWD_FILE = "bindPasswordFile";
+
+    /**
+     * The value for the short option compress.
+     */
+    public static final char OPTION_SHORT_COMPRESS = 'c';
+
+    /**
+     * The value for the long option compress.
+     */
+    public static final String OPTION_LONG_COMPRESS = "compress";
+
+    /**
+     * The value for the short option filename.
+     */
+    public static final char OPTION_SHORT_FILENAME = 'f';
+
+    /**
+     * The value for the long option filename.
+     */
+    public static final String OPTION_LONG_FILENAME = "filename";
+
+    /**
+     * The value for the short option ldifFile.
+     */
+    public static final char OPTION_SHORT_LDIF_FILE = 'l';
+
+    /**
+     * The value for the long option ldifFile.
+     */
+    public static final String OPTION_LONG_LDIF_FILE = "ldifFile";
+
+    /**
+     * The value for the short option useStartTLS.
+     */
+    public static final char OPTION_SHORT_START_TLS = 'q';
+
+    /**
+     * The value for the long option useStartTLS.
+     */
+    public static final String OPTION_LONG_START_TLS = "useStartTLS";
+
+    /**
+     * The value for the short option randomSeed.
+     */
+    public static final char OPTION_SHORT_RANDOM_SEED = 's';
+
+    /**
+     * The value for the long option randomSeed.
+     */
+    public static final String OPTION_LONG_RANDOM_SEED = "randomSeed";
+
+    /**
+     * The value for the short option keyStorePath.
+     */
+    public static final char OPTION_SHORT_KEYSTOREPATH = 'K';
+
+    /**
+     * The value for the long option keyStorePath.
+     */
+    public static final String OPTION_LONG_KEYSTOREPATH = "keyStorePath";
+
+    /**
+     * The value for the short option trustStorePath.
+     */
+    public static final char OPTION_SHORT_TRUSTSTOREPATH = 'P';
+
+    /**
+     * The value for the long option trustStorePath.
+     */
+    public static final String OPTION_LONG_TRUSTSTOREPATH = "trustStorePath";
+
+    /**
+     * The value for the short option keyStorePassword.
+     */
+    public static final char OPTION_SHORT_KEYSTORE_PWD = 'W';
+
+    /**
+     * The value for the long option keyStorePassword.
+     */
+    public static final String OPTION_LONG_KEYSTORE_PWD = "keyStorePassword";
+
+    /**
+     * The value for the short option trustStorePassword.
+     */
+    public static final char OPTION_SHORT_TRUSTSTORE_PWD = 'T';
+
+    /**
+     * The value for the long option trustStorePassword.
+     */
+    public static final String OPTION_LONG_TRUSTSTORE_PWD = "trustStorePassword";
+
+    /**
+     * The value for the short option keyStorePasswordFile .
+     */
+    public static final char OPTION_SHORT_KEYSTORE_PWD_FILE = 'u';
+
+    /**
+     * The value for the long option keyStorePasswordFile .
+     */
+    public static final String OPTION_LONG_KEYSTORE_PWD_FILE = "keyStorePasswordFile";
+
+    /**
+     * The value for the short option keyStorePasswordFile .
+     */
+    public static final char OPTION_SHORT_TRUSTSTORE_PWD_FILE = 'U';
+
+    /**
+     * The value for the long option keyStorePasswordFile .
+     */
+    public static final String OPTION_LONG_TRUSTSTORE_PWD_FILE = "trustStorePasswordFile";
+
+    /**
+     * The value for the short option trustAll .
+     */
+    public static final char OPTION_SHORT_TRUSTALL = 'X';
+
+    /**
+     * The value for the long option trustAll .
+     */
+    public static final String OPTION_LONG_TRUSTALL = "trustAll";
+
+    /**
+     * The value for the short option certNickname .
+     */
+    public static final char OPTION_SHORT_CERT_NICKNAME = 'N';
+
+    /**
+     * The value for the long option certNickname .
+     */
+    public static final String OPTION_LONG_CERT_NICKNAME = "certNickname";
+
+    /**
+     * The value for the long option assertionFilter .
+     */
+    public static final String OPTION_LONG_ASSERTION_FILE = "assertionFilter";
+
+    /**
+     * The value for the short option dry-run.
+     */
+    public static final char OPTION_SHORT_DRYRUN = 'n';
+
+    /**
+     * The value for the long option dry-run.
+     */
+    public static final String OPTION_LONG_DRYRUN = "dry-run";
+
+    /**
+     * The value for the short option help.
+     */
+    public static final char OPTION_SHORT_HELP = 'H';
+
+    /**
+     * The value for the long option help.
+     */
+    public static final String OPTION_LONG_HELP = "help";
+
+    /**
+     * The value for the long option cli.
+     */
+    public static final String OPTION_LONG_CLI = "cli";
+
+    /**
+     * The value for the short option cli.
+     */
+    public static final char OPTION_SHORT_CLI = 'i';
+
+    /**
+     * The value for the short option proxyAs.
+     */
+    public static final char OPTION_SHORT_PROXYAUTHID = 'Y';
+
+    /**
+     * The value for the long option proxyAs.
+     */
+    public static final String OPTION_LONG_PROXYAUTHID = "proxyAs";
+
+    /**
+     * The value for the short option saslOption.
+     */
+    public static final char OPTION_SHORT_SASLOPTION = 'o';
+
+    /**
+     * The value for the long option saslOption.
+     */
+    public static final String OPTION_LONG_SASLOPTION = "saslOption";
+
+    /**
+     * The value for the short option geteffectiverights control authzid.
+     */
+    public static final char OPTION_SHORT_EFFECTIVERIGHTSUSER = 'g';
+
+    /**
+     * The value for the long option geteffectiverights control authzid.
+     */
+    public static final String OPTION_LONG_EFFECTIVERIGHTSUSER = "getEffectiveRightsAuthzid";
+
+    /**
+     * The value for the short option geteffectiveights control attributes.
+     */
+    public static final char OPTION_SHORT_EFFECTIVERIGHTSATTR = 'e';
+
+    /**
+     * The value for the long option geteffectiverights control specific
+     * attribute list.
+     */
+    public static final String OPTION_LONG_EFFECTIVERIGHTSATTR = "getEffectiveRightsAttribute";
+
+    /**
+     * The value for the short option protocol version attributes.
+     */
+    public static final char OPTION_SHORT_PROTOCOL_VERSION = 'V';
+
+    /**
+     * The value for the long option protocol version attribute.
+     */
+    public static final String OPTION_LONG_PROTOCOL_VERSION = "ldapVersion";
+
+    /**
+     * The value for the long option version.
+     */
+    public static final char OPTION_SHORT_PRODUCT_VERSION = 'V';
+
+    /**
+     * The value for the long option version.
+     */
+    public static final String OPTION_LONG_PRODUCT_VERSION = "version";
+
+    /**
+     * The value for the short option description attributes.
+     */
+    public static final char OPTION_SHORT_DESCRIPTION = 'd';
+
+    /**
+     * The value for the long option description attribute.
+     */
+    public static final String OPTION_LONG_DESCRIPTION = "description";
+
+    /**
+     * The value for the short option groupName attributes.
+     */
+    public static final char OPTION_SHORT_GROUPNAME = 'g';
+
+    /**
+     * The value for the long option groupName attribute.
+     */
+    public static final String OPTION_LONG_GROUPNAME = "groupName";
+
+    /**
+     * The value for the short option newGroupName attribute.
+     */
+    public static final char OPTION_SHORT_NEWGROUPNAME = 'n';
+
+    /**
+     * The value for the long option groupName attribute.
+     */
+    public static final String OPTION_LONG_NEWGROUPNAME = "newGroupName";
+
+    /**
+     * The value for the short option member-name attributes.
+     */
+    public static final char OPTION_SHORT_MEMBERNAME = 'm';
+
+    /**
+     * The value for the long member-name version attribute.
+     */
+    public static final String OPTION_LONG_MEMBERNAME = "memberName";
+
+    /**
+     * The value for the short option serverID attributes.
+     */
+    public static final String OPTION_SHORT_SERVERID = null;
+
+    /**
+     * The value for the long option serverID attribute.
+     */
+    public static final String OPTION_LONG_SERVERID = "serverID";
+
+    /**
+     * The value for the short option userID attributes.
+     */
+    public static final String OPTION_SHORT_USERID = null;
+
+    /**
+     * The value for the long option userID attribute.
+     */
+    public static final String OPTION_LONG_USERID = "userID";
+
+    /**
+     * The value for the short option set.
+     */
+    public static final Character OPTION_SHORT_SET = null;
+
+    /**
+     * The value for the long option set.
+     */
+    public static final String OPTION_LONG_SET = "set";
+
+    /**
+     * Value for the quiet option short form.
+     */
+    public static final Character OPTION_SHORT_QUIET = 'Q';
+
+    /**
+     * Value for the quiet option long form.
+     */
+    public static final String OPTION_LONG_QUIET = "quiet";
+
+    /**
+     * Value for non-interactive session short form.
+     */
+    public static final Character OPTION_SHORT_NO_PROMPT = 'n';
+
+    /**
+     * Value for non-interactive session long form.
+     */
+    public static final String OPTION_LONG_NO_PROMPT = "no-prompt";
+
+    /**
+     * Long form of script friendly option.
+     */
+    public static final String OPTION_LONG_SCRIPT_FRIENDLY = "script-friendly";
+
+    /**
+     * Short form of script friendly option.
+     */
+    public static final Character OPTION_SHORT_SCRIPT_FRIENDLY = 's';
+
+    /**
+     * Value for verbose option short form.
+     */
+    public static final Character OPTION_SHORT_VERBOSE = 'v';
+
+    /**
+     * Value for verbose option long form.
+     */
+    public static final String OPTION_LONG_VERBOSE = "verbose";
+
+    /**
+     * The value for the long option propertiesFilePAth .
+     */
+    public static final String OPTION_LONG_PROP_FILE_PATH = "propertiesFilePath";
+
+    /**
+     * The value for the long option propertiesFilePAth .
+     */
+    public static final String OPTION_LONG_NO_PROP_FILE = "noPropertiesFile";
+
+    /**
+     * Long form of referenced host name.
+     */
+    public static final String OPTION_LONG_REFERENCED_HOST_NAME = "referencedHostName";
+
+    /**
+     * Long form of admin UID.
+     */
+    public static final String OPTION_LONG_ADMIN_UID = "adminUID";
+
+    /**
+     * Long form of report authorization ID connection option.
+     */
+    public static final String OPTION_LONG_REPORT_AUTHZ_ID = "reportAuthzID";
+
+    /**
+     * Long form of use password policy control connection option.
+     */
+    public static final String OPTION_LONG_USE_PW_POLICY_CTL = "usePasswordPolicyControl";
+
+    /**
+     * Long form of use SASL external connection option.
+     */
+    public static final String OPTION_LONG_USE_SASL_EXTERNAL = "useSASLExternal";
+
+    /**
+     * Long form of option for the command-line encoding option.
+     */
+    public static final String OPTION_LONG_ENCODING = "encoding";
+
+    /**
+     * Long form of option specifying no wrapping of the command-line.
+     */
+    public static final String OPTION_LONG_DONT_WRAP = "dontWrap";
+
+    /**
+     * The value for the long option targetDN.
+     */
+    public static final String OPTION_LONG_TARGETDN = "targetDN";
+
+    /**
+     * Long form of email notification upon completion option.
+     */
+    public static final String OPTION_LONG_COMPLETION_NOTIFICATION_EMAIL = "completionNotify";
+
+    /**
+     * Short form of email notification upon completion option.
+     */
+    public static final Character OPTION_SHORT_COMPLETION_NOTIFICATION_EMAIL = null;
+
+    /**
+     * Long form of email notification upon error option.
+     */
+    public static final String OPTION_LONG_ERROR_NOTIFICATION_EMAIL = "errorNotify";
+
+    /**
+     * Short form of email notification upon error option.
+     */
+    public static final Character OPTION_SHORT_ERROR_NOTIFICATION_EMAIL = null;
+
+    /**
+     * Long form of dependency option.
+     */
+    public static final String OPTION_LONG_DEPENDENCY = "dependency";
+
+    /**
+     * Short form of dependency option.
+     */
+    public static final Character OPTION_SHORT_DEPENDENCY = null;
+
+    /**
+     * Long form of failed dependency action option.
+     */
+    public static final String OPTION_LONG_FAILED_DEPENDENCY_ACTION = "failedDependencyAction";
+
+    /**
+     * Short form of failed dependency action option.
+     */
+    public static final Character OPTION_SHORT_FAILED_DEPENDENCY_ACTION = null;
+
+    /**
+     * The default separator to be used in tables.
+     */
+    public static final String LIST_TABLE_SEPARATOR = ":";
+
+    /**
+     *
+     * The value for the short option output LDIF filename.
+     */
+    public static final char OPTION_SHORT_OUTPUT_LDIF_FILENAME = 'o';
+
+    /**
+     * The value for the long option output LDIF filename.
+     */
+    public static final String OPTION_LONG_OUTPUT_LDIF_FILENAME = "outputLDIF";
+
+    // Prevent instantiation.
+    private CliConstants() {
+
+    }
+
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java
new file mode 100644
index 0000000..038ed2a
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java
@@ -0,0 +1,120 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at 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 2014 ForgeRock AS
+ */
+package com.forgerock.opendj.cli;
+
+import static com.forgerock.opendj.cli.CliConstants.*;
+import static com.forgerock.opendj.cli.CliMessages.*;
+
+/**
+ * This class regroup commons arguments used by the different CLI.
+ */
+public final class CommonArguments {
+
+    // Prevent instantiation.
+    private CommonArguments() {
+        // Nothing to do.
+    }
+
+    /**
+     * Returns the "show usage" boolean argument.
+     *
+     * @return The "show usage" argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to create this argument.
+     */
+    public static final BooleanArgument getShowUsage() throws ArgumentException {
+        return new BooleanArgument("showUsage", OPTION_SHORT_HELP, OPTION_LONG_HELP,
+                INFO_DESCRIPTION_SHOWUSAGE.get());
+    }
+
+    /**
+     * Returns the "verbose" boolean argument.
+     *
+     * @return The "verbose" argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to create this argument.
+     */
+    public static final BooleanArgument getVerbose() throws ArgumentException {
+        final BooleanArgument verbose = new BooleanArgument("verbose", 'v', "verbose", INFO_DESCRIPTION_VERBOSE.get());
+        verbose.setPropertyName("verbose");
+        return verbose;
+    }
+
+    /**
+     * Returns the "get properties file" string argument.
+     *
+     * @return The "get properties file" argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to create this argument.
+     */
+    public static final StringArgument getPropertiesFileArgument() throws ArgumentException {
+        return new StringArgument("propertiesFilePath", null, OPTION_LONG_PROP_FILE_PATH,
+                false, false, true, INFO_PROP_FILE_PATH_PLACEHOLDER.get(), null, null,
+                INFO_DESCRIPTION_PROP_FILE_PATH.get());
+    }
+
+    /**
+     * Returns the "No properties file" boolean argument.
+     *
+     * @return The "No properties file" argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to create this argument.
+     */
+    public static final BooleanArgument getNoPropertiesFileArgument() throws ArgumentException {
+        return new BooleanArgument("noPropertiesFileArgument", null, OPTION_LONG_NO_PROP_FILE,
+                INFO_DESCRIPTION_NO_PROP_FILE.get());
+    }
+
+    /**
+     * Returns the "Continue On Error" boolean argument.
+     *
+     * @return The "Continue On Error" argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to create this argument.
+     */
+    public static final BooleanArgument getContinueOnErrorArgument() throws ArgumentException {
+        final BooleanArgument continueOnError = new BooleanArgument("continueOnError", 'c', "continueOnError",
+                INFO_DESCRIPTION_CONTINUE_ON_ERROR.get());
+        continueOnError.setPropertyName("continueOnError");
+        return continueOnError;
+    }
+
+    /**
+     * Returns the "version" integer argument.
+     *
+     * @return The "version" argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to create this argument.
+     */
+    public static final IntegerArgument getVersionArgument() throws ArgumentException {
+        final IntegerArgument version = new IntegerArgument("version", OPTION_SHORT_PROTOCOL_VERSION,
+                OPTION_LONG_PROTOCOL_VERSION, false, false, true, INFO_PROTOCOL_VERSION_PLACEHOLDER.get(), 3, null,
+                INFO_DESCRIPTION_VERSION.get());
+        version.setPropertyName(OPTION_LONG_PROTOCOL_VERSION);
+        return version;
+    }
+
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/ConsoleApplication.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ConsoleApplication.java
new file mode 100755
index 0000000..fa3e472
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ConsoleApplication.java
@@ -0,0 +1,328 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ *      Portions copyright 2011-2014 ForgeRock AS
+ *      Portions copyright 2011 Nemanja Lukić
+ */
+package com.forgerock.opendj.cli;
+
+import static com.forgerock.opendj.cli.CliMessages.INFO_ERROR_EMPTY_RESPONSE;
+import static com.forgerock.opendj.cli.CliMessages.INFO_MENU_PROMPT_RETURN_TO_CONTINUE;
+import static com.forgerock.opendj.cli.CliMessages.INFO_PROMPT_SINGLE_DEFAULT;
+import static com.forgerock.opendj.cli.Utils.MAX_LINE_WIDTH;
+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;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+
+import org.forgerock.i18n.LocalizableMessage;
+
+/**
+ * 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));
+
+    private final InputStream in = System.in;
+
+    private final PrintStream out;
+
+    private final Console console = System.console();
+
+    /**
+     * Creates a new console application instance.
+     */
+    public ConsoleApplication() {
+        this(System.out, System.err);
+    }
+
+    /**
+     * Creates a new console application instance with provided standard and error out streams.
+     *
+     * @param out
+     *            The output stream.
+     * @param err
+     *            The error stream.
+     */
+    public ConsoleApplication(PrintStream out, PrintStream err) {
+        this.out = out;
+        this.err = err;
+    }
+
+    /**
+     * 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.
+     */
+    public final PrintStream getErrorStream() {
+        return err;
+    }
+
+    /**
+     * Returns the application input stream.
+     *
+     * @return The application input stream.
+     */
+    public final InputStream getInputStream() {
+        return in;
+    }
+
+    /**
+     * Returns the application output stream.
+     *
+     * @return The application output stream.
+     */
+    public final PrintStream getOutputStream() {
+        return out;
+    }
+
+    /**
+     * 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.
+     */
+    public boolean isInteractive() {
+        return true;
+    }
+
+    /**
+     * 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.
+     */
+    public boolean isQuiet() {
+        return 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.
+     */
+    public boolean isScriptFriendly() {
+        return 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.
+     */
+    public boolean isVerbose() {
+        return false;
+    }
+
+    /**
+     * 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) {
+            // Ignore the exception - applications don't care.
+        }
+    }
+
+    /**
+     * Displays a message to the error stream.
+     *
+     * @param msg
+     *            The message.
+     */
+    public final void print(final LocalizableMessage msg) {
+        err.print(wrapText(msg, MAX_LINE_WIDTH));
+    }
+
+    /**
+     * Displays a blank line to the error stream.
+     */
+    public final void println() {
+        err.println();
+    }
+
+    /**
+     * Displays a message to the error stream.
+     *
+     * @param msg
+     *            The message.
+     */
+    public final void println(final LocalizableMessage msg) {
+        err.println(wrapText(msg, MAX_LINE_WIDTH));
+    }
+
+    /**
+     * 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(final LocalizableMessage msg, final int indent) {
+        err.println(wrapText(msg, MAX_LINE_WIDTH, indent));
+    }
+
+    /**
+     * Displays a message to the error stream if verbose mode is enabled.
+     *
+     * @param msg
+     *            The verbose message.
+     */
+    public final void printVerboseMessage(final LocalizableMessage msg) {
+        if (isVerbose() || isInteractive()) {
+            err.println(wrapText(msg, MAX_LINE_WIDTH));
+        }
+    }
+
+    /**
+     * 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
+     *             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 {
+        while (true) {
+            if (defaultValue != null) {
+                prompt = INFO_PROMPT_SINGLE_DEFAULT.get(prompt.toString(), defaultValue);
+            }
+            final String response = readLineOfInput(prompt);
+
+            if ("".equals(response)) {
+                if (defaultValue == null) {
+                    print(INFO_ERROR_EMPTY_RESPONSE.get());
+                } else {
+                    return defaultValue;
+                }
+            } else {
+                return response;
+            }
+        }
+    }
+
+    /**
+     * Interactively reads a password from the console.
+     *
+     * @param prompt
+     *            The password prompt.
+     * @return The password.
+     * @throws CLIException
+     *             If the password could not be retrieved for some reason.
+     */
+    public final char[] readPassword(final LocalizableMessage prompt) throws CLIException {
+        if (console != null) {
+            if (prompt != null) {
+                err.print(wrapText(prompt, MAX_LINE_WIDTH));
+                err.print(" ");
+            }
+            try {
+                final char[] password = console.readPassword();
+                if (password == null) {
+                    throw new EOFException("End of input");
+                }
+                return password;
+            } catch (final Throwable e) {
+                throw CLIException.adaptInputException(e);
+            }
+        } else {
+            // FIXME: should go direct to char[] and avoid the String.
+            return readLineOfInput(prompt).toCharArray();
+        }
+    }
+
+    /**
+     * Interactively retrieves a line of input from the console.
+     *
+     * @param prompt
+     *            The prompt.
+     * @return The line of input.
+     * @throws CLIException
+     *             If the line of input could not be retrieved for some reason.
+     */
+    private final String readLineOfInput(final LocalizableMessage prompt) throws CLIException {
+        if (prompt != null) {
+            err.print(wrapText(prompt, MAX_LINE_WIDTH));
+            err.print(" ");
+        }
+        try {
+            final String s = reader.readLine();
+            if (s == null) {
+                throw CLIException.adaptInputException(new EOFException("End of input"));
+            } else {
+                return s;
+            }
+        } catch (final IOException e) {
+            throw CLIException.adaptInputException(e);
+        }
+    }
+
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/FileBasedArgument.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/FileBasedArgument.java
new file mode 100644
index 0000000..f621b9b
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/FileBasedArgument.java
@@ -0,0 +1,242 @@
+/*
+ * 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.*;
+import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.LinkedHashMap;
+
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.LocalizableMessageBuilder;
+
+/**
+ * This class defines an argument whose value will be read from a file rather
+ * than actually specified on the command-line. When a value is specified on the
+ * command line, it will be treated as the path to the file containing the
+ * actual value rather than the value itself. <BR>
+ * <BR>
+ * Note that if if no filename is provided on the command line but a default
+ * value is specified programmatically or if the default value is read from a
+ * specified property, then that default value will be taken as the actual value
+ * rather than a filename. <BR>
+ * <BR>
+ * Also note that this argument type assumes that the entire value for the
+ * argument is on a single line in the specified file. If the file contains
+ * multiple lines, then only the first line will be read.
+ */
+public final class FileBasedArgument extends Argument {
+    // The mapping between filenames specified and the first lines read
+    // from those
+    // files.
+    private final LinkedHashMap<String, String> namesToValues;
+
+    /**
+     * Creates a new file-based argument with the provided information.
+     *
+     * @param name
+     *            The generic name that should be used to refer to this
+     *            argument.
+     * @param shortIdentifier
+     *            The single-character identifier for this argument, or
+     *            <CODE>null</CODE> if there is none.
+     * @param longIdentifier
+     *            The long identifier for this argument, or <CODE>null</CODE> if
+     *            there is none.
+     * @param isRequired
+     *            Indicates whether this argument must be specified on the
+     *            command line.
+     * @param isMultiValued
+     *            Indicates whether this argument may be specified more than
+     *            once to provide multiple values.
+     * @param valuePlaceholder
+     *            The placeholder for the argument value that will be displayed
+     *            in usage information, or <CODE>null</CODE> if this argument
+     *            does not require a value.
+     * @param defaultValue
+     *            The default value that should be used for this argument if
+     *            none is provided in a properties file or on the command line.
+     *            This may be <CODE>null</CODE> if there is no generic default.
+     * @param propertyName
+     *            The name of the property in a property file that may be used
+     *            to override the default value but will be overridden by a
+     *            command-line argument.
+     * @param description
+     *            LocalizableMessage for the description of this argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to
+     *             create this argument.
+     */
+    public FileBasedArgument(final String name, final Character shortIdentifier,
+            final String longIdentifier, final boolean isRequired, final boolean isMultiValued,
+            final LocalizableMessage valuePlaceholder, final String defaultValue,
+            final String propertyName, final LocalizableMessage description)
+            throws ArgumentException {
+        super(name, shortIdentifier, longIdentifier, isRequired, isMultiValued, true,
+                valuePlaceholder, defaultValue, propertyName, description);
+
+        namesToValues = new LinkedHashMap<String, String>();
+    }
+
+    /**
+     * Creates a new file-based argument with the provided information.
+     *
+     * @param name
+     *            The generic name that should be used to refer to this
+     *            argument.
+     * @param shortIdentifier
+     *            The single-character identifier for this argument, or
+     *            <CODE>null</CODE> if there is none.
+     * @param longIdentifier
+     *            The long identifier for this argument, or <CODE>null</CODE> if
+     *            there is none.
+     * @param isRequired
+     *            Indicates whether this argument must be specified on the
+     *            command line.
+     * @param valuePlaceholder
+     *            The placeholder for the argument value that will be displayed
+     *            in usage information, or <CODE>null</CODE> if this argument
+     *            does not require a value.
+     * @param description
+     *            LocalizableMessage for the description of this argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to
+     *             create this argument.
+     */
+    public FileBasedArgument(final String name, final Character shortIdentifier,
+            final String longIdentifier, final boolean isRequired,
+            final LocalizableMessage valuePlaceholder, final LocalizableMessage description)
+            throws ArgumentException {
+        super(name, shortIdentifier, longIdentifier, isRequired, false, true, valuePlaceholder,
+                null, null, description);
+
+        namesToValues = new LinkedHashMap<String, String>();
+    }
+
+    /**
+     * Adds a value to the set of values for this argument. This should only be
+     * called if the value is allowed by the <CODE>valueIsAcceptable</CODE>
+     * method. Note that in this case, correct behavior depends on a previous
+     * successful call to <CODE>valueIsAcceptable</CODE> so that the value read
+     * from the file may be stored in the name-to-value hash and used in place
+     * of the filename here.
+     *
+     * @param valueString
+     *            The string representation of the value to add to this
+     *            argument.
+     */
+    @Override
+    public void addValue(final String valueString) {
+        final String actualValue = namesToValues.get(valueString);
+        if (actualValue != null) {
+            super.addValue(actualValue);
+        }
+    }
+
+    /**
+     * Retrieves a map between the filenames specified on the command line and
+     * the first lines read from those files.
+     *
+     * @return A map between the filenames specified on the command line and the
+     *         first lines read from those files.
+     */
+    public LinkedHashMap<String, String> getNameToValueMap() {
+        return namesToValues;
+    }
+
+    /**
+     * Indicates whether the provided value is acceptable for use in this
+     * argument.
+     *
+     * @param valueString
+     *            The value for which to make the determination.
+     * @param invalidReason
+     *            A buffer into which the invalid reason may be written if the
+     *            value is not acceptable.
+     * @return <CODE>true</CODE> if the value is acceptable, or
+     *         <CODE>false</CODE> if it is not.
+     */
+    @Override
+    public boolean valueIsAcceptable(final String valueString,
+            final LocalizableMessageBuilder invalidReason) {
+        // First, make sure that the specified file exists.
+        File valueFile;
+        try {
+            valueFile = new File(valueString);
+            if (!valueFile.exists()) {
+                invalidReason.append(ERR_FILEARG_NO_SUCH_FILE.get(valueString, getName()));
+                return false;
+            }
+        } catch (final Exception e) {
+            invalidReason.append(ERR_FILEARG_CANNOT_VERIFY_FILE_EXISTENCE.get(valueString,
+                    getName(), getExceptionMessage(e)));
+            return false;
+        }
+
+        // Open the file for reading.
+        BufferedReader reader;
+        try {
+            reader = new BufferedReader(new FileReader(valueFile));
+        } catch (final Exception e) {
+            invalidReason.append(ERR_FILEARG_CANNOT_OPEN_FILE.get(valueString, getName(),
+                    getExceptionMessage(e)));
+            return false;
+        }
+
+        // Read the first line and close the file.
+        String line;
+        try {
+            line = reader.readLine();
+        } catch (final Exception e) {
+            invalidReason.append(ERR_FILEARG_CANNOT_READ_FILE.get(valueString, getName(),
+                    getExceptionMessage(e)));
+            return false;
+        } finally {
+            try {
+                reader.close();
+            } catch (final Exception e) {
+                // Ignored.
+            }
+        }
+
+        // If the line read is null, then that means the file was empty.
+        if (line == null) {
+
+            invalidReason.append(ERR_FILEARG_EMPTY_FILE.get(valueString, getName()));
+            return false;
+        }
+
+        // Store the value in the hash so it will be available for addValue.
+        // We won't do any validation on the value itself, so anything that we
+        // read will be considered acceptable.
+        namesToValues.put(valueString, line);
+        return true;
+    }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/IntegerArgument.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/IntegerArgument.java
new file mode 100644
index 0000000..5ede248
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/IntegerArgument.java
@@ -0,0 +1,483 @@
+/*
+ * 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-2010 Sun Microsystems, Inc.
+ *      Portions copyright 2014 ForgeRock AS
+ */
+package com.forgerock.opendj.cli;
+
+import static com.forgerock.opendj.cli.CliMessages.*;
+
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.LocalizableMessageBuilder;
+
+/**
+ * This class defines an argument type that will only accept integer values, and
+ * potentially only those in a given range.
+ */
+public final class IntegerArgument extends Argument {
+    // Indicates whether a lower bound will be enforced for this argument.
+    private final boolean hasLowerBound;
+
+    // Indicates whether an upper bound will be enforced for this
+    // argument.
+    private final boolean hasUpperBound;
+
+    // The lower bound that will be enforced for this argument.
+    private final double lowerBound;
+
+    // The upper bound that will be enforced for this argument.
+    private final double upperBound;
+
+    /**
+     * Creates a new integer argument with the provided information.
+     *
+     * @param name
+     *            The generic name that should be used to refer to this
+     *            argument.
+     * @param shortIdentifier
+     *            The single-character identifier for this argument, or
+     *            <CODE>null</CODE> if there is none.
+     * @param longIdentifier
+     *            The long identifier for this argument, or <CODE>null</CODE> if
+     *            there is none.
+     * @param isRequired
+     *            Indicates whether this argument must be specified on the
+     *            command line.
+     * @param isMultiValued
+     *            Indicates whether this argument may be specified more than
+     *            once to provide multiple values.
+     * @param needsValue
+     *            Indicates whether this argument requires a value.
+     * @param valuePlaceholder
+     *            The placeholder for the argument value that will be displayed
+     *            in usage information, or <CODE>null</CODE> if this argument
+     *            does not require a value.
+     * @param defaultValue
+     *            The default value that should be used for this argument if
+     *            none is provided in a properties file or on the command line.
+     *            This may be <CODE>null</CODE> if there is no generic default.
+     * @param propertyName
+     *            The name of the property in a property file that may be used
+     *            to override the default value but will be overridden by a
+     *            command-line argument.
+     * @param hasLowerBound
+     *            Indicates whether a lower bound should be enforced for values
+     *            of this argument.
+     * @param lowerBound
+     *            The lower bound that should be enforced for values of this
+     *            argument.
+     * @param hasUpperBound
+     *            Indicates whether an upperbound should be enforced for values
+     *            of this argument.
+     * @param upperBound
+     *            The upper bound that should be enforced for values of this
+     *            argument.
+     * @param description
+     *            LocalizableMessage for the description of this argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to
+     *             create this argument.
+     */
+    public IntegerArgument(final String name, final Character shortIdentifier,
+            final String longIdentifier, final boolean isRequired, final boolean isMultiValued,
+            final boolean needsValue, final LocalizableMessage valuePlaceholder,
+            final double defaultValue, final String propertyName, final boolean hasLowerBound,
+            final double lowerBound, final boolean hasUpperBound, final double upperBound,
+            final LocalizableMessage description) throws ArgumentException {
+        super(name, shortIdentifier, longIdentifier, isRequired, isMultiValued, needsValue,
+                valuePlaceholder, String.valueOf(defaultValue), propertyName, description);
+
+        this.hasLowerBound = hasLowerBound;
+        this.hasUpperBound = hasUpperBound;
+        this.lowerBound = lowerBound;
+        this.upperBound = upperBound;
+
+        if (hasLowerBound && hasUpperBound && (lowerBound > upperBound)) {
+            final LocalizableMessage message =
+                    ERR_INTARG_LOWER_BOUND_ABOVE_UPPER_BOUND.get(name, lowerBound, upperBound);
+            throw new ArgumentException(message);
+        }
+    }
+
+    /**
+     * Creates a new integer argument with the provided information.
+     *
+     * @param name
+     *            The generic name that should be used to refer to this
+     *            argument.
+     * @param shortIdentifier
+     *            The single-character identifier for this argument, or
+     *            <CODE>null</CODE> if there is none.
+     * @param longIdentifier
+     *            The long identifier for this argument, or <CODE>null</CODE> if
+     *            there is none.
+     * @param isRequired
+     *            Indicates whether this argument must be specified on the
+     *            command line.
+     * @param isMultiValued
+     *            Indicates whether this argument may be specified more than
+     *            once to provide multiple values.
+     * @param needsValue
+     *            Indicates whether this argument requires a value.
+     * @param valuePlaceholder
+     *            The placeholder for the argument value that will be displayed
+     *            in usage information, or <CODE>null</CODE> if this argument
+     *            does not require a value.
+     * @param defaultValue
+     *            The default value that should be used for this argument if
+     *            none is provided in a properties file or on the command line.
+     *            This may be <CODE>null</CODE> if there is no generic default.
+     * @param propertyName
+     *            The name of the property in a property file that may be used
+     *            to override the default value but will be overridden by a
+     *            command-line argument.
+     * @param description
+     *            LocalizableMessage for the description of this argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to
+     *             create this argument.
+     */
+    public IntegerArgument(final String name, final Character shortIdentifier,
+            final String longIdentifier, final boolean isRequired, final boolean isMultiValued,
+            final boolean needsValue, final LocalizableMessage valuePlaceholder,
+            final double defaultValue, final String propertyName,
+            final LocalizableMessage description) throws ArgumentException {
+        super(name, shortIdentifier, longIdentifier, isRequired, isMultiValued, needsValue,
+                valuePlaceholder, String.format("%f", defaultValue), propertyName, description);
+
+        hasLowerBound = false;
+        hasUpperBound = false;
+        lowerBound = Integer.MIN_VALUE;
+        upperBound = Integer.MAX_VALUE;
+    }
+
+    /**
+     * Creates a new integer argument with the provided information.
+     *
+     * @param name
+     *            The generic name that should be used to refer to this
+     *            argument.
+     * @param shortIdentifier
+     *            The single-character identifier for this argument, or
+     *            <CODE>null</CODE> if there is none.
+     * @param longIdentifier
+     *            The long identifier for this argument, or <CODE>null</CODE> if
+     *            there is none.
+     * @param isRequired
+     *            Indicates whether this argument must be specified on the
+     *            command line.
+     * @param isMultiValued
+     *            Indicates whether this argument may be specified more than
+     *            once to provide multiple values.
+     * @param needsValue
+     *            Indicates whether this argument requires a value.
+     * @param valuePlaceholder
+     *            The placeholder for the argument value that will be displayed
+     *            in usage information, or <CODE>null</CODE> if this argument
+     *            does not require a value.
+     * @param defaultValue
+     *            The default value that should be used for this argument if
+     *            none is provided in a properties file or on the command line.
+     *            This may be <CODE>null</CODE> if there is no generic default.
+     * @param propertyName
+     *            The name of the property in a property file that may be used
+     *            to override the default value but will be overridden by a
+     *            command-line argument.
+     * @param hasLowerBound
+     *            Indicates whether a lower bound should be enforced for values
+     *            of this argument.
+     * @param lowerBound
+     *            The lower bound that should be enforced for values of this
+     *            argument.
+     * @param hasUpperBound
+     *            Indicates whether an upperbound should be enforced for values
+     *            of this argument.
+     * @param upperBound
+     *            The upper bound that should be enforced for values of this
+     *            argument.
+     * @param description
+     *            LocalizableMessage for the description of this argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to
+     *             create this argument.
+     */
+    public IntegerArgument(final String name, final Character shortIdentifier,
+            final String longIdentifier, final boolean isRequired, final boolean isMultiValued,
+            final boolean needsValue, final LocalizableMessage valuePlaceholder,
+            final int defaultValue, final String propertyName, final boolean hasLowerBound,
+            final double lowerBound, final boolean hasUpperBound, final double upperBound,
+            final LocalizableMessage description) throws ArgumentException {
+        super(name, shortIdentifier, longIdentifier, isRequired, isMultiValued, needsValue,
+                valuePlaceholder, String.valueOf(defaultValue), propertyName, description);
+
+        this.hasLowerBound = hasLowerBound;
+        this.hasUpperBound = hasUpperBound;
+        this.lowerBound = lowerBound;
+        this.upperBound = upperBound;
+
+        if (hasLowerBound && hasUpperBound && (lowerBound > upperBound)) {
+            final LocalizableMessage message =
+                    ERR_INTARG_LOWER_BOUND_ABOVE_UPPER_BOUND.get(name, lowerBound, upperBound);
+            throw new ArgumentException(message);
+        }
+    }
+
+    /**
+     * Creates a new integer argument with the provided information.
+     *
+     * @param name
+     *            The generic name that should be used to refer to this
+     *            argument.
+     * @param shortIdentifier
+     *            The single-character identifier for this argument, or
+     *            <CODE>null</CODE> if there is none.
+     * @param longIdentifier
+     *            The long identifier for this argument, or <CODE>null</CODE> if
+     *            there is none.
+     * @param isRequired
+     *            Indicates whether this argument must be specified on the
+     *            command line.
+     * @param isMultiValued
+     *            Indicates whether this argument may be specified more than
+     *            once to provide multiple values.
+     * @param needsValue
+     *            Indicates whether this argument requires a value.
+     * @param valuePlaceholder
+     *            The placeholder for the argument value that will be displayed
+     *            in usage information, or <CODE>null</CODE> if this argument
+     *            does not require a value.
+     * @param defaultValue
+     *            The default value that should be used for this argument if
+     *            none is provided in a properties file or on the command line.
+     *            This may be <CODE>null</CODE> if there is no generic default.
+     * @param propertyName
+     *            The name of the property in a property file that may be used
+     *            to override the default value but will be overridden by a
+     *            command-line argument.
+     * @param description
+     *            LocalizableMessage for the description of this argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to
+     *             create this argument.
+     */
+    public IntegerArgument(final String name, final Character shortIdentifier,
+            final String longIdentifier, final boolean isRequired, final boolean isMultiValued,
+            final boolean needsValue, final LocalizableMessage valuePlaceholder,
+            final int defaultValue, final String propertyName, final LocalizableMessage description)
+            throws ArgumentException {
+        super(name, shortIdentifier, longIdentifier, isRequired, isMultiValued, needsValue,
+                valuePlaceholder, String.valueOf(defaultValue), propertyName, description);
+
+        hasLowerBound = false;
+        hasUpperBound = false;
+        lowerBound = Integer.MIN_VALUE;
+        upperBound = Integer.MAX_VALUE;
+    }
+
+    /**
+     * Creates a new integer argument with the provided information.
+     *
+     * @param name
+     *            The generic name that should be used to refer to this
+     *            argument.
+     * @param shortIdentifier
+     *            The single-character identifier for this argument, or
+     *            <CODE>null</CODE> if there is none.
+     * @param longIdentifier
+     *            The long identifier for this argument, or <CODE>null</CODE> if
+     *            there is none.
+     * @param isRequired
+     *            Indicates whether this argument must be specified on the
+     *            command line.
+     * @param needsValue
+     *            Indicates whether this argument requires a value.
+     * @param valuePlaceholder
+     *            The placeholder for the argument value that will be displayed
+     *            in usage information, or <CODE>null</CODE> if this argument
+     *            does not require a value.
+     * @param hasLowerBound
+     *            Indicates whether a lower bound should be enforced for values
+     *            of this argument.
+     * @param lowerBound
+     *            The lower bound that should be enforced for values of this
+     *            argument.
+     * @param hasUpperBound
+     *            Indicates whether an upperbound should be enforced for values
+     *            of this argument.
+     * @param upperBound
+     *            The upper bound that should be enforced for values of this
+     *            argument.
+     * @param description
+     *            LocalizableMessage for the description of this argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to
+     *             create this argument.
+     */
+    public IntegerArgument(final String name, final Character shortIdentifier,
+            final String longIdentifier, final boolean isRequired, final boolean needsValue,
+            final LocalizableMessage valuePlaceholder, final boolean hasLowerBound,
+            final double lowerBound, final boolean hasUpperBound, final double upperBound,
+            final LocalizableMessage description) throws ArgumentException {
+        super(name, shortIdentifier, longIdentifier, isRequired, false, needsValue,
+                valuePlaceholder, null, null, description);
+
+        this.hasLowerBound = hasLowerBound;
+        this.hasUpperBound = hasUpperBound;
+        this.lowerBound = lowerBound;
+        this.upperBound = upperBound;
+
+        if (hasLowerBound && hasUpperBound && (lowerBound > upperBound)) {
+            final LocalizableMessage message =
+                    ERR_INTARG_LOWER_BOUND_ABOVE_UPPER_BOUND.get(name, lowerBound, upperBound);
+            throw new ArgumentException(message);
+        }
+    }
+
+    /**
+     * Creates a new integer argument with the provided information.
+     *
+     * @param name
+     *            The generic name that should be used to refer to this
+     *            argument.
+     * @param shortIdentifier
+     *            The single-character identifier for this argument, or
+     *            <CODE>null</CODE> if there is none.
+     * @param longIdentifier
+     *            The long identifier for this argument, or <CODE>null</CODE> if
+     *            there is none.
+     * @param isRequired
+     *            Indicates whether this argument must be specified on the
+     *            command line.
+     * @param needsValue
+     *            Indicates whether this argument requires a value.
+     * @param valuePlaceholder
+     *            The placeholder for the argument value that will be displayed
+     *            in usage information, or <CODE>null</CODE> if this argument
+     *            does not require a value.
+     * @param description
+     *            LocalizableMessage for the description of this argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to
+     *             create this argument.
+     */
+    public IntegerArgument(final String name, final Character shortIdentifier,
+            final String longIdentifier, final boolean isRequired, final boolean needsValue,
+            final LocalizableMessage valuePlaceholder, final LocalizableMessage description)
+            throws ArgumentException {
+        super(name, shortIdentifier, longIdentifier, isRequired, false, needsValue,
+                valuePlaceholder, null, null, description);
+
+        hasLowerBound = false;
+        hasUpperBound = false;
+        lowerBound = Double.MIN_VALUE;
+        upperBound = Double.MAX_VALUE;
+    }
+
+    /**
+     * Retrieves the lower bound that may be enforced for values of this
+     * argument.
+     *
+     * @return The lower bound that may be enforced for values of this argument.
+     */
+    public double getLowerBound() {
+        return lowerBound;
+    }
+
+    /**
+     * Retrieves the upper bound that may be enforced for values of this
+     * argument.
+     *
+     * @return The upper bound that may be enforced for values of this argument.
+     */
+    public double getUpperBound() {
+        return upperBound;
+    }
+
+    /**
+     * Indicates whether a lower bound should be enforced for values of this
+     * argument.
+     *
+     * @return <CODE>true</CODE> if a lower bound should be enforced for values
+     *         of this argument, or <CODE>false</CODE> if not.
+     */
+    public boolean hasLowerBound() {
+        return hasLowerBound;
+    }
+
+    /**
+     * Indicates whether a upper bound should be enforced for values of this
+     * argument.
+     *
+     * @return <CODE>true</CODE> if a upper bound should be enforced for values
+     *         of this argument, or <CODE>false</CODE> if not.
+     */
+    public boolean hasUpperBound() {
+        return hasUpperBound;
+    }
+
+    /**
+     * Indicates whether the provided value is acceptable for use in this
+     * argument.
+     *
+     * @param valueString
+     *            The value for which to make the determination.
+     * @param invalidReason
+     *            A buffer into which the invalid reason may be written if the
+     *            value is not acceptable.
+     * @return <CODE>true</CODE> if the value is acceptable, or
+     *         <CODE>false</CODE> if it is not.
+     */
+    @Override
+    public boolean valueIsAcceptable(final String valueString,
+            final LocalizableMessageBuilder invalidReason) {
+        // First, the value must be decodable as an integer.
+        double intValue;
+        try {
+            intValue = Double.parseDouble(valueString);
+        } catch (final Exception e) {
+            invalidReason.append(ERR_ARG_CANNOT_DECODE_AS_INT.get(valueString, getName()));
+            return false;
+        }
+
+        // If there is a lower bound, then the value must be greater than or
+        // equal to it.
+        if (hasLowerBound && (intValue < lowerBound)) {
+            invalidReason.append(ERR_INTARG_VALUE_BELOW_LOWER_BOUND.get(getName(), intValue,
+                    lowerBound));
+            return false;
+        }
+
+        // If there is an upper bound, then the value must be less than or
+        // equal to it.
+        if (hasUpperBound && (intValue > upperBound)) {
+
+            invalidReason.append(ERR_INTARG_VALUE_ABOVE_UPPER_BOUND.get(getName(), intValue,
+                    upperBound));
+            return false;
+        }
+
+        // At this point, the value should be acceptable.
+        return true;
+    }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiChoiceArgument.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiChoiceArgument.java
new file mode 100644
index 0000000..44d4632
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiChoiceArgument.java
@@ -0,0 +1,239 @@
+/*
+ * 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_MCARG_VALUE_NOT_ALLOWED;
+
+import java.util.Collection;
+
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.LocalizableMessageBuilder;
+
+/**
+ * This class defines an argument type that will only accept one or more of a
+ * specific set of string values.
+ *
+ * @param <T>
+ *            The type of values returned by this argument.
+ */
+public final class MultiChoiceArgument<T> extends Argument {
+    // Indicates whether argument values should be treated in a
+    // case-sensitive manner.
+    private final boolean caseSensitive;
+
+    // The set of values that will be allowed for use with this argument.
+    private final Collection<T> allowedValues;
+
+    /**
+     * Creates a new string argument with the provided information.
+     *
+     * @param name
+     *            The generic name that should be used to refer to this
+     *            argument.
+     * @param shortIdentifier
+     *            The single-character identifier for this argument, or
+     *            <CODE>null</CODE> if there is none.
+     * @param longIdentifier
+     *            The long identifier for this argument, or <CODE>null</CODE> if
+     *            there is none.
+     * @param isRequired
+     *            Indicates whether this argument must be specified on the
+     *            command line.
+     * @param isMultiValued
+     *            Indicates whether this argument may be specified more than
+     *            once to provide multiple values.
+     * @param needsValue
+     *            Indicates whether this argument requires a value.
+     * @param valuePlaceholder
+     *            The placeholder for the argument value that will be displayed
+     *            in usage information, or <CODE>null</CODE> if this argument
+     *            does not require a value.
+     * @param defaultValue
+     *            The default value that should be used for this argument if
+     *            none is provided in a properties file or on the command line.
+     *            This may be <CODE>null</CODE> if there is no generic default.
+     * @param propertyName
+     *            The name of the property in a property file that may be used
+     *            to override the default value but will be overridden by a
+     *            command-line argument.
+     * @param allowedValues
+     *            The set of values that are allowed for use for this argument.
+     *            If they are not to be treated in a case-sensitive value then
+     *            they should all be formatted in lowercase.
+     * @param caseSensitive
+     *            Indicates whether the set of allowed values should be treated
+     *            in a case-sensitive manner.
+     * @param description
+     *            LocalizableMessage for the description of this argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to
+     *             create this argument.
+     */
+    public MultiChoiceArgument(final String name, final Character shortIdentifier,
+            final String longIdentifier, final boolean isRequired, final boolean isMultiValued,
+            final boolean needsValue, final LocalizableMessage valuePlaceholder,
+            final String defaultValue, final String propertyName,
+            final Collection<T> allowedValues, final boolean caseSensitive,
+            final LocalizableMessage description) throws ArgumentException {
+        super(name, shortIdentifier, longIdentifier, isRequired, isMultiValued, needsValue,
+                valuePlaceholder, defaultValue, propertyName, description);
+
+        this.allowedValues = allowedValues;
+        this.caseSensitive = caseSensitive;
+    }
+
+    /**
+     * Creates a new string argument with the provided information.
+     *
+     * @param name
+     *            The generic name that should be used to refer to this
+     *            argument.
+     * @param shortIdentifier
+     *            The single-character identifier for this argument, or
+     *            <CODE>null</CODE> if there is none.
+     * @param longIdentifier
+     *            The long identifier for this argument, or <CODE>null</CODE> if
+     *            there is none.
+     * @param isRequired
+     *            Indicates whether this argument must be specified on the
+     *            command line.
+     * @param needsValue
+     *            Indicates whether this argument requires a value.
+     * @param valuePlaceholder
+     *            The placeholder for the argument value that will be displayed
+     *            in usage information, or <CODE>null</CODE> if this argument
+     *            does not require a value.
+     * @param allowedValues
+     *            The set of values that are allowed for use for this argument.
+     *            If they are not to be treated in a case-sensitive value then
+     *            they should all be formatted in lowercase.
+     * @param caseSensitive
+     *            Indicates whether the set of allowed values should be treated
+     *            in a case-sensitive manner.
+     * @param description
+     *            LocalizableMessage for the description of this argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to
+     *             create this argument.
+     */
+    public MultiChoiceArgument(final String name, final Character shortIdentifier,
+            final String longIdentifier, final boolean isRequired, final boolean needsValue,
+            final LocalizableMessage valuePlaceholder, final Collection<T> allowedValues,
+            final boolean caseSensitive, final LocalizableMessage description)
+            throws ArgumentException {
+        super(name, shortIdentifier, longIdentifier, isRequired, false, needsValue,
+                valuePlaceholder, null, null, description);
+
+        this.allowedValues = allowedValues;
+        this.caseSensitive = caseSensitive;
+    }
+
+    /**
+     * Retrieves the set of allowed values for this argument. The contents of
+     * this set must not be altered by the caller.
+     *
+     * @return The set of allowed values for this argument.
+     */
+    public Collection<T> getAllowedValues() {
+        return allowedValues;
+    }
+
+    /**
+     * Retrieves the string vale for this argument. If it has multiple values,
+     * then the first will be returned. If it does not have any values, then the
+     * default value will be returned.
+     *
+     * @return The string value for this argument, or <CODE>null</CODE> if there
+     *         are no values and no default value has been given.
+     * @throws ArgumentException
+     *             The value cannot be parsed.
+     */
+    public T getTypedValue() throws ArgumentException {
+        final String v = super.getValue();
+        if (v == null) {
+            return null;
+        }
+        for (final T o : allowedValues) {
+            if ((caseSensitive && o.toString().equals(v)) || o.toString().equalsIgnoreCase(v)) {
+                return o;
+            }
+        }
+        // TODO: Some message
+        throw new ArgumentException(null);
+    }
+
+    /**
+     * Indicates whether the set of allowed values for this argument should be
+     * treated in a case-sensitive manner.
+     *
+     * @return <CODE>true</CODE> if the values are to be treated in a
+     *         case-sensitive manner, or <CODE>false</CODE> if not.
+     */
+    public boolean isCaseSensitive() {
+        return caseSensitive;
+    }
+
+    /**
+     * Specifies the default value that will be used for this argument if it is
+     * not specified on the command line and it is not set from a properties
+     * file.
+     *
+     * @param defaultValue
+     *            The default value that will be used for this argument if it is
+     *            not specified on the command line and it is not set from a
+     *            properties file.
+     */
+    public void setDefaultValue(final T defaultValue) {
+        super.setDefaultValue(defaultValue.toString());
+    }
+
+    /**
+     * Indicates whether the provided value is acceptable for use in this
+     * argument.
+     *
+     * @param valueString
+     *            The value for which to make the determination.
+     * @param invalidReason
+     *            A buffer into which the invalid reason may be written if the
+     *            value is not acceptable.
+     * @return <CODE>true</CODE> if the value is acceptable, or
+     *         <CODE>false</CODE> if it is not.
+     */
+    @Override
+    public boolean valueIsAcceptable(final String valueString,
+            final LocalizableMessageBuilder invalidReason) {
+        for (final T o : allowedValues) {
+            if ((caseSensitive && o.toString().equals(valueString))
+                    || o.toString().equalsIgnoreCase(valueString)) {
+                return true;
+            }
+        }
+        invalidReason.append(ERR_MCARG_VALUE_NOT_ALLOWED.get(getName(), valueString));
+
+        return false;
+    }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java
new file mode 100644
index 0000000..c25f19d
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java
@@ -0,0 +1,483 @@
+/*
+ * 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 2010 Sun Microsystems, Inc.
+ *      Portions copyright 2012-2014 ForgeRock AS.
+ */
+package com.forgerock.opendj.cli;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * Utility class for printing aligned columns of text.
+ * <P>
+ * This class allows you to specify:
+ * <UL>
+ * <LI>The number of columns in the output. This will determine the dimension of
+ * the string arrays passed to add(String[]) or addTitle(String[]).
+ * <LI>spacing/gap between columns
+ * <LI>character to use for title border (null means no border)
+ * <LI>column alignment. Only LEFT/CENTER is supported for now.
+ * </UL>
+ * <P>
+ * Example usage:
+ *
+ * <PRE>
+ * MyPrinter mp = new MyPrinter(3, 2, &quot;-&quot;);
+ * String oneRow[] = new String[3];
+ * oneRow[0] = &quot;User Name&quot;;
+ * oneRow[1] = &quot;Email Address&quot;;
+ * oneRow[2] = &quot;Phone Number&quot;;
+ * mp.addTitle(oneRow);
+ * oneRow[0] = &quot;Bob&quot;;
+ * oneRow[1] = &quot;bob@foo.com&quot;;
+ * oneRow[2] = &quot;123-4567&quot;;
+ * mp.add(oneRow);
+ * oneRow[0] = &quot;John&quot;;
+ * oneRow[1] = &quot;john@foo.com&quot;;
+ * oneRow[2] = &quot;456-7890&quot;;
+ * mp.add(oneRow);
+ * mp.print();
+ * </PRE>
+ * <P>
+ * The above would print:
+ * <P>
+ *
+ * <PRE>
+ *  --------------------------------------
+ *  User Name  Email Address  Phone Number
+ *  --------------------------------------
+ *  Bob        bob@foo.com    123-4567
+ *  John       john@foo.com   456-7890
+ * </PRE>
+ * <P>
+ * This class also supports multi-row titles and having title strings spanning
+ * multiple columns. Example usage:
+ *
+ * <PRE>
+ * TestPrinter tp = new TestPrinter(4, 2, &quot;-&quot;);
+ * String oneRow[] = new String[4];
+ * int[] span = new int[4];
+ * span[0] = 2; // spans 2 columns
+ * span[1] = 0; // spans 0 columns
+ * span[2] = 2; // spans 2 columns
+ * span[3] = 0; // spans 0 columns
+ * tp.setTitleAlign(CENTER);
+ * oneRow[0] = &quot;Name&quot;;
+ * oneRow[1] = &quot;&quot;;
+ * oneRow[2] = &quot;Contact&quot;;
+ * oneRow[3] = &quot;&quot;;
+ * tp.addTitle(oneRow, span);
+ * oneRow[0] = &quot;First&quot;;
+ * oneRow[1] = &quot;Last&quot;;
+ * oneRow[2] = &quot;Email&quot;;
+ * oneRow[3] = &quot;Phone&quot;;
+ * tp.addTitle(oneRow);
+ * oneRow[0] = &quot;Bob&quot;;
+ * oneRow[1] = &quot;Jones&quot;;
+ * oneRow[2] = &quot;bob@foo.com&quot;;
+ * oneRow[3] = &quot;123-4567&quot;;
+ * tp.add(oneRow);
+ * oneRow[0] = &quot;John&quot;;
+ * oneRow[1] = &quot;Doe&quot;;
+ * oneRow[2] = &quot;john@foo.com&quot;;
+ * oneRow[3] = &quot;456-7890&quot;;
+ * tp.add(oneRow);
+ * tp.println();
+ * </PRE>
+ * <P>
+ * The above would print:
+ * <P>
+ *
+ * <PRE>
+ *      ------------------------------------
+ *          Name             Contact
+ *      First  Last      Email       Phone
+ *      ------------------------------------
+ *      Bob    Jones  bob@foo.com   123-4567
+ *      John   Doe    john@foo.com  456-7890
+ * </PRE>
+ */
+public final class MultiColumnPrinter {
+
+    /**
+     * Left ID.
+     */
+    static public final int LEFT = 0;
+
+    /**
+     * Center ID.
+     */
+    static public final int CENTER = 1;
+
+    /**
+     * Right ID.
+     */
+    static public final int RIGHT = 2;
+
+    private int numCol = 2;
+
+    private int gap = 4;
+
+    private int align = CENTER;
+
+    private int titleAlign = CENTER;
+
+    private String border = null;
+
+    private List<String[]> titleTable = null;
+
+    private List<int[]> titleSpanTable = null;
+
+    private final int[] curLength;
+
+    private final ConsoleApplication app;
+
+    /**
+     * Creates a sorted new MultiColumnPrinter class using LEFT alignment and
+     * with no title border.
+     *
+     * @param numCol
+     *            number of columns
+     * @param gap
+     *            gap between each column
+     * @param app
+     *            the console application to use for outputting data
+     */
+    public MultiColumnPrinter(final int numCol, final int gap, final ConsoleApplication app) {
+        this(numCol, gap, null, LEFT, app);
+    }
+
+    /**
+     * Creates a sorted new MultiColumnPrinter class using LEFT alignment.
+     *
+     * @param numCol
+     *            number of columns
+     * @param gap
+     *            gap between each column
+     * @param border
+     *            character used to frame the titles
+     * @param app
+     *            the console application to use for outputting data
+     */
+    public MultiColumnPrinter(final int numCol, final int gap, final String border,
+            final ConsoleApplication app) {
+        this(numCol, gap, border, LEFT, app);
+    }
+
+    /**
+     * Creates a new MultiColumnPrinter class.
+     *
+     * @param numCol
+     *            number of columns
+     * @param gap
+     *            gap between each column
+     * @param border
+     *            character used to frame the titles
+     * @param align
+     *            type of alignment within columns
+     * @param app
+     *            the console application to use for outputting data
+     */
+    public MultiColumnPrinter(final int numCol, final int gap, final String border, final int align,
+            final ConsoleApplication app) {
+
+        titleTable = new Vector<String[]>();
+        titleSpanTable = new Vector<int[]>();
+        curLength = new int[numCol];
+
+        this.numCol = numCol;
+        this.gap = gap;
+        this.border = border;
+        this.align = align;
+        this.titleAlign = LEFT;
+
+        this.app = app;
+    }
+
+    /**
+     * Adds to the row of strings to be used as the title for the table.
+     *
+     * @param row
+     *            Array of strings to print in one row of title.
+     */
+    public void addTitle(final String[] row) {
+        if (row == null) {
+            return;
+        }
+
+        final int[] span = new int[row.length];
+        for (int i = 0; i < row.length; i++) {
+            span[i] = 1;
+        }
+
+        addTitle(row, span);
+    }
+
+    /**
+     * Adds to the row of strings to be used as the title for the table. Also
+     * allows for certain title strings to span multiple columns The span
+     * parameter is an array of integers which indicate how many columns the
+     * corresponding title string will occupy. For a row that is 4 columns
+     * wide, it is possible to have some title strings in a row to 'span'
+     * multiple columns:
+     * <P>
+     *
+     * <PRE>
+     * ------------------------------------
+     *     Name             Contact
+     * First  Last      Email       Phone
+     * ------------------------------------
+     * Bob    Jones  bob@foo.com   123-4567
+     * John   Doe    john@foo.com  456-7890
+     * </PRE>
+     *
+     * In the example above, the title row has a string 'Name' that spans 2
+     * columns. The string 'Contact' also spans 2 columns. The above is done
+     * by passing in to addTitle() an array that contains:
+     *
+     * <PRE>
+     * span[0] = 2; // spans 2 columns
+     * span[1] = 0; // spans 0 columns, ignore
+     * span[2] = 2; // spans 2 columns
+     * span[3] = 0; // spans 0 columns, ignore
+     * </PRE>
+     * <P>
+     * A span value of 1 is the default. The method addTitle(String[] row)
+     * basically does:
+     *
+     * <PRE>
+     * int[] span = new int[row.length];
+     * for (int i = 0; i &lt; row.length; i++) {
+     *     span[i] = 1;
+     * }
+     * addTitle(row, span);
+     * </PRE>
+     *
+     * @param row
+     *            Array of strings to print in one row of title.
+     * @param span
+     *            Array of integers that reflect the number of columns the
+     *            corresponding title string will occupy.
+     */
+    public void addTitle(final String[] row, final int[] span) {
+        // Need to create a new instance of it, otherwise the new values
+        // will always overwrite the old values.
+
+        final String[] rowInstance = new String[row.length];
+        for (int i = 0; i < row.length; i++) {
+            rowInstance[i] = row[i];
+        }
+        titleTable.add(rowInstance);
+
+        titleSpanTable.add(span);
+    }
+
+    /**
+     * Clears title strings.
+     */
+    public void clearTitle() {
+        titleTable.clear();
+        titleSpanTable.clear();
+    }
+
+    /**
+     * Adds one row of text to output.
+     *
+     * @param row
+     *            Array of strings to print in one row.
+     */
+    public void printRow(final String... row) {
+        for (int i = 0; i < numCol; i++) {
+            if (titleAlign == RIGHT) {
+                final int spaceBefore = curLength[i] - row[i].length();
+                printSpaces(spaceBefore);
+                app.getOutputStream().print(row[i]);
+                if (i < numCol - 1) {
+                    printSpaces(gap);
+                }
+            } else if (align == CENTER) {
+                int space1, space2;
+                space1 = (curLength[i] - row[i].length()) / 2;
+                space2 = curLength[i] - row[i].length() - space1;
+
+                printSpaces(space1);
+                app.getOutputStream().print(row[i]);
+                printSpaces(space2);
+                if (i < numCol - 1) {
+                    printSpaces(gap);
+                }
+            } else {
+                app.getOutputStream().print(row[i]);
+                if (i < numCol - 1) {
+                    printSpaces(curLength[i] - row[i].length() + gap);
+                }
+            }
+        }
+        app.getOutputStream().println("");
+    }
+
+    /**
+     * Prints the table title.
+     */
+    public void printTitle() {
+        // Get the longest string for each column and store in curLength[]
+
+        // Scan through title rows
+        Iterator<int[]> spanEnum = titleSpanTable.iterator();
+        for (String[] row : titleTable) {
+            final int[] curSpan = spanEnum.next();
+
+            for (int i = 0; i < numCol; i++) {
+                // None of the fields should be null, but if it
+                // happens to be so, replace it with "-".
+                if (row[i] == null) {
+                    row[i] = "-";
+                }
+
+                int len = row[i].length();
+
+                /*
+                 * If a title string spans multiple columns, then the space it
+                 * occupies in each column is at most len/span (since we have
+                 * gap to take into account as well).
+                 */
+                final int span = curSpan[i];
+                int rem = 0;
+                if (span > 1) {
+                    rem = len % span;
+                    len = len / span;
+                }
+
+                if (curLength[i] < len) {
+                    curLength[i] = len;
+
+                    if ((span > 1) && ((i + span) <= numCol)) {
+                        for (int j = i + 1; j < (i + span); ++j) {
+                            curLength[j] = len;
+                        }
+
+                        /*
+                         * Add remainder to last column in span to avoid
+                         * round-off errors.
+                         */
+                        curLength[(i + span) - 1] += rem;
+                    }
+                }
+            }
+        }
+
+        printBorder();
+
+        spanEnum = titleSpanTable.iterator();
+        for (String[] row : titleTable) {
+            final int[] curSpan = spanEnum.next();
+
+            for (int i = 0; i < numCol; i++) {
+                int availableSpace = 0;
+                final int span = curSpan[i];
+
+                if (span == 0) {
+                    continue;
+                }
+
+                availableSpace = curLength[i];
+
+                if ((span > 1) && ((i + span) <= numCol)) {
+                    for (int j = i + 1; j < (i + span); ++j) {
+                        availableSpace += gap;
+                        availableSpace += curLength[j];
+                    }
+                }
+
+                if (titleAlign == RIGHT) {
+                    final int spaceBefore = availableSpace - row[i].length();
+                    printSpaces(spaceBefore);
+                    app.getOutputStream().print(row[i]);
+                    if (i < numCol - 1) {
+                        printSpaces(gap);
+                    }
+                } else if (titleAlign == CENTER) {
+                    int spaceBefore, spaceAfter;
+                    spaceBefore = (availableSpace - row[i].length()) / 2;
+                    spaceAfter = availableSpace - row[i].length() - spaceBefore;
+
+                    printSpaces(spaceBefore);
+                    app.getOutputStream().print(row[i]);
+                    printSpaces(spaceAfter);
+                    if (i < numCol - 1) {
+                        printSpaces(gap);
+                    }
+                } else {
+                    app.getOutputStream().print(row[i]);
+                    if (i < numCol - 1) {
+                        printSpaces(availableSpace - row[i].length() + gap);
+                    }
+                }
+
+            }
+            app.getOutputStream().println("");
+        }
+        printBorder();
+    }
+
+    /**
+     * Set alignment for title strings.
+     *
+     * @param titleAlign
+     *            The alignment which should be one of {@code LEFT},
+     *            {@code RIGHT}, or {@code CENTER}.
+     */
+    public void setTitleAlign(final int titleAlign) {
+        this.titleAlign = titleAlign;
+    }
+
+    private void printBorder() {
+        if (border == null) {
+            return;
+        }
+
+        // For the value in each column
+        for (int i = 0; i < numCol; i++) {
+            for (int j = 0; j < curLength[i]; j++) {
+                app.getOutputStream().print(border);
+            }
+        }
+
+        // For the gap between each column
+        for (int i = 0; i < numCol - 1; i++) {
+            for (int j = 0; j < gap; j++) {
+                app.getOutputStream().print(border);
+            }
+        }
+        app.getOutputStream().println("");
+    }
+
+    private void printSpaces(final int count) {
+        for (int i = 0; i < count; ++i) {
+            app.getOutputStream().print(" ");
+        }
+    }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/StringArgument.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/StringArgument.java
new file mode 100644
index 0000000..7c5fb6d
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/StringArgument.java
@@ -0,0 +1,136 @@
+/*
+ * 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 org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.LocalizableMessageBuilder;
+
+/**
+ * This class defines an argument type that will accept any string value.
+ */
+public final class StringArgument extends Argument {
+    /**
+     * Creates a new string argument with the provided information.
+     *
+     * @param name
+     *            The generic name that should be used to refer to this
+     *            argument.
+     * @param shortIdentifier
+     *            The single-character identifier for this argument, or
+     *            <CODE>null</CODE> if there is none.
+     * @param longIdentifier
+     *            The long identifier for this argument, or <CODE>null</CODE> if
+     *            there is none.
+     * @param isRequired
+     *            Indicates whether this argument must be specified on the
+     *            command line.
+     * @param isMultiValued
+     *            Indicates whether this argument may be specified more than
+     *            once to provide multiple values.
+     * @param needsValue
+     *            Indicates whether this argument requires a value.
+     * @param valuePlaceholder
+     *            The placeholder for the argument value that will be displayed
+     *            in usage information, or <CODE>null</CODE> if this argument
+     *            does not require a value.
+     * @param defaultValue
+     *            The default value that should be used for this argument if
+     *            none is provided in a properties file or on the command line.
+     *            This may be <CODE>null</CODE> if there is no generic default.
+     * @param propertyName
+     *            The name of the property in a property file that may be used
+     *            to override the default value but will be overridden by a
+     *            command-line argument.
+     * @param description
+     *            LocalizableMessage for the description of this argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to
+     *             create this argument.
+     */
+    public StringArgument(final String name, final Character shortIdentifier,
+            final String longIdentifier, final boolean isRequired, final boolean isMultiValued,
+            final boolean needsValue, final LocalizableMessage valuePlaceholder,
+            final String defaultValue, final String propertyName,
+            final LocalizableMessage description) throws ArgumentException {
+        super(name, shortIdentifier, longIdentifier, isRequired, isMultiValued, needsValue,
+                valuePlaceholder, defaultValue, propertyName, description);
+    }
+
+    /**
+     * Creates a new string argument with the provided information.
+     *
+     * @param name
+     *            The generic name that should be used to refer to this
+     *            argument.
+     * @param shortIdentifier
+     *            The single-character identifier for this argument, or
+     *            <CODE>null</CODE> if there is none.
+     * @param longIdentifier
+     *            The long identifier for this argument, or <CODE>null</CODE> if
+     *            there is none.
+     * @param isRequired
+     *            Indicates whether this argument must be specified on the
+     *            command line.
+     * @param needsValue
+     *            Indicates whether this argument requires a value.
+     * @param valuePlaceholder
+     *            The placeholder for the argument value that will be displayed
+     *            in usage information, or <CODE>null</CODE> if this argument
+     *            does not require a value.
+     * @param description
+     *            LocalizableMessage for the description of this argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to
+     *             create this argument.
+     */
+    public StringArgument(final String name, final Character shortIdentifier,
+            final String longIdentifier, final boolean isRequired, final boolean needsValue,
+            final LocalizableMessage valuePlaceholder, final LocalizableMessage description)
+            throws ArgumentException {
+        super(name, shortIdentifier, longIdentifier, isRequired, false, needsValue,
+                valuePlaceholder, null, null, description);
+    }
+
+    /**
+     * Indicates whether the provided value is acceptable for use in this
+     * argument.
+     *
+     * @param valueString
+     *            The value for which to make the determination.
+     * @param invalidReason
+     *            A buffer into which the invalid reason may be written if the
+     *            value is not acceptable.
+     * @return <CODE>true</CODE> if the value is acceptable, or
+     *         <CODE>false</CODE> if it is not.
+     */
+    @Override
+    public boolean valueIsAcceptable(final String valueString,
+            final LocalizableMessageBuilder invalidReason) {
+        // All values will be acceptable for this argument.
+        return true;
+    }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java
new file mode 100644
index 0000000..a5c7012
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java
@@ -0,0 +1,336 @@
+/*
+ * 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-2010 Sun Microsystems, Inc.
+ *      Portions copyright 2014 ForgeRock AS
+ */
+package com.forgerock.opendj.cli;
+
+import static com.forgerock.opendj.cli.CliMessages.*;
+import static com.forgerock.opendj.util.StaticUtils.EOL;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.StringTokenizer;
+
+import org.forgerock.i18n.LocalizableMessage;
+
+/**
+ * This class provides utility functions for all the client side tools.
+ */
+final public class Utils {
+    /**
+     * The name of a command-line script used to launch a tool.
+     */
+    public static final String PROPERTY_SCRIPT_NAME = "com.forgerock.opendj.ldap.tools.scriptName";
+
+    /**
+     * The column at which to wrap long lines of output in the command-line
+     * tools.
+     */
+    static public final int MAX_LINE_WIDTH;
+
+    static {
+        int columns = 80;
+        try {
+            final String s = System.getenv("COLUMNS");
+            if (s != null) {
+                columns = Integer.parseInt(s);
+            }
+        } catch (final Exception e) {
+            // Do nothing.
+        }
+        MAX_LINE_WIDTH = columns - 1;
+    }
+
+    /**
+     * Filters the provided value to ensure that it is appropriate for use as an
+     * exit code. Exit code values are generally only allowed to be between 0
+     * and 255, so any value outside of this range will be converted to 255,
+     * which is the typical exit code used to indicate an overflow value.
+     *
+     * @param exitCode
+     *            The exit code value to be processed.
+     * @return An integer value between 0 and 255, inclusive. If the provided
+     *         exit code was already between 0 and 255, then the original value
+     *         will be returned. If the provided value was out of this range,
+     *         then 255 will be returned.
+     */
+    public static int filterExitCode(final int exitCode) {
+        if (exitCode < 0) {
+            return 255;
+        } else if (exitCode > 255) {
+            return 255;
+        } else {
+            return exitCode;
+        }
+    }
+
+    /**
+     * Read the data from the specified file and return it in a byte array.
+     *
+     * @param filePath
+     *            The path to the file that should be read.
+     * @return A byte array containing the contents of the requested file.
+     * @throws IOException
+     *             If a problem occurs while trying to read the specified file.
+     */
+    public static byte[] readBytesFromFile(final String filePath) throws IOException {
+        byte[] val = null;
+        FileInputStream fis = null;
+        try {
+            final File file = new File(filePath);
+            fis = new FileInputStream(file);
+            final long length = file.length();
+            val = new byte[(int) length];
+            // Read in the bytes
+            int offset = 0;
+            int numRead = 0;
+            while (offset < val.length
+                    && (numRead = fis.read(val, offset, val.length - offset)) >= 0) {
+                offset += numRead;
+            }
+
+            // Ensure all the bytes have been read in
+            if (offset < val.length) {
+                throw new IOException("Could not completely read file " + filePath);
+            }
+
+            return val;
+        } finally {
+            if (fis != null) {
+                fis.close();
+            }
+        }
+    }
+
+    /**
+     * Retrieves a user-friendly string that indicates the length of time (in
+     * days, hours, minutes, and seconds) in the specified number of seconds.
+     *
+     * @param numSeconds
+     *            The number of seconds to be converted to a more user-friendly
+     *            value.
+     * @return The user-friendly representation of the specified number of
+     *         seconds.
+     */
+    public static LocalizableMessage secondsToTimeString(final int numSeconds) {
+        if (numSeconds < 60) {
+            // We can express it in seconds.
+            return INFO_TIME_IN_SECONDS.get(numSeconds);
+        } else if (numSeconds < 3600) {
+            // We can express it in minutes and seconds.
+            final int m = numSeconds / 60;
+            final int s = numSeconds % 60;
+            return INFO_TIME_IN_MINUTES_SECONDS.get(m, s);
+        } else if (numSeconds < 86400) {
+            // We can express it in hours, minutes, and seconds.
+            final int h = numSeconds / 3600;
+            final int m = (numSeconds % 3600) / 60;
+            final int s = numSeconds % 3600 % 60;
+            return INFO_TIME_IN_HOURS_MINUTES_SECONDS.get(h, m, s);
+        } else {
+            // We can express it in days, hours, minutes, and seconds.
+            final int d = numSeconds / 86400;
+            final int h = (numSeconds % 86400) / 3600;
+            final int m = (numSeconds % 86400 % 3600) / 60;
+            final int s = numSeconds % 86400 % 3600 % 60;
+            return INFO_TIME_IN_DAYS_HOURS_MINUTES_SECONDS.get(d, h, m, s);
+        }
+    }
+
+    /**
+     * Inserts line breaks into the provided buffer to wrap text at no more than
+     * the specified column width. Wrapping will only be done at space
+     * boundaries and if there are no spaces within the specified width, then
+     * wrapping will be performed at the first space after the specified column.
+     *
+     * @param message
+     *            The message to be wrapped.
+     * @param width
+     *            The maximum number of characters to allow on a line if there
+     *            is a suitable breaking point.
+     * @return The wrapped text.
+     */
+    public static String wrapText(final LocalizableMessage message, final int width) {
+        return wrapText(message.toString(), width, 0);
+    }
+
+    /**
+     * Inserts line breaks into the provided buffer to wrap text at no more than
+     * the specified column width. Wrapping will only be done at space
+     * boundaries and if there are no spaces within the specified width, then
+     * wrapping will be performed at the first space after the specified column.
+     * In addition each line will be indented by the specified amount.
+     *
+     * @param message
+     *            The message to be wrapped.
+     * @param width
+     *            The maximum number of characters to allow on a line if there
+     *            is a suitable breaking point (including any indentation).
+     * @param indent
+     *            The number of columns to indent each line.
+     * @return The wrapped text.
+     */
+    public static String wrapText(final LocalizableMessage message, final int width, final int indent) {
+        return wrapText(message.toString(), width, indent);
+    }
+
+    /**
+     * Inserts line breaks into the provided buffer to wrap text at no more than
+     * the specified column width. Wrapping will only be done at space
+     * boundaries and if there are no spaces within the specified width, then
+     * wrapping will be performed at the first space after the specified column.
+     *
+     * @param text
+     *            The text to be wrapped.
+     * @param width
+     *            The maximum number of characters to allow on a line if there
+     *            is a suitable breaking point.
+     * @return The wrapped text.
+     */
+    public static String wrapText(final String text, final int width) {
+        return wrapText(text, width, 0);
+    }
+
+    /**
+     * Inserts line breaks into the provided buffer to wrap text at no more than
+     * the specified column width. Wrapping will only be done at space
+     * boundaries and if there are no spaces within the specified width, then
+     * wrapping will be performed at the first space after the specified column.
+     * In addition each line will be indented by the specified amount.
+     *
+     * @param text
+     *            The text to be wrapped.
+     * @param width
+     *            The maximum number of characters to allow on a line if there
+     *            is a suitable breaking point (including any indentation).
+     * @param indent
+     *            The number of columns to indent each line.
+     * @return The wrapped text.
+     */
+    public static String wrapText(final String text, int width, final int indent) {
+        // Calculate the real width and indentation padding.
+        width -= indent;
+        final StringBuilder pb = new StringBuilder();
+        for (int i = 0; i < indent; i++) {
+            pb.append(' ');
+        }
+        final String padding = pb.toString();
+
+        final StringBuilder buffer = new StringBuilder();
+        if (text != null) {
+            final StringTokenizer lineTokenizer = new StringTokenizer(text, "\r\n", true);
+            while (lineTokenizer.hasMoreTokens()) {
+                final String line = lineTokenizer.nextToken();
+                if (line.equals("\r") || line.equals("\n")) {
+                    // It's an end-of-line character, so append it as-is.
+                    buffer.append(line);
+                } else if (line.length() <= width) {
+                    // The line fits in the specified width, so append it as-is.
+                    buffer.append(padding);
+                    buffer.append(line);
+                } else {
+                    // The line doesn't fit in the specified width, so it needs
+                    // to be wrapped. Do so at space boundaries.
+                    StringBuilder lineBuffer = new StringBuilder();
+                    StringBuilder delimBuffer = new StringBuilder();
+                    final StringTokenizer wordTokenizer = new StringTokenizer(line, " ", true);
+                    while (wordTokenizer.hasMoreTokens()) {
+                        final String word = wordTokenizer.nextToken();
+                        if (word.equals(" ")) {
+                            // It's a space, so add it to the delim buffer only
+                            // if the line buffer is not empty.
+                            if (lineBuffer.length() > 0) {
+                                delimBuffer.append(word);
+                            }
+                        } else if (word.length() > width) {
+                            // This is a long word that can't be wrapped, so
+                            // we'll just have to make do.
+                            if (lineBuffer.length() > 0) {
+                                buffer.append(padding);
+                                buffer.append(lineBuffer);
+                                buffer.append(EOL);
+                                lineBuffer = new StringBuilder();
+                            }
+                            buffer.append(padding);
+                            buffer.append(word);
+
+                            if (wordTokenizer.hasMoreTokens()) {
+                                // The next token must be a space, so remove it.
+                                // If there are still more tokens after that, then append an
+                                // EOL.
+                                wordTokenizer.nextToken();
+                                if (wordTokenizer.hasMoreTokens()) {
+                                    buffer.append(EOL);
+                                }
+                            }
+
+                            if (delimBuffer.length() > 0) {
+                                delimBuffer = new StringBuilder();
+                            }
+                        } else {
+                            // It's not a space, so see if we can fit it on the
+                            // current line.
+                            final int newLineLength =
+                                    lineBuffer.length() + delimBuffer.length() + word.length();
+                            if (newLineLength < width) {
+                                // It does fit on the line, so add it.
+                                lineBuffer.append(delimBuffer).append(word);
+
+                                if (delimBuffer.length() > 0) {
+                                    delimBuffer = new StringBuilder();
+                                }
+                            } else {
+                                // It doesn't fit on the line, so end the
+                                // current line and start a new one.
+                                buffer.append(padding);
+                                buffer.append(lineBuffer);
+                                buffer.append(EOL);
+
+                                lineBuffer = new StringBuilder();
+                                lineBuffer.append(word);
+
+                                if (delimBuffer.length() > 0) {
+                                    delimBuffer = new StringBuilder();
+                                }
+                            }
+                        }
+                    }
+
+                    // If there's anything left in the line buffer, then add it
+                    // to the final buffer.
+                    buffer.append(padding);
+                    buffer.append(lineBuffer);
+                }
+            }
+        }
+        return buffer.toString();
+    }
+
+    // Prevent instantiation.
+    private Utils() {
+        // Do nothing.
+    }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/package-info.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/package-info.java
new file mode 100644
index 0000000..a6ff22a
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/package-info.java
@@ -0,0 +1,29 @@
+/*
+ * 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 2014 ForgeRock AS.
+ */
+/**
+ * Classes implementing the OpenDJ CLI shared APIs.
+ */
+package com.forgerock.opendj.cli;
diff --git a/opendj-cli/src/main/java/org/forgerock/opendj/cli/package-info.java b/opendj-cli/src/main/java/org/forgerock/opendj/cli/package-info.java
index 53090ca..c6d2414 100644
--- a/opendj-cli/src/main/java/org/forgerock/opendj/cli/package-info.java
+++ b/opendj-cli/src/main/java/org/forgerock/opendj/cli/package-info.java
@@ -23,7 +23,6 @@
  *
  *      Copyright 2014 ForgeRock AS.
  */
-
 /**
  * Classes implementing the OpenDJ CLI shared APIs.
  */
diff --git a/opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties b/opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties
index d330757..bfb49e9 100755
--- a/opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties
+++ b/opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties
@@ -26,4 +26,478 @@
 #
 # CLI messages
 #
-ERR_CONSOLE_INPUT_ERROR=The response could not be read from the console due to the following error: %s
\ No newline at end of file
+ERR_ARG_NO_IDENTIFIER=The %s argument does not have either a \
+ single-character or a long identifier that may be used to specify it.  At \
+ least one of these must be specified for each argument
+ERR_ARG_NO_VALUE_PLACEHOLDER=The %s argument is configured to take \
+ a value but no value placeholder has been defined for it
+ERR_ARG_NO_INT_VALUE=The %s argument does not have any value that \
+ may be retrieved as an integer
+ERR_ARG_CANNOT_DECODE_AS_INT=The provided value "%s" for the %s \
+ argument cannot be decoded as an integer
+ERR_ARG_INT_MULTIPLE_VALUES=The %s argument has multiple values and \
+ therefore cannot be decoded as a single integer value
+ERR_ARG_NO_BOOLEAN_VALUE=The %s argument does not have any value \
+ that may be retrieved as a Boolean
+ERR_ARG_CANNOT_DECODE_AS_BOOLEAN=The provided value "%s" for the %s \
+ argument cannot be decoded as a Boolean
+ERR_ARG_BOOLEAN_MULTIPLE_VALUES=The %s argument has multiple values \
+ and therefore cannot be decoded as a single Boolean value
+ERR_INTARG_LOWER_BOUND_ABOVE_UPPER_BOUND=The %s argument \
+ configuration is invalid because the lower bound of %d is greater than the \
+ upper bound of %d
+ERR_INTARG_VALUE_BELOW_LOWER_BOUND=The provided %s value %d is \
+ unacceptable because it is below the lower bound of %d
+ERR_INTARG_VALUE_ABOVE_UPPER_BOUND=The provided %s value %d is \
+ unacceptable because it is above the upper bound of %d
+ERR_BOOLEANARG_NO_VALUE_ALLOWED=The provided %s value is \
+ unacceptable because Boolean arguments are never allowed to have values
+ERR_MCARG_VALUE_NOT_ALLOWED=The provided %s value %s is \
+ unacceptable because it is not included in the set of allowed values for that \
+ argument
+ERR_FILEARG_NO_SUCH_FILE=The file %s specified for argument %s does \
+ not exist
+ERR_FILEARG_CANNOT_VERIFY_FILE_EXISTENCE=An error occurred while \
+ trying to verify the existence of file %s specified for argument %s:  %s
+ERR_FILEARG_CANNOT_OPEN_FILE=An error occurred while trying to open \
+ file %s specified for argument %s for reading:  %s
+ERR_FILEARG_CANNOT_READ_FILE=An error occurred while trying to read \
+ from file %s specified for argument %s:  %s
+ERR_FILEARG_EMPTY_FILE=The file %s specified for argument %s exists \
+ but is empty
+ERR_ARGPARSER_DUPLICATE_SHORT_ID=Cannot add argument %s to the \
+ argument list because its short identifier -%s conflicts with the %s argument \
+ that has already been defined
+ERR_ARGPARSER_DUPLICATE_LONG_ID=Cannot add argument %s to the \
+ argument list because its long identifier --%s conflicts with the %s argument \
+ that has already been defined
+ERR_ARGPARSER_CANNOT_READ_PROPERTIES_FILE=An error occurred while \
+ attempting to read the contents of the argument properties file %s:  %s
+ERR_ARGPARSER_TOO_MANY_TRAILING_ARGS=The provided set of \
+ command-line arguments contained too many unnamed trailing arguments.  The \
+ maximum number of allowed trailing arguments is %d
+ERR_ARGPARSER_LONG_ARG_WITHOUT_NAME=The provided argument "%s" is \
+ invalid because it does not include the argument name
+ERR_ARGPARSER_NO_ARGUMENT_WITH_LONG_ID=Argument --%s is not allowed \
+ for use with this program
+ERR_ARGPARSER_NO_VALUE_FOR_ARGUMENT_WITH_LONG_ID=Argument --%s \
+ requires a value but none was provided
+ERR_ARGPARSER_VALUE_UNACCEPTABLE_FOR_LONG_ID=The provided value \
+ "%s" for argument --%s is not acceptable:  %s
+ERR_ARGPARSER_NOT_MULTIVALUED_FOR_LONG_ID=The argument --%s was \
+ included multiple times in the provided set of arguments but it does not \
+ allow multiple values
+ERR_ARGPARSER_ARG_FOR_LONG_ID_DOESNT_TAKE_VALUE=A value was \
+ provided for argument --%s but that argument does not take a value
+ERR_ARGPARSER_INVALID_DASH_AS_ARGUMENT=The dash character by itself \
+ is invalid for use as an argument name
+ERR_ARGPARSER_NO_ARGUMENT_WITH_SHORT_ID=Argument -%s is not allowed \
+ for use with this program
+ERR_ARGPARSER_NO_VALUE_FOR_ARGUMENT_WITH_SHORT_ID=Argument -%s \
+ requires a value but none was provided
+ERR_ARGPARSER_VALUE_UNACCEPTABLE_FOR_SHORT_ID=The provided value \
+ "%s" for argument -%s is not acceptable:  %s
+ERR_ARGPARSER_NOT_MULTIVALUED_FOR_SHORT_ID=The argument -%s was \
+ included multiple times in the provided set of arguments but it does not \
+ allow multiple values
+ERR_ARGPARSER_CANT_MIX_ARGS_WITH_VALUES=The provided argument block \
+ '-%s%s' is illegal because the '%s' argument requires a value but is in the \
+ same block as at least one other argument that doesn't require a value
+ERR_ARGPARSER_DISALLOWED_TRAILING_ARGUMENT=Argument "%s" does not \
+ start with one or two dashes and unnamed trailing arguments are not allowed
+ERR_ARGPARSER_TOO_FEW_TRAILING_ARGUMENTS=At least %d unnamed \
+ trailing arguments are required in the argument list, but too few were \
+ provided
+ERR_ARGPARSER_NO_VALUE_FOR_REQUIRED_ARG=The argument %s is required \
+ to have a value but none was provided in the argument list and no default \
+ value is available
+INFO_TIME_IN_SECONDS=%d seconds
+INFO_TIME_IN_MINUTES_SECONDS=%d minutes, %d seconds
+INFO_TIME_IN_HOURS_MINUTES_SECONDS=%d hours, %d minutes, %d seconds
+INFO_TIME_IN_DAYS_HOURS_MINUTES_SECONDS=%d days, %d hours, %d minutes, %d \
+ seconds
+INFO_SUBCMDPARSER_WHERE_OPTIONS_INCLUDE=Command options:
+ERR_MENU_BAD_CHOICE_SINGLE=Invalid response. Please enter a valid \
+menu option
+INFO_MENU_PROMPT_SINGLE=Enter choice:
+INFO_MENU_PROMPT_RETURN_TO_CONTINUE=Press RETURN to continue
+ERR_CONSOLE_INPUT_ERROR=The response could not be read from the console due to the following error: %s
+INFO_LDAP_CONN_PROMPT_SECURITY_SERVER_CERTIFICATE=Server Certificate:
+INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE=%s
+INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION=Do you trust this server certificate?
+INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_NO=No
+INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_SESSION=Yes, for this session only
+INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_ALWAYS=Yes, also add it to a truststore
+INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_DETAILS=View certificate details
+INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_USER_DN=User DN  : %s
+INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_VALIDITY=Validity : From '%s'%n             To '%s'
+INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_ISSUER=Issuer   : %s
+INFO_PROMPT_SINGLE_DEFAULT=%s [%s]:
+INFO_ARGPARSER_USAGE_JAVA_CLASSNAME=Usage:  java %s  {options}
+INFO_ARGPARSER_USAGE_JAVA_SCRIPTNAME=Usage:  %s  {options}
+INFO_ARGPARSER_USAGE_TRAILINGARGS={trailing-arguments}
+INFO_ARGPARSER_USAGE_DEFAULT_VALUE=Default value: %s
+#
+# Extension messages
+#
+#
+# Tools messages
+#
+ERR_CANNOT_INITIALIZE_ARGS=An unexpected error occurred while \
+ attempting to initialize the command-line arguments:  %s
+ERR_ERROR_PARSING_ARGS=An error occurred while parsing the \
+ command-line arguments:  %s
+INFO_PROCESSING_OPERATION=Processing %s request for %s
+INFO_OPERATION_FAILED=%s operation failed
+INFO_OPERATION_SUCCESSFUL=%s operation successful for DN %s
+INFO_PROCESSING_COMPARE_OPERATION=Comparing type %s with value %s in \
+ entry %s
+INFO_COMPARE_OPERATION_RESULT_FALSE=Compare operation returned false for \
+ entry %s
+INFO_COMPARE_OPERATION_RESULT_TRUE=Compare operation returned true for \
+ entry %s
+INFO_DESCRIPTION_TRUSTALL=Trust all server SSL certificates
+INFO_DESCRIPTION_BINDDN=DN to use to bind to the server
+INFO_DESCRIPTION_BINDPASSWORD=Password to use to bind to \
+ the server
+INFO_DESCRIPTION_BINDPASSWORDFILE=Bind password file
+INFO_DESCRIPTION_ENCODING=Use the specified character set for \
+ command-line input
+INFO_DESCRIPTION_VERBOSE=Use verbose mode
+INFO_DESCRIPTION_KEYSTOREPATH=Certificate key store path
+INFO_DESCRIPTION_TRUSTSTOREPATH=Certificate trust store path
+INFO_DESCRIPTION_KEYSTOREPASSWORD=Certificate key store PIN
+INFO_DESCRIPTION_HOST=Directory server hostname or IP address
+INFO_DESCRIPTION_PORT=Directory server port number
+INFO_DESCRIPTION_SHOWUSAGE=Display this usage information
+INFO_DESCRIPTION_CONTROLS=Use a request control with the provided \
+ information
+INFO_DESCRIPTION_CONTINUE_ON_ERROR=Continue processing even if there are \
+ errors
+INFO_DESCRIPTION_USE_SSL=Use SSL for secure communication with the server
+INFO_DESCRIPTION_START_TLS=Use StartTLS to secure communication with the \
+ server
+INFO_MODIFY_DESCRIPTION_DEFAULT_ADD=Treat records with no changetype as \
+ add operations
+INFO_SEARCH_DESCRIPTION_BASEDN=Search base DN
+INFO_SEARCH_DESCRIPTION_SIZE_LIMIT=Maximum number of entries to return \
+ from the search
+INFO_SEARCH_DESCRIPTION_TIME_LIMIT=Maximum length of time in seconds to \
+ allow for the search
+INFO_SEARCH_DESCRIPTION_SEARCH_SCOPE=Search scope ('base', 'one', 'sub', \
+ or 'subordinate')
+INFO_SEARCH_DESCRIPTION_DEREFERENCE_POLICY=Alias dereference policy \
+ ('never', 'always', 'search', or 'find')
+ERR_LDAPAUTH_UNSUPPORTED_SASL_MECHANISM=The requested SASL mechanism \
+ "%s" is not supported by this client
+ERR_LDAPAUTH_SASL_AUTHID_REQUIRED=The "authid" SASL property is \
+ required for use with the %s mechanism
+INFO_DESCRIPTION_VERSION=LDAP protocol version number
+ERR_DESCRIPTION_INVALID_VERSION=Invalid LDAP version number '%s'. \
+ Allowed values are 2 and 3
+ERR_SEARCH_NO_FILTERS=No filters specified for the search request
+INFO_DESCRIPTION_DONT_WRAP=Do not wrap long lines
+INFO_DESCRIPTION_NOOP=Show what would be done but do not perform any \
+ operation
+INFO_DESCRIPTION_TYPES_ONLY=Only retrieve attribute names but not their \
+ values
+INFO_DESCRIPTION_ASSERTION_FILTER=Use the LDAP assertion control with the \
+ provided filter
+ERR_LDAP_ASSERTION_INVALID_FILTER=The search filter provided for the \
+ LDAP assertion control was invalid:  %s
+INFO_DESCRIPTION_PREREAD_ATTRS=Use the LDAP ReadEntry pre-read control
+INFO_DESCRIPTION_POSTREAD_ATTRS=Use the LDAP ReadEntry post-read control
+INFO_LDAPMODIFY_PREREAD_ENTRY=Target entry before the operation:
+INFO_LDAPMODIFY_POSTREAD_ENTRY=Target entry after the operation:
+INFO_DESCRIPTION_PROXY_AUTHZID=Use the proxied authorization control with \
+ the given authorization ID
+INFO_DESCRIPTION_PSEARCH_INFO=Use the persistent search control
+ERR_PSEARCH_MISSING_DESCRIPTOR=The request to use the persistent \
+ search control did not include a descriptor that indicates the options to use \
+ with that control
+ERR_PSEARCH_DOESNT_START_WITH_PS=The persistent search descriptor %s \
+ did not start with the required 'ps' string
+ERR_PSEARCH_INVALID_CHANGE_TYPE=The provided change type value %s is \
+ invalid.  The recognized change types are add, delete, modify, modifydn, and \
+ any
+ERR_PSEARCH_INVALID_CHANGESONLY=The provided changesOnly value %s is \
+ invalid.  Allowed values are 1 to only return matching entries that have \
+ changed since the beginning of the search, or 0 to also include existing \
+ entries that match the search criteria
+ERR_PSEARCH_INVALID_RETURN_ECS=The provided returnECs value %s is \
+ invalid.  Allowed values are 1 to request that the entry change notification \
+ control be included in updated entries, or 0 to exclude the control from \
+ matching entries
+INFO_DESCRIPTION_REPORT_AUTHZID=Use the authorization identity control
+INFO_BIND_AUTHZID_RETURNED=# Bound with authorization ID %s
+INFO_SEARCH_DESCRIPTION_FILENAME=File containing a list of search filter \
+ strings
+INFO_DESCRIPTION_MATCHED_VALUES_FILTER=Use the LDAP matched values \
+ control with the provided filter
+ERR_LDAP_MATCHEDVALUES_INVALID_FILTER=The provided matched values \
+ filter was invalid:  %s
+ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ=An error occurred while \
+ attempting to open the LDIF file %s for reading:  %s
+ERR_LDIF_FILE_CANNOT_OPEN_FOR_WRITE=An error occurred while \
+ attempting to open the LDIF file %s for writing:  %s
+ERR_LDIF_FILE_READ_ERROR=An error occurred while attempting to read \
+ the contents of LDIF file %s:  %s
+INFO_BIND_PASSWORD_EXPIRED=# Your password has expired
+INFO_BIND_PASSWORD_EXPIRING=# Your password will expire in %s
+INFO_BIND_ACCOUNT_LOCKED=# Your account has been locked
+INFO_BIND_MUST_CHANGE_PASSWORD=# You must change your password before any \
+ other operations will be allowed
+INFO_BIND_GRACE_LOGINS_REMAINING=# You have %d grace logins remaining
+INFO_DESCRIPTION_USE_PWP_CONTROL=Use the password policy request control
+INFO_LDAPPWMOD_DESCRIPTION_AUTHZID=Authorization ID for the \
+ user entry whose password should be changed
+INFO_LDAPPWMOD_DESCRIPTION_NEWPW=New password to provide \
+ for the target user
+INFO_LDAPPWMOD_DESCRIPTION_NEWPWFILE=Path to a file \
+ containing the new password to provide for the target user
+INFO_LDAPPWMOD_DESCRIPTION_CURRENTPW=Current password for \
+ the target user
+INFO_LDAPPWMOD_DESCRIPTION_CURRENTPWFILE=Path to a file \
+ containing the current password for the target user
+ERR_LDAPPWMOD_CONFLICTING_ARGS=The %s and %s arguments may not be \
+ provided together
+ERR_LDAPPWMOD_FAILED=The LDAP password modify operation failed: \
+ %d (%s)
+ERR_LDAPPWMOD_FAILURE_ERROR_MESSAGE=Error Message:  %s
+ERR_LDAPPWMOD_FAILURE_MATCHED_DN=Matched DN:  %s
+INFO_LDAPPWMOD_SUCCESSFUL=The LDAP password modify operation was \
+ successful
+INFO_LDAPPWMOD_ADDITIONAL_INFO=Additional Info:  %s
+INFO_LDAPPWMOD_GENERATED_PASSWORD=Generated Password:  %s
+INFO_COMPARE_CANNOT_BASE64_DECODE_ASSERTION_VALUE=The assertion value was \
+ indicated to be base64-encoded, but an error occurred while trying to decode \
+ the value
+INFO_COMPARE_CANNOT_READ_ASSERTION_VALUE_FROM_FILE=Unable to read the \
+ assertion value from the specified file:  %s
+ERR_LDAPCOMPARE_NO_DNS=No entry DNs provided for the compare \
+ operation
+INFO_LDAPCOMPARE_TOOL_DESCRIPTION=This utility can be used to perform \
+ LDAP compare operations in the Directory Server
+INFO_LDAPMODIFY_TOOL_DESCRIPTION=This utility can be used to perform LDAP \
+ modify, add, delete, and modify DN operations in the Directory Server
+INFO_LDAPPWMOD_TOOL_DESCRIPTION=This utility can be used to perform LDAP \
+ password modify operations in the Directory Server
+INFO_LDAPSEARCH_TOOL_DESCRIPTION=This utility can be used to perform LDAP \
+ search operations in the Directory Server
+ERR_TOOL_CONFLICTING_ARGS=You may not provide both the --%s and \
+ the --%s arguments
+ERR_LDAPCOMPARE_NO_ATTR=No attribute was specified to use as the \
+ target for the comparison
+ERR_LDAPCOMPARE_INVALID_ATTR_STRING=Invalid attribute string '%s'. \
+ The attribute string must be in one of the following forms: \
+ 'attribute:value', 'attribute::base64value', or 'attribute:<valueFilePath'
+ERR_TOOL_INVALID_CONTROL_STRING=Invalid control specification '%s'
+ERR_TOOL_SASLEXTERNAL_NEEDS_SSL_OR_TLS=SASL EXTERNAL \
+ authentication may only be requested if SSL or StartTLS is used
+ERR_TOOL_SASLEXTERNAL_NEEDS_KEYSTORE=SASL EXTERNAL authentication \
+ may only be used if a client certificate key store is specified
+INFO_LDAPSEARCH_PSEARCH_CHANGE_TYPE=# Persistent search change type:  %s
+INFO_LDAPSEARCH_PSEARCH_PREVIOUS_DN=# Persistent search previous entry \
+ DN:  %s
+INFO_LDAPSEARCH_ACCTUSABLE_HEADER=# Account Usability Response Control
+INFO_LDAPSEARCH_ACCTUSABLE_IS_USABLE=#   The account is usable
+INFO_LDAPSEARCH_ACCTUSABLE_TIME_UNTIL_EXPIRATION=#   Time until password \
+ expiration:  %s
+INFO_LDAPSEARCH_ACCTUSABLE_NOT_USABLE=#   The account is not usable
+INFO_LDAPSEARCH_ACCTUSABLE_ACCT_INACTIVE=#   The account has been \
+ deactivated
+INFO_LDAPSEARCH_ACCTUSABLE_PW_RESET=#   The password has been reset
+INFO_LDAPSEARCH_ACCTUSABLE_PW_EXPIRED=#   The password has expired
+INFO_LDAPSEARCH_ACCTUSABLE_REMAINING_GRACE=#   Number of grace logins \
+ remaining:  %d
+INFO_LDAPSEARCH_ACCTUSABLE_LOCKED=#   The account is locked
+INFO_LDAPSEARCH_ACCTUSABLE_TIME_UNTIL_UNLOCK=#   Time until the account \
+ is unlocked:  %s
+INFO_DESCRIPTION_KEYSTOREPASSWORD_FILE=Certificate key store PIN file
+INFO_DESCRIPTION_TRUSTSTOREPASSWORD=Certificate trust store PIN
+INFO_DESCRIPTION_TRUSTSTOREPASSWORD_FILE=Certificate trust store PIN file
+INFO_DESCRIPTION_COUNT_ENTRIES=Count the number of entries returned by \
+ the server
+INFO_LDAPSEARCH_MATCHING_ENTRY_COUNT=# Total number of matching entries: \
+ %d
+INFO_DESCRIPTION_SIMPLE_PAGE_SIZE=Use the simple paged results control \
+ with the given page size
+ERR_PAGED_RESULTS_REQUIRES_SINGLE_FILTER=The simple paged results \
+ control may only be used with a single search filter
+INFO_DESCRIPTION_CERT_NICKNAME=Nickname of certificate for SSL client \
+ authentication
+ERR_TOOL_RESULT_CODE=Result Code:  %d (%s)
+ERR_TOOL_ERROR_MESSAGE=Additional Information:  %s
+ERR_TOOL_MATCHED_DN=Matched DN:  %s
+INFO_LDAPMODIFY_DESCRIPTION_FILENAME=LDIF file containing \
+ the changes to apply
+INFO_DESCRIPTION_SORT_ORDER=Sort the results using the provided sort \
+ order
+ERR_LDAP_SORTCONTROL_INVALID_ORDER=The provided sort order was \
+ invalid:  %s
+INFO_DESCRIPTION_VLV=Use the virtual list view control to retrieve the \
+ specified results page
+ERR_LDAPSEARCH_VLV_REQUIRES_SORT=If the --%s argument is provided, \
+ then the --%s argument must also be given
+ERR_LDAPSEARCH_VLV_INVALID_DESCRIPTOR=The provided virtual list view \
+ descriptor was invalid.  It must be a value in the form \
+ 'beforeCount:afterCount:offset:contentCount' (where offset specifies the \
+ index of the target entry and contentCount specifies the estimated total \
+ number of results or zero if it is not known), or \
+ 'beforeCount:afterCount:assertionValue' (where the entry should be the first \
+ entry whose primary sort value is greater than or equal to the provided \
+ assertionValue).  In either case, beforeCount is the number of entries to \
+ return before the target value and afterCount is the number of entries to \
+ return after the target value
+WARN_LDAPSEARCH_SORT_ERROR=# Server-side sort failed:  %s
+INFO_LDAPSEARCH_VLV_TARGET_OFFSET=# VLV Target Offset:  %d
+INFO_LDAPSEARCH_VLV_CONTENT_COUNT=# VLV Content Count:  %d
+WARN_LDAPSEARCH_VLV_ERROR=# Virtual list view processing failed: \
+ %s
+INFO_DESCRIPTION_EFFECTIVERIGHTS_USER=Use geteffectiverights control with \
+ the provided authzid
+INFO_DESCRIPTION_EFFECTIVERIGHTS_ATTR=Specifies geteffectiverights \
+ control specific attribute list
+ERR_EFFECTIVERIGHTS_INVALID_AUTHZID=The authorization ID "%s" \
+ contained in the geteffectiverights control is invalid because it does not \
+ start with "dn:" to indicate a user DN
+INFO_DESCRIPTION_PRODUCT_VERSION=Display Directory Server version \
+ information
+INFO_DESCRIPTION_SCRIPT_FRIENDLY=Use script-friendly mode
+ERR_LDAP_CONN_CANNOT_INITIALIZE_SSL=ERROR:  Unable to perform SSL \
+ initialization:  %s
+ERR_LDAP_CONN_CANNOT_PARSE_SASL_OPTION=ERROR:  The provided SASL \
+ option string "%s" could not be parsed in the form "name=value"
+INFO_LDAP_CONN_DESCRIPTION_SASLOPTIONS=SASL bind options
+INFO_DESCRIPTION_PROP_FILE_PATH=Path to the file containing default \
+  property values used for command line arguments
+INFO_DESCRIPTION_NO_PROP_FILE=No properties file will be \
+  used to get default command line argument values
+INFO_DESCRIPTION_GENERAL_ARGS=General options:
+INFO_DESCRIPTION_IO_ARGS=Utility input/output options:
+INFO_DESCRIPTION_LDAP_CONNECTION_ARGS=LDAP connection options:
+INFO_FILE_PLACEHOLDER={file}
+INFO_KEYSTOREPATH_PLACEHOLDER={keyStorePath}
+INFO_TRUSTSTOREPATH_PLACEHOLDER={trustStorePath}
+INFO_BINDPWD_FILE_PLACEHOLDER={bindPasswordFile}
+INFO_HOST_PLACEHOLDER={host}
+INFO_PORT_PLACEHOLDER={port}
+INFO_BASEDN_PLACEHOLDER={baseDN}
+INFO_BINDDN_PLACEHOLDER={bindDN}
+INFO_BINDPWD_PLACEHOLDER={bindPassword}
+INFO_KEYSTORE_PWD_PLACEHOLDER={keyStorePassword}
+INFO_TRUSTSTORE_PWD_FILE_PLACEHOLDER={path}
+INFO_TRUSTSTORE_PWD_PLACEHOLDER={trustStorePassword}
+INFO_NICKNAME_PLACEHOLDER={nickname}
+INFO_ASSERTION_FILTER_PLACEHOLDER={filter}
+INFO_FILTER_PLACEHOLDER={filter}
+INFO_PROXYAUTHID_PLACEHOLDER={authzID}
+INFO_SASL_OPTION_PLACEHOLDER={name=value}
+INFO_PROTOCOL_VERSION_PLACEHOLDER={version}
+INFO_PROP_FILE_PATH_PLACEHOLDER={propertiesFilePath}
+INFO_ATTRIBUTE_PLACEHOLDER={attribute}
+INFO_NUM_ENTRIES_PLACEHOLDER={numEntries}
+INFO_LDAP_CONTROL_PLACEHOLDER={controloid[:criticality[:value|::b64value|:<filePath]]}
+INFO_ENCODING_PLACEHOLDER={encoding}
+INFO_ATTRIBUTE_LIST_PLACEHOLDER={attrList}
+INFO_NEW_PASSWORD_PLACEHOLDER={newPassword}
+INFO_CURRENT_PASSWORD_PLACEHOLDER={currentPassword}
+INFO_SEARCH_SCOPE_PLACEHOLDER={searchScope}
+INFO_SORT_ORDER_PLACEHOLDER={sortOrder}
+INFO_VLV_PLACEHOLDER={before:after:index:count | before:after:value}
+INFO_DEREFERENCE_POLICE_PLACEHOLDER={dereferencePolicy}
+INFO_SIZE_LIMIT_PLACEHOLDER={sizeLimit}
+INFO_TIME_LIMIT_PLACEHOLDER={timeLimit}
+INFO_TARGETDN_PLACEHOLDER={targetDN}
+INFO_KEYSTORE_PWD_FILE_PLACEHOLDER={keyStorePasswordFile}
+INFO_PSEARCH_PLACEHOLDER=ps[:changetype[:changesonly[:entrychgcontrols]]]
+ERR_CANNOT_READ_TRUSTSTORE=Cannot access trust store '%s'.  Verify \
+ that the provided trust store exists and that you have read access rights to it
+ERR_CANNOT_READ_KEYSTORE=Cannot access key store '%s'.  Verify \
+ that the provided key store exists and that you have read access rights to it
+INFO_DESCRIPTION_ADMIN_PORT=Directory server administration port number
+ERR_LDAPCOMPARE_ERROR_READING_FILE=An error occurred reading file \
+ '%s'.  Check that the file exists and that you have read access rights to \
+ it.  Details: %s
+ERR_LDAPCOMPARE_FILENAME_AND_DNS=Both entry DNs and a file name \
+ were provided for the compare operation.  These arguments are not compatible
+INFO_ERROR_EMPTY_RESPONSE=ERROR: a response must be provided in order to continue
+ERR_DECODE_CONTROL_FAILURE=# %s
+INFO_SEARCHRATE_TOOL_DESCRIPTION=This utility can be used to measure \
+  search throughput and response time of a directory service using \
+  user-defined searches.\n\n\
+  Example:\n\n\ \ searchrate -p 1389 -D "cn=directory manager" -w password \\\n\
+  \ \ \ \ -F -c 4 -t 4 -b "dc=example,dc=com" -g "rand(0,2000)" "(uid=user.%%d)"
+INFO_SEARCHRATE_TOOL_DESCRIPTION_BASEDN=Base DN format string.
+INFO_MODRATE_TOOL_DESCRIPTION=This utility can be used to measure \
+  modify throughput and response time of a directory service using \
+  user-defined modifications.\n\n\
+  Example:\n\n\ \ modrate -p 1389 -D "cn=directory manager" -w password \\\n\
+  \ \ \ \ -F -c 4 -t 4 -b "uid=user.%%d,ou=people,dc=example,dc=com" \\\n\
+  \ \ \ \ -g "rand(0,2000)" -g "randstr(16)" 'description:%%2$s'
+INFO_MODRATE_TOOL_DESCRIPTION_TARGETDN=Target entry DN format string
+INFO_AUTHRATE_TOOL_DESCRIPTION=This utility can be used to measure \
+  bind throughput and response time of a directory service using \
+  user-defined bind or search-then-bind operations.\n\nFormat strings may be \
+  used in the bind DN option as well as the authid and authzid SASL bind \
+  options. A search operation may be used to retrieve the bind DN by \
+  specifying the base DN and a filter. The retrieved entry DN will be appended \
+  as the last argument in the argument list when evaluating format strings.\n\n\
+  Example (bind only):\n\n\ \ authrate -p 1389 -D "uid=user.%%d,ou=people,dc=example,dc=com" \\\n\
+  \ \ \ \  -w password -f -c 10 -g "rand(0,2000)"\n\n\
+  Example (search then bind):\n\n\ \ authrate -p 1389 -D '%%2$s' -w password -f -c 10 \\\n\
+  \ \ \ \ -b "ou=people,dc=example,dc=com" -s one -g "rand(0,2000)" "(uid=user.%%d)"
+INFO_OUTPUT_LDIF_FILE_PLACEHOLDER={file}
+INFO_LDIFMODIFY_DESCRIPTION_OUTPUT_FILENAME=Write updated entries to %s \
+ instead of stdout
+INFO_LDIFDIFF_DESCRIPTION_OUTPUT_FILENAME=Write differences to %s \
+ instead of stdout
+INFO_LDIFSEARCH_DESCRIPTION_OUTPUT_FILENAME=Write search results to %s \
+ instead of stdout
+ERR_LDIFMODIFY_MULTIPLE_USES_OF_STDIN=Unable to use stdin for both the source \
+ LDIF and changes LDIF
+ERR_LDIFDIFF_MULTIPLE_USES_OF_STDIN=Unable to use stdin for both the source \
+ LDIF and target LDIF
+ERR_LDIFMODIFY_PATCH_FAILED=The changes could not be applied for the following \
+ reason: %s
+ERR_LDIFDIFF_DIFF_FAILED=The differences could not be computed for the following \
+ reason: %s
+ERR_LDIFSEARCH_FAILED=The search could not be performed for the following \
+ reason: %s
+INFO_LDIFMODIFY_TOOL_DESCRIPTION=This utility can be used to apply a set of \
+ modify, add, and delete operations to entries contained in an LDIF file
+INFO_LDIFDIFF_TOOL_DESCRIPTION=This utility can be used to compare two LDIF \
+ files and report the differences in LDIF format
+INFO_LDIFSEARCH_TOOL_DESCRIPTION=This utility can be used to perform search \
+ operations against entries contained in an LDIF file
+ #
+ # MakeLDIF tool
+ #
+INFO_MAKELDIF_TOOL_DESCRIPTION=This utility can be used to generate LDIF \
+ data based on a definition in a template file
+INFO_CONSTANT_PLACEHOLDER={name=value}
+INFO_SEED_PLACEHOLDER={seed}
+INFO_PATH_PLACEHOLDER={path}
+INFO_MAKELDIF_DESCRIPTION_CONSTANT=A constant that overrides the value \
+ set in the template file
+INFO_MAKELDIF_DESCRIPTION_LDIF=The path to the LDIF file to be written
+INFO_MAKELDIF_DESCRIPTION_SEED=The seed to use to initialize the random \
+ number generator
+INFO_MAKELDIF_DESCRIPTION_HELP=Show this usage information
+INFO_MAKELDIF_DESCRIPTION_RESOURCE_PATH=Path to look for \
+ MakeLDIF resources (e.g., data files)
+ERR_MAKELDIF_NO_SUCH_RESOURCE_DIRECTORY=The specified resource \
+ directory %s could not be found
+INFO_MAKELDIF_PROCESSED_N_ENTRIES=Processed %d entries
+INFO_MAKELDIF_PROCESSING_COMPLETE=LDIF processing complete. %d entries \
+ written
+ERR_MAKELDIF_EXCEPTION_DURING_PARSE=An error occurred while \
+ parsing template file :  %s
+ERR_MAKELDIF_UNABLE_TO_CREATE_LDIF=An error occurred while \
+ attempting to open LDIF file %s for writing:  %s
+ERR_MAKELDIF_ERROR_WRITING_LDIF=An error occurred while writing data \
+ to LDIF file %s:  %s
+ERR_MAKELDIF_EXCEPTION_DURING_PROCESSING=An error occurred while \
+ processing :  %s
+ERR_CONSTANT_ARG_CANNOT_DECODE=Unable to parse a constant argument, \
+ expecting name=value but got %s

--
Gitblit v1.10.0