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

neil_a_wilson
16.32.2006 dff791d6bdd93ee5127094248f5e3cbc0d529653
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.

This commit also includes test cases for this issue as well as the use of
alternate human-readable names for certain types of controls. These test cases
are more applicable to issue #989, but need the --countEntries option for
verifying that the LDAP subentries control is working as expected.
6 files modified
399 ■■■■■ changed files
opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java 23 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java 46 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearchOptions.java 26 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPDeleteTestCase.java 75 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java 138 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java 91 ●●●●● patch | view | raw | blame | history
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,
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;
  }
}
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;
  }
}
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);
  }
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.
   *
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.
   *