From dff791d6bdd93ee5127094248f5e3cbc0d529653 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Thu, 16 Nov 2006 01:32:41 +0000
Subject: [PATCH] Update the ldapsearch tool to provide a --countEntries option that can be used to count the number of matching entries.  The count will be displayed as a comment at the end of the results, and will also be used as the exit code for the tool.  This addresses issue #1013.

---
 opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java                                 |   46 +++++-
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPDeleteTestCase.java |   75 ++++++++++
 opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearchOptions.java                          |   26 +++
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java |  138 +++++++++++++++++++
 opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java                            |   23 +++
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java |   91 +++++++++++++
 6 files changed, 390 insertions(+), 9 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java
index 49f1f79..e7b3fec 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java
@@ -7323,6 +7323,25 @@
 
 
   /**
+   * The message ID for the message that will be used as the description of the
+   * countEntries property.  This does not take any arguments.
+   */
+  public static final int MSGID_DESCRIPTION_COUNT_ENTRIES =
+       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 750;
+
+
+
+  /**
+   * The message ID for the message that will be used to provide the number of
+   * matching entries for a search request.  This takes a single argument, which
+   * is the number of matching entries.
+   */
+  public static final int MSGID_LDAPSEARCH_MATCHING_ENTRY_COUNT =
+       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 751;
+
+
+
+  /**
    * Associates a set of generic messages with the message IDs defined in this
    * class.
    */
@@ -7999,6 +8018,8 @@
                     "SASL bind options");
     registerMessage(MSGID_DESCRIPTION_DONT_WRAP,
                     "Do not wrap long lines");
+    registerMessage(MSGID_DESCRIPTION_COUNT_ENTRIES,
+                    "Count the number of entries returned by the server");
     registerMessage(MSGID_LDAPAUTH_PROPERTY_DESCRIPTION_KDC,
                     "Specifies the KDC to use for the Kerberos " +
                     "authentication.");
@@ -9428,6 +9449,8 @@
                     "#   The account is locked.");
     registerMessage(MSGID_LDAPSEARCH_ACCTUSABLE_TIME_UNTIL_UNLOCK,
                     "#   Time until the account is unlocked:  %s.");
+    registerMessage(MSGID_LDAPSEARCH_MATCHING_ENTRY_COUNT,
+                    "# Total number of matching entries:  %d.");
 
 
     registerMessage(MSGID_TOOL_CONFLICTING_ARGS,
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java
index 538b086..525fbe9 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java
@@ -130,18 +130,24 @@
    * @param searchOptions  The constraints for the search.
    * @param wrapColumn     The column at which to wrap long lines.
    *
+   * @return  The number of matching entries returned by the server.  If there
+   *          were multiple search filters provided, then this will be the
+   *          total number of matching entries for all searches.
+   *
    * @throws  IOException  If a problem occurs while attempting to communicate
    *                       with the Directory Server.
    *
    * @throws  LDAPException  If the Directory Server returns an error response.
    */
-  public void executeSearch(LDAPConnection connection, String baseDN,
-                            ArrayList<LDAPFilter> filters,
-                            LinkedHashSet<String> attributes,
-                            LDAPSearchOptions searchOptions,
-                            int wrapColumn )
+  public int executeSearch(LDAPConnection connection, String baseDN,
+                           ArrayList<LDAPFilter> filters,
+                           LinkedHashSet<String> attributes,
+                           LDAPSearchOptions searchOptions,
+                           int wrapColumn )
          throws IOException, LDAPException
   {
+    int matchingEntries = 0;
+
     for (LDAPFilter filter: filters)
     {
       ASN1OctetString asn1OctetStr = new ASN1OctetString(baseDN);
@@ -267,6 +273,7 @@
               StringBuilder sb = new StringBuilder();
               toLDIF(searchEntryOp, sb, wrapColumn, typesOnly);
               out.println(sb.toString());
+              matchingEntries++;
               break;
 
             case OP_TYPE_SEARCH_RESULT_REFERENCE:
@@ -312,6 +319,15 @@
         throw new IOException(ae.getMessage());
       }
     }
+
+    if (searchOptions.countMatchingEntries())
+    {
+      int    msgID   = MSGID_LDAPSEARCH_MATCHING_ENTRY_COUNT;
+      String message = getMessage(msgID, matchingEntries);
+      out.println(message);
+      out.println();
+    }
+    return matchingEntries;
   }
 
   /**
@@ -522,6 +538,7 @@
     LinkedHashSet<String> attributes = new LinkedHashSet<String>();
 
     BooleanArgument   continueOnError          = null;
+    BooleanArgument   countEntries             = null;
     BooleanArgument   dontWrap                 = null;
     BooleanArgument   noop                     = null;
     BooleanArgument   reportAuthzID            = null;
@@ -741,6 +758,10 @@
                                      MSGID_DESCRIPTION_DONT_WRAP);
       argParser.addArgument(dontWrap);
 
+      countEntries = new BooleanArgument("countentries", null, "countEntries",
+                                         MSGID_DESCRIPTION_COUNT_ENTRIES);
+      argParser.addArgument(countEntries);
+
       continueOnError =
            new BooleanArgument("continueOnError", 'c', "continueOnError",
                                MSGID_DESCRIPTION_CONTINUE_ON_ERROR);
@@ -935,6 +956,7 @@
     searchOptions.setVerbose(verbose.isPresent());
     searchOptions.setContinueOnError(continueOnError.isPresent());
     searchOptions.setEncoding(encodingStr.getValue());
+    searchOptions.setCountMatchingEntries(countEntries.isPresent());
     try
     {
       searchOptions.setTimeLimit(timeLimit.getIntValue());
@@ -1276,8 +1298,17 @@
       connection.connectToHost(bindDNValue, bindPasswordValue, nextMessageID);
 
       LDAPSearch ldapSearch = new LDAPSearch(nextMessageID, out, err);
-      ldapSearch.executeSearch(connection, baseDNValue, filters, attributes,
-                               searchOptions, wrapColumn);
+      int matchingEntries = ldapSearch.executeSearch(connection, baseDNValue,
+                                                     filters, attributes,
+                                                     searchOptions, wrapColumn);
+      if (countEntries.isPresent())
+      {
+        return matchingEntries;
+      }
+      else
+      {
+        return 0;
+      }
 
     } catch(LDAPException le)
     {
@@ -1303,7 +1334,6 @@
         connection.close();
       }
     }
-    return 0;
   }
 
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearchOptions.java b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearchOptions.java
index 0d84db0..8913370 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearchOptions.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearchOptions.java
@@ -56,6 +56,7 @@
   private int sizeLimit = 0;
   private int timeLimit = 0;
   private boolean typesOnly = false;
+  private boolean countMatchingEntries = false;
 
   /**
    * Creates the options instance.
@@ -228,5 +229,30 @@
     this.typesOnly = typesOnly;
   }
 
+
+  /**
+   * Indicates whether to report the number of matching entries returned by the
+   * server.
+   *
+   * @return  {@code true} if the number of matching entries should be reported,
+   *          or {@code false} if not.
+   */
+  public boolean countMatchingEntries()
+  {
+    return countMatchingEntries;
+  }
+
+
+  /**
+   * Specifies whether to report the number of matching entries returned by the
+   * server.
+   *
+   * @param  countMatchingEntries  Specifies whether to report the number of
+   *                               matching entries returned by the server.
+   */
+  public void setCountMatchingEntries(boolean countMatchingEntries)
+  {
+    this.countMatchingEntries = countMatchingEntries;
+  }
 }
 
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPDeleteTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPDeleteTestCase.java
index 844c7a4..23eb329 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPDeleteTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPDeleteTestCase.java
@@ -798,6 +798,52 @@
 
 
   /**
+   * Tests a subtree delete operation using an alternate name for the control.
+   *
+   * @throws  Exception  If an unexpectd problem occurs.
+   */
+  @Test()
+  public void testSubtreeDeleteAltName()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Entry e = TestCaseUtils.makeEntry(
+         "dn: uid=test.user,o=test",
+         "objectClass: top",
+         "objectClass: person",
+         "objectClass: organizationalPerson",
+         "objectClass: inetOrgPerson",
+         "uid: test.user",
+         "givenName: Test",
+         "sn: User",
+         "cn: Test User",
+         "userPassword: password");
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(e.getDN(), e.getObjectClasses(), e.getUserAttributes(),
+                         e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "-J", "subtreedelete:true",
+      "o=test"
+    };
+
+    assertEquals(LDAPDelete.mainDelete(args, false, null, System.err), 0);
+  }
+
+
+
+  /**
    * Tests a simple delete using the client-side no-op option.
    *
    * @throws  Exception  If an unexpectd problem occurs.
@@ -844,7 +890,34 @@
       "o=test"
     };
 
-    LDAPDelete.mainDelete(args, false, null, null);
+    assertEquals(LDAPDelete.mainDelete(args, false, null, System.err), 0);
+  }
+
+
+
+  /**
+   * Tests a simple delete using the server-side no-op control with an alternate
+   * name for the no-op control.
+   *
+   * @throws  Exception  If an unexpectd problem occurs.
+   */
+  @Test()
+  public void testDeleteServerSideNoOpAltName()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "-J", "no-op:true",
+      "o=test"
+    };
+
+    assertEquals(LDAPDelete.mainDelete(args, false, null, System.err), 0);
   }
 
 
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java
index 5419bed..8145442 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java
@@ -882,6 +882,33 @@
 
 
   /**
+   * Tests a simple modify operation using LDAP No-Op control with an alternate
+   * name.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testModifyLDAPNoOpAltName()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "-J", "no-op:true",
+      "-f", modifyFilePath
+    };
+
+    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
+  }
+
+
+
+  /**
    * Tests a simple add operation using LDAP No-Op control.
    *
    * @throws  Exception  If an unexpected problem occurs.
@@ -915,6 +942,40 @@
 
 
   /**
+   * Tests a simple add operation using LDAP No-Op control with an alternate
+   * name.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAddLDAPNoOpAltName()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    String path = TestCaseUtils.createTempFile(
+         "dn: ou=People,o=test",
+         "changetype: add",
+         "objectClass: top",
+         "objectClass: organizationalUnit",
+         "ou: People");
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "-J", "no-op:true",
+      "-f", path
+    };
+
+    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
+  }
+
+
+
+  /**
    * Tests a simple delete operation using LDAP No-Op control.
    *
    * @throws  Exception  If an unexpected problem occurs.
@@ -945,6 +1006,37 @@
 
 
   /**
+   * Tests a simple delete operation using LDAP No-Op control with an alternate
+   * name.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testDeleteLDAPNoOpAltName()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    String path = TestCaseUtils.createTempFile(
+         "dn: o=test",
+         "changetype: delete");
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "-J", "no-op:true",
+      "-f", path
+    };
+
+    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
+  }
+
+
+
+  /**
    * Tests a simple modify DN operation using LDAP No-Op control.
    *
    * @throws  Exception  If an unexpected problem occurs.
@@ -990,6 +1082,52 @@
 
 
   /**
+   * Tests a simple modify DN operation using LDAP No-Op control with an
+   * alternate name.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testModifyDNLDAPNoOpAltName()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Entry e = TestCaseUtils.makeEntry(
+         "dn: ou=People,o=test",
+         "objectClass: top",
+         "objectClass: organizationalUnit",
+         "ou: People");
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(e.getDN(), e.getObjectClasses(),
+                         e.getUserAttributes(), e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+    String path = TestCaseUtils.createTempFile(
+         "dn: ou=People,o=test",
+         "changetype: moddn",
+         "newRDN: ou=Users",
+         "deleteOldRDN: 1");
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "-J", "no-op:true",
+      "-f", path
+    };
+
+    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
+  }
+
+
+
+  /**
    * Tests a simple modify operation using the LDAP assertion control in which
    * the assertion is true.
    *
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
index e587352..46f47aa 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
@@ -1271,6 +1271,33 @@
 
 
   /**
+   * Tests with the account usability control with an alternate name for an
+   * authenticated search.
+   */
+  @Test()
+  public void testAccountUsabilityControlAltName()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "-b", "o=test",
+      "-s", "base",
+      "-J", "accountusable:true",
+      "(objectClass=*)"
+    };
+
+    assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 0);
+  }
+
+
+
+  /**
    * Tests with the LDAP assertion control in which the assertion is true.
    */
   @Test()
@@ -1350,6 +1377,70 @@
 
 
   /**
+   * Tests the use of the LDAP subentries control.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testSubentriesControl()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Entry e = TestCaseUtils.makeEntry("dn: cn=test,o=test",
+                                      "objectClass: top",
+                                      "objectClass: ldapSubEntry",
+                                      "cn: test");
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(e.getDN(), e.getObjectClasses(), e.getUserAttributes(),
+                         e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-b", "o=test",
+      "-s", "sub",
+      "--countEntries",
+      "(objectClass=*)"
+    };
+
+    assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 1);
+
+    args = new String[]
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-b", "o=test",
+      "-s", "sub",
+      "--countEntries",
+      "-J", OID_LDAP_SUBENTRIES + ":true",
+      "(objectClass=*)"
+    };
+
+    assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 2);
+
+    args = new String[]
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-b", "o=test",
+      "-s", "sub",
+      "--countEntries",
+      "-J", "subentries:true",
+      "(objectClass=*)"
+    };
+
+    assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 2);
+  }
+
+
+
+  /**
    * Tests the inclusion of multiple arbitrary controls in the request to the
    * server.
    *

--
Gitblit v1.10.0