From 88496c3a54b4c6e969cb0dce5cf67e5da6846740 Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Mon, 31 Oct 2016 13:59:39 +0000
Subject: [PATCH] OPENDJ-2772 Code cleanup

---
 opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentConstants.java                      |    6 
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFSearch.java             |  156 --
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java               |   14 
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java                |   13 
 opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/LDAPSearchITCase.java       |    4 
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java               |   15 
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPToolException.java      |   68 +
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java             |   14 
 opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties       |    8 
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFModify.java             |  150 +--
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPCompare.java            |  179 +--
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java             |  575 +++++--------
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFDiff.java               |  107 -
 opendj-server-legacy/src/main/java/org/opends/server/tools/makeldif/MakeLDIFInputStream.java  |   14 
 opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java                        |   14 
 opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/LDAPCompareITCase.java      |    4 
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPPasswordModify.java     |  137 +--
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPModify.java             |  305 ++----
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPToolArgumentParser.java |  161 +++
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/Utils.java                  |  479 ++++++++---
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java                |   14 
 21 files changed, 1,177 insertions(+), 1,260 deletions(-)

diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentConstants.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentConstants.java
index 049c7cc..02f0710 100644
--- a/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentConstants.java
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentConstants.java
@@ -375,15 +375,15 @@
     /** Value for the restart option long form. */
     public static final String OPTION_LONG_RESTART = "restart";
 
-    /** The value for the hidden testonly argument. */
-    public static final String OPTION_LONG_TESTONLY_ARGUMENT = "testOnly";
-
     /** The value for the backend type long option. */
     public static final String OPTION_LONG_BACKEND_TYPE = "backendType";
 
     /** The value for the backend type short option. */
     public static final Character OPTION_SHORT_BACKEND_TYPE = 't';
 
+    /** The value to use to read from stdin or write to stdout. */
+    public static final String USE_SYSTEM_STREAM_TOKEN = "-";
+
     /** Prevent instantiation. */
     private ArgumentConstants() {
     }
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
index ce34201..74b242e 100644
--- a/opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java
@@ -632,6 +632,20 @@
     }
 
     /**
+     * Returns the "-S, --scriptFriendly" boolean argument used in the sdk toolkit.
+     *
+     * @return The "-S, --scriptFriendly" argument.
+     * @throws ArgumentException
+     *             If there is a problem with any of the parameters used to create this argument.
+     */
+    public static BooleanArgument scriptFriendlySdkArgument() throws ArgumentException {
+        return BooleanArgument.builder("scriptFriendly")
+                .shortIdentifier('S')
+                .description(INFO_DESCRIPTION_SCRIPT_FRIENDLY.get())
+                .buildArgument();
+    }
+
+    /**
      * Returns the "LDAP port" integer argument.
      *
      * @param defaultLdapPort
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java
index 47dcec1..e30f947 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java
@@ -397,8 +397,10 @@
     int run(final String[] args) {
         // Create the command-line argument parser for use with this program.
         final LocalizableMessage toolDescription = INFO_ADDRATE_TOOL_DESCRIPTION.get();
-        final ArgumentParser argParser =
-            new ArgumentParser(AddRate.class.getName(), toolDescription, false, true, 1, 1, "template-file-path");
+        final ArgumentParser argParser = LDAPToolArgumentParser.builder(AddRate.class.getName())
+                .toolDescription(toolDescription)
+                .trailingArguments(1, "template-file-path")
+                .build();
         argParser.setVersionHandler(newSdkVersionHandler());
         argParser.setShortToolDescription(REF_SHORT_DESC_ADDRATE.get());
         argParser.setDocToolDescriptionSupplement(SUPPLEMENT_DESCRIPTION_RATE_TOOLS.get());
@@ -533,10 +535,7 @@
         verbose = verboseArgument();
         argParser.addArgument(verbose);
 
-        scriptFriendly =
-                BooleanArgument.builder("scriptFriendly")
-                        .shortIdentifier('S')
-                        .description(INFO_DESCRIPTION_SCRIPT_FRIENDLY.get())
-                        .buildAndAddToParser(argParser);
+        scriptFriendly = scriptFriendlySdkArgument();
+        argParser.addArgument(scriptFriendly);
     }
 }
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java
index f0f6f8f..b8ea716 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java
@@ -375,9 +375,10 @@
     int run(final String[] args) {
         // Create the command-line argument parser for use with this program.
         final LocalizableMessage toolDescription = INFO_AUTHRATE_TOOL_DESCRIPTION.get();
-        final ArgumentParser argParser =
-                new ArgumentParser(AuthRate.class.getName(), toolDescription, false, true, 0, 0,
-                        "[filter format string] [attributes ...]");
+        final ArgumentParser argParser = LDAPToolArgumentParser.builder(AuthRate.class.getName())
+                .toolDescription(toolDescription)
+                .trailingArguments("[filter format string] [attributes ...]")
+                .build();
         argParser.setVersionHandler(newSdkVersionHandler());
         argParser.setShortToolDescription(REF_SHORT_DESC_AUTHRATE.get());
         argParser.setDocToolDescriptionSupplement(SUPPLEMENT_DESCRIPTION_RATE_TOOLS.get());
@@ -446,11 +447,8 @@
             verbose = verboseArgument();
             argParser.addArgument(verbose);
 
-            scriptFriendly =
-                    BooleanArgument.builder("scriptFriendly")
-                            .shortIdentifier('S')
-                            .description(INFO_DESCRIPTION_SCRIPT_FRIENDLY.get())
-                            .buildAndAddToParser(argParser);
+            scriptFriendly = scriptFriendlySdkArgument();
+            argParser.addArgument(scriptFriendly);
         } catch (final ArgumentException ae) {
             final LocalizableMessage message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
             errPrintln(message);
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPCompare.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPCompare.java
index b5ac74c..117fb6d 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPCompare.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPCompare.java
@@ -18,13 +18,18 @@
 
 import static com.forgerock.opendj.cli.ArgumentConstants.*;
 import static com.forgerock.opendj.cli.ToolVersionHandler.newSdkVersionHandler;
+import static com.forgerock.opendj.ldap.tools.LDAPToolException.newToolParamException;
 import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
 import static com.forgerock.opendj.cli.Utils.filterExitCode;
 import static com.forgerock.opendj.cli.Utils.readBytesFromFile;
+import static com.forgerock.opendj.ldap.tools.Utils.addControlsToRequest;
 import static com.forgerock.opendj.ldap.tools.Utils.printErrorMessage;
-import static com.forgerock.opendj.ldap.tools.Utils.printPasswordPolicyResults;
+import static com.forgerock.opendj.ldap.tools.Utils.readAssertionControl;
+import static com.forgerock.opendj.ldap.tools.Utils.ensureLdapProtocolVersionIsSupported;
+import static com.forgerock.opendj.ldap.tools.Utils.getConnection;
 import static com.forgerock.opendj.cli.CommonArguments.*;
 
+import static com.forgerock.opendj.ldap.tools.Utils.readControls;
 import static org.forgerock.util.Utils.closeSilently;
 
 import java.io.BufferedReader;
@@ -34,26 +39,20 @@
 import java.io.InputStreamReader;
 import java.io.PrintStream;
 import java.util.ArrayList;
+import java.util.List;
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizedIllegalArgumentException;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.Connection;
-import org.forgerock.opendj.ldap.ConnectionFactory;
-import org.forgerock.opendj.ldap.DecodeException;
-import org.forgerock.opendj.ldap.Filter;
 import org.forgerock.opendj.ldap.LdapException;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.controls.AssertionRequestControl;
-import org.forgerock.opendj.ldap.controls.Control;
 import org.forgerock.opendj.ldap.controls.ProxiedAuthV2RequestControl;
-import org.forgerock.opendj.ldap.requests.BindRequest;
 import org.forgerock.opendj.ldap.requests.CompareRequest;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.responses.Result;
 
 import com.forgerock.opendj.cli.ArgumentException;
-import com.forgerock.opendj.cli.ArgumentParser;
 import com.forgerock.opendj.cli.BooleanArgument;
 import com.forgerock.opendj.cli.ConnectionFactoryProvider;
 import com.forgerock.opendj.cli.ConsoleApplication;
@@ -69,7 +68,14 @@
      *            The command-line arguments provided to this program.
      */
     public static void main(final String[] args) {
-        final int retCode = new LDAPCompare().run(args);
+        final LDAPCompare ldapCompare = new LDAPCompare();
+        int retCode;
+        try {
+            retCode = ldapCompare.run(args);
+        } catch (final LDAPToolException e) {
+            e.printErrorMessage(ldapCompare);
+            retCode = e.getResultCode();
+        }
         System.exit(filterExitCode(retCode));
     }
 
@@ -100,50 +106,40 @@
     }
 
     private int executeCompare(final CompareRequest request, final Connection connection) {
-        println(INFO_PROCESSING_COMPARE_OPERATION.get(request.getAttributeDescription().toString(),
-                request.getAssertionValueAsString(), request.getName().toString()));
+        final String dnStr = request.getName().toString();
+        println(INFO_PROCESSING_COMPARE_OPERATION.get(
+            request.getAttributeDescription().toString(), request.getAssertionValueAsString(), dnStr));
         if (connection != null) {
             try {
                 Result result = connection.compare(request);
-                if (result.getResultCode() == ResultCode.COMPARE_FALSE) {
-                    println(INFO_COMPARE_OPERATION_RESULT_FALSE.get(request.getName().toString()));
+                if (ResultCode.COMPARE_FALSE == result.getResultCode()) {
+                    println(INFO_COMPARE_OPERATION_RESULT_FALSE.get(dnStr));
                 } else {
-                    println(INFO_COMPARE_OPERATION_RESULT_TRUE.get(request.getName().toString()));
+                    println(INFO_COMPARE_OPERATION_RESULT_TRUE.get(dnStr));
                 }
-            } catch (final LdapException ere) {
-                final LocalizableMessage msg = INFO_OPERATION_FAILED.get("COMPARE");
-                errPrintln(msg);
-                final Result r = ere.getResult();
-                errPrintln(ERR_TOOL_RESULT_CODE.get(r.getResultCode().intValue(), r.getResultCode()
-                        .toString()));
-                if (r.getDiagnosticMessage() != null && r.getDiagnosticMessage().length() > 0) {
-                    errPrintln(LocalizableMessage.raw(r.getDiagnosticMessage()));
-                }
-                if (r.getMatchedDN() != null && r.getMatchedDN().length() > 0) {
-                    errPrintln(ERR_TOOL_MATCHED_DN.get(r.getMatchedDN()));
-                }
-                return r.getResultCode().intValue();
+            } catch (final LdapException e) {
+                return printErrorMessage(this, e, ERR_LDAP_COMPARE_FAILED);
             }
         }
         return ResultCode.SUCCESS.intValue();
     }
 
-    int run(final String[] args) {
+    int run(final String[] args) throws LDAPToolException {
         // Create the command-line argument parser for use with this program.
         final LocalizableMessage toolDescription = INFO_LDAPCOMPARE_TOOL_DESCRIPTION.get();
-        final ArgumentParser argParser = new ArgumentParser(
-            LDAPCompare.class.getName(), toolDescription, false, true, 1, 0, "attribute:value [DN ...]");
+        final LDAPToolArgumentParser argParser = LDAPToolArgumentParser.builder(LDAPCompare.class.getName())
+                .toolDescription(toolDescription)
+                .trailingArguments(1, "attribute:value [DN ...]")
+                .build();
         argParser.setVersionHandler(newSdkVersionHandler());
         argParser.setShortToolDescription(REF_SHORT_DESC_LDAPCOMPARE.get());
 
         ConnectionFactoryProvider connectionFactoryProvider;
-        ConnectionFactory connectionFactory;
-        BindRequest bindRequest;
 
         BooleanArgument continueOnError;
-        BooleanArgument noop;
+        BooleanArgument dryRun;
         BooleanArgument showUsage;
-        IntegerArgument version;
+        IntegerArgument ldapProtocolVersion;
         StringArgument assertionFilter;
         StringArgument controlStr;
         StringArgument encodingStr;
@@ -177,8 +173,8 @@
             controlStr = controlArgument();
             argParser.addArgument(controlStr);
 
-            version = ldapVersionArgument();
-            argParser.addArgument(version);
+            ldapProtocolVersion = ldapVersionArgument();
+            argParser.addArgument(ldapProtocolVersion);
 
             encodingStr = encodingArgument();
             argParser.addArgument(encodingStr);
@@ -186,8 +182,8 @@
             continueOnError = continueOnErrorArgument();
             argParser.addArgument(continueOnError);
 
-            noop = noOpArgument();
-            argParser.addArgument(noop);
+            dryRun = noOpArgument();
+            argParser.addArgument(dryRun);
 
             verbose = verboseArgument();
             argParser.addArgument(verbose);
@@ -196,41 +192,17 @@
             argParser.addArgument(showUsage);
             argParser.setUsageArgument(showUsage, getOutputStream());
         } catch (final ArgumentException ae) {
-            errPrintln(ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+            throw newToolParamException(ae, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
         }
 
-        // Parse the command-line arguments provided to this program.
-        try {
-            argParser.parseArguments(args);
-
-            // If we should just display usage or version information, then print it and exit.
-            if (argParser.usageOrVersionDisplayed()) {
-                return 0;
-            }
-
-            connectionFactory = connectionFactoryProvider.getUnauthenticatedConnectionFactory();
-            bindRequest = connectionFactoryProvider.getBindRequest();
-        } catch (final ArgumentException ae) {
-            argParser.displayMessageAndUsageReference(getErrStream(), ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+        argParser.parseArguments(args, getErrStream(), connectionFactoryProvider);
+        if (argParser.usageOrVersionDisplayed()) {
+            return ResultCode.SUCCESS.intValue();
         }
+        ensureLdapProtocolVersionIsSupported(ldapProtocolVersion);
 
-        try {
-            final int versionNumber = version.getIntValue();
-            if (versionNumber != 2 && versionNumber != 3) {
-                argParser.displayMessageAndUsageReference(
-                    getErrStream(), ERR_DESCRIPTION_INVALID_VERSION.get(String.valueOf(versionNumber)));
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-            }
-        } catch (final ArgumentException ae) {
-            argParser.displayMessageAndUsageReference(
-                getErrStream(), ERR_DESCRIPTION_INVALID_VERSION.get(version.getValue()));
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-        }
-
-        final ArrayList<String> dnStrings = new ArrayList<>();
-        final ArrayList<String> attrAndDNStrings = argParser.getTrailingArguments();
+        final List<String> dnStrings = new ArrayList<>();
+        final List<String> attrAndDNStrings = argParser.getTrailingArguments();
 
         if (attrAndDNStrings.isEmpty()) {
             argParser.displayMessageAndUsageReference(getErrStream(), ERR_LDAPCOMPARE_NO_ATTR.get());
@@ -272,17 +244,15 @@
                 try {
                     attributeVal = ByteString.valueOfBase64(base64);
                 } catch (final LocalizedIllegalArgumentException e) {
-                    errPrintln(INFO_COMPARE_CANNOT_BASE64_DECODE_ASSERTION_VALUE.get());
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+                    throw newToolParamException(e, INFO_COMPARE_CANNOT_BASE64_DECODE_ASSERTION_VALUE.get());
                 }
             } else if (nextChar == '<') {
                 try {
                     final String filePath = remainder.substring(1, remainder.length());
                     attributeVal = ByteString.wrap(readBytesFromFile(filePath));
                 } catch (final Exception e) {
-                    errPrintln(INFO_COMPARE_CANNOT_READ_ASSERTION_VALUE_FROM_FILE.get(String
-                            .valueOf(e)));
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+                    throw newToolParamException(
+                            e, INFO_COMPARE_CANNOT_READ_ASSERTION_VALUE_FROM_FILE.get(String.valueOf(e)));
                 }
             } else {
                 attributeVal = ByteString.valueOfUtf8(remainder);
@@ -292,39 +262,14 @@
         }
 
         final CompareRequest compare = Requests.newCompareRequest("", attributeType, attributeVal);
-
-        if (controlStr.isPresent()) {
-            for (final String ctrlString : controlStr.getValues()) {
-                try {
-                    final Control ctrl = Utils.getControl(ctrlString);
-                    compare.addControl(ctrl);
-                } catch (final DecodeException de) {
-                    errPrintln(ERR_TOOL_INVALID_CONTROL_STRING.get(ctrlString));
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            }
-        }
+        addControlsToRequest(compare, readControls(controlStr));
 
         if (proxyAuthzID.isPresent()) {
-            final Control proxyControl =
-                    ProxiedAuthV2RequestControl.newControl(proxyAuthzID.getValue());
-            compare.addControl(proxyControl);
+            compare.addControl(ProxiedAuthV2RequestControl.newControl(proxyAuthzID.getValue()));
         }
 
         if (assertionFilter.isPresent()) {
-            final String filterString = assertionFilter.getValue();
-            Filter filter;
-            try {
-                filter = Filter.valueOf(filterString);
-
-                // FIXME -- Change this to the correct OID when the official one
-                // is assigned.
-                final Control assertionControl = AssertionRequestControl.newControl(true, filter);
-                compare.addControl(assertionControl);
-            } catch (final LocalizedIllegalArgumentException le) {
-                errPrintln(ERR_LDAP_ASSERTION_INVALID_FILTER.get(le.getMessage()));
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-            }
+            compare.addControl(readAssertionControl(assertionFilter.getValue()));
         }
 
         BufferedReader rdr = null;
@@ -335,24 +280,15 @@
             try {
                 rdr = new BufferedReader(new FileReader(filename.getValue()));
             } catch (final FileNotFoundException t) {
-                errPrintln(ERR_LDAPCOMPARE_ERROR_READING_FILE.get(filename.getValue(), t.toString()));
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+                throw newToolParamException(
+                        t, ERR_LDAPCOMPARE_ERROR_READING_FILE.get(filename.getValue(), t.toString()));
             }
         }
 
-        Connection connection = null;
-        try {
-            if (!noop.isPresent()) {
-                try {
-                    connection = connectionFactory.getConnection();
-                    if (bindRequest != null) {
-                        printPasswordPolicyResults(this, connection.bind(bindRequest));
-                    }
-                } catch (final LdapException ere) {
-                    return printErrorMessage(this, ere);
-                }
-            }
-
+        try (final Connection connection = getConnection(argParser.getConnectionFactory(),
+                                                         argParser.getBindRequest(),
+                                                         dryRun,
+                                                         this)) {
             int result;
             if (rdr == null) {
                 for (final String dn : dnStrings) {
@@ -373,15 +309,14 @@
                         }
                     }
                 } catch (final IOException ioe) {
-                    errPrintln(ERR_LDAPCOMPARE_ERROR_READING_FILE.get(filename.getValue(), ioe
-                            .toString()));
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+                    throw newToolParamException(
+                            ioe, ERR_LDAPCOMPARE_ERROR_READING_FILE.get(filename.getValue(), ioe.toString()));
                 }
             }
         } finally {
-            closeSilently(connection, rdr);
+            closeSilently(rdr);
         }
 
-        return 0;
+        return ResultCode.SUCCESS.intValue();
     }
 }
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPModify.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPModify.java
index 8ad5c11..1ea3f8f 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPModify.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPModify.java
@@ -18,10 +18,15 @@
 
 import static com.forgerock.opendj.cli.ArgumentConstants.*;
 import static com.forgerock.opendj.cli.ToolVersionHandler.newSdkVersionHandler;
+import static com.forgerock.opendj.ldap.tools.LDAPToolException.newToolParamException;
 import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
 import static com.forgerock.opendj.cli.Utils.filterExitCode;
+import static com.forgerock.opendj.ldap.tools.Utils.getConnection;
+import static com.forgerock.opendj.ldap.tools.Utils.printlnTextMsg;
+import static com.forgerock.opendj.ldap.tools.Utils.readAssertionControl;
+import static com.forgerock.opendj.ldap.tools.Utils.readControls;
+import static com.forgerock.opendj.ldap.tools.Utils.ensureLdapProtocolVersionIsSupported;
 import static com.forgerock.opendj.ldap.tools.Utils.printErrorMessage;
-import static com.forgerock.opendj.ldap.tools.Utils.printPasswordPolicyResults;
 import static com.forgerock.opendj.cli.CommonArguments.*;
 
 import static org.forgerock.util.Utils.closeSilently;
@@ -34,15 +39,11 @@
 import java.util.StringTokenizer;
 
 import org.forgerock.i18n.LocalizableMessage;
-import org.forgerock.i18n.LocalizedIllegalArgumentException;
 import org.forgerock.opendj.ldap.Connection;
-import org.forgerock.opendj.ldap.ConnectionFactory;
 import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.DecodeOptions;
-import org.forgerock.opendj.ldap.Filter;
 import org.forgerock.opendj.ldap.LdapException;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.controls.AssertionRequestControl;
 import org.forgerock.opendj.ldap.controls.Control;
 import org.forgerock.opendj.ldap.controls.PostReadRequestControl;
 import org.forgerock.opendj.ldap.controls.PostReadResponseControl;
@@ -50,7 +51,6 @@
 import org.forgerock.opendj.ldap.controls.PreReadResponseControl;
 import org.forgerock.opendj.ldap.controls.ProxiedAuthV2RequestControl;
 import org.forgerock.opendj.ldap.requests.AddRequest;
-import org.forgerock.opendj.ldap.requests.BindRequest;
 import org.forgerock.opendj.ldap.requests.DeleteRequest;
 import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
 import org.forgerock.opendj.ldap.requests.ModifyRequest;
@@ -63,7 +63,6 @@
 import org.forgerock.opendj.ldif.LDIFEntryWriter;
 
 import com.forgerock.opendj.cli.ArgumentException;
-import com.forgerock.opendj.cli.ArgumentParser;
 import com.forgerock.opendj.cli.BooleanArgument;
 import com.forgerock.opendj.cli.ConnectionFactoryProvider;
 import com.forgerock.opendj.cli.ConsoleApplication;
@@ -75,7 +74,13 @@
  * to the Directory Server.
  */
 public final class LDAPModify extends ConsoleApplication {
-    private class VisitorImpl implements ChangeRecordVisitor<Integer, java.lang.Void> {
+    private final class VisitorImpl implements ChangeRecordVisitor<Integer, Void> {
+        private final Connection connection;
+
+        private VisitorImpl(final Connection connection) {
+            this.connection = connection;
+        }
+
         @Override
         public Integer visitChangeRecord(final Void aVoid, final AddRequest change) {
             for (final Control control : controls) {
@@ -83,16 +88,17 @@
             }
             final String opType = "ADD";
             println(INFO_PROCESSING_OPERATION.get(opType, change.getName().toString()));
-            if (connection != null) {
-                try {
-                    Result r = connection.add(change);
-                    printResult(opType, change.getName().toString(), r);
-                    return r.getResultCode().intValue();
-                } catch (final LdapException ere) {
-                    return printErrorMessage(LDAPModify.this, ere);
-                }
+            if (dryRun()) {
+                return ResultCode.SUCCESS.intValue();
             }
-            return ResultCode.SUCCESS.intValue();
+
+            try {
+                Result r = connection.add(change);
+                printResult(opType, change.getName().toString(), r);
+                return r.getResultCode().intValue();
+            } catch (final LdapException ere) {
+                return printErrorMessage(LDAPModify.this, ere, ERR_LDAP_MODIFY_FAILED);
+            }
         }
 
         @Override
@@ -102,16 +108,17 @@
             }
             final String opType = "DELETE";
             println(INFO_PROCESSING_OPERATION.get(opType, change.getName().toString()));
-            if (connection != null) {
-                try {
-                    Result r = connection.delete(change);
-                    printResult(opType, change.getName().toString(), r);
-                    return r.getResultCode().intValue();
-                } catch (final LdapException ere) {
-                    return printErrorMessage(LDAPModify.this, ere);
-                }
+            if (dryRun()) {
+                return ResultCode.SUCCESS.intValue();
             }
-            return ResultCode.SUCCESS.intValue();
+
+            try {
+                Result r = connection.delete(change);
+                printResult(opType, change.getName().toString(), r);
+                return r.getResultCode().intValue();
+            } catch (final LdapException ere) {
+                return printErrorMessage(LDAPModify.this, ere, ERR_LDAP_MODIFY_FAILED);
+            }
         }
 
         @Override
@@ -121,16 +128,17 @@
             }
             final String opType = "MODIFY DN";
             println(INFO_PROCESSING_OPERATION.get(opType, change.getName().toString()));
-            if (connection != null) {
-                try {
-                    Result r = connection.modifyDN(change);
-                    printResult(opType, change.getName().toString(), r);
-                    return r.getResultCode().intValue();
-                } catch (final LdapException ere) {
-                    return printErrorMessage(LDAPModify.this, ere);
-                }
+            if (dryRun()) {
+                return ResultCode.SUCCESS.intValue();
             }
-            return ResultCode.SUCCESS.intValue();
+
+            try {
+                Result r = connection.modifyDN(change);
+                printResult(opType, change.getName().toString(), r);
+                return r.getResultCode().intValue();
+            } catch (final LdapException ere) {
+                return printErrorMessage(LDAPModify.this, ere, ERR_LDAP_MODIFY_FAILED);
+            }
         }
 
         @Override
@@ -140,36 +148,29 @@
             }
             final String opType = "MODIFY";
             println(INFO_PROCESSING_OPERATION.get(opType, change.getName().toString()));
-            if (connection != null) {
-                try {
-                    Result r = connection.modify(change);
-                    printResult(opType, change.getName().toString(), r);
-                    return r.getResultCode().intValue();
-                } catch (final LdapException ere) {
-                    return printErrorMessage(LDAPModify.this, ere);
-                }
+            if (dryRun()) {
+                return ResultCode.SUCCESS.intValue();
             }
-            return ResultCode.SUCCESS.intValue();
+
+            try {
+                Result r = connection.modify(change);
+                printResult(opType, change.getName().toString(), r);
+                return r.getResultCode().intValue();
+            } catch (final LdapException ere) {
+                return printErrorMessage(LDAPModify.this, ere, ERR_LDAP_MODIFY_FAILED);
+            }
         }
 
         private void printResult(final String operationType, final String name, final Result r) {
-            if (r.getResultCode() != ResultCode.SUCCESS && r.getResultCode() != ResultCode.REFERRAL) {
-                final LocalizableMessage msg = INFO_OPERATION_FAILED.get(operationType);
-                errPrintln(msg);
-                errPrintln(ERR_TOOL_RESULT_CODE.get(r.getResultCode().intValue(), r.getResultCode()));
-                if (r.getDiagnosticMessage() != null && r.getDiagnosticMessage().length() > 0) {
-                    errPrintln(LocalizableMessage.raw(r.getDiagnosticMessage()));
-                }
-                if (r.getMatchedDN() != null && r.getMatchedDN().length() > 0) {
-                    errPrintln(ERR_TOOL_MATCHED_DN.get(r.getMatchedDN()));
-                }
+            final ResultCode rc = r.getResultCode();
+            if (ResultCode.SUCCESS != rc && ResultCode.REFERRAL != rc) {
+                printErrorMessage(LDAPModify.this, r, ERR_LDAP_MODIFY_FAILED);
             } else {
                 println(INFO_OPERATION_SUCCESSFUL.get(operationType, name));
-                if (r.getDiagnosticMessage() != null && r.getDiagnosticMessage().length() > 0) {
-                    errPrintln(LocalizableMessage.raw(r.getDiagnosticMessage()));
-                }
-                if (r.getReferralURIs() != null) {
-                    for (final String uri : r.getReferralURIs()) {
+                printlnTextMsg(LDAPModify.this, r.getDiagnosticMessage());
+                final List<String> referralURIs = r.getReferralURIs();
+                if (referralURIs != null) {
+                    for (final String uri : referralURIs) {
                         println(LocalizableMessage.raw(uri));
                     }
                 }
@@ -200,9 +201,12 @@
             } catch (final IOException ioe) {
                 throw new RuntimeException(ioe);
             }
-
-            // TODO: CSN control
         }
+
+        private boolean dryRun() {
+            return connection == null;
+        }
+        // TODO: CSN control
     }
 
     /**
@@ -212,16 +216,19 @@
      *            The command-line arguments provided to this program.
      */
     public static void main(final String[] args) {
-        final int retCode = new LDAPModify().run(args);
+        final LDAPModify ldapModify = new LDAPModify();
+        int retCode;
+        try {
+            retCode = ldapModify.run(args);
+        } catch (final LDAPToolException e) {
+            e.printErrorMessage(ldapModify);
+            retCode = e.getResultCode();
+        }
         System.exit(filterExitCode(retCode));
     }
 
-    private Connection connection;
-
     private EntryWriter writer;
-
     private Collection<Control> controls;
-
     private BooleanArgument verbose;
 
     private LDAPModify() {
@@ -238,22 +245,21 @@
         return verbose.isPresent();
     }
 
-    int run(final String[] args) {
+    int run(final String[] args) throws LDAPToolException {
         // Create the command-line argument parser for use with this program.
         final LocalizableMessage toolDescription = INFO_LDAPMODIFY_TOOL_DESCRIPTION.get();
-        final ArgumentParser argParser =
-                new ArgumentParser(LDAPModify.class.getName(), toolDescription, false);
+        final LDAPToolArgumentParser argParser = LDAPToolArgumentParser.builder(LDAPModify.class.getName())
+                .toolDescription(toolDescription)
+                .build();
         argParser.setVersionHandler(newSdkVersionHandler());
         argParser.setShortToolDescription(REF_SHORT_DESC_LDAPMODIFY.get());
 
         ConnectionFactoryProvider connectionFactoryProvider;
-        ConnectionFactory connectionFactory;
-        BindRequest bindRequest;
 
         BooleanArgument continueOnError;
         BooleanArgument noop;
         BooleanArgument showUsage;
-        IntegerArgument version;
+        IntegerArgument ldapProtocolVersion;
         StringArgument assertionFilter;
         StringArgument controlStr;
         StringArgument filename;
@@ -274,12 +280,9 @@
             argParser.addArgument(noPropertiesFileArgument);
             argParser.setNoPropertiesFileArgument(noPropertiesFileArgument);
 
-            filename =
-                    StringArgument.builder(OPTION_LONG_FILENAME)
-                            .shortIdentifier(OPTION_SHORT_FILENAME)
-                            .description(INFO_LDAPMODIFY_DESCRIPTION_FILENAME.get())
-                            .valuePlaceholder(INFO_FILE_PLACEHOLDER.get())
-                            .buildAndAddToParser(argParser);
+            filename = filenameArgument(INFO_LDAPMODIFY_DESCRIPTION_FILENAME.get());
+            argParser.addArgument(filename);
+
             proxyAuthzID =
                     StringArgument.builder(OPTION_LONG_PROXYAUTHID)
                             .shortIdentifier(OPTION_SHORT_PROXYAUTHID)
@@ -301,16 +304,12 @@
                             .description(INFO_DESCRIPTION_POSTREAD_ATTRS.get())
                             .valuePlaceholder(INFO_ATTRIBUTE_LIST_PLACEHOLDER.get())
                             .buildAndAddToParser(argParser);
-            controlStr =
-                    StringArgument.builder("control")
-                            .shortIdentifier('J')
-                            .description(INFO_DESCRIPTION_CONTROLS.get())
-                            .multiValued()
-                            .valuePlaceholder(INFO_LDAP_CONTROL_PLACEHOLDER.get())
-                            .buildAndAddToParser(argParser);
 
-            version = ldapVersionArgument();
-            argParser.addArgument(version);
+            controlStr = controlArgument();
+            argParser.addArgument(controlStr);
+
+            ldapProtocolVersion = ldapVersionArgument();
+            argParser.addArgument(ldapProtocolVersion);
 
             continueOnError = continueOnErrorArgument();
             argParser.addArgument(continueOnError);
@@ -329,119 +328,42 @@
             return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
         }
 
-        // Parse the command-line arguments provided to this program.
-        try {
-            argParser.parseArguments(args);
-
-            // If we should just display usage or version information, then print it and exit.
-            if (argParser.usageOrVersionDisplayed()) {
-                return 0;
-            }
-
-            connectionFactory = connectionFactoryProvider.getUnauthenticatedConnectionFactory();
-            bindRequest = connectionFactoryProvider.getBindRequest();
-        } catch (final ArgumentException ae) {
-            argParser.displayMessageAndUsageReference(getErrStream(), ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+        argParser.parseArguments(args, getErrStream(), connectionFactoryProvider);
+        if (argParser.usageOrVersionDisplayed()) {
+            return ResultCode.SUCCESS.intValue();
         }
+        ensureLdapProtocolVersionIsSupported(ldapProtocolVersion);
 
-        try {
-            final int versionNumber = version.getIntValue();
-            if (versionNumber != 2 && versionNumber != 3) {
-                errPrintln(ERR_DESCRIPTION_INVALID_VERSION.get(String.valueOf(versionNumber)));
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-            }
-        } catch (final ArgumentException ae) {
-            errPrintln(ERR_DESCRIPTION_INVALID_VERSION.get(version.getValue()));
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-        }
-
-        controls = new LinkedList<>();
-        if (controlStr.isPresent()) {
-            for (final String ctrlString : controlStr.getValues()) {
-                try {
-                    final Control ctrl = Utils.getControl(ctrlString);
-                    controls.add(ctrl);
-                } catch (final DecodeException de) {
-                    errPrintln(ERR_TOOL_INVALID_CONTROL_STRING.get(ctrlString));
-                    ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            }
-        }
-
+        controls = readControls(controlStr);
         if (proxyAuthzID.isPresent()) {
-            final Control proxyControl =
-                    ProxiedAuthV2RequestControl.newControl(proxyAuthzID.getValue());
-            controls.add(proxyControl);
+            controls.add(ProxiedAuthV2RequestControl.newControl(proxyAuthzID.getValue()));
         }
 
         if (assertionFilter.isPresent()) {
-            final String filterString = assertionFilter.getValue();
-            Filter filter;
-            try {
-                filter = Filter.valueOf(filterString);
-
-                // FIXME -- Change this to the correct OID when the official one
-                // is assigned.
-                final Control assertionControl = AssertionRequestControl.newControl(true, filter);
-                controls.add(assertionControl);
-            } catch (final LocalizedIllegalArgumentException le) {
-                errPrintln(ERR_LDAP_ASSERTION_INVALID_FILTER.get(le.getMessage()));
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-            }
+            controls.add(readAssertionControl(assertionFilter.getValue()));
         }
-
-        if (preReadAttributes.isPresent()) {
-            final String valueStr = preReadAttributes.getValue();
-            final StringTokenizer tokenizer = new StringTokenizer(valueStr, ", ");
-            final List<String> attributes = new LinkedList<>();
-            while (tokenizer.hasMoreTokens()) {
-                attributes.add(tokenizer.nextToken());
-            }
-            controls.add(PreReadRequestControl.newControl(true, attributes));
-        }
-
-        if (postReadAttributes.isPresent()) {
-            final String valueStr = postReadAttributes.getValue();
-            final StringTokenizer tokenizer = new StringTokenizer(valueStr, ", ");
-            final List<String> attributes = new LinkedList<>();
-            while (tokenizer.hasMoreTokens()) {
-                attributes.add(tokenizer.nextToken());
-            }
-            final PostReadRequestControl control =
-                    PostReadRequestControl.newControl(true, attributes);
-            controls.add(control);
-        }
+        addReadAttributesToControl(controls, preReadAttributes, true);
+        addReadAttributesToControl(controls, postReadAttributes, false);
 
         writer = new LDIFEntryWriter(getOutputStream());
-        final VisitorImpl visitor = new VisitorImpl();
         ChangeRecordReader reader = null;
-        try {
-            if (!noop.isPresent()) {
-                try {
-                    connection = connectionFactory.getConnection();
-                    if (bindRequest != null) {
-                        printPasswordPolicyResults(this, connection.bind(bindRequest));
-                    }
-                } catch (final LdapException ere) {
-                    return printErrorMessage(this, ere);
-                }
-            }
-
+        try (final Connection connection = getConnection(argParser.getConnectionFactory(),
+                                                         argParser.getBindRequest(),
+                                                         noop,
+                                                         this)) {
             if (filename.isPresent()) {
+                final String filePath = filename.getValue();
                 try {
-                    reader = new LDIFChangeRecordReader(new FileInputStream(filename.getValue()));
+                    reader = new LDIFChangeRecordReader(new FileInputStream(filePath));
                 } catch (final Exception e) {
-                    final LocalizableMessage message =
-                            ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ.get(filename.getValue(), e
-                                    .getLocalizedMessage());
-                    errPrintln(message);
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+                    throw newToolParamException(
+                            e, ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ.get(filePath, e.getLocalizedMessage()));
                 }
             } else {
                 reader = new LDIFChangeRecordReader(getInputStream());
             }
 
+            final VisitorImpl visitor = new VisitorImpl(connection);
             try {
                 while (reader.hasNext()) {
                     final ChangeRecord cr = reader.readChangeRecord();
@@ -451,13 +373,26 @@
                     }
                 }
             } catch (final IOException ioe) {
-                errPrintln(ERR_LDIF_FILE_READ_ERROR.get(filename.getValue(), ioe.getLocalizedMessage()));
-                return ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
+                throw newToolParamException(
+                        ioe, ERR_LDIF_FILE_READ_ERROR.get(filename.getValue(), ioe.getLocalizedMessage()));
             }
         } finally {
-            closeSilently(reader, connection);
+            closeSilently(reader);
         }
 
         return ResultCode.SUCCESS.intValue();
     }
+
+    private void addReadAttributesToControl(
+            final Collection<Control> controls, final StringArgument attributesArg, final boolean preRead) {
+        if (attributesArg.isPresent()) {
+            final StringTokenizer tokenizer = new StringTokenizer(attributesArg.getValue(), ", ");
+            final List<String> attributes = new LinkedList<>();
+            while (tokenizer.hasMoreTokens()) {
+                attributes.add(tokenizer.nextToken());
+            }
+            controls.add(preRead ? PreReadRequestControl.newControl(true, attributes)
+                                 : PostReadRequestControl.newControl(true, attributes));
+        }
+    }
 }
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPPasswordModify.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPPasswordModify.java
index 1871364..3904290 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPPasswordModify.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPPasswordModify.java
@@ -18,24 +18,26 @@
 
 import static com.forgerock.opendj.cli.CliMessages.INFO_FILE_PLACEHOLDER;
 import static com.forgerock.opendj.cli.ToolVersionHandler.newSdkVersionHandler;
+import static com.forgerock.opendj.cli.Utils.throwIfArgumentsConflict;
+import static com.forgerock.opendj.ldap.tools.LDAPToolException.newToolParamException;
 import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
 import static com.forgerock.opendj.cli.Utils.filterExitCode;
 import static com.forgerock.opendj.cli.CommonArguments.*;
+import static com.forgerock.opendj.ldap.tools.Utils.addControlsToRequest;
+import static com.forgerock.opendj.ldap.tools.Utils.ensureLdapProtocolVersionIsSupported;
+import static com.forgerock.opendj.ldap.tools.Utils.printErrorMessage;
+import static com.forgerock.opendj.ldap.tools.Utils.readControls;
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.Connection;
-import org.forgerock.opendj.ldap.ConnectionFactory;
-import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.LdapException;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.controls.Control;
 import org.forgerock.opendj.ldap.requests.PasswordModifyExtendedRequest;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.responses.PasswordModifyExtendedResult;
 
 import com.forgerock.opendj.cli.ArgumentException;
-import com.forgerock.opendj.cli.ArgumentParser;
 import com.forgerock.opendj.cli.BooleanArgument;
 import com.forgerock.opendj.cli.ConnectionFactoryProvider;
 import com.forgerock.opendj.cli.ConsoleApplication;
@@ -65,7 +67,14 @@
      *            The command-line arguments provided to this program.
      */
     public static void main(final String[] args) {
-        final int retCode = new LDAPPasswordModify().run(args);
+        final LDAPPasswordModify ldapPasswordModify = new LDAPPasswordModify();
+        int retCode;
+        try {
+            retCode = ldapPasswordModify.run(args);
+        } catch (final LDAPToolException e) {
+            e.printErrorMessage(ldapPasswordModify);
+            retCode = e.getResultCode();
+        }
         System.exit(filterExitCode(retCode));
     }
 
@@ -85,21 +94,22 @@
         return verbose.isPresent();
     }
 
-    private int run(final String[] args) {
+    private int run(final String[] args) throws LDAPToolException {
         // Create the command-line argument parser for use with this program.
         final LocalizableMessage toolDescription = INFO_LDAPPWMOD_TOOL_DESCRIPTION.get();
-        final ArgumentParser argParser =
-                new ArgumentParser(LDAPPasswordModify.class.getName(), toolDescription, false);
+        final LDAPToolArgumentParser argParser = LDAPToolArgumentParser.builder(LDAPPasswordModify.class.getName())
+                .toolDescription(toolDescription)
+                .needAuthenticatedConnectionFactory()
+                .build();
         argParser.setVersionHandler(newSdkVersionHandler());
         argParser.setShortToolDescription(REF_SHORT_DESC_LDAPPASSWORDMODIFY.get());
 
         ConnectionFactoryProvider connectionFactoryProvider;
-        ConnectionFactory connectionFactory;
 
         FileBasedArgument currentPWFile;
         FileBasedArgument newPWFile;
         BooleanArgument showUsage;
-        IntegerArgument version;
+        IntegerArgument ldapProtocolVersion;
         StringArgument currentPW;
         StringArgument controlStr;
         StringArgument newPW;
@@ -148,16 +158,11 @@
                             .description(INFO_LDAPPWMOD_DESCRIPTION_AUTHZID.get())
                             .valuePlaceholder(INFO_PROXYAUTHID_PLACEHOLDER.get())
                             .buildAndAddToParser(argParser);
-            controlStr =
-                    StringArgument.builder("control")
-                            .shortIdentifier('J')
-                            .description(INFO_DESCRIPTION_CONTROLS.get())
-                            .multiValued()
-                            .valuePlaceholder(INFO_LDAP_CONTROL_PLACEHOLDER.get())
-                            .buildAndAddToParser(argParser);
+            controlStr = controlArgument();
+            argParser.addArgument(controlStr);
 
-            version = ldapVersionArgument();
-            argParser.addArgument(version);
+            ldapProtocolVersion = ldapVersionArgument();
+            argParser.addArgument(ldapProtocolVersion);
 
             verbose = verboseArgument();
             argParser.addArgument(verbose);
@@ -170,66 +175,27 @@
             return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
         }
 
-        // Parse the command-line arguments provided to this program.
-        try {
-            argParser.parseArguments(args);
-
-            // If we should just display usage or version information, then print it and exit.
-            if (argParser.usageOrVersionDisplayed()) {
-                return 0;
-            }
-
-            connectionFactory = connectionFactoryProvider.getAuthenticatedConnectionFactory();
-        } catch (final ArgumentException ae) {
-            argParser.displayMessageAndUsageReference(getErrStream(), ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+        argParser.parseArgumentsNoBindRequest(args, getErrStream(), connectionFactoryProvider);
+        if (argParser.usageOrVersionDisplayed()) {
+            return ResultCode.SUCCESS.intValue();
         }
+        ensureLdapProtocolVersionIsSupported(ldapProtocolVersion);
 
         final PasswordModifyExtendedRequest request = Requests.newPasswordModifyExtendedRequest();
+        addControlsToRequest(request, readControls(controlStr));
+
         try {
-            final int versionNumber = version.getIntValue();
-            if (versionNumber != 2 && versionNumber != 3) {
-                errPrintln(ERR_DESCRIPTION_INVALID_VERSION.get(String.valueOf(versionNumber)));
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-            }
-        } catch (final ArgumentException ae) {
-            errPrintln(ERR_DESCRIPTION_INVALID_VERSION.get(version.getValue()));
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-        }
-
-        if (controlStr.isPresent()) {
-            for (final String ctrlString : controlStr.getValues()) {
-                try {
-                    final Control ctrl = Utils.getControl(ctrlString);
-                    request.addControl(ctrl);
-                } catch (final DecodeException de) {
-                    errPrintln(ERR_TOOL_INVALID_CONTROL_STRING.get(ctrlString));
-                    ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            }
-        }
-
-        if (newPW.isPresent() && newPWFile.isPresent()) {
-            final LocalizableMessage message =
-                    ERR_LDAPPWMOD_CONFLICTING_ARGS.get(newPW.getLongIdentifier(), newPWFile
-                            .getLongIdentifier());
-            errPrintln(message);
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-        }
-
-        if (currentPW.isPresent() && currentPWFile.isPresent()) {
-            final LocalizableMessage message =
-                    ERR_LDAPPWMOD_CONFLICTING_ARGS.get(currentPW.getLongIdentifier(), currentPWFile
-                            .getLongIdentifier());
-            errPrintln(message);
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+            throwIfArgumentsConflict(newPW, newPWFile);
+            throwIfArgumentsConflict(currentPW, currentPWFile);
+        } catch (final ArgumentException e) {
+            throw newToolParamException(e, e.getMessageObject());
         }
 
         Connection connection;
         try {
-            connection = connectionFactory.getConnection();
+            connection = argParser.getConnectionFactory().getConnection();
         } catch (final LdapException ere) {
-            return Utils.printErrorMessage(this, ere);
+            return printErrorMessage(this, ere, ERR_LDAPPWMOD_FAILED);
         }
 
         if (proxyAuthzID.isPresent()) {
@@ -252,37 +218,16 @@
         try {
             result = connection.extendedRequest(request);
         } catch (final LdapException e) {
-            LocalizableMessage message =
-                    ERR_LDAPPWMOD_FAILED.get(e.getResult().getResultCode().intValue(), e
-                            .getResult().getResultCode().toString());
-            errPrintln(message);
-
-            final String errorMessage = e.getResult().getDiagnosticMessage();
-            if (errorMessage != null && errorMessage.length() > 0) {
-                message = ERR_LDAPPWMOD_FAILURE_ERROR_MESSAGE.get(errorMessage);
-                errPrintln(message);
-            }
-
-            final String matchedDN = e.getResult().getMatchedDN();
-            if (matchedDN != null && matchedDN.length() > 0) {
-                message = ERR_LDAPPWMOD_FAILURE_MATCHED_DN.get(matchedDN);
-                errPrintln(message);
-            }
-            return e.getResult().getResultCode().intValue();
+            return printErrorMessage(this, e, ERR_LDAPPWMOD_FAILED);
         }
 
         println(INFO_LDAPPWMOD_SUCCESSFUL.get());
-
-        final String additionalInfo = result.getDiagnosticMessage();
-        if (additionalInfo != null && additionalInfo.length() > 0) {
-            println(INFO_LDAPPWMOD_ADDITIONAL_INFO.get(additionalInfo));
-        }
-
+        Utils.printlnTextMsg(this, INFO_LDAPPWMOD_ADDITIONAL_INFO, result.getDiagnosticMessage());
         if (result.getGeneratedPassword() != null) {
-            println(INFO_LDAPPWMOD_GENERATED_PASSWORD.get(ByteString.valueOfBytes(
-                    result.getGeneratedPassword()).toString()));
+            println(INFO_LDAPPWMOD_GENERATED_PASSWORD.get(
+                    ByteString.valueOfBytes(result.getGeneratedPassword()).toString()));
         }
 
-        return 0;
+        return ResultCode.SUCCESS.intValue();
     }
 }
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java
index 0781922..3e8d9ab 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java
@@ -16,8 +16,6 @@
  */
 package com.forgerock.opendj.ldap.tools;
 
-import java.io.BufferedReader;
-import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintStream;
 import java.util.ArrayList;
@@ -27,10 +25,10 @@
 import java.util.StringTokenizer;
 
 import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.LocalizableMessageDescriptor;
 import org.forgerock.i18n.LocalizedIllegalArgumentException;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.Connection;
-import org.forgerock.opendj.ldap.ConnectionFactory;
 import org.forgerock.opendj.ldap.DN;
 import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.DecodeOptions;
@@ -40,7 +38,6 @@
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchResultHandler;
 import org.forgerock.opendj.ldap.SearchScope;
-import org.forgerock.opendj.ldap.controls.AssertionRequestControl;
 import org.forgerock.opendj.ldap.controls.Control;
 import org.forgerock.opendj.ldap.controls.EntryChangeNotificationResponseControl;
 import org.forgerock.opendj.ldap.controls.GetEffectiveRightsRequestControl;
@@ -63,7 +60,6 @@
 import org.forgerock.opendj.ldif.LDIFEntryWriter;
 
 import com.forgerock.opendj.cli.ArgumentException;
-import com.forgerock.opendj.cli.ArgumentParser;
 import com.forgerock.opendj.cli.BooleanArgument;
 import com.forgerock.opendj.cli.ConnectionFactoryProvider;
 import com.forgerock.opendj.cli.ConsoleApplication;
@@ -75,8 +71,16 @@
 
 import static com.forgerock.opendj.cli.CliMessages.INFO_NUM_ENTRIES_PLACEHOLDER;
 import static com.forgerock.opendj.cli.ToolVersionHandler.newSdkVersionHandler;
+import static com.forgerock.opendj.ldap.tools.LDAPToolException.newToolParamException;
+import static com.forgerock.opendj.ldap.tools.Utils.addControlsToRequest;
+import static com.forgerock.opendj.ldap.tools.Utils.ensureLdapProtocolVersionIsSupported;
 import static com.forgerock.opendj.ldap.tools.Utils.printErrorMessage;
 import static com.forgerock.opendj.ldap.tools.Utils.printPasswordPolicyResults;
+import static com.forgerock.opendj.ldap.tools.Utils.printlnTextMsg;
+import static com.forgerock.opendj.ldap.tools.Utils.readAssertionControl;
+import static com.forgerock.opendj.ldap.tools.Utils.readControls;
+import static com.forgerock.opendj.ldap.tools.Utils.readFiltersFromFile;
+import static com.forgerock.opendj.ldap.tools.Utils.readFilterFromString;
 import static org.forgerock.util.Utils.*;
 
 import static com.forgerock.opendj.cli.ArgumentConstants.*;
@@ -95,11 +99,9 @@
 
             try {
                 final EntryChangeNotificationResponseControl control =
-                        entry.getControl(EntryChangeNotificationResponseControl.DECODER,
-                                new DecodeOptions());
+                        entry.getControl(EntryChangeNotificationResponseControl.DECODER, new DecodeOptions());
                 if (control != null) {
-                    println(INFO_LDAPSEARCH_PSEARCH_CHANGE_TYPE.get(control.getChangeType()
-                            .toString()));
+                    println(INFO_LDAPSEARCH_PSEARCH_CHANGE_TYPE.get(control.getChangeType().toString()));
                     final DN previousDN = control.getPreviousName();
                     if (previousDN != null) {
                         println(INFO_LDAPSEARCH_PSEARCH_PREVIOUS_DN.get(previousDN.toString()));
@@ -111,20 +113,14 @@
 
             try {
                 final AccountUsabilityResponseControl control =
-                        entry.getControl(AccountUsabilityResponseControl.DECODER,
-                                new DecodeOptions());
-
+                        entry.getControl(AccountUsabilityResponseControl.DECODER, new DecodeOptions());
                 if (control != null) {
                     println(INFO_LDAPSEARCH_ACCTUSABLE_HEADER.get());
                     if (control.isUsable()) {
                         println(INFO_LDAPSEARCH_ACCTUSABLE_IS_USABLE.get());
                         if (control.getSecondsBeforeExpiration() > 0) {
-                            final int timeToExp = control.getSecondsBeforeExpiration();
-                            final LocalizableMessage timeToExpStr =
-                                    secondsToTimeString(timeToExp);
-
-                            println(INFO_LDAPSEARCH_ACCTUSABLE_TIME_UNTIL_EXPIRATION
-                                    .get(timeToExpStr));
+                            println(INFO_LDAPSEARCH_ACCTUSABLE_TIME_UNTIL_EXPIRATION.get(
+                                    secondsToTimeString(control.getSecondsBeforeExpiration())));
                         }
                     } else {
                         println(INFO_LDAPSEARCH_ACCTUSABLE_NOT_USABLE.get());
@@ -136,21 +132,16 @@
                         }
                         if (control.isExpired()) {
                             println(INFO_LDAPSEARCH_ACCTUSABLE_PW_EXPIRED.get());
-
                             if (control.getRemainingGraceLogins() > 0) {
-                                println(INFO_LDAPSEARCH_ACCTUSABLE_REMAINING_GRACE.get(control
-                                        .getRemainingGraceLogins()));
+                                println(INFO_LDAPSEARCH_ACCTUSABLE_REMAINING_GRACE.get(
+                                        control.getRemainingGraceLogins()));
                             }
                         }
                         if (control.isLocked()) {
                             println(INFO_LDAPSEARCH_ACCTUSABLE_LOCKED.get());
                             if (control.getSecondsBeforeUnlock() > 0) {
-                                final int timeToUnlock = control.getSecondsBeforeUnlock();
-                                final LocalizableMessage timeToUnlockStr =
-                                        secondsToTimeString(timeToUnlock);
-
-                                println(INFO_LDAPSEARCH_ACCTUSABLE_TIME_UNTIL_UNLOCK
-                                        .get(timeToUnlockStr));
+                                println(INFO_LDAPSEARCH_ACCTUSABLE_TIME_UNTIL_UNLOCK.get(
+                                        secondsToTimeString(control.getSecondsBeforeUnlock())));
                             }
                         }
                     }
@@ -184,7 +175,17 @@
      */
 
     public static void main(final String[] args) {
-        final int retCode = new LDAPSearch().run(args);
+        final LDAPSearch ldapSearch = new LDAPSearch();
+        int retCode;
+        try {
+            retCode = ldapSearch.run(args);
+        } catch (final LDAPToolException e) {
+            e.printErrorMessage(ldapSearch);
+            retCode = e.getResultCode();
+        } catch (final ArgumentException e) {
+            ldapSearch.errPrintln(ERR_ERROR_PARSING_ARGS.get(e.getMessageObject()));
+            retCode = ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+        }
         System.exit(filterExitCode(retCode));
     }
 
@@ -217,18 +218,17 @@
     }
 
     /** Run ldapsearch with provided command-line arguments. */
-    int run(final String[] args) {
+    int run(final String[] args) throws LDAPToolException, ArgumentException {
         // Create the command-line argument parser for use with this program.
         final LocalizableMessage toolDescription = INFO_LDAPSEARCH_TOOL_DESCRIPTION.get();
-        final ArgumentParser argParser =
-                new ArgumentParser(LDAPSearch.class.getName(), toolDescription, false, true, 0, 0,
-                        "[filter] [attributes ...]");
+        final LDAPToolArgumentParser argParser = LDAPToolArgumentParser.builder(LDAPSearch.class.getName())
+                .toolDescription(toolDescription)
+                .trailingArguments("[filter] [attributes ...]")
+                .build();
         argParser.setVersionHandler(newSdkVersionHandler());
         argParser.setShortToolDescription(REF_SHORT_DESC_LDAPSEARCH.get());
 
         ConnectionFactoryProvider connectionFactoryProvider;
-        ConnectionFactory connectionFactory;
-        BindRequest bindRequest;
 
         BooleanArgument countEntries;
         BooleanArgument dontWrap;
@@ -236,11 +236,11 @@
         BooleanArgument typesOnly;
         IntegerArgument simplePageSize;
         IntegerArgument timeLimit;
-        IntegerArgument version;
+        IntegerArgument ldapProtocolVersion;
         StringArgument baseDN;
         StringArgument controlStr;
         MultiChoiceArgument<DereferenceAliasesPolicy> dereferencePolicy;
-        StringArgument filename;
+        StringArgument filterFile;
         StringArgument matchedValuesFilter;
         StringArgument pSearchInfo;
         MultiChoiceArgument<SearchScope> searchScope;
@@ -253,8 +253,7 @@
         IntegerArgument sizeLimit;
         try {
             connectionFactoryProvider = new ConnectionFactoryProvider(argParser, this);
-            final StringArgument propertiesFileArgument =
-                propertiesFileArgument();
+            final StringArgument propertiesFileArgument = propertiesFileArgument();
             argParser.addArgument(propertiesFileArgument);
             argParser.setFilePropertiesArgument(propertiesFileArgument);
 
@@ -273,12 +272,9 @@
             searchScope = searchScopeArgument();
             argParser.addArgument(searchScope);
 
-            filename =
-                    StringArgument.builder(OPTION_LONG_FILENAME)
-                            .shortIdentifier(OPTION_SHORT_FILENAME)
-                            .description(INFO_SEARCH_DESCRIPTION_FILENAME.get())
-                            .valuePlaceholder(INFO_FILE_PLACEHOLDER.get())
-                            .buildAndAddToParser(argParser);
+            filterFile = filenameArgument(INFO_SEARCH_DESCRIPTION_FILENAME.get());
+            argParser.addArgument(filterFile);
+
             proxyAuthzID =
                     StringArgument.builder(OPTION_LONG_PROXYAUTHID)
                             .shortIdentifier(OPTION_SHORT_PROXYAUTHID)
@@ -322,14 +318,10 @@
                             .description(INFO_DESCRIPTION_VLV.get())
                             .valuePlaceholder(INFO_VLV_PLACEHOLDER.get())
                             .buildAndAddToParser(argParser);
-            controlStr =
-                    StringArgument.builder("control")
-                            .shortIdentifier('J')
-                            .description(INFO_DESCRIPTION_CONTROLS.get())
-                            .docDescriptionSupplement(SUPPLEMENT_DESCRIPTION_CONTROLS.get())
-                            .multiValued()
-                            .valuePlaceholder(INFO_LDAP_CONTROL_PLACEHOLDER.get())
-                            .buildAndAddToParser(argParser);
+
+            controlStr = controlArgument();
+            argParser.addArgument(controlStr);
+
             effectiveRightsUser =
                     StringArgument.builder(OPTION_LONG_EFFECTIVERIGHTSUSER)
                             .shortIdentifier(OPTION_SHORT_EFFECTIVERIGHTSUSER)
@@ -344,8 +336,8 @@
                             .valuePlaceholder(INFO_ATTRIBUTE_PLACEHOLDER.get())
                             .buildAndAddToParser(argParser);
 
-            version = ldapVersionArgument();
-            argParser.addArgument(version);
+            ldapProtocolVersion = ldapVersionArgument();
+            argParser.addArgument(ldapProtocolVersion);
 
             dereferencePolicy =
                     MultiChoiceArgument.<DereferenceAliasesPolicy>builder("dereferencePolicy")
@@ -397,68 +389,28 @@
             argParser.addArgument(showUsage);
             argParser.setUsageArgument(showUsage, getOutputStream());
         } catch (final ArgumentException ae) {
-            errPrintln(ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+            throw newToolParamException(ae, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
         }
 
-        // Parse the command-line arguments provided to this program.
-        try {
-            argParser.parseArguments(args);
-
-            // If we should just display usage or version information,
-            // then print it and exit.
-            if (argParser.usageOrVersionDisplayed()) {
-                return 0;
-            }
-
-            connectionFactory = connectionFactoryProvider.getUnauthenticatedConnectionFactory();
-            bindRequest = connectionFactoryProvider.getBindRequest();
-        } catch (final ArgumentException ae) {
-            argParser.displayMessageAndUsageReference(getErrStream(), ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+        argParser.parseArguments(args, getErrStream(), connectionFactoryProvider);
+        if (argParser.usageOrVersionDisplayed()) {
+            return ResultCode.SUCCESS.intValue();
         }
 
         final List<Filter> filters = new LinkedList<>();
         final List<String> attributes = new LinkedList<>();
-        final ArrayList<String> filterAndAttributeStrings = argParser.getTrailingArguments();
+        final List<String> filterAndAttributeStrings = argParser.getTrailingArguments();
         if (!filterAndAttributeStrings.isEmpty()) {
-            /* The list of trailing arguments should be structured as follow:
-             - If a filter file is present, trailing arguments are
-             considered as attributes
-             - If filter file is not present, the first trailing argument is
-             considered the filter, the other as attributes.*/
-            if (!filename.isPresent()) {
-                final String filterString = filterAndAttributeStrings.remove(0);
-
-                try {
-                    filters.add(Filter.valueOf(filterString));
-                } catch (final LocalizedIllegalArgumentException e) {
-                    errPrintln(e.getMessageObject());
-                    return ResultCode.CLIENT_SIDE_FILTER_ERROR.intValue();
-                }
+            // If filter file is not present, the first trailing argument is considered the filter
+            if (!filterFile.isPresent()) {
+                filters.add(readFilterFromString(filterAndAttributeStrings.remove(0)));
             }
-            // The rest are attributes
+            // The rest of trailing argument are attributes
             attributes.addAll(filterAndAttributeStrings);
         }
 
-        if (filename.isPresent()) {
-            // Read the filter strings.
-            try (BufferedReader in = new BufferedReader(new FileReader(filename.getValue()))) {
-                String line = null;
-                while ((line = in.readLine()) != null) {
-                    if ("".equals(line.trim())) {
-                        // ignore empty lines.
-                        continue;
-                    }
-                    filters.add(Filter.valueOf(line));
-                }
-            } catch (final LocalizedIllegalArgumentException e) {
-                errPrintln(e.getMessageObject());
-                return ResultCode.CLIENT_SIDE_FILTER_ERROR.intValue();
-            } catch (final IOException e) {
-                errPrintln(LocalizableMessage.raw(e.toString()));
-                return ResultCode.CLIENT_SIDE_FILTER_ERROR.intValue();
-            }
+        if (filterFile.isPresent()) {
+            filters.addAll(readFiltersFromFile(filterFile.getValue()));
         }
 
         if (filters.isEmpty()) {
@@ -466,182 +418,43 @@
             return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
         }
 
-        SearchScope scope;
+        final SearchScope scope = searchScope.getTypedValue();
+        final SearchRequest search;
         try {
-            scope = searchScope.getTypedValue();
-        } catch (final ArgumentException ex1) {
-            errPrintln(ex1.getMessageObject());
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-        }
-
-        SearchRequest search;
-        try {
-            search =
-                    Requests.newSearchRequest(DN.valueOf(baseDN.getValue()), scope, filters.get(0),
-                            attributes.toArray(new String[attributes.size()]));
+            search = Requests.newSearchRequest(DN.valueOf(baseDN.getValue()), scope, filters.get(0),
+                                               attributes.toArray(new String[attributes.size()]));
+            ensureLdapProtocolVersionIsSupported(ldapProtocolVersion);
         } catch (final LocalizedIllegalArgumentException e) {
             errPrintln(e.getMessageObject());
             return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
         }
 
-        // Read the LDAP version number.
-        try {
-            final int versionNumber = version.getIntValue();
-            if (versionNumber != 2 && versionNumber != 3) {
-                errPrintln(ERR_DESCRIPTION_INVALID_VERSION.get(String.valueOf(versionNumber)));
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-            }
-        } catch (final ArgumentException ae) {
-            errPrintln(ERR_DESCRIPTION_INVALID_VERSION.get(version.getValue()));
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-        }
-
         search.setTypesOnly(typesOnly.isPresent());
-        // searchOptions.setShowOperations(noop.isPresent());
-        // searchOptions.setVerbose(verbose.isPresent());
-        // searchOptions.setContinueOnError(continueOnError.isPresent());
-        // searchOptions.setEncoding(encodingStr.getValue());
-        // searchOptions.setCountMatchingEntries(countEntries.isPresent());
-        try {
-            search.setTimeLimit(timeLimit.getIntValue());
-            search.setSizeLimit(sizeLimit.getIntValue());
-        } catch (final ArgumentException ex1) {
-            errPrintln(ex1.getMessageObject());
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-        }
-        try {
-            search.setDereferenceAliasesPolicy(dereferencePolicy.getTypedValue());
-        } catch (final ArgumentException ex1) {
-            errPrintln(ex1.getMessageObject());
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-        }
-
-        if (controlStr.isPresent()) {
-            for (final String ctrlString : controlStr.getValues()) {
-                try {
-                    final Control ctrl = Utils.getControl(ctrlString);
-                    search.addControl(ctrl);
-                } catch (final DecodeException de) {
-                    errPrintln(ERR_TOOL_INVALID_CONTROL_STRING.get(ctrlString));
-                    ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            }
-        }
+        search.setTimeLimit(timeLimit.getIntValue());
+        search.setSizeLimit(sizeLimit.getIntValue());
+        search.setDereferenceAliasesPolicy(dereferencePolicy.getTypedValue());
+        addControlsToRequest(search, readControls(controlStr));
 
         if (effectiveRightsUser.isPresent()) {
             final String authzID = effectiveRightsUser.getValue();
             if (!authzID.startsWith("dn:")) {
-                errPrintln(ERR_EFFECTIVERIGHTS_INVALID_AUTHZID.get(authzID));
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+                throw newToolParamException(ERR_EFFECTIVERIGHTS_INVALID_AUTHZID.get(authzID));
             }
-            final Control effectiveRightsControl =
-                    GetEffectiveRightsRequestControl.newControl(false, authzID.substring(3),
-                            effectiveRightsAttrs.getValues().toArray(
-                                    new String[effectiveRightsAttrs.getValues().size()]));
-            search.addControl(effectiveRightsControl);
+            final List<String> attrValues = effectiveRightsAttrs.getValues();
+            search.addControl(GetEffectiveRightsRequestControl.newControl(
+                            false, authzID.substring(3), attrValues.toArray(new String[attrValues.size()])));
         }
 
         if (proxyAuthzID.isPresent()) {
-            final Control proxyControl =
-                    ProxiedAuthV2RequestControl.newControl(proxyAuthzID.getValue());
-            search.addControl(proxyControl);
+            search.addControl(ProxiedAuthV2RequestControl.newControl(proxyAuthzID.getValue()));
         }
 
         if (pSearchInfo.isPresent()) {
-            final String infoString = StaticUtils.toLowerCase(pSearchInfo.getValue().trim());
-            boolean changesOnly = true;
-            boolean returnECs = true;
-
-            final StringTokenizer tokenizer = new StringTokenizer(infoString, ":");
-
-            if (!tokenizer.hasMoreTokens()) {
-                errPrintln(ERR_PSEARCH_MISSING_DESCRIPTOR.get());
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-            } else {
-                final String token = tokenizer.nextToken();
-                if (!"ps".equals(token)) {
-                    errPrintln(ERR_PSEARCH_DOESNT_START_WITH_PS.get(infoString));
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            }
-
-            final ArrayList<PersistentSearchChangeType> ct = new ArrayList<>(4);
-            if (tokenizer.hasMoreTokens()) {
-                final StringTokenizer st = new StringTokenizer(tokenizer.nextToken(), ", ");
-                if (!st.hasMoreTokens()) {
-                    ct.add(PersistentSearchChangeType.ADD);
-                    ct.add(PersistentSearchChangeType.DELETE);
-                    ct.add(PersistentSearchChangeType.MODIFY);
-                    ct.add(PersistentSearchChangeType.MODIFY_DN);
-                } else {
-                    do {
-                        final String token = st.nextToken();
-                        if ("add".equals(token)) {
-                            ct.add(PersistentSearchChangeType.ADD);
-                        } else if ("delete".equals(token) || "del".equals(token)) {
-                            ct.add(PersistentSearchChangeType.DELETE);
-                        } else if ("modify".equals(token) || "mod".equals(token)) {
-                            ct.add(PersistentSearchChangeType.MODIFY);
-                        } else if ("modifydn".equals(token) || "moddn".equals(token)
-                                || "modrdn".equals(token)) {
-                            ct.add(PersistentSearchChangeType.MODIFY_DN);
-                        } else if ("any".equals(token) || "all".equals(token)) {
-                            ct.add(PersistentSearchChangeType.ADD);
-                            ct.add(PersistentSearchChangeType.DELETE);
-                            ct.add(PersistentSearchChangeType.MODIFY);
-                            ct.add(PersistentSearchChangeType.MODIFY_DN);
-                        } else {
-                            errPrintln(ERR_PSEARCH_INVALID_CHANGE_TYPE.get(token));
-                            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                        }
-                    } while (st.hasMoreTokens());
-                }
-            }
-
-            if (tokenizer.hasMoreTokens()) {
-                final String token = tokenizer.nextToken();
-                if ("1".equals(token) || "true".equals(token) || "yes".equals(token)) {
-                    changesOnly = true;
-                } else if ("0".equals(token) || "false".equals(token) || "no".equals(token)) {
-                    changesOnly = false;
-                } else {
-                    errPrintln(ERR_PSEARCH_INVALID_CHANGESONLY.get(token));
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            }
-
-            if (tokenizer.hasMoreTokens()) {
-                final String token = tokenizer.nextToken();
-                if ("1".equals(token) || "true".equals(token) || "yes".equals(token)) {
-                    returnECs = true;
-                } else if ("0".equals(token) || "false".equals(token) || "no".equals(token)) {
-                    returnECs = false;
-                } else {
-                    errPrintln(ERR_PSEARCH_INVALID_RETURN_ECS.get(token));
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            }
-
-            final PersistentSearchRequestControl psearchControl =
-                    PersistentSearchRequestControl.newControl(true, changesOnly, returnECs, ct
-                            .toArray(new PersistentSearchChangeType[ct.size()]));
-            search.addControl(psearchControl);
+            search.addControl(computePSearchControl(pSearchInfo));
         }
 
         if (assertionFilter.isPresent()) {
-            final String filterString = assertionFilter.getValue();
-            Filter filter;
-            try {
-                filter = Filter.valueOf(filterString);
-
-                // FIXME -- Change this to the correct OID when the official one
-                // is assigned.
-                final Control assertionControl = AssertionRequestControl.newControl(true, filter);
-                search.addControl(assertionControl);
-            } catch (final LocalizedIllegalArgumentException le) {
-                errPrintln(ERR_LDAP_ASSERTION_INVALID_FILTER.get(le.getMessage()));
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-            }
+            search.addControl(readAssertionControl(assertionFilter.getValue()));
         }
 
         if (matchedValuesFilter.isPresent()) {
@@ -649,84 +462,33 @@
             final List<Filter> mvFilters = new ArrayList<>();
             for (final String s : mvFilterStrings) {
                 try {
-                    final Filter f = Filter.valueOf(s);
-                    mvFilters.add(f);
+                    mvFilters.add(Filter.valueOf(s));
                 } catch (final LocalizedIllegalArgumentException le) {
-                    errPrintln(ERR_LDAP_MATCHEDVALUES_INVALID_FILTER.get(le.getMessage()));
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+                    throw newToolParamException(le, ERR_LDAP_MATCHEDVALUES_INVALID_FILTER.get(le.getMessage()));
                 }
             }
-
-            final MatchedValuesRequestControl mvc =
-                    MatchedValuesRequestControl.newControl(true, mvFilters);
-            search.addControl(mvc);
+            search.addControl(MatchedValuesRequestControl.newControl(true, mvFilters));
         }
 
         if (sortOrder.isPresent()) {
             try {
-                search.addControl(ServerSideSortRequestControl.newControl(false, sortOrder
-                        .getValue()));
+                search.addControl(ServerSideSortRequestControl.newControl(false, sortOrder.getValue()));
             } catch (final LocalizedIllegalArgumentException le) {
-                errPrintln(ERR_LDAP_SORTCONTROL_INVALID_ORDER.get(le.getMessageObject()));
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+                throw newToolParamException(le, ERR_LDAP_SORTCONTROL_INVALID_ORDER.get(le.getMessageObject()));
             }
         }
 
         if (vlvDescriptor.isPresent()) {
-            if (!sortOrder.isPresent()) {
-                final LocalizableMessage message =
-                        ERR_LDAPSEARCH_VLV_REQUIRES_SORT.get(vlvDescriptor.getLongIdentifier(),
-                                sortOrder.getLongIdentifier());
-                errPrintln(message);
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-            }
-
-            final StringTokenizer tokenizer = new StringTokenizer(vlvDescriptor.getValue(), ":");
-            final int numTokens = tokenizer.countTokens();
-            if (numTokens == 3) {
-                try {
-                    final int beforeCount = Integer.parseInt(tokenizer.nextToken());
-                    final int afterCount = Integer.parseInt(tokenizer.nextToken());
-                    final ByteString assertionValue = ByteString.valueOfUtf8(tokenizer.nextToken());
-                    search.addControl(VirtualListViewRequestControl.newAssertionControl(true,
-                            assertionValue, beforeCount, afterCount, null));
-                } catch (final Exception e) {
-                    errPrintln(ERR_LDAPSEARCH_VLV_INVALID_DESCRIPTOR.get());
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            } else if (numTokens == 4) {
-                try {
-                    final int beforeCount = Integer.parseInt(tokenizer.nextToken());
-                    final int afterCount = Integer.parseInt(tokenizer.nextToken());
-                    final int offset = Integer.parseInt(tokenizer.nextToken());
-                    final int contentCount = Integer.parseInt(tokenizer.nextToken());
-                    search.addControl(VirtualListViewRequestControl.newOffsetControl(true, offset,
-                            contentCount, beforeCount, afterCount, null));
-                } catch (final Exception e) {
-                    errPrintln(ERR_LDAPSEARCH_VLV_INVALID_DESCRIPTOR.get());
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            } else {
-                errPrintln(ERR_LDAPSEARCH_VLV_INVALID_DESCRIPTOR.get());
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-            }
+            search.addControl(readVLVControl(vlvDescriptor, sortOrder));
         }
 
         int pageSize = 0;
         if (simplePageSize.isPresent()) {
             if (filters.size() > 1) {
-                errPrintln(ERR_PAGED_RESULTS_REQUIRES_SINGLE_FILTER.get());
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+                throw newToolParamException(ERR_PAGED_RESULTS_REQUIRES_SINGLE_FILTER.get());
             }
-
-            try {
-                pageSize = simplePageSize.getIntValue();
-                search.addControl(SimplePagedResultsControl.newControl(true, pageSize, ByteString
-                        .empty()));
-            } catch (final ArgumentException ae) {
-                errPrintln(ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-            }
+            pageSize = simplePageSize.getIntValue();
+            search.addControl(SimplePagedResultsControl.newControl(true, pageSize, ByteString.empty()));
         }
 
         int wrapColumn = 80;
@@ -740,10 +502,11 @@
              processing was successful or that there were no matching entries,
              based on countEntries.isPresent() (but in either case the return value
              should be zero).*/
-            return 0;
+            return ResultCode.SUCCESS.intValue();
         }
 
-        try (Connection connection = connectionFactory.getConnection()) {
+        try (Connection connection = argParser.getConnectionFactory().getConnection()) {
+            final BindRequest bindRequest = argParser.getBindRequest();
             if (bindRequest != null) {
                 printPasswordPolicyResults(this, connection.bind(bindRequest));
             }
@@ -755,10 +518,8 @@
                 Result result = connection.search(search, resultHandler);
                 try {
                     final ServerSideSortResponseControl control =
-                            result.getControl(ServerSideSortResponseControl.DECODER,
-                                    new DecodeOptions());
-                    if (control != null
-                            && control.getResult() != ResultCode.SUCCESS) {
+                            result.getControl(ServerSideSortResponseControl.DECODER, new DecodeOptions());
+                    if (control != null && ResultCode.SUCCESS != control.getResult()) {
                         println(WARN_LDAPSEARCH_SORT_ERROR.get(control.getResult().toString()));
                     }
                 } catch (final DecodeException e) {
@@ -767,21 +528,13 @@
 
                 try {
                     final VirtualListViewResponseControl control =
-                            result.getControl(VirtualListViewResponseControl.DECODER,
-                                    new DecodeOptions());
+                            result.getControl(VirtualListViewResponseControl.DECODER, new DecodeOptions());
                     if (control != null) {
-                        if (control.getResult() == ResultCode.SUCCESS) {
-                            LocalizableMessage msg =
-                                    INFO_LDAPSEARCH_VLV_TARGET_OFFSET.get(control
-                                            .getTargetPosition());
-                            println(msg);
-
-                            msg = INFO_LDAPSEARCH_VLV_CONTENT_COUNT.get(control.getContentCount());
-                            println(msg);
+                        if (ResultCode.SUCCESS == control.getResult()) {
+                            println(INFO_LDAPSEARCH_VLV_TARGET_OFFSET.get(control.getTargetPosition()));
+                            println(INFO_LDAPSEARCH_VLV_CONTENT_COUNT.get(control.getContentCount()));
                         } else {
-                            final LocalizableMessage msg =
-                                    WARN_LDAPSEARCH_VLV_ERROR.get(control.getResult().toString());
-                            println(msg);
+                            println(WARN_LDAPSEARCH_VLV_ERROR.get(control.getResult().toString()));
                         }
                     }
                 } catch (final DecodeException e) {
@@ -790,9 +543,8 @@
 
                 try {
                     SimplePagedResultsControl control =
-                            result.getControl(SimplePagedResultsControl.DECODER,
-                                    new DecodeOptions());
-                    if (control != null && control.getCookie().length() > 0) {
+                            result.getControl(SimplePagedResultsControl.DECODER, new DecodeOptions());
+                    if (control != null && !control.getCookie().isEmpty()) {
                         if (!isQuiet()) {
                             pressReturnToContinue();
                         }
@@ -811,15 +563,10 @@
                 }
 
                 errPrintln();
-                errPrintln(ERR_TOOL_RESULT_CODE.get(result.getResultCode().intValue(), result
-                        .getResultCode().toString()));
-                if (result.getDiagnosticMessage() != null
-                        && result.getDiagnosticMessage().length() > 0) {
-                    errPrintln(LocalizableMessage.raw(result.getDiagnosticMessage()));
-                }
-                if (result.getMatchedDN() != null && result.getMatchedDN().length() > 0) {
-                    errPrintln(ERR_TOOL_MATCHED_DN.get(result.getMatchedDN()));
-                }
+                final ResultCode rc = result.getResultCode();
+                errPrintln(ERR_TOOL_RESULT_CODE.get(rc.intValue(), rc.toString()));
+                printlnTextMsg(this, result.getDiagnosticMessage());
+                printlnTextMsg(this, ERR_TOOL_MATCHED_DN, result.getMatchedDN());
 
                 filterIndex++;
                 if (filterIndex < filters.size()) {
@@ -832,11 +579,133 @@
                 println(INFO_LDAPSEARCH_MATCHING_ENTRY_COUNT.get(resultHandler.entryCount));
                 println();
             }
-            return 0;
+            return ResultCode.SUCCESS.intValue();
         } catch (final LdapException ere) {
-            return printErrorMessage(this, ere);
+            return printErrorMessage(this, ere, ERR_LDAP_SEARCH_FAILED);
         } finally {
             closeSilently(ldifWriter);
         }
     }
+
+    private Control readVLVControl(final StringArgument vlvDescriptor, final StringArgument sortOrder)
+            throws LDAPToolException {
+        if (!sortOrder.isPresent()) {
+            throw newToolParamException(ERR_LDAPSEARCH_VLV_REQUIRES_SORT.get(
+                    vlvDescriptor.getLongIdentifier(), sortOrder.getLongIdentifier()));
+        }
+
+        final StringTokenizer tokenizer = new StringTokenizer(vlvDescriptor.getValue(), ":");
+        final int numTokens = tokenizer.countTokens();
+        try {
+            if (numTokens == 3) {
+                final int beforeCount = Integer.parseInt(tokenizer.nextToken());
+                final int afterCount = Integer.parseInt(tokenizer.nextToken());
+                final ByteString assertionValue = ByteString.valueOfUtf8(tokenizer.nextToken());
+                return VirtualListViewRequestControl.newAssertionControl(
+                        true, assertionValue, beforeCount, afterCount, null);
+            } else if (numTokens == 4) {
+                final int beforeCount = Integer.parseInt(tokenizer.nextToken());
+                final int afterCount = Integer.parseInt(tokenizer.nextToken());
+                final int offset = Integer.parseInt(tokenizer.nextToken());
+                final int contentCount = Integer.parseInt(tokenizer.nextToken());
+                return VirtualListViewRequestControl.newOffsetControl(
+                        true, offset, contentCount, beforeCount, afterCount, null);
+            } else {
+                throw newToolParamException(ERR_LDAPSEARCH_VLV_INVALID_DESCRIPTOR.get());
+            }
+        } catch (final Exception e) {
+            throw newToolParamException(e, ERR_LDAPSEARCH_VLV_INVALID_DESCRIPTOR.get());
+        }
+    }
+
+    private Control computePSearchControl(final StringArgument pSearchInfo) throws LDAPToolException {
+        final String infoString = StaticUtils.toLowerCase(pSearchInfo.getValue().trim());
+        final StringTokenizer tokenizer = new StringTokenizer(infoString, ":");
+
+        if (!tokenizer.hasMoreTokens()) {
+            throw newToolParamException(ERR_PSEARCH_MISSING_DESCRIPTOR.get());
+        }
+
+        final String pSearchToken = tokenizer.nextToken();
+        if (!"ps".equals(pSearchToken)) {
+            throw newToolParamException(ERR_PSEARCH_DOESNT_START_WITH_PS.get(infoString));
+        }
+
+        final List<PersistentSearchChangeType> pSearchChangeTypes = new ArrayList<>(4);
+        if (tokenizer.hasMoreTokens()) {
+            final StringTokenizer st = new StringTokenizer(tokenizer.nextToken(), ", ");
+            if (!st.hasMoreTokens()) {
+                addAllPersistentSearchChangeTypes(pSearchChangeTypes);
+            } else {
+                do {
+                    addPersistentSearchChangeTypes(st.nextToken(), pSearchChangeTypes);
+                } while (st.hasMoreTokens());
+            }
+        }
+
+        boolean changesOnly = true;
+        if (tokenizer.hasMoreTokens()) {
+            changesOnly = readBooleanToken(tokenizer.nextToken(), ERR_PSEARCH_INVALID_CHANGESONLY);
+
+        }
+
+        boolean returnECs = true;
+        if (tokenizer.hasMoreTokens()) {
+            returnECs = readBooleanToken(tokenizer.nextToken(), ERR_PSEARCH_INVALID_RETURN_ECS);
+        }
+
+        return PersistentSearchRequestControl.newControl(true, changesOnly, returnECs,
+                pSearchChangeTypes.toArray(new PersistentSearchChangeType[pSearchChangeTypes.size()]));
+    }
+
+    private boolean readBooleanToken(final String token, final LocalizableMessageDescriptor.Arg1<Object> errorMsg)
+            throws LDAPToolException {
+        switch (token) {
+        case "1":
+        case "true":
+        case "yes":
+            return true;
+        case "0":
+        case "false":
+        case "no":
+            return false;
+        default:
+            throw newToolParamException(errorMsg.get(token));
+        }
+    }
+
+    private void addPersistentSearchChangeTypes(
+            final String token, final List<PersistentSearchChangeType> pSearchChangeTypes) throws LDAPToolException {
+        switch (token) {
+        case "add":
+            pSearchChangeTypes.add(PersistentSearchChangeType.ADD);
+            break;
+        case "del":
+        case "delete":
+            pSearchChangeTypes.add(PersistentSearchChangeType.DELETE);
+            break;
+        case "mod":
+        case "modify":
+            pSearchChangeTypes.add(PersistentSearchChangeType.MODIFY);
+            break;
+        case "moddn":
+        case "modrdn":
+        case "modifydn":
+            pSearchChangeTypes.add(PersistentSearchChangeType.MODIFY_DN);
+            break;
+        case "any":
+        case "all":
+            addAllPersistentSearchChangeTypes(pSearchChangeTypes);
+            break;
+        default:
+            throw newToolParamException(ERR_PSEARCH_INVALID_CHANGE_TYPE.get(token));
+        }
+    }
+
+    private void addAllPersistentSearchChangeTypes(final List<PersistentSearchChangeType> pSearchChangeTypes) {
+        pSearchChangeTypes.add(PersistentSearchChangeType.ADD);
+        pSearchChangeTypes.add(PersistentSearchChangeType.DELETE);
+        pSearchChangeTypes.add(PersistentSearchChangeType.MODIFY);
+        pSearchChangeTypes.add(PersistentSearchChangeType.MODIFY_DN);
+    }
 }
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPToolArgumentParser.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPToolArgumentParser.java
new file mode 100644
index 0000000..520236f
--- /dev/null
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPToolArgumentParser.java
@@ -0,0 +1,161 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2016 ForgeRock AS.
+ */
+package com.forgerock.opendj.ldap.tools;
+
+import static com.forgerock.opendj.ldap.tools.LDAPToolException.newToolExceptionAlreadyPrinted;
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.ERR_ERROR_PARSING_ARGS;
+
+import java.io.PrintStream;
+
+import com.forgerock.opendj.cli.ArgumentException;
+import com.forgerock.opendj.cli.ArgumentParser;
+import com.forgerock.opendj.cli.ConnectionFactoryProvider;
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.opendj.ldap.ConnectionFactory;
+import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.requests.BindRequest;
+
+/**
+ * Facility class to help ldap* tools to parse arguments and get {@link org.forgerock.opendj.ldap.ConnectionFactory}
+ * and {@link org.forgerock.opendj.ldap.requests.BindRequest} objects back.
+ */
+final class LDAPToolArgumentParser extends ArgumentParser {
+
+    private static final boolean WITH_BIND_REQUEST = true;
+    private static final boolean WITHOUT_BIND_REQUEST = false;
+
+    /**
+     * Returns a builder which can be used for incrementally constructing a new
+     * {@link LDAPToolArgumentParser}.
+     *
+     * @param mainClassName
+     *         The tool main class name.
+     * @return A builder to continue building the parser.
+     */
+    static Builder builder(final String mainClassName) {
+        return new Builder(mainClassName);
+    }
+
+    ConnectionFactory getConnectionFactory() {
+        return connectionFactory;
+    }
+
+    BindRequest getBindRequest() {
+        return bindRequest;
+    }
+
+    /** A fluent API for incrementally constructing ldap argument parser. */
+    static final class Builder {
+        private final String mainClassName;
+        private LocalizableMessage toolDescription;
+        private boolean allowTrailingArgs;
+        private int minTrailingArguments;
+        private int maxTrailingArguments;
+        private String trailingArgsDisplayName;
+        private boolean needAuthenticatedConnectionFactory;
+
+        Builder(final String mainClassName) {
+            this.mainClassName = mainClassName;
+        }
+
+        Builder toolDescription(final LocalizableMessage toolDescription) {
+            this.toolDescription = toolDescription;
+            return this;
+        }
+
+        /** No trailing arguments number limitations. **/
+        Builder trailingArguments(final String displayNames) {
+            return trailingArguments(0, 0, displayNames);
+        }
+
+        /** Specify that the tool accept exactly the provided number of trailing argument(s). */
+        Builder trailingArguments(final int nbTrailingArguments, final String displayNames) {
+            return trailingArguments(nbTrailingArguments, nbTrailingArguments, displayNames);
+        }
+
+        /** Only specifying the minimum number of trailing arguments implies that this number has no upper bound. **/
+        Builder trailingArgumentsUnbounded(final int min, final String displayNames) {
+            return trailingArguments(min, 0, displayNames);
+        }
+
+        Builder trailingArguments(final int min, final int max, final String displayNames) {
+            this.minTrailingArguments = min;
+            this.maxTrailingArguments = max;
+            this.trailingArgsDisplayName = displayNames;
+            this.allowTrailingArgs = true;
+            return this;
+        }
+
+        Builder needAuthenticatedConnectionFactory() {
+            this.needAuthenticatedConnectionFactory = true;
+            return this;
+        }
+
+        LDAPToolArgumentParser build() {
+            return new LDAPToolArgumentParser(mainClassName, toolDescription, false,
+                                              allowTrailingArgs, minTrailingArguments, maxTrailingArguments,
+                                              trailingArgsDisplayName, needAuthenticatedConnectionFactory);
+        }
+    }
+
+    private ConnectionFactory connectionFactory;
+    private BindRequest bindRequest;
+    private final boolean needAuthenticatedCF;
+
+    private LDAPToolArgumentParser(final String mainClassName,
+                                   final LocalizableMessage toolDescription,
+                                   final boolean longArgumentsCaseSensitive,
+                                   final boolean allowsTrailingArguments,
+                                   final int minTrailingArguments,
+                                   final int maxTrailingArguments,
+                                   final String trailingArgsDisplayName,
+                                   final boolean needAuthenticatedCF) {
+        super(mainClassName, toolDescription, longArgumentsCaseSensitive, allowsTrailingArguments,
+              minTrailingArguments, maxTrailingArguments, trailingArgsDisplayName);
+        this.needAuthenticatedCF = needAuthenticatedCF;
+    }
+
+    void parseArguments(final String[] args, final PrintStream stream, final ConnectionFactoryProvider provider)
+            throws LDAPToolException {
+        parseLdapToolArgument(args, provider, stream, WITH_BIND_REQUEST);
+    }
+
+    void parseArgumentsNoBindRequest(final String[] args,
+                                     final PrintStream stream,
+                                     final ConnectionFactoryProvider provider) throws LDAPToolException {
+        parseLdapToolArgument(args, provider, stream, WITHOUT_BIND_REQUEST);
+    }
+
+    private void parseLdapToolArgument(final String[] args,
+                                       final ConnectionFactoryProvider provider,
+                                       final PrintStream stream,
+                                       final boolean withBindRequest) throws LDAPToolException {
+        try {
+            parseArguments(args);
+            if (usageOrVersionDisplayed()) {
+                return;
+            }
+            connectionFactory = needAuthenticatedCF ? provider.getAuthenticatedConnectionFactory()
+                                                    : provider.getUnauthenticatedConnectionFactory();
+            if (withBindRequest) {
+                bindRequest = provider.getBindRequest();
+            }
+        } catch (final ArgumentException ae) {
+            displayMessageAndUsageReference(stream, ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
+            throw newToolExceptionAlreadyPrinted(ae, ResultCode.CLIENT_SIDE_PARAM_ERROR);
+        }
+    }
+}
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPToolException.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPToolException.java
new file mode 100644
index 0000000..ad6900e
--- /dev/null
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPToolException.java
@@ -0,0 +1,68 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ *  Copyright 2016 ForgeRock AS.
+ */
+
+package com.forgerock.opendj.ldap.tools;
+
+import com.forgerock.opendj.cli.ConsoleApplication;
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.opendj.ldap.ResultCode;
+
+@SuppressWarnings("serial")
+final class LDAPToolException extends Exception {
+
+    static LDAPToolException newToolExceptionAlreadyPrinted(final Exception rootException, ResultCode rc) {
+        return new LDAPToolException(rootException, rc);
+    }
+
+    static LDAPToolException newToolParamException(final Exception rootException, final LocalizableMessage message) {
+        return new LDAPToolException(rootException, ResultCode.CLIENT_SIDE_PARAM_ERROR, message);
+    }
+
+    static LDAPToolException newToolParamException(final LocalizableMessage message) {
+        return new LDAPToolException(null, ResultCode.CLIENT_SIDE_PARAM_ERROR, message);
+    }
+
+    static LDAPToolException newToolException(final Exception rootException,
+                                              final ResultCode rc,
+                                              final LocalizableMessage message) {
+        return new LDAPToolException(rootException, rc, message);
+    }
+
+    private final ResultCode resultCode;
+    private final LocalizableMessage errorMsg;
+
+    private LDAPToolException(final Exception rootException, final ResultCode rc) {
+        super(rootException);
+        this.resultCode = rc;
+        this.errorMsg = null;
+    }
+
+    private LDAPToolException(final Exception rootException, final ResultCode rc, final LocalizableMessage errorMsg) {
+        super(errorMsg.toString(), rootException);
+        this.resultCode = rc;
+        this.errorMsg = errorMsg;
+    }
+
+    int getResultCode() {
+        return resultCode.intValue();
+    }
+
+    void printErrorMessage(final ConsoleApplication console) {
+        if (errorMsg != null) {
+            console.errPrintln(errorMsg);
+        }
+    }
+}
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFDiff.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFDiff.java
index 0a90670..61838cf 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFDiff.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFDiff.java
@@ -19,15 +19,16 @@
 import static com.forgerock.opendj.cli.ArgumentConstants.OPTION_LONG_OUTPUT_LDIF_FILENAME;
 import static com.forgerock.opendj.cli.ArgumentConstants.OPTION_SHORT_OUTPUT_LDIF_FILENAME;
 import static com.forgerock.opendj.cli.ToolVersionHandler.newSdkVersionHandler;
+import static com.forgerock.opendj.ldap.tools.LDAPToolException.newToolParamException;
 import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
 import static com.forgerock.opendj.cli.Utils.filterExitCode;
 import static com.forgerock.opendj.cli.CommonArguments.*;
 
+import static com.forgerock.opendj.ldap.tools.Utils.getLDIFToolInputStream;
+import static com.forgerock.opendj.ldap.tools.Utils.getLDIFToolOutputStream;
+import static com.forgerock.opendj.ldap.tools.Utils.parseArguments;
 import static org.forgerock.util.Utils.closeSilently;
 
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -59,7 +60,14 @@
      *            The command-line arguments provided to this program.
      */
     public static void main(final String[] args) {
-        final int retCode = new LDIFDiff().run(args);
+        final LDIFDiff ldifDiff = new LDIFDiff();
+        int retCode;
+        try {
+            retCode = ldifDiff.run(args);
+        } catch (final LDAPToolException e) {
+            e.printErrorMessage(ldifDiff);
+            retCode = e.getResultCode();
+        }
         System.exit(filterExitCode(retCode));
     }
 
@@ -67,11 +75,13 @@
         // Nothing to do.
     }
 
-    private int run(final String[] args) {
+    private int run(final String[] args) throws LDAPToolException {
         // Create the command-line argument parser for use with this program.
         final LocalizableMessage toolDescription = INFO_LDIFDIFF_TOOL_DESCRIPTION.get();
-        final ArgumentParser argParser = new ArgumentParser(
-            LDIFDiff.class.getName(), toolDescription, false, true, 2, 2, "source target");
+        final ArgumentParser argParser = LDAPToolArgumentParser.builder(LDIFDiff.class.getName())
+                .toolDescription(toolDescription)
+                .trailingArguments(2, "source target")
+                .build();
         argParser.setVersionHandler(newSdkVersionHandler());
         argParser.setShortToolDescription(REF_SHORT_DESC_LDIFDIFF.get());
 
@@ -91,22 +101,12 @@
             argParser.addArgument(showUsage);
             argParser.setUsageArgument(showUsage, getOutputStream());
         } catch (final ArgumentException ae) {
-            final LocalizableMessage message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
-            errPrintln(message);
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+            throw newToolParamException(ae, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
         }
 
-        // Parse the command-line arguments provided to this program.
-        try {
-            argParser.parseArguments(args);
-
-            /* If we should just display usage or version information, then print it and exit. */
-            if (argParser.usageOrVersionDisplayed()) {
-                return ResultCode.SUCCESS.intValue();
-            }
-        } catch (final ArgumentException ae) {
-            argParser.displayMessageAndUsageReference(getErrStream(), ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+        parseArguments(argParser, getErrorStream(), args);
+        if (argParser.usageOrVersionDisplayed()) {
+            return ResultCode.SUCCESS.intValue();
         }
 
         InputStream sourceInputStream = null;
@@ -114,68 +114,15 @@
         OutputStream outputStream = null;
 
         try {
-            // First source file.
             final List<String> trailingArguments = argParser.getTrailingArguments();
-            if (!"-".equals(trailingArguments.get(0))) {
-                try {
-                    sourceInputStream = new FileInputStream(trailingArguments.get(0));
-                } catch (final FileNotFoundException e) {
-                    final LocalizableMessage message =
-                            ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ.get(trailingArguments.get(0), e
-                                    .getLocalizedMessage());
-                    errPrintln(message);
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
+            sourceInputStream = getLDIFToolInputStream(this, trailingArguments.get(0));
+            targetInputStream = getLDIFToolInputStream(this, trailingArguments.get(1));
+            outputStream = getLDIFToolOutputStream(this, outputFilename);
+
+            if (System.in == sourceInputStream && System.in  == targetInputStream) {
+                throw newToolParamException(ERR_LDIFDIFF_MULTIPLE_USES_OF_STDIN.get());
             }
 
-            // Patch file.
-            if (!"-".equals(trailingArguments.get(1))) {
-                try {
-                    targetInputStream = new FileInputStream(trailingArguments.get(1));
-                } catch (final FileNotFoundException e) {
-                    final LocalizableMessage message =
-                            ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ.get(trailingArguments.get(1), e
-                                    .getLocalizedMessage());
-                    errPrintln(message);
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            }
-
-            // Output file.
-            if (outputFilename.isPresent() && !"-".equals(outputFilename.getValue())) {
-                try {
-                    outputStream = new FileOutputStream(outputFilename.getValue());
-                } catch (final FileNotFoundException e) {
-                    final LocalizableMessage message =
-                            ERR_LDIF_FILE_CANNOT_OPEN_FOR_WRITE.get(outputFilename.getValue(), e
-                                    .getLocalizedMessage());
-                    errPrintln(message);
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            }
-
-            // Default to stdin/stdout for all streams if not specified.
-            if (sourceInputStream == null) {
-                // Command line parameter was "-".
-                sourceInputStream = System.in;
-            }
-
-            if (targetInputStream == null) {
-                targetInputStream = System.in;
-            }
-
-            if (outputStream == null) {
-                outputStream = System.out;
-            }
-
-            /* Check that we are not attempting to read both the source and target from stdin. */
-            if (sourceInputStream == targetInputStream) {
-                final LocalizableMessage message = ERR_LDIFDIFF_MULTIPLE_USES_OF_STDIN.get();
-                errPrintln(message);
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-            }
-
-            // Perform the diff.
             try (LDIFEntryReader sourceReader = new LDIFEntryReader(sourceInputStream);
                 LDIFEntryReader targetReader = new LDIFEntryReader(targetInputStream);
                 LDIFChangeRecordWriter outputWriter = new LDIFChangeRecordWriter(outputStream)) {
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFModify.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFModify.java
index 5e6a3bc..ff016b5 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFModify.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFModify.java
@@ -18,14 +18,15 @@
 import static com.forgerock.opendj.cli.ArgumentConstants.OPTION_LONG_OUTPUT_LDIF_FILENAME;
 import static com.forgerock.opendj.cli.ArgumentConstants.OPTION_SHORT_OUTPUT_LDIF_FILENAME;
 import static com.forgerock.opendj.cli.ToolVersionHandler.newSdkVersionHandler;
+import static com.forgerock.opendj.ldap.tools.LDAPToolException.newToolParamException;
 import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
 import static com.forgerock.opendj.cli.Utils.filterExitCode;
+import static com.forgerock.opendj.ldap.tools.Utils.getLDIFToolInputStream;
+import static com.forgerock.opendj.ldap.tools.Utils.getLDIFToolOutputStream;
+import static com.forgerock.opendj.ldap.tools.Utils.parseArguments;
 import static org.forgerock.util.Utils.closeSilently;
 import static com.forgerock.opendj.cli.CommonArguments.*;
 
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -64,7 +65,14 @@
      *            The command-line arguments provided to this program.
      */
     public static void main(final String[] args) {
-        final int retCode = new LDIFModify().run(args);
+        final LDIFModify ldifModify = new LDIFModify();
+        int retCode;
+        try {
+            retCode = ldifModify.run(args);
+        } catch (final LDAPToolException e) {
+            e.printErrorMessage(ldifModify);
+            retCode = e.getResultCode();
+        }
         System.exit(filterExitCode(retCode));
     }
 
@@ -72,11 +80,13 @@
         // Nothing to do.
     }
 
-    private int run(final String[] args) {
+    private int run(final String[] args) throws LDAPToolException {
         // Create the command-line argument parser for use with this program.
         final LocalizableMessage toolDescription = INFO_LDIFMODIFY_TOOL_DESCRIPTION.get();
-        final ArgumentParser argParser = new ArgumentParser(
-            LDIFModify.class.getName(), toolDescription, false, true, 1, 2, "source [changes]");
+        final ArgumentParser argParser = LDAPToolArgumentParser.builder(LDIFModify.class.getName())
+                .toolDescription(toolDescription)
+                .trailingArguments(1, 2, "source [changes]")
+                .build();
         argParser.setVersionHandler(newSdkVersionHandler());
         argParser.setShortToolDescription(REF_SHORT_DESC_LDIFMODIFY.get());
 
@@ -100,23 +110,12 @@
             argParser.addArgument(showUsage);
             argParser.setUsageArgument(showUsage, getOutputStream());
         } catch (final ArgumentException ae) {
-            final LocalizableMessage message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
-            errPrintln(message);
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+            throw newToolParamException(ae, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
         }
 
-        // Parse the command-line arguments provided to this program.
-        try {
-            argParser.parseArguments(args);
-
-            // If we should just display usage or version information,
-            // then print it and exit.
-            if (argParser.usageOrVersionDisplayed()) {
-                return ResultCode.SUCCESS.intValue();
-            }
-        } catch (final ArgumentException ae) {
-            argParser.displayMessageAndUsageReference(getErrStream(), ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+        parseArguments(argParser, getErrStream(), args);
+        if (argParser.usageOrVersionDisplayed()) {
+            return ResultCode.SUCCESS.intValue();
         }
 
         InputStream sourceInputStream = null;
@@ -127,68 +126,15 @@
         LDIFEntryWriter outputWriter = null;
 
         try {
-            // First source file.
             final List<String> trailingArguments = argParser.getTrailingArguments();
-            if (!"-".equals(trailingArguments.get(0))) {
-                try {
-                    sourceInputStream = new FileInputStream(trailingArguments.get(0));
-                } catch (final FileNotFoundException e) {
-                    final LocalizableMessage message =
-                            ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ.get(trailingArguments.get(0), e
-                                    .getLocalizedMessage());
-                    errPrintln(message);
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
+            sourceInputStream = getLDIFToolInputStream(this, trailingArguments.get(0));
+            changesInputStream = getLDIFToolInputStream(this, trailingArguments.get(1));
+            outputStream = getLDIFToolOutputStream(this, outputFilename);
+
+            if (System.in == sourceInputStream && System.in == changesInputStream) {
+                throw newToolParamException(ERR_LDIFMODIFY_MULTIPLE_USES_OF_STDIN.get());
             }
 
-            // Patch file.
-            if (trailingArguments.size() > 1 && !"-".equals(trailingArguments.get(1))) {
-                try {
-                    changesInputStream = new FileInputStream(trailingArguments.get(1));
-                } catch (final FileNotFoundException e) {
-                    final LocalizableMessage message =
-                            ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ.get(trailingArguments.get(1), e
-                                    .getLocalizedMessage());
-                    errPrintln(message);
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            }
-
-            // Output file.
-            if (outputFilename.isPresent() && !"-".equals(outputFilename.getValue())) {
-                try {
-                    outputStream = new FileOutputStream(outputFilename.getValue());
-                } catch (final FileNotFoundException e) {
-                    final LocalizableMessage message =
-                            ERR_LDIF_FILE_CANNOT_OPEN_FOR_WRITE.get(outputFilename.getValue(), e
-                                    .getLocalizedMessage());
-                    errPrintln(message);
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            }
-
-            // Default to stdin/stdout for all streams if not specified.
-            if (sourceInputStream == null) {
-                // Command line parameter was "-".
-                sourceInputStream = System.in;
-            }
-
-            if (changesInputStream == null) {
-                changesInputStream = System.in;
-            }
-
-            if (outputStream == null) {
-                outputStream = System.out;
-            }
-
-            /* Check that we are not attempting to read both the source and changes from stdin. */
-            if (sourceInputStream == changesInputStream) {
-                final LocalizableMessage message = ERR_LDIFMODIFY_MULTIPLE_USES_OF_STDIN.get();
-                errPrintln(message);
-                return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-            }
-
-            // Apply the changes.
             sourceReader = new LDIFEntryReader(sourceInputStream);
             changesReader = new LDIFChangeRecordReader(changesInputStream);
             outputWriter = new LDIFEntryWriter(outputStream);
@@ -198,8 +144,7 @@
                 public Entry handleDuplicateEntry(final AddRequest change, final Entry existingEntry)
                         throws DecodeException {
                     try {
-                        RejectedChangeRecordListener.FAIL_FAST.handleDuplicateEntry(change,
-                                existingEntry);
+                        RejectedChangeRecordListener.FAIL_FAST.handleDuplicateEntry(change, existingEntry);
                     } catch (final DecodeException e) {
                         logErrorOrFail(e);
                     }
@@ -210,8 +155,8 @@
                 public Entry handleDuplicateEntry(final ModifyDNRequest change,
                         final Entry existingEntry, final Entry renamedEntry) throws DecodeException {
                     try {
-                        RejectedChangeRecordListener.FAIL_FAST.handleDuplicateEntry(change,
-                                existingEntry, renamedEntry);
+                        RejectedChangeRecordListener.FAIL_FAST.handleDuplicateEntry(
+                                change, existingEntry, renamedEntry);
                     } catch (final DecodeException e) {
                         logErrorOrFail(e);
                     }
@@ -219,44 +164,40 @@
                 }
 
                 @Override
-                public void handleRejectedChangeRecord(final AddRequest change,
-                        final LocalizableMessage reason) throws DecodeException {
+                public void handleRejectedChangeRecord(
+                        final AddRequest change, final LocalizableMessage reason) throws DecodeException {
                     try {
-                        RejectedChangeRecordListener.FAIL_FAST.handleRejectedChangeRecord(change,
-                                reason);
+                        RejectedChangeRecordListener.FAIL_FAST.handleRejectedChangeRecord(change, reason);
                     } catch (final DecodeException e) {
                         logErrorOrFail(e);
                     }
                 }
 
                 @Override
-                public void handleRejectedChangeRecord(final DeleteRequest change,
-                        final LocalizableMessage reason) throws DecodeException {
+                public void handleRejectedChangeRecord(
+                        final DeleteRequest change, final LocalizableMessage reason) throws DecodeException {
                     try {
-                        RejectedChangeRecordListener.FAIL_FAST.handleRejectedChangeRecord(change,
-                                reason);
+                        RejectedChangeRecordListener.FAIL_FAST.handleRejectedChangeRecord(change, reason);
                     } catch (final DecodeException e) {
                         logErrorOrFail(e);
                     }
                 }
 
                 @Override
-                public void handleRejectedChangeRecord(final ModifyDNRequest change,
-                        final LocalizableMessage reason) throws DecodeException {
+                public void handleRejectedChangeRecord(
+                        final ModifyDNRequest change, final LocalizableMessage reason) throws DecodeException {
                     try {
-                        RejectedChangeRecordListener.FAIL_FAST.handleRejectedChangeRecord(change,
-                                reason);
+                        RejectedChangeRecordListener.FAIL_FAST.handleRejectedChangeRecord(change, reason);
                     } catch (final DecodeException e) {
                         logErrorOrFail(e);
                     }
                 }
 
                 @Override
-                public void handleRejectedChangeRecord(final ModifyRequest change,
-                        final LocalizableMessage reason) throws DecodeException {
+                public void handleRejectedChangeRecord(
+                        final ModifyRequest change, final LocalizableMessage reason) throws DecodeException {
                     try {
-                        RejectedChangeRecordListener.FAIL_FAST.handleRejectedChangeRecord(change,
-                                reason);
+                        RejectedChangeRecordListener.FAIL_FAST.handleRejectedChangeRecord(change, reason);
                     } catch (final DecodeException e) {
                         logErrorOrFail(e);
                     }
@@ -274,15 +215,14 @@
             LDIF.copyTo(LDIF.patch(sourceReader, changesReader, listener), outputWriter);
         } catch (final IOException e) {
             if (e instanceof LocalizableException) {
-                errPrintln(ERR_LDIFMODIFY_PATCH_FAILED.get(((LocalizableException) e)
-                        .getMessageObject()));
+                errPrintln(ERR_LDIFMODIFY_PATCH_FAILED.get(((LocalizableException) e).getMessageObject()));
             } else {
                 errPrintln(ERR_LDIFMODIFY_PATCH_FAILED.get(e.getLocalizedMessage()));
             }
             return ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
         } finally {
-            closeSilently(sourceReader, changesReader, outputWriter);
-            closeSilently(sourceInputStream, changesInputStream, outputStream);
+            closeSilently(sourceReader, changesReader, outputWriter,
+                          sourceInputStream, changesInputStream, outputStream);
         }
 
         return ResultCode.SUCCESS.intValue();
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFSearch.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFSearch.java
index a844af6..fa326ac 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFSearch.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFSearch.java
@@ -17,20 +17,18 @@
 
 import static com.forgerock.opendj.cli.ArgumentConstants.*;
 import static com.forgerock.opendj.cli.ToolVersionHandler.newSdkVersionHandler;
+import static com.forgerock.opendj.ldap.tools.LDAPToolException.newToolParamException;
 import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
 import static com.forgerock.opendj.cli.Utils.filterExitCode;
 import static com.forgerock.opendj.cli.CommonArguments.*;
 
-import static org.forgerock.util.Utils.closeSilently;
+import static com.forgerock.opendj.ldap.tools.Utils.getLDIFToolInputStream;
+import static com.forgerock.opendj.ldap.tools.Utils.getLDIFToolOutputStream;
+import static com.forgerock.opendj.ldap.tools.Utils.parseArguments;
+import static com.forgerock.opendj.ldap.tools.Utils.readFiltersFromFile;
+import static com.forgerock.opendj.ldap.tools.Utils.readFilterFromString;
 
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FileReader;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -64,7 +62,14 @@
      *            The command-line arguments provided to this program.
      */
     public static void main(final String[] args) {
-        final int retCode = new LDIFSearch().run(args);
+        final LDIFSearch ldifSearch = new LDIFSearch();
+        int retCode;
+        try {
+            retCode = ldifSearch.run(args);
+        } catch (final LDAPToolException e) {
+            e.printErrorMessage(ldifSearch);
+            retCode = e.getResultCode();
+        }
         System.exit(filterExitCode(retCode));
     }
 
@@ -72,11 +77,13 @@
         // Nothing to do.
     }
 
-    private int run(final String[] args) {
+    private int run(final String[] args) throws LDAPToolException {
         /* Create the command-line argument parser for use with this program. */
         final LocalizableMessage toolDescription = INFO_LDIFSEARCH_TOOL_DESCRIPTION.get();
-        final ArgumentParser argParser = new ArgumentParser(
-            LDIFSearch.class.getName(), toolDescription, false, true, 1, 0, "source [filter] [attributes ...]");
+        final ArgumentParser argParser = LDAPToolArgumentParser.builder(LDIFSearch.class.getName())
+                .toolDescription(toolDescription)
+                .trailingArgumentsUnbounded(1, "source [filter] [attributes ...]")
+                .build();
         argParser.setVersionHandler(newSdkVersionHandler());
         argParser.setShortToolDescription(REF_SHORT_DESC_LDIFSEARCH.get());
 
@@ -84,7 +91,7 @@
         final StringArgument outputFilename;
         final BooleanArgument typesOnly;
         final IntegerArgument timeLimit;
-        final StringArgument filename;
+        final StringArgument filterFile;
         final StringArgument baseDN;
         final MultiChoiceArgument<SearchScope> searchScope;
         final IntegerArgument sizeLimit;
@@ -108,12 +115,9 @@
             searchScope = searchScopeArgument();
             argParser.addArgument(searchScope);
 
-            filename =
-                    StringArgument.builder(OPTION_LONG_FILENAME)
-                            .shortIdentifier(OPTION_SHORT_FILENAME)
-                            .description(INFO_SEARCH_DESCRIPTION_FILENAME.get())
-                            .valuePlaceholder(INFO_FILE_PLACEHOLDER.get())
-                            .buildAndAddToParser(argParser);
+            filterFile = filenameArgument(INFO_SEARCH_DESCRIPTION_FILENAME.get());
+            argParser.addArgument(filterFile);
+
             typesOnly =
                     BooleanArgument.builder("typesOnly")
                             .shortIdentifier('A')
@@ -138,67 +142,29 @@
             argParser.addArgument(showUsage);
             argParser.setUsageArgument(showUsage, getOutputStream());
         } catch (final ArgumentException ae) {
-            final LocalizableMessage message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
-            errPrintln(message);
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+            throw newToolParamException(ae, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
         }
 
-        // Parse the command-line arguments provided to this program.
-        try {
-            argParser.parseArguments(args);
-
-            /* If we should just display usage or version information, then print it and exit. */
-            if (argParser.usageOrVersionDisplayed()) {
-                return ResultCode.SUCCESS.intValue();
-            }
-        } catch (final ArgumentException ae) {
-            argParser.displayMessageAndUsageReference(getErrStream(), ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+        parseArguments(argParser, getErrorStream(), args);
+        if (argParser.usageOrVersionDisplayed()) {
+            return ResultCode.SUCCESS.intValue();
         }
 
         final List<Filter> filters = new LinkedList<>();
         final List<String> attributes = new LinkedList<>();
         final List<String> trailingArguments = argParser.getTrailingArguments();
         if (trailingArguments.size() > 1) {
-            final List<String> filterAndAttributeStrings =
-                    trailingArguments.subList(1, trailingArguments.size());
-
-            /* The list of trailing arguments should be structured as follow:
-             - If a filter file is present, trailing arguments are
-             considered as attributes
-             - If filter file is not present, the first trailing argument is
-             considered the filter, the other as attributes.*/
-            if (!filename.isPresent()) {
-                final String filterString = filterAndAttributeStrings.remove(0);
-                try {
-                    filters.add(Filter.valueOf(filterString));
-                } catch (final LocalizedIllegalArgumentException e) {
-                    errPrintln(e.getMessageObject());
-                    return ResultCode.CLIENT_SIDE_FILTER_ERROR.intValue();
-                }
+            final List<String> filterAndAttributeStrings = trailingArguments.subList(1, trailingArguments.size());
+            // If filter file is not present, the first trailing argument is considered the filter
+            if (!filterFile.isPresent()) {
+                filters.add(readFilterFromString(filterAndAttributeStrings.remove(0)));
             }
-            // The rest are attributes
+            // The rest of trailing argument are attributes
             attributes.addAll(filterAndAttributeStrings);
         }
 
-        if (filename.isPresent()) {
-            // Read the filter strings.
-            try (BufferedReader in = new BufferedReader(new FileReader(filename.getValue()))) {
-                String line = null;
-                while ((line = in.readLine()) != null) {
-                    if ("".equals(line.trim())) {
-                        // ignore empty lines.
-                        continue;
-                    }
-                    filters.add(Filter.valueOf(line));
-                }
-            } catch (final LocalizedIllegalArgumentException e) {
-                errPrintln(e.getMessageObject());
-                return ResultCode.CLIENT_SIDE_FILTER_ERROR.intValue();
-            } catch (final IOException e) {
-                errPrintln(LocalizableMessage.raw(e.toString()));
-                return ResultCode.CLIENT_SIDE_FILTER_ERROR.intValue();
-            }
+        if (filterFile.isPresent()) {
+            filters.addAll(readFiltersFromFile(filterFile.getValue()));
         }
 
         if (filters.isEmpty()) {
@@ -215,55 +181,13 @@
                             typesOnly.isPresent()).setTimeLimit(timeLimit.getIntValue())
                             .setSizeLimit(sizeLimit.getIntValue());
         } catch (final ArgumentException | LocalizedIllegalArgumentException e) {
-            errPrintln(e.getMessageObject());
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+            throw newToolParamException(e, e.getMessageObject());
         }
 
-        InputStream sourceInputStream = null;
-        OutputStream outputStream = null;
-
-        try {
-            // First source file.
-            if (!"-".equals(trailingArguments.get(0))) {
-                try {
-                    sourceInputStream = new FileInputStream(trailingArguments.get(0));
-                } catch (final FileNotFoundException e) {
-                    final LocalizableMessage message =
-                            ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ.get(trailingArguments.get(0), e
-                                    .getLocalizedMessage());
-                    errPrintln(message);
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            }
-
-            // Output file.
-            if (outputFilename.isPresent() && !"-".equals(outputFilename.getValue())) {
-                try {
-                    outputStream = new FileOutputStream(outputFilename.getValue());
-                } catch (final FileNotFoundException e) {
-                    final LocalizableMessage message =
-                            ERR_LDIF_FILE_CANNOT_OPEN_FOR_WRITE.get(outputFilename.getValue(), e
-                                    .getLocalizedMessage());
-                    errPrintln(message);
-                    return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
-                }
-            }
-
-            // Default to stdin/stdout for all streams if not specified.
-            if (sourceInputStream == null) {
-                // Command line parameter was "-".
-                sourceInputStream = System.in;
-            }
-
-            if (outputStream == null) {
-                outputStream = System.out;
-            }
-
-            // Perform the search.
-            try (LDIFEntryReader sourceReader = new LDIFEntryReader(sourceInputStream);
-                LDIFEntryWriter outputWriter = new LDIFEntryWriter(outputStream)) {
-                LDIF.copyTo(LDIF.search(sourceReader, search), outputWriter);
-            }
+        try (final LDIFEntryReader sourceReader =
+                     new LDIFEntryReader(getLDIFToolInputStream(this, trailingArguments.get(0)));
+             final LDIFEntryWriter outputWriter = new LDIFEntryWriter(getLDIFToolOutputStream(this, outputFilename))) {
+            LDIF.copyTo(LDIF.search(sourceReader, search), outputWriter);
         } catch (final IOException e) {
             if (e instanceof LocalizableException) {
                 errPrintln(ERR_LDIFSEARCH_FAILED.get(((LocalizableException) e).getMessageObject()));
@@ -271,8 +195,6 @@
                 errPrintln(ERR_LDIFSEARCH_FAILED.get(e.getLocalizedMessage()));
             }
             return ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
-        } finally {
-            closeSilently(sourceInputStream, outputStream);
         }
 
         return ResultCode.SUCCESS.intValue();
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java
index a150056..1094b5e 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java
@@ -17,6 +17,7 @@
 package com.forgerock.opendj.ldap.tools;
 
 import static com.forgerock.opendj.cli.ArgumentConstants.*;
+import static com.forgerock.opendj.cli.CommonArguments.showUsageArgument;
 import static com.forgerock.opendj.cli.ToolVersionHandler.newSdkVersionHandler;
 import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
 import static com.forgerock.opendj.cli.Utils.filterExitCode;
@@ -70,8 +71,10 @@
     /** Run Make LDIF with provided command-line arguments. */
     int run(final String[] args) {
         final LocalizableMessage toolDescription = INFO_MAKELDIF_TOOL_DESCRIPTION.get();
-        final ArgumentParser argParser = new ArgumentParser(MakeLDIF.class.getName(), toolDescription,
-                false, true, 1, 1, "template-file-path");
+        final ArgumentParser argParser = LDAPToolArgumentParser.builder(MakeLDIF.class.getName())
+                .toolDescription(toolDescription)
+                .trailingArguments(1, "template-file-path")
+                .build();
         argParser.setVersionHandler(newSdkVersionHandler());
         argParser.setShortToolDescription(REF_SHORT_DESC_MAKELDIF.get());
         argParser.setDocToolDescriptionSupplement(SUPPLEMENT_DESCRIPTION_MAKELDIF.get());
@@ -110,11 +113,9 @@
                             .multiValued()
                             .valuePlaceholder(INFO_CONSTANT_PLACEHOLDER.get())
                             .buildAndAddToParser(argParser);
-            showUsage =
-                    BooleanArgument.builder(OPTION_LONG_HELP)
-                            .shortIdentifier(OPTION_SHORT_HELP)
-                            .description(INFO_MAKELDIF_DESCRIPTION_HELP.get())
-                            .buildAndAddToParser(argParser);
+
+            showUsage = showUsageArgument();
+            argParser.addArgument(showUsage);
 
             wrapColumn =
                     IntegerArgument.builder("wrapColumn")
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java
index 102ea9f..d464da9 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java
@@ -150,9 +150,10 @@
     private int run(final String[] args) {
         // Creates the command-line argument parser for use with this program
         final LocalizableMessage toolDescription = INFO_MODRATE_TOOL_DESCRIPTION.get();
-        final ArgumentParser argParser =
-                new ArgumentParser(ModRate.class.getName(), toolDescription, false, true, 1, 0,
-                        "[(attribute:value format string) ...]");
+        final ArgumentParser argParser = LDAPToolArgumentParser.builder(ModRate.class.getName())
+                .toolDescription(toolDescription)
+                .trailingArgumentsUnbounded(1, "[(attribute:value format string) ...]")
+                .build();
         argParser.setVersionHandler(newSdkVersionHandler());
         argParser.setShortToolDescription(REF_SHORT_DESC_MODRATE.get());
         argParser.setDocToolDescriptionSupplement(SUPPLEMENT_DESCRIPTION_RATE_TOOLS.get());
@@ -194,11 +195,8 @@
             argParser.addArgument(showUsage);
             argParser.setUsageArgument(showUsage, getOutputStream());
 
-            scriptFriendly =
-                    BooleanArgument.builder("scriptFriendly")
-                            .shortIdentifier('S')
-                            .description(INFO_DESCRIPTION_SCRIPT_FRIENDLY.get())
-                            .buildAndAddToParser(argParser);
+            scriptFriendly = scriptFriendlySdkArgument();
+            argParser.addArgument(scriptFriendly);
         } catch (final ArgumentException ae) {
             final LocalizableMessage message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
             errPrintln(message);
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java
index b0f839b..3643917 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java
@@ -196,9 +196,10 @@
     private int run(final String[] args) {
         // Create the command-line argument parser for use with this program.
         final LocalizableMessage toolDescription = INFO_SEARCHRATE_TOOL_DESCRIPTION.get();
-        final ArgumentParser argParser =
-                new ArgumentParser(SearchRate.class.getName(), toolDescription, false, true, 1, 0,
-                        "[filter format string] [attributes ...]");
+        final ArgumentParser argParser = LDAPToolArgumentParser.builder(SearchRate.class.getName())
+                .toolDescription(toolDescription)
+                .trailingArgumentsUnbounded(1, "[filter format string] [attributes ...]")
+                .build();
         argParser.setVersionHandler(newSdkVersionHandler());
         argParser.setShortToolDescription(REF_SHORT_DESC_SEARCHRATE.get());
         argParser.setDocToolDescriptionSupplement(SUPPLEMENT_DESCRIPTION_RATE_TOOLS.get());
@@ -254,11 +255,8 @@
             verbose = verboseArgument();
             argParser.addArgument(verbose);
 
-            scriptFriendly =
-                    BooleanArgument.builder("scriptFriendly")
-                            .shortIdentifier('S')
-                            .description(INFO_DESCRIPTION_SCRIPT_FRIENDLY.get())
-                            .buildAndAddToParser(argParser);
+            scriptFriendly = scriptFriendlySdkArgument();
+            argParser.addArgument(scriptFriendly);
         } catch (final ArgumentException ae) {
             final LocalizableMessage message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
             errPrintln(message);
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/Utils.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/Utils.java
index 8b0a853..a0cdc1d 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/Utils.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/Utils.java
@@ -16,17 +16,47 @@
  */
 package com.forgerock.opendj.ldap.tools;
 
+import static com.forgerock.opendj.cli.ArgumentConstants.USE_SYSTEM_STREAM_TOKEN;
 import static com.forgerock.opendj.cli.Utils.readBytesFromFile;
 import static com.forgerock.opendj.cli.Utils.secondsToTimeString;
+import static com.forgerock.opendj.ldap.tools.LDAPToolException.newToolException;
+import static com.forgerock.opendj.ldap.tools.LDAPToolException.newToolExceptionAlreadyPrinted;
+import static com.forgerock.opendj.ldap.tools.LDAPToolException.newToolParamException;
 import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
 
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.forgerock.opendj.cli.ArgumentException;
+import com.forgerock.opendj.cli.ArgumentParser;
+import com.forgerock.opendj.cli.BooleanArgument;
+import com.forgerock.opendj.cli.IntegerArgument;
+import com.forgerock.opendj.cli.StringArgument;
 import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.LocalizableMessageDescriptor;
+import org.forgerock.i18n.LocalizedIllegalArgumentException;
 import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.Connection;
+import org.forgerock.opendj.ldap.ConnectionFactory;
 import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.DecodeOptions;
+import org.forgerock.opendj.ldap.Filter;
 import org.forgerock.opendj.ldap.LdapException;
+import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.controls.AssertionRequestControl;
 import org.forgerock.opendj.ldap.controls.AuthorizationIdentityRequestControl;
 import org.forgerock.opendj.ldap.controls.AuthorizationIdentityResponseControl;
+import org.forgerock.opendj.ldap.controls.Control;
 import org.forgerock.opendj.ldap.controls.GenericControl;
 import org.forgerock.opendj.ldap.controls.GetEffectiveRightsRequestControl;
 import org.forgerock.opendj.ldap.controls.PasswordExpiredResponseControl;
@@ -36,116 +66,26 @@
 import org.forgerock.opendj.ldap.controls.PasswordPolicyResponseControl;
 import org.forgerock.opendj.ldap.controls.PasswordPolicyWarningType;
 import org.forgerock.opendj.ldap.controls.SubtreeDeleteRequestControl;
+import org.forgerock.opendj.ldap.requests.BindRequest;
+import org.forgerock.opendj.ldap.requests.Request;
 import org.forgerock.opendj.ldap.responses.BindResult;
 
 import com.forgerock.opendj.cli.ConsoleApplication;
 import com.forgerock.opendj.ldap.controls.AccountUsabilityRequestControl;
-import com.forgerock.opendj.util.StaticUtils;
+import org.forgerock.opendj.ldap.responses.Result;
 
 /**
  * This class provides utility functions for all the client side tools.
  */
 final class Utils {
 
-    /**
-     * Parse the specified command line argument to create the appropriate
-     * LDAPControl. The argument string should be in the format
-     * controloid[:criticality[:value|::b64value|:&lt;fileurl]]
-     *
-     * @param argString
-     *            The argument string containing the encoded control
-     *            information.
-     * @return The control decoded from the provided string, or
-     *         <CODE>null</CODE> if an error occurs while parsing the argument
-     *         value.
-     * @throws org.forgerock.opendj.ldap.DecodeException
-     *             If an error occurs.
-     */
-    static GenericControl getControl(final String argString) throws DecodeException {
-        String controlOID = null;
-        boolean controlCriticality = false;
-        ByteString controlValue = null;
+    static int printErrorMessage(final ConsoleApplication app, final LdapException ldapException) {
+        return printErrorMessage(app, ldapException, null);
+    }
 
-        int idx = argString.indexOf(":");
-
-        if (idx < 0) {
-            controlOID = argString;
-        } else {
-            controlOID = argString.substring(0, idx);
-        }
-
-        final String lowerOID = StaticUtils.toLowerCase(controlOID);
-        if ("accountusable".equals(lowerOID) || "accountusability".equals(lowerOID)) {
-            controlOID = AccountUsabilityRequestControl.OID;
-        } else if ("authzid".equals(lowerOID) || "authorizationidentity".equals(lowerOID)) {
-            controlOID = AuthorizationIdentityRequestControl.OID;
-        } else if ("noop".equals(lowerOID) || "no-op".equals(lowerOID)) {
-            // controlOID = OID_LDAP_NOOP_OPENLDAP_ASSIGNED;
-        } else if ("subentries".equals(lowerOID)) {
-            // controlOID = OID_LDAP_SUBENTRIES;
-        } else if ("managedsait".equals(lowerOID)) {
-            // controlOID = OID_MANAGE_DSAIT_CONTROL;
-        } else if ("pwpolicy".equals(lowerOID) || "passwordpolicy".equals(lowerOID)) {
-            controlOID = PasswordPolicyRequestControl.OID;
-        } else if ("subtreedelete".equals(lowerOID) || "treedelete".equals(lowerOID)) {
-            controlOID = SubtreeDeleteRequestControl.OID;
-        } else if ("realattrsonly".equals(lowerOID) || "realattributesonly".equals(lowerOID)) {
-            // controlOID = OID_REAL_ATTRS_ONLY;
-        } else if ("virtualattrsonly".equals(lowerOID) || "virtualattributesonly".equals(lowerOID)) {
-            // controlOID = OID_VIRTUAL_ATTRS_ONLY;
-        } else if ("effectiverights".equals(lowerOID) || "geteffectiverights".equals(lowerOID)) {
-            controlOID = GetEffectiveRightsRequestControl.OID;
-        }
-
-        if (idx < 0) {
-            return GenericControl.newControl(controlOID);
-        }
-
-        final String remainder = argString.substring(idx + 1, argString.length());
-
-        idx = remainder.indexOf(":");
-        if (idx == -1) {
-            if ("true".equalsIgnoreCase(remainder)) {
-                controlCriticality = true;
-            } else if ("false".equalsIgnoreCase(remainder)) {
-                controlCriticality = false;
-            } else {
-                // TODO: I18N
-                throw DecodeException.error(LocalizableMessage
-                        .raw("Invalid format for criticality value:" + remainder));
-            }
-            return GenericControl.newControl(controlOID, controlCriticality);
-
-        }
-
-        final String critical = remainder.substring(0, idx);
-        if ("true".equalsIgnoreCase(critical)) {
-            controlCriticality = true;
-        } else if ("false".equalsIgnoreCase(critical)) {
-            controlCriticality = false;
-        } else {
-            // TODO: I18N
-            throw DecodeException.error(LocalizableMessage
-                    .raw("Invalid format for criticality value:" + critical));
-        }
-
-        final String valString = remainder.substring(idx + 1, remainder.length());
-        if (valString.charAt(0) == ':') {
-            controlValue = ByteString.valueOfBase64(valString.substring(1, valString.length()));
-        } else if (valString.charAt(0) == '<') {
-            // Read data from the file.
-            final String filePath = valString.substring(1, valString.length());
-            try {
-                final byte[] val = readBytesFromFile(filePath);
-                controlValue = ByteString.wrap(val);
-            } catch (final Exception e) {
-                return null;
-            }
-        } else {
-            controlValue = ByteString.valueOfUtf8(valString);
-        }
-
-        return GenericControl.newControl(controlOID, controlCriticality, controlValue);
+    static int printErrorMessage(final ConsoleApplication app,
+            final LdapException ldapException, final LocalizableMessageDescriptor.Arg2<Number, Object> errorMsg) {
+        return printErrorMessage(app, ldapException.getResult(), errorMsg);
     }
 
     /**
@@ -154,34 +94,41 @@
      *
      * @param app
      *            The console app to use to write the error message.
-     * @param ere
+     * @param result
      *            The error result.
+     * @param errorMsg
+     *            The error message associated to the application to use to display error result code and label.
      * @return The error code.
      */
-    static int printErrorMessage(final ConsoleApplication app, final LdapException ere) {
-         /* if ((ere.getMessage() != null) && (ere.getMessage().length() > 0)) {
-             app.println(LocalizableMessage.raw(ere.getMessage()));
-         }*/
+    static int printErrorMessage(final ConsoleApplication app,
+            final Result result, final LocalizableMessageDescriptor.Arg2<Number, Object> errorMsg) {
+        final ResultCode resultCode = result.getResultCode();
+        final int rc = resultCode.intValue();
 
-        if (ere.getResult().getResultCode().intValue() >= 0) {
-            app.errPrintln(ERR_TOOL_RESULT_CODE.get(ere.getResult().getResultCode().intValue(), ere
-                    .getResult().getResultCode().toString()));
+        if (rc != ResultCode.UNDEFINED.intValue() && errorMsg != null) {
+            app.errPrintln(errorMsg.get(rc, resultCode.toString()));
+        }
+        printlnTextMsg(app, ERR_TOOL_ERROR_MESSAGE, result.getDiagnosticMessage());
+        printlnTextMsg(app, ERR_TOOL_MATCHED_DN, result.getMatchedDN());
+
+        final Throwable cause = result.getCause();
+        if (app.isVerbose() && cause != null) {
+            cause.printStackTrace(app.getErrorStream());
         }
 
-        if (ere.getResult().getDiagnosticMessage() != null
-                && ere.getResult().getDiagnosticMessage().length() > 0) {
-            app.errPrintln(ERR_TOOL_ERROR_MESSAGE.get(ere.getResult().getDiagnosticMessage()));
-        }
+        return rc;
+    }
 
-        if (ere.getResult().getMatchedDN() != null && ere.getResult().getMatchedDN().length() > 0) {
-            app.errPrintln(ERR_TOOL_MATCHED_DN.get(ere.getResult().getMatchedDN()));
+    static void printSuccessMessage(
+            final ConsoleApplication app, final Result r, final String operationType, final String dn) {
+        app.println(INFO_OPERATION_SUCCESSFUL.get(operationType, dn));
+        printlnTextMsg(app, r.getDiagnosticMessage());
+        final List<String> referralURIs = r.getReferralURIs();
+        if (referralURIs != null) {
+            for (final String uri : referralURIs) {
+                app.println(LocalizableMessage.raw(uri));
+            }
         }
-
-        if (app.isVerbose() && ere.getResult().getCause() != null) {
-            ere.getResult().getCause().printStackTrace(app.getErrorStream());
-        }
-
-        return ere.getResult().getResultCode().intValue();
     }
 
     static void printPasswordPolicyResults(final ConsoleApplication app, final BindResult result) {
@@ -196,8 +143,8 @@
         }
 
         try {
-            final PasswordExpiredResponseControl control = result.getControl(PasswordExpiredResponseControl.DECODER,
-                                                                             new DecodeOptions());
+            final PasswordExpiredResponseControl control = result.getControl(
+                    PasswordExpiredResponseControl.DECODER, new DecodeOptions());
             if (control != null) {
                 app.println(INFO_BIND_PASSWORD_EXPIRED.get());
             }
@@ -206,43 +153,63 @@
         }
 
         try {
-            final PasswordExpiringResponseControl control = result.getControl(PasswordExpiringResponseControl.DECODER,
-                                                                              new DecodeOptions());
+            final PasswordExpiringResponseControl control = result.getControl(
+                    PasswordExpiringResponseControl.DECODER, new DecodeOptions());
             if (control != null) {
-                final LocalizableMessage timeString = secondsToTimeString(control.getSecondsUntilExpiration());
-                app.println(INFO_BIND_PASSWORD_EXPIRING.get(timeString));
+                app.println(INFO_BIND_PASSWORD_EXPIRING.get(secondsToTimeString(control.getSecondsUntilExpiration())));
             }
         } catch (final DecodeException e) {
             app.errPrintln(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage()));
         }
 
         try {
-            final PasswordPolicyResponseControl control = result.getControl(PasswordPolicyResponseControl.DECODER,
-                                                                            new DecodeOptions());
+            final PasswordPolicyResponseControl control = result.getControl(
+                    PasswordPolicyResponseControl.DECODER, new DecodeOptions());
             if (control != null) {
-                final PasswordPolicyErrorType errorType = control.getErrorType();
-                if (errorType == PasswordPolicyErrorType.PASSWORD_EXPIRED) {
-                    app.println(INFO_BIND_PASSWORD_EXPIRED.get());
-                } else if (errorType == PasswordPolicyErrorType.ACCOUNT_LOCKED) {
-                    app.println(INFO_BIND_ACCOUNT_LOCKED.get());
-                } else if (errorType == PasswordPolicyErrorType.CHANGE_AFTER_RESET) {
-
-                    app.println(INFO_BIND_MUST_CHANGE_PASSWORD.get());
-                }
-
-                final PasswordPolicyWarningType warningType = control.getWarningType();
-                if (warningType == PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION) {
-                    final LocalizableMessage timeString = secondsToTimeString(control.getWarningValue());
-                    app.println(INFO_BIND_PASSWORD_EXPIRING.get(timeString));
-                } else if (warningType == PasswordPolicyWarningType.GRACE_LOGINS_REMAINING) {
-                    app.println(INFO_BIND_GRACE_LOGINS_REMAINING.get(control.getWarningValue()));
-                }
+                printPasswordPolicyError(control.getErrorType(), app);
+                printPasswordPolicyWarning(control.getWarningType(), control.getWarningValue(), app);
             }
         } catch (final DecodeException e) {
             app.errPrintln(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage()));
         }
     }
 
+    private static void printPasswordPolicyError(final PasswordPolicyErrorType errorType,
+                                                 final ConsoleApplication app) {
+        if (errorType == null) {
+            return;
+        }
+
+        switch (errorType) {
+        case PASSWORD_EXPIRED:
+            app.println(INFO_BIND_PASSWORD_EXPIRED.get());
+            break;
+        case ACCOUNT_LOCKED:
+            app.println(INFO_BIND_ACCOUNT_LOCKED.get());
+            break;
+        case CHANGE_AFTER_RESET:
+            app.println(INFO_BIND_MUST_CHANGE_PASSWORD.get());
+            break;
+        }
+    }
+
+    private static void printPasswordPolicyWarning(final PasswordPolicyWarningType warningType,
+                                                   final int warningValue,
+                                                   final ConsoleApplication app) {
+        if (warningType == null) {
+            return;
+        }
+
+        switch (warningType) {
+        case TIME_BEFORE_EXPIRATION:
+            app.println(INFO_BIND_PASSWORD_EXPIRING.get(secondsToTimeString(warningValue)));
+            break;
+        case GRACE_LOGINS_REMAINING:
+            app.println(INFO_BIND_GRACE_LOGINS_REMAINING.get(warningValue));
+            break;
+        }
+    }
+
     /**
      * Sets default system property settings for the xxxrate performance tools.
      */
@@ -260,6 +227,230 @@
         }
     }
 
+    static List<Filter> readFiltersFromFile(final String fileName) throws LDAPToolException {
+        final List<Filter> filters = new ArrayList<>();
+        try (final BufferedReader in = new BufferedReader(new FileReader(fileName))) {
+            String line;
+            while ((line = in.readLine()) != null) {
+                if ("".equals(line.trim())) {
+                    // ignore empty lines.
+                    continue;
+                }
+                filters.add(Filter.valueOf(line));
+            }
+            return filters;
+        } catch (final IOException e) {
+            throw newToolException(e, ResultCode.CLIENT_SIDE_FILTER_ERROR, LocalizableMessage.raw(e.toString()));
+        } catch (final LocalizedIllegalArgumentException e) {
+            throw newToolException(e, ResultCode.CLIENT_SIDE_FILTER_ERROR, e.getMessageObject());
+        }
+    }
+
+    static Filter readFilterFromString(final String filterStr) throws LDAPToolException {
+        try {
+            return Filter.valueOf(filterStr);
+        } catch (final LocalizedIllegalArgumentException e) {
+            throw newToolException(e, ResultCode.CLIENT_SIDE_FILTER_ERROR, e.getMessageObject());
+        }
+    }
+
+    static void parseArguments(final ArgumentParser argParser, final PrintStream stream, final String[] args)
+            throws LDAPToolException {
+        try {
+            argParser.parseArguments(args);
+        } catch (final ArgumentException e) {
+            argParser.displayMessageAndUsageReference(stream, ERR_ERROR_PARSING_ARGS.get(e.getMessage()));
+            throw newToolExceptionAlreadyPrinted(e, ResultCode.CLIENT_SIDE_PARAM_ERROR);
+        }
+    }
+
+    static InputStream getLDIFToolInputStream(final ConsoleApplication app, final String filePath)
+            throws LDAPToolException {
+        if (!USE_SYSTEM_STREAM_TOKEN.equals(filePath)) {
+            try {
+                return new FileInputStream(filePath);
+            } catch (final FileNotFoundException e) {
+                throw newToolParamException(
+                        e, ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ.get(filePath, e.getLocalizedMessage()));
+            }
+        } else {
+            return app.getInputStream();
+        }
+    }
+
+    static OutputStream getLDIFToolOutputStream(final ConsoleApplication app, final StringArgument outputFileArg)
+            throws LDAPToolException {
+        final String filePath = outputFileArg.getValue();
+        if (outputFileArg.isPresent() && !USE_SYSTEM_STREAM_TOKEN.equals(filePath)) {
+            try {
+                return new FileOutputStream(filePath);
+            } catch (final FileNotFoundException e) {
+                throw newToolParamException(
+                        e, ERR_LDIF_FILE_CANNOT_OPEN_FOR_WRITE.get(filePath, e.getLocalizedMessage()));
+            }
+        } else {
+            return app.getOutputStream();
+        }
+    }
+
+    static void ensureLdapProtocolVersionIsSupported(final IntegerArgument version) throws LDAPToolException {
+        try {
+            final int versionNumber = version.getIntValue();
+            if (versionNumber != 2 && versionNumber != 3) {
+                throw newToolParamException(ERR_DESCRIPTION_INVALID_VERSION.get(String.valueOf(versionNumber)));
+            }
+        } catch (final ArgumentException e) {
+            throw newToolParamException(e, ERR_DESCRIPTION_INVALID_VERSION.get(String.valueOf(version.getValue())));
+        }
+    }
+
+    static void addControlsToRequest(final Request request, final List<Control> controls) throws LDAPToolException {
+        for (final Control control : controls) {
+            request.addControl(control);
+        }
+    }
+
+    static List<Control> readControls(final StringArgument controlArg) throws LDAPToolException {
+        final List<Control> controls = new LinkedList<>();
+        if (controlArg.isPresent()) {
+            for (final String ctrlString : controlArg.getValues()) {
+                try {
+                    controls.add(getControl(ctrlString));
+                } catch (final DecodeException e) {
+                    throw newToolParamException(e, ERR_TOOL_INVALID_CONTROL_STRING.get(ctrlString));
+                }
+            }
+        }
+        return controls;
+    }
+
+    /**
+     * Parse the specified command line argument to create the appropriate
+     * LDAPControl. The argument string should be in the format
+     * controloid[:criticality[:value|::b64value|:&lt;fileurl]]
+     *
+     * @param argString
+     *            The argument string containing the encoded control
+     *            information.
+     * @return The control decoded from the provided string, or
+     *         <CODE>null</CODE> if an error occurs while parsing the argument
+     *         value.
+     * @throws org.forgerock.opendj.ldap.DecodeException
+     *             If an error occurs.
+     */
+    private static GenericControl getControl(final String argString) throws DecodeException {
+        final String[] control = argString.split(":");
+        final int nbControlElements = control.length;
+
+        final String controlOID = readControlID(control[0]);
+        if (nbControlElements == 1) {
+            return GenericControl.newControl(controlOID);
+        }
+
+        final boolean critic = readControlCriticality(control[1], argString);
+        if (nbControlElements == 2) {
+            return GenericControl.newControl(controlOID, critic);
+        }
+
+        final ByteString controlValue;
+        if (control[2].isEmpty()) {
+            controlValue = ByteString.valueOfBase64(control[3]);
+        } else if (control[2].startsWith("<")) {
+            // Read data from the file.
+            try {
+                controlValue = ByteString.wrap(readBytesFromFile(control[2].substring(1)));
+            } catch (final Exception e) {
+                return null;
+            }
+        } else {
+            controlValue = ByteString.valueOfUtf8(control[2]);
+        }
+
+        return GenericControl.newControl(controlOID, critic, controlValue);
+    }
+
+    private static String readControlID(final String controlOidStr) {
+        switch (controlOidStr.toLowerCase()) {
+        case "accountusable":
+        case "accountusability":
+            return AccountUsabilityRequestControl.OID;
+        case "authzid":
+        case "authorizationidentity":
+            return AuthorizationIdentityRequestControl.OID;
+        case "pwpolicy":
+        case "passwordpolicy":
+            return PasswordPolicyRequestControl.OID;
+        case "treedelete":
+        case "subtreedelete":
+            return SubtreeDeleteRequestControl.OID;
+        case "effectiverights":
+        case "geteffectiverights":
+            return GetEffectiveRightsRequestControl.OID;
+        case "noop":
+        case "no-op":
+        case "subentries":
+        case "managedsait":
+        case "realattributesonly":
+        case "realattrsonly":
+        case "virtualattributesonly":
+        case "virtualattrsonly":
+        default:
+            // TODO‌ Support these request controls once migrated in the sdk
+            return controlOidStr;
+        }
+    }
+
+    private static boolean readControlCriticality(final String criticalityStr, final String controlStr)
+            throws DecodeException {
+        if ("true".equalsIgnoreCase(criticalityStr)) {
+            return true;
+        } else if ("false".equalsIgnoreCase(criticalityStr)) {
+            return false;
+        } else {
+            throw DecodeException.error(ERR_DECODE_CONTROL_CRITICALITY.get(criticalityStr, controlStr));
+        }
+    }
+
+    static Control readAssertionControl(final String assertionFilter)
+            throws LDAPToolException {
+        try {
+            // FIXME -- Change this to the correct OID when the official one is assigned.
+            return AssertionRequestControl.newControl(true, Filter.valueOf(assertionFilter));
+        } catch (final LocalizedIllegalArgumentException e) {
+            throw newToolParamException(e, ERR_LDAP_ASSERTION_INVALID_FILTER.get(e.getMessage()));
+        }
+    }
+
+    static Connection getConnection(final ConnectionFactory connectionFactory, final BindRequest bindRequest,
+            final BooleanArgument dryRunArg, final ConsoleApplication app) throws LDAPToolException {
+        if (!dryRunArg.isPresent()) {
+            try {
+                final Connection connection = connectionFactory.getConnection();
+                if (bindRequest != null) {
+                    printPasswordPolicyResults(app, connection.bind(bindRequest));
+                }
+                return connection;
+            } catch (final LdapException e) {
+                printErrorMessage(app, e, ERR_LDAPP_BIND_FAILED);
+                throw newToolExceptionAlreadyPrinted(e, e.getResult().getResultCode());
+            }
+        }
+        return null;
+    }
+
+    static void printlnTextMsg(final ConsoleApplication app, final String msg) {
+        printlnTextMsg(app, null, msg);
+    }
+
+    static void printlnTextMsg(final ConsoleApplication app,
+                               final LocalizableMessageDescriptor.Arg1<Object> localizableMsg,
+                               final String msg) {
+        if (msg != null && !msg.isEmpty()) {
+            app.errPrintln(localizableMsg == null ? LocalizableMessage.raw(msg)
+                                                  : localizableMsg.get(msg));
+        }
+    }
+
     /** Prevent instantiation. */
     private Utils() {
         // Do nothing.
diff --git a/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties b/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties
index eb1b3ca..e296870 100644
--- a/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties
+++ b/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties
@@ -106,8 +106,6 @@
  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
@@ -296,6 +294,7 @@
 INFO_TOOL_WARMING_UP=Warming up for %d seconds...
 ERR_AUTHRATE_NO_BIND_DN_PROVIDED=Authentication information must be provided \
  to use this tool
+ERR_DECODE_CONTROL_CRITICALITY=Invalid format for criticality value '%s' in control '%s'
  #
  # MakeLDIF tool
  #
@@ -309,7 +308,6 @@
 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)
 INFO_MAKELDIF_PROCESSED_N_ENTRIES=Processed %d entries
@@ -326,6 +324,10 @@
 INFO_MAKELDIF_DESCRIPTION_WRAP_COLUMN=Maximum length of an output line \
  (0 for no wrapping)
 INFO_MAKELDIF_WRAP_COLUMN_PLACEHOLDER={wrapColumn}
+ERR_LDAPP_BIND_FAILED=The LDAP bind request failed: %d (%s)
+ERR_LDAP_SEARCH_FAILED=The LDAP search request failed: %d (%s)
+ERR_LDAP_MODIFY_FAILED=The LDAP modify request failed: %d (%s)
+ERR_LDAP_COMPARE_FAILED=The LDAP compare request failed: %d (%s)
 #
 # AddRate Tool
 #
diff --git a/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/LDAPCompareITCase.java b/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/LDAPCompareITCase.java
index 0efba74..95b2f26 100644
--- a/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/LDAPCompareITCase.java
+++ b/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/LDAPCompareITCase.java
@@ -84,11 +84,13 @@
         ByteStringBuilder err = new ByteStringBuilder();
 
         try (PrintStream outStream = new PrintStream(out.asOutputStream());
-            PrintStream errStream = new PrintStream(err.asOutputStream())) {
+             PrintStream errStream = new PrintStream(err.asOutputStream())) {
             LDAPCompare ldapCompare = new LDAPCompare(outStream, errStream);
 
             ldapCompare.run(arguments);
             checkOuputStreams(out, err, expectedOut, expectedErr);
+        } catch (final LDAPToolException ae) {
+            checkOuputStreams(out, err, expectedOut, expectedErr);
         }
     }
 }
diff --git a/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/LDAPSearchITCase.java b/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/LDAPSearchITCase.java
index 545c6f0..bb25ca3 100644
--- a/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/LDAPSearchITCase.java
+++ b/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/LDAPSearchITCase.java
@@ -78,10 +78,12 @@
         ByteStringBuilder err = new ByteStringBuilder();
 
         try (PrintStream outStream = new PrintStream(out.asOutputStream());
-            PrintStream errStream = new PrintStream(err.asOutputStream())) {
+             PrintStream errStream = new PrintStream(err.asOutputStream())) {
             LDAPSearch ldapSearch = new LDAPSearch(outStream, errStream);
             ldapSearch.run(arguments);
             checkOuputStreams(out, err, expectedOut, expectedErr);
+        } catch (final LDAPToolException e) {
+            checkOuputStreams(out, err, expectedOut, expectedErr);
         }
     }
 }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/tools/makeldif/MakeLDIFInputStream.java b/opendj-server-legacy/src/main/java/org/opends/server/tools/makeldif/MakeLDIFInputStream.java
index ddcb82d..3df8112 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/tools/makeldif/MakeLDIFInputStream.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/tools/makeldif/MakeLDIFInputStream.java
@@ -65,14 +65,6 @@
   /** The queue used to hold generated entries until they can be read. */
   private LinkedBlockingQueue<TemplateEntry> entryQueue;
 
-  /** The background thread being used to actually generate the entries. */
-  private MakeLDIFInputStreamThread generatorThread;
-
-  /** The template file to use to generate the entries. */
-  private TemplateFile templateFile;
-
-
-
   /**
    * Creates a new MakeLDIF input stream that will generate entries based on the
    * provided template file.
@@ -81,8 +73,6 @@
    */
   public MakeLDIFInputStream(TemplateFile templateFile)
   {
-    this.templateFile = templateFile;
-
     allGenerated = false;
     closed       = false;
     entryQueue   = new LinkedBlockingQueue<>(10);
@@ -102,8 +92,8 @@
       ioException = ioe;
     }
 
-    generatorThread = new MakeLDIFInputStreamThread(this, templateFile);
-    generatorThread.start();
+    /* The background thread being used to actually generate the entries. */
+    new MakeLDIFInputStreamThread(this, templateFile).start();
   }
 
 

--
Gitblit v1.10.0