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

Gaetan Boismal
08.59.2016 88496c3a54b4c6e969cb0dce5cf67e5da6846740
OPENDJ-2772 Code cleanup

* Improve code readability
* Rename local variables (like parameters)
* Factorize duplicated pieces of code across tools in Utils
* arguments parsing
* reading filters from string or file
* reading controls
* print messages
* Create LDAPToolArgumentParser to have a fluent way to build
argument parser and to mutualize argument parsing code generation for
ldap* tools.
* Create LDAPToolException class to handle error which may occur
during tool exection. This class manages return code and localizable
error message and allow code factorization in Utils.

* Mutualize generation code of the following arguments:
* -f, --fileName
* -S, --scriptFriendly
* --showUsage argument generation in make-ldif
* -J, --control

* MakeLDIFInputStream.java
* Remove unused templateFile field
* Inline generatorThread field

* Other little cleanup actions
2 files added
19 files modified
2437 ■■■■ changed files
opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentConstants.java 6 ●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java 14 ●●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java 13 ●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java 14 ●●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPCompare.java 179 ●●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPModify.java 305 ●●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPPasswordModify.java 137 ●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java 575 ●●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPToolArgumentParser.java 161 ●●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPToolException.java 68 ●●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFDiff.java 107 ●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFModify.java 150 ●●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFSearch.java 156 ●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java 15 ●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java 14 ●●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java 14 ●●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/Utils.java 479 ●●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties 8 ●●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/LDAPCompareITCase.java 4 ●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/LDAPSearchITCase.java 4 ●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/tools/makeldif/MakeLDIFInputStream.java 14 ●●●● patch | view | raw | blame | history
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() {
    }
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
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);
    }
}
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);
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();
    }
}
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));
        }
    }
}
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();
    }
}
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);
    }
}
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPToolArgumentParser.java
New file
@@ -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);
        }
    }
}
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPToolException.java
New file
@@ -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);
        }
    }
}
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)) {
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();
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();
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")
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);
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);
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.
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
#
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);
        }
    }
}
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);
        }
    }
}
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();
  }