From 2095a28e3b117ddc7b565bc7fbe410af70cb48f4 Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Mon, 31 Oct 2016 14:00:38 +0000
Subject: [PATCH] OPENDJ-2772 Align SDK client tool with server's

---
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java |  202 +++++++++++++++++++++++--------------------------
 1 files changed, 95 insertions(+), 107 deletions(-)

diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java
index 3e8d9ab..9404f88 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java
@@ -48,6 +48,7 @@
 import org.forgerock.opendj.ldap.controls.ServerSideSortRequestControl;
 import org.forgerock.opendj.ldap.controls.ServerSideSortResponseControl;
 import org.forgerock.opendj.ldap.controls.SimplePagedResultsControl;
+import org.forgerock.opendj.ldap.controls.SubentriesRequestControl;
 import org.forgerock.opendj.ldap.controls.VirtualListViewRequestControl;
 import org.forgerock.opendj.ldap.controls.VirtualListViewResponseControl;
 import org.forgerock.opendj.ldap.requests.BindRequest;
@@ -74,12 +75,12 @@
 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.computeWrapColumn;
 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.*;
 
@@ -90,6 +91,44 @@
 
 /** A tool that can be used to issue Search requests to the Directory Server. */
 public final class LDAPSearch extends ConsoleApplication {
+
+    /**
+     * The main method for ldapsearch tool.
+     *
+     * @param args
+     *            The command-line arguments provided to this program.
+     */
+    public static void main(final String[] args) {
+        System.exit(filterExitCode(run(System.out, System.err, args)));
+    }
+
+    /**
+     * Run {@link LDAPSearch} tool with the provided arguments.
+     * Output and errors will be written on the provided streams.
+     * This method can be used to run the tool programmatically.
+     *
+     * @param out
+     *      {@link PrintStream} which will be used by the tool to write results and information messages.
+     * @param err
+     *      {@link PrintStream} which will be used by the tool to write errors.
+     * @param args
+     *      Arguments set to pass to the tool.
+     * @return
+     *      An integer which represents the result code of the tool.
+     */
+    public static int run(final PrintStream out, final PrintStream err, final String... args) {
+        final LDAPSearch ldapSearch = new LDAPSearch(out, err);
+        try {
+            return ldapSearch.run(args);
+        } catch (final LDAPToolException e) {
+            e.printErrorMessage(ldapSearch);
+            return e.getResultCode();
+        } catch (final ArgumentException e) {
+            ldapSearch.errPrintln(ERR_ERROR_PARSING_ARGS.get(e.getMessageObject()));
+            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+        }
+    }
+
     private class LDAPSearchResultHandler implements SearchResultHandler {
         private int entryCount;
 
@@ -167,43 +206,11 @@
         }
     }
 
-    /**
-     * The main method for LDAPSearch tool.
-     *
-     * @param args
-     *            The command-line arguments provided to this program.
-     */
-
-    public static void main(final String[] 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));
-    }
-
     private BooleanArgument verbose;
 
     private EntryWriter ldifWriter;
 
-    private LDAPSearch() {
-        // Nothing to do.
-    }
-
-    /**
-     * Constructor to allow tests.
-     *
-     * @param out output stream of console application
-     * @param err error stream of console application
-     */
-    LDAPSearch(PrintStream out, PrintStream err) {
+    private LDAPSearch(final PrintStream out, final PrintStream err) {
         super(out, err);
     }
 
@@ -217,13 +224,12 @@
         return verbose.isPresent();
     }
 
-    /** Run ldapsearch with provided command-line arguments. */
-    int run(final String[] args) throws LDAPToolException, ArgumentException {
+    private 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 LDAPToolArgumentParser argParser = LDAPToolArgumentParser.builder(LDAPSearch.class.getName())
                 .toolDescription(toolDescription)
-                .trailingArguments("[filter] [attributes ...]")
+                .trailingArgumentsUnbounded(1, "filter [attributes ...]")
                 .build();
         argParser.setVersionHandler(newSdkVersionHandler());
         argParser.setShortToolDescription(REF_SHORT_DESC_LDAPSEARCH.get());
@@ -231,7 +237,7 @@
         ConnectionFactoryProvider connectionFactoryProvider;
 
         BooleanArgument countEntries;
-        BooleanArgument dontWrap;
+        IntegerArgument wrapColumn;
         BooleanArgument noop;
         BooleanArgument typesOnly;
         IntegerArgument simplePageSize;
@@ -240,7 +246,6 @@
         StringArgument baseDN;
         StringArgument controlStr;
         MultiChoiceArgument<DereferenceAliasesPolicy> dereferencePolicy;
-        StringArgument filterFile;
         StringArgument matchedValuesFilter;
         StringArgument pSearchInfo;
         MultiChoiceArgument<SearchScope> searchScope;
@@ -251,8 +256,11 @@
         StringArgument proxyAuthzID;
         StringArgument assertionFilter;
         IntegerArgument sizeLimit;
+        BooleanArgument subEntriesArgument;
+
         try {
             connectionFactoryProvider = new ConnectionFactoryProvider(argParser, this);
+
             final StringArgument propertiesFileArgument = propertiesFileArgument();
             argParser.addArgument(propertiesFileArgument);
             argParser.setFilePropertiesArgument(propertiesFileArgument);
@@ -272,9 +280,6 @@
             searchScope = searchScopeArgument();
             argParser.addArgument(searchScope);
 
-            filterFile = filenameArgument(INFO_SEARCH_DESCRIPTION_FILENAME.get());
-            argParser.addArgument(filterFile);
-
             proxyAuthzID =
                     StringArgument.builder(OPTION_LONG_PROXYAUTHID)
                             .shortIdentifier(OPTION_SHORT_PROXYAUTHID)
@@ -366,15 +371,17 @@
                             .defaultValue(0)
                             .valuePlaceholder(INFO_TIME_LIMIT_PLACEHOLDER.get())
                             .buildAndAddToParser(argParser);
-            dontWrap =
-                    BooleanArgument.builder("dontWrap")
-                            .shortIdentifier('t')
-                            .description(INFO_DESCRIPTION_DONT_WRAP.get())
-                            .buildAndAddToParser(argParser);
+            wrapColumn = wrapColumnArgument();
+            argParser.addArgument(wrapColumn);
             countEntries =
                     BooleanArgument.builder("countEntries")
                             .description(INFO_DESCRIPTION_COUNT_ENTRIES.get())
                             .buildAndAddToParser(argParser);
+            subEntriesArgument =
+                    BooleanArgument.builder(OPTION_LONG_SUBENTRIES)
+                            .shortIdentifier(OPTION_SHORT_SUBENTRIES)
+                            .description(INFO_DESCRIPTION_SUBENTRIES.get())
+                            .buildAndAddToParser(argParser);
 
             final BooleanArgument continueOnError = continueOnErrorArgument();
             argParser.addArgument(continueOnError);
@@ -397,43 +404,32 @@
             return ResultCode.SUCCESS.intValue();
         }
 
-        final List<Filter> filters = new LinkedList<>();
+        final List<String> trailingArgs = argParser.getTrailingArguments();
+        final Filter filter = readFilterFromString(trailingArgs.get(0));
         final List<String> attributes = new LinkedList<>();
-        final List<String> filterAndAttributeStrings = argParser.getTrailingArguments();
-        if (!filterAndAttributeStrings.isEmpty()) {
-            // If filter file is not present, the first trailing argument is considered the filter
-            if (!filterFile.isPresent()) {
-                filters.add(readFilterFromString(filterAndAttributeStrings.remove(0)));
-            }
+        if (trailingArgs.size() > 1) {
             // The rest of trailing argument are attributes
-            attributes.addAll(filterAndAttributeStrings);
-        }
-
-        if (filterFile.isPresent()) {
-            filters.addAll(readFiltersFromFile(filterFile.getValue()));
-        }
-
-        if (filters.isEmpty()) {
-            argParser.displayMessageAndUsageReference(getErrStream(), ERR_SEARCH_NO_FILTERS.get());
-            return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+            attributes.addAll(trailingArgs.subList(1, trailingArgs.size()));
         }
 
         final SearchScope scope = searchScope.getTypedValue();
-        final SearchRequest search;
+        final SearchRequest searchRequest;
         try {
-            search = Requests.newSearchRequest(DN.valueOf(baseDN.getValue()), scope, filters.get(0),
-                                               attributes.toArray(new String[attributes.size()]));
+            searchRequest = Requests.newSearchRequest(DN.valueOf(baseDN.getValue()),
+                                                      scope,
+                                                      filter,
+                                                      attributes.toArray(new String[attributes.size()]));
             ensureLdapProtocolVersionIsSupported(ldapProtocolVersion);
         } catch (final LocalizedIllegalArgumentException e) {
             errPrintln(e.getMessageObject());
             return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
         }
 
-        search.setTypesOnly(typesOnly.isPresent());
-        search.setTimeLimit(timeLimit.getIntValue());
-        search.setSizeLimit(sizeLimit.getIntValue());
-        search.setDereferenceAliasesPolicy(dereferencePolicy.getTypedValue());
-        addControlsToRequest(search, readControls(controlStr));
+        searchRequest.setTypesOnly(typesOnly.isPresent())
+                     .setTimeLimit(timeLimit.getIntValue())
+                     .setSizeLimit(sizeLimit.getIntValue())
+                     .setDereferenceAliasesPolicy(dereferencePolicy.getTypedValue());
+        addControlsToRequest(searchRequest, readControls(controlStr));
 
         if (effectiveRightsUser.isPresent()) {
             final String authzID = effectiveRightsUser.getValue();
@@ -441,20 +437,20 @@
                 throw newToolParamException(ERR_EFFECTIVERIGHTS_INVALID_AUTHZID.get(authzID));
             }
             final List<String> attrValues = effectiveRightsAttrs.getValues();
-            search.addControl(GetEffectiveRightsRequestControl.newControl(
+            searchRequest.addControl(GetEffectiveRightsRequestControl.newControl(
                             false, authzID.substring(3), attrValues.toArray(new String[attrValues.size()])));
         }
 
         if (proxyAuthzID.isPresent()) {
-            search.addControl(ProxiedAuthV2RequestControl.newControl(proxyAuthzID.getValue()));
+            searchRequest.addControl(ProxiedAuthV2RequestControl.newControl(proxyAuthzID.getValue()));
         }
 
         if (pSearchInfo.isPresent()) {
-            search.addControl(computePSearchControl(pSearchInfo));
+            searchRequest.addControl(computePSearchControl(pSearchInfo));
         }
 
         if (assertionFilter.isPresent()) {
-            search.addControl(readAssertionControl(assertionFilter.getValue()));
+            searchRequest.addControl(readAssertionControl(assertionFilter.getValue()));
         }
 
         if (matchedValuesFilter.isPresent()) {
@@ -467,33 +463,29 @@
                     throw newToolParamException(le, ERR_LDAP_MATCHEDVALUES_INVALID_FILTER.get(le.getMessage()));
                 }
             }
-            search.addControl(MatchedValuesRequestControl.newControl(true, mvFilters));
+            searchRequest.addControl(MatchedValuesRequestControl.newControl(true, mvFilters));
         }
 
         if (sortOrder.isPresent()) {
             try {
-                search.addControl(ServerSideSortRequestControl.newControl(false, sortOrder.getValue()));
+                searchRequest.addControl(ServerSideSortRequestControl.newControl(false, sortOrder.getValue()));
             } catch (final LocalizedIllegalArgumentException le) {
                 throw newToolParamException(le, ERR_LDAP_SORTCONTROL_INVALID_ORDER.get(le.getMessageObject()));
             }
         }
 
         if (vlvDescriptor.isPresent()) {
-            search.addControl(readVLVControl(vlvDescriptor, sortOrder));
+            searchRequest.addControl(readVLVControl(vlvDescriptor, sortOrder));
+        }
+
+        if (subEntriesArgument.isPresent()) {
+            searchRequest.addControl(SubentriesRequestControl.newControl(true, true));
         }
 
         int pageSize = 0;
         if (simplePageSize.isPresent()) {
-            if (filters.size() > 1) {
-                throw newToolParamException(ERR_PAGED_RESULTS_REQUIRES_SINGLE_FILTER.get());
-            }
             pageSize = simplePageSize.getIntValue();
-            search.addControl(SimplePagedResultsControl.newControl(true, pageSize, ByteString.empty()));
-        }
-
-        int wrapColumn = 80;
-        if (dontWrap.isPresent()) {
-            wrapColumn = 0;
+            searchRequest.addControl(SimplePagedResultsControl.newControl(true, pageSize, ByteString.empty()));
         }
 
         if (noop.isPresent()) {
@@ -511,11 +503,13 @@
                 printPasswordPolicyResults(this, connection.bind(bindRequest));
             }
 
-            int filterIndex = 0;
-            ldifWriter = new LDIFEntryWriter(getOutputStream()).setWrapColumn(wrapColumn);
+            ldifWriter = new LDIFEntryWriter(getOutputStream()).setWrapColumn(computeWrapColumn(wrapColumn));
             final LDAPSearchResultHandler resultHandler = new LDAPSearchResultHandler();
-            while (true) {
-                Result result = connection.search(search, resultHandler);
+            boolean hasRemainingPages;
+            Result result;
+            do {
+                hasRemainingPages = false;
+                result = connection.search(searchRequest, resultHandler);
                 try {
                     final ServerSideSortResponseControl control =
                             result.getControl(ServerSideSortResponseControl.DECODER, new DecodeOptions());
@@ -548,33 +542,27 @@
                         if (!isQuiet()) {
                             pressReturnToContinue();
                         }
-                        final Iterator<Control> iterator = search.getControls().iterator();
+                        final Iterator<Control> iterator = searchRequest.getControls().iterator();
                         while (iterator.hasNext()) {
                             if (SimplePagedResultsControl.OID.equals(iterator.next().getOID())) {
                                 iterator.remove();
                             }
                         }
                         control = SimplePagedResultsControl.newControl(true, pageSize, control.getCookie());
-                        search.addControl(control);
-                        continue;
+                        searchRequest.addControl(control);
+                        hasRemainingPages = true;
                     }
                 } catch (final DecodeException e) {
                     errPrintln(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage()));
                 }
+            } while (hasRemainingPages);
 
-                errPrintln();
-                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());
+            errPrintln();
+            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()) {
-                    search.setFilter(filters.get(filterIndex));
-                } else {
-                    break;
-                }
-            }
             if (countEntries.isPresent() && !isQuiet()) {
                 println(INFO_LDAPSEARCH_MATCHING_ENTRY_COUNT.get(resultHandler.entryCount));
                 println();

--
Gitblit v1.10.0