Wednesday, October 27, 2010

AD/LDAP reconciliation using paging

Depending on the settings on your target AD/LDAP server you may need to use pageing when doing larger searches. AD2008 per default only gives you a 1000 objects in any one LDAP search which means that most recons requires either paging or many very well defined LDAP filters that gives you a very small return sets.

Paging is supported in JNDI by activating a specific RequestControl. The LDAP server will then serve you pageSize number of records together with a cookie that you send back to get another batch. Rinse and repeat until you have all the result records.

The example code was written for TDS but can be used with any LDAP V3 compliant LDAP server.

private HashMap performTimSearch(String ouPath){
  NamingEnumeration results=null;
  //keyed on the String name, contains SearchResult objects
  HashMap totalResults=new HashMap(30000);  
   logger.debug("\nNo TIM admin password. No TIM search will be performed.");
   return totalResults;
  try {
      // Create initial context
      LdapContext ctx = new InitialLdapContext(timEnv,null);
      // Activate paged results
      int pageSize = 400;
      byte[] cookie = null;
      PagedResultsControl pageControl = new PagedResultsControl(pageSize, Control.NONCRITICAL);

      //activate search controls
      SearchControls searchCtrl = new SearchControls();
      ctx.setRequestControls(new Control[] { new PagedResultsControl(pageSize, Control.NONCRITICAL) });
      int total=0;
      // Specify the attributes to match     
      String matchAttrs = "(objectclass=user)";

      do {
          /* perform the search */
       results =, matchAttrs, searchCtrl);

       while (results != null && results.hasMore()) {
        SearchResult entry = (SearchResult);
        totalResults.put(entry.getName(), entry);
          // Examine the paged results control response
          Control[] controls = ctx.getResponseControls();
          if (controls != null) {
            for (int i = 0; i < controls.length; i++) {
              if (controls[i] instanceof PagedResultsResponseControl) {
                PagedResultsResponseControl prrc = (PagedResultsResponseControl) controls[i];    
                cookie = prrc.getCookie();
          } else {
            logger.debug("No controls were sent from the server");
          // Re-activate paged results
          ctx.setRequestControls(new Control[] { new PagedResultsControl(
              pageSize, cookie, Control.CRITICAL) });

        } while (cookie != null);

      logger.debug("Finished TIM search. Found " + total + " users.");
      // Close the context when we're done
  } catch (Exception e) {
   logger.error("TIM search failed");
     return totalResults;


  1. Hi Martin,

    Another great post.
    Thanks for the code.

    Vijay Chinnasamy

  2. Martin,

    That eased my work.