/**
   * Get the existing RequestControls from the LdapContext, call {@link #createRequestControl()} to
   * get a new instance, build a new array of Controls and set it on the LdapContext.
   *
   * <p>The {@link Control} feature is specific for LDAP v3 and thus applies only to {@link
   * LdapContext}. However, the generic DirContextProcessor mechanism used for calling <code>
   * preProcess</code> and <code>postProcess</code> uses DirContext, since it also works for LDAP
   * v2. This is the reason that DirContext has to be cast to a LdapContext.
   *
   * @param ctx an LdapContext instance.
   * @throws NamingException
   * @throws IllegalArgumentException if the supplied DirContext is not an LdapContext.
   */
  public void preProcess(DirContext ctx) throws NamingException {
    LdapContext ldapContext;
    if (ctx instanceof LdapContext) {
      ldapContext = (LdapContext) ctx;
    } else {
      throw new IllegalArgumentException(
          "Request Control operations require LDAPv3 - " + "Context must be of type LdapContext");
    }

    Control[] requestControls = ldapContext.getRequestControls();
    if (requestControls == null) {
      requestControls = new Control[0];
    }
    Control newControl = createRequestControl();

    Control[] newControls = new Control[requestControls.length + 1];
    for (int i = 0; i < requestControls.length; i++) {
      if (replaceSameControlEnabled && requestControls[i].getClass() == newControl.getClass()) {
        log.debug("Replacing already existing control in context: " + newControl);
        requestControls[i] = newControl;
        ldapContext.setRequestControls(requestControls);
        return;
      }
      newControls[i] = requestControls[i];
    }

    // Add the new Control at the end of the array.
    newControls[newControls.length - 1] = newControl;

    ldapContext.setRequestControls(newControls);
  }
  @SuppressWarnings("rawtypes")
  protected void applyRequestControls(AbstractQuery query) {

    try {
      List<Control> controls = new ArrayList<Control>();

      String orderBy = query.getOrderBy();
      if (orderBy != null) {
        orderBy = orderBy.substring(0, orderBy.length() - 4);
        if (UserQueryProperty.USER_ID.getName().equals(orderBy)) {
          controls.add(new SortControl(ldapConfiguration.getUserIdAttribute(), Control.CRITICAL));

        } else if (UserQueryProperty.EMAIL.getName().equals(orderBy)) {
          controls.add(
              new SortControl(ldapConfiguration.getUserEmailAttribute(), Control.CRITICAL));

        } else if (UserQueryProperty.FIRST_NAME.getName().equals(orderBy)) {
          controls.add(
              new SortControl(ldapConfiguration.getUserFirstnameAttribute(), Control.CRITICAL));

        } else if (UserQueryProperty.LAST_NAME.getName().equals(orderBy)) {
          controls.add(
              new SortControl(ldapConfiguration.getUserLastnameAttribute(), Control.CRITICAL));
        }
      }

      initialContext.setRequestControls(controls.toArray(new Control[0]));

    } catch (Exception e) {
      throw new IdentityProviderException("Exception while setting paging settings", e);
    }
  }
  protected void applyRequestControls(AbstractQuery<?, ?> query) {

    try {
      List<Control> controls = new ArrayList<Control>();

      List<QueryOrderingProperty> orderBy = query.getOrderingProperties();
      if (orderBy != null) {
        for (QueryOrderingProperty orderingProperty : orderBy) {
          String propertyName = orderingProperty.getQueryProperty().getName();
          if (UserQueryProperty.USER_ID.getName().equals(propertyName)) {
            controls.add(new SortControl(ldapConfiguration.getUserIdAttribute(), Control.CRITICAL));

          } else if (UserQueryProperty.EMAIL.getName().equals(propertyName)) {
            controls.add(
                new SortControl(ldapConfiguration.getUserEmailAttribute(), Control.CRITICAL));

          } else if (UserQueryProperty.FIRST_NAME.getName().equals(propertyName)) {
            controls.add(
                new SortControl(ldapConfiguration.getUserFirstnameAttribute(), Control.CRITICAL));

          } else if (UserQueryProperty.LAST_NAME.getName().equals(propertyName)) {
            controls.add(
                new SortControl(ldapConfiguration.getUserLastnameAttribute(), Control.CRITICAL));
          }
        }
      }

      initialContext.setRequestControls(controls.toArray(new Control[0]));

    } catch (Exception e) {
      throw new IdentityProviderException("Exception while setting paging settings", e);
    }
  }
  /**
   * Search for entry that matches the specific <code>DirectoryQuery</code> conditions. Results will
   * be order using the values of a specific attribute
   *
   * @param q DirectoryQuery
   * @param attribute Name of the attribute that determines the order
   * @return java.util.List<DirectoryEntry>
   * @exception LDAPException
   */
  public List<Identity> sortedSearch(final LDAPDirectoryQuery q, final String attribute)
      throws LDAPException {
    TreeMap<String, Identity> results =
        new TreeMap<String, Identity>(Collator.getInstance(new Locale("es")));
    try {
      LdapContext ctx = connection.connect();
      if (ctx == null) {
        throw new LDAPException("Directory service not available");
      }
      SearchControls ctls = new SearchControls();
      if (connection.hasCountLimit()) {
        ctls.setCountLimit(connection.getCountLimit());
      }
      ctls.setSearchScope(connection.getScope());
      ctx.setRequestControls(new Control[] {new SortControl(attribute, Control.NONCRITICAL)});

      String filter = getQueryString(ctx, q);
      NamingEnumeration<SearchResult> answer = ctx.search(baseDN, filter, ctls);
      while (answer.hasMoreElements()) {
        SearchResult sr = answer.nextElement();
        LDAPDirectoryEntry _e = new LDAPDirectoryEntry(sr.getNameInNamespace());
        @SuppressWarnings("unchecked")
        NamingEnumeration<Attribute> ne =
            (NamingEnumeration<Attribute>) sr.getAttributes().getAll();
        while (ne.hasMore()) {
          Attribute att = ne.next();
          Object[] attrs = new Object[att.size()];
          @SuppressWarnings("unchecked")
          NamingEnumeration<Object> nea = (NamingEnumeration<Object>) att.getAll();
          for (int i = 0; nea.hasMore(); i++) {
            attrs[i] = nea.next();
          }
          _e.setAttribute(att.getID(), attrs);
        }
        String _value = String.valueOf(_e.getAttribute(attribute)[0]);
        while (results.containsKey(_value)) {
          _value = _value.concat("0");
        }
        results.put(_value, _e);
      }
    } catch (NullPointerException e) {
      _log.log(java.util.logging.Level.ALL, "sortedSearch() null pointer");
      throw new LDAPException("sorted search null pointer");
    } catch (NamingException e) {
      _log.log(java.util.logging.Level.ALL, "sortedSearch() - " + e.getMessage());
      throw new LDAPException(e.getMessage());
    } catch (IOException e) {
      _log.log(java.util.logging.Level.ALL, "sortedSearch() - " + e.getMessage());
      throw new LDAPException(e.getMessage());
    } finally {
      connection.disconnect();
    }
    return new ArrayList<Identity>(results.values());
  }
  public static void main(String[] args) throws Exception {
    String server = "ldap://*****:*****@tql17ad.com";
    String password = "******";
    boolean ssl = false;

    Provider prov = null;

    if (ssl) {
      prov = Security.getProvider("com.sun.net.ssl.internal.ssl.Provider");

      if (prov == null) {
        Class moProviderClass = Class.forName("com.sun.net.ssl.internal.ssl.Provider");
        Provider moProvider = (Provider) moProviderClass.newInstance();
        Security.addProvider(moProvider);
      }
    }

    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, server + rootCtx);
    env.put(Context.AUTHORITATIVE, "true");
    env.put(Context.SECURITY_PRINCIPAL, admin);
    env.put(Context.SECURITY_CREDENTIALS, password);

    if (ssl) {
      env.put(Context.SECURITY_PROTOCOL, "ssl");
    }

    // DirContext ctx = new InitialDirContext(env);
    LdapContext ctx = new InitialLdapContext(env, null);
    SearchControls searchCtls = new SearchControls();

    // Specify the attributes to return
    // String returnedAtts[] = { "name", "cn" };
    String returnedAtts[] = {"objectGUID", "cn", "whenChanged"};
    searchCtls.setReturningAttributes(returnedAtts);

    // Specify the search scope
    searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
    int pageSize = 100;
    byte[] cookie = null;

    // ***************change************
    Control[] ctls = new Control[] {new PagedResultsControl(pageSize)};
    ctx.setRequestControls(ctls);
    // ***************change************

    int totalResults = 0;
    System.out.println("Initial context = " + ctx);

    do {
      NamingEnumeration nenum =
          ctx.search("", "(&(whenChanged>=19000101000000.0Z)(objectclass=group))", searchCtls);

      // loop through the results in each page
      while (nenum != null && nenum.hasMoreElements()) {
        SearchResult sr = (SearchResult) nenum.next();

        // print out the name
        System.out.println("name: " + sr.getName());
        // increment the counter
        totalResults++;
      }

      // examine the response controls
      cookie = parseControls(ctx.getResponseControls());

      System.out.println("cookie = " + cookie + " length = " + cookie.length);

      // pass the cookie back to the server for the next page
      ctx.setRequestControls(
          new Control[] {new PagedResultsControl(pageSize, cookie, Control.CRITICAL)});

    } while ((cookie != null) && (cookie.length != 0));

    ctx.close();
    System.out.println("Total entries: " + totalResults);

    // NamingEnumeration nenum = ctx.search("", "(objectclass=group)",
    // null);
    /*
     * NamingEnumeration nenum = ctx.search("", "(objectclass=group)",
     * searchCtls);
     *
     * int cnt = 0; for (NamingEnumeration ne = nenum; ne.hasMoreElements();
     * cnt++) { SearchResult sr = (SearchResult)ne.nextElement();
     * System.out.println(sr.getName()); }
     *
     * System.out.println(cnt);
     */
  }