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

Matthew Swift
25.33.2012 263d085885df024dca9250cc03c807912b0a7662
opendj3/opendj-ldap-sdk-examples/pom.xml
@@ -7,17 +7,16 @@
  ! (the "License").  You may not use this file except in compliance
  ! with the License.
  !
  ! You can obtain a copy of the license at
  ! trunk/opendj3/legal-notices/CDDLv1_0.txt
  ! You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
  ! or http://forgerock.org/license/CDDLv1.0.html.
  ! See the License for the specific language governing permissions
  ! and limitations under the License.
  !
  ! When distributing Covered Code, include this CDDL HEADER in each
  ! file and include the License file at
  ! trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
  ! add the following below this CDDL HEADER, with the fields enclosed
  ! by brackets "[]" replaced with your own identifying information:
  ! file and include the License file at legal-notices/CDDLv1_0.txt.
  ! If applicable, add the following below this CDDL HEADER, with the
  ! fields enclosed by brackets "[]" replaced with your own identifying
  ! information:
  !      Portions Copyright [yyyy] [name of copyright owner]
  !
  ! CDDL HEADER END
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/GetInfo.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -26,160 +25,125 @@
 */
package org.forgerock.opendj.examples;
import java.io.IOException;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldif.LDIFEntryWriter;
/**
 * Demonstrates accessing server information about capabilities and schema.
 */
public final class GetInfo
{
  // Connection information
  private static String host;
  private static int port;
  // The kind of server information to request (all, controls, extops)
  private static String infoType;
public final class GetInfo {
    // Connection information
    private static String host;
    private static int port;
    // The kind of server information to request (all, controls, extops)
    private static String infoType;
  /**
   * Access the directory over LDAP to request information about capabilities
   * and schema.
   *
   * @param args
   *          The command line arguments
   */
  public static void main(final String[] args)
  {
    parseArgs(args);
    connect();
  }
  /**
   * Authenticate over LDAP.
   */
  private static void connect()
  {
    final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port);
    Connection connection = null;
    try
    {
      connection = factory.getConnection();
      connection.bind("", "".toCharArray()); // Anonymous bind
      final String attributeList;
      if (infoType.toLowerCase().equals("controls"))
      {
        attributeList = "supportedControl";
      }
      else if (infoType.toLowerCase().equals("extops"))
      {
        attributeList = "supportedExtension";
      }
      else
      {
        attributeList = "+"; // All operational attributes
      }
      final SearchResultEntry entry = connection.searchSingleEntry(
          "",                      // DN is "" for root DSE.
          SearchScope.BASE_OBJECT, // Read only the root DSE.
          "objectclass=*",         // Every object matches this filter.
          attributeList);          // Return these requested attributes.
      final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
      writer.writeComment("Root DSE for LDAP server at " + host + ":" + port);
      if (entry != null)
      {
        writer.writeEntry(entry);
      }
      writer.flush();
    }
    catch (final ErrorResultException e)
    {
      System.err.println(e.getMessage());
      System.exit(e.getResult().getResultCode().intValue());
      return;
    }
    catch (final InterruptedException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
      return;
    }
    catch (final IOException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
      return;
    }
    finally
    {
      if (connection != null)
      {
        connection.close();
      }
    }
  }
  private static void giveUp()
  {
    printUsage();
    System.exit(1);
  }
  /**
   * Parse command line arguments.
   *
   * @param args
   *          host port bind-dn bind-password info-type
   */
  private static void parseArgs(final String[] args)
  {
    if (args.length != 3)
    {
      giveUp();
    /**
     * Access the directory over LDAP to request information about capabilities
     * and schema.
     *
     * @param args
     *            The command line arguments
     */
    public static void main(final String[] args) {
        parseArgs(args);
        connect();
    }
    host = args[0];
    port = Integer.parseInt(args[1]);
    infoType = args[2]; // all, controls, or extops
    if (!(infoType.toLowerCase().equals("all")
        || infoType.toLowerCase().equals("controls")
        || infoType.toLowerCase().equals("extops")))
    {
      giveUp();
    /**
     * Authenticate over LDAP.
     */
    private static void connect() {
        final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port);
        Connection connection = null;
        try {
            connection = factory.getConnection();
            connection.bind("", "".toCharArray()); // Anonymous bind
            final String attributeList;
            if (infoType.toLowerCase().equals("controls")) {
                attributeList = "supportedControl";
            } else if (infoType.toLowerCase().equals("extops")) {
                attributeList = "supportedExtension";
            } else {
                attributeList = "+"; // All operational attributes
            }
            final SearchResultEntry entry = connection.searchSingleEntry("", // DN
                                                                             // is
                                                                             // ""
                                                                             // for
                                                                             // root
                                                                             // DSE.
                    SearchScope.BASE_OBJECT, // Read only the root DSE.
                    "objectclass=*", // Every object matches this filter.
                    attributeList); // Return these requested attributes.
            final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
            writer.writeComment("Root DSE for LDAP server at " + host + ":" + port);
            if (entry != null) {
                writer.writeEntry(entry);
            }
            writer.flush();
        } catch (final ErrorResultException e) {
            System.err.println(e.getMessage());
            System.exit(e.getResult().getResultCode().intValue());
            return;
        } catch (final InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
            return;
        } catch (final IOException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
            return;
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
    }
  }
    private static void giveUp() {
        printUsage();
        System.exit(1);
    }
    /**
     * Parse command line arguments.
     *
     * @param args
     *            host port bind-dn bind-password info-type
     */
    private static void parseArgs(final String[] args) {
        if (args.length != 3) {
            giveUp();
        }
  private static void printUsage()
  {
    System.err.println("Usage: host port info-type");
    System.err.println("\tAll arguments are required.");
    System.err
        .println("\tinfo-type to get can be either all, controls, or extops.");
  }
        host = args[0];
        port = Integer.parseInt(args[1]);
        infoType = args[2]; // all, controls, or extops
        if (!(infoType.toLowerCase().equals("all") || infoType.toLowerCase().equals("controls") || infoType
                .toLowerCase().equals("extops"))) {
            giveUp();
        }
    }
    private static void printUsage() {
        System.err.println("Usage: host port info-type");
        System.err.println("\tAll arguments are required.");
        System.err.println("\tinfo-type to get can be either all, controls, or extops.");
    }
  private GetInfo()
  {
    // Not used.
  }
    private GetInfo() {
        // Not used.
    }
}
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Modify.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,17 +27,18 @@
package org.forgerock.opendj.examples;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldif.*;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldif.ChangeRecord;
import org.forgerock.opendj.ldif.ConnectionChangeRecordWriter;
import org.forgerock.opendj.ldif.LDIFChangeRecordReader;
/**
 * An example client application which applies update operations to a Directory
@@ -50,115 +50,86 @@
 *  <host> <port> <username> <password> [<ldifFile>]
 * </pre>
 */
public final class Modify
{
  /**
   * Main method.
   *
   * @param args
   *          The command line arguments: host, port, username, password, LDIF
   *          file name containing the update operations (will use stdin if not
   *          provided).
   */
  public static void main(final String[] args)
  {
    if (args.length < 4 || args.length > 5)
    {
      System.err
          .println("Usage: host port username password [ldifFileName]");
      System.exit(1);
public final class Modify {
    /**
     * Main method.
     *
     * @param args
     *            The command line arguments: host, port, username, password,
     *            LDIF file name containing the update operations (will use
     *            stdin if not provided).
     */
    public static void main(final String[] args) {
        if (args.length < 4 || args.length > 5) {
            System.err.println("Usage: host port username password [ldifFileName]");
            System.exit(1);
        }
        // Parse command line arguments.
        final String hostName = args[0];
        final int port = Integer.parseInt(args[1]);
        final String userName = args[2];
        final String password = args[3];
        // Create the LDIF reader which will either used the named file, if
        // provided, or stdin.
        InputStream ldif;
        if (args.length > 4) {
            try {
                ldif = new FileInputStream(args[4]);
            } catch (final FileNotFoundException e) {
                System.err.println(e.getMessage());
                System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
                return;
            }
        } else {
            ldif = System.in;
        }
        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(ldif);
        // Connect and bind to the server.
        final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName, port);
        Connection connection = null;
        try {
            connection = factory.getConnection();
            connection.bind(userName, password.toCharArray());
            // Write the changes.
            final ConnectionChangeRecordWriter writer =
                    new ConnectionChangeRecordWriter(connection);
            while (reader.hasNext()) {
                ChangeRecord changeRecord = reader.readChangeRecord();
                writer.writeChangeRecord(changeRecord);
                System.err.println("Successfully modified entry "
                        + changeRecord.getName().toString());
            }
        } catch (final ErrorResultException e) {
            System.err.println(e.getMessage());
            System.exit(e.getResult().getResultCode().intValue());
            return;
        } catch (final InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
            return;
        } catch (final IOException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
            return;
        } finally {
            if (connection != null) {
                connection.close();
            }
            try {
                reader.close();
            } catch (final IOException ignored) {
                // Ignore.
            }
        }
    }
    // Parse command line arguments.
    final String hostName = args[0];
    final int port = Integer.parseInt(args[1]);
    final String userName = args[2];
    final String password = args[3];
    // Create the LDIF reader which will either used the named file, if
    // provided, or stdin.
    InputStream ldif;
    if (args.length > 4)
    {
      try
      {
        ldif = new FileInputStream(args[4]);
      }
      catch (final FileNotFoundException e)
      {
        System.err.println(e.getMessage());
        System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
        return;
      }
    private Modify() {
        // Not used.
    }
    else
    {
      ldif = System.in;
    }
    final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
        ldif);
    // Connect and bind to the server.
    final LDAPConnectionFactory factory = new LDAPConnectionFactory(
        hostName, port);
    Connection connection = null;
    try
    {
      connection = factory.getConnection();
      connection.bind(userName, password.toCharArray());
      // Write the changes.
      final ConnectionChangeRecordWriter writer =
          new ConnectionChangeRecordWriter(connection);
      while (reader.hasNext())
      {
        ChangeRecord changeRecord = reader.readChangeRecord();
        writer.writeChangeRecord(changeRecord);
        System.err.println("Successfully modified entry "
            + changeRecord.getName().toString());
      }
    }
    catch (final ErrorResultException e)
    {
      System.err.println(e.getMessage());
      System.exit(e.getResult().getResultCode().intValue());
      return;
    }
    catch (final InterruptedException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
      return;
    }
    catch (final IOException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
      return;
    }
    finally
    {
      if (connection != null)
      {
        connection.close();
      }
      try
      {
        reader.close();
      }
      catch (final IOException ignored)
      {
        // Ignore.
      }
    }
  }
  private Modify()
  {
    // Not used.
  }
}
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Proxy.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,20 +27,46 @@
package org.forgerock.opendj.examples;
import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.controls.*;
import org.forgerock.opendj.ldap.requests.*;
import org.forgerock.opendj.ldap.responses.*;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ConnectionFactory;
import org.forgerock.opendj.ldap.Connections;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.IntermediateResponseHandler;
import org.forgerock.opendj.ldap.LDAPClientContext;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.LDAPListener;
import org.forgerock.opendj.ldap.LDAPListenerOptions;
import org.forgerock.opendj.ldap.RequestContext;
import org.forgerock.opendj.ldap.RequestHandler;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.RoundRobinLoadBalancingAlgorithm;
import org.forgerock.opendj.ldap.SearchResultHandler;
import org.forgerock.opendj.ldap.ServerConnectionFactory;
import org.forgerock.opendj.ldap.controls.ProxiedAuthV2RequestControl;
import org.forgerock.opendj.ldap.requests.AddRequest;
import org.forgerock.opendj.ldap.requests.BindRequest;
import org.forgerock.opendj.ldap.requests.CancelExtendedRequest;
import org.forgerock.opendj.ldap.requests.CompareRequest;
import org.forgerock.opendj.ldap.requests.DeleteRequest;
import org.forgerock.opendj.ldap.requests.ExtendedRequest;
import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
import org.forgerock.opendj.ldap.requests.ModifyRequest;
import org.forgerock.opendj.ldap.requests.Request;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.CompareResult;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
/**
 * An LDAP load balancing proxy which forwards requests to one or more remote
@@ -60,534 +85,402 @@
 *      [&lt;remoteAddress2> &lt;remotePort2> ...]
 * </pre>
 */
public final class Proxy
{
  private static final class ProxyBackend implements
      RequestHandler<RequestContext>
  {
    private final ConnectionFactory factory;
    private final ConnectionFactory bindFactory;
public final class Proxy {
    private static final class ProxyBackend implements RequestHandler<RequestContext> {
        private final ConnectionFactory factory;
        private final ConnectionFactory bindFactory;
    private ProxyBackend(final ConnectionFactory factory,
        final ConnectionFactory bindFactory)
    {
      this.factory = factory;
      this.bindFactory = bindFactory;
    }
    private abstract class AbstractRequestCompletionHandler
        <R extends Result, H extends ResultHandler<? super R>>
        implements ResultHandler<R>
    {
      final H resultHandler;
      final Connection connection;
      AbstractRequestCompletionHandler(final Connection connection,
          final H resultHandler)
      {
        this.connection = connection;
        this.resultHandler = resultHandler;
      }
      @Override
      public final void handleErrorResult(final ErrorResultException error)
      {
        connection.close();
        resultHandler.handleErrorResult(error);
      }
      @Override
      public final void handleResult(final R result)
      {
        connection.close();
        resultHandler.handleResult(result);
      }
    }
    private abstract class ConnectionCompletionHandler<R extends Result>
        implements ResultHandler<Connection>
    {
      private final ResultHandler<? super R> resultHandler;
      ConnectionCompletionHandler(final ResultHandler<? super R> resultHandler)
      {
        this.resultHandler = resultHandler;
      }
      @Override
      public final void handleErrorResult(final ErrorResultException error)
      {
        resultHandler.handleErrorResult(error);
      }
      @Override
      public abstract void handleResult(Connection connection);
    }
    private final class RequestCompletionHandler<R extends Result> extends
        AbstractRequestCompletionHandler<R, ResultHandler<? super R>>
    {
      RequestCompletionHandler(final Connection connection,
          final ResultHandler<? super R> resultHandler)
      {
        super(connection, resultHandler);
      }
    }
    private final class SearchRequestCompletionHandler extends
        AbstractRequestCompletionHandler<Result, SearchResultHandler> implements
        SearchResultHandler
    {
      SearchRequestCompletionHandler(final Connection connection,
          final SearchResultHandler resultHandler)
      {
        super(connection, resultHandler);
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public final boolean handleEntry(final SearchResultEntry entry)
      {
        return resultHandler.handleEntry(entry);
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public final boolean handleReference(
          final SearchResultReference reference)
      {
        return resultHandler.handleReference(reference);
      }
    }
    private volatile ProxiedAuthV2RequestControl proxiedAuthControl = null;
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleAdd(final RequestContext requestContext,
        final AddRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super Result> resultHandler)
    {
      addProxiedAuthControl(request);
      final ConnectionCompletionHandler<Result> outerHandler =
          new ConnectionCompletionHandler<Result>(resultHandler)
      {
        @Override
        public void handleResult(final Connection connection)
        {
          final RequestCompletionHandler<Result> innerHandler =
              new RequestCompletionHandler<Result>(connection, resultHandler);
          connection.addAsync(request, intermediateResponseHandler,
              innerHandler);
        private ProxyBackend(final ConnectionFactory factory, final ConnectionFactory bindFactory) {
            this.factory = factory;
            this.bindFactory = bindFactory;
        }
      };
        private abstract class AbstractRequestCompletionHandler<R extends Result, H extends ResultHandler<? super R>>
                implements ResultHandler<R> {
            final H resultHandler;
            final Connection connection;
      factory.getConnectionAsync(outerHandler);
    }
            AbstractRequestCompletionHandler(final Connection connection, final H resultHandler) {
                this.connection = connection;
                this.resultHandler = resultHandler;
            }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleBind(final RequestContext requestContext,
        final int version, final BindRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super BindResult> resultHandler)
    {
      if (request.getAuthenticationType() != ((byte) 0x80))
      {
        // TODO: SASL authentication not implemented.
        resultHandler.handleErrorResult(newErrorResult(
            ResultCode.PROTOCOL_ERROR,
            "non-SIMPLE authentication not supported: "
                + request.getAuthenticationType()));
      }
      else
      {
        // Authenticate using a separate bind connection pool, because we
        // don't want to change the state of the pooled connection.
        final ConnectionCompletionHandler<BindResult> outerHandler =
            new ConnectionCompletionHandler<BindResult>(resultHandler)
        {
          @Override
          public void handleResult(final Connection connection)
          {
            final ResultHandler<BindResult> innerHandler =
                new ResultHandler<BindResult>()
            {
              @Override
              public final void handleErrorResult(
                  final ErrorResultException error)
              {
            @Override
            public final void handleErrorResult(final ErrorResultException error) {
                connection.close();
                resultHandler.handleErrorResult(error);
              }
            }
              @Override
              public final void handleResult(final BindResult result)
              {
            @Override
            public final void handleResult(final R result) {
                connection.close();
                proxiedAuthControl = ProxiedAuthV2RequestControl
                    .newControl("dn:" + request.getName());
                resultHandler.handleResult(result);
              }
            };
            connection.bindAsync(request, intermediateResponseHandler,
                innerHandler);
          }
            }
        };
        proxiedAuthControl = null;
        bindFactory.getConnectionAsync(outerHandler);
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleCompare(final RequestContext requestContext,
        final CompareRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super CompareResult> resultHandler)
    {
      addProxiedAuthControl(request);
      final ConnectionCompletionHandler<CompareResult> outerHandler =
          new ConnectionCompletionHandler<CompareResult>(resultHandler)
      {
        @Override
        public void handleResult(final Connection connection)
        {
          final RequestCompletionHandler<CompareResult> innerHandler =
              new RequestCompletionHandler<CompareResult>(connection,
                  resultHandler);
          connection.compareAsync(request, intermediateResponseHandler,
              innerHandler);
        }
      };
        private abstract class ConnectionCompletionHandler<R extends Result> implements
                ResultHandler<Connection> {
            private final ResultHandler<? super R> resultHandler;
      factory.getConnectionAsync(outerHandler);
    }
            ConnectionCompletionHandler(final ResultHandler<? super R> resultHandler) {
                this.resultHandler = resultHandler;
            }
            @Override
            public final void handleErrorResult(final ErrorResultException error) {
                resultHandler.handleErrorResult(error);
            }
            @Override
            public abstract void handleResult(Connection connection);
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleDelete(final RequestContext requestContext,
        final DeleteRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super Result> resultHandler)
    {
      addProxiedAuthControl(request);
      final ConnectionCompletionHandler<Result> outerHandler =
          new ConnectionCompletionHandler<Result>(resultHandler)
      {
        @Override
        public void handleResult(final Connection connection)
        {
          final RequestCompletionHandler<Result> innerHandler =
              new RequestCompletionHandler<Result>(connection, resultHandler);
          connection.deleteAsync(request, intermediateResponseHandler,
              innerHandler);
        }
      };
      factory.getConnectionAsync(outerHandler);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <R extends ExtendedResult> void handleExtendedRequest(
        final RequestContext requestContext, final ExtendedRequest<R> request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super R> resultHandler)
    {
      if (request.getOID().equals(CancelExtendedRequest.OID))
      {
        // TODO: not implemented.
        resultHandler.handleErrorResult(newErrorResult(
            ResultCode.PROTOCOL_ERROR,
            "Cancel extended request operation not supported"));
      }
      else if (request.getOID().equals(StartTLSExtendedRequest.OID))
      {
        // TODO: not implemented.
        resultHandler.handleErrorResult(newErrorResult(
            ResultCode.PROTOCOL_ERROR,
            "StartTLS extended request operation not supported"));
      }
      else
      {
        // Forward all other extended operations.
        addProxiedAuthControl(request);
        final ConnectionCompletionHandler<R> outerHandler =
            new ConnectionCompletionHandler<R>(resultHandler)
        {
          @Override
          public void handleResult(final Connection connection)
          {
            final RequestCompletionHandler<R> innerHandler =
                new RequestCompletionHandler<R>(connection, resultHandler);
            connection.extendedRequestAsync(request,
                intermediateResponseHandler, innerHandler);
          }
        };
        factory.getConnectionAsync(outerHandler);
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleModify(final RequestContext requestContext,
        final ModifyRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super Result> resultHandler)
    {
      addProxiedAuthControl(request);
      final ConnectionCompletionHandler<Result> outerHandler =
          new ConnectionCompletionHandler<Result>(resultHandler)
      {
        @Override
        public void handleResult(final Connection connection)
        {
          final RequestCompletionHandler<Result> innerHandler =
              new RequestCompletionHandler<Result>(connection, resultHandler);
          connection.modifyAsync(request, intermediateResponseHandler,
              innerHandler);
        private final class RequestCompletionHandler<R extends Result> extends
                AbstractRequestCompletionHandler<R, ResultHandler<? super R>> {
            RequestCompletionHandler(final Connection connection,
                    final ResultHandler<? super R> resultHandler) {
                super(connection, resultHandler);
            }
        }
      };
        private final class SearchRequestCompletionHandler extends
                AbstractRequestCompletionHandler<Result, SearchResultHandler> implements
                SearchResultHandler {
      factory.getConnectionAsync(outerHandler);
    }
            SearchRequestCompletionHandler(final Connection connection,
                    final SearchResultHandler resultHandler) {
                super(connection, resultHandler);
            }
            /**
             * {@inheritDoc}
             */
            @Override
            public final boolean handleEntry(final SearchResultEntry entry) {
                return resultHandler.handleEntry(entry);
            }
            /**
             * {@inheritDoc}
             */
            @Override
            public final boolean handleReference(final SearchResultReference reference) {
                return resultHandler.handleReference(reference);
            }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleModifyDN(final RequestContext requestContext,
        final ModifyDNRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super Result> resultHandler)
    {
      addProxiedAuthControl(request);
      final ConnectionCompletionHandler<Result> outerHandler =
          new ConnectionCompletionHandler<Result>(resultHandler)
      {
        @Override
        public void handleResult(final Connection connection)
        {
          final RequestCompletionHandler<Result> innerHandler =
              new RequestCompletionHandler<Result>(connection, resultHandler);
          connection.modifyDNAsync(request, intermediateResponseHandler,
              innerHandler);
        }
      };
        private volatile ProxiedAuthV2RequestControl proxiedAuthControl = null;
      factory.getConnectionAsync(outerHandler);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleSearch(final RequestContext requestContext,
        final SearchRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final SearchResultHandler resultHandler)
    {
      addProxiedAuthControl(request);
      final ConnectionCompletionHandler<Result> outerHandler =
          new ConnectionCompletionHandler<Result>(resultHandler)
      {
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleResult(final Connection connection)
        {
          final SearchRequestCompletionHandler innerHandler =
              new SearchRequestCompletionHandler(connection, resultHandler);
          connection.searchAsync(request, intermediateResponseHandler,
              innerHandler);
        public void handleAdd(final RequestContext requestContext, final AddRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super Result> resultHandler) {
            addProxiedAuthControl(request);
            final ConnectionCompletionHandler<Result> outerHandler =
                    new ConnectionCompletionHandler<Result>(resultHandler) {
                        @Override
                        public void handleResult(final Connection connection) {
                            final RequestCompletionHandler<Result> innerHandler =
                                    new RequestCompletionHandler<Result>(connection, resultHandler);
                            connection.addAsync(request, intermediateResponseHandler, innerHandler);
                        }
                    };
            factory.getConnectionAsync(outerHandler);
        }
      };
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleBind(final RequestContext requestContext, final int version,
                final BindRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super BindResult> resultHandler) {
      factory.getConnectionAsync(outerHandler);
            if (request.getAuthenticationType() != ((byte) 0x80)) {
                // TODO: SASL authentication not implemented.
                resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
                        "non-SIMPLE authentication not supported: "
                                + request.getAuthenticationType()));
            } else {
                // Authenticate using a separate bind connection pool, because
                // we
                // don't want to change the state of the pooled connection.
                final ConnectionCompletionHandler<BindResult> outerHandler =
                        new ConnectionCompletionHandler<BindResult>(resultHandler) {
                            @Override
                            public void handleResult(final Connection connection) {
                                final ResultHandler<BindResult> innerHandler =
                                        new ResultHandler<BindResult>() {
                                            @Override
                                            public final void handleErrorResult(
                                                    final ErrorResultException error) {
                                                connection.close();
                                                resultHandler.handleErrorResult(error);
                                            }
                                            @Override
                                            public final void handleResult(final BindResult result) {
                                                connection.close();
                                                proxiedAuthControl =
                                                        ProxiedAuthV2RequestControl
                                                                .newControl("dn:"
                                                                        + request.getName());
                                                resultHandler.handleResult(result);
                                            }
                                        };
                                connection.bindAsync(request, intermediateResponseHandler,
                                        innerHandler);
                            }
                        };
                proxiedAuthControl = null;
                bindFactory.getConnectionAsync(outerHandler);
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleCompare(final RequestContext requestContext,
                final CompareRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super CompareResult> resultHandler) {
            addProxiedAuthControl(request);
            final ConnectionCompletionHandler<CompareResult> outerHandler =
                    new ConnectionCompletionHandler<CompareResult>(resultHandler) {
                        @Override
                        public void handleResult(final Connection connection) {
                            final RequestCompletionHandler<CompareResult> innerHandler =
                                    new RequestCompletionHandler<CompareResult>(connection,
                                            resultHandler);
                            connection.compareAsync(request, intermediateResponseHandler,
                                    innerHandler);
                        }
                    };
            factory.getConnectionAsync(outerHandler);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleDelete(final RequestContext requestContext, final DeleteRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super Result> resultHandler) {
            addProxiedAuthControl(request);
            final ConnectionCompletionHandler<Result> outerHandler =
                    new ConnectionCompletionHandler<Result>(resultHandler) {
                        @Override
                        public void handleResult(final Connection connection) {
                            final RequestCompletionHandler<Result> innerHandler =
                                    new RequestCompletionHandler<Result>(connection, resultHandler);
                            connection.deleteAsync(request, intermediateResponseHandler,
                                    innerHandler);
                        }
                    };
            factory.getConnectionAsync(outerHandler);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public <R extends ExtendedResult> void handleExtendedRequest(
                final RequestContext requestContext, final ExtendedRequest<R> request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super R> resultHandler) {
            if (request.getOID().equals(CancelExtendedRequest.OID)) {
                // TODO: not implemented.
                resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
                        "Cancel extended request operation not supported"));
            } else if (request.getOID().equals(StartTLSExtendedRequest.OID)) {
                // TODO: not implemented.
                resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
                        "StartTLS extended request operation not supported"));
            } else {
                // Forward all other extended operations.
                addProxiedAuthControl(request);
                final ConnectionCompletionHandler<R> outerHandler =
                        new ConnectionCompletionHandler<R>(resultHandler) {
                            @Override
                            public void handleResult(final Connection connection) {
                                final RequestCompletionHandler<R> innerHandler =
                                        new RequestCompletionHandler<R>(connection, resultHandler);
                                connection.extendedRequestAsync(request,
                                        intermediateResponseHandler, innerHandler);
                            }
                        };
                factory.getConnectionAsync(outerHandler);
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleModify(final RequestContext requestContext, final ModifyRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super Result> resultHandler) {
            addProxiedAuthControl(request);
            final ConnectionCompletionHandler<Result> outerHandler =
                    new ConnectionCompletionHandler<Result>(resultHandler) {
                        @Override
                        public void handleResult(final Connection connection) {
                            final RequestCompletionHandler<Result> innerHandler =
                                    new RequestCompletionHandler<Result>(connection, resultHandler);
                            connection.modifyAsync(request, intermediateResponseHandler,
                                    innerHandler);
                        }
                    };
            factory.getConnectionAsync(outerHandler);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleModifyDN(final RequestContext requestContext,
                final ModifyDNRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super Result> resultHandler) {
            addProxiedAuthControl(request);
            final ConnectionCompletionHandler<Result> outerHandler =
                    new ConnectionCompletionHandler<Result>(resultHandler) {
                        @Override
                        public void handleResult(final Connection connection) {
                            final RequestCompletionHandler<Result> innerHandler =
                                    new RequestCompletionHandler<Result>(connection, resultHandler);
                            connection.modifyDNAsync(request, intermediateResponseHandler,
                                    innerHandler);
                        }
                    };
            factory.getConnectionAsync(outerHandler);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleSearch(final RequestContext requestContext, final SearchRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final SearchResultHandler resultHandler) {
            addProxiedAuthControl(request);
            final ConnectionCompletionHandler<Result> outerHandler =
                    new ConnectionCompletionHandler<Result>(resultHandler) {
                        @Override
                        public void handleResult(final Connection connection) {
                            final SearchRequestCompletionHandler innerHandler =
                                    new SearchRequestCompletionHandler(connection, resultHandler);
                            connection.searchAsync(request, intermediateResponseHandler,
                                    innerHandler);
                        }
                    };
            factory.getConnectionAsync(outerHandler);
        }
        private void addProxiedAuthControl(final Request request) {
            final ProxiedAuthV2RequestControl control = proxiedAuthControl;
            if (control != null) {
                request.addControl(control);
            }
        }
    }
    /**
     * Main method.
     *
     * @param args
     *            The command line arguments: listen address, listen port,
     *            remote address1, remote port1, remote address2, remote port2,
     *            ...
     */
    public static void main(final String[] args) {
        if (args.length < 4 || args.length % 2 != 0) {
            System.err.println("Usage: listenAddress listenPort "
                    + "remoteAddress1 remotePort1 remoteAddress2 remotePort2");
            System.exit(1);
        }
        // Parse command line arguments.
        final String localAddress = args[0];
        final int localPort = Integer.parseInt(args[1]);
    private void addProxiedAuthControl(final Request request)
    {
      final ProxiedAuthV2RequestControl control = proxiedAuthControl;
      if (control != null)
      {
        request.addControl(control);
      }
        // Create load balancer.
        final List<ConnectionFactory> factories = new LinkedList<ConnectionFactory>();
        final List<ConnectionFactory> bindFactories = new LinkedList<ConnectionFactory>();
        for (int i = 2; i < args.length; i += 2) {
            final String remoteAddress = args[i];
            final int remotePort = Integer.parseInt(args[i + 1]);
            factories.add(Connections.newFixedConnectionPool(new LDAPConnectionFactory(
                    remoteAddress, remotePort), Integer.MAX_VALUE));
            bindFactories.add(Connections.newFixedConnectionPool(new LDAPConnectionFactory(
                    remoteAddress, remotePort), Integer.MAX_VALUE));
        }
        final RoundRobinLoadBalancingAlgorithm algorithm =
                new RoundRobinLoadBalancingAlgorithm(factories);
        final RoundRobinLoadBalancingAlgorithm bindAlgorithm =
                new RoundRobinLoadBalancingAlgorithm(bindFactories);
        final ConnectionFactory factory = Connections.newLoadBalancer(algorithm);
        final ConnectionFactory bindFactory = Connections.newLoadBalancer(bindAlgorithm);
        // Create a server connection adapter.
        final ProxyBackend backend = new ProxyBackend(factory, bindFactory);
        final ServerConnectionFactory<LDAPClientContext, Integer> connectionHandler =
                Connections.newServerConnectionFactory(backend);
        // Create listener.
        final LDAPListenerOptions options = new LDAPListenerOptions().setBacklog(4096);
        LDAPListener listener = null;
        try {
            listener = new LDAPListener(localAddress, localPort, connectionHandler, options);
            System.out.println("Press any key to stop the server...");
            System.in.read();
        } catch (final IOException e) {
            System.out.println("Error listening on " + localAddress + ":" + localPort);
            e.printStackTrace();
        } finally {
            if (listener != null) {
                listener.close();
            }
        }
    }
  }
  /**
   * Main method.
   *
   * @param args
   *          The command line arguments: listen address, listen port, remote
   *          address1, remote port1, remote address2, remote port2, ...
   */
  public static void main(final String[] args)
  {
    if (args.length < 4 || args.length % 2 != 0)
    {
      System.err.println("Usage: listenAddress listenPort "
          + "remoteAddress1 remotePort1 remoteAddress2 remotePort2");
      System.exit(1);
    private Proxy() {
        // Not used.
    }
    // Parse command line arguments.
    final String localAddress = args[0];
    final int localPort = Integer.parseInt(args[1]);
    // Create load balancer.
    final List<ConnectionFactory> factories =
        new LinkedList<ConnectionFactory>();
    final List<ConnectionFactory> bindFactories =
        new LinkedList<ConnectionFactory>();
    for (int i = 2; i < args.length; i += 2)
    {
      final String remoteAddress = args[i];
      final int remotePort = Integer.parseInt(args[i + 1]);
      factories.add(Connections.newFixedConnectionPool(
          new LDAPConnectionFactory(remoteAddress, remotePort),
          Integer.MAX_VALUE));
      bindFactories.add(Connections.newFixedConnectionPool(
          new LDAPConnectionFactory(remoteAddress, remotePort),
          Integer.MAX_VALUE));
    }
    final RoundRobinLoadBalancingAlgorithm algorithm =
        new RoundRobinLoadBalancingAlgorithm(factories);
    final RoundRobinLoadBalancingAlgorithm bindAlgorithm =
        new RoundRobinLoadBalancingAlgorithm(bindFactories);
    final ConnectionFactory factory = Connections.newLoadBalancer(algorithm);
    final ConnectionFactory bindFactory = Connections
        .newLoadBalancer(bindAlgorithm);
    // Create a server connection adapter.
    final ProxyBackend backend = new ProxyBackend(factory, bindFactory);
    final ServerConnectionFactory<LDAPClientContext, Integer> connectionHandler
      = Connections.newServerConnectionFactory(backend);
    // Create listener.
    final LDAPListenerOptions options = new LDAPListenerOptions()
        .setBacklog(4096);
    LDAPListener listener = null;
    try
    {
      listener = new LDAPListener(localAddress, localPort, connectionHandler,
          options);
      System.out.println("Press any key to stop the server...");
      System.in.read();
    }
    catch (final IOException e)
    {
      System.out
          .println("Error listening on " + localAddress + ":" + localPort);
      e.printStackTrace();
    }
    finally
    {
      if (listener != null)
      {
        listener.close();
      }
    }
  }
  private Proxy()
  {
    // Not used.
  }
}
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ReadSchema.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,13 +27,17 @@
package org.forgerock.opendj.examples;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.schema.*;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.schema.AttributeType;
import org.forgerock.opendj.ldap.schema.MatchingRule;
import org.forgerock.opendj.ldap.schema.ObjectClass;
import org.forgerock.opendj.ldap.schema.Schema;
import org.forgerock.opendj.ldap.schema.Syntax;
/**
 * An example client application which prints a summary of the schema on the
@@ -45,103 +48,83 @@
 *  &lt;host> &lt;port> &lt;username> &lt;password>
 * </pre>
 */
public final class ReadSchema
{
  /**
   * Main method.
   *
   * @param args
   *          The command line arguments: host, port, username, password.
   */
  public static void main(final String[] args)
  {
    if (args.length != 4)
    {
      System.err.println("Usage: host port username password");
      System.exit(1);
public final class ReadSchema {
    /**
     * Main method.
     *
     * @param args
     *            The command line arguments: host, port, username, password.
     */
    public static void main(final String[] args) {
        if (args.length != 4) {
            System.err.println("Usage: host port username password");
            System.exit(1);
        }
        // Parse command line arguments.
        final String hostName = args[0];
        final int port = Integer.parseInt(args[1]);
        final String userName = args[2];
        final String password = args[3];
        // Connect and bind to the server.
        final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName, port);
        Connection connection = null;
        try {
            connection = factory.getConnection();
            connection.bind(userName, password.toCharArray());
            // Read the schema.
            Schema schema = Schema.readSchemaForEntry(connection, DN.rootDN());
            System.out.println("Attribute types");
            for (AttributeType at : schema.getAttributeTypes()) {
                System.out.println("  " + at.getNameOrOID());
            }
            System.out.println();
            System.out.println("Object classes");
            for (ObjectClass oc : schema.getObjectClasses()) {
                System.out.println("  " + oc.getNameOrOID());
            }
            System.out.println();
            System.out.println("Matching rules");
            for (MatchingRule mr : schema.getMatchingRules()) {
                System.out.println("  " + mr.getNameOrOID());
            }
            System.out.println();
            System.out.println("Syntaxes");
            for (Syntax s : schema.getSyntaxes()) {
                System.out.println("  " + s.getDescription());
            }
            System.out.println();
            // Etc...
            System.out.println("WARNINGS");
            for (LocalizableMessage m : schema.getWarnings()) {
                System.out.println("  " + m.toString());
            }
            System.out.println();
        } catch (final ErrorResultException e) {
            System.err.println(e.getMessage());
            System.exit(e.getResult().getResultCode().intValue());
            return;
        } catch (final InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
            return;
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
    }
    // Parse command line arguments.
    final String hostName = args[0];
    final int port = Integer.parseInt(args[1]);
    final String userName = args[2];
    final String password = args[3];
    // Connect and bind to the server.
    final LDAPConnectionFactory factory = new LDAPConnectionFactory(
        hostName, port);
    Connection connection = null;
    try
    {
      connection = factory.getConnection();
      connection.bind(userName, password.toCharArray());
      // Read the schema.
      Schema schema = Schema.readSchemaForEntry(connection, DN.rootDN());
      System.out.println("Attribute types");
      for (AttributeType at : schema.getAttributeTypes())
      {
        System.out.println("  " + at.getNameOrOID());
      }
      System.out.println();
      System.out.println("Object classes");
      for (ObjectClass oc : schema.getObjectClasses())
      {
        System.out.println("  " + oc.getNameOrOID());
      }
      System.out.println();
      System.out.println("Matching rules");
      for (MatchingRule mr : schema.getMatchingRules())
      {
        System.out.println("  " + mr.getNameOrOID());
      }
      System.out.println();
      System.out.println("Syntaxes");
      for (Syntax s : schema.getSyntaxes())
      {
        System.out.println("  " + s.getDescription());
      }
      System.out.println();
      // Etc...
      System.out.println("WARNINGS");
      for (LocalizableMessage m : schema.getWarnings())
      {
        System.out.println("  " + m.toString());
      }
      System.out.println();
    private ReadSchema() {
        // Not used.
    }
    catch (final ErrorResultException e)
    {
      System.err.println(e.getMessage());
      System.exit(e.getResult().getResultCode().intValue());
      return;
    }
    catch (final InterruptedException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
      return;
    }
    finally
    {
      if (connection != null)
      {
        connection.close();
      }
    }
  }
  private ReadSchema()
  {
    // Not used.
  }
}
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SASLAuth.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -47,8 +46,6 @@
import org.forgerock.opendj.ldap.requests.PlainSASLBindRequest;
import org.forgerock.opendj.ldap.requests.Requests;
/**
 * An example client application which performs SASL PLAIN authentication to a
 * directory server over LDAP with StartTLS. This example takes the following
@@ -60,9 +57,8 @@
 * <li>authcid - Authentication identity</li>
 * <li>passwd - Password of the user to authenticate</li>
 * </ul>
 * The host, port, authcid, and passwd are required.
 * SASL PLAIN is described in <a href="http://www.ietf.org/rfc/rfc4616.txt">RFC
 * 4616</a>.
 * The host, port, authcid, and passwd are required. SASL PLAIN is described in
 * <a href="http://www.ietf.org/rfc/rfc4616.txt">RFC 4616</a>.
 * <p>
 * The authzid and authcid are prefixed as described in <a
 * href="http://tools.ietf.org/html/rfc4513#section-5.2.1.8">RFC 4513, section
@@ -72,139 +68,114 @@
 * By default, OpenDJ is set up for SASL PLAIN to use the Exact Match Identity
 * Mapper to find entries by searching uid values for the user ID. In other
 * words, the following examples are equivalent.
 *
 * <pre>
 * dn:uid=bjensen,ou=people,dc=example,dc=com
 * u:bjensen
 * </pre>
 */
public final class SASLAuth
{
  /**
   * Authenticate to the directory using SASL PLAIN.
   * @param args The command line arguments
   */
  public static void main(String[] args)
  {
    parseArgs(args);
    Connection connection = null;
public final class SASLAuth {
    /**
     * Authenticate to the directory using SASL PLAIN.
     *
     * @param args
     *            The command line arguments
     */
    public static void main(String[] args) {
        parseArgs(args);
        Connection connection = null;
    try
    {
      final LDAPConnectionFactory factory =
          new LDAPConnectionFactory(host, port, getTrustAllOptions());
      connection = factory.getConnection();
      PlainSASLBindRequest request = Requests.newPlainSASLBindRequest(
          authcid, passwd.toCharArray());
      if (authzid != null) request.setAuthorizationID(authzid);
      connection.bind(request);
      System.out.println("Authenticated as " + authcid + ".");
        try {
            final LDAPConnectionFactory factory =
                    new LDAPConnectionFactory(host, port, getTrustAllOptions());
            connection = factory.getConnection();
            PlainSASLBindRequest request =
                    Requests.newPlainSASLBindRequest(authcid, passwd.toCharArray());
            if (authzid != null) {
                request.setAuthorizationID(authzid);
            }
            connection.bind(request);
            System.out.println("Authenticated as " + authcid + ".");
        } catch (final ErrorResultException e) {
            System.err.println(e.getMessage());
            System.exit(e.getResult().getResultCode().intValue());
            return;
        } catch (final InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
            return;
        } catch (final GeneralSecurityException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_CONNECT_ERROR.intValue());
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
    }
    catch (final ErrorResultException e)
    {
      System.err.println(e.getMessage());
      System.exit(e.getResult().getResultCode().intValue());
      return;
    /**
     * For StartTLS the connection factory needs SSL context options. In the
     * general case, a trust manager in the SSL context serves to check server
     * certificates, and a key manager handles client keys when the server
     * checks certificates from our client.
     *
     * OpenDJ directory server lets you install by default with a self-signed
     * certificate that is not in the system trust store. To simplify this
     * implementation trusts all server certificates.
     */
    private static LDAPOptions getTrustAllOptions() throws GeneralSecurityException {
        LDAPOptions lo = new LDAPOptions();
        SSLContext sslContext =
                new SSLContextBuilder().setTrustManager(TrustManagers.trustAll()).getSSLContext();
        lo.setSSLContext(sslContext);
        lo.setUseStartTLS(true);
        return lo;
    }
    catch (final InterruptedException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
      return;
    private static String host;
    private static int port;
    private static String authzid;
    private static String authcid;
    private static String passwd;
    /**
     * Parse command line arguments.
     *
     * @param args
     *            host port [authzid] authcid passwd
     */
    private static void parseArgs(String[] args) {
        if (args.length < 4 || args.length > 5) {
            giveUp();
        }
        host = args[0];
        port = Integer.parseInt(args[1]);
        if (args.length == 5) {
            authzid = args[2];
            authcid = args[3];
            passwd = args[4];
        } else {
            authzid = null;
            authcid = args[2];
            passwd = args[3];
        }
    }
    catch (final GeneralSecurityException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_CONNECT_ERROR.intValue());
    private static void giveUp() {
        printUsage();
        System.exit(1);
    }
    finally
    {
      if (connection != null)
        connection.close();
    private static void printUsage() {
        System.err.println("Usage: host port [authzid] authcid passwd");
        System.err.println("\tThe port must be able to handle LDAP with StartTLS.");
        System.err.println("\tSee http://www.ietf.org/rfc/rfc4616.txt for more on SASL PLAIN.");
    }
  }
  /**
   * For StartTLS the connection factory needs SSL context options.
   * In the general case, a trust manager in the SSL context serves to check
   * server certificates, and a key manager handles client keys when the server
   * checks certificates from our client.
   *
   * OpenDJ directory server lets you install by default with a self-signed
   * certificate that is not in the system trust store. To simplify this
   * implementation trusts all server certificates.
   */
  private static LDAPOptions getTrustAllOptions()
    throws GeneralSecurityException
  {
    LDAPOptions lo = new LDAPOptions();
    SSLContext sslContext = new SSLContextBuilder()
      .setTrustManager(TrustManagers.trustAll()).getSSLContext();
    lo.setSSLContext(sslContext);
    lo.setUseStartTLS(true);
    return lo;
  }
  private static String host;
  private static int port;
  private static String authzid;
  private static String authcid;
  private static String passwd;
  /**
   * Parse command line arguments.
   * @param args host port [authzid] authcid passwd
   */
  private static void parseArgs(String[] args)
  {
    if (args.length < 4 || args.length > 5) giveUp();
    host = args[0];
    port = Integer.parseInt(args[1]);
    if (args.length == 5)
    {
      authzid = args[2];
      authcid = args[3];
      passwd = args[4];
    private SASLAuth() {
        // Not used.
    }
    else
    {
      authzid = null;
      authcid = args[2];
      passwd = args[3];
    }
  }
  private static void giveUp()
  {
    printUsage();
    System.exit(1);
  }
  private static void printUsage()
  {
    System.err.println(
      "Usage: host port [authzid] authcid passwd");
    System.err.println(
      "\tThe port must be able to handle LDAP with StartTLS.");
    System.err.println(
      "\tSee http://www.ietf.org/rfc/rfc4616.txt for more on SASL PLAIN.");
  }
  private SASLAuth()
  {
    // Not used.
  }
}
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Search.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,16 +27,19 @@
package org.forgerock.opendj.examples;
import java.io.IOException;
import java.util.Arrays;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.responses.*;
import org.forgerock.opendj.ldif.*;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.ErrorResultIOException;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
import org.forgerock.opendj.ldif.ConnectionEntryReader;
import org.forgerock.opendj.ldif.LDIFEntryWriter;
/**
 * An example client application which searches a Directory Server. This example
@@ -48,141 +50,103 @@
 *      &lt;baseDN> &lt;scope> &lt;filter> [&lt;attibute> &lt;attribute> ...]
 * </pre>
 */
public final class Search
{
  /**
   * Main method.
   *
   * @param args
   *          The command line arguments: host, port, username, password, base
   *          DN, scope, filter, and zero or more attributes to be retrieved.
   */
  public static void main(final String[] args)
  {
    if (args.length < 7)
    {
      System.err
          .println("Usage: host port username password baseDN scope "
              + "filter [attribute ...]");
      System.exit(1);
    }
    // Parse command line arguments.
    final String hostName = args[0];
    final int port = Integer.parseInt(args[1]);
    final String userName = args[2];
    final String password = args[3];
    final String baseDN = args[4];
    final String scopeString = args[5];
    final String filter = args[6];
    String[] attributes;
    if (args.length > 7)
    {
      attributes = Arrays.copyOfRange(args, 7, args.length);
    }
    else
    {
      attributes = new String[0];
    }
    SearchScope scope;
    if (scopeString.equalsIgnoreCase("base"))
    {
      scope = SearchScope.BASE_OBJECT;
    }
    else if (scopeString.equalsIgnoreCase("one"))
    {
      scope = SearchScope.SINGLE_LEVEL;
    }
    else if (scopeString.equalsIgnoreCase("sub"))
    {
      scope = SearchScope.WHOLE_SUBTREE;
    }
    else if (scopeString.equalsIgnoreCase("subordinates"))
    {
      scope = SearchScope.SUBORDINATES;
    }
    else
    {
      System.err.println("Unknown scope: " + scopeString);
      System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
      return;
    }
    // Create an LDIF writer which will write the search results to stdout.
    final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
    // Connect and bind to the server.
    final LDAPConnectionFactory factory = new LDAPConnectionFactory(
        hostName, port);
    Connection connection = null;
    try
    {
      connection = factory.getConnection();
      connection.bind(userName, password.toCharArray());
      // Read the entries and output them as LDIF.
      final ConnectionEntryReader reader = connection.search(baseDN,
          scope, filter, attributes);
      while (reader.hasNext())
      {
        if (!reader.isReference())
        {
          final SearchResultEntry entry = reader.readEntry();
          writer.writeComment("Search result entry: "
              + entry.getName().toString());
          writer.writeEntry(entry);
public final class Search {
    /**
     * Main method.
     *
     * @param args
     *            The command line arguments: host, port, username, password,
     *            base DN, scope, filter, and zero or more attributes to be
     *            retrieved.
     */
    public static void main(final String[] args) {
        if (args.length < 7) {
            System.err.println("Usage: host port username password baseDN scope "
                    + "filter [attribute ...]");
            System.exit(1);
        }
        else
        {
          final SearchResultReference ref = reader.readReference();
          // Got a continuation reference.
          writer.writeComment("Search result reference: "
              + ref.getURIs().toString());
        // Parse command line arguments.
        final String hostName = args[0];
        final int port = Integer.parseInt(args[1]);
        final String userName = args[2];
        final String password = args[3];
        final String baseDN = args[4];
        final String scopeString = args[5];
        final String filter = args[6];
        String[] attributes;
        if (args.length > 7) {
            attributes = Arrays.copyOfRange(args, 7, args.length);
        } else {
            attributes = new String[0];
        }
      }
      writer.flush();
    }
    catch (final ErrorResultException e)
    {
      System.err.println(e.getMessage());
      System.exit(e.getResult().getResultCode().intValue());
      return;
    }
    catch (final ErrorResultIOException e)
    {
      System.err.println(e.getMessage());
      System
          .exit(e.getCause().getResult().getResultCode().intValue());
      return;
    }
    catch (final InterruptedException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
      return;
    }
    catch (final IOException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
      return;
    }
    finally
    {
      if (connection != null)
      {
        connection.close();
      }
    }
  }
        SearchScope scope;
        if (scopeString.equalsIgnoreCase("base")) {
            scope = SearchScope.BASE_OBJECT;
        } else if (scopeString.equalsIgnoreCase("one")) {
            scope = SearchScope.SINGLE_LEVEL;
        } else if (scopeString.equalsIgnoreCase("sub")) {
            scope = SearchScope.WHOLE_SUBTREE;
        } else if (scopeString.equalsIgnoreCase("subordinates")) {
            scope = SearchScope.SUBORDINATES;
        } else {
            System.err.println("Unknown scope: " + scopeString);
            System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
            return;
        }
        // Create an LDIF writer which will write the search results to stdout.
        final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
  private Search()
  {
    // Not used.
  }
        // Connect and bind to the server.
        final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName, port);
        Connection connection = null;
        try {
            connection = factory.getConnection();
            connection.bind(userName, password.toCharArray());
            // Read the entries and output them as LDIF.
            final ConnectionEntryReader reader =
                    connection.search(baseDN, scope, filter, attributes);
            while (reader.hasNext()) {
                if (!reader.isReference()) {
                    final SearchResultEntry entry = reader.readEntry();
                    writer.writeComment("Search result entry: " + entry.getName().toString());
                    writer.writeEntry(entry);
                } else {
                    final SearchResultReference ref = reader.readReference();
                    // Got a continuation reference.
                    writer.writeComment("Search result reference: " + ref.getURIs().toString());
                }
            }
            writer.flush();
        } catch (final ErrorResultException e) {
            System.err.println(e.getMessage());
            System.exit(e.getResult().getResultCode().intValue());
            return;
        } catch (final ErrorResultIOException e) {
            System.err.println(e.getMessage());
            System.exit(e.getCause().getResult().getResultCode().intValue());
            return;
        } catch (final InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
            return;
        } catch (final IOException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
            return;
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
    }
    private Search() {
        // Not used.
    }
}
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,18 +27,25 @@
package org.forgerock.opendj.examples;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.requests.*;
import org.forgerock.opendj.ldap.responses.*;
import org.forgerock.opendj.ldif.*;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.SearchResultHandler;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.requests.BindRequest;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
import org.forgerock.opendj.ldif.LDIFEntryWriter;
/**
 * An example client application which searches a Directory Server using the
@@ -50,268 +56,198 @@
 *      &lt;baseDN> &lt;scope> &lt;filter> [&lt;attibute> &lt;attribute> ...]
 * </pre>
 */
public final class SearchAsync
{
  private static final class BindResultHandlerImpl implements
      ResultHandler<BindResult>
  {
public final class SearchAsync {
    private static final class BindResultHandlerImpl implements ResultHandler<BindResult> {
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleErrorResult(final ErrorResultException error) {
            System.err.println(error.getMessage());
            resultCode = error.getResult().getResultCode().intValue();
            COMPLETION_LATCH.countDown();
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleResult(final BindResult result) {
            // Bind succeeded: initiate search.
            final SearchRequest request =
                    Requests.newSearchRequest(baseDN, scope, filter, attributes);
            connection.searchAsync(request, null, new SearchResultHandlerImpl());
        }
    }
    private static final class ConnectResultHandlerImpl implements ResultHandler<Connection> {
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleErrorResult(final ErrorResultException error) {
            System.err.println(error.getMessage());
            resultCode = error.getResult().getResultCode().intValue();
            COMPLETION_LATCH.countDown();
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleResult(final Connection connection) {
            // Connect succeeded: save connection and initiate bind.
            SearchAsync.connection = connection;
            final BindRequest request =
                    Requests.newSimpleBindRequest(userName, password.toCharArray());
            connection.bindAsync(request, null, new BindResultHandlerImpl());
        }
    }
    private static final class SearchResultHandlerImpl implements SearchResultHandler {
        /**
         * {@inheritDoc}
         */
        @Override
        public synchronized boolean handleEntry(final SearchResultEntry entry) {
            try {
                WRITER.writeComment("Search result entry: " + entry.getName().toString());
                WRITER.writeEntry(entry);
            } catch (final IOException e) {
                System.err.println(e.getMessage());
                resultCode = ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
                COMPLETION_LATCH.countDown();
                return false;
            }
            return true;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleErrorResult(final ErrorResultException error) {
            System.err.println(error.getMessage());
            resultCode = error.getResult().getResultCode().intValue();
            COMPLETION_LATCH.countDown();
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public synchronized boolean handleReference(final SearchResultReference reference) {
            try {
                WRITER.writeComment("Search result reference: " + reference.getURIs().toString());
            } catch (final IOException e) {
                System.err.println(e.getMessage());
                resultCode = ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
                COMPLETION_LATCH.countDown();
                return false;
            }
            return true;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleResult(final Result result) {
            resultCode = result.getResultCode().intValue();
            COMPLETION_LATCH.countDown();
        }
    }
    private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1);
    private static final LDIFEntryWriter WRITER = new LDIFEntryWriter(System.out);
    private static String userName;
    private static String password;
    private static String baseDN;
    private static SearchScope scope;
    private static String filter;
    private static String[] attributes;
    private static Connection connection = null;
    private static int resultCode = 0;
    /**
     * {@inheritDoc}
     * Main method.
     *
     * @param args
     *            The command line arguments: host, port, username, password,
     *            base DN, scope, filter, and zero or more attributes to be
     *            retrieved.
     */
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      System.err.println(error.getMessage());
      resultCode = error.getResult().getResultCode().intValue();
      COMPLETION_LATCH.countDown();
    public static void main(final String[] args) {
        if (args.length < 7) {
            System.err.println("Usage: host port username password baseDN scope "
                    + "filter [attribute ...]");
            System.exit(1);
        }
        // Parse command line arguments.
        final String hostName = args[0];
        final int port = Integer.parseInt(args[1]);
        userName = args[2];
        password = args[3];
        baseDN = args[4];
        final String scopeString = args[5];
        filter = args[6];
        if (args.length > 7) {
            attributes = Arrays.copyOfRange(args, 7, args.length);
        } else {
            attributes = new String[0];
        }
        if (scopeString.equalsIgnoreCase("base")) {
            scope = SearchScope.BASE_OBJECT;
        } else if (scopeString.equalsIgnoreCase("one")) {
            scope = SearchScope.SINGLE_LEVEL;
        } else if (scopeString.equalsIgnoreCase("sub")) {
            scope = SearchScope.WHOLE_SUBTREE;
        } else if (scopeString.equalsIgnoreCase("subordinates")) {
            scope = SearchScope.SUBORDINATES;
        } else {
            System.err.println("Unknown scope: " + scopeString);
            System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
            return;
        }
        // Initiate the asynchronous connect, bind, and search.
        final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName, port);
        factory.getConnectionAsync(new ConnectResultHandlerImpl());
        // Await completion.
        try {
            COMPLETION_LATCH.await();
        } catch (final InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
            return;
        }
        try {
            WRITER.flush();
        } catch (final IOException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
            return;
        }
        if (connection != null) {
            connection.close();
        }
        System.exit(resultCode);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleResult(final BindResult result)
    {
      // Bind succeeded: initiate search.
      final SearchRequest request = Requests.newSearchRequest(baseDN,
          scope, filter, attributes);
      connection.searchAsync(request, null, new SearchResultHandlerImpl());
    private SearchAsync() {
        // Not used.
    }
  }
  private static final class ConnectResultHandlerImpl implements
      ResultHandler<Connection>
  {
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      System.err.println(error.getMessage());
      resultCode = error.getResult().getResultCode().intValue();
      COMPLETION_LATCH.countDown();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleResult(final Connection connection)
    {
      // Connect succeeded: save connection and initiate bind.
      SearchAsync.connection = connection;
      final BindRequest request = Requests.newSimpleBindRequest(
          userName, password.toCharArray());
      connection.bindAsync(request, null, new BindResultHandlerImpl());
    }
  }
  private static final class SearchResultHandlerImpl implements
      SearchResultHandler
  {
    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized boolean handleEntry(
        final SearchResultEntry entry)
    {
      try
      {
        WRITER.writeComment("Search result entry: "
            + entry.getName().toString());
        WRITER.writeEntry(entry);
      }
      catch (final IOException e)
      {
        System.err.println(e.getMessage());
        resultCode = ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
        COMPLETION_LATCH.countDown();
        return false;
      }
      return true;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      System.err.println(error.getMessage());
      resultCode = error.getResult().getResultCode().intValue();
      COMPLETION_LATCH.countDown();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized boolean handleReference(
        final SearchResultReference reference)
    {
      try
      {
        WRITER.writeComment("Search result reference: "
            + reference.getURIs().toString());
      }
      catch (final IOException e)
      {
        System.err.println(e.getMessage());
        resultCode = ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
        COMPLETION_LATCH.countDown();
        return false;
      }
      return true;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleResult(final Result result)
    {
      resultCode = result.getResultCode().intValue();
      COMPLETION_LATCH.countDown();
    }
  }
  private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(
      1);
  private static final LDIFEntryWriter WRITER = new LDIFEntryWriter(
      System.out);
  private static String userName;
  private static String password;
  private static String baseDN;
  private static SearchScope scope;
  private static String filter;
  private static String[] attributes;
  private static Connection connection = null;
  private static int resultCode = 0;
  /**
   * Main method.
   *
   * @param args
   *          The command line arguments: host, port, username, password, base
   *          DN, scope, filter, and zero or more attributes to be retrieved.
   */
  public static void main(final String[] args)
  {
    if (args.length < 7)
    {
      System.err
          .println("Usage: host port username password baseDN scope "
              + "filter [attribute ...]");
      System.exit(1);
    }
    // Parse command line arguments.
    final String hostName = args[0];
    final int port = Integer.parseInt(args[1]);
    userName = args[2];
    password = args[3];
    baseDN = args[4];
    final String scopeString = args[5];
    filter = args[6];
    if (args.length > 7)
    {
      attributes = Arrays.copyOfRange(args, 7, args.length);
    }
    else
    {
      attributes = new String[0];
    }
    if (scopeString.equalsIgnoreCase("base"))
    {
      scope = SearchScope.BASE_OBJECT;
    }
    else if (scopeString.equalsIgnoreCase("one"))
    {
      scope = SearchScope.SINGLE_LEVEL;
    }
    else if (scopeString.equalsIgnoreCase("sub"))
    {
      scope = SearchScope.WHOLE_SUBTREE;
    }
    else if (scopeString.equalsIgnoreCase("subordinates"))
    {
      scope = SearchScope.SUBORDINATES;
    }
    else
    {
      System.err.println("Unknown scope: " + scopeString);
      System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
      return;
    }
    // Initiate the asynchronous connect, bind, and search.
    final LDAPConnectionFactory factory = new LDAPConnectionFactory(
        hostName, port);
    factory.getConnectionAsync(new ConnectResultHandlerImpl());
    // Await completion.
    try
    {
      COMPLETION_LATCH.await();
    }
    catch (final InterruptedException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
      return;
    }
    try
    {
      WRITER.flush();
    }
    catch (final IOException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
      return;
    }
    if (connection != null)
    {
      connection.close();
    }
    System.exit(resultCode);
  }
  private SearchAsync()
  {
    // Not used.
  }
}
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchBind.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -79,13 +78,13 @@
        char[] password = c.readPassword("Password: ");
        // Search using mail address, and then bind with the DN and password.
        final LDAPConnectionFactory factory = new LDAPConnectionFactory(host,
                port);
        final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port);
        Connection connection = null;
        try {
            connection = factory.getConnection();
            SearchResultEntry entry = connection.searchSingleEntry(baseDN,
                    SearchScope.WHOLE_SUBTREE, "(mail=" + mail + ")", "cn");
            SearchResultEntry entry =
                    connection.searchSingleEntry(baseDN, SearchScope.WHOLE_SUBTREE, "(mail=" + mail
                            + ")", "cn");
            DN bindDN = entry.getName();
            BindResult result = connection.bind(bindDN.toString(), password);
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Server.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,8 +27,6 @@
package org.forgerock.opendj.examples;
import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
import java.io.FileInputStream;
@@ -42,12 +39,45 @@
import javax.net.ssl.SSLContext;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.requests.*;
import org.forgerock.opendj.ldap.responses.*;
import org.forgerock.opendj.ldif.*;
import org.forgerock.opendj.ldap.CancelledResultException;
import org.forgerock.opendj.ldap.Connections;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.Filter;
import org.forgerock.opendj.ldap.IntermediateResponseHandler;
import org.forgerock.opendj.ldap.KeyManagers;
import org.forgerock.opendj.ldap.LDAPClientContext;
import org.forgerock.opendj.ldap.LDAPListener;
import org.forgerock.opendj.ldap.LDAPListenerOptions;
import org.forgerock.opendj.ldap.LinkedHashMapEntry;
import org.forgerock.opendj.ldap.Matcher;
import org.forgerock.opendj.ldap.Modification;
import org.forgerock.opendj.ldap.ModificationType;
import org.forgerock.opendj.ldap.RequestContext;
import org.forgerock.opendj.ldap.RequestHandler;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.SSLContextBuilder;
import org.forgerock.opendj.ldap.SearchResultHandler;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.ServerConnection;
import org.forgerock.opendj.ldap.ServerConnectionFactory;
import org.forgerock.opendj.ldap.TrustManagers;
import org.forgerock.opendj.ldap.requests.AddRequest;
import org.forgerock.opendj.ldap.requests.BindRequest;
import org.forgerock.opendj.ldap.requests.CompareRequest;
import org.forgerock.opendj.ldap.requests.DeleteRequest;
import org.forgerock.opendj.ldap.requests.ExtendedRequest;
import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
import org.forgerock.opendj.ldap.requests.ModifyRequest;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.CompareResult;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
import org.forgerock.opendj.ldap.responses.Responses;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldif.LDIFEntryReader;
/**
 * An LDAP directory server which exposes data contained in an LDIF file. This
@@ -64,527 +94,392 @@
 *  &lt;listenAddress> &lt;listenPort> [&lt;ldifFile>]
 * </pre>
 */
public final class Server
{
  private static final class MemoryBackend implements
      RequestHandler<RequestContext>
  {
    private final ConcurrentSkipListMap<DN, Entry> entries;
    private final ReentrantReadWriteLock entryLock = new ReentrantReadWriteLock();
public final class Server {
    private static final class MemoryBackend implements RequestHandler<RequestContext> {
        private final ConcurrentSkipListMap<DN, Entry> entries;
        private final ReentrantReadWriteLock entryLock = new ReentrantReadWriteLock();
    private MemoryBackend(final ConcurrentSkipListMap<DN, Entry> entries)
    {
      this.entries = entries;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleAdd(final RequestContext requestContext,
        final AddRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super Result> resultHandler)
    {
      // TODO: controls.
      entryLock.writeLock().lock();
      try
      {
        DN dn = request.getName();
        if (entries.containsKey(dn))
        {
          resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
              ResultCode.ENTRY_ALREADY_EXISTS, "The entry " + dn.toString()
                  + " already exists"));
        private MemoryBackend(final ConcurrentSkipListMap<DN, Entry> entries) {
            this.entries = entries;
        }
        DN parent = dn.parent();
        if (!entries.containsKey(parent))
        {
          resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
              ResultCode.NO_SUCH_OBJECT,
              "The parent entry " + parent.toString() + " does not exist"));
        }
        else
        {
          entries.put(dn, request);
          resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
        }
      }
      finally
      {
        entryLock.writeLock().unlock();
      }
    }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleAdd(final RequestContext requestContext, final AddRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super Result> resultHandler) {
            // TODO: controls.
            entryLock.writeLock().lock();
            try {
                DN dn = request.getName();
                if (entries.containsKey(dn)) {
                    resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
                            ResultCode.ENTRY_ALREADY_EXISTS, "The entry " + dn.toString()
                                    + " already exists"));
                }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleBind(final RequestContext requestContext,
        final int version, final BindRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super BindResult> resultHandler)
    {
      if (request.getAuthenticationType() != ((byte) 0x80))
      {
        // TODO: SASL authentication not implemented.
        resultHandler.handleErrorResult(newErrorResult(
            ResultCode.PROTOCOL_ERROR,
            "non-SIMPLE authentication not supported: "
                + request.getAuthenticationType()));
      }
      else
      {
        // TODO: always succeed.
        resultHandler.handleResult(Responses.newBindResult(ResultCode.SUCCESS));
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleCompare(final RequestContext requestContext,
        final CompareRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super CompareResult> resultHandler)
    {
      // TODO:
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleDelete(final RequestContext requestContext,
        final DeleteRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super Result> resultHandler)
    {
      // TODO: controls.
      entryLock.writeLock().lock();
      try
      {
        // TODO: check for children.
        DN dn = request.getName();
        if (!entries.containsKey(dn))
        {
          resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
              ResultCode.NO_SUCH_OBJECT, "The entry " + dn.toString()
                  + " does not exist"));
        }
        else
        {
          entries.remove(dn);
          resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
        }
      }
      finally
      {
        entryLock.writeLock().unlock();
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <R extends ExtendedResult> void handleExtendedRequest(
        final RequestContext requestContext, final ExtendedRequest<R> request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super R> resultHandler)
    {
      // TODO: not implemented.
      resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
          "Extended request operation not supported"));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleModify(final RequestContext requestContext,
        final ModifyRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super Result> resultHandler)
    {
      // TODO: controls.
      // TODO: read lock is not really enough since concurrent updates may
      // still occur to the same entry.
      entryLock.readLock().lock();
      try
      {
        DN dn = request.getName();
        Entry entry = entries.get(dn);
        if (entry == null)
        {
          resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
              ResultCode.NO_SUCH_OBJECT, "The entry " + dn.toString()
                  + " does not exist"));
        }
        Entry newEntry = new LinkedHashMapEntry(entry);
        for (Modification mod : request.getModifications())
        {
          ModificationType modType = mod.getModificationType();
          if (modType.equals(ModificationType.ADD))
          {
            // TODO: Reject empty attribute and duplicate values.
            newEntry.addAttribute(mod.getAttribute(), null);
          }
          else if (modType.equals(ModificationType.DELETE))
          {
            // TODO: Reject missing values.
            newEntry.removeAttribute(mod.getAttribute(), null);
          }
          else if (modType.equals(ModificationType.REPLACE))
          {
            newEntry.replaceAttribute(mod.getAttribute());
          }
          else
          {
            resultHandler.handleErrorResult(newErrorResult(
                ResultCode.PROTOCOL_ERROR,
                "Modify request contains an unsupported modification type"));
            return;
          }
        }
        entries.put(dn, newEntry);
        resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
      }
      finally
      {
        entryLock.readLock().unlock();
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleModifyDN(final RequestContext requestContext,
        final ModifyDNRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super Result> resultHandler)
    {
      // TODO: not implemented.
      resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
          "ModifyDN request operation not supported"));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleSearch(final RequestContext requestContext,
        final SearchRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final SearchResultHandler resultHandler)
    {
      // TODO: controls, limits, etc.
      entryLock.readLock().lock();
      try
      {
        DN dn = request.getName();
        Entry baseEntry = entries.get(dn);
        if (baseEntry == null)
        {
          resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
              ResultCode.NO_SUCH_OBJECT, "The entry " + dn.toString()
                  + " does not exist"));
          return;
        }
        SearchScope scope = request.getScope();
        Filter filter = request.getFilter();
        Matcher matcher = filter.matcher();
        if (scope.equals(SearchScope.BASE_OBJECT))
        {
          if (matcher.matches(baseEntry).toBoolean())
          {
            sendEntry(request, resultHandler, baseEntry);
          }
        }
        else if (scope.equals(SearchScope.SINGLE_LEVEL))
        {
          NavigableMap<DN, Entry> subtree = entries.tailMap(dn, false);
          for (Entry entry : subtree.values())
          {
            // Check for cancellation.
            requestContext.checkIfCancelled(false);
            DN childDN = entry.getName();
            if (childDN.isChildOf(dn))
            {
              if (!matcher.matches(entry).toBoolean())
              {
                continue;
              }
              if (!sendEntry(request, resultHandler, entry))
              {
                // Caller has asked to stop sending results.
                break;
              }
                DN parent = dn.parent();
                if (!entries.containsKey(parent)) {
                    resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
                            ResultCode.NO_SUCH_OBJECT, "The parent entry " + parent.toString()
                                    + " does not exist"));
                } else {
                    entries.put(dn, request);
                    resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
                }
            } finally {
                entryLock.writeLock().unlock();
            }
            else if (!childDN.isSubordinateOrEqualTo(dn))
            {
              // The remaining entries will be out of scope.
              break;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleBind(final RequestContext requestContext, final int version,
                final BindRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super BindResult> resultHandler) {
            if (request.getAuthenticationType() != ((byte) 0x80)) {
                // TODO: SASL authentication not implemented.
                resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
                        "non-SIMPLE authentication not supported: "
                                + request.getAuthenticationType()));
            } else {
                // TODO: always succeed.
                resultHandler.handleResult(Responses.newBindResult(ResultCode.SUCCESS));
            }
          }
        }
        else if (scope.equals(SearchScope.WHOLE_SUBTREE))
        {
          NavigableMap<DN, Entry> subtree = entries.tailMap(dn);
          for (Entry entry : subtree.values())
          {
            // Check for cancellation.
            requestContext.checkIfCancelled(false);
            DN childDN = entry.getName();
            if (childDN.isSubordinateOrEqualTo(dn))
            {
              if (!matcher.matches(entry).toBoolean())
              {
                continue;
              }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleCompare(final RequestContext requestContext,
                final CompareRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super CompareResult> resultHandler) {
            // TODO:
        }
              if (!sendEntry(request, resultHandler, entry))
              {
                // Caller has asked to stop sending results.
                break;
              }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleDelete(final RequestContext requestContext, final DeleteRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super Result> resultHandler) {
            // TODO: controls.
            entryLock.writeLock().lock();
            try {
                // TODO: check for children.
                DN dn = request.getName();
                if (!entries.containsKey(dn)) {
                    resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
                            ResultCode.NO_SUCH_OBJECT, "The entry " + dn.toString()
                                    + " does not exist"));
                } else {
                    entries.remove(dn);
                    resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
                }
            } finally {
                entryLock.writeLock().unlock();
            }
            else
            {
              // The remaining entries will be out of scope.
              break;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public <R extends ExtendedResult> void handleExtendedRequest(
                final RequestContext requestContext, final ExtendedRequest<R> request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super R> resultHandler) {
            // TODO: not implemented.
            resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
                    "Extended request operation not supported"));
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleModify(final RequestContext requestContext, final ModifyRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super Result> resultHandler) {
            // TODO: controls.
            // TODO: read lock is not really enough since concurrent updates may
            // still occur to the same entry.
            entryLock.readLock().lock();
            try {
                DN dn = request.getName();
                Entry entry = entries.get(dn);
                if (entry == null) {
                    resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
                            ResultCode.NO_SUCH_OBJECT, "The entry " + dn.toString()
                                    + " does not exist"));
                }
                Entry newEntry = new LinkedHashMapEntry(entry);
                for (Modification mod : request.getModifications()) {
                    ModificationType modType = mod.getModificationType();
                    if (modType.equals(ModificationType.ADD)) {
                        // TODO: Reject empty attribute and duplicate values.
                        newEntry.addAttribute(mod.getAttribute(), null);
                    } else if (modType.equals(ModificationType.DELETE)) {
                        // TODO: Reject missing values.
                        newEntry.removeAttribute(mod.getAttribute(), null);
                    } else if (modType.equals(ModificationType.REPLACE)) {
                        newEntry.replaceAttribute(mod.getAttribute());
                    } else {
                        resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
                                "Modify request contains an unsupported modification type"));
                        return;
                    }
                }
                entries.put(dn, newEntry);
                resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
            } finally {
                entryLock.readLock().unlock();
            }
          }
        }
        else
        {
          resultHandler.handleErrorResult(newErrorResult(
              ResultCode.PROTOCOL_ERROR,
              "Search request contains an unsupported search scope"));
          return;
        }
        resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
      }
      catch (CancelledResultException e)
      {
        resultHandler.handleErrorResult(e);
      }
      finally
      {
        entryLock.readLock().unlock();
      }
    }
    private boolean sendEntry(SearchRequest request,
        SearchResultHandler resultHandler, Entry entry)
    {
      // TODO: check filter, strip attributes.
      return resultHandler.handleEntry(Responses.newSearchResultEntry(entry));
    }
  }
  /**
   * Main method.
   *
   * @param args
   *          The command line arguments: listen address, listen port, ldifFile
   */
  public static void main(final String[] args)
  {
    if (args.length != 3 && args.length != 6)
    {
      System.err.println("Usage: listenAddress listenPort ldifFile "
          + "[keyStoreFile keyStorePassword certNickname]");
      System.exit(1);
    }
    // Parse command line arguments.
    final String localAddress = args[0];
    final int localPort = Integer.parseInt(args[1]);
    final String ldifFileName = args[2];
    final String keyStoreFileName = (args.length == 6) ? args[3] : null;
    final String keyStorePassword = (args.length == 6) ? args[4] : null;
    final String certNickname = (args.length == 6) ? args[5] : null;
    // Create the memory backend.
    final ConcurrentSkipListMap<DN, Entry> entries =
        readEntriesFromLDIF(ldifFileName);
    final MemoryBackend backend = new MemoryBackend(entries);
    // Create a server connection adapter.
    final ServerConnectionFactory<LDAPClientContext, Integer> connectionHandler =
        Connections.newServerConnectionFactory(backend);
    // Create listener.
    LDAPListener listener = null;
    try
    {
      final LDAPListenerOptions options = new LDAPListenerOptions()
          .setBacklog(4096);
      if (keyStoreFileName != null)
      {
        // Configure SSL/TLS and enable it when connections are accepted.
        final SSLContext sslContext = new SSLContextBuilder()
            .setKeyManager(
                KeyManagers.useSingleCertificate(certNickname, KeyManagers
                    .useKeyStoreFile(keyStoreFileName,
                        keyStorePassword.toCharArray(), null)))
            .setTrustManager(TrustManagers.trustAll()).getSSLContext();
        ServerConnectionFactory<LDAPClientContext, Integer> sslWrapper =
            new ServerConnectionFactory<LDAPClientContext, Integer>()
        {
          public ServerConnection<Integer> handleAccept(
              LDAPClientContext clientContext) throws ErrorResultException
          {
            clientContext.enableTLS(sslContext, null, null, false, false);
            return connectionHandler.handleAccept(clientContext);
          }
        };
        listener = new LDAPListener(localAddress, localPort, sslWrapper,
            options);
      }
      else
      {
        // No SSL.
        listener = new LDAPListener(localAddress, localPort, connectionHandler,
            options);
      }
      System.out.println("Press any key to stop the server...");
      System.in.read();
    }
    catch (final Exception e)
    {
      System.out
          .println("Error listening on " + localAddress + ":" + localPort);
      e.printStackTrace();
    }
    finally
    {
      if (listener != null)
      {
        listener.close();
      }
    }
  }
  /**
   * Reads the entries from the named LDIF file.
   *
   * @param ldifFileName
   *          The name of the LDIF file.
   * @return The entries.
   */
  private static ConcurrentSkipListMap<DN, Entry> readEntriesFromLDIF(
      final String ldifFileName)
  {
    final ConcurrentSkipListMap<DN, Entry> entries;
    // Read the LDIF.
    InputStream ldif;
    try
    {
      ldif = new FileInputStream(ldifFileName);
    }
    catch (final FileNotFoundException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
      return null; // Satisfy compiler.
    }
    entries = new ConcurrentSkipListMap<DN, Entry>();
    final LDIFEntryReader reader = new LDIFEntryReader(ldif);
    try
    {
      while (reader.hasNext())
      {
        Entry entry = reader.readEntry();
        entries.put(entry.getName(), entry);
      }
    }
    catch (final IOException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
      return null; // Satisfy compiler.
    }
    finally
    {
      try
      {
        reader.close();
      }
      catch (final IOException ignored)
      {
        // Ignore.
      }
    }
    // Quickly sanity check that every entry (except root entries) have a
    // parent.
    boolean isValid = true;
    for (DN dn : entries.keySet())
    {
      if (dn.size() > 1)
      {
        DN parent = dn.parent();
        if (!entries.containsKey(parent))
        {
          System.err.println("The entry \"" + dn.toString()
              + "\" does not have a parent");
          isValid = false;
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleModifyDN(final RequestContext requestContext,
                final ModifyDNRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super Result> resultHandler) {
            // TODO: not implemented.
            resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
                    "ModifyDN request operation not supported"));
        }
      }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleSearch(final RequestContext requestContext, final SearchRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final SearchResultHandler resultHandler) {
            // TODO: controls, limits, etc.
            entryLock.readLock().lock();
            try {
                DN dn = request.getName();
                Entry baseEntry = entries.get(dn);
                if (baseEntry == null) {
                    resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
                            ResultCode.NO_SUCH_OBJECT, "The entry " + dn.toString()
                                    + " does not exist"));
                    return;
                }
                SearchScope scope = request.getScope();
                Filter filter = request.getFilter();
                Matcher matcher = filter.matcher();
                if (scope.equals(SearchScope.BASE_OBJECT)) {
                    if (matcher.matches(baseEntry).toBoolean()) {
                        sendEntry(request, resultHandler, baseEntry);
                    }
                } else if (scope.equals(SearchScope.SINGLE_LEVEL)) {
                    NavigableMap<DN, Entry> subtree = entries.tailMap(dn, false);
                    for (Entry entry : subtree.values()) {
                        // Check for cancellation.
                        requestContext.checkIfCancelled(false);
                        DN childDN = entry.getName();
                        if (childDN.isChildOf(dn)) {
                            if (!matcher.matches(entry).toBoolean()) {
                                continue;
                            }
                            if (!sendEntry(request, resultHandler, entry)) {
                                // Caller has asked to stop sending results.
                                break;
                            }
                        } else if (!childDN.isSubordinateOrEqualTo(dn)) {
                            // The remaining entries will be out of scope.
                            break;
                        }
                    }
                } else if (scope.equals(SearchScope.WHOLE_SUBTREE)) {
                    NavigableMap<DN, Entry> subtree = entries.tailMap(dn);
                    for (Entry entry : subtree.values()) {
                        // Check for cancellation.
                        requestContext.checkIfCancelled(false);
                        DN childDN = entry.getName();
                        if (childDN.isSubordinateOrEqualTo(dn)) {
                            if (!matcher.matches(entry).toBoolean()) {
                                continue;
                            }
                            if (!sendEntry(request, resultHandler, entry)) {
                                // Caller has asked to stop sending results.
                                break;
                            }
                        } else {
                            // The remaining entries will be out of scope.
                            break;
                        }
                    }
                } else {
                    resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
                            "Search request contains an unsupported search scope"));
                    return;
                }
                resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
            } catch (CancelledResultException e) {
                resultHandler.handleErrorResult(e);
            } finally {
                entryLock.readLock().unlock();
            }
        }
        private boolean sendEntry(SearchRequest request, SearchResultHandler resultHandler,
                Entry entry) {
            // TODO: check filter, strip attributes.
            return resultHandler.handleEntry(Responses.newSearchResultEntry(entry));
        }
    }
    if (!isValid)
    {
      System.exit(1);
    /**
     * Main method.
     *
     * @param args
     *            The command line arguments: listen address, listen port,
     *            ldifFile
     */
    public static void main(final String[] args) {
        if (args.length != 3 && args.length != 6) {
            System.err.println("Usage: listenAddress listenPort ldifFile "
                    + "[keyStoreFile keyStorePassword certNickname]");
            System.exit(1);
        }
        // Parse command line arguments.
        final String localAddress = args[0];
        final int localPort = Integer.parseInt(args[1]);
        final String ldifFileName = args[2];
        final String keyStoreFileName = (args.length == 6) ? args[3] : null;
        final String keyStorePassword = (args.length == 6) ? args[4] : null;
        final String certNickname = (args.length == 6) ? args[5] : null;
        // Create the memory backend.
        final ConcurrentSkipListMap<DN, Entry> entries = readEntriesFromLDIF(ldifFileName);
        final MemoryBackend backend = new MemoryBackend(entries);
        // Create a server connection adapter.
        final ServerConnectionFactory<LDAPClientContext, Integer> connectionHandler =
                Connections.newServerConnectionFactory(backend);
        // Create listener.
        LDAPListener listener = null;
        try {
            final LDAPListenerOptions options = new LDAPListenerOptions().setBacklog(4096);
            if (keyStoreFileName != null) {
                // Configure SSL/TLS and enable it when connections are
                // accepted.
                final SSLContext sslContext =
                        new SSLContextBuilder().setKeyManager(
                                KeyManagers.useSingleCertificate(certNickname, KeyManagers
                                        .useKeyStoreFile(keyStoreFileName, keyStorePassword
                                                .toCharArray(), null))).setTrustManager(
                                TrustManagers.trustAll()).getSSLContext();
                ServerConnectionFactory<LDAPClientContext, Integer> sslWrapper =
                        new ServerConnectionFactory<LDAPClientContext, Integer>() {
                            public ServerConnection<Integer> handleAccept(
                                    LDAPClientContext clientContext) throws ErrorResultException {
                                clientContext.enableTLS(sslContext, null, null, false, false);
                                return connectionHandler.handleAccept(clientContext);
                            }
                        };
                listener = new LDAPListener(localAddress, localPort, sslWrapper, options);
            } else {
                // No SSL.
                listener = new LDAPListener(localAddress, localPort, connectionHandler, options);
            }
            System.out.println("Press any key to stop the server...");
            System.in.read();
        } catch (final Exception e) {
            System.out.println("Error listening on " + localAddress + ":" + localPort);
            e.printStackTrace();
        } finally {
            if (listener != null) {
                listener.close();
            }
        }
    }
    return entries;
  }
    /**
     * Reads the entries from the named LDIF file.
     *
     * @param ldifFileName
     *            The name of the LDIF file.
     * @return The entries.
     */
    private static ConcurrentSkipListMap<DN, Entry> readEntriesFromLDIF(final String ldifFileName) {
        final ConcurrentSkipListMap<DN, Entry> entries;
        // Read the LDIF.
        InputStream ldif;
        try {
            ldif = new FileInputStream(ldifFileName);
        } catch (final FileNotFoundException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
            return null; // Satisfy compiler.
        }
        entries = new ConcurrentSkipListMap<DN, Entry>();
        final LDIFEntryReader reader = new LDIFEntryReader(ldif);
        try {
            while (reader.hasNext()) {
                Entry entry = reader.readEntry();
                entries.put(entry.getName(), entry);
            }
        } catch (final IOException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
            return null; // Satisfy compiler.
        } finally {
            try {
                reader.close();
            } catch (final IOException ignored) {
                // Ignore.
            }
        }
  private Server()
  {
    // Not used.
  }
        // Quickly sanity check that every entry (except root entries) have a
        // parent.
        boolean isValid = true;
        for (DN dn : entries.keySet()) {
            if (dn.size() > 1) {
                DN parent = dn.parent();
                if (!entries.containsKey(parent)) {
                    System.err
                            .println("The entry \"" + dn.toString() + "\" does not have a parent");
                    isValid = false;
                }
            }
        }
        if (!isValid) {
            System.exit(1);
        }
        return entries;
    }
    private Server() {
        // Not used.
    }
}
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SimpleAuth.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -27,8 +26,6 @@
package org.forgerock.opendj.examples;
import java.security.GeneralSecurityException;
import javax.net.ssl.SSLContext;
@@ -41,8 +38,6 @@
import org.forgerock.opendj.ldap.SSLContextBuilder;
import org.forgerock.opendj.ldap.TrustManagers;
/**
 * An example client application which performs simple authentication to a
 * directory server. This example takes the following command line parameters:
@@ -54,211 +49,168 @@
 * <li>use-starttls - (Optional) connect with StartTLS</li>
 * <li>use-ssl - (Optional) connect over SSL</li>
 * </ul>
 * The host, port, bind-dn, and bind-password are required. The use-starttls
 * and use-ssl parameters are optional and mutually exclusive.
 * The host, port, bind-dn, and bind-password are required. The use-starttls and
 * use-ssl parameters are optional and mutually exclusive.
 */
public final class SimpleAuth
{
public final class SimpleAuth {
  /**
   * Authenticate to the directory either over LDAP, over LDAPS, or using
   * StartTLS.
   *
   * @param args The command line arguments
   */
  public static void main(final String[] args)
  {
    parseArgs(args);
    // Connect and bind to the server, then close the connection.
    if (useStartTLS) connectStartTLS();
    else if (useSSL) connectSSL();
    else connect();
  }
  /**
   * Authenticate over LDAP.
   */
  private static void connect()
  {
    final LDAPConnectionFactory factory = new LDAPConnectionFactory(
      host, port);
    Connection connection = null;
    try
    {
      connection = factory.getConnection();
      connection.bind(bindDN, bindPassword.toCharArray());
      System.out.println("Authenticated as " + bindDN + ".");
    /**
     * Authenticate to the directory either over LDAP, over LDAPS, or using
     * StartTLS.
     *
     * @param args
     *            The command line arguments
     */
    public static void main(final String[] args) {
        parseArgs(args);
        // Connect and bind to the server, then close the connection.
        if (useStartTLS) {
            connectStartTLS();
        } else if (useSSL) {
            connectSSL();
        } else {
            connect();
        }
    }
    catch (final ErrorResultException e)
    {
      System.err.println(e.getMessage());
      System.exit(e.getResult().getResultCode().intValue());
      return;
    /**
     * Authenticate over LDAP.
     */
    private static void connect() {
        final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port);
        Connection connection = null;
        try {
            connection = factory.getConnection();
            connection.bind(bindDN, bindPassword.toCharArray());
            System.out.println("Authenticated as " + bindDN + ".");
        } catch (final ErrorResultException e) {
            System.err.println(e.getMessage());
            System.exit(e.getResult().getResultCode().intValue());
            return;
        } catch (final InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
            return;
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
    }
    catch (final InterruptedException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
      return;
    /**
     * For StartTLS and SSL the connection factory needs SSL context options. In
     * the general case, a trust manager in the SSL context serves to check
     * server certificates, and a key manager handles client keys when the
     * server checks certificates from our client.
     *
     * OpenDJ directory server lets you install by default with a self-signed
     * certificate that is not in the system trust store. To simplify this
     * implementation trusts all server certificates.
     */
    private static LDAPOptions getTrustAllOptions() throws GeneralSecurityException {
        LDAPOptions lo = new LDAPOptions();
        SSLContext sslContext =
                new SSLContextBuilder().setTrustManager(TrustManagers.trustAll()).getSSLContext();
        lo.setSSLContext(sslContext);
        lo.setUseStartTLS(useStartTLS);
        return lo;
    }
    finally
    {
      if (connection != null) connection.close();
    /**
     * Perform authentication over a secure connection, trusting all server
     * certificates.
     */
    private static void trustAllConnect() {
        Connection connection = null;
        try {
            final LDAPConnectionFactory factory =
                    new LDAPConnectionFactory(host, port, getTrustAllOptions());
            connection = factory.getConnection();
            connection.bind(bindDN, bindPassword.toCharArray());
            System.out.println("Authenticated as " + bindDN + ".");
        } catch (final ErrorResultException e) {
            System.err.println(e.getMessage());
            System.exit(e.getResult().getResultCode().intValue());
            return;
        } catch (final InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
            return;
        } catch (final GeneralSecurityException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_CONNECT_ERROR.intValue());
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
    }
  }
  /**
   * For StartTLS and SSL the connection factory needs SSL context options.
   * In the general case, a trust manager in the SSL context serves to check
   * server certificates, and a key manager handles client keys when the server
   * checks certificates from our client.
   *
   * OpenDJ directory server lets you install by default with a self-signed
   * certificate that is not in the system trust store. To simplify this
   * implementation trusts all server certificates.
   */
  private static LDAPOptions getTrustAllOptions()
    throws GeneralSecurityException
  {
    LDAPOptions lo = new LDAPOptions();
    SSLContext sslContext = new SSLContextBuilder()
      .setTrustManager(TrustManagers.trustAll()).getSSLContext();
    lo.setSSLContext(sslContext);
    lo.setUseStartTLS(useStartTLS);
    return lo;
  }
  /**
   * Perform authentication over a secure connection, trusting all server
   * certificates.
   */
  private static void trustAllConnect()
  {
    Connection connection = null;
    try
    {
      final LDAPConnectionFactory factory =
          new LDAPConnectionFactory(host, port, getTrustAllOptions());
      connection = factory.getConnection();
      connection.bind(bindDN, bindPassword.toCharArray());
      System.out.println("Authenticated as " + bindDN + ".");
    /**
     * Authenticate using StartTLS.
     */
    private static void connectStartTLS() {
        trustAllConnect();
    }
    catch (final ErrorResultException e)
    {
      System.err.println(e.getMessage());
      System.exit(e.getResult().getResultCode().intValue());
      return;
    /**
     * Authenticate over LDAPS.
     */
    private static void connectSSL() {
        trustAllConnect();
    }
    catch (final InterruptedException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
      return;
    private static String host;
    private static int port;
    private static String bindDN;
    private static String bindPassword;
    private static boolean useStartTLS = false;
    private static boolean useSSL = false;
    /**
     * Parse command line arguments.
     *
     * @param args
     *            host port bind-dn bind-password [ use-starttls | use-ssl ]
     */
    private static void parseArgs(String[] args) {
        if (args.length < 4 || args.length > 5) {
            giveUp();
        }
        host = args[0];
        port = Integer.parseInt(args[1]);
        bindDN = args[2];
        bindPassword = args[3];
        if (args.length == 5) {
            if (args[4].toLowerCase().equals("use-starttls")) {
                useStartTLS = true;
                useSSL = false;
            } else if (args[4].toLowerCase().equals("use-ssl")) {
                useStartTLS = false;
                useSSL = true;
            } else {
                giveUp();
            }
        }
    }
    catch (final GeneralSecurityException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_CONNECT_ERROR.intValue());
    private static void giveUp() {
        printUsage();
        System.exit(1);
    }
    finally
    {
      if (connection != null)
        connection.close();
    private static void printUsage() {
        System.err.println("Usage: host port bind-dn bind-password [ use-starttls | use-ssl ]");
        System.err.println("\thost, port, bind-dn, and bind-password arguments are required.");
        System.err.println("\tuse-starttls and use-ssl are optional and mutually exclusive.");
    }
  }
  /**
   * Authenticate using StartTLS.
   */
  private static void connectStartTLS()
  {
    trustAllConnect();
  }
  /**
   * Authenticate over LDAPS.
   */
  private static void connectSSL()
  {
    trustAllConnect();
  }
  private static String host;
  private static int port;
  private static String bindDN;
  private static String bindPassword;
  private static boolean useStartTLS = false;
  private static boolean useSSL = false;
  /**
   * Parse command line arguments.
   * @param args host port bind-dn bind-password [ use-starttls | use-ssl ]
   */
  private static void parseArgs(String[] args)
  {
    if (args.length < 4 || args.length > 5) giveUp();
    host = args[0];
    port = Integer.parseInt(args[1]);
    bindDN = args[2];
    bindPassword = args[3];
    if (args.length == 5)
    {
      if (args[4].toLowerCase().equals("use-starttls"))
      {
        useStartTLS = true;
        useSSL = false;
      }
      else if (args[4].toLowerCase().equals("use-ssl"))
      {
        useStartTLS = false;
        useSSL = true;
      }
      else giveUp();
    private SimpleAuth() {
        // Not used.
    }
  }
  private static void giveUp()
  {
    printUsage();
    System.exit(1);
  }
  private static void printUsage()
  {
    System.err.println(
      "Usage: host port bind-dn bind-password [ use-starttls | use-ssl ]");
    System.err.println(
      "\thost, port, bind-dn, and bind-password arguments are required.");
    System.err.println(
      "\tuse-starttls and use-ssl are optional and mutually exclusive.");
  }
  private SimpleAuth()
  {
    // Not used.
  }
}
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/package-info.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -30,3 +29,4 @@
 * OpenDJ LDAP SDK.
 */
package org.forgerock.opendj.examples;
opendj3/opendj-ldap-sdk/pom.xml
@@ -7,17 +7,16 @@
  ! (the "License").  You may not use this file except in compliance
  ! with the License.
  !
  ! You can obtain a copy of the license at
  ! trunk/opendj3/legal-notices/CDDLv1_0.txt
  ! You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
  ! or http://forgerock.org/license/CDDLv1.0.html.
  ! See the License for the specific language governing permissions
  ! and limitations under the License.
  !
  ! When distributing Covered Code, include this CDDL HEADER in each
  ! file and include the License file at
  ! trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
  ! add the following below this CDDL HEADER, with the fields enclosed
  ! by brackets "[]" replaced with your own identifying information:
  ! file and include the License file at legal-notices/CDDLv1_0.txt.
  ! If applicable, add the following below this CDDL HEADER, with the
  ! fields enclosed by brackets "[]" replaced with your own identifying
  ! information:
  !      Portions Copyright [yyyy] [name of copyright owner]
  !
  ! CDDL HEADER END
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/ASN1BufferReader.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,9 +27,10 @@
package com.forgerock.opendj.ldap;
import static com.forgerock.opendj.ldap.LDAPConstants.*;
import static com.forgerock.opendj.ldap.LDAPConstants.ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES;
import static com.forgerock.opendj.ldap.LDAPConstants.ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE;
import static com.forgerock.opendj.ldap.LDAPConstants.ELEMENT_READ_STATE_NEED_TYPE;
import static com.forgerock.opendj.ldap.LDAPConstants.ELEMENT_READ_STATE_NEED_VALUE_BYTES;
import static org.forgerock.opendj.ldap.CoreMessages.*;
import java.io.IOException;
@@ -49,770 +49,598 @@
import com.forgerock.opendj.util.StaticUtils;
/**
 * Grizzly ASN1 reader implementation.
 */
final class ASN1BufferReader extends AbstractASN1Reader implements ASN1Reader
{
  private final class ChildSequenceLimiter implements SequenceLimiter
  {
    private SequenceLimiter parent;
final class ASN1BufferReader extends AbstractASN1Reader implements ASN1Reader {
    private final class ChildSequenceLimiter implements SequenceLimiter {
        private SequenceLimiter parent;
    private ChildSequenceLimiter child;
        private ChildSequenceLimiter child;
    private int readLimit;
        private int readLimit;
    private int bytesRead;
        private int bytesRead;
        public void checkLimit(final int readSize) throws IOException {
            if ((readLimit > 0) && (bytesRead + readSize > readLimit)) {
                final LocalizableMessage message = ERR_ASN1_TRUNCATED_LENGTH_BYTE.get();
                throw DecodeException.fatalError(message);
            }
            parent.checkLimit(readSize);
    public void checkLimit(final int readSize) throws IOException
    {
      if ((readLimit > 0) && (bytesRead + readSize > readLimit))
      {
        final LocalizableMessage message = ERR_ASN1_TRUNCATED_LENGTH_BYTE.get();
        throw DecodeException.fatalError(message);
      }
      parent.checkLimit(readSize);
      bytesRead += readSize;
    }
    public SequenceLimiter endSequence() throws IOException
    {
      parent.checkLimit(remaining());
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINE) && remaining() > 0)
      {
        StaticUtils.DEBUG_LOG
            .fine(String.format(
                "Ignoring %d unused trailing bytes in ASN.1 SEQUENCE",
                remaining()));
      }
      for (int i = 0; i < remaining(); i++)
      {
        buffer.get();
      }
      return parent;
    }
    public int remaining()
    {
      return readLimit - bytesRead;
    }
    public ChildSequenceLimiter startSequence(final int readLimit)
    {
      if (child == null)
      {
        child = new ChildSequenceLimiter();
        child.parent = this;
      }
      child.readLimit = readLimit;
      child.bytesRead = 0;
      return child;
    }
  }
  private final class RootSequenceLimiter implements SequenceLimiter
  {
    private ChildSequenceLimiter child;
    public void checkLimit(final int readSize) throws IOException
    {
      if (buffer.remaining() < readSize)
      {
        final LocalizableMessage message = ERR_ASN1_TRUNCATED_LENGTH_BYTE.get();
        throw DecodeException.fatalError(message);
      }
    }
    public ChildSequenceLimiter endSequence() throws DecodeException
    {
      final LocalizableMessage message = ERR_ASN1_SEQUENCE_READ_NOT_STARTED
          .get();
      throw new IllegalStateException(message.toString());
    }
    public int remaining()
    {
      return buffer.remaining();
    }
    public ChildSequenceLimiter startSequence(final int readLimit)
    {
      if (child == null)
      {
        child = new ChildSequenceLimiter();
        child.parent = this;
      }
      child.readLimit = readLimit;
      child.bytesRead = 0;
      return child;
    }
  }
  private interface SequenceLimiter
  {
    public void checkLimit(int readSize) throws IOException;
    public SequenceLimiter endSequence() throws IOException;
    public int remaining();
    public SequenceLimiter startSequence(int readLimit);
  }
  private static final int MAX_STRING_BUFFER_SIZE = 1024;
  private int state = ELEMENT_READ_STATE_NEED_TYPE;
  private byte peekType = 0;
  private int peekLength = -1;
  private int lengthBytesNeeded = 0;
  private final int maxElementSize;
  private final CompositeBuffer buffer;
  private SequenceLimiter readLimiter;
  private final byte[] stringBuffer;
  /**
   * Creates a new ASN1 reader whose source is the provided input stream and
   * having a user defined maximum BER element size.
   *
   * @param maxElementSize
   *          The maximum BER element size, or <code>0</code> to indicate that
   *          there is no limit.
   * @param memoryManager
   *          The memory manager to use for buffering.
   */
  ASN1BufferReader(final int maxElementSize,
      final MemoryManager<?> memoryManager)
  {
    this.readLimiter = new RootSequenceLimiter();
    this.stringBuffer = new byte[MAX_STRING_BUFFER_SIZE];
    this.maxElementSize = maxElementSize;
    this.buffer = BuffersBuffer.create(memoryManager);
  }
  /**
   * Closes this ASN.1 reader and the underlying stream.
   *
   * @throws IOException
   *           if an I/O error occurs
   */
  public void close() throws IOException
  {
    buffer.dispose();
  }
  /**
   * Determines if a complete ASN.1 element is ready to be read from the stream
   * reader.
   *
   * @return <code>true</code> if another complete element is available or
   *         <code>false</code> otherwise.
   * @throws IOException
   *           If an error occurs while trying to decode an ASN1 element.
   */
  public boolean elementAvailable() throws IOException
  {
    return !((state == ELEMENT_READ_STATE_NEED_TYPE) && !needTypeState(true))
        && !((state == ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE)
            && !needFirstLengthByteState(true))
        && !((state == ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES)
            && !needAdditionalLengthBytesState(true))
        && peekLength <= readLimiter.remaining();
  }
  /**
   * Determines if the input stream contains at least one ASN.1 element to be
   * read.
   *
   * @return <code>true</code> if another element is available or
   *         <code>false</code> otherwise.
   * @throws IOException
   *           If an error occurs while trying to decode an ASN1 element.
   */
  public boolean hasNextElement() throws IOException
  {
    return (state != ELEMENT_READ_STATE_NEED_TYPE) || needTypeState(true);
  }
  /**
   * {@inheritDoc}
   */
  public int peekLength() throws IOException
  {
    peekType();
    switch (state)
    {
    case ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE:
      needFirstLengthByteState(false);
      break;
    case ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES:
      needAdditionalLengthBytesState(false);
    }
    return peekLength;
  }
  /**
   * {@inheritDoc}
   */
  public byte peekType() throws IOException
  {
    if (state == ELEMENT_READ_STATE_NEED_TYPE)
    {
      needTypeState(false);
    }
    return peekType;
  }
  /**
   * {@inheritDoc}
   */
  public boolean readBoolean() throws IOException
  {
    // Read the header if haven't done so already
    peekLength();
    if (peekLength != 1)
    {
      final LocalizableMessage message = ERR_ASN1_BOOLEAN_INVALID_LENGTH
          .get(peekLength);
      throw DecodeException.fatalError(message);
    }
    readLimiter.checkLimit(peekLength);
    final byte readByte = buffer.get();
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String.format(
          "READ ASN.1 BOOLEAN(type=0x%x, length=%d, value=%s)", peekType,
          peekLength, String.valueOf(readByte != 0x00)));
    }
    state = ELEMENT_READ_STATE_NEED_TYPE;
    return readByte != 0x00;
  }
  /**
   * {@inheritDoc}
   */
  public void readEndSequence() throws IOException
  {
    readLimiter = readLimiter.endSequence();
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String.format("READ ASN.1 END SEQUENCE"));
    }
    // Reset the state
    state = ELEMENT_READ_STATE_NEED_TYPE;
  }
  /**
   * {@inheritDoc}
   */
  public void readEndSet() throws IOException
  {
    // From an implementation point of view, a set is equivalent to a
    // sequence.
    readEndSequence();
  }
  /**
   * {@inheritDoc}
   */
  public int readEnumerated() throws IOException
  {
    // Read the header if haven't done so already
    peekLength();
    if ((peekLength < 1) || (peekLength > 4))
    {
      final LocalizableMessage message = ERR_ASN1_INTEGER_INVALID_LENGTH
          .get(peekLength);
      throw DecodeException.fatalError(message);
    }
    // From an implementation point of view, an enumerated value is
    // equivalent to an integer.
    return (int) readInteger();
  }
  /**
   * {@inheritDoc}
   */
  public long readInteger() throws IOException
  {
    // Read the header if haven't done so already
    peekLength();
    if ((peekLength < 1) || (peekLength > 8))
    {
      final LocalizableMessage message = ERR_ASN1_INTEGER_INVALID_LENGTH
          .get(peekLength);
      throw DecodeException.fatalError(message);
    }
    readLimiter.checkLimit(peekLength);
    if (peekLength > 4)
    {
      long longValue = 0;
      for (int i = 0; i < peekLength; i++)
      {
        final int readByte = buffer.get();
        if ((i == 0) && (((byte) readByte) < 0))
        {
          longValue = 0xFFFFFFFFFFFFFFFFL;
            bytesRead += readSize;
        }
        longValue = (longValue << 8) | (readByte & 0xFF);
      }
      state = ELEMENT_READ_STATE_NEED_TYPE;
      return longValue;
    }
    else
    {
      int intValue = 0;
      for (int i = 0; i < peekLength; i++)
      {
        final int readByte = buffer.get();
        if ((i == 0) && (((byte) readByte) < 0))
        {
          intValue = 0xFFFFFFFF;
        public SequenceLimiter endSequence() throws IOException {
            parent.checkLimit(remaining());
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINE) && remaining() > 0) {
                StaticUtils.DEBUG_LOG.fine(String.format(
                        "Ignoring %d unused trailing bytes in ASN.1 SEQUENCE", remaining()));
            }
            for (int i = 0; i < remaining(); i++) {
                buffer.get();
            }
            return parent;
        }
        intValue = (intValue << 8) | (readByte & 0xFF);
      }
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "READ ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", peekType,
            peekLength, intValue));
      }
        public int remaining() {
            return readLimit - bytesRead;
        }
      state = ELEMENT_READ_STATE_NEED_TYPE;
      return intValue;
    }
  }
        public ChildSequenceLimiter startSequence(final int readLimit) {
            if (child == null) {
                child = new ChildSequenceLimiter();
                child.parent = this;
            }
            child.readLimit = readLimit;
            child.bytesRead = 0;
  /**
   * {@inheritDoc}
   */
  public void readNull() throws IOException
  {
    // Read the header if haven't done so already
    peekLength();
    // Make sure that the decoded length is exactly zero byte.
    if (peekLength != 0)
    {
      final LocalizableMessage message = ERR_ASN1_NULL_INVALID_LENGTH
          .get(peekLength);
      throw DecodeException.fatalError(message);
            return child;
        }
    }
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String.format(
          "READ ASN.1 NULL(type=0x%x, length=%d)", peekType, peekLength));
    private final class RootSequenceLimiter implements SequenceLimiter {
        private ChildSequenceLimiter child;
        public void checkLimit(final int readSize) throws IOException {
            if (buffer.remaining() < readSize) {
                final LocalizableMessage message = ERR_ASN1_TRUNCATED_LENGTH_BYTE.get();
                throw DecodeException.fatalError(message);
            }
        }
        public ChildSequenceLimiter endSequence() throws DecodeException {
            final LocalizableMessage message = ERR_ASN1_SEQUENCE_READ_NOT_STARTED.get();
            throw new IllegalStateException(message.toString());
        }
        public int remaining() {
            return buffer.remaining();
        }
        public ChildSequenceLimiter startSequence(final int readLimit) {
            if (child == null) {
                child = new ChildSequenceLimiter();
                child.parent = this;
            }
            child.readLimit = readLimit;
            child.bytesRead = 0;
            return child;
        }
    }
    state = ELEMENT_READ_STATE_NEED_TYPE;
  }
    private interface SequenceLimiter {
        public void checkLimit(int readSize) throws IOException;
        public SequenceLimiter endSequence() throws IOException;
        public int remaining();
  /**
   * {@inheritDoc}
   */
  public ByteString readOctetString() throws IOException
  {
    // Read the header if haven't done so already
    peekLength();
    if (peekLength == 0)
    {
      state = ELEMENT_READ_STATE_NEED_TYPE;
      return ByteString.empty();
        public SequenceLimiter startSequence(int readLimit);
    }
    readLimiter.checkLimit(peekLength);
    // Copy the value and construct the element to return.
    final byte[] value = new byte[peekLength];
    buffer.get(value);
    private static final int MAX_STRING_BUFFER_SIZE = 1024;
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG
          .finest(String.format("READ ASN.1 OCTETSTRING(type=0x%x, length=%d)",
              peekType, peekLength));
    private int state = ELEMENT_READ_STATE_NEED_TYPE;
    private byte peekType = 0;
    private int peekLength = -1;
    private int lengthBytesNeeded = 0;
    private final int maxElementSize;
    private final CompositeBuffer buffer;
    private SequenceLimiter readLimiter;
    private final byte[] stringBuffer;
    /**
     * Creates a new ASN1 reader whose source is the provided input stream and
     * having a user defined maximum BER element size.
     *
     * @param maxElementSize
     *            The maximum BER element size, or <code>0</code> to indicate
     *            that there is no limit.
     * @param memoryManager
     *            The memory manager to use for buffering.
     */
    ASN1BufferReader(final int maxElementSize, final MemoryManager<?> memoryManager) {
        this.readLimiter = new RootSequenceLimiter();
        this.stringBuffer = new byte[MAX_STRING_BUFFER_SIZE];
        this.maxElementSize = maxElementSize;
        this.buffer = BuffersBuffer.create(memoryManager);
    }
    state = ELEMENT_READ_STATE_NEED_TYPE;
    return ByteString.wrap(value);
  }
  /**
   * {@inheritDoc}
   */
  public ByteStringBuilder readOctetString(final ByteStringBuilder builder)
      throws IOException
  {
    // Read the header if haven't done so already
    peekLength();
    if (peekLength == 0)
    {
      state = ELEMENT_READ_STATE_NEED_TYPE;
      return builder;
    /**
     * Closes this ASN.1 reader and the underlying stream.
     *
     * @throws IOException
     *             if an I/O error occurs
     */
    public void close() throws IOException {
        buffer.dispose();
    }
    readLimiter.checkLimit(peekLength);
    // Copy the value and construct the element to return.
    // TODO: Is there a more efficient way to do this?
    for (int i = 0; i < peekLength; i++)
    {
      builder.append(buffer.get());
    /**
     * Determines if a complete ASN.1 element is ready to be read from the
     * stream reader.
     *
     * @return <code>true</code> if another complete element is available or
     *         <code>false</code> otherwise.
     * @throws IOException
     *             If an error occurs while trying to decode an ASN1 element.
     */
    public boolean elementAvailable() throws IOException {
        return !((state == ELEMENT_READ_STATE_NEED_TYPE) && !needTypeState(true))
                && !((state == ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE)
                        && !needFirstLengthByteState(true))
                && !((state == ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES)
                        && !needAdditionalLengthBytesState(true))
                && peekLength <= readLimiter.remaining();
    }
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG
          .finest(String.format("READ ASN.1 OCTETSTRING(type=0x%x, length=%d)",
              peekType, peekLength));
    /**
     * Determines if the input stream contains at least one ASN.1 element to be
     * read.
     *
     * @return <code>true</code> if another element is available or
     *         <code>false</code> otherwise.
     * @throws IOException
     *             If an error occurs while trying to decode an ASN1 element.
     */
    public boolean hasNextElement() throws IOException {
        return (state != ELEMENT_READ_STATE_NEED_TYPE) || needTypeState(true);
    }
    state = ELEMENT_READ_STATE_NEED_TYPE;
    return builder;
  }
    /**
     * {@inheritDoc}
     */
    public int peekLength() throws IOException {
        peekType();
        switch (state) {
        case ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE:
            needFirstLengthByteState(false);
            break;
        case ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES:
            needAdditionalLengthBytesState(false);
        }
  /**
   * {@inheritDoc}
   */
  public String readOctetStringAsString() throws IOException
  {
    // Read the header if haven't done so already
    peekLength();
    if (peekLength == 0)
    {
      state = ELEMENT_READ_STATE_NEED_TYPE;
      return "";
        return peekLength;
    }
    byte[] readBuffer;
    if (peekLength <= stringBuffer.length)
    {
      readBuffer = stringBuffer;
    }
    else
    {
      readBuffer = new byte[peekLength];
    /**
     * {@inheritDoc}
     */
    public byte peekType() throws IOException {
        if (state == ELEMENT_READ_STATE_NEED_TYPE) {
            needTypeState(false);
        }
        return peekType;
    }
    readLimiter.checkLimit(peekLength);
    buffer.get(readBuffer, 0, peekLength);
    /**
     * {@inheritDoc}
     */
    public boolean readBoolean() throws IOException {
        // Read the header if haven't done so already
        peekLength();
    state = ELEMENT_READ_STATE_NEED_TYPE;
        if (peekLength != 1) {
            final LocalizableMessage message = ERR_ASN1_BOOLEAN_INVALID_LENGTH.get(peekLength);
            throw DecodeException.fatalError(message);
        }
    String str;
    try
    {
      str = new String(readBuffer, 0, peekLength, "UTF-8");
    }
    catch (final Exception e)
    {
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING))
      {
        StaticUtils.DEBUG_LOG
            .warning("Unable to decode ASN.1 OCTETSTRING bytes as UTF-8 string: "
                + e.toString());
      }
        readLimiter.checkLimit(peekLength);
        final byte readByte = buffer.get();
      str = new String(stringBuffer, 0, peekLength);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
            StaticUtils.DEBUG_LOG.finest(String.format(
                    "READ ASN.1 BOOLEAN(type=0x%x, length=%d, value=%s)", peekType, peekLength,
                    String.valueOf(readByte != 0x00)));
        }
        state = ELEMENT_READ_STATE_NEED_TYPE;
        return readByte != 0x00;
    }
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String.format(
          "READ ASN.1 OCTETSTRING(type=0x%x, length=%d, value=%s)", peekType,
          peekLength, str));
    /**
     * {@inheritDoc}
     */
    public void readEndSequence() throws IOException {
        readLimiter = readLimiter.endSequence();
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
            StaticUtils.DEBUG_LOG.finest(String.format("READ ASN.1 END SEQUENCE"));
        }
        // Reset the state
        state = ELEMENT_READ_STATE_NEED_TYPE;
    }
    return str;
  }
  /**
   * {@inheritDoc}
   */
  public void readStartSequence() throws IOException
  {
    // Read the header if haven't done so already
    peekLength();
    readLimiter = readLimiter.startSequence(peekLength);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String.format(
          "READ ASN.1 START SEQUENCE(type=0x%x, length=%d)", peekType,
          peekLength));
    /**
     * {@inheritDoc}
     */
    public void readEndSet() throws IOException {
        // From an implementation point of view, a set is equivalent to a
        // sequence.
        readEndSequence();
    }
    // Reset the state
    state = ELEMENT_READ_STATE_NEED_TYPE;
  }
    /**
     * {@inheritDoc}
     */
    public int readEnumerated() throws IOException {
        // Read the header if haven't done so already
        peekLength();
        if ((peekLength < 1) || (peekLength > 4)) {
            final LocalizableMessage message = ERR_ASN1_INTEGER_INVALID_LENGTH.get(peekLength);
            throw DecodeException.fatalError(message);
        }
  /**
   * {@inheritDoc}
   */
  public void readStartSet() throws IOException
  {
    // From an implementation point of view, a set is equivalent to a
    // sequence.
    readStartSequence();
  }
  /**
   * {@inheritDoc}
   */
  public ASN1Reader skipElement() throws IOException
  {
    // Read the header if haven't done so already
    peekLength();
    readLimiter.checkLimit(peekLength);
    for (int i = 0; i < peekLength; i++)
    {
      buffer.get();
    }
    state = ELEMENT_READ_STATE_NEED_TYPE;
    return this;
  }
  void appendBytesRead(final Buffer buffer)
  {
    this.buffer.append(buffer);
  }
  void disposeBytesRead()
  {
    this.buffer.shrink();
  }
  /**
   * Internal helper method reading the additional ASN.1 length bytes and
   * transition to the next state if successful.
   *
   * @param ensureRead
   *          <code>true</code> to check for availability first.
   * @return <code>true</code> if the length bytes was successfully read.
   * @throws IOException
   *           If an error occurs while reading from the stream.
   */
  private boolean needAdditionalLengthBytesState(final boolean ensureRead)
      throws IOException
  {
    if (ensureRead && (readLimiter.remaining() < lengthBytesNeeded))
    {
      return false;
        // From an implementation point of view, an enumerated value is
        // equivalent to an integer.
        return (int) readInteger();
    }
    byte readByte;
    readLimiter.checkLimit(lengthBytesNeeded);
    while (lengthBytesNeeded > 0)
    {
      readByte = buffer.get();
      peekLength = (peekLength << 8) | (readByte & 0xFF);
      lengthBytesNeeded--;
    /**
     * {@inheritDoc}
     */
    public long readInteger() throws IOException {
        // Read the header if haven't done so already
        peekLength();
        if ((peekLength < 1) || (peekLength > 8)) {
            final LocalizableMessage message = ERR_ASN1_INTEGER_INVALID_LENGTH.get(peekLength);
            throw DecodeException.fatalError(message);
        }
        readLimiter.checkLimit(peekLength);
        if (peekLength > 4) {
            long longValue = 0;
            for (int i = 0; i < peekLength; i++) {
                final int readByte = buffer.get();
                if ((i == 0) && (((byte) readByte) < 0)) {
                    longValue = 0xFFFFFFFFFFFFFFFFL;
                }
                longValue = (longValue << 8) | (readByte & 0xFF);
            }
            state = ELEMENT_READ_STATE_NEED_TYPE;
            return longValue;
        } else {
            int intValue = 0;
            for (int i = 0; i < peekLength; i++) {
                final int readByte = buffer.get();
                if ((i == 0) && (((byte) readByte) < 0)) {
                    intValue = 0xFFFFFFFF;
                }
                intValue = (intValue << 8) | (readByte & 0xFF);
            }
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
                StaticUtils.DEBUG_LOG.finest(String.format(
                        "READ ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", peekType, peekLength,
                        intValue));
            }
            state = ELEMENT_READ_STATE_NEED_TYPE;
            return intValue;
        }
    }
    // Make sure that the element is not larger than the maximum allowed
    // message size.
    if ((maxElementSize > 0) && (peekLength > maxElementSize))
    {
      final LocalizableMessage m = ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED
          .get(peekLength, maxElementSize);
      throw DecodeException.fatalError(m);
    }
    state = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
    return true;
  }
    /**
     * {@inheritDoc}
     */
    public void readNull() throws IOException {
        // Read the header if haven't done so already
        peekLength();
        // Make sure that the decoded length is exactly zero byte.
        if (peekLength != 0) {
            final LocalizableMessage message = ERR_ASN1_NULL_INVALID_LENGTH.get(peekLength);
            throw DecodeException.fatalError(message);
        }
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
            StaticUtils.DEBUG_LOG.finest(String.format("READ ASN.1 NULL(type=0x%x, length=%d)",
                    peekType, peekLength));
        }
  /**
   * Internal helper method reading the first length bytes and transition to the
   * next state if successful.
   *
   * @param ensureRead
   *          <code>true</code> to check for availability first.
   * @return <code>true</code> if the length bytes was successfully read
   * @throws IOException
   *           If an error occurs while trying to decode an ASN1 element.
   */
  private boolean needFirstLengthByteState(final boolean ensureRead)
      throws IOException
  {
    if (ensureRead && (readLimiter.remaining() <= 0))
    {
      return false;
        state = ELEMENT_READ_STATE_NEED_TYPE;
    }
    readLimiter.checkLimit(1);
    byte readByte = buffer.get();
    peekLength = (readByte & 0x7F);
    if (peekLength != readByte)
    {
      lengthBytesNeeded = peekLength;
      if (lengthBytesNeeded > 4)
      {
        final LocalizableMessage message = ERR_ASN1_INVALID_NUM_LENGTH_BYTES
            .get(lengthBytesNeeded);
        throw DecodeException.fatalError(message);
      }
      peekLength = 0x00;
    /**
     * {@inheritDoc}
     */
    public ByteString readOctetString() throws IOException {
        // Read the header if haven't done so already
        peekLength();
      if (ensureRead && (readLimiter.remaining() < lengthBytesNeeded))
      {
        state = ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES;
        return false;
      }
        if (peekLength == 0) {
            state = ELEMENT_READ_STATE_NEED_TYPE;
            return ByteString.empty();
        }
      readLimiter.checkLimit(lengthBytesNeeded);
      while (lengthBytesNeeded > 0)
      {
        readByte = buffer.get();
        peekLength = (peekLength << 8) | (readByte & 0xFF);
        lengthBytesNeeded--;
      }
        readLimiter.checkLimit(peekLength);
        // Copy the value and construct the element to return.
        final byte[] value = new byte[peekLength];
        buffer.get(value);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
            StaticUtils.DEBUG_LOG.finest(String.format(
                    "READ ASN.1 OCTETSTRING(type=0x%x, length=%d)", peekType, peekLength));
        }
        state = ELEMENT_READ_STATE_NEED_TYPE;
        return ByteString.wrap(value);
    }
    // Make sure that the element is not larger than the maximum allowed
    // message size.
    if ((maxElementSize > 0) && (peekLength > maxElementSize))
    {
      final LocalizableMessage m = ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED
          .get(peekLength, maxElementSize);
      throw DecodeException.fatalError(m);
    }
    state = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
    return true;
  }
    /**
     * {@inheritDoc}
     */
    public ByteStringBuilder readOctetString(final ByteStringBuilder builder) throws IOException {
        // Read the header if haven't done so already
        peekLength();
        if (peekLength == 0) {
            state = ELEMENT_READ_STATE_NEED_TYPE;
            return builder;
        }
        readLimiter.checkLimit(peekLength);
        // Copy the value and construct the element to return.
        // TODO: Is there a more efficient way to do this?
        for (int i = 0; i < peekLength; i++) {
            builder.append(buffer.get());
        }
  /**
   * Internal helper method reading the ASN.1 type byte and transition to the
   * next state if successful.
   *
   * @param ensureRead
   *          <code>true</code> to check for availability first.
   * @return <code>true</code> if the type byte was successfully read
   * @throws IOException
   *           If an error occurs while trying to decode an ASN1 element.
   */
  private boolean needTypeState(final boolean ensureRead) throws IOException
  {
    // Read just the type.
    if (ensureRead && (readLimiter.remaining() <= 0))
    {
      return false;
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
            StaticUtils.DEBUG_LOG.finest(String.format(
                    "READ ASN.1 OCTETSTRING(type=0x%x, length=%d)", peekType, peekLength));
        }
        state = ELEMENT_READ_STATE_NEED_TYPE;
        return builder;
    }
    readLimiter.checkLimit(1);
    peekType = buffer.get();
    state = ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE;
    return true;
  }
    /**
     * {@inheritDoc}
     */
    public String readOctetStringAsString() throws IOException {
        // Read the header if haven't done so already
        peekLength();
        if (peekLength == 0) {
            state = ELEMENT_READ_STATE_NEED_TYPE;
            return "";
        }
        byte[] readBuffer;
        if (peekLength <= stringBuffer.length) {
            readBuffer = stringBuffer;
        } else {
            readBuffer = new byte[peekLength];
        }
        readLimiter.checkLimit(peekLength);
        buffer.get(readBuffer, 0, peekLength);
        state = ELEMENT_READ_STATE_NEED_TYPE;
        String str;
        try {
            str = new String(readBuffer, 0, peekLength, "UTF-8");
        } catch (final Exception e) {
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING)) {
                StaticUtils.DEBUG_LOG
                        .warning("Unable to decode ASN.1 OCTETSTRING bytes as UTF-8 string: "
                                + e.toString());
            }
            str = new String(stringBuffer, 0, peekLength);
        }
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
            StaticUtils.DEBUG_LOG.finest(String.format(
                    "READ ASN.1 OCTETSTRING(type=0x%x, length=%d, value=%s)", peekType, peekLength,
                    str));
        }
        return str;
    }
    /**
     * {@inheritDoc}
     */
    public void readStartSequence() throws IOException {
        // Read the header if haven't done so already
        peekLength();
        readLimiter = readLimiter.startSequence(peekLength);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
            StaticUtils.DEBUG_LOG.finest(String.format(
                    "READ ASN.1 START SEQUENCE(type=0x%x, length=%d)", peekType, peekLength));
        }
        // Reset the state
        state = ELEMENT_READ_STATE_NEED_TYPE;
    }
    /**
     * {@inheritDoc}
     */
    public void readStartSet() throws IOException {
        // From an implementation point of view, a set is equivalent to a
        // sequence.
        readStartSequence();
    }
    /**
     * {@inheritDoc}
     */
    public ASN1Reader skipElement() throws IOException {
        // Read the header if haven't done so already
        peekLength();
        readLimiter.checkLimit(peekLength);
        for (int i = 0; i < peekLength; i++) {
            buffer.get();
        }
        state = ELEMENT_READ_STATE_NEED_TYPE;
        return this;
    }
    void appendBytesRead(final Buffer buffer) {
        this.buffer.append(buffer);
    }
    void disposeBytesRead() {
        this.buffer.shrink();
    }
    /**
     * Internal helper method reading the additional ASN.1 length bytes and
     * transition to the next state if successful.
     *
     * @param ensureRead
     *            <code>true</code> to check for availability first.
     * @return <code>true</code> if the length bytes was successfully read.
     * @throws IOException
     *             If an error occurs while reading from the stream.
     */
    private boolean needAdditionalLengthBytesState(final boolean ensureRead) throws IOException {
        if (ensureRead && (readLimiter.remaining() < lengthBytesNeeded)) {
            return false;
        }
        byte readByte;
        readLimiter.checkLimit(lengthBytesNeeded);
        while (lengthBytesNeeded > 0) {
            readByte = buffer.get();
            peekLength = (peekLength << 8) | (readByte & 0xFF);
            lengthBytesNeeded--;
        }
        // Make sure that the element is not larger than the maximum allowed
        // message size.
        if ((maxElementSize > 0) && (peekLength > maxElementSize)) {
            final LocalizableMessage m =
                    ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED
                            .get(peekLength, maxElementSize);
            throw DecodeException.fatalError(m);
        }
        state = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
        return true;
    }
    /**
     * Internal helper method reading the first length bytes and transition to
     * the next state if successful.
     *
     * @param ensureRead
     *            <code>true</code> to check for availability first.
     * @return <code>true</code> if the length bytes was successfully read
     * @throws IOException
     *             If an error occurs while trying to decode an ASN1 element.
     */
    private boolean needFirstLengthByteState(final boolean ensureRead) throws IOException {
        if (ensureRead && (readLimiter.remaining() <= 0)) {
            return false;
        }
        readLimiter.checkLimit(1);
        byte readByte = buffer.get();
        peekLength = (readByte & 0x7F);
        if (peekLength != readByte) {
            lengthBytesNeeded = peekLength;
            if (lengthBytesNeeded > 4) {
                final LocalizableMessage message =
                        ERR_ASN1_INVALID_NUM_LENGTH_BYTES.get(lengthBytesNeeded);
                throw DecodeException.fatalError(message);
            }
            peekLength = 0x00;
            if (ensureRead && (readLimiter.remaining() < lengthBytesNeeded)) {
                state = ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES;
                return false;
            }
            readLimiter.checkLimit(lengthBytesNeeded);
            while (lengthBytesNeeded > 0) {
                readByte = buffer.get();
                peekLength = (peekLength << 8) | (readByte & 0xFF);
                lengthBytesNeeded--;
            }
        }
        // Make sure that the element is not larger than the maximum allowed
        // message size.
        if ((maxElementSize > 0) && (peekLength > maxElementSize)) {
            final LocalizableMessage m =
                    ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED
                            .get(peekLength, maxElementSize);
            throw DecodeException.fatalError(m);
        }
        state = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
        return true;
    }
    /**
     * Internal helper method reading the ASN.1 type byte and transition to the
     * next state if successful.
     *
     * @param ensureRead
     *            <code>true</code> to check for availability first.
     * @return <code>true</code> if the type byte was successfully read
     * @throws IOException
     *             If an error occurs while trying to decode an ASN1 element.
     */
    private boolean needTypeState(final boolean ensureRead) throws IOException {
        // Read just the type.
        if (ensureRead && (readLimiter.remaining() <= 0)) {
            return false;
        }
        readLimiter.checkLimit(1);
        peekType = buffer.get();
        state = ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE;
        return true;
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/ASN1BufferWriter.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -27,8 +26,6 @@
 */
package com.forgerock.opendj.ldap;
import static org.forgerock.opendj.asn1.ASN1Constants.BOOLEAN_VALUE_FALSE;
import static org.forgerock.opendj.asn1.ASN1Constants.BOOLEAN_VALUE_TRUE;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_ASN1_SEQUENCE_WRITE_NOT_STARTED;
@@ -49,693 +46,499 @@
import com.forgerock.opendj.util.StaticUtils;
/**
 * Grizzly ASN1 writer implementation.
 */
final class ASN1BufferWriter extends AbstractASN1Writer implements ASN1Writer,
    Cacheable
{
  private class ChildSequenceBuffer implements SequenceBuffer
  {
    private SequenceBuffer parent;
final class ASN1BufferWriter extends AbstractASN1Writer implements ASN1Writer, Cacheable {
    private class ChildSequenceBuffer implements SequenceBuffer {
        private SequenceBuffer parent;
    private ChildSequenceBuffer child;
        private ChildSequenceBuffer child;
    private final ByteStringBuilder buffer = new ByteStringBuilder(
        BUFFER_INIT_SIZE);
        private final ByteStringBuilder buffer = new ByteStringBuilder(BUFFER_INIT_SIZE);
        public SequenceBuffer endSequence() throws IOException {
            writeLength(parent, buffer.length());
            parent.writeByteArray(buffer.getBackingArray(), 0, buffer.length());
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
                StaticUtils.DEBUG_LOG.finest(String.format("WRITE ASN.1 END SEQUENCE(length=%d)",
                        buffer.length()));
            }
    public SequenceBuffer endSequence() throws IOException
    {
      writeLength(parent, buffer.length());
      parent.writeByteArray(buffer.getBackingArray(), 0, buffer.length());
            return parent;
        }
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "WRITE ASN.1 END SEQUENCE(length=%d)", buffer.length()));
      }
        public SequenceBuffer startSequence(final byte type) throws IOException {
            if (child == null) {
                child = new ChildSequenceBuffer();
                child.parent = this;
            }
      return parent;
            buffer.append(type);
            child.buffer.clear();
            return child;
        }
        public void writeByte(final byte b) throws IOException {
            buffer.append(b);
        }
        public void writeByteArray(final byte[] bs, final int offset, final int length)
                throws IOException {
            buffer.append(bs, offset, length);
        }
    }
    private static final class RecyclableBuffer extends ByteBufferWrapper {
        private volatile boolean usable = true;
        private RecyclableBuffer() {
            visible = ByteBuffer.allocate(BUFFER_INIT_SIZE);
            allowBufferDispose = true;
        }
    public SequenceBuffer startSequence(final byte type) throws IOException
    {
      if (child == null)
      {
        child = new ChildSequenceBuffer();
        child.parent = this;
      }
        @Override
        public void dispose() {
            usable = true;
        }
      buffer.append(type);
      child.buffer.clear();
      return child;
        /**
         * Ensures that the specified number of additional bytes will fit in the
         * buffer and resizes it if necessary.
         *
         * @param size
         *            The number of additional bytes.
         */
        public void ensureAdditionalCapacity(final int size) {
            final int newCount = visible.position() + size;
            if (newCount > visible.capacity()) {
                final ByteBuffer newByteBuffer =
                        ByteBuffer.allocate(Math.max(visible.capacity() << 1, newCount));
                visible.flip();
                visible = newByteBuffer.put(visible);
            }
        }
    }
    private class RootSequenceBuffer implements SequenceBuffer {
        private ChildSequenceBuffer child;
        public SequenceBuffer endSequence() throws IOException {
            final LocalizableMessage message = ERR_ASN1_SEQUENCE_WRITE_NOT_STARTED.get();
            throw new IllegalStateException(message.toString());
        }
    public void writeByte(final byte b) throws IOException
    {
      buffer.append(b);
        public SequenceBuffer startSequence(final byte type) throws IOException {
            if (child == null) {
                child = new ChildSequenceBuffer();
                child.parent = this;
            }
            outBuffer.ensureAdditionalCapacity(1);
            outBuffer.put(type);
            child.buffer.clear();
            return child;
        }
        public void writeByte(final byte b) throws IOException {
            outBuffer.ensureAdditionalCapacity(1);
            outBuffer.put(b);
        }
        public void writeByteArray(final byte[] bs, final int offset, final int length)
                throws IOException {
            outBuffer.ensureAdditionalCapacity(length);
            outBuffer.put(bs, offset, length);
        }
    }
    private interface SequenceBuffer {
        public SequenceBuffer endSequence() throws IOException;
        public SequenceBuffer startSequence(byte type) throws IOException;
    public void writeByteArray(final byte[] bs, final int offset,
        final int length) throws IOException
    {
      buffer.append(bs, offset, length);
    }
  }
        public void writeByte(byte b) throws IOException;
  private static final class RecyclableBuffer extends ByteBufferWrapper
  {
    private volatile boolean usable = true;
    private RecyclableBuffer()
    {
      visible = ByteBuffer.allocate(BUFFER_INIT_SIZE);
      allowBufferDispose = true;
        public void writeByteArray(byte[] bs, int offset, int length) throws IOException;
    }
    private static final int BUFFER_INIT_SIZE = 1024;
    private final static ThreadCache.CachedTypeIndex<ASN1BufferWriter> WRITER_INDEX = ThreadCache
            .obtainIndex(ASN1BufferWriter.class, 1);
    static ASN1BufferWriter getWriter() {
        ASN1BufferWriter asn1Writer = ThreadCache.takeFromCache(WRITER_INDEX);
        if (asn1Writer == null) {
            asn1Writer = new ASN1BufferWriter();
        }
    @Override
    public void dispose()
    {
      usable = true;
        if (!asn1Writer.outBuffer.usable) {
            // If the output buffer is unusable, create a new one.
            asn1Writer.outBuffer = new RecyclableBuffer();
        }
        asn1Writer.outBuffer.clear();
        return asn1Writer;
    }
    private SequenceBuffer sequenceBuffer;
    private RecyclableBuffer outBuffer;
    private final RootSequenceBuffer rootBuffer;
    /**
     * Ensures that the specified number of additional bytes will fit in the
     * buffer and resizes it if necessary.
     *
     * @param size
     *          The number of additional bytes.
     * Creates a new ASN.1 writer that writes to a StreamWriter.
     */
    public void ensureAdditionalCapacity(final int size)
    {
      final int newCount = visible.position() + size;
      if (newCount > visible.capacity())
      {
        final ByteBuffer newByteBuffer = ByteBuffer.allocate(Math.max(visible
            .capacity() << 1, newCount));
        visible.flip();
        visible = newByteBuffer.put(visible);
      }
    }
  }
  private class RootSequenceBuffer implements SequenceBuffer
  {
    private ChildSequenceBuffer child;
    public SequenceBuffer endSequence() throws IOException
    {
      final LocalizableMessage message = ERR_ASN1_SEQUENCE_WRITE_NOT_STARTED
          .get();
      throw new IllegalStateException(message.toString());
    private ASN1BufferWriter() {
        this.sequenceBuffer = this.rootBuffer = new RootSequenceBuffer();
        this.outBuffer = new RecyclableBuffer();
    }
    public SequenceBuffer startSequence(final byte type) throws IOException
    {
      if (child == null)
      {
        child = new ChildSequenceBuffer();
        child.parent = this;
      }
      outBuffer.ensureAdditionalCapacity(1);
      outBuffer.put(type);
      child.buffer.clear();
      return child;
    /**
     * Closes this ASN.1 writer and the underlying outputstream. Any unfinished
     * sequences will be ended.
     *
     * @throws IOException
     *             if an error occurs while closing the stream.
     */
    public void close() throws IOException {
        outBuffer = null;
    }
    public void writeByte(final byte b) throws IOException
    {
      outBuffer.ensureAdditionalCapacity(1);
      outBuffer.put(b);
    /**
     * Flushes the stream.
     *
     * @throws IOException
     *             If an I/O error occurs
     */
    public void flush() throws IOException {
        // Do nothing
    }
    public void writeByteArray(final byte[] bs, final int offset,
        final int length) throws IOException
    {
      outBuffer.ensureAdditionalCapacity(length);
      outBuffer.put(bs, offset, length);
    }
  }
  private interface SequenceBuffer
  {
    public SequenceBuffer endSequence() throws IOException;
    public SequenceBuffer startSequence(byte type) throws IOException;
    public void writeByte(byte b) throws IOException;
    public void writeByteArray(byte[] bs, int offset, int length)
        throws IOException;
  }
  private static final int BUFFER_INIT_SIZE = 1024;
  private final static ThreadCache.CachedTypeIndex<ASN1BufferWriter> WRITER_INDEX = ThreadCache
      .obtainIndex(ASN1BufferWriter.class, 1);
  static ASN1BufferWriter getWriter()
  {
    ASN1BufferWriter asn1Writer = ThreadCache.takeFromCache(WRITER_INDEX);
    if (asn1Writer == null)
    {
      asn1Writer = new ASN1BufferWriter();
    public void recycle() {
        sequenceBuffer = rootBuffer;
        outBuffer.clear();
        ThreadCache.putToCache(WRITER_INDEX, this);
    }
    if (!asn1Writer.outBuffer.usable)
    {
      // If the output buffer is unusable, create a new one.
      asn1Writer.outBuffer = new RecyclableBuffer();
    }
    asn1Writer.outBuffer.clear();
    return asn1Writer;
  }
    /**
     * {@inheritDoc}
     */
    public ASN1Writer writeBoolean(final byte type, final boolean booleanValue) throws IOException {
        sequenceBuffer.writeByte(type);
        writeLength(sequenceBuffer, 1);
        sequenceBuffer.writeByte(booleanValue ? BOOLEAN_VALUE_TRUE : BOOLEAN_VALUE_FALSE);
  private SequenceBuffer sequenceBuffer;
  private RecyclableBuffer outBuffer;
  private final RootSequenceBuffer rootBuffer;
  /**
   * Creates a new ASN.1 writer that writes to a StreamWriter.
   */
  private ASN1BufferWriter()
  {
    this.sequenceBuffer = this.rootBuffer = new RootSequenceBuffer();
    this.outBuffer = new RecyclableBuffer();
  }
  /**
   * Closes this ASN.1 writer and the underlying outputstream. Any unfinished
   * sequences will be ended.
   *
   * @throws IOException
   *           if an error occurs while closing the stream.
   */
  public void close() throws IOException
  {
    outBuffer = null;
  }
  /**
   * Flushes the stream.
   *
   * @throws IOException
   *           If an I/O error occurs
   */
  public void flush() throws IOException
  {
    // Do nothing
  }
  public void recycle()
  {
    sequenceBuffer = rootBuffer;
    outBuffer.clear();
    ThreadCache.putToCache(WRITER_INDEX, this);
  }
  /**
   * {@inheritDoc}
   */
  public ASN1Writer writeBoolean(final byte type, final boolean booleanValue)
      throws IOException
  {
    sequenceBuffer.writeByte(type);
    writeLength(sequenceBuffer, 1);
    sequenceBuffer.writeByte(booleanValue ? BOOLEAN_VALUE_TRUE
        : BOOLEAN_VALUE_FALSE);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String.format(
          "WRITE ASN.1 BOOLEAN(type=0x%x, length=%d, value=%s)", type, 1,
          String.valueOf(booleanValue)));
    }
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public ASN1Writer writeEndSequence() throws IOException
  {
    sequenceBuffer = sequenceBuffer.endSequence();
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public ASN1Writer writeEndSet() throws IOException
  {
    return writeEndSequence();
  }
  /**
   * {@inheritDoc}
   */
  public ASN1Writer writeEnumerated(final byte type, final int intValue)
      throws IOException
  {
    return writeInteger(type, intValue);
  }
  /**
   * {@inheritDoc}
   */
  public ASN1Writer writeInteger(final byte type, final int intValue)
      throws IOException
  {
    sequenceBuffer.writeByte(type);
    if (((intValue < 0) && ((intValue & 0xFFFFFF80) == 0xFFFFFF80))
        || ((intValue & 0x0000007F) == intValue))
    {
      writeLength(sequenceBuffer, 1);
      sequenceBuffer.writeByte((byte) (intValue & 0xFF));
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 1,
            intValue));
      }
    }
    else if (((intValue < 0) && ((intValue & 0xFFFF8000) == 0xFFFF8000))
        || ((intValue & 0x00007FFF) == intValue))
    {
      writeLength(sequenceBuffer, 2);
      sequenceBuffer.writeByte((byte) ((intValue >> 8) & 0xFF));
      sequenceBuffer.writeByte((byte) (intValue & 0xFF));
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 2,
            intValue));
      }
    }
    else if (((intValue < 0) && ((intValue & 0xFF800000) == 0xFF800000))
        || ((intValue & 0x007FFFFF) == intValue))
    {
      writeLength(sequenceBuffer, 3);
      sequenceBuffer.writeByte((byte) ((intValue >> 16) & 0xFF));
      sequenceBuffer.writeByte((byte) ((intValue >> 8) & 0xFF));
      sequenceBuffer.writeByte((byte) (intValue & 0xFF));
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 3,
            intValue));
      }
    }
    else
    {
      writeLength(sequenceBuffer, 4);
      sequenceBuffer.writeByte((byte) ((intValue >> 24) & 0xFF));
      sequenceBuffer.writeByte((byte) ((intValue >> 16) & 0xFF));
      sequenceBuffer.writeByte((byte) ((intValue >> 8) & 0xFF));
      sequenceBuffer.writeByte((byte) (intValue & 0xFF));
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 4,
            intValue));
      }
    }
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public ASN1Writer writeInteger(final byte type, final long longValue)
      throws IOException
  {
    sequenceBuffer.writeByte(type);
    if (((longValue < 0) && ((longValue & 0xFFFFFFFFFFFFFF80L) == 0xFFFFFFFFFFFFFF80L))
        || ((longValue & 0x000000000000007FL) == longValue))
    {
      writeLength(sequenceBuffer, 1);
      sequenceBuffer.writeByte((byte) (longValue & 0xFF));
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 1,
            longValue));
      }
    }
    else if (((longValue < 0) && ((longValue & 0xFFFFFFFFFFFF8000L) == 0xFFFFFFFFFFFF8000L))
        || ((longValue & 0x0000000000007FFFL) == longValue))
    {
      writeLength(sequenceBuffer, 2);
      sequenceBuffer.writeByte((byte) ((longValue >> 8) & 0xFF));
      sequenceBuffer.writeByte((byte) (longValue & 0xFF));
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 2,
            longValue));
      }
    }
    else if (((longValue < 0) && ((longValue & 0xFFFFFFFFFF800000L) == 0xFFFFFFFFFF800000L))
        || ((longValue & 0x00000000007FFFFFL) == longValue))
    {
      writeLength(sequenceBuffer, 3);
      sequenceBuffer.writeByte((byte) ((longValue >> 16) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 8) & 0xFF));
      sequenceBuffer.writeByte((byte) (longValue & 0xFF));
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 3,
            longValue));
      }
    }
    else if (((longValue < 0) && ((longValue & 0xFFFFFFFF80000000L) == 0xFFFFFFFF80000000L))
        || ((longValue & 0x000000007FFFFFFFL) == longValue))
    {
      writeLength(sequenceBuffer, 4);
      sequenceBuffer.writeByte((byte) ((longValue >> 24) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 16) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 8) & 0xFF));
      sequenceBuffer.writeByte((byte) (longValue & 0xFF));
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 4,
            longValue));
      }
    }
    else if (((longValue < 0) && ((longValue & 0xFFFFFF8000000000L) == 0xFFFFFF8000000000L))
        || ((longValue & 0x0000007FFFFFFFFFL) == longValue))
    {
      writeLength(sequenceBuffer, 5);
      sequenceBuffer.writeByte((byte) ((longValue >> 32) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 24) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 16) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 8) & 0xFF));
      sequenceBuffer.writeByte((byte) (longValue & 0xFF));
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 5,
            longValue));
      }
    }
    else if (((longValue < 0) && ((longValue & 0xFFFF800000000000L) == 0xFFFF800000000000L))
        || ((longValue & 0x00007FFFFFFFFFFFL) == longValue))
    {
      writeLength(sequenceBuffer, 6);
      sequenceBuffer.writeByte((byte) ((longValue >> 40) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 32) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 24) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 16) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 8) & 0xFF));
      sequenceBuffer.writeByte((byte) (longValue & 0xFF));
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 6,
            longValue));
      }
    }
    else if (((longValue < 0) && ((longValue & 0xFF80000000000000L) == 0xFF80000000000000L))
        || ((longValue & 0x007FFFFFFFFFFFFFL) == longValue))
    {
      writeLength(sequenceBuffer, 7);
      sequenceBuffer.writeByte((byte) ((longValue >> 48) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 40) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 32) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 24) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 16) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 8) & 0xFF));
      sequenceBuffer.writeByte((byte) (longValue & 0xFF));
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 7,
            longValue));
      }
    }
    else
    {
      writeLength(sequenceBuffer, 8);
      sequenceBuffer.writeByte((byte) ((longValue >> 56) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 48) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 40) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 32) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 24) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 16) & 0xFF));
      sequenceBuffer.writeByte((byte) ((longValue >> 8) & 0xFF));
      sequenceBuffer.writeByte((byte) (longValue & 0xFF));
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 8,
            longValue));
      }
    }
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public ASN1Writer writeNull(final byte type) throws IOException
  {
    sequenceBuffer.writeByte(type);
    writeLength(sequenceBuffer, 0);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String.format(
          "WRITE ASN.1 NULL(type=0x%x, length=%d)", type, 0));
    }
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public ASN1Writer writeOctetString(final byte type, final byte[] value,
      final int offset, final int length) throws IOException
  {
    sequenceBuffer.writeByte(type);
    writeLength(sequenceBuffer, length);
    sequenceBuffer.writeByteArray(value, offset, length);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String.format(
          "WRITE ASN.1 OCTETSTRING(type=0x%x, length=%d)", type, length));
    }
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public ASN1Writer writeOctetString(final byte type, final ByteSequence value)
      throws IOException
  {
    sequenceBuffer.writeByte(type);
    writeLength(sequenceBuffer, value.length());
    // TODO: Is there a more efficient way to do this?
    for (int i = 0; i < value.length(); i++)
    {
      sequenceBuffer.writeByte(value.byteAt(i));
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
            StaticUtils.DEBUG_LOG.finest(String.format(
                    "WRITE ASN.1 BOOLEAN(type=0x%x, length=%d, value=%s)", type, 1, String
                            .valueOf(booleanValue)));
        }
        return this;
    }
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String
          .format("WRITE ASN.1 OCTETSTRING(type=0x%x, length=%d)", type, value
              .length()));
    }
    return this;
  }
    /**
     * {@inheritDoc}
     */
    public ASN1Writer writeEndSequence() throws IOException {
        sequenceBuffer = sequenceBuffer.endSequence();
  /**
   * {@inheritDoc}
   */
  public ASN1Writer writeOctetString(final byte type, final String value)
      throws IOException
  {
    sequenceBuffer.writeByte(type);
    if (value == null)
    {
      writeLength(sequenceBuffer, 0);
      return this;
        return this;
    }
    final byte[] bytes = StaticUtils.getBytes(value);
    writeLength(sequenceBuffer, bytes.length);
    sequenceBuffer.writeByteArray(bytes, 0, bytes.length);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String.format(
          "WRITE ASN.1 OCTETSTRING(type=0x%x, length=%d, " + "value=%s)", type,
          bytes.length, value));
    /**
     * {@inheritDoc}
     */
    public ASN1Writer writeEndSet() throws IOException {
        return writeEndSequence();
    }
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public ASN1Writer writeStartSequence(final byte type) throws IOException
  {
    // Get a child sequence buffer
    sequenceBuffer = sequenceBuffer.startSequence(type);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String.format(
          "WRITE ASN.1 START SEQUENCE(type=0x%x)", type));
    /**
     * {@inheritDoc}
     */
    public ASN1Writer writeEnumerated(final byte type, final int intValue) throws IOException {
        return writeInteger(type, intValue);
    }
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public ASN1Writer writeStartSet(final byte type) throws IOException
  {
    // From an implementation point of view, a set is equivalent to a
    // sequence.
    return writeStartSequence(type);
  }
  Buffer getBuffer()
  {
    outBuffer.usable = false;
    return outBuffer.flip();
  }
  /**
   * Writes the provided value for use as the length of an ASN.1 element.
   *
   * @param buffer
   *          The sequence buffer to write to.
   * @param length
   *          The length to encode for use in an ASN.1 element.
   * @throws IOException
   *           if an error occurs while writing.
   */
  private void writeLength(final SequenceBuffer buffer, final int length)
      throws IOException
  {
    if (length < 128)
    {
      buffer.writeByte((byte) length);
    /**
     * {@inheritDoc}
     */
    public ASN1Writer writeInteger(final byte type, final int intValue) throws IOException {
        sequenceBuffer.writeByte(type);
        if (((intValue < 0) && ((intValue & 0xFFFFFF80) == 0xFFFFFF80))
                || ((intValue & 0x0000007F) == intValue)) {
            writeLength(sequenceBuffer, 1);
            sequenceBuffer.writeByte((byte) (intValue & 0xFF));
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
                StaticUtils.DEBUG_LOG.finest(String.format(
                        "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 1, intValue));
            }
        } else if (((intValue < 0) && ((intValue & 0xFFFF8000) == 0xFFFF8000))
                || ((intValue & 0x00007FFF) == intValue)) {
            writeLength(sequenceBuffer, 2);
            sequenceBuffer.writeByte((byte) ((intValue >> 8) & 0xFF));
            sequenceBuffer.writeByte((byte) (intValue & 0xFF));
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
                StaticUtils.DEBUG_LOG.finest(String.format(
                        "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 2, intValue));
            }
        } else if (((intValue < 0) && ((intValue & 0xFF800000) == 0xFF800000))
                || ((intValue & 0x007FFFFF) == intValue)) {
            writeLength(sequenceBuffer, 3);
            sequenceBuffer.writeByte((byte) ((intValue >> 16) & 0xFF));
            sequenceBuffer.writeByte((byte) ((intValue >> 8) & 0xFF));
            sequenceBuffer.writeByte((byte) (intValue & 0xFF));
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
                StaticUtils.DEBUG_LOG.finest(String.format(
                        "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 3, intValue));
            }
        } else {
            writeLength(sequenceBuffer, 4);
            sequenceBuffer.writeByte((byte) ((intValue >> 24) & 0xFF));
            sequenceBuffer.writeByte((byte) ((intValue >> 16) & 0xFF));
            sequenceBuffer.writeByte((byte) ((intValue >> 8) & 0xFF));
            sequenceBuffer.writeByte((byte) (intValue & 0xFF));
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
                StaticUtils.DEBUG_LOG.finest(String.format(
                        "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 4, intValue));
            }
        }
        return this;
    }
    else if ((length & 0x000000FF) == length)
    {
      buffer.writeByte((byte) 0x81);
      buffer.writeByte((byte) (length & 0xFF));
    /**
     * {@inheritDoc}
     */
    public ASN1Writer writeInteger(final byte type, final long longValue) throws IOException {
        sequenceBuffer.writeByte(type);
        if (((longValue < 0) && ((longValue & 0xFFFFFFFFFFFFFF80L) == 0xFFFFFFFFFFFFFF80L))
                || ((longValue & 0x000000000000007FL) == longValue)) {
            writeLength(sequenceBuffer, 1);
            sequenceBuffer.writeByte((byte) (longValue & 0xFF));
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
                StaticUtils.DEBUG_LOG.finest(String.format(
                        "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 1, longValue));
            }
        } else if (((longValue < 0) && ((longValue & 0xFFFFFFFFFFFF8000L) == 0xFFFFFFFFFFFF8000L))
                || ((longValue & 0x0000000000007FFFL) == longValue)) {
            writeLength(sequenceBuffer, 2);
            sequenceBuffer.writeByte((byte) ((longValue >> 8) & 0xFF));
            sequenceBuffer.writeByte((byte) (longValue & 0xFF));
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
                StaticUtils.DEBUG_LOG.finest(String.format(
                        "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 2, longValue));
            }
        } else if (((longValue < 0) && ((longValue & 0xFFFFFFFFFF800000L) == 0xFFFFFFFFFF800000L))
                || ((longValue & 0x00000000007FFFFFL) == longValue)) {
            writeLength(sequenceBuffer, 3);
            sequenceBuffer.writeByte((byte) ((longValue >> 16) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 8) & 0xFF));
            sequenceBuffer.writeByte((byte) (longValue & 0xFF));
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
                StaticUtils.DEBUG_LOG.finest(String.format(
                        "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 3, longValue));
            }
        } else if (((longValue < 0) && ((longValue & 0xFFFFFFFF80000000L) == 0xFFFFFFFF80000000L))
                || ((longValue & 0x000000007FFFFFFFL) == longValue)) {
            writeLength(sequenceBuffer, 4);
            sequenceBuffer.writeByte((byte) ((longValue >> 24) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 16) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 8) & 0xFF));
            sequenceBuffer.writeByte((byte) (longValue & 0xFF));
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
                StaticUtils.DEBUG_LOG.finest(String.format(
                        "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 4, longValue));
            }
        } else if (((longValue < 0) && ((longValue & 0xFFFFFF8000000000L) == 0xFFFFFF8000000000L))
                || ((longValue & 0x0000007FFFFFFFFFL) == longValue)) {
            writeLength(sequenceBuffer, 5);
            sequenceBuffer.writeByte((byte) ((longValue >> 32) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 24) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 16) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 8) & 0xFF));
            sequenceBuffer.writeByte((byte) (longValue & 0xFF));
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
                StaticUtils.DEBUG_LOG.finest(String.format(
                        "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 5, longValue));
            }
        } else if (((longValue < 0) && ((longValue & 0xFFFF800000000000L) == 0xFFFF800000000000L))
                || ((longValue & 0x00007FFFFFFFFFFFL) == longValue)) {
            writeLength(sequenceBuffer, 6);
            sequenceBuffer.writeByte((byte) ((longValue >> 40) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 32) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 24) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 16) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 8) & 0xFF));
            sequenceBuffer.writeByte((byte) (longValue & 0xFF));
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
                StaticUtils.DEBUG_LOG.finest(String.format(
                        "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 6, longValue));
            }
        } else if (((longValue < 0) && ((longValue & 0xFF80000000000000L) == 0xFF80000000000000L))
                || ((longValue & 0x007FFFFFFFFFFFFFL) == longValue)) {
            writeLength(sequenceBuffer, 7);
            sequenceBuffer.writeByte((byte) ((longValue >> 48) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 40) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 32) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 24) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 16) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 8) & 0xFF));
            sequenceBuffer.writeByte((byte) (longValue & 0xFF));
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
                StaticUtils.DEBUG_LOG.finest(String.format(
                        "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 7, longValue));
            }
        } else {
            writeLength(sequenceBuffer, 8);
            sequenceBuffer.writeByte((byte) ((longValue >> 56) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 48) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 40) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 32) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 24) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 16) & 0xFF));
            sequenceBuffer.writeByte((byte) ((longValue >> 8) & 0xFF));
            sequenceBuffer.writeByte((byte) (longValue & 0xFF));
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
                StaticUtils.DEBUG_LOG.finest(String.format(
                        "WRITE ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", type, 8, longValue));
            }
        }
        return this;
    }
    else if ((length & 0x0000FFFF) == length)
    {
      buffer.writeByte((byte) 0x82);
      buffer.writeByte((byte) ((length >> 8) & 0xFF));
      buffer.writeByte((byte) (length & 0xFF));
    /**
     * {@inheritDoc}
     */
    public ASN1Writer writeNull(final byte type) throws IOException {
        sequenceBuffer.writeByte(type);
        writeLength(sequenceBuffer, 0);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
            StaticUtils.DEBUG_LOG.finest(String.format("WRITE ASN.1 NULL(type=0x%x, length=%d)",
                    type, 0));
        }
        return this;
    }
    else if ((length & 0x00FFFFFF) == length)
    {
      buffer.writeByte((byte) 0x83);
      buffer.writeByte((byte) ((length >> 16) & 0xFF));
      buffer.writeByte((byte) ((length >> 8) & 0xFF));
      buffer.writeByte((byte) (length & 0xFF));
    /**
     * {@inheritDoc}
     */
    public ASN1Writer writeOctetString(final byte type, final byte[] value, final int offset,
            final int length) throws IOException {
        sequenceBuffer.writeByte(type);
        writeLength(sequenceBuffer, length);
        sequenceBuffer.writeByteArray(value, offset, length);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
            StaticUtils.DEBUG_LOG.finest(String.format(
                    "WRITE ASN.1 OCTETSTRING(type=0x%x, length=%d)", type, length));
        }
        return this;
    }
    else
    {
      buffer.writeByte((byte) 0x84);
      buffer.writeByte((byte) ((length >> 24) & 0xFF));
      buffer.writeByte((byte) ((length >> 16) & 0xFF));
      buffer.writeByte((byte) ((length >> 8) & 0xFF));
      buffer.writeByte((byte) (length & 0xFF));
    /**
     * {@inheritDoc}
     */
    public ASN1Writer writeOctetString(final byte type, final ByteSequence value)
            throws IOException {
        sequenceBuffer.writeByte(type);
        writeLength(sequenceBuffer, value.length());
        // TODO: Is there a more efficient way to do this?
        for (int i = 0; i < value.length(); i++) {
            sequenceBuffer.writeByte(value.byteAt(i));
        }
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
            StaticUtils.DEBUG_LOG.finest(String.format(
                    "WRITE ASN.1 OCTETSTRING(type=0x%x, length=%d)", type, value.length()));
        }
        return this;
    }
  }
    /**
     * {@inheritDoc}
     */
    public ASN1Writer writeOctetString(final byte type, final String value) throws IOException {
        sequenceBuffer.writeByte(type);
        if (value == null) {
            writeLength(sequenceBuffer, 0);
            return this;
        }
        final byte[] bytes = StaticUtils.getBytes(value);
        writeLength(sequenceBuffer, bytes.length);
        sequenceBuffer.writeByteArray(bytes, 0, bytes.length);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
            StaticUtils.DEBUG_LOG.finest(String.format(
                    "WRITE ASN.1 OCTETSTRING(type=0x%x, length=%d, " + "value=%s)", type,
                    bytes.length, value));
        }
        return this;
    }
    /**
     * {@inheritDoc}
     */
    public ASN1Writer writeStartSequence(final byte type) throws IOException {
        // Get a child sequence buffer
        sequenceBuffer = sequenceBuffer.startSequence(type);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
            StaticUtils.DEBUG_LOG.finest(String.format("WRITE ASN.1 START SEQUENCE(type=0x%x)",
                    type));
        }
        return this;
    }
    /**
     * {@inheritDoc}
     */
    public ASN1Writer writeStartSet(final byte type) throws IOException {
        // From an implementation point of view, a set is equivalent to a
        // sequence.
        return writeStartSequence(type);
    }
    Buffer getBuffer() {
        outBuffer.usable = false;
        return outBuffer.flip();
    }
    /**
     * Writes the provided value for use as the length of an ASN.1 element.
     *
     * @param buffer
     *            The sequence buffer to write to.
     * @param length
     *            The length to encode for use in an ASN.1 element.
     * @throws IOException
     *             if an error occurs while writing.
     */
    private void writeLength(final SequenceBuffer buffer, final int length) throws IOException {
        if (length < 128) {
            buffer.writeByte((byte) length);
        } else if ((length & 0x000000FF) == length) {
            buffer.writeByte((byte) 0x81);
            buffer.writeByte((byte) (length & 0xFF));
        } else if ((length & 0x0000FFFF) == length) {
            buffer.writeByte((byte) 0x82);
            buffer.writeByte((byte) ((length >> 8) & 0xFF));
            buffer.writeByte((byte) (length & 0xFF));
        } else if ((length & 0x00FFFFFF) == length) {
            buffer.writeByte((byte) 0x83);
            buffer.writeByte((byte) ((length >> 16) & 0xFF));
            buffer.writeByte((byte) ((length >> 8) & 0xFF));
            buffer.writeByte((byte) (length & 0xFF));
        } else {
            buffer.writeByte((byte) 0x84);
            buffer.writeByte((byte) ((length >> 24) & 0xFF));
            buffer.writeByte((byte) ((length >> 16) & 0xFF));
            buffer.writeByte((byte) ((length >> 8) & 0xFF));
            buffer.writeByte((byte) (length & 0xFF));
        }
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/AbstractLDAPFutureResultImpl.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,148 +27,109 @@
package com.forgerock.opendj.ldap;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.IntermediateResponseHandler;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.responses.IntermediateResponse;
import org.forgerock.opendj.ldap.responses.Result;
import com.forgerock.opendj.util.AsynchronousFutureResult;
/**
 * Abstract future result implementation.
 *
 * @param <S>
 *          The type of result returned by this future.
 *            The type of result returned by this future.
 */
abstract class AbstractLDAPFutureResultImpl<S extends Result> extends
    AsynchronousFutureResult<S> implements IntermediateResponseHandler
{
  private final Connection connection;
abstract class AbstractLDAPFutureResultImpl<S extends Result> extends AsynchronousFutureResult<S>
        implements IntermediateResponseHandler {
    private final Connection connection;
  private final int requestID;
    private final int requestID;
  private IntermediateResponseHandler intermediateResponseHandler;
    private IntermediateResponseHandler intermediateResponseHandler;
  private volatile long timestamp;
    private volatile long timestamp;
    AbstractLDAPFutureResultImpl(final int requestID, final ResultHandler<? super S> resultHandler,
            final IntermediateResponseHandler intermediateResponseHandler,
            final Connection connection) {
        super(resultHandler);
        this.requestID = requestID;
        this.connection = connection;
        this.intermediateResponseHandler = intermediateResponseHandler;
        this.timestamp = System.currentTimeMillis();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public final int getRequestID() {
        return requestID;
    }
  AbstractLDAPFutureResultImpl(final int requestID,
      final ResultHandler<? super S> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler,
      final Connection connection)
  {
    super(resultHandler);
    this.requestID = requestID;
    this.connection = connection;
    this.intermediateResponseHandler = intermediateResponseHandler;
    this.timestamp = System.currentTimeMillis();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public final int getRequestID()
  {
    return requestID;
  }
  @Override
  public final boolean handleIntermediateResponse(
      final IntermediateResponse response)
  {
    // FIXME: there's a potential race condition here - the future could
    // get cancelled between the isDone() call and the handler
    // invocation. We'd need to add support for intermediate handlers in
    // the synchronizer.
    if (!isDone())
    {
      updateTimestamp();
      if (intermediateResponseHandler != null)
      {
        if (!intermediateResponseHandler.handleIntermediateResponse(response))
        {
          intermediateResponseHandler = null;
    @Override
    public final boolean handleIntermediateResponse(final IntermediateResponse response) {
        // FIXME: there's a potential race condition here - the future could
        // get cancelled between the isDone() call and the handler
        // invocation. We'd need to add support for intermediate handlers in
        // the synchronizer.
        if (!isDone()) {
            updateTimestamp();
            if (intermediateResponseHandler != null) {
                if (!intermediateResponseHandler.handleIntermediateResponse(response)) {
                    intermediateResponseHandler = null;
                }
            }
        }
      }
        return true;
    }
    return true;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  protected final ErrorResultException handleCancelRequest(
      final boolean mayInterruptIfRunning)
  {
    connection.abandonAsync(Requests.newAbandonRequest(requestID));
    return null;
  }
  @Override
  protected void toString(final StringBuilder sb)
  {
    sb.append(" requestID = ");
    sb.append(requestID);
    sb.append(" timestamp = ");
    sb.append(timestamp);
    super.toString(sb);
  }
  final void adaptErrorResult(final Result result)
  {
    final S errorResult = newErrorResult(result.getResultCode(),
        result.getDiagnosticMessage(), result.getCause());
    setResultOrError(errorResult);
  }
  final long getTimestamp()
  {
    return timestamp;
  }
  abstract S newErrorResult(ResultCode resultCode, String diagnosticMessage,
      Throwable cause);
  final void setResultOrError(final S result)
  {
    if (result.getResultCode().isExceptional())
    {
      handleErrorResult(ErrorResultException.newErrorResult(result));
    /**
     * {@inheritDoc}
     */
    @Override
    protected final ErrorResultException handleCancelRequest(final boolean mayInterruptIfRunning) {
        connection.abandonAsync(Requests.newAbandonRequest(requestID));
        return null;
    }
    else
    {
      handleResult(result);
    @Override
    protected void toString(final StringBuilder sb) {
        sb.append(" requestID = ");
        sb.append(requestID);
        sb.append(" timestamp = ");
        sb.append(timestamp);
        super.toString(sb);
    }
  }
    final void adaptErrorResult(final Result result) {
        final S errorResult =
                newErrorResult(result.getResultCode(), result.getDiagnosticMessage(), result
                        .getCause());
        setResultOrError(errorResult);
    }
    final long getTimestamp() {
        return timestamp;
    }
  final void updateTimestamp()
  {
    timestamp = System.currentTimeMillis();
  }
    abstract S newErrorResult(ResultCode resultCode, String diagnosticMessage, Throwable cause);
    final void setResultOrError(final S result) {
        if (result.getResultCode().isExceptional()) {
            handleErrorResult(ErrorResultException.newErrorResult(result));
        } else {
            handleResult(result);
        }
    }
    final void updateTimestamp() {
        timestamp = System.currentTimeMillis();
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/AbstractLDAPMessageHandler.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -27,210 +26,141 @@
package com.forgerock.opendj.ldap;
import java.io.IOException;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.requests.*;
import org.forgerock.opendj.ldap.responses.*;
import org.forgerock.opendj.ldap.requests.AbandonRequest;
import org.forgerock.opendj.ldap.requests.AddRequest;
import org.forgerock.opendj.ldap.requests.CompareRequest;
import org.forgerock.opendj.ldap.requests.DeleteRequest;
import org.forgerock.opendj.ldap.requests.ExtendedRequest;
import org.forgerock.opendj.ldap.requests.GenericBindRequest;
import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
import org.forgerock.opendj.ldap.requests.ModifyRequest;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.requests.UnbindRequest;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.CompareResult;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
import org.forgerock.opendj.ldap.responses.IntermediateResponse;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
/**
 * Abstract LDAP message handler.
 *
 * @param <P>
 *          A user provided handler parameter.
 *            A user provided handler parameter.
 */
abstract class AbstractLDAPMessageHandler<P> implements LDAPMessageHandler<P>
{
  public void abandonRequest(final P param, final int messageID,
      final AbandonRequest request) throws UnexpectedRequestException,
      IOException
  {
    throw new UnexpectedRequestException(messageID, request);
  }
abstract class AbstractLDAPMessageHandler<P> implements LDAPMessageHandler<P> {
    public void abandonRequest(final P param, final int messageID, final AbandonRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void addRequest(final P param, final int messageID, final AddRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void addResult(final P param, final int messageID, final Result result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
  public void addRequest(final P param, final int messageID,
      final AddRequest request) throws UnexpectedRequestException, IOException
  {
    throw new UnexpectedRequestException(messageID, request);
  }
    public void bindRequest(final P param, final int messageID, final int version,
            final GenericBindRequest request) throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void bindResult(final P param, final int messageID, final BindResult result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
    public void compareRequest(final P param, final int messageID, final CompareRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
  public void addResult(final P param, final int messageID, final Result result)
      throws UnexpectedResponseException, IOException
  {
    throw new UnexpectedResponseException(messageID, result);
  }
    public void compareResult(final P param, final int messageID, final CompareResult result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
    public void deleteRequest(final P param, final int messageID, final DeleteRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void deleteResult(final P param, final int messageID, final Result result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
  public void bindRequest(final P param, final int messageID,
      final int version, final GenericBindRequest request)
      throws UnexpectedRequestException, IOException
  {
    throw new UnexpectedRequestException(messageID, request);
  }
    public <R extends ExtendedResult> void extendedRequest(final P param, final int messageID,
            final ExtendedRequest<R> request) throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void extendedResult(final P param, final int messageID, final ExtendedResult result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
    public void intermediateResponse(final P param, final int messageID,
            final IntermediateResponse response) throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, response);
    }
  public void bindResult(final P param, final int messageID,
      final BindResult result) throws UnexpectedResponseException, IOException
  {
    throw new UnexpectedResponseException(messageID, result);
  }
    public void modifyDNRequest(final P param, final int messageID, final ModifyDNRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void modifyDNResult(final P param, final int messageID, final Result result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
    public void modifyRequest(final P param, final int messageID, final ModifyRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
  public void compareRequest(final P param, final int messageID,
      final CompareRequest request) throws UnexpectedRequestException,
      IOException
  {
    throw new UnexpectedRequestException(messageID, request);
  }
    public void modifyResult(final P param, final int messageID, final Result result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
    public void searchRequest(final P param, final int messageID, final SearchRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void searchResult(final P param, final int messageID, final Result result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
  public void compareResult(final P param, final int messageID,
      final CompareResult result) throws UnexpectedResponseException,
      IOException
  {
    throw new UnexpectedResponseException(messageID, result);
  }
    public void searchResultEntry(final P param, final int messageID, final SearchResultEntry entry)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, entry);
    }
    public void searchResultReference(final P param, final int messageID,
            final SearchResultReference reference) throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, reference);
    }
    public void unbindRequest(final P param, final int messageID, final UnbindRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
  public void deleteRequest(final P param, final int messageID,
      final DeleteRequest request) throws UnexpectedRequestException,
      IOException
  {
    throw new UnexpectedRequestException(messageID, request);
  }
  public void deleteResult(final P param, final int messageID,
      final Result result) throws UnexpectedResponseException, IOException
  {
    throw new UnexpectedResponseException(messageID, result);
  }
  public <R extends ExtendedResult> void extendedRequest(final P param,
      final int messageID, final ExtendedRequest<R> request)
      throws UnexpectedRequestException, IOException
  {
    throw new UnexpectedRequestException(messageID, request);
  }
  public void extendedResult(final P param, final int messageID,
      final ExtendedResult result) throws UnexpectedResponseException,
      IOException
  {
    throw new UnexpectedResponseException(messageID, result);
  }
  public void intermediateResponse(final P param, final int messageID,
      final IntermediateResponse response) throws UnexpectedResponseException,
      IOException
  {
    throw new UnexpectedResponseException(messageID, response);
  }
  public void modifyDNRequest(final P param, final int messageID,
      final ModifyDNRequest request) throws UnexpectedRequestException,
      IOException
  {
    throw new UnexpectedRequestException(messageID, request);
  }
  public void modifyDNResult(final P param, final int messageID,
      final Result result) throws UnexpectedResponseException, IOException
  {
    throw new UnexpectedResponseException(messageID, result);
  }
  public void modifyRequest(final P param, final int messageID,
      final ModifyRequest request) throws UnexpectedRequestException,
      IOException
  {
    throw new UnexpectedRequestException(messageID, request);
  }
  public void modifyResult(final P param, final int messageID,
      final Result result) throws UnexpectedResponseException, IOException
  {
    throw new UnexpectedResponseException(messageID, result);
  }
  public void searchRequest(final P param, final int messageID,
      final SearchRequest request) throws UnexpectedRequestException,
      IOException
  {
    throw new UnexpectedRequestException(messageID, request);
  }
  public void searchResult(final P param, final int messageID,
      final Result result) throws UnexpectedResponseException, IOException
  {
    throw new UnexpectedResponseException(messageID, result);
  }
  public void searchResultEntry(final P param, final int messageID,
      final SearchResultEntry entry) throws UnexpectedResponseException,
      IOException
  {
    throw new UnexpectedResponseException(messageID, entry);
  }
  public void searchResultReference(final P param, final int messageID,
      final SearchResultReference reference)
      throws UnexpectedResponseException, IOException
  {
    throw new UnexpectedResponseException(messageID, reference);
  }
  public void unbindRequest(final P param, final int messageID,
      final UnbindRequest request) throws UnexpectedRequestException,
      IOException
  {
    throw new UnexpectedRequestException(messageID, request);
  }
  public void unrecognizedMessage(final P param, final int messageID,
      final byte messageTag, final ByteString messageBytes)
      throws UnsupportedMessageException, IOException
  {
    throw new UnsupportedMessageException(messageID, messageTag, messageBytes);
  }
    public void unrecognizedMessage(final P param, final int messageID, final byte messageTag,
            final ByteString messageBytes) throws UnsupportedMessageException, IOException {
        throw new UnsupportedMessageException(messageID, messageTag, messageBytes);
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/ConnectionSecurityLayerFilter.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,8 +27,6 @@
package com.forgerock.opendj.ldap;
import org.forgerock.opendj.ldap.ConnectionSecurityLayer;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.glassfish.grizzly.AbstractTransformer;
@@ -40,141 +37,92 @@
import org.glassfish.grizzly.memory.Buffers;
import org.glassfish.grizzly.memory.MemoryManager;
/**
 * Connection security layer filter adapter.
 */
final class ConnectionSecurityLayerFilter extends
    AbstractCodecFilter<Buffer, Buffer>
{
  /**
   * <tt>Transformer</tt>, which decodes SASL encrypted data, contained in the
   * input Buffer, to the output Buffer.
   */
  private static final class Decoder extends
      AbstractTransformer<Buffer, Buffer>
  {
    private static final int BUFFER_SIZE = 4096;
    private final byte[] buffer = new byte[BUFFER_SIZE];
    private final ConnectionSecurityLayer layer;
    private final MemoryManager<?> memoryManager;
final class ConnectionSecurityLayerFilter extends AbstractCodecFilter<Buffer, Buffer> {
    /**
     * <tt>Transformer</tt>, which decodes SASL encrypted data, contained in the
     * input Buffer, to the output Buffer.
     */
    private static final class Decoder extends AbstractTransformer<Buffer, Buffer> {
        private static final int BUFFER_SIZE = 4096;
        private final byte[] buffer = new byte[BUFFER_SIZE];
        private final ConnectionSecurityLayer layer;
        private final MemoryManager<?> memoryManager;
        public Decoder(final ConnectionSecurityLayer layer, final MemoryManager<?> memoryManager) {
            this.layer = layer;
            this.memoryManager = memoryManager;
        }
        public String getName() {
            return this.getClass().getName();
        }
    public Decoder(final ConnectionSecurityLayer layer,
        final MemoryManager<?> memoryManager)
    {
      this.layer = layer;
      this.memoryManager = memoryManager;
        public boolean hasInputRemaining(final AttributeStorage storage, final Buffer input) {
            return input != null && input.hasRemaining();
        }
        @Override
        public TransformationResult<Buffer, Buffer> transformImpl(final AttributeStorage storage,
                final Buffer input) {
            final int len = Math.min(buffer.length, input.remaining());
            input.get(buffer, 0, len);
            try {
                final Buffer output = Buffers.wrap(memoryManager, layer.unwrap(buffer, 0, len));
                return TransformationResult.createCompletedResult(output, input);
            } catch (final ErrorResultException e) {
                return TransformationResult.createErrorResult(e.getResult().getResultCode()
                        .intValue(), e.getMessage());
            }
        }
    }
    /**
     * <tt>Transformer</tt>, which encodes SASL encrypted data, contained in the
     * input Buffer, to the output Buffer.
     */
    private static final class Encoder extends AbstractTransformer<Buffer, Buffer> {
        private static final int BUFFER_SIZE = 4096;
        private final byte[] buffer = new byte[BUFFER_SIZE];
        private final ConnectionSecurityLayer layer;
        private final MemoryManager<?> memoryManager;
        private Encoder(final ConnectionSecurityLayer layer, final MemoryManager<?> memoryManager) {
            this.layer = layer;
            this.memoryManager = memoryManager;
        }
    public String getName()
    {
      return this.getClass().getName();
        public String getName() {
            return this.getClass().getName();
        }
        public boolean hasInputRemaining(final AttributeStorage storage, final Buffer input) {
            return input != null && input.hasRemaining();
        }
        @Override
        public TransformationResult<Buffer, Buffer> transformImpl(final AttributeStorage storage,
                final Buffer input) {
            final int len = Math.min(buffer.length, input.remaining());
            input.get(buffer, 0, len);
            try {
                final Buffer output = Buffers.wrap(memoryManager, layer.wrap(buffer, 0, len));
                return TransformationResult.createCompletedResult(output, input);
            } catch (final ErrorResultException e) {
                return TransformationResult.createErrorResult(e.getResult().getResultCode()
                        .intValue(), e.getMessage());
            }
        }
    }
    public boolean hasInputRemaining(final AttributeStorage storage,
        final Buffer input)
    {
      return input != null && input.hasRemaining();
    ConnectionSecurityLayerFilter(final ConnectionSecurityLayer layer,
            final MemoryManager<?> memoryManager) {
        super(new Decoder(layer, memoryManager), new Encoder(layer, memoryManager));
    }
    @Override
    public TransformationResult<Buffer, Buffer> transformImpl(
        final AttributeStorage storage, final Buffer input)
    {
      final int len = Math.min(buffer.length, input.remaining());
      input.get(buffer, 0, len);
      try
      {
        final Buffer output = Buffers.wrap(memoryManager,
            layer.unwrap(buffer, 0, len));
        return TransformationResult.createCompletedResult(output, input);
      }
      catch (final ErrorResultException e)
      {
        return TransformationResult.createErrorResult(e.getResult()
            .getResultCode().intValue(), e.getMessage());
      }
    }
  }
  /**
   * <tt>Transformer</tt>, which encodes SASL encrypted data, contained in the
   * input Buffer, to the output Buffer.
   */
  private static final class Encoder extends
      AbstractTransformer<Buffer, Buffer>
  {
    private static final int BUFFER_SIZE = 4096;
    private final byte[] buffer = new byte[BUFFER_SIZE];
    private final ConnectionSecurityLayer layer;
    private final MemoryManager<?> memoryManager;
    private Encoder(final ConnectionSecurityLayer layer,
        final MemoryManager<?> memoryManager)
    {
      this.layer = layer;
      this.memoryManager = memoryManager;
    }
    public String getName()
    {
      return this.getClass().getName();
    }
    public boolean hasInputRemaining(final AttributeStorage storage,
        final Buffer input)
    {
      return input != null && input.hasRemaining();
    }
    @Override
    public TransformationResult<Buffer, Buffer> transformImpl(
        final AttributeStorage storage, final Buffer input)
    {
      final int len = Math.min(buffer.length, input.remaining());
      input.get(buffer, 0, len);
      try
      {
        final Buffer output = Buffers.wrap(memoryManager,
            layer.wrap(buffer, 0, len));
        return TransformationResult.createCompletedResult(output, input);
      }
      catch (final ErrorResultException e)
      {
        return TransformationResult.createErrorResult(e.getResult()
            .getResultCode().intValue(), e.getMessage());
      }
    }
  }
  ConnectionSecurityLayerFilter(final ConnectionSecurityLayer layer,
      final MemoryManager<?> memoryManager)
  {
    super(new Decoder(layer, memoryManager), new Encoder(layer, memoryManager));
  }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/DefaultTCPNIOTransport.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,8 +27,6 @@
package com.forgerock.opendj.ldap;
import java.io.IOException;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
@@ -37,155 +34,124 @@
import org.glassfish.grizzly.strategies.SameThreadIOStrategy;
import org.glassfish.grizzly.strategies.WorkerThreadIOStrategy;
/**
 * The default {@link TCPNIOTransport} which all {@code LDAPConnectionFactory}s
 * and {@code LDAPListener}s will use unless otherwise specified in their
 * options.
 */
final class DefaultTCPNIOTransport
{
  private static TCPNIOTransport defaultTransport = null;
final class DefaultTCPNIOTransport {
    private static TCPNIOTransport defaultTransport = null;
    /**
     * Returns the default {@link TCPNIOTransport} which all
     * {@code LDAPConnectionFactory}s and {@code LDAPListener}s will use unless
     * otherwise specified in their options.
     *
     * @return The default {@link TCPNIOTransport}.
     */
    static synchronized TCPNIOTransport getInstance() {
        if (defaultTransport == null) {
            final TCPNIOTransportBuilder builder = TCPNIOTransportBuilder.newInstance();
            // Determine which threading strategy to use, and total number of
            // threads.
            final String useWorkerThreadsStr =
                    System.getProperty("org.forgerock.opendj.transport.useWorkerThreads");
            final boolean useWorkerThreadStrategy;
            if (useWorkerThreadsStr != null) {
                useWorkerThreadStrategy = Boolean.parseBoolean(useWorkerThreadsStr);
            } else {
                // The most best performing strategy to use is the
                // SameThreadIOStrategy,
                // however it can only be used in cases where result listeners
                // will not
                // block.
                useWorkerThreadStrategy = true;
            }
  /**
   * Returns the default {@link TCPNIOTransport} which all
   * {@code LDAPConnectionFactory}s and {@code LDAPListener}s will use unless
   * otherwise specified in their options.
   *
   * @return The default {@link TCPNIOTransport}.
   */
  static synchronized TCPNIOTransport getInstance()
  {
    if (defaultTransport == null)
    {
      final TCPNIOTransportBuilder builder = TCPNIOTransportBuilder
          .newInstance();
            if (useWorkerThreadStrategy) {
                builder.setIOStrategy(WorkerThreadIOStrategy.getInstance());
            } else {
                builder.setIOStrategy(SameThreadIOStrategy.getInstance());
            }
      // Determine which threading strategy to use, and total number of threads.
      final String useWorkerThreadsStr = System
          .getProperty("org.forgerock.opendj.transport.useWorkerThreads");
      final boolean useWorkerThreadStrategy;
      if (useWorkerThreadsStr != null)
      {
        useWorkerThreadStrategy = Boolean
            .parseBoolean(useWorkerThreadsStr);
      }
      else
      {
        // The most best performing strategy to use is the SameThreadIOStrategy,
        // however it can only be used in cases where result listeners will not
        // block.
        useWorkerThreadStrategy = true;
      }
            // Calculate thread counts.
            final int cpus = Runtime.getRuntime().availableProcessors();
      if (useWorkerThreadStrategy)
      {
        builder.setIOStrategy(WorkerThreadIOStrategy.getInstance());
      }
      else
      {
        builder.setIOStrategy(SameThreadIOStrategy.getInstance());
      }
            // Calculate the number of selector threads.
            final String selectorsStr =
                    System.getProperty("org.forgerock.opendj.transport.selectors");
            final int selectorThreadCount;
      // Calculate thread counts.
      final int cpus = Runtime.getRuntime().availableProcessors();
            if (selectorsStr != null) {
                selectorThreadCount = Integer.parseInt(selectorsStr);
            } else {
                selectorThreadCount =
                        useWorkerThreadStrategy ? Math.max(2, cpus / 4) : Math.max(5,
                                (cpus / 2) - 1);
            }
      // Calculate the number of selector threads.
      final String selectorsStr = System
          .getProperty("org.forgerock.opendj.transport.selectors");
      final int selectorThreadCount;
            builder.getSelectorThreadPoolConfig().setCorePoolSize(selectorThreadCount)
                    .setMaxPoolSize(selectorThreadCount).setPoolName(
                            "OpenDJ LDAP SDK Grizzly selector thread");
      if (selectorsStr != null)
      {
        selectorThreadCount = Integer.parseInt(selectorsStr);
      }
      else
      {
        selectorThreadCount = useWorkerThreadStrategy ? Math.max(2,
            cpus / 4) : Math.max(5, (cpus / 2) - 1);
      }
            // Calculate the number of worker threads.
            if (builder.getWorkerThreadPoolConfig() != null) {
                final String workersStr =
                        System.getProperty("org.forgerock.opendj.transport.workers");
                final int workerThreadCount;
      builder.getSelectorThreadPoolConfig()
          .setCorePoolSize(selectorThreadCount)
          .setMaxPoolSize(selectorThreadCount)
          .setPoolName("OpenDJ LDAP SDK Grizzly selector thread");
                if (workersStr != null) {
                    workerThreadCount = Integer.parseInt(workersStr);
                } else {
                    workerThreadCount = useWorkerThreadStrategy ? Math.max(5, (cpus * 2)) : 0;
                }
      // Calculate the number of worker threads.
      if (builder.getWorkerThreadPoolConfig() != null)
      {
        final String workersStr = System
            .getProperty("org.forgerock.opendj.transport.workers");
        final int workerThreadCount;
                builder.getWorkerThreadPoolConfig().setCorePoolSize(workerThreadCount)
                        .setMaxPoolSize(workerThreadCount).setPoolName(
                                "OpenDJ LDAP SDK Grizzly worker thread");
            }
        if (workersStr != null)
        {
          workerThreadCount = Integer.parseInt(workersStr);
        }
        else
        {
          workerThreadCount = useWorkerThreadStrategy ? Math.max(5,
              (cpus * 2)) : 0;
            // Parse IO related options.
            final String lingerStr = System.getProperty("org.forgerock.opendj.transport.linger");
            if (lingerStr != null) {
                // Disabled by default.
                builder.setLinger(Integer.parseInt(lingerStr));
            }
            final String tcpNoDelayStr =
                    System.getProperty("org.forgerock.opendj.transport.tcpNoDelay");
            if (tcpNoDelayStr != null) {
                // Enabled by default.
                builder.setTcpNoDelay(Boolean.parseBoolean(tcpNoDelayStr));
            }
            final String reuseAddressStr =
                    System.getProperty("org.forgerock.opendj.transport.reuseAddress");
            if (reuseAddressStr != null) {
                // Enabled by default.
                builder.setReuseAddress(Boolean.parseBoolean(reuseAddressStr));
            }
            defaultTransport = builder.build();
            // FIXME: raise bug in Grizzly. We should not need to do this, but
            // failure
            // to do so causes many deadlocks.
            defaultTransport.setSelectorRunnersCount(selectorThreadCount);
            try {
                defaultTransport.start();
            } catch (final IOException e) {
                throw new RuntimeException(e);
            }
        }
        builder.getWorkerThreadPoolConfig()
            .setCorePoolSize(workerThreadCount)
            .setMaxPoolSize(workerThreadCount)
            .setPoolName("OpenDJ LDAP SDK Grizzly worker thread");
      }
      // Parse IO related options.
      final String lingerStr = System
          .getProperty("org.forgerock.opendj.transport.linger");
      if (lingerStr != null)
      {
        // Disabled by default.
        builder.setLinger(Integer.parseInt(lingerStr));
      }
      final String tcpNoDelayStr = System
          .getProperty("org.forgerock.opendj.transport.tcpNoDelay");
      if (tcpNoDelayStr != null)
      {
        // Enabled by default.
        builder.setTcpNoDelay(Boolean.parseBoolean(tcpNoDelayStr));
      }
      final String reuseAddressStr = System
          .getProperty("org.forgerock.opendj.transport.reuseAddress");
      if (reuseAddressStr != null)
      {
        // Enabled by default.
        builder
            .setReuseAddress(Boolean.parseBoolean(reuseAddressStr));
      }
      defaultTransport = builder.build();
      // FIXME: raise bug in Grizzly. We should not need to do this, but failure
      // to do so causes many deadlocks.
      defaultTransport.setSelectorRunnersCount(selectorThreadCount);
      try
      {
        defaultTransport.start();
      }
      catch (final IOException e)
      {
        throw new RuntimeException(e);
      }
        return defaultTransport;
    }
    return defaultTransport;
  }
  private DefaultTCPNIOTransport()
  {
    // Prevent instantiation.
  }
    private DefaultTCPNIOTransport() {
        // Prevent instantiation.
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/InternalConnection.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,316 +27,278 @@
package com.forgerock.opendj.ldap;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.requests.*;
import org.forgerock.opendj.ldap.responses.*;
import org.forgerock.opendj.ldap.AbstractAsynchronousConnection;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ConnectionEventListener;
import org.forgerock.opendj.ldap.FutureResult;
import org.forgerock.opendj.ldap.IntermediateResponseHandler;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.SearchResultHandler;
import org.forgerock.opendj.ldap.ServerConnection;
import org.forgerock.opendj.ldap.requests.AbandonRequest;
import org.forgerock.opendj.ldap.requests.AddRequest;
import org.forgerock.opendj.ldap.requests.BindRequest;
import org.forgerock.opendj.ldap.requests.CompareRequest;
import org.forgerock.opendj.ldap.requests.DeleteRequest;
import org.forgerock.opendj.ldap.requests.ExtendedRequest;
import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
import org.forgerock.opendj.ldap.requests.ModifyRequest;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.requests.UnbindRequest;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.CompareResult;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
import org.forgerock.opendj.ldap.responses.Responses;
import org.forgerock.opendj.ldap.responses.Result;
import com.forgerock.opendj.util.CompletedFutureResult;
import com.forgerock.opendj.util.Validator;
/**
 * This class defines a pseudo-connection object that can be used for performing
 * internal operations directly against a {@code ServerConnection}
 * implementation.
 */
public final class InternalConnection extends AbstractAsynchronousConnection
{
  private static final class InternalBindFutureResultImpl extends
      AbstractLDAPFutureResultImpl<BindResult>
  {
    private final BindRequest bindRequest;
public final class InternalConnection extends AbstractAsynchronousConnection {
    private static final class InternalBindFutureResultImpl extends
            AbstractLDAPFutureResultImpl<BindResult> {
        private final BindRequest bindRequest;
        InternalBindFutureResultImpl(final int messageID, final BindRequest bindRequest,
                final ResultHandler<? super BindResult> resultHandler,
                final IntermediateResponseHandler intermediateResponseHandler,
                final Connection connection) {
            super(messageID, resultHandler, intermediateResponseHandler, connection);
            this.bindRequest = bindRequest;
        }
        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder();
            sb.append("InternalBindFutureResultImpl(");
            sb.append("bindRequest = ");
            sb.append(bindRequest);
            super.toString(sb);
            sb.append(")");
            return sb.toString();
        }
    InternalBindFutureResultImpl(final int messageID,
        final BindRequest bindRequest,
        final ResultHandler<? super BindResult> resultHandler,
        final IntermediateResponseHandler intermediateResponseHandler,
        final Connection connection)
    {
      super(messageID, resultHandler, intermediateResponseHandler, connection);
      this.bindRequest = bindRequest;
        /**
         * {@inheritDoc}
         */
        @Override
        BindResult newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
                final Throwable cause) {
            return Responses.newBindResult(resultCode).setDiagnosticMessage(diagnosticMessage)
                    .setCause(cause);
        }
    }
    private final ServerConnection<Integer> serverConnection;
    private final List<ConnectionEventListener> listeners =
            new CopyOnWriteArrayList<ConnectionEventListener>();
    private final AtomicInteger messageID = new AtomicInteger();
    @Override
    public String toString()
    {
      final StringBuilder sb = new StringBuilder();
      sb.append("InternalBindFutureResultImpl(");
      sb.append("bindRequest = ");
      sb.append(bindRequest);
      super.toString(sb);
      sb.append(")");
      return sb.toString();
    /**
     * Sets the server connection associated with this internal connection.
     *
     * @param serverConnection
     *            The server connection.
     */
    public InternalConnection(final ServerConnection<Integer> serverConnection) {
        this.serverConnection = serverConnection;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    BindResult newErrorResult(final ResultCode resultCode,
        final String diagnosticMessage, final Throwable cause)
    {
      return Responses.newBindResult(resultCode)
          .setDiagnosticMessage(diagnosticMessage).setCause(cause);
    public FutureResult<Void> abandonAsync(final AbandonRequest request) {
        final int i = messageID.getAndIncrement();
        serverConnection.handleAbandon(i, request);
        return new CompletedFutureResult<Void>((Void) null, i);
    }
  }
    /**
     * {@inheritDoc}
     */
    @Override
    public FutureResult<Result> addAsync(final AddRequest request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final ResultHandler<? super Result> resultHandler) {
        final int i = messageID.getAndIncrement();
        final LDAPFutureResultImpl future =
                new LDAPFutureResultImpl(i, request, resultHandler, intermediateResponseHandler,
                        this);
        serverConnection.handleAdd(i, request, future, future);
        return future;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void addConnectionEventListener(final ConnectionEventListener listener) {
        Validator.ensureNotNull(listener);
        listeners.add(listener);
    }
  private final ServerConnection<Integer> serverConnection;
  private final List<ConnectionEventListener> listeners =
    new CopyOnWriteArrayList<ConnectionEventListener>();
  private final AtomicInteger messageID = new AtomicInteger();
    /**
     * {@inheritDoc}
     */
    @Override
    public FutureResult<BindResult> bindAsync(final BindRequest request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final ResultHandler<? super BindResult> resultHandler) {
        final int i = messageID.getAndIncrement();
        final InternalBindFutureResultImpl future =
                new InternalBindFutureResultImpl(i, request, resultHandler,
                        intermediateResponseHandler, this);
        serverConnection.handleBind(i, 3, request, future, future);
        return future;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void close(final UnbindRequest request, final String reason) {
        final int i = messageID.getAndIncrement();
        serverConnection.handleConnectionClosed(i, request);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public FutureResult<CompareResult> compareAsync(final CompareRequest request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final ResultHandler<? super CompareResult> resultHandler) {
        final int i = messageID.getAndIncrement();
        final LDAPCompareFutureResultImpl future =
                new LDAPCompareFutureResultImpl(i, request, resultHandler,
                        intermediateResponseHandler, this);
        serverConnection.handleCompare(i, request, future, future);
        return future;
    }
  /**
   * Sets the server connection associated with this internal connection.
   *
   * @param serverConnection
   *          The server connection.
   */
  public InternalConnection(final ServerConnection<Integer> serverConnection)
  {
    this.serverConnection = serverConnection;
  }
    /**
     * {@inheritDoc}
     */
    @Override
    public FutureResult<Result> deleteAsync(final DeleteRequest request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final ResultHandler<? super Result> resultHandler) {
        final int i = messageID.getAndIncrement();
        final LDAPFutureResultImpl future =
                new LDAPFutureResultImpl(i, request, resultHandler, intermediateResponseHandler,
                        this);
        serverConnection.handleDelete(i, request, future, future);
        return future;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(
            final ExtendedRequest<R> request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final ResultHandler<? super R> resultHandler) {
        final int i = messageID.getAndIncrement();
        final LDAPExtendedFutureResultImpl<R> future =
                new LDAPExtendedFutureResultImpl<R>(i, request, resultHandler,
                        intermediateResponseHandler, this);
        serverConnection.handleExtendedRequest(i, request, future, future);
        return future;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isClosed() {
        // FIXME: this should be true after close has been called.
        return false;
    }
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<Void> abandonAsync(final AbandonRequest request)
  {
    final int i = messageID.getAndIncrement();
    serverConnection.handleAbandon(i, request);
    return new CompletedFutureResult<Void>((Void) null, i);
  }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isValid() {
        // FIXME: this should be false if this connection is disconnected.
        return true;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public FutureResult<Result> modifyAsync(final ModifyRequest request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final ResultHandler<? super Result> resultHandler) {
        final int i = messageID.getAndIncrement();
        final LDAPFutureResultImpl future =
                new LDAPFutureResultImpl(i, request, resultHandler, intermediateResponseHandler,
                        this);
        serverConnection.handleModify(i, request, future, future);
        return future;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final ResultHandler<? super Result> resultHandler) {
        final int i = messageID.getAndIncrement();
        final LDAPFutureResultImpl future =
                new LDAPFutureResultImpl(i, request, resultHandler, intermediateResponseHandler,
                        this);
        serverConnection.handleModifyDN(i, request, future, future);
        return future;
    }
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<Result> addAsync(final AddRequest request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final ResultHandler<? super Result> resultHandler)
  {
    final int i = messageID.getAndIncrement();
    final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request,
        resultHandler, intermediateResponseHandler, this);
    serverConnection.handleAdd(i, request, future, future);
    return future;
  }
    /**
     * {@inheritDoc}
     */
    @Override
    public void removeConnectionEventListener(final ConnectionEventListener listener) {
        Validator.ensureNotNull(listener);
        listeners.remove(listener);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public FutureResult<Result> searchAsync(final SearchRequest request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final SearchResultHandler resultHandler) {
        final int i = messageID.getAndIncrement();
        final LDAPSearchFutureResultImpl future =
                new LDAPSearchFutureResultImpl(i, request, resultHandler,
                        intermediateResponseHandler, this);
        serverConnection.handleSearch(i, request, future, future);
        return future;
    }
  /**
   * {@inheritDoc}
   */
  @Override
  public void addConnectionEventListener(final ConnectionEventListener listener)
  {
    Validator.ensureNotNull(listener);
    listeners.add(listener);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<BindResult> bindAsync(final BindRequest request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final ResultHandler<? super BindResult> resultHandler)
  {
    final int i = messageID.getAndIncrement();
    final InternalBindFutureResultImpl future = new InternalBindFutureResultImpl(
        i, request, resultHandler, intermediateResponseHandler, this);
    serverConnection.handleBind(i, 3, request, future, future);
    return future;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void close(final UnbindRequest request, final String reason)
  {
    final int i = messageID.getAndIncrement();
    serverConnection.handleConnectionClosed(i, request);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<CompareResult> compareAsync(final CompareRequest request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final ResultHandler<? super CompareResult> resultHandler)
  {
    final int i = messageID.getAndIncrement();
    final LDAPCompareFutureResultImpl future = new LDAPCompareFutureResultImpl(
        i, request, resultHandler, intermediateResponseHandler, this);
    serverConnection.handleCompare(i, request, future, future);
    return future;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<Result> deleteAsync(final DeleteRequest request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final ResultHandler<? super Result> resultHandler)
  {
    final int i = messageID.getAndIncrement();
    final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request,
        resultHandler, intermediateResponseHandler, this);
    serverConnection.handleDelete(i, request, future, future);
    return future;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(
      final ExtendedRequest<R> request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final ResultHandler<? super R> resultHandler)
  {
    final int i = messageID.getAndIncrement();
    final LDAPExtendedFutureResultImpl<R> future = new LDAPExtendedFutureResultImpl<R>(
        i, request, resultHandler, intermediateResponseHandler, this);
    serverConnection.handleExtendedRequest(i, request, future, future);
    return future;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isClosed()
  {
    // FIXME: this should be true after close has been called.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isValid()
  {
    // FIXME: this should be false if this connection is disconnected.
    return true;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<Result> modifyAsync(final ModifyRequest request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final ResultHandler<? super Result> resultHandler)
  {
    final int i = messageID.getAndIncrement();
    final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request,
        resultHandler, intermediateResponseHandler, this);
    serverConnection.handleModify(i, request, future, future);
    return future;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final ResultHandler<? super Result> resultHandler)
  {
    final int i = messageID.getAndIncrement();
    final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request,
        resultHandler, intermediateResponseHandler, this);
    serverConnection.handleModifyDN(i, request, future, future);
    return future;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void removeConnectionEventListener(
      final ConnectionEventListener listener)
  {
    Validator.ensureNotNull(listener);
    listeners.remove(listener);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<Result> searchAsync(final SearchRequest request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final SearchResultHandler resultHandler)
  {
    final int i = messageID.getAndIncrement();
    final LDAPSearchFutureResultImpl future = new LDAPSearchFutureResultImpl(i,
        request, resultHandler, intermediateResponseHandler, this);
    serverConnection.handleSearch(i, request, future, future);
    return future;
  }
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    StringBuilder builder = new StringBuilder();
    builder.append("InternalConnection(");
    builder.append(String.valueOf(serverConnection));
    builder.append(')');
    return builder.toString();
  }
    /**
     * {@inheritDoc}
     */
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("InternalConnection(");
        builder.append(String.valueOf(serverConnection));
        builder.append(')');
        return builder.toString();
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPBindFutureResultImpl.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,8 +27,6 @@
package com.forgerock.opendj.ldap;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.IntermediateResponseHandler;
import org.forgerock.opendj.ldap.ResultCode;
@@ -38,68 +35,50 @@
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.Responses;
/**
 * Bind result future implementation.
 */
final class LDAPBindFutureResultImpl extends
    AbstractLDAPFutureResultImpl<BindResult>
{
  private final BindClient bindClient;
final class LDAPBindFutureResultImpl extends AbstractLDAPFutureResultImpl<BindResult> {
    private final BindClient bindClient;
    LDAPBindFutureResultImpl(final int requestID, final BindClient bindClient,
            final ResultHandler<? super BindResult> resultHandler,
            final IntermediateResponseHandler intermediateResponseHandler,
            final Connection connection) {
        super(requestID, resultHandler, intermediateResponseHandler, connection);
        this.bindClient = bindClient;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    protected boolean isCancelable() {
        return false;
    }
  LDAPBindFutureResultImpl(final int requestID, final BindClient bindClient,
      final ResultHandler<? super BindResult> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler,
      final Connection connection)
  {
    super(requestID, resultHandler, intermediateResponseHandler, connection);
    this.bindClient = bindClient;
  }
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("LDAPBindFutureResultImpl(");
        sb.append("bindClient = ");
        sb.append(bindClient);
        super.toString(sb);
        sb.append(")");
        return sb.toString();
    }
    BindClient getBindClient() {
        return bindClient;
    }
  /**
   * {@inheritDoc}
   */
  @Override
  protected boolean isCancelable() {
    return false;
  }
  @Override
  public String toString()
  {
    final StringBuilder sb = new StringBuilder();
    sb.append("LDAPBindFutureResultImpl(");
    sb.append("bindClient = ");
    sb.append(bindClient);
    super.toString(sb);
    sb.append(")");
    return sb.toString();
  }
  BindClient getBindClient()
  {
    return bindClient;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  BindResult newErrorResult(final ResultCode resultCode,
      final String diagnosticMessage, final Throwable cause)
  {
    return Responses.newBindResult(resultCode).setDiagnosticMessage(
        diagnosticMessage).setCause(cause);
  }
    /**
     * {@inheritDoc}
     */
    @Override
    BindResult newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
            final Throwable cause) {
        return Responses.newBindResult(resultCode).setDiagnosticMessage(diagnosticMessage)
                .setCause(cause);
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPClientFilter.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,8 +27,6 @@
package com.forgerock.opendj.ldap;
import static com.forgerock.opendj.ldap.LDAPConstants.OID_NOTICE_OF_DISCONNECTION;
import java.io.EOFException;
@@ -41,8 +38,21 @@
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.requests.*;
import org.forgerock.opendj.ldap.responses.*;
import org.forgerock.opendj.ldap.requests.AddRequest;
import org.forgerock.opendj.ldap.requests.BindClient;
import org.forgerock.opendj.ldap.requests.DeleteRequest;
import org.forgerock.opendj.ldap.requests.GenericBindRequest;
import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
import org.forgerock.opendj.ldap.requests.ModifyRequest;
import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.CompareResult;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
import org.forgerock.opendj.ldap.responses.IntermediateResponse;
import org.forgerock.opendj.ldap.responses.Responses;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.EmptyCompletionHandler;
@@ -52,593 +62,458 @@
import org.glassfish.grizzly.filterchain.FilterChainContext;
import org.glassfish.grizzly.filterchain.NextAction;
/**
 * Grizzly filter implementation for decoding LDAP responses and handling client
 * side logic for SSL and SASL operations over LDAP.
 */
final class LDAPClientFilter extends BaseFilter
{
  private static final Attribute<LDAPConnection> LDAP_CONNECTION_ATTR =
    Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPClientConnection");
  private static final Attribute<ASN1BufferReader> LDAP_ASN1_READER_ATTR =
    Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPASN1Reader");
final class LDAPClientFilter extends BaseFilter {
    private static final Attribute<LDAPConnection> LDAP_CONNECTION_ATTR =
            Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPClientConnection");
    private static final Attribute<ASN1BufferReader> LDAP_ASN1_READER_ATTR =
            Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPASN1Reader");
  private final int maxASN1ElementSize;
  private final LDAPReader ldapReader;
    private final int maxASN1ElementSize;
    private final LDAPReader ldapReader;
  private static final AbstractLDAPMessageHandler<FilterChainContext>
    CLIENT_RESPONSE_HANDLER = new AbstractLDAPMessageHandler<FilterChainContext>()
  {
    @Override
    public void addResult(final FilterChainContext ctx, final int messageID,
        final Result result) throws UnexpectedResponseException, IOException
    {
      final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if(ldapConnection != null)
      {
        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
            .removePendingRequest(messageID);
    private static final AbstractLDAPMessageHandler<FilterChainContext> CLIENT_RESPONSE_HANDLER =
            new AbstractLDAPMessageHandler<FilterChainContext>() {
                @Override
                public void addResult(final FilterChainContext ctx, final int messageID,
                        final Result result) throws UnexpectedResponseException, IOException {
                    final LDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
        if (pendingRequest != null)
        {
          if (pendingRequest instanceof LDAPFutureResultImpl)
          {
            final LDAPFutureResultImpl future =
                (LDAPFutureResultImpl) pendingRequest;
            if (future.getRequest() instanceof AddRequest)
            {
              future.setResultOrError(result);
              return;
            }
          }
          throw new UnexpectedResponseException(messageID, result);
        }
      }
    }
    @Override
    public void bindResult(final FilterChainContext ctx, final int messageID,
        final BindResult result) throws UnexpectedResponseException,
        IOException
    {
      final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if(ldapConnection != null)
      {
        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
            .removePendingRequest(messageID);
        if (pendingRequest != null)
        {
          if (pendingRequest instanceof LDAPBindFutureResultImpl)
          {
            final LDAPBindFutureResultImpl future =
                ((LDAPBindFutureResultImpl) pendingRequest);
            final BindClient bindClient = future.getBindClient();
            try
            {
              if (!bindClient.evaluateResult(result))
              {
                // The server is expecting a multi stage bind response.
                final int msgID =
                    ldapConnection.continuePendingBindRequest(future);
                final ASN1BufferWriter asn1Writer =
                    ASN1BufferWriter.getWriter();
                try
                {
                  final GenericBindRequest nextRequest = bindClient
                      .nextBindRequest();
                  new LDAPWriter().bindRequest(asn1Writer, msgID, 3,
                      nextRequest);
                  ctx.write(asn1Writer.getBuffer(), null);
                        if (pendingRequest != null) {
                            if (pendingRequest instanceof LDAPFutureResultImpl) {
                                final LDAPFutureResultImpl future =
                                        (LDAPFutureResultImpl) pendingRequest;
                                if (future.getRequest() instanceof AddRequest) {
                                    future.setResultOrError(result);
                                    return;
                                }
                            }
                            throw new UnexpectedResponseException(messageID, result);
                        }
                    }
                }
                finally
                {
                  asn1Writer.recycle();
                @Override
                public void bindResult(final FilterChainContext ctx, final int messageID,
                        final BindResult result) throws UnexpectedResponseException, IOException {
                    final LDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
                        if (pendingRequest != null) {
                            if (pendingRequest instanceof LDAPBindFutureResultImpl) {
                                final LDAPBindFutureResultImpl future =
                                        ((LDAPBindFutureResultImpl) pendingRequest);
                                final BindClient bindClient = future.getBindClient();
                                try {
                                    if (!bindClient.evaluateResult(result)) {
                                        // The server is expecting a multi stage
                                        // bind response.
                                        final int msgID =
                                                ldapConnection.continuePendingBindRequest(future);
                                        final ASN1BufferWriter asn1Writer =
                                                ASN1BufferWriter.getWriter();
                                        try {
                                            final GenericBindRequest nextRequest =
                                                    bindClient.nextBindRequest();
                                            new LDAPWriter().bindRequest(asn1Writer, msgID, 3,
                                                    nextRequest);
                                            ctx.write(asn1Writer.getBuffer(), null);
                                        } finally {
                                            asn1Writer.recycle();
                                        }
                                        return;
                                    }
                                } catch (final ErrorResultException e) {
                                    future.adaptErrorResult(e.getResult());
                                    return;
                                } catch (final IOException e) {
                                    // FIXME: I18N need to have a better error
                                    // message.
                                    // FIXME: Is this the best result code?
                                    final Result errorResult =
                                            Responses
                                                    .newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
                                                    .setDiagnosticMessage(
                                                            "An error occurred during multi-stage authentication")
                                                    .setCause(e);
                                    future.adaptErrorResult(errorResult);
                                    return;
                                }
                                if (result.getResultCode() == ResultCode.SUCCESS) {
                                    final ConnectionSecurityLayer l =
                                            bindClient.getConnectionSecurityLayer();
                                    if (l != null) {
                                        // The connection needs to be secured by
                                        // the SASL
                                        // mechanism.
                                        ldapConnection
                                                .installFilter(new ConnectionSecurityLayerFilter(l,
                                                        ctx.getConnection().getTransport()
                                                                .getMemoryManager()));
                                    }
                                }
                                ldapConnection.setBindOrStartTLSInProgress(false);
                                future.setResultOrError(result);
                                return;
                            }
                            throw new UnexpectedResponseException(messageID, result);
                        }
                    }
                }
                return;
              }
            }
            catch (final ErrorResultException e)
            {
              future.adaptErrorResult(e.getResult());
              return;
            }
            catch (final IOException e)
            {
              // FIXME: I18N need to have a better error message.
              // FIXME: Is this the best result code?
              final Result errorResult = Responses.newResult(
                  ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
                  "An error occurred during multi-stage authentication")
                  .setCause(e);
              future.adaptErrorResult(errorResult);
              return;
            }
            if (result.getResultCode() == ResultCode.SUCCESS)
            {
              final ConnectionSecurityLayer l = bindClient
                  .getConnectionSecurityLayer();
              if (l != null)
              {
                // The connection needs to be secured by the SASL
                // mechanism.
                ldapConnection.installFilter(new ConnectionSecurityLayerFilter(l, ctx
                    .getConnection().getTransport().getMemoryManager()));
              }
            }
                @Override
                public void compareResult(final FilterChainContext ctx, final int messageID,
                        final CompareResult result) throws UnexpectedResponseException, IOException {
                    final LDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
            ldapConnection.setBindOrStartTLSInProgress(false);
            future.setResultOrError(result);
            return;
          }
          throw new UnexpectedResponseException(messageID, result);
        }
      }
    }
                        if (pendingRequest != null) {
                            if (pendingRequest instanceof LDAPCompareFutureResultImpl) {
                                final LDAPCompareFutureResultImpl future =
                                        (LDAPCompareFutureResultImpl) pendingRequest;
                                future.setResultOrError(result);
                                return;
                            }
                            throw new UnexpectedResponseException(messageID, result);
                        }
                    }
                }
                @Override
                public void deleteResult(final FilterChainContext ctx, final int messageID,
                        final Result result) throws UnexpectedResponseException, IOException {
                    final LDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
                        if (pendingRequest != null) {
                            if (pendingRequest instanceof LDAPFutureResultImpl) {
                                final LDAPFutureResultImpl future =
                                        (LDAPFutureResultImpl) pendingRequest;
                                if (future.getRequest() instanceof DeleteRequest) {
                                    future.setResultOrError(result);
                                    return;
                                }
                            }
                            throw new UnexpectedResponseException(messageID, result);
                        }
                    }
                }
    @Override
    public void compareResult(final FilterChainContext ctx,
        final int messageID, final CompareResult result)
        throws UnexpectedResponseException, IOException
    {
      final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if(ldapConnection != null)
      {
        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
            .removePendingRequest(messageID);
                @Override
                public void extendedResult(final FilterChainContext ctx, final int messageID,
                        final ExtendedResult result) throws UnexpectedResponseException,
                        IOException {
                    final LDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (ldapConnection != null) {
                        if (messageID == 0) {
                            if ((result.getOID() != null)
                                    && result.getOID().equals(OID_NOTICE_OF_DISCONNECTION)) {
        if (pendingRequest != null)
        {
          if (pendingRequest instanceof LDAPCompareFutureResultImpl)
          {
            final LDAPCompareFutureResultImpl future =
                (LDAPCompareFutureResultImpl) pendingRequest;
            future.setResultOrError(result);
            return;
          }
          throw new UnexpectedResponseException(messageID, result);
        }
      }
    }
                                final Result errorResult =
                                        Responses
                                                .newResult(result.getResultCode())
                                                .setDiagnosticMessage(result.getDiagnosticMessage());
                                ldapConnection.close(null, true, errorResult);
                                return;
                            } else {
                                // Unsolicited notification received.
                                ldapConnection.handleUnsolicitedNotification(result);
                            }
                        }
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
                        if (pendingRequest != null) {
                            if (pendingRequest instanceof LDAPExtendedFutureResultImpl<?>) {
                                final LDAPExtendedFutureResultImpl<?> extendedFuture =
                                        ((LDAPExtendedFutureResultImpl<?>) pendingRequest);
                                try {
                                    handleExtendedResult0(ldapConnection, extendedFuture, result);
                                } catch (final DecodeException de) {
                                    // FIXME: should the connection be closed as
                                    // well?
                                    final Result errorResult =
                                            Responses.newResult(
                                                    ResultCode.CLIENT_SIDE_DECODING_ERROR)
                                                    .setDiagnosticMessage(de.getLocalizedMessage())
                                                    .setCause(de);
                                    extendedFuture.adaptErrorResult(errorResult);
                                }
                            } else {
                                throw new UnexpectedResponseException(messageID, result);
                            }
                        }
                    }
                }
    @Override
    public void deleteResult(final FilterChainContext ctx, final int messageID,
        final Result result) throws UnexpectedResponseException, IOException
    {
      final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if(ldapConnection != null)
      {
        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
            .removePendingRequest(messageID);
                @Override
                public void intermediateResponse(final FilterChainContext ctx, final int messageID,
                        final IntermediateResponse response) throws UnexpectedResponseException,
                        IOException {
                    final LDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.getPendingRequest(messageID);
        if (pendingRequest != null)
        {
          if (pendingRequest instanceof LDAPFutureResultImpl)
          {
            final LDAPFutureResultImpl future =
                (LDAPFutureResultImpl) pendingRequest;
            if (future.getRequest() instanceof DeleteRequest)
            {
              future.setResultOrError(result);
              return;
            }
          }
          throw new UnexpectedResponseException(messageID, result);
        }
      }
    }
                        if (pendingRequest != null) {
                            pendingRequest.handleIntermediateResponse(response);
                        }
                    }
                }
                @Override
                public void modifyDNResult(final FilterChainContext ctx, final int messageID,
                        final Result result) throws UnexpectedResponseException, IOException {
                    final LDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
                        if (pendingRequest != null) {
                            if (pendingRequest instanceof LDAPFutureResultImpl) {
                                final LDAPFutureResultImpl future =
                                        (LDAPFutureResultImpl) pendingRequest;
                                if (future.getRequest() instanceof ModifyDNRequest) {
                                    future.setResultOrError(result);
                                    return;
                                }
                            }
                            throw new UnexpectedResponseException(messageID, result);
                        }
                    }
                }
    @Override
    public void extendedResult(final FilterChainContext ctx,
        final int messageID, final ExtendedResult result)
        throws UnexpectedResponseException, IOException
    {
      final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if(ldapConnection != null)
      {
        if (messageID == 0)
        {
          if ((result.getOID() != null)
              && result.getOID().equals(OID_NOTICE_OF_DISCONNECTION))
          {
                @Override
                public void modifyResult(final FilterChainContext ctx, final int messageID,
                        final Result result) throws UnexpectedResponseException, IOException {
                    final LDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
            final Result errorResult = Responses
                .newResult(result.getResultCode()).setDiagnosticMessage(
                    result.getDiagnosticMessage());
            ldapConnection.close(null, true, errorResult);
            return;
          }
          else
          {
            // Unsolicited notification received.
            ldapConnection.handleUnsolicitedNotification(result);
          }
        }
                        if (pendingRequest != null) {
                            if (pendingRequest instanceof LDAPFutureResultImpl) {
                                final LDAPFutureResultImpl future =
                                        (LDAPFutureResultImpl) pendingRequest;
                                if (future.getRequest() instanceof ModifyRequest) {
                                    future.setResultOrError(result);
                                    return;
                                }
                            }
                            throw new UnexpectedResponseException(messageID, result);
                        }
                    }
                }
        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
            .removePendingRequest(messageID);
                @Override
                public void searchResult(final FilterChainContext ctx, final int messageID,
                        final Result result) throws UnexpectedResponseException, IOException {
                    final LDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
        if(pendingRequest != null)
        {
          if (pendingRequest instanceof LDAPExtendedFutureResultImpl<?>)
          {
            final LDAPExtendedFutureResultImpl<?> extendedFuture =
              ((LDAPExtendedFutureResultImpl<?>) pendingRequest);
            try
            {
              handleExtendedResult0(ldapConnection, extendedFuture, result);
            }
            catch (final DecodeException de)
            {
              // FIXME: should the connection be closed as well?
              final Result errorResult = Responses.newResult(
                  ResultCode.CLIENT_SIDE_DECODING_ERROR).setDiagnosticMessage(
                  de.getLocalizedMessage()).setCause(de);
              extendedFuture.adaptErrorResult(errorResult);
            }
          }
          else
          {
            throw new UnexpectedResponseException(messageID, result);
          }
        }
      }
    }
                        if (pendingRequest != null) {
                            if (pendingRequest instanceof LDAPSearchFutureResultImpl) {
                                ((LDAPSearchFutureResultImpl) pendingRequest)
                                        .setResultOrError(result);
                            } else {
                                throw new UnexpectedResponseException(messageID, result);
                            }
                        }
                    }
                }
                @Override
                public void searchResultEntry(final FilterChainContext ctx, final int messageID,
                        final SearchResultEntry entry) throws UnexpectedResponseException,
                        IOException {
                    final LDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.getPendingRequest(messageID);
                        if (pendingRequest != null) {
                            if (pendingRequest instanceof LDAPSearchFutureResultImpl) {
                                ((LDAPSearchFutureResultImpl) pendingRequest).handleEntry(entry);
                            } else {
                                throw new UnexpectedResponseException(messageID, entry);
                            }
                        }
                    }
                }
    @Override
    public void intermediateResponse(final FilterChainContext ctx,
        final int messageID, final IntermediateResponse response)
        throws UnexpectedResponseException, IOException
    {
      final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if(ldapConnection != null)
      {
        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
            .getPendingRequest(messageID);
                @Override
                public void searchResultReference(final FilterChainContext ctx,
                        final int messageID, final SearchResultReference reference)
                        throws UnexpectedResponseException, IOException {
                    final LDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.getPendingRequest(messageID);
        if (pendingRequest != null)
        {
          pendingRequest.handleIntermediateResponse(response);
        }
      }
    }
                        if (pendingRequest != null) {
                            if (pendingRequest instanceof LDAPSearchFutureResultImpl) {
                                ((LDAPSearchFutureResultImpl) pendingRequest)
                                        .handleReference(reference);
                            } else {
                                throw new UnexpectedResponseException(messageID, reference);
                            }
                        }
                    }
                }
                // Needed in order to expose type information.
                private <R extends ExtendedResult> void handleExtendedResult0(
                        final LDAPConnection conn, final LDAPExtendedFutureResultImpl<R> future,
                        final ExtendedResult result) throws DecodeException {
                    final R decodedResponse =
                            future.decodeResult(result, conn.getLDAPOptions().getDecodeOptions());
                    if (future.getRequest() instanceof StartTLSExtendedRequest) {
                        if (result.getResultCode() == ResultCode.SUCCESS) {
                            try {
                                final StartTLSExtendedRequest request =
                                        (StartTLSExtendedRequest) future.getRequest();
                                conn.startTLS(request.getSSLContext(), request
                                        .getEnabledProtocols(), request.getEnabledCipherSuites(),
                                        new EmptyCompletionHandler<SSLEngine>() {
                                            @Override
                                            public void completed(final SSLEngine result) {
                                                conn.setBindOrStartTLSInProgress(false);
                                                future.setResultOrError(decodedResponse);
                                            }
    @Override
    public void modifyDNResult(final FilterChainContext ctx,
        final int messageID, final Result result)
        throws UnexpectedResponseException, IOException
    {
      final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if(ldapConnection != null)
      {
        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
            .removePendingRequest(messageID);
                                            @Override
                                            public void failed(final Throwable throwable) {
                                                final Result errorResult =
                                                        Responses.newResult(
                                                                ResultCode.CLIENT_SIDE_LOCAL_ERROR)
                                                                .setCause(throwable)
                                                                .setDiagnosticMessage(
                                                                        "SSL handshake failed");
                                                conn.setBindOrStartTLSInProgress(false);
                                                conn.close(null, false, errorResult);
                                                future.adaptErrorResult(errorResult);
                                            }
                                        });
                                return;
                            } catch (final IOException e) {
                                final Result errorResult =
                                        Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
                                                .setCause(e).setDiagnosticMessage(e.getMessage());
                                future.adaptErrorResult(errorResult);
                                conn.close(null, false, errorResult);
                                return;
                            }
                        }
                    }
        if (pendingRequest != null)
        {
          if (pendingRequest instanceof LDAPFutureResultImpl)
          {
            final LDAPFutureResultImpl future =
                (LDAPFutureResultImpl) pendingRequest;
            if (future.getRequest() instanceof ModifyDNRequest)
            {
              future.setResultOrError(result);
              return;
            }
          }
          throw new UnexpectedResponseException(messageID, result);
        }
      }
    }
    @Override
    public void modifyResult(final FilterChainContext ctx, final int messageID,
        final Result result) throws UnexpectedResponseException, IOException
    {
      final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if(ldapConnection != null)
      {
        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
            .removePendingRequest(messageID);
        if (pendingRequest != null)
        {
          if (pendingRequest instanceof LDAPFutureResultImpl)
          {
            final LDAPFutureResultImpl future =
                (LDAPFutureResultImpl) pendingRequest;
            if (future.getRequest() instanceof ModifyRequest)
            {
              future.setResultOrError(result);
              return;
            }
          }
          throw new UnexpectedResponseException(messageID, result);
        }
      }
    }
    @Override
    public void searchResult(final FilterChainContext ctx, final int messageID,
        final Result result) throws UnexpectedResponseException, IOException
    {
      final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if(ldapConnection != null)
      {
        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
            .removePendingRequest(messageID);
        if (pendingRequest != null)
        {
          if (pendingRequest instanceof LDAPSearchFutureResultImpl)
          {
            ((LDAPSearchFutureResultImpl) pendingRequest)
                .setResultOrError(result);
          }
          else
          {
            throw new UnexpectedResponseException(messageID, result);
          }
        }
      }
    }
    @Override
    public void searchResultEntry(final FilterChainContext ctx,
        final int messageID, final SearchResultEntry entry)
        throws UnexpectedResponseException, IOException
    {
      final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if(ldapConnection != null)
      {
        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
            .getPendingRequest(messageID);
        if (pendingRequest != null)
        {
          if (pendingRequest instanceof LDAPSearchFutureResultImpl)
          {
            ((LDAPSearchFutureResultImpl) pendingRequest).handleEntry(entry);
          }
          else
          {
            throw new UnexpectedResponseException(messageID, entry);
          }
        }
      }
    }
    @Override
    public void searchResultReference(final FilterChainContext ctx,
        final int messageID, final SearchResultReference reference)
        throws UnexpectedResponseException, IOException
    {
      final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if(ldapConnection != null)
      {
        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
            .getPendingRequest(messageID);
        if (pendingRequest != null)
        {
          if (pendingRequest instanceof LDAPSearchFutureResultImpl)
          {
            ((LDAPSearchFutureResultImpl) pendingRequest)
                .handleReference(reference);
          }
          else
          {
            throw new UnexpectedResponseException(messageID, reference);
          }
        }
      }
    }
    // Needed in order to expose type information.
    private <R extends ExtendedResult> void handleExtendedResult0(
        final LDAPConnection conn,
        final LDAPExtendedFutureResultImpl<R> future,
        final ExtendedResult result) throws DecodeException
    {
      final R decodedResponse = future.decodeResult(result, conn
          .getLDAPOptions().getDecodeOptions());
      if (future.getRequest() instanceof StartTLSExtendedRequest)
      {
        if (result.getResultCode() == ResultCode.SUCCESS)
        {
          try
          {
            final StartTLSExtendedRequest request = (StartTLSExtendedRequest) future
                .getRequest();
            conn.startTLS(request.getSSLContext(),
                request.getEnabledProtocols(), request.getEnabledCipherSuites(),
                new EmptyCompletionHandler<SSLEngine>()
                {
                  @Override
                  public void completed(final SSLEngine result)
                  {
                    conn.setBindOrStartTLSInProgress(false);
                    future.setResultOrError(decodedResponse);
                  }
                }
            };
    LDAPClientFilter(final LDAPReader ldapReader, final int maxASN1ElementSize) {
        this.ldapReader = ldapReader;
        this.maxASN1ElementSize = maxASN1ElementSize;
    }
                  @Override
                  public void failed(final Throwable throwable)
                  {
                    final Result errorResult = Responses.newResult(
                        ResultCode.CLIENT_SIDE_LOCAL_ERROR).setCause(throwable)
                        .setDiagnosticMessage("SSL handshake failed");
                    conn.setBindOrStartTLSInProgress(false);
                    conn.close(null, false, errorResult);
                    future.adaptErrorResult(errorResult);
                  }
                });
    @Override
    public void exceptionOccurred(final FilterChainContext ctx, final Throwable error) {
        final Connection<?> connection = ctx.getConnection();
        if (!connection.isOpen()) {
            // Grizzly doens't not deregister the read interest from the
            // selector so closing the connection results in an
            // EOFException.
            // Just ignore errors on closed connections.
            return;
          }
          catch (final IOException e)
          {
            final Result errorResult = Responses.newResult(
                ResultCode.CLIENT_SIDE_LOCAL_ERROR).setCause(e)
                .setDiagnosticMessage(e.getMessage());
            future.adaptErrorResult(errorResult);
            conn.close(null, false, errorResult);
            return;
          }
        }
      }
        final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(connection);
      future.setResultOrError(decodedResponse);
    }
  };
  LDAPClientFilter(final LDAPReader ldapReader, final int maxASN1ElementSize)
  {
    this.ldapReader = ldapReader;
    this.maxASN1ElementSize = maxASN1ElementSize;
  }
  @Override
  public void exceptionOccurred(final FilterChainContext ctx,
      final Throwable error)
  {
    final Connection<?> connection = ctx.getConnection();
    if (!connection.isOpen())
    {
      // Grizzly doens't not deregister the read interest from the
      // selector so closing the connection results in an
      // EOFException.
      // Just ignore errors on closed connections.
      return;
    }
    final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(connection);
    Result errorResult;
    if (error instanceof EOFException)
    {
      // FIXME: Is this the best result code?
      errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_SERVER_DOWN)
          .setCause(error);
    }
    else
    {
      // FIXME: what other sort of IOExceptions can be thrown?
      // FIXME: Is this the best result code?
      errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
          .setCause(error);
    }
    ldapConnection.close(null, false, errorResult);
  }
  @Override
  public NextAction handleClose(final FilterChainContext ctx)
      throws IOException
  {
    final Connection<?> connection = ctx.getConnection();
    final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR
        .remove(connection);
    if (ldapConnection != null)
    {
      TimeoutChecker.INSTANCE.removeConnection(ldapConnection);
      final Result errorResult = Responses
          .newResult(ResultCode.CLIENT_SIDE_SERVER_DOWN);
      ldapConnection.close(null, false, errorResult);
    }
    return ctx.getInvokeAction();
  }
  @Override
  public NextAction handleRead(final FilterChainContext ctx) throws IOException
  {
    final Buffer buffer = (Buffer) ctx.getMessage();
    ASN1BufferReader asn1Reader = LDAP_ASN1_READER_ATTR
        .get(ctx.getConnection());
    if (asn1Reader == null)
    {
      asn1Reader = new ASN1BufferReader(maxASN1ElementSize, ctx.getConnection()
          .getTransport().getMemoryManager());
      LDAP_ASN1_READER_ATTR.set(ctx.getConnection(), asn1Reader);
    }
    asn1Reader.appendBytesRead(buffer);
    try
    {
      while (asn1Reader.elementAvailable())
      {
        ldapReader.decode(asn1Reader, CLIENT_RESPONSE_HANDLER, ctx);
      }
    }
    catch(IOException ioe)
    {
      final LDAPConnection ldapConnection =
          LDAP_CONNECTION_ATTR.get(ctx.getConnection());
      final Result errorResult =
          Responses.newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR)
              .setCause(ioe).setDiagnosticMessage(ioe.getMessage());
      ldapConnection.close(null, false, errorResult);
      throw ioe;
    }
    finally
    {
      asn1Reader.disposeBytesRead();
        Result errorResult;
        if (error instanceof EOFException) {
            // FIXME: Is this the best result code?
            errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_SERVER_DOWN).setCause(error);
        } else {
            // FIXME: what other sort of IOExceptions can be thrown?
            // FIXME: Is this the best result code?
            errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR).setCause(error);
        }
        ldapConnection.close(null, false, errorResult);
    }
    return ctx.getStopAction();
  }
    @Override
    public NextAction handleClose(final FilterChainContext ctx) throws IOException {
        final Connection<?> connection = ctx.getConnection();
        final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.remove(connection);
        if (ldapConnection != null) {
            TimeoutChecker.INSTANCE.removeConnection(ldapConnection);
            final Result errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_SERVER_DOWN);
            ldapConnection.close(null, false, errorResult);
        }
        return ctx.getInvokeAction();
    }
    @Override
    public NextAction handleRead(final FilterChainContext ctx) throws IOException {
        final Buffer buffer = (Buffer) ctx.getMessage();
        ASN1BufferReader asn1Reader = LDAP_ASN1_READER_ATTR.get(ctx.getConnection());
        if (asn1Reader == null) {
            asn1Reader =
                    new ASN1BufferReader(maxASN1ElementSize, ctx.getConnection().getTransport()
                            .getMemoryManager());
            LDAP_ASN1_READER_ATTR.set(ctx.getConnection(), asn1Reader);
        }
        asn1Reader.appendBytesRead(buffer);
        try {
            while (asn1Reader.elementAvailable()) {
                ldapReader.decode(asn1Reader, CLIENT_RESPONSE_HANDLER, ctx);
            }
        } catch (IOException ioe) {
            final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx.getConnection());
            final Result errorResult =
                    Responses.newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR).setCause(ioe)
                            .setDiagnosticMessage(ioe.getMessage());
            ldapConnection.close(null, false, errorResult);
            throw ioe;
        } finally {
            asn1Reader.disposeBytesRead();
        }
  void registerConnection(final Connection<?> connection,
      final LDAPConnection ldapConnection)
  {
    TimeoutChecker.INSTANCE.addConnection(ldapConnection);
    LDAP_CONNECTION_ATTR.set(connection, ldapConnection);
  }
        return ctx.getStopAction();
    }
    void registerConnection(final Connection<?> connection, final LDAPConnection ldapConnection) {
        TimeoutChecker.INSTANCE.addConnection(ldapConnection);
        LDAP_CONNECTION_ATTR.set(connection, ldapConnection);
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPCompareFutureResultImpl.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,8 +27,6 @@
package com.forgerock.opendj.ldap;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.IntermediateResponseHandler;
import org.forgerock.opendj.ldap.ResultCode;
@@ -38,59 +35,42 @@
import org.forgerock.opendj.ldap.responses.CompareResult;
import org.forgerock.opendj.ldap.responses.Responses;
/**
 * Compare result future implementation.
 */
final class LDAPCompareFutureResultImpl extends
    AbstractLDAPFutureResultImpl<CompareResult>
{
  private final CompareRequest request;
final class LDAPCompareFutureResultImpl extends AbstractLDAPFutureResultImpl<CompareResult> {
    private final CompareRequest request;
    LDAPCompareFutureResultImpl(final int requestID, final CompareRequest request,
            final ResultHandler<? super CompareResult> resultHandler,
            final IntermediateResponseHandler intermediateResponseHandler,
            final Connection connection) {
        super(requestID, resultHandler, intermediateResponseHandler, connection);
        this.request = request;
    }
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("LDAPCompareFutureResultImpl(");
        sb.append("request = ");
        sb.append(request);
        super.toString(sb);
        sb.append(")");
        return sb.toString();
    }
  LDAPCompareFutureResultImpl(final int requestID,
      final CompareRequest request,
      final ResultHandler<? super CompareResult> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler,
      final Connection connection)
  {
    super(requestID, resultHandler, intermediateResponseHandler, connection);
    this.request = request;
  }
    CompareRequest getRequest() {
        return request;
    }
  @Override
  public String toString()
  {
    final StringBuilder sb = new StringBuilder();
    sb.append("LDAPCompareFutureResultImpl(");
    sb.append("request = ");
    sb.append(request);
    super.toString(sb);
    sb.append(")");
    return sb.toString();
  }
  CompareRequest getRequest()
  {
    return request;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  CompareResult newErrorResult(final ResultCode resultCode,
      final String diagnosticMessage, final Throwable cause)
  {
    return Responses.newCompareResult(resultCode).setDiagnosticMessage(
        diagnosticMessage).setCause(cause);
  }
    /**
     * {@inheritDoc}
     */
    @Override
    CompareResult newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
            final Throwable cause) {
        return Responses.newCompareResult(resultCode).setDiagnosticMessage(diagnosticMessage)
                .setCause(cause);
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnection.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,8 +27,6 @@
package com.forgerock.opendj.ldap;
import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
import java.io.IOException;
@@ -44,9 +41,35 @@
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.requests.*;
import org.forgerock.opendj.ldap.responses.*;
import org.forgerock.opendj.ldap.AbstractAsynchronousConnection;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ConnectionEventListener;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.FutureResult;
import org.forgerock.opendj.ldap.IntermediateResponseHandler;
import org.forgerock.opendj.ldap.LDAPOptions;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.SearchResultHandler;
import org.forgerock.opendj.ldap.requests.AbandonRequest;
import org.forgerock.opendj.ldap.requests.AddRequest;
import org.forgerock.opendj.ldap.requests.BindClient;
import org.forgerock.opendj.ldap.requests.BindRequest;
import org.forgerock.opendj.ldap.requests.CompareRequest;
import org.forgerock.opendj.ldap.requests.DeleteRequest;
import org.forgerock.opendj.ldap.requests.ExtendedRequest;
import org.forgerock.opendj.ldap.requests.GenericBindRequest;
import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
import org.forgerock.opendj.ldap.requests.ModifyRequest;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
import org.forgerock.opendj.ldap.requests.UnbindRequest;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.CompareResult;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
import org.forgerock.opendj.ldap.responses.Responses;
import org.forgerock.opendj.ldap.responses.Result;
import org.glassfish.grizzly.CompletionHandler;
import org.glassfish.grizzly.filterchain.Filter;
import org.glassfish.grizzly.filterchain.FilterChain;
@@ -58,1041 +81,807 @@
import com.forgerock.opendj.util.StaticUtils;
import com.forgerock.opendj.util.Validator;
/**
 * LDAP connection implementation.
 * <p>
 * TODO: handle illegal state exceptions.
 */
final class LDAPConnection extends AbstractAsynchronousConnection implements
    Connection
{
  private final org.glassfish.grizzly.Connection<?> connection;
  private Result connectionInvalidReason;
  private boolean isClosed = false;
  private final List<ConnectionEventListener> listeners =
      new CopyOnWriteArrayList<ConnectionEventListener>();
  private final AtomicInteger nextMsgID = new AtomicInteger(1);
  private final AtomicBoolean bindOrStartTLSInProgress = new AtomicBoolean(
      false);
  private final ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>> pendingRequests =
      new ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>>();
  private final Object stateLock = new Object();
  private final LDAPWriter ldapWriter = new LDAPWriter();
  private final LDAPOptions options;
final class LDAPConnection extends AbstractAsynchronousConnection implements Connection {
    private final org.glassfish.grizzly.Connection<?> connection;
    private Result connectionInvalidReason;
    private boolean isClosed = false;
    private final List<ConnectionEventListener> listeners =
            new CopyOnWriteArrayList<ConnectionEventListener>();
    private final AtomicInteger nextMsgID = new AtomicInteger(1);
    private final AtomicBoolean bindOrStartTLSInProgress = new AtomicBoolean(false);
    private final ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>> pendingRequests =
            new ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>>();
    private final Object stateLock = new Object();
    private final LDAPWriter ldapWriter = new LDAPWriter();
    private final LDAPOptions options;
  /**
   * Creates a new LDAP connection.
   *
   * @param connection
   *          The Grizzly connection.
   * @param options
   *          The LDAP client options.
   */
  LDAPConnection(final org.glassfish.grizzly.Connection<?> connection,
      final LDAPOptions options)
  {
    this.connection = connection;
    this.options = options;
  }
  /**
   * {@inheritDoc}
   */
  public FutureResult<Void> abandonAsync(final AbandonRequest request)
  {
    final AbstractLDAPFutureResultImpl<?> pendingRequest;
    final int messageID = nextMsgID.getAndIncrement();
    synchronized (stateLock)
    {
      if (connectionInvalidReason != null)
      {
        return new CompletedFutureResult<Void>(
            newErrorResult(connectionInvalidReason), messageID);
      }
      if (bindOrStartTLSInProgress.get())
      {
        final Result errorResult = Responses.newResult(
            ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
            "Bind or Start TLS operation in progress");
        return new CompletedFutureResult<Void>(newErrorResult(errorResult),
            messageID);
      }
      // First remove the future associated with the request to be abandoned.
      pendingRequest = pendingRequests.remove(request.getRequestID());
    /**
     * Creates a new LDAP connection.
     *
     * @param connection
     *            The Grizzly connection.
     * @param options
     *            The LDAP client options.
     */
    LDAPConnection(final org.glassfish.grizzly.Connection<?> connection, final LDAPOptions options) {
        this.connection = connection;
        this.options = options;
    }
    if (pendingRequest == null)
    {
      // There has never been a request with the specified message ID or the
      // response has already been received and handled. We can ignore this
      // abandon request.
    /**
     * {@inheritDoc}
     */
    public FutureResult<Void> abandonAsync(final AbandonRequest request) {
        final AbstractLDAPFutureResultImpl<?> pendingRequest;
        final int messageID = nextMsgID.getAndIncrement();
      // Message ID will be -1 since no request was sent.
      return new CompletedFutureResult<Void>((Void) null);
    }
        synchronized (stateLock) {
            if (connectionInvalidReason != null) {
                return new CompletedFutureResult<Void>(newErrorResult(connectionInvalidReason),
                        messageID);
            }
            if (bindOrStartTLSInProgress.get()) {
                final Result errorResult =
                        Responses.newResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
                                "Bind or Start TLS operation in progress");
                return new CompletedFutureResult<Void>(newErrorResult(errorResult), messageID);
            }
    pendingRequest.cancel(false);
    try
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        ldapWriter.abandonRequest(asn1Writer, messageID, request);
        connection.write(asn1Writer.getBuffer(), null);
        return new CompletedFutureResult<Void>((Void) null, messageID);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
    catch (final IOException e)
    {
      // FIXME: what other sort of IOExceptions can be thrown?
      // FIXME: Is this the best result code?
      final Result errorResult = Responses.newResult(
          ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
      connectionErrorOccurred(errorResult);
      return new CompletedFutureResult<Void>(newErrorResult(errorResult),
          messageID);
    }
  }
  /**
   * {@inheritDoc}
   */
  public FutureResult<Result> addAsync(final AddRequest request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final ResultHandler<? super Result> resultHandler)
  {
    final int messageID = nextMsgID.getAndIncrement();
    final LDAPFutureResultImpl future = new LDAPFutureResultImpl(messageID,
        request, resultHandler, intermediateResponseHandler, this);
    synchronized (stateLock)
    {
      if (connectionInvalidReason != null)
      {
        future.adaptErrorResult(connectionInvalidReason);
        return future;
      }
      if (bindOrStartTLSInProgress.get())
      {
        future.setResultOrError(Responses
            .newResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
                "Bind or Start TLS operation in progress"));
        return future;
      }
      pendingRequests.put(messageID, future);
    }
    try
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        ldapWriter.addRequest(asn1Writer, messageID, request);
        connection.write(asn1Writer.getBuffer(), null);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
    catch (final IOException e)
    {
      pendingRequests.remove(messageID);
      // FIXME: what other sort of IOExceptions can be thrown?
      // FIXME: Is this the best result code?
      final Result errorResult = Responses.newResult(
          ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
      connectionErrorOccurred(errorResult);
      future.adaptErrorResult(errorResult);
    }
    return future;
  }
  /**
   * {@inheritDoc}
   */
  public void addConnectionEventListener(final ConnectionEventListener listener)
  {
    Validator.ensureNotNull(listener);
    listeners.add(listener);
  }
  /**
   * {@inheritDoc}
   */
  public FutureResult<BindResult> bindAsync(final BindRequest request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final ResultHandler<? super BindResult> resultHandler)
  {
    final int messageID = nextMsgID.getAndIncrement();
    BindClient context;
    try
    {
      context = request
          .createBindClient(connection.getPeerAddress() instanceof InetSocketAddress ?
              ((InetSocketAddress) connection.getPeerAddress()).getHostName()
              : connection.getPeerAddress().toString());
    }
    catch (final Exception e)
    {
      // FIXME: I18N need to have a better error message.
      // FIXME: Is this the best result code?
      final Result errorResult = Responses
          .newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
          .setDiagnosticMessage(
              "An error occurred while creating a bind context").setCause(e);
      final ErrorResultException error = ErrorResultException
          .newErrorResult(errorResult);
      if (resultHandler != null)
      {
        resultHandler.handleErrorResult(error);
      }
      return new CompletedFutureResult<BindResult>(error, messageID);
    }
    final LDAPBindFutureResultImpl future = new LDAPBindFutureResultImpl(
        messageID, context, resultHandler, intermediateResponseHandler, this);
    synchronized (stateLock)
    {
      if (connectionInvalidReason != null)
      {
        future.adaptErrorResult(connectionInvalidReason);
        return future;
      }
      if (!pendingRequests.isEmpty())
      {
        future.setResultOrError(Responses.newBindResult(
            ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
            "There are other operations pending on this connection"));
        return future;
      }
      if (!bindOrStartTLSInProgress.compareAndSet(false, true))
      {
        future.setResultOrError(Responses.newBindResult(
            ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
            "Bind or Start TLS operation in progress"));
        return future;
      }
      pendingRequests.put(messageID, future);
    }
    try
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        // Use the bind client to get the initial request instead of using the
        // bind request passed to this method.
        final GenericBindRequest initialRequest = context.nextBindRequest();
        ldapWriter.bindRequest(asn1Writer, messageID, 3, initialRequest);
        connection.write(asn1Writer.getBuffer(), null);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
    catch (final IOException e)
    {
      pendingRequests.remove(messageID);
      bindOrStartTLSInProgress.set(false);
      // FIXME: what other sort of IOExceptions can be thrown?
      // FIXME: Is this the best result code?
      final Result errorResult = Responses.newResult(
          ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
      connectionErrorOccurred(errorResult);
      future.adaptErrorResult(errorResult);
    }
    return future;
  }
  /**
   * {@inheritDoc}
   */
  public void close(final UnbindRequest request, final String reason)
  {
    // FIXME: I18N need to internationalize this message.
    Validator.ensureNotNull(request);
    close(
        request,
        false,
        Responses.newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED)
            .setDiagnosticMessage(
                "Connection closed by client"
                    + (reason != null ? ": " + reason : "")));
  }
  /**
   * {@inheritDoc}
   */
  public FutureResult<CompareResult> compareAsync(final CompareRequest request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final ResultHandler<? super CompareResult> resultHandler)
  {
    final int messageID = nextMsgID.getAndIncrement();
    final LDAPCompareFutureResultImpl future = new LDAPCompareFutureResultImpl(
        messageID, request, resultHandler, intermediateResponseHandler, this);
    synchronized (stateLock)
    {
      if (connectionInvalidReason != null)
      {
        future.adaptErrorResult(connectionInvalidReason);
        return future;
      }
      if (bindOrStartTLSInProgress.get())
      {
        future.setResultOrError(Responses.newCompareResult(
            ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
            "Bind or Start TLS operation in progress"));
        return future;
      }
      pendingRequests.put(messageID, future);
    }
    try
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        ldapWriter.compareRequest(asn1Writer, messageID, request);
        connection.write(asn1Writer.getBuffer(), null);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
    catch (final IOException e)
    {
      pendingRequests.remove(messageID);
      // FIXME: what other sort of IOExceptions can be thrown?
      // FIXME: Is this the best result code?
      final Result errorResult = Responses.newResult(
          ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
      connectionErrorOccurred(errorResult);
      future.adaptErrorResult(errorResult);
    }
    return future;
  }
  /**
   * {@inheritDoc}
   */
  public FutureResult<Result> deleteAsync(final DeleteRequest request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final ResultHandler<? super Result> resultHandler)
  {
    final int messageID = nextMsgID.getAndIncrement();
    final LDAPFutureResultImpl future = new LDAPFutureResultImpl(messageID,
        request, resultHandler, intermediateResponseHandler, this);
    synchronized (stateLock)
    {
      if (connectionInvalidReason != null)
      {
        future.adaptErrorResult(connectionInvalidReason);
        return future;
      }
      if (bindOrStartTLSInProgress.get())
      {
        future.setResultOrError(Responses
            .newResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
                "Bind or Start TLS operation in progress"));
        return future;
      }
      pendingRequests.put(messageID, future);
    }
    try
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        ldapWriter.deleteRequest(asn1Writer, messageID, request);
        connection.write(asn1Writer.getBuffer(), null);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
    catch (final IOException e)
    {
      pendingRequests.remove(messageID);
      // FIXME: what other sort of IOExceptions can be thrown?
      // FIXME: Is this the best result code?
      final Result errorResult = Responses.newResult(
          ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
      connectionErrorOccurred(errorResult);
      future.adaptErrorResult(errorResult);
    }
    return future;
  }
  /**
   * {@inheritDoc}
   */
  public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(
      final ExtendedRequest<R> request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final ResultHandler<? super R> resultHandler)
  {
    final int messageID = nextMsgID.getAndIncrement();
    final LDAPExtendedFutureResultImpl<R> future = new LDAPExtendedFutureResultImpl<R>(
        messageID, request, resultHandler, intermediateResponseHandler, this);
    synchronized (stateLock)
    {
      if (connectionInvalidReason != null)
      {
        future.adaptErrorResult(connectionInvalidReason);
        return future;
      }
      if (request.getOID().equals(StartTLSExtendedRequest.OID))
      {
        if (!pendingRequests.isEmpty())
        {
          future.setResultOrError(request.getResultDecoder()
              .newExtendedErrorResult(ResultCode.OPERATIONS_ERROR, "",
                  "There are pending operations on this connection"));
          return future;
            // First remove the future associated with the request to be
            // abandoned.
            pendingRequest = pendingRequests.remove(request.getRequestID());
        }
        if (isTLSEnabled())
        {
          future.setResultOrError(request.getResultDecoder()
              .newExtendedErrorResult(ResultCode.OPERATIONS_ERROR, "",
                  "This connection is already TLS enabled"));
          return future;
        if (pendingRequest == null) {
            // There has never been a request with the specified message ID or
            // the
            // response has already been received and handled. We can ignore
            // this
            // abandon request.
            // Message ID will be -1 since no request was sent.
            return new CompletedFutureResult<Void>((Void) null);
        }
        if (!bindOrStartTLSInProgress.compareAndSet(false, true))
        {
          future.setResultOrError(request.getResultDecoder()
              .newExtendedErrorResult(ResultCode.OPERATIONS_ERROR, "",
                  "Bind or Start TLS operation in progress"));
          return future;
        pendingRequest.cancel(false);
        try {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                ldapWriter.abandonRequest(asn1Writer, messageID, request);
                connection.write(asn1Writer.getBuffer(), null);
                return new CompletedFutureResult<Void>((Void) null, messageID);
            } finally {
                asn1Writer.recycle();
            }
        } catch (final IOException e) {
            // FIXME: what other sort of IOExceptions can be thrown?
            // FIXME: Is this the best result code?
            final Result errorResult =
                    Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
            connectionErrorOccurred(errorResult);
            return new CompletedFutureResult<Void>(newErrorResult(errorResult), messageID);
        }
      }
      else
      {
        if (bindOrStartTLSInProgress.get())
        {
          future.setResultOrError(request.getResultDecoder()
              .newExtendedErrorResult(ResultCode.OPERATIONS_ERROR, "",
                  "Bind or Start TLS operation in progress"));
          return future;
    }
    /**
     * {@inheritDoc}
     */
    public FutureResult<Result> addAsync(final AddRequest request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final ResultHandler<? super Result> resultHandler) {
        final int messageID = nextMsgID.getAndIncrement();
        final LDAPFutureResultImpl future =
                new LDAPFutureResultImpl(messageID, request, resultHandler,
                        intermediateResponseHandler, this);
        synchronized (stateLock) {
            if (connectionInvalidReason != null) {
                future.adaptErrorResult(connectionInvalidReason);
                return future;
            }
            if (bindOrStartTLSInProgress.get()) {
                future.setResultOrError(Responses.newResult(ResultCode.OPERATIONS_ERROR)
                        .setDiagnosticMessage("Bind or Start TLS operation in progress"));
                return future;
            }
            pendingRequests.put(messageID, future);
        }
      }
      pendingRequests.put(messageID, future);
    }
    try
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        ldapWriter.extendedRequest(asn1Writer, messageID, request);
        connection.write(asn1Writer.getBuffer(), null);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
    catch (final IOException e)
    {
      pendingRequests.remove(messageID);
      bindOrStartTLSInProgress.set(false);
        try {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                ldapWriter.addRequest(asn1Writer, messageID, request);
                connection.write(asn1Writer.getBuffer(), null);
            } finally {
                asn1Writer.recycle();
            }
        } catch (final IOException e) {
            pendingRequests.remove(messageID);
      // FIXME: what other sort of IOExceptions can be thrown?
      // FIXME: Is this the best result code?
      final Result errorResult = Responses.newResult(
          ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
      connectionErrorOccurred(errorResult);
      future.adaptErrorResult(errorResult);
    }
            // FIXME: what other sort of IOExceptions can be thrown?
            // FIXME: Is this the best result code?
            final Result errorResult =
                    Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
            connectionErrorOccurred(errorResult);
            future.adaptErrorResult(errorResult);
        }
    return future;
  }
  /**
   * {@inheritDoc}
   */
  public boolean isClosed()
  {
    synchronized (stateLock)
    {
      return isClosed;
    }
  }
  /**
   * {@inheritDoc}
   */
  public boolean isValid()
  {
    synchronized (stateLock)
    {
      return connectionInvalidReason == null && !isClosed;
    }
  }
  /**
   * {@inheritDoc}
   */
  public FutureResult<Result> modifyAsync(final ModifyRequest request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final ResultHandler<? super Result> resultHandler)
  {
    final int messageID = nextMsgID.getAndIncrement();
    final LDAPFutureResultImpl future = new LDAPFutureResultImpl(messageID,
        request, resultHandler, intermediateResponseHandler, this);
    synchronized (stateLock)
    {
      if (connectionInvalidReason != null)
      {
        future.adaptErrorResult(connectionInvalidReason);
        return future;
      }
      if (bindOrStartTLSInProgress.get())
      {
        future.setResultOrError(Responses
            .newResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
                "Bind or Start TLS operation in progress"));
        return future;
      }
      pendingRequests.put(messageID, future);
    }
    try
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        ldapWriter.modifyRequest(asn1Writer, messageID, request);
        connection.write(asn1Writer.getBuffer(), null);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
    catch (final IOException e)
    {
      pendingRequests.remove(messageID);
      // FIXME: what other sort of IOExceptions can be thrown?
      // FIXME: Is this the best result code?
      final Result errorResult = Responses.newResult(
          ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
      connectionErrorOccurred(errorResult);
      future.adaptErrorResult(errorResult);
    /**
     * {@inheritDoc}
     */
    public void addConnectionEventListener(final ConnectionEventListener listener) {
        Validator.ensureNotNull(listener);
        listeners.add(listener);
    }
    return future;
  }
    /**
     * {@inheritDoc}
     */
    public FutureResult<BindResult> bindAsync(final BindRequest request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final ResultHandler<? super BindResult> resultHandler) {
        final int messageID = nextMsgID.getAndIncrement();
  /**
   * {@inheritDoc}
   */
  public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final ResultHandler<? super Result> resultHandler)
  {
    final int messageID = nextMsgID.getAndIncrement();
    final LDAPFutureResultImpl future = new LDAPFutureResultImpl(messageID,
        request, resultHandler, intermediateResponseHandler, this);
    synchronized (stateLock)
    {
      if (connectionInvalidReason != null)
      {
        future.adaptErrorResult(connectionInvalidReason);
        return future;
      }
      if (bindOrStartTLSInProgress.get())
      {
        future.setResultOrError(Responses
            .newResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
                "Bind or Start TLS operation in progress"));
        return future;
      }
      pendingRequests.put(messageID, future);
    }
    try
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        ldapWriter.modifyDNRequest(asn1Writer, messageID, request);
        connection.write(asn1Writer.getBuffer(), null);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
    catch (final IOException e)
    {
      pendingRequests.remove(messageID);
      // FIXME: what other sort of IOExceptions can be thrown?
      // FIXME: Is this the best result code?
      final Result errorResult = Responses.newResult(
          ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
      connectionErrorOccurred(errorResult);
      future.adaptErrorResult(errorResult);
    }
    return future;
  }
  /**
   * {@inheritDoc}
   */
  public void removeConnectionEventListener(
      final ConnectionEventListener listener)
  {
    Validator.ensureNotNull(listener);
    listeners.remove(listener);
  }
  /**
   * {@inheritDoc}
   */
  public FutureResult<Result> searchAsync(final SearchRequest request,
      final IntermediateResponseHandler intermediateResponseHandler,
      final SearchResultHandler resultHandler)
  {
    final int messageID = nextMsgID.getAndIncrement();
    final LDAPSearchFutureResultImpl future = new LDAPSearchFutureResultImpl(
        messageID, request, resultHandler, intermediateResponseHandler, this);
    synchronized (stateLock)
    {
      if (connectionInvalidReason != null)
      {
        future.adaptErrorResult(connectionInvalidReason);
        return future;
      }
      if (bindOrStartTLSInProgress.get())
      {
        future.setResultOrError(Responses
            .newResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
                "Bind or Start TLS operation in progress"));
        return future;
      }
      pendingRequests.put(messageID, future);
    }
    try
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        ldapWriter.searchRequest(asn1Writer, messageID, request);
        connection.write(asn1Writer.getBuffer(), null);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
    catch (final IOException e)
    {
      pendingRequests.remove(messageID);
      // FIXME: what other sort of IOExceptions can be thrown?
      // FIXME: Is this the best result code?
      final Result errorResult = Responses.newResult(
          ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
      connectionErrorOccurred(errorResult);
      future.adaptErrorResult(errorResult);
    }
    return future;
  }
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    StringBuilder builder = new StringBuilder();
    builder.append("LDAPConnection(");
    builder.append(connection.getLocalAddress());
    builder.append(',');
    builder.append(connection.getPeerAddress());
    builder.append(')');
    return builder.toString();
  }
  int continuePendingBindRequest(final LDAPBindFutureResultImpl future)
      throws ErrorResultException
  {
    final int newMsgID = nextMsgID.getAndIncrement();
    synchronized (stateLock)
    {
      if (connectionInvalidReason != null)
      {
        throw newErrorResult(connectionInvalidReason);
      }
      pendingRequests.put(newMsgID, future);
    }
    return newMsgID;
  }
  long cancelExpiredRequests(final long currentTime)
  {
    final long timeout = options.getTimeout(TimeUnit.MILLISECONDS);
    long delay = timeout;
    if (timeout > 0)
    {
      for (int requestID : pendingRequests.keySet())
      {
        final AbstractLDAPFutureResultImpl<?> future = pendingRequests
            .get(requestID);
        if (future != null)
        {
          final long diff = (future.getTimestamp() + timeout) - currentTime;
          if (diff <= 0 && pendingRequests.remove(requestID) != null)
          {
            StaticUtils.DEBUG_LOG.fine("Cancelling expired future result: "
                + future);
            final Result result = Responses
                .newResult(ResultCode.CLIENT_SIDE_TIMEOUT);
            future.adaptErrorResult(result);
            abandonAsync(Requests.newAbandonRequest(future.getRequestID()));
          }
          else
          {
            delay = Math.min(delay, diff);
          }
        BindClient context;
        try {
            context =
                    request.createBindClient(
                            connection.getPeerAddress() instanceof InetSocketAddress
                            ? ((InetSocketAddress) connection.getPeerAddress()).getHostName()
                            : connection.getPeerAddress().toString());
        } catch (final Exception e) {
            // FIXME: I18N need to have a better error message.
            // FIXME: Is this the best result code?
            final Result errorResult =
                    Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
                            "An error occurred while creating a bind context").setCause(e);
            final ErrorResultException error = ErrorResultException.newErrorResult(errorResult);
            if (resultHandler != null) {
                resultHandler.handleErrorResult(error);
            }
            return new CompletedFutureResult<BindResult>(error, messageID);
        }
      }
    }
    return delay;
  }
        final LDAPBindFutureResultImpl future =
                new LDAPBindFutureResultImpl(messageID, context, resultHandler,
                        intermediateResponseHandler, this);
        synchronized (stateLock) {
            if (connectionInvalidReason != null) {
                future.adaptErrorResult(connectionInvalidReason);
                return future;
            }
            if (!pendingRequests.isEmpty()) {
                future.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR)
                        .setDiagnosticMessage(
                                "There are other operations pending on this connection"));
                return future;
            }
  void close(final UnbindRequest unbindRequest,
      final boolean isDisconnectNotification, final Result reason)
  {
    boolean notifyClose = false;
    boolean notifyErrorOccurred = false;
            if (!bindOrStartTLSInProgress.compareAndSet(false, true)) {
                future.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR)
                        .setDiagnosticMessage("Bind or Start TLS operation in progress"));
                return future;
            }
    synchronized (stateLock)
    {
      if (isClosed)
      {
        // Already closed.
        return;
      }
      if (connectionInvalidReason != null)
      {
        // Already closed.
        isClosed = true;
        return;
      }
      if (unbindRequest != null)
      {
        // User closed.
        isClosed = true;
        notifyClose = true;
      }
      else
      {
        notifyErrorOccurred = true;
      }
      // Mark the connection as invalid.
      if (!isDisconnectNotification)
      {
        // Connection termination was detected locally, so use the provided
        // reason for all subsequent requests.
        connectionInvalidReason = reason;
      }
      else
      {
        // Connection termination was triggered remotely. We don't want to
        // blindly pass on the result code to requests since it could be
        // confused for a genuine response. For example, if the disconnect
        // contained the invalidCredentials result code then this could be
        // misinterpreted as a genuine authentication failure for subsequent
        // bind requests.
        connectionInvalidReason = Responses.newResult(
            ResultCode.CLIENT_SIDE_SERVER_DOWN).setDiagnosticMessage(
            "Connection closed by server");
      }
    }
    // First abort all outstanding requests.
    for (int requestID : pendingRequests.keySet())
    {
      final AbstractLDAPFutureResultImpl<?> future = pendingRequests
          .remove(requestID);
      if (future != null)
      {
        future.adaptErrorResult(connectionInvalidReason);
      }
    }
    // Now try cleanly closing the connection if possible.
    // Only send unbind if specified.
    if (unbindRequest != null && !isDisconnectNotification)
    {
      try
      {
        final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
        try
        {
          ldapWriter.unbindRequest(asn1Writer, nextMsgID.getAndIncrement(),
              unbindRequest);
          connection.write(asn1Writer.getBuffer(), null);
            pendingRequests.put(messageID, future);
        }
        finally
        {
          asn1Writer.recycle();
        try {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                // Use the bind client to get the initial request instead of
                // using the
                // bind request passed to this method.
                final GenericBindRequest initialRequest = context.nextBindRequest();
                ldapWriter.bindRequest(asn1Writer, messageID, 3, initialRequest);
                connection.write(asn1Writer.getBuffer(), null);
            } finally {
                asn1Writer.recycle();
            }
        } catch (final IOException e) {
            pendingRequests.remove(messageID);
            bindOrStartTLSInProgress.set(false);
            // FIXME: what other sort of IOExceptions can be thrown?
            // FIXME: Is this the best result code?
            final Result errorResult =
                    Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
            connectionErrorOccurred(errorResult);
            future.adaptErrorResult(errorResult);
        }
      }
      catch (final IOException e)
      {
        // Underlying channel prob blown up. Just ignore.
      }
        return future;
    }
    try
    {
      connection.close();
    }
    catch (final IOException e)
    {
      // Ignore.
    /**
     * {@inheritDoc}
     */
    public void close(final UnbindRequest request, final String reason) {
        // FIXME: I18N need to internationalize this message.
        Validator.ensureNotNull(request);
        close(request, false, Responses.newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED)
                .setDiagnosticMessage(
                        "Connection closed by client" + (reason != null ? ": " + reason : "")));
    }
    // Notify listeners.
    if (notifyClose)
    {
      for (final ConnectionEventListener listener : listeners)
      {
        listener.handleConnectionClosed();
      }
    }
    /**
     * {@inheritDoc}
     */
    public FutureResult<CompareResult> compareAsync(final CompareRequest request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final ResultHandler<? super CompareResult> resultHandler) {
        final int messageID = nextMsgID.getAndIncrement();
        final LDAPCompareFutureResultImpl future =
                new LDAPCompareFutureResultImpl(messageID, request, resultHandler,
                        intermediateResponseHandler, this);
    if (notifyErrorOccurred)
    {
      for (final ConnectionEventListener listener : listeners)
      {
        // Use the reason provided in the disconnect notification.
        listener.handleConnectionError(isDisconnectNotification,
            newErrorResult(reason));
      }
    }
  }
  LDAPOptions getLDAPOptions()
  {
    return options;
  }
  AbstractLDAPFutureResultImpl<?> getPendingRequest(final Integer messageID)
  {
    return pendingRequests.get(messageID);
  }
  void handleUnsolicitedNotification(final ExtendedResult result)
  {
    if (isClosed())
    {
      // Don't notify after connection is closed.
      return;
    }
    for (final ConnectionEventListener listener : listeners)
    {
      listener.handleUnsolicitedNotification(result);
    }
  }
  /**
   * Installs a new Grizzly filter (e.g. SSL/SASL) beneath the top-level LDAP
   * filter.
   *
   * @param filter
   *          The filter to be installed.
   */
  void installFilter(final Filter filter)
  {
    synchronized (stateLock)
    {
      // Determine the index where the filter should be added.
      FilterChain oldFilterChain = (FilterChain) connection.getProcessor();
      int filterIndex = oldFilterChain.size() - 1;
      if (filter instanceof SSLFilter)
      {
        // Beneath any ConnectionSecurityLayerFilters if present, otherwise
        // beneath the LDAP filter.
        for (int i = oldFilterChain.size() - 2; i >= 0; i--)
        {
          if (!(oldFilterChain.get(i) instanceof ConnectionSecurityLayerFilter))
          {
            filterIndex = i + 1;
            break;
          }
        synchronized (stateLock) {
            if (connectionInvalidReason != null) {
                future.adaptErrorResult(connectionInvalidReason);
                return future;
            }
            if (bindOrStartTLSInProgress.get()) {
                future.setResultOrError(Responses.newCompareResult(ResultCode.OPERATIONS_ERROR)
                        .setDiagnosticMessage("Bind or Start TLS operation in progress"));
                return future;
            }
            pendingRequests.put(messageID, future);
        }
      }
      // Create the new filter chain.
      FilterChain newFilterChain = FilterChainBuilder.stateless()
          .addAll(oldFilterChain)
          .add(filterIndex, filter)
          .build();
      connection.setProcessor(newFilterChain);
    }
  }
        try {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                ldapWriter.compareRequest(asn1Writer, messageID, request);
                connection.write(asn1Writer.getBuffer(), null);
            } finally {
                asn1Writer.recycle();
            }
        } catch (final IOException e) {
            pendingRequests.remove(messageID);
  /**
   * Indicates whether or not TLS is enabled on this connection.
   *
   * @return {@code true} if TLS is enabled on this connection, otherwise
   *         {@code false}.
   */
  boolean isTLSEnabled()
  {
    synchronized (stateLock)
    {
      final FilterChain currentFilterChain = (FilterChain) connection
          .getProcessor();
      for (Filter filter : currentFilterChain)
      {
        if (filter instanceof SSLFilter)
        {
          return true;
            // FIXME: what other sort of IOExceptions can be thrown?
            // FIXME: Is this the best result code?
            final Result errorResult =
                    Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
            connectionErrorOccurred(errorResult);
            future.adaptErrorResult(errorResult);
        }
      }
      return false;
        return future;
    }
  }
    /**
     * {@inheritDoc}
     */
    public FutureResult<Result> deleteAsync(final DeleteRequest request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final ResultHandler<? super Result> resultHandler) {
        final int messageID = nextMsgID.getAndIncrement();
        final LDAPFutureResultImpl future =
                new LDAPFutureResultImpl(messageID, request, resultHandler,
                        intermediateResponseHandler, this);
        synchronized (stateLock) {
            if (connectionInvalidReason != null) {
                future.adaptErrorResult(connectionInvalidReason);
                return future;
            }
            if (bindOrStartTLSInProgress.get()) {
                future.setResultOrError(Responses.newResult(ResultCode.OPERATIONS_ERROR)
                        .setDiagnosticMessage("Bind or Start TLS operation in progress"));
                return future;
            }
            pendingRequests.put(messageID, future);
        }
  AbstractLDAPFutureResultImpl<?> removePendingRequest(final Integer messageID)
  {
    return pendingRequests.remove(messageID);
  }
        try {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                ldapWriter.deleteRequest(asn1Writer, messageID, request);
                connection.write(asn1Writer.getBuffer(), null);
            } finally {
                asn1Writer.recycle();
            }
        } catch (final IOException e) {
            pendingRequests.remove(messageID);
            // FIXME: what other sort of IOExceptions can be thrown?
            // FIXME: Is this the best result code?
            final Result errorResult =
                    Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
            connectionErrorOccurred(errorResult);
            future.adaptErrorResult(errorResult);
        }
  void setBindOrStartTLSInProgress(final boolean state)
  {
    bindOrStartTLSInProgress.set(state);
  }
  void startTLS(final SSLContext sslContext,
      final List<String> protocols, final List<String> cipherSuites,
      final CompletionHandler<SSLEngine> completionHandler) throws IOException
  {
    synchronized (stateLock)
    {
      if (isTLSEnabled())
      {
        throw new IllegalStateException("TLS already enabled");
      }
      SSLEngineConfigurator sslEngineConfigurator = new SSLEngineConfigurator(
          sslContext, true, false, false);
      sslEngineConfigurator.setEnabledProtocols(protocols.isEmpty() ? null
          : protocols.toArray(new String[protocols.size()]));
      sslEngineConfigurator
          .setEnabledCipherSuites(cipherSuites.isEmpty() ? null : cipherSuites
              .toArray(new String[cipherSuites.size()]));
      SSLFilter sslFilter = new SSLFilter(null, sslEngineConfigurator);
      installFilter(sslFilter);
      sslFilter.handshake(connection, completionHandler);
        return future;
    }
  }
    /**
     * {@inheritDoc}
     */
    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(
            final ExtendedRequest<R> request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final ResultHandler<? super R> resultHandler) {
        final int messageID = nextMsgID.getAndIncrement();
        final LDAPExtendedFutureResultImpl<R> future =
                new LDAPExtendedFutureResultImpl<R>(messageID, request, resultHandler,
                        intermediateResponseHandler, this);
        synchronized (stateLock) {
            if (connectionInvalidReason != null) {
                future.adaptErrorResult(connectionInvalidReason);
                return future;
            }
            if (request.getOID().equals(StartTLSExtendedRequest.OID)) {
                if (!pendingRequests.isEmpty()) {
                    future.setResultOrError(request.getResultDecoder().newExtendedErrorResult(
                            ResultCode.OPERATIONS_ERROR, "",
                            "There are pending operations on this connection"));
                    return future;
                }
                if (isTLSEnabled()) {
                    future.setResultOrError(request.getResultDecoder().newExtendedErrorResult(
                            ResultCode.OPERATIONS_ERROR, "",
                            "This connection is already TLS enabled"));
                    return future;
                }
                if (!bindOrStartTLSInProgress.compareAndSet(false, true)) {
                    future.setResultOrError(request.getResultDecoder().newExtendedErrorResult(
                            ResultCode.OPERATIONS_ERROR, "",
                            "Bind or Start TLS operation in progress"));
                    return future;
                }
            } else {
                if (bindOrStartTLSInProgress.get()) {
                    future.setResultOrError(request.getResultDecoder().newExtendedErrorResult(
                            ResultCode.OPERATIONS_ERROR, "",
                            "Bind or Start TLS operation in progress"));
                    return future;
                }
            }
            pendingRequests.put(messageID, future);
        }
  private void connectionErrorOccurred(final Result reason)
  {
    close(null, false, reason);
  }
        try {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                ldapWriter.extendedRequest(asn1Writer, messageID, request);
                connection.write(asn1Writer.getBuffer(), null);
            } finally {
                asn1Writer.recycle();
            }
        } catch (final IOException e) {
            pendingRequests.remove(messageID);
            bindOrStartTLSInProgress.set(false);
            // FIXME: what other sort of IOExceptions can be thrown?
            // FIXME: Is this the best result code?
            final Result errorResult =
                    Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
            connectionErrorOccurred(errorResult);
            future.adaptErrorResult(errorResult);
        }
        return future;
    }
    /**
     * {@inheritDoc}
     */
    public boolean isClosed() {
        synchronized (stateLock) {
            return isClosed;
        }
    }
    /**
     * {@inheritDoc}
     */
    public boolean isValid() {
        synchronized (stateLock) {
            return connectionInvalidReason == null && !isClosed;
        }
    }
    /**
     * {@inheritDoc}
     */
    public FutureResult<Result> modifyAsync(final ModifyRequest request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final ResultHandler<? super Result> resultHandler) {
        final int messageID = nextMsgID.getAndIncrement();
        final LDAPFutureResultImpl future =
                new LDAPFutureResultImpl(messageID, request, resultHandler,
                        intermediateResponseHandler, this);
        synchronized (stateLock) {
            if (connectionInvalidReason != null) {
                future.adaptErrorResult(connectionInvalidReason);
                return future;
            }
            if (bindOrStartTLSInProgress.get()) {
                future.setResultOrError(Responses.newResult(ResultCode.OPERATIONS_ERROR)
                        .setDiagnosticMessage("Bind or Start TLS operation in progress"));
                return future;
            }
            pendingRequests.put(messageID, future);
        }
        try {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                ldapWriter.modifyRequest(asn1Writer, messageID, request);
                connection.write(asn1Writer.getBuffer(), null);
            } finally {
                asn1Writer.recycle();
            }
        } catch (final IOException e) {
            pendingRequests.remove(messageID);
            // FIXME: what other sort of IOExceptions can be thrown?
            // FIXME: Is this the best result code?
            final Result errorResult =
                    Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
            connectionErrorOccurred(errorResult);
            future.adaptErrorResult(errorResult);
        }
        return future;
    }
    /**
     * {@inheritDoc}
     */
    public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final ResultHandler<? super Result> resultHandler) {
        final int messageID = nextMsgID.getAndIncrement();
        final LDAPFutureResultImpl future =
                new LDAPFutureResultImpl(messageID, request, resultHandler,
                        intermediateResponseHandler, this);
        synchronized (stateLock) {
            if (connectionInvalidReason != null) {
                future.adaptErrorResult(connectionInvalidReason);
                return future;
            }
            if (bindOrStartTLSInProgress.get()) {
                future.setResultOrError(Responses.newResult(ResultCode.OPERATIONS_ERROR)
                        .setDiagnosticMessage("Bind or Start TLS operation in progress"));
                return future;
            }
            pendingRequests.put(messageID, future);
        }
        try {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                ldapWriter.modifyDNRequest(asn1Writer, messageID, request);
                connection.write(asn1Writer.getBuffer(), null);
            } finally {
                asn1Writer.recycle();
            }
        } catch (final IOException e) {
            pendingRequests.remove(messageID);
            // FIXME: what other sort of IOExceptions can be thrown?
            // FIXME: Is this the best result code?
            final Result errorResult =
                    Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
            connectionErrorOccurred(errorResult);
            future.adaptErrorResult(errorResult);
        }
        return future;
    }
    /**
     * {@inheritDoc}
     */
    public void removeConnectionEventListener(final ConnectionEventListener listener) {
        Validator.ensureNotNull(listener);
        listeners.remove(listener);
    }
    /**
     * {@inheritDoc}
     */
    public FutureResult<Result> searchAsync(final SearchRequest request,
            final IntermediateResponseHandler intermediateResponseHandler,
            final SearchResultHandler resultHandler) {
        final int messageID = nextMsgID.getAndIncrement();
        final LDAPSearchFutureResultImpl future =
                new LDAPSearchFutureResultImpl(messageID, request, resultHandler,
                        intermediateResponseHandler, this);
        synchronized (stateLock) {
            if (connectionInvalidReason != null) {
                future.adaptErrorResult(connectionInvalidReason);
                return future;
            }
            if (bindOrStartTLSInProgress.get()) {
                future.setResultOrError(Responses.newResult(ResultCode.OPERATIONS_ERROR)
                        .setDiagnosticMessage("Bind or Start TLS operation in progress"));
                return future;
            }
            pendingRequests.put(messageID, future);
        }
        try {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                ldapWriter.searchRequest(asn1Writer, messageID, request);
                connection.write(asn1Writer.getBuffer(), null);
            } finally {
                asn1Writer.recycle();
            }
        } catch (final IOException e) {
            pendingRequests.remove(messageID);
            // FIXME: what other sort of IOExceptions can be thrown?
            // FIXME: Is this the best result code?
            final Result errorResult =
                    Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
            connectionErrorOccurred(errorResult);
            future.adaptErrorResult(errorResult);
        }
        return future;
    }
    /**
     * {@inheritDoc}
     */
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("LDAPConnection(");
        builder.append(connection.getLocalAddress());
        builder.append(',');
        builder.append(connection.getPeerAddress());
        builder.append(')');
        return builder.toString();
    }
    int continuePendingBindRequest(final LDAPBindFutureResultImpl future)
            throws ErrorResultException {
        final int newMsgID = nextMsgID.getAndIncrement();
        synchronized (stateLock) {
            if (connectionInvalidReason != null) {
                throw newErrorResult(connectionInvalidReason);
            }
            pendingRequests.put(newMsgID, future);
        }
        return newMsgID;
    }
    long cancelExpiredRequests(final long currentTime) {
        final long timeout = options.getTimeout(TimeUnit.MILLISECONDS);
        long delay = timeout;
        if (timeout > 0) {
            for (int requestID : pendingRequests.keySet()) {
                final AbstractLDAPFutureResultImpl<?> future = pendingRequests.get(requestID);
                if (future != null) {
                    final long diff = (future.getTimestamp() + timeout) - currentTime;
                    if (diff <= 0 && pendingRequests.remove(requestID) != null) {
                        StaticUtils.DEBUG_LOG.fine("Cancelling expired future result: " + future);
                        final Result result = Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT);
                        future.adaptErrorResult(result);
                        abandonAsync(Requests.newAbandonRequest(future.getRequestID()));
                    } else {
                        delay = Math.min(delay, diff);
                    }
                }
            }
        }
        return delay;
    }
    void close(final UnbindRequest unbindRequest, final boolean isDisconnectNotification,
            final Result reason) {
        boolean notifyClose = false;
        boolean notifyErrorOccurred = false;
        synchronized (stateLock) {
            if (isClosed) {
                // Already closed.
                return;
            }
            if (connectionInvalidReason != null) {
                // Already closed.
                isClosed = true;
                return;
            }
            if (unbindRequest != null) {
                // User closed.
                isClosed = true;
                notifyClose = true;
            } else {
                notifyErrorOccurred = true;
            }
            // Mark the connection as invalid.
            if (!isDisconnectNotification) {
                // Connection termination was detected locally, so use the
                // provided
                // reason for all subsequent requests.
                connectionInvalidReason = reason;
            } else {
                // Connection termination was triggered remotely. We don't want
                // to
                // blindly pass on the result code to requests since it could be
                // confused for a genuine response. For example, if the
                // disconnect
                // contained the invalidCredentials result code then this could
                // be
                // misinterpreted as a genuine authentication failure for
                // subsequent
                // bind requests.
                connectionInvalidReason =
                        Responses.newResult(ResultCode.CLIENT_SIDE_SERVER_DOWN)
                                .setDiagnosticMessage("Connection closed by server");
            }
        }
        // First abort all outstanding requests.
        for (int requestID : pendingRequests.keySet()) {
            final AbstractLDAPFutureResultImpl<?> future = pendingRequests.remove(requestID);
            if (future != null) {
                future.adaptErrorResult(connectionInvalidReason);
            }
        }
        // Now try cleanly closing the connection if possible.
        // Only send unbind if specified.
        if (unbindRequest != null && !isDisconnectNotification) {
            try {
                final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
                try {
                    ldapWriter
                            .unbindRequest(asn1Writer, nextMsgID.getAndIncrement(), unbindRequest);
                    connection.write(asn1Writer.getBuffer(), null);
                } finally {
                    asn1Writer.recycle();
                }
            } catch (final IOException e) {
                // Underlying channel prob blown up. Just ignore.
            }
        }
        try {
            connection.close();
        } catch (final IOException e) {
            // Ignore.
        }
        // Notify listeners.
        if (notifyClose) {
            for (final ConnectionEventListener listener : listeners) {
                listener.handleConnectionClosed();
            }
        }
        if (notifyErrorOccurred) {
            for (final ConnectionEventListener listener : listeners) {
                // Use the reason provided in the disconnect notification.
                listener.handleConnectionError(isDisconnectNotification, newErrorResult(reason));
            }
        }
    }
    LDAPOptions getLDAPOptions() {
        return options;
    }
    AbstractLDAPFutureResultImpl<?> getPendingRequest(final Integer messageID) {
        return pendingRequests.get(messageID);
    }
    void handleUnsolicitedNotification(final ExtendedResult result) {
        if (isClosed()) {
            // Don't notify after connection is closed.
            return;
        }
        for (final ConnectionEventListener listener : listeners) {
            listener.handleUnsolicitedNotification(result);
        }
    }
    /**
     * Installs a new Grizzly filter (e.g. SSL/SASL) beneath the top-level LDAP
     * filter.
     *
     * @param filter
     *            The filter to be installed.
     */
    void installFilter(final Filter filter) {
        synchronized (stateLock) {
            // Determine the index where the filter should be added.
            FilterChain oldFilterChain = (FilterChain) connection.getProcessor();
            int filterIndex = oldFilterChain.size() - 1;
            if (filter instanceof SSLFilter) {
                // Beneath any ConnectionSecurityLayerFilters if present,
                // otherwise
                // beneath the LDAP filter.
                for (int i = oldFilterChain.size() - 2; i >= 0; i--) {
                    if (!(oldFilterChain.get(i) instanceof ConnectionSecurityLayerFilter)) {
                        filterIndex = i + 1;
                        break;
                    }
                }
            }
            // Create the new filter chain.
            FilterChain newFilterChain =
                    FilterChainBuilder.stateless().addAll(oldFilterChain).add(filterIndex, filter)
                            .build();
            connection.setProcessor(newFilterChain);
        }
    }
    /**
     * Indicates whether or not TLS is enabled on this connection.
     *
     * @return {@code true} if TLS is enabled on this connection, otherwise
     *         {@code false}.
     */
    boolean isTLSEnabled() {
        synchronized (stateLock) {
            final FilterChain currentFilterChain = (FilterChain) connection.getProcessor();
            for (Filter filter : currentFilterChain) {
                if (filter instanceof SSLFilter) {
                    return true;
                }
            }
            return false;
        }
    }
    AbstractLDAPFutureResultImpl<?> removePendingRequest(final Integer messageID) {
        return pendingRequests.remove(messageID);
    }
    void setBindOrStartTLSInProgress(final boolean state) {
        bindOrStartTLSInProgress.set(state);
    }
    void startTLS(final SSLContext sslContext, final List<String> protocols,
            final List<String> cipherSuites, final CompletionHandler<SSLEngine> completionHandler)
            throws IOException {
        synchronized (stateLock) {
            if (isTLSEnabled()) {
                throw new IllegalStateException("TLS already enabled");
            }
            SSLEngineConfigurator sslEngineConfigurator =
                    new SSLEngineConfigurator(sslContext, true, false, false);
            sslEngineConfigurator.setEnabledProtocols(protocols.isEmpty() ? null : protocols
                    .toArray(new String[protocols.size()]));
            sslEngineConfigurator.setEnabledCipherSuites(cipherSuites.isEmpty() ? null
                    : cipherSuites.toArray(new String[cipherSuites.size()]));
            SSLFilter sslFilter = new SSLFilter(null, sslEngineConfigurator);
            installFilter(sslFilter);
            sslFilter.handshake(connection, completionHandler);
        }
    }
    private void connectionErrorOccurred(final Result reason) {
        close(null, false, reason);
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnectionFactoryImpl.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,8 +27,6 @@
package com.forgerock.opendj.ldap;
import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
import java.io.IOException;
@@ -38,7 +35,13 @@
import javax.net.ssl.SSLEngine;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ConnectionFactory;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.FutureResult;
import org.forgerock.opendj.ldap.LDAPOptions;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
@@ -54,306 +57,226 @@
import com.forgerock.opendj.util.FutureResultTransformer;
import com.forgerock.opendj.util.RecursiveFutureResult;
/**
 * LDAP connection factory implementation.
 */
public final class LDAPConnectionFactoryImpl implements ConnectionFactory
{
public final class LDAPConnectionFactoryImpl implements ConnectionFactory {
  @SuppressWarnings("rawtypes")
  private final class ConnectionCompletionHandler implements
      CompletionHandler<org.glassfish.grizzly.Connection>
  {
    private final FutureResultTransformer<Result, Connection> startTLSFutureResult;
    private final RecursiveFutureResult<LDAPConnection, ExtendedResult> connectionFutureResult;
    private LDAPConnection connection;
    @SuppressWarnings("rawtypes")
    private final class ConnectionCompletionHandler implements
            CompletionHandler<org.glassfish.grizzly.Connection> {
        private final FutureResultTransformer<Result, Connection> startTLSFutureResult;
        private final RecursiveFutureResult<LDAPConnection, ExtendedResult> connectionFutureResult;
        private LDAPConnection connection;
        private ConnectionCompletionHandler(final ResultHandler<? super Connection> handler) {
            this.startTLSFutureResult = new FutureResultTransformer<Result, Connection>(handler) {
    private ConnectionCompletionHandler(final ResultHandler<? super Connection> handler)
    {
      this.startTLSFutureResult = new FutureResultTransformer<Result, Connection>(
          handler)
      {
        @Override
        protected ErrorResultException transformErrorResult(
            final ErrorResultException errorResult)
        {
          // Ensure that the connection is closed.
          try
          {
            if (connection != null)
            {
              connection.close();
            }
          }
          catch (final Exception e)
          {
            // Ignore.
          }
          return errorResult;
        }
        @Override
        protected LDAPConnection transformResult(final Result result)
            throws ErrorResultException
        {
          return connection;
        }
      };
      this.connectionFutureResult = new RecursiveFutureResult<LDAPConnection, ExtendedResult>(
          startTLSFutureResult)
      {
        @Override
        protected FutureResult<? extends ExtendedResult> chainResult(
            final LDAPConnection innerResult,
            final ResultHandler<? super ExtendedResult> handler)
            throws ErrorResultException
        {
          connection = innerResult;
          if (options.getSSLContext() != null && options.useStartTLS())
          {
            // Chain StartTLS extended request.
            final StartTLSExtendedRequest startTLS = Requests
                .newStartTLSExtendedRequest(options.getSSLContext());
            startTLS.addEnabledCipherSuite(options.getEnabledCipherSuites()
                .toArray(new String[options.getEnabledCipherSuites().size()]));
            startTLS.addEnabledProtocol(options.getEnabledProtocols().toArray(
                new String[options.getEnabledProtocols().size()]));
            return connection.extendedRequestAsync(startTLS, null, handler);
          }
          else if (options.getSSLContext() != null)
          {
            // Install SSL/TLS layer.
            try
            {
              connection.startTLS(options.getSSLContext(),
                  options.getEnabledProtocols(),
                  options.getEnabledCipherSuites(),
                  new EmptyCompletionHandler<SSLEngine>()
                  {
                    @Override
                    public void completed(final SSLEngine result)
                    {
                      handler.handleResult(null);
                @Override
                protected ErrorResultException transformErrorResult(
                        final ErrorResultException errorResult) {
                    // Ensure that the connection is closed.
                    try {
                        if (connection != null) {
                            connection.close();
                        }
                    } catch (final Exception e) {
                        // Ignore.
                    }
                    return errorResult;
                }
                @Override
                protected LDAPConnection transformResult(final Result result)
                        throws ErrorResultException {
                    return connection;
                }
            };
                    @Override
                    public void failed(final Throwable throwable)
                    {
                      handler.handleErrorResult(newErrorResult(
                          ResultCode.CLIENT_SIDE_CONNECT_ERROR,
                          throwable.getMessage(), throwable));
                    }
                  });
              return null;
            }
            catch (final IOException ioe)
            {
              throw newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
                  ioe.getMessage(), ioe);
            }
          }
          else
          {
            // Plain connection.
            handler.handleResult(null);
            return new CompletedFutureResult<ExtendedResult>(
                (ExtendedResult) null);
          }
            this.connectionFutureResult =
                    new RecursiveFutureResult<LDAPConnection, ExtendedResult>(startTLSFutureResult) {
                        @Override
                        protected FutureResult<? extends ExtendedResult> chainResult(
                                final LDAPConnection innerResult,
                                final ResultHandler<? super ExtendedResult> handler)
                                throws ErrorResultException {
                            connection = innerResult;
                            if (options.getSSLContext() != null && options.useStartTLS()) {
                                // Chain StartTLS extended request.
                                final StartTLSExtendedRequest startTLS =
                                        Requests.newStartTLSExtendedRequest(options.getSSLContext());
                                startTLS.addEnabledCipherSuite(options
                                        .getEnabledCipherSuites()
                                        .toArray(
                                                new String[options.getEnabledCipherSuites().size()]));
                                startTLS.addEnabledProtocol(options.getEnabledProtocols().toArray(
                                        new String[options.getEnabledProtocols().size()]));
                                return connection.extendedRequestAsync(startTLS, null, handler);
                            } else if (options.getSSLContext() != null) {
                                // Install SSL/TLS layer.
                                try {
                                    connection.startTLS(options.getSSLContext(), options
                                            .getEnabledProtocols(), options
                                            .getEnabledCipherSuites(),
                                            new EmptyCompletionHandler<SSLEngine>() {
                                                @Override
                                                public void completed(final SSLEngine result) {
                                                    handler.handleResult(null);
                                                }
                                                @Override
                                                public void failed(final Throwable throwable) {
                                                    handler.handleErrorResult(newErrorResult(
                                                            ResultCode.CLIENT_SIDE_CONNECT_ERROR,
                                                            throwable.getMessage(), throwable));
                                                }
                                            });
                                    return null;
                                } catch (final IOException ioe) {
                                    throw newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR, ioe
                                            .getMessage(), ioe);
                                }
                            } else {
                                // Plain connection.
                                handler.handleResult(null);
                                return new CompletedFutureResult<ExtendedResult>(
                                        (ExtendedResult) null);
                            }
                        }
                    };
            startTLSFutureResult.setFutureResult(connectionFutureResult);
        }
      };
        /**
         * {@inheritDoc}
         */
        @Override
        public void cancelled() {
            // Ignore this.
        }
      startTLSFutureResult.setFutureResult(connectionFutureResult);
        /**
         * {@inheritDoc}
         */
        @Override
        public void completed(final org.glassfish.grizzly.Connection connection) {
            connectionFutureResult.handleResult(adaptConnection(connection));
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void failed(final Throwable throwable) {
            connectionFutureResult.handleErrorResult(adaptConnectionException(throwable));
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void updated(final org.glassfish.grizzly.Connection connection) {
            // Ignore this.
        }
    }
    private final SocketAddress socketAddress;
    private final TCPNIOTransport transport;
    private final FilterChain defaultFilterChain;
    private final LDAPClientFilter clientFilter;
    private final LDAPOptions options;
    /**
     * Creates a new LDAP connection factory implementation which can be used to
     * create connections to the Directory Server at the provided host and port
     * address using provided connection options.
     *
     * @param address
     *            The address of the Directory Server to connect to.
     * @param options
     *            The LDAP connection options to use when creating connections.
     */
    public LDAPConnectionFactoryImpl(final SocketAddress address, final LDAPOptions options) {
        if (options.getTCPNIOTransport() == null) {
            this.transport = DefaultTCPNIOTransport.getInstance();
        } else {
            this.transport = options.getTCPNIOTransport();
        }
        this.socketAddress = address;
        this.options = new LDAPOptions(options);
        this.clientFilter =
                new LDAPClientFilter(new LDAPReader(this.options.getDecodeOptions()), 0);
        this.defaultFilterChain =
                FilterChainBuilder.stateless().add(new TransportFilter()).add(clientFilter).build();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void cancelled()
    {
      // Ignore this.
    public Connection getConnection() throws ErrorResultException, InterruptedException {
        return getConnectionAsync(null).get();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void completed(final org.glassfish.grizzly.Connection connection)
    {
      connectionFutureResult.handleResult(adaptConnection(connection));
    public FutureResult<Connection> getConnectionAsync(
            final ResultHandler<? super Connection> handler) {
        final ConnectionCompletionHandler ch = new ConnectionCompletionHandler(handler);
        try {
            ch.connectionFutureResult.setFutureResult(transport.connect(socketAddress, ch));
            return ch.startTLSFutureResult;
        } catch (final IOException e) {
            final ErrorResultException result = adaptConnectionException(e);
            return new CompletedFutureResult<Connection>(result);
        }
    }
    /**
     * Returns the address of the Directory Server.
     *
     * @return The address of the Directory Server.
     */
    public SocketAddress getSocketAddress() {
        return socketAddress;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void failed(final Throwable throwable)
    {
      connectionFutureResult
          .handleErrorResult(adaptConnectionException(throwable));
    public String toString() {
        final StringBuilder builder = new StringBuilder();
        builder.append("LDAPConnectionFactory(");
        builder.append(getSocketAddress().toString());
        builder.append(')');
        return builder.toString();
    }
    private LDAPConnection adaptConnection(final org.glassfish.grizzly.Connection<?> connection) {
        // Test shows that its much faster with non block writes but risk
        // running out of memory if the server is slow.
        connection.configureBlocking(true);
        connection.setProcessor(defaultFilterChain);
    /**
     * {@inheritDoc}
     */
    @Override
    public void updated(final org.glassfish.grizzly.Connection connection)
    {
      // Ignore this.
        final LDAPConnection ldapConnection = new LDAPConnection(connection, options);
        clientFilter.registerConnection(connection, ldapConnection);
        return ldapConnection;
    }
  }
    private ErrorResultException adaptConnectionException(Throwable t) {
        if (t instanceof ExecutionException) {
            t = t.getCause();
        }
  private final SocketAddress socketAddress;
  private final TCPNIOTransport transport;
  private final FilterChain defaultFilterChain;
  private final LDAPClientFilter clientFilter;
  private final LDAPOptions options;
  /**
   * Creates a new LDAP connection factory implementation which can be used to
   * create connections to the Directory Server at the provided host and port
   * address using provided connection options.
   *
   * @param address
   *          The address of the Directory Server to connect to.
   * @param options
   *          The LDAP connection options to use when creating connections.
   */
  public LDAPConnectionFactoryImpl(final SocketAddress address,
      final LDAPOptions options)
  {
    if (options.getTCPNIOTransport() == null)
    {
      this.transport = DefaultTCPNIOTransport.getInstance();
        return newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR, t.getMessage(), t);
    }
    else
    {
      this.transport = options.getTCPNIOTransport();
    }
    this.socketAddress = address;
    this.options = new LDAPOptions(options);
    this.clientFilter = new LDAPClientFilter(new LDAPReader(
        this.options.getDecodeOptions()), 0);
    this.defaultFilterChain = FilterChainBuilder.stateless()
        .add(new TransportFilter())
        .add(clientFilter)
        .build();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public Connection getConnection() throws ErrorResultException,
      InterruptedException
  {
    return getConnectionAsync(null).get();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<Connection> getConnectionAsync(
      final ResultHandler<? super Connection> handler)
  {
    final ConnectionCompletionHandler ch = new ConnectionCompletionHandler(handler);
    try
    {
      ch.connectionFutureResult.setFutureResult(transport.connect(socketAddress, ch));
      return ch.startTLSFutureResult;
    }
    catch (final IOException e)
    {
      final ErrorResultException result = adaptConnectionException(e);
      return new CompletedFutureResult<Connection>(result);
    }
  }
  /**
   * Returns the address of the Directory Server.
   *
   * @return The address of the Directory Server.
   */
  public SocketAddress getSocketAddress()
  {
    return socketAddress;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String toString()
  {
    final StringBuilder builder = new StringBuilder();
    builder.append("LDAPConnectionFactory(");
    builder.append(getSocketAddress().toString());
    builder.append(')');
    return builder.toString();
  }
  private LDAPConnection adaptConnection(
      final org.glassfish.grizzly.Connection<?> connection)
  {
    // Test shows that its much faster with non block writes but risk
    // running out of memory if the server is slow.
    connection.configureBlocking(true);
    connection.setProcessor(defaultFilterChain);
    final LDAPConnection ldapConnection = new LDAPConnection(connection,
        options);
    clientFilter.registerConnection(connection, ldapConnection);
    return ldapConnection;
  }
  private ErrorResultException adaptConnectionException(Throwable t)
  {
    if (t instanceof ExecutionException)
    {
      t = t.getCause();
    }
    return newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR, t.getMessage(),
        t);
  }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConstants.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -26,307 +25,302 @@
 */
package com.forgerock.opendj.ldap;
/**
 * This class defines a number of constants used in the LDAP protocol.
 */
public final class LDAPConstants
{
public final class LDAPConstants {
  /**
   * The protocol op type for bind requests.
   */
  public static final byte OP_TYPE_BIND_REQUEST = 0x60;
    /**
     * The protocol op type for bind requests.
     */
    public static final byte OP_TYPE_BIND_REQUEST = 0x60;
  /**
   * The protocol op type for bind responses.
   */
  public static final byte OP_TYPE_BIND_RESPONSE = 0x61;
    /**
     * The protocol op type for bind responses.
     */
    public static final byte OP_TYPE_BIND_RESPONSE = 0x61;
  /**
   * The protocol op type for unbind requests.
   */
  public static final byte OP_TYPE_UNBIND_REQUEST = 0x42;
    /**
     * The protocol op type for unbind requests.
     */
    public static final byte OP_TYPE_UNBIND_REQUEST = 0x42;
  /**
   * The protocol op type for search requests.
   */
  public static final byte OP_TYPE_SEARCH_REQUEST = 0x63;
    /**
     * The protocol op type for search requests.
     */
    public static final byte OP_TYPE_SEARCH_REQUEST = 0x63;
  /**
   * The protocol op type for search result entries.
   */
  public static final byte OP_TYPE_SEARCH_RESULT_ENTRY = 0x64;
    /**
     * The protocol op type for search result entries.
     */
    public static final byte OP_TYPE_SEARCH_RESULT_ENTRY = 0x64;
  /**
   * The protocol op type for search result references.
   */
  public static final byte OP_TYPE_SEARCH_RESULT_REFERENCE = 0x73;
    /**
     * The protocol op type for search result references.
     */
    public static final byte OP_TYPE_SEARCH_RESULT_REFERENCE = 0x73;
  /**
   * The protocol op type for search result done elements.
   */
  public static final byte OP_TYPE_SEARCH_RESULT_DONE = 0x65;
    /**
     * The protocol op type for search result done elements.
     */
    public static final byte OP_TYPE_SEARCH_RESULT_DONE = 0x65;
  /**
   * The protocol op type for modify requests.
   */
  public static final byte OP_TYPE_MODIFY_REQUEST = 0x66;
    /**
     * The protocol op type for modify requests.
     */
    public static final byte OP_TYPE_MODIFY_REQUEST = 0x66;
  /**
   * The protocol op type for modify responses.
   */
  public static final byte OP_TYPE_MODIFY_RESPONSE = 0x67;
    /**
     * The protocol op type for modify responses.
     */
    public static final byte OP_TYPE_MODIFY_RESPONSE = 0x67;
  /**
   * The protocol op type for add requests.
   */
  public static final byte OP_TYPE_ADD_REQUEST = 0x68;
    /**
     * The protocol op type for add requests.
     */
    public static final byte OP_TYPE_ADD_REQUEST = 0x68;
  /**
   * The protocol op type for add responses.
   */
  public static final byte OP_TYPE_ADD_RESPONSE = 0x69;
    /**
     * The protocol op type for add responses.
     */
    public static final byte OP_TYPE_ADD_RESPONSE = 0x69;
  /**
   * The protocol op type for delete requests.
   */
  public static final byte OP_TYPE_DELETE_REQUEST = 0x4A;
    /**
     * The protocol op type for delete requests.
     */
    public static final byte OP_TYPE_DELETE_REQUEST = 0x4A;
  /**
   * The protocol op type for delete responses.
   */
  public static final byte OP_TYPE_DELETE_RESPONSE = 0x6B;
    /**
     * The protocol op type for delete responses.
     */
    public static final byte OP_TYPE_DELETE_RESPONSE = 0x6B;
  /**
   * The protocol op type for modify DN requests.
   */
  public static final byte OP_TYPE_MODIFY_DN_REQUEST = 0x6C;
    /**
     * The protocol op type for modify DN requests.
     */
    public static final byte OP_TYPE_MODIFY_DN_REQUEST = 0x6C;
  /**
   * The protocol op type for modify DN responses.
   */
  public static final byte OP_TYPE_MODIFY_DN_RESPONSE = 0x6D;
    /**
     * The protocol op type for modify DN responses.
     */
    public static final byte OP_TYPE_MODIFY_DN_RESPONSE = 0x6D;
  /**
   * The protocol op type for compare requests.
   */
  public static final byte OP_TYPE_COMPARE_REQUEST = 0x6E;
    /**
     * The protocol op type for compare requests.
     */
    public static final byte OP_TYPE_COMPARE_REQUEST = 0x6E;
  /**
   * The protocol op type for compare responses.
   */
  public static final byte OP_TYPE_COMPARE_RESPONSE = 0x6F;
    /**
     * The protocol op type for compare responses.
     */
    public static final byte OP_TYPE_COMPARE_RESPONSE = 0x6F;
  /**
   * The protocol op type for abandon requests.
   */
  public static final byte OP_TYPE_ABANDON_REQUEST = 0x50;
    /**
     * The protocol op type for abandon requests.
     */
    public static final byte OP_TYPE_ABANDON_REQUEST = 0x50;
  /**
   * The protocol op type for extended requests.
   */
  public static final byte OP_TYPE_EXTENDED_REQUEST = 0x77;
    /**
     * The protocol op type for extended requests.
     */
    public static final byte OP_TYPE_EXTENDED_REQUEST = 0x77;
  /**
   * The protocol op type for extended responses.
   */
  public static final byte OP_TYPE_EXTENDED_RESPONSE = 0x78;
    /**
     * The protocol op type for extended responses.
     */
    public static final byte OP_TYPE_EXTENDED_RESPONSE = 0x78;
  /**
   * The protocol op type for intermediate responses.
   */
  public static final byte OP_TYPE_INTERMEDIATE_RESPONSE = 0x79;
    /**
     * The protocol op type for intermediate responses.
     */
    public static final byte OP_TYPE_INTERMEDIATE_RESPONSE = 0x79;
  /**
   * The BER type to use for encoding the sequence of controls in an LDAP
   * message.
   */
  public static final byte TYPE_CONTROL_SEQUENCE = (byte) 0xA0;
    /**
     * The BER type to use for encoding the sequence of controls in an LDAP
     * message.
     */
    public static final byte TYPE_CONTROL_SEQUENCE = (byte) 0xA0;
  /**
   * The BER type to use for encoding the sequence of referral URLs in an
   * LDAPResult element.
   */
  public static final byte TYPE_REFERRAL_SEQUENCE = (byte) 0xA3;
    /**
     * The BER type to use for encoding the sequence of referral URLs in an
     * LDAPResult element.
     */
    public static final byte TYPE_REFERRAL_SEQUENCE = (byte) 0xA3;
  /**
   * The BER type to use for the AuthenticationChoice element in a bind request
   * when simple authentication is to be used.
   */
  public static final byte TYPE_AUTHENTICATION_SIMPLE = (byte) 0x80;
    /**
     * The BER type to use for the AuthenticationChoice element in a bind
     * request when simple authentication is to be used.
     */
    public static final byte TYPE_AUTHENTICATION_SIMPLE = (byte) 0x80;
  /**
   * The BER type to use for the AuthenticationChoice element in a bind request
   * when SASL authentication is to be used.
   */
  public static final byte TYPE_AUTHENTICATION_SASL = (byte) 0xA3;
    /**
     * The BER type to use for the AuthenticationChoice element in a bind
     * request when SASL authentication is to be used.
     */
    public static final byte TYPE_AUTHENTICATION_SASL = (byte) 0xA3;
  /**
   * The BER type to use for the server SASL credentials in a bind response.
   */
  public static final byte TYPE_SERVER_SASL_CREDENTIALS = (byte) 0x87;
    /**
     * The BER type to use for the server SASL credentials in a bind response.
     */
    public static final byte TYPE_SERVER_SASL_CREDENTIALS = (byte) 0x87;
  /**
   * The BER type to use for AND filter components.
   */
  public static final byte TYPE_FILTER_AND = (byte) 0xA0;
    /**
     * The BER type to use for AND filter components.
     */
    public static final byte TYPE_FILTER_AND = (byte) 0xA0;
  /**
   * The BER type to use for OR filter components.
   */
  public static final byte TYPE_FILTER_OR = (byte) 0xA1;
    /**
     * The BER type to use for OR filter components.
     */
    public static final byte TYPE_FILTER_OR = (byte) 0xA1;
  /**
   * The BER type to use for NOT filter components.
   */
  public static final byte TYPE_FILTER_NOT = (byte) 0xA2;
    /**
     * The BER type to use for NOT filter components.
     */
    public static final byte TYPE_FILTER_NOT = (byte) 0xA2;
  /**
   * The BER type to use for equality filter components.
   */
  public static final byte TYPE_FILTER_EQUALITY = (byte) 0xA3;
    /**
     * The BER type to use for equality filter components.
     */
    public static final byte TYPE_FILTER_EQUALITY = (byte) 0xA3;
  /**
   * The BER type to use for substring filter components.
   */
  public static final byte TYPE_FILTER_SUBSTRING = (byte) 0xA4;
    /**
     * The BER type to use for substring filter components.
     */
    public static final byte TYPE_FILTER_SUBSTRING = (byte) 0xA4;
  /**
   * The BER type to use for greater than or equal to filter components.
   */
  public static final byte TYPE_FILTER_GREATER_OR_EQUAL = (byte) 0xA5;
    /**
     * The BER type to use for greater than or equal to filter components.
     */
    public static final byte TYPE_FILTER_GREATER_OR_EQUAL = (byte) 0xA5;
  /**
   * The BER type to use for less than or equal to filter components.
   */
  public static final byte TYPE_FILTER_LESS_OR_EQUAL = (byte) 0xA6;
    /**
     * The BER type to use for less than or equal to filter components.
     */
    public static final byte TYPE_FILTER_LESS_OR_EQUAL = (byte) 0xA6;
  /**
   * The BER type to use for presence filter components.
   */
  public static final byte TYPE_FILTER_PRESENCE = (byte) 0x87;
    /**
     * The BER type to use for presence filter components.
     */
    public static final byte TYPE_FILTER_PRESENCE = (byte) 0x87;
  /**
   * The BER type to use for approximate filter components.
   */
  public static final byte TYPE_FILTER_APPROXIMATE = (byte) 0xA8;
    /**
     * The BER type to use for approximate filter components.
     */
    public static final byte TYPE_FILTER_APPROXIMATE = (byte) 0xA8;
  /**
   * The BER type to use for extensible matching filter components.
   */
  public static final byte TYPE_FILTER_EXTENSIBLE_MATCH = (byte) 0xA9;
    /**
     * The BER type to use for extensible matching filter components.
     */
    public static final byte TYPE_FILTER_EXTENSIBLE_MATCH = (byte) 0xA9;
  /**
   * The BER type to use for the subInitial component of a substring filter.
   */
  public static final byte TYPE_SUBINITIAL = (byte) 0x80;
    /**
     * The BER type to use for the subInitial component of a substring filter.
     */
    public static final byte TYPE_SUBINITIAL = (byte) 0x80;
  /**
   * The BER type to use for the subAny component(s) of a substring filter.
   */
  public static final byte TYPE_SUBANY = (byte) 0x81;
    /**
     * The BER type to use for the subAny component(s) of a substring filter.
     */
    public static final byte TYPE_SUBANY = (byte) 0x81;
  /**
   * The BER type to use for the subFinal components of a substring filter.
   */
  public static final byte TYPE_SUBFINAL = (byte) 0x82;
    /**
     * The BER type to use for the subFinal components of a substring filter.
     */
    public static final byte TYPE_SUBFINAL = (byte) 0x82;
  /**
   * The BER type to use for the matching rule OID in a matching rule assertion.
   */
  public static final byte TYPE_MATCHING_RULE_ID = (byte) 0x81;
    /**
     * The BER type to use for the matching rule OID in a matching rule
     * assertion.
     */
    public static final byte TYPE_MATCHING_RULE_ID = (byte) 0x81;
  /**
   * The BER type to use for the attribute type in a matching rule assertion.
   */
  public static final byte TYPE_MATCHING_RULE_TYPE = (byte) 0x82;
    /**
     * The BER type to use for the attribute type in a matching rule assertion.
     */
    public static final byte TYPE_MATCHING_RULE_TYPE = (byte) 0x82;
  /**
   * The BER type to use for the assertion value in a matching rule assertion.
   */
  public static final byte TYPE_MATCHING_RULE_VALUE = (byte) 0x83;
    /**
     * The BER type to use for the assertion value in a matching rule assertion.
     */
    public static final byte TYPE_MATCHING_RULE_VALUE = (byte) 0x83;
  /**
   * The BER type to use for the DN attributes flag in a matching rule
   * assertion.
   */
  public static final byte TYPE_MATCHING_RULE_DN_ATTRIBUTES = (byte) 0x84;
    /**
     * The BER type to use for the DN attributes flag in a matching rule
     * assertion.
     */
    public static final byte TYPE_MATCHING_RULE_DN_ATTRIBUTES = (byte) 0x84;
  /**
   * The BER type to use for the newSuperior component of a modify DN request.
   */
  public static final byte TYPE_MODIFY_DN_NEW_SUPERIOR = (byte) 0x80;
    /**
     * The BER type to use for the newSuperior component of a modify DN request.
     */
    public static final byte TYPE_MODIFY_DN_NEW_SUPERIOR = (byte) 0x80;
  /**
   * The BER type to use for the OID of an extended request.
   */
  public static final byte TYPE_EXTENDED_REQUEST_OID = (byte) 0x80;
    /**
     * The BER type to use for the OID of an extended request.
     */
    public static final byte TYPE_EXTENDED_REQUEST_OID = (byte) 0x80;
  /**
   * The BER type to use for the value of an extended request.
   */
  public static final byte TYPE_EXTENDED_REQUEST_VALUE = (byte) 0x81;
    /**
     * The BER type to use for the value of an extended request.
     */
    public static final byte TYPE_EXTENDED_REQUEST_VALUE = (byte) 0x81;
  /**
   * The BER type to use for the OID of an extended response.
   */
  public static final byte TYPE_EXTENDED_RESPONSE_OID = (byte) 0x8A;
    /**
     * The BER type to use for the OID of an extended response.
     */
    public static final byte TYPE_EXTENDED_RESPONSE_OID = (byte) 0x8A;
  /**
   * The BER type to use for the value of an extended response.
   */
  public static final byte TYPE_EXTENDED_RESPONSE_VALUE = (byte) 0x8B;
    /**
     * The BER type to use for the value of an extended response.
     */
    public static final byte TYPE_EXTENDED_RESPONSE_VALUE = (byte) 0x8B;
  /**
   * The BER type to use for the OID of an intermediate response message.
   */
  public static final byte TYPE_INTERMEDIATE_RESPONSE_OID = (byte) 0x80;
    /**
     * The BER type to use for the OID of an intermediate response message.
     */
    public static final byte TYPE_INTERMEDIATE_RESPONSE_OID = (byte) 0x80;
  /**
   * The BER type to use for the value of an intermediate response message.
   */
  public static final byte TYPE_INTERMEDIATE_RESPONSE_VALUE = (byte) 0x81;
    /**
     * The BER type to use for the value of an intermediate response message.
     */
    public static final byte TYPE_INTERMEDIATE_RESPONSE_VALUE = (byte) 0x81;
  /**
   * The OID for the Kerberos V GSSAPI mechanism.
   */
  public static final String OID_GSSAPI_KERBEROS_V = "1.2.840.113554.1.2.2";
    /**
     * The OID for the Kerberos V GSSAPI mechanism.
     */
    public static final String OID_GSSAPI_KERBEROS_V = "1.2.840.113554.1.2.2";
  /**
   * The OID for the LDAP notice of disconnection extended operation.
   */
  public static final String OID_NOTICE_OF_DISCONNECTION = "1.3.6.1.4.1.1466.20036";
    /**
     * The OID for the LDAP notice of disconnection extended operation.
     */
    public static final String OID_NOTICE_OF_DISCONNECTION = "1.3.6.1.4.1.1466.20036";
  /**
   * The ASN.1 element decoding state that indicates that the next byte read
   * should be the BER type for a new element.
   */
  public static final int ELEMENT_READ_STATE_NEED_TYPE = 0;
    /**
     * The ASN.1 element decoding state that indicates that the next byte read
     * should be the BER type for a new element.
     */
    public static final int ELEMENT_READ_STATE_NEED_TYPE = 0;
  /**
   * The ASN.1 element decoding state that indicates that the next byte read
   * should be the first byte for the element length.
   */
  public static final int ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE = 1;
    /**
     * The ASN.1 element decoding state that indicates that the next byte read
     * should be the first byte for the element length.
     */
    public static final int ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE = 1;
  /**
   * The ASN.1 element decoding state that indicates that the next byte read
   * should be additional bytes of a multi-byte length.
   */
  public static final int ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES = 2;
    /**
     * The ASN.1 element decoding state that indicates that the next byte read
     * should be additional bytes of a multi-byte length.
     */
    public static final int ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES = 2;
  /**
   * The ASN.1 element decoding state that indicates that the next byte read
   * should be applied to the value of the element.
   */
  public static final int ELEMENT_READ_STATE_NEED_VALUE_BYTES = 3;
    /**
     * The ASN.1 element decoding state that indicates that the next byte read
     * should be applied to the value of the element.
     */
    public static final int ELEMENT_READ_STATE_NEED_VALUE_BYTES = 3;
  private LDAPConstants()
  {
    // Prevent instantiation.
  }
    private LDAPConstants() {
        // Prevent instantiation.
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPExtendedFutureResultImpl.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,87 +27,67 @@
package com.forgerock.opendj.ldap;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.DecodeOptions;
import org.forgerock.opendj.ldap.IntermediateResponseHandler;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.requests.ExtendedRequest;
import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
/**
 * Extended result future implementation.
 *
 * @param <R>
 *          The type of result returned by this future.
 *            The type of result returned by this future.
 */
final class LDAPExtendedFutureResultImpl<R extends ExtendedResult> extends
    AbstractLDAPFutureResultImpl<R>
{
  private final ExtendedRequest<R> request;
        AbstractLDAPFutureResultImpl<R> {
    private final ExtendedRequest<R> request;
    LDAPExtendedFutureResultImpl(final int requestID, final ExtendedRequest<R> request,
            final ResultHandler<? super R> resultHandler,
            final IntermediateResponseHandler intermediateResponseHandler,
            final Connection connection) {
        super(requestID, resultHandler, intermediateResponseHandler, connection);
        this.request = request;
    }
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("LDAPExtendedFutureResultImpl(");
        sb.append("request = ");
        sb.append(request);
        super.toString(sb);
        sb.append(")");
        return sb.toString();
    }
  LDAPExtendedFutureResultImpl(final int requestID,
      final ExtendedRequest<R> request,
      final ResultHandler<? super R> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler,
      final Connection connection)
  {
    super(requestID, resultHandler, intermediateResponseHandler, connection);
    this.request = request;
  }
    /**
     * {@inheritDoc}
     */
    @Override
    protected boolean isCancelable() {
        return !request.getOID().equals(StartTLSExtendedRequest.OID);
    }
    R decodeResult(final ExtendedResult result, final DecodeOptions options) throws DecodeException {
        return request.getResultDecoder().decodeExtendedResult(result, options);
    }
    ExtendedRequest<R> getRequest() {
        return request;
    }
  @Override
  public String toString()
  {
    final StringBuilder sb = new StringBuilder();
    sb.append("LDAPExtendedFutureResultImpl(");
    sb.append("request = ");
    sb.append(request);
    super.toString(sb);
    sb.append(")");
    return sb.toString();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  protected boolean isCancelable() {
    return !request.getOID().equals(StartTLSExtendedRequest.OID);
  }
  R decodeResult(final ExtendedResult result, final DecodeOptions options)
      throws DecodeException
  {
    return request.getResultDecoder().decodeExtendedResult(result, options);
  }
  ExtendedRequest<R> getRequest()
  {
    return request;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  R newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
      final Throwable cause)
  {
    return request.getResultDecoder().newExtendedErrorResult(resultCode, "",
        diagnosticMessage);
  }
    /**
     * {@inheritDoc}
     */
    @Override
    R newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
            final Throwable cause) {
        return request.getResultDecoder().newExtendedErrorResult(resultCode, "", diagnosticMessage);
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPFutureResultImpl.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,8 +27,6 @@
package com.forgerock.opendj.ldap;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.IntermediateResponseHandler;
import org.forgerock.opendj.ldap.ResultCode;
@@ -38,57 +35,42 @@
import org.forgerock.opendj.ldap.responses.Responses;
import org.forgerock.opendj.ldap.responses.Result;
/**
 * Result future implementation.
 */
final class LDAPFutureResultImpl extends AbstractLDAPFutureResultImpl<Result>
{
  private final Request request;
final class LDAPFutureResultImpl extends AbstractLDAPFutureResultImpl<Result> {
    private final Request request;
    LDAPFutureResultImpl(final int requestID, final Request request,
            final ResultHandler<? super Result> resultHandler,
            final IntermediateResponseHandler intermediateResponseHandler,
            final Connection connection) {
        super(requestID, resultHandler, intermediateResponseHandler, connection);
        this.request = request;
    }
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("LDAPFutureResultImpl(");
        sb.append("request = ");
        sb.append(request);
        super.toString(sb);
        sb.append(")");
        return sb.toString();
    }
  LDAPFutureResultImpl(final int requestID, final Request request,
      final ResultHandler<? super Result> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler,
      final Connection connection)
  {
    super(requestID, resultHandler, intermediateResponseHandler, connection);
    this.request = request;
  }
    Request getRequest() {
        return request;
    }
  @Override
  public String toString()
  {
    final StringBuilder sb = new StringBuilder();
    sb.append("LDAPFutureResultImpl(");
    sb.append("request = ");
    sb.append(request);
    super.toString(sb);
    sb.append(")");
    return sb.toString();
  }
  Request getRequest()
  {
    return request;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  Result newErrorResult(final ResultCode resultCode,
      final String diagnosticMessage, final Throwable cause)
  {
    return Responses.newResult(resultCode)
        .setDiagnosticMessage(diagnosticMessage).setCause(cause);
  }
    /**
     * {@inheritDoc}
     */
    @Override
    Result newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
            final Throwable cause) {
        return Responses.newResult(resultCode).setDiagnosticMessage(diagnosticMessage).setCause(
                cause);
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPListenerImpl.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,8 +27,6 @@
package com.forgerock.opendj.ldap;
import java.io.Closeable;
import java.io.IOException;
import java.net.SocketAddress;
@@ -47,126 +44,93 @@
import com.forgerock.opendj.util.StaticUtils;
/**
 * LDAP listener implementation.
 */
public final class LDAPListenerImpl implements Closeable
{
  private final TCPNIOTransport transport;
  private final FilterChain defaultFilterChain;
  private final ServerConnectionFactory<LDAPClientContext, Integer> connectionFactory;
  private final TCPNIOServerConnection serverConnection;
public final class LDAPListenerImpl implements Closeable {
    private final TCPNIOTransport transport;
    private final FilterChain defaultFilterChain;
    private final ServerConnectionFactory<LDAPClientContext, Integer> connectionFactory;
    private final TCPNIOServerConnection serverConnection;
    /**
     * Creates a new LDAP listener implementation which will listen for LDAP
     * client connections using the provided address and connection options.
     *
     * @param address
     *            The address to listen on.
     * @param factory
     *            The server connection factory which will be used to create
     *            server connections.
     * @param options
     *            The LDAP listener options.
     * @throws IOException
     *             If an error occurred while trying to listen on the provided
     *             address.
     */
    public LDAPListenerImpl(final SocketAddress address,
            final ServerConnectionFactory<LDAPClientContext, Integer> factory,
            final LDAPListenerOptions options) throws IOException {
        if (options.getTCPNIOTransport() == null) {
            this.transport = DefaultTCPNIOTransport.getInstance();
        } else {
            this.transport = options.getTCPNIOTransport();
        }
        this.connectionFactory = factory;
        final DecodeOptions decodeOptions = new DecodeOptions(options.getDecodeOptions());
        this.defaultFilterChain =
                FilterChainBuilder.stateless().add(new TransportFilter()).add(
                        new LDAPServerFilter(this, new LDAPReader(decodeOptions), 0)).build();
  /**
   * Creates a new LDAP listener implementation which will listen for LDAP
   * client connections using the provided address and connection options.
   *
   * @param address
   *          The address to listen on.
   * @param factory
   *          The server connection factory which will be used to create server
   *          connections.
   * @param options
   *          The LDAP listener options.
   * @throws IOException
   *           If an error occurred while trying to listen on the provided
   *           address.
   */
  public LDAPListenerImpl(final SocketAddress address,
      final ServerConnectionFactory<LDAPClientContext, Integer> factory,
      final LDAPListenerOptions options) throws IOException
  {
    if (options.getTCPNIOTransport() == null)
    {
      this.transport = DefaultTCPNIOTransport.getInstance();
        this.serverConnection = transport.bind(address, options.getBacklog());
        this.serverConnection.setProcessor(defaultFilterChain);
    }
    else
    {
      this.transport = options.getTCPNIOTransport();
    /**
     * {@inheritDoc}
     */
    @Override
    public void close() {
        try {
            serverConnection.close().get();
        } catch (final InterruptedException e) {
            // Cannot handle here.
            Thread.currentThread().interrupt();
        } catch (final Exception e) {
            // Ignore the exception.
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING)) {
                StaticUtils.DEBUG_LOG.log(Level.WARNING,
                        "Exception occurred while closing listener:" + e.getMessage(), e);
            }
        }
    }
    this.connectionFactory = factory;
    final DecodeOptions decodeOptions = new DecodeOptions(options
        .getDecodeOptions());
    this.defaultFilterChain = FilterChainBuilder
        .stateless()
        .add(new TransportFilter())
        .add(new LDAPServerFilter(this, new LDAPReader(decodeOptions), 0))
        .build();
    this.serverConnection = transport.bind(address, options.getBacklog());
    this.serverConnection.setProcessor(defaultFilterChain);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void close()
  {
    try
    {
      serverConnection.close().get();
    /**
     * Returns the address that this LDAP listener is listening on.
     *
     * @return The address that this LDAP listener is listening on.
     */
    public SocketAddress getSocketAddress() {
        return serverConnection.getLocalAddress();
    }
    catch (final InterruptedException e)
    {
      // Cannot handle here.
      Thread.currentThread().interrupt();
    /**
     * {@inheritDoc}
     */
    public String toString() {
        final StringBuilder builder = new StringBuilder();
        builder.append("LDAPListener(");
        builder.append(getSocketAddress().toString());
        builder.append(')');
        return builder.toString();
    }
    catch (final Exception e)
    {
      // Ignore the exception.
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING))
      {
        StaticUtils.DEBUG_LOG.log(Level.WARNING,
            "Exception occurred while closing listener:" + e.getMessage(), e);
      }
    ServerConnectionFactory<LDAPClientContext, Integer> getConnectionFactory() {
        return connectionFactory;
    }
  }
  /**
   * Returns the address that this LDAP listener is listening on.
   *
   * @return The address that this LDAP listener is listening on.
   */
  public SocketAddress getSocketAddress()
  {
    return serverConnection.getLocalAddress();
  }
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    final StringBuilder builder = new StringBuilder();
    builder.append("LDAPListener(");
    builder.append(getSocketAddress().toString());
    builder.append(')');
    return builder.toString();
  }
  ServerConnectionFactory<LDAPClientContext, Integer> getConnectionFactory()
  {
    return connectionFactory;
  }
  FilterChain getDefaultFilterChain()
  {
    return defaultFilterChain;
  }
    FilterChain getDefaultFilterChain() {
        return defaultFilterChain;
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPMessageHandler.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -27,133 +26,97 @@
package com.forgerock.opendj.ldap;
import java.io.IOException;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.requests.*;
import org.forgerock.opendj.ldap.responses.*;
import org.forgerock.opendj.ldap.requests.AbandonRequest;
import org.forgerock.opendj.ldap.requests.AddRequest;
import org.forgerock.opendj.ldap.requests.CompareRequest;
import org.forgerock.opendj.ldap.requests.DeleteRequest;
import org.forgerock.opendj.ldap.requests.ExtendedRequest;
import org.forgerock.opendj.ldap.requests.GenericBindRequest;
import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
import org.forgerock.opendj.ldap.requests.ModifyRequest;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.requests.UnbindRequest;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.CompareResult;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
import org.forgerock.opendj.ldap.responses.IntermediateResponse;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
/**
 * LDAP message handler interface.
 *
 * @param <P>
 *          A user provided handler parameter.
 *            A user provided handler parameter.
 */
interface LDAPMessageHandler<P>
{
  void abandonRequest(P param, int messageID, AbandonRequest request)
      throws UnexpectedRequestException, IOException;
interface LDAPMessageHandler<P> {
    void abandonRequest(P param, int messageID, AbandonRequest request)
            throws UnexpectedRequestException, IOException;
    void addRequest(P param, int messageID, AddRequest request) throws UnexpectedRequestException,
            IOException;
    void addResult(P param, int messageID, Result result) throws UnexpectedResponseException,
            IOException;
  void addRequest(P param, int messageID, AddRequest request)
      throws UnexpectedRequestException, IOException;
    void bindRequest(P param, int messageID, int version, GenericBindRequest request)
            throws UnexpectedRequestException, IOException;
    void bindResult(P param, int messageID, BindResult result) throws UnexpectedResponseException,
            IOException;
    void compareRequest(P param, int messageID, CompareRequest request)
            throws UnexpectedRequestException, IOException;
  void addResult(P param, int messageID, Result result)
      throws UnexpectedResponseException, IOException;
    void compareResult(P param, int messageID, CompareResult result)
            throws UnexpectedResponseException, IOException;
    void deleteRequest(P param, int messageID, DeleteRequest request)
            throws UnexpectedRequestException, IOException;
    void deleteResult(P param, int messageID, Result result) throws UnexpectedResponseException,
            IOException;
  void bindRequest(P param, int messageID, int version,
      GenericBindRequest request) throws UnexpectedRequestException,
      IOException;
    <R extends ExtendedResult> void extendedRequest(P param, int messageID,
            ExtendedRequest<R> request) throws UnexpectedRequestException, IOException;
    void extendedResult(P param, int messageID, ExtendedResult result)
            throws UnexpectedResponseException, IOException;
    void intermediateResponse(P param, int messageID, IntermediateResponse response)
            throws UnexpectedResponseException, IOException;
  void bindResult(P param, int messageID, BindResult result)
      throws UnexpectedResponseException, IOException;
    void modifyDNRequest(P param, int messageID, ModifyDNRequest request)
            throws UnexpectedRequestException, IOException;
    void modifyDNResult(P param, int messageID, Result result) throws UnexpectedResponseException,
            IOException;
    void modifyRequest(P param, int messageID, ModifyRequest request)
            throws UnexpectedRequestException, IOException;
  void compareRequest(P param, int messageID, CompareRequest request)
      throws UnexpectedRequestException, IOException;
    void modifyResult(P param, int messageID, Result result) throws UnexpectedResponseException,
            IOException;
    void searchRequest(P param, int messageID, SearchRequest request)
            throws UnexpectedRequestException, IOException;
    void searchResult(P param, int messageID, Result result) throws UnexpectedResponseException,
            IOException;
  void compareResult(P param, int messageID, CompareResult result)
      throws UnexpectedResponseException, IOException;
    void searchResultEntry(P param, int messageID, SearchResultEntry entry)
            throws UnexpectedResponseException, IOException;
    void searchResultReference(P param, int messageID, SearchResultReference reference)
            throws UnexpectedResponseException, IOException;
    void unbindRequest(P param, int messageID, UnbindRequest request)
            throws UnexpectedRequestException, IOException;
  void deleteRequest(P param, int messageID, DeleteRequest request)
      throws UnexpectedRequestException, IOException;
  void deleteResult(P param, int messageID, Result result)
      throws UnexpectedResponseException, IOException;
  <R extends ExtendedResult> void extendedRequest(P param, int messageID,
      ExtendedRequest<R> request) throws UnexpectedRequestException,
      IOException;
  void extendedResult(P param, int messageID, ExtendedResult result)
      throws UnexpectedResponseException, IOException;
  void intermediateResponse(P param, int messageID,
      IntermediateResponse response) throws UnexpectedResponseException,
      IOException;
  void modifyDNRequest(P param, int messageID, ModifyDNRequest request)
      throws UnexpectedRequestException, IOException;
  void modifyDNResult(P param, int messageID, Result result)
      throws UnexpectedResponseException, IOException;
  void modifyRequest(P param, int messageID, ModifyRequest request)
      throws UnexpectedRequestException, IOException;
  void modifyResult(P param, int messageID, Result result)
      throws UnexpectedResponseException, IOException;
  void searchRequest(P param, int messageID, SearchRequest request)
      throws UnexpectedRequestException, IOException;
  void searchResult(P param, int messageID, Result result)
      throws UnexpectedResponseException, IOException;
  void searchResultEntry(P param, int messageID, SearchResultEntry entry)
      throws UnexpectedResponseException, IOException;
  void searchResultReference(P param, int messageID,
      SearchResultReference reference) throws UnexpectedResponseException,
      IOException;
  void unbindRequest(P param, int messageID, UnbindRequest request)
      throws UnexpectedRequestException, IOException;
  void unrecognizedMessage(P param, int messageID, byte messageTag,
      ByteString messageBytes) throws UnsupportedMessageException, IOException;
    void unrecognizedMessage(P param, int messageID, byte messageTag, ByteString messageBytes)
            throws UnsupportedMessageException, IOException;
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPReader.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,8 +27,6 @@
package com.forgerock.opendj.ldap;
import static com.forgerock.opendj.ldap.LDAPConstants.*;
import static org.forgerock.opendj.asn1.ASN1Constants.UNIVERSAL_BOOLEAN_TYPE;
import static org.forgerock.opendj.asn1.ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE;
@@ -42,1778 +39,1444 @@
import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.forgerock.opendj.asn1.ASN1Reader;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.AttributeDescription;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.DecodeOptions;
import org.forgerock.opendj.ldap.DereferenceAliasesPolicy;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.Filter;
import org.forgerock.opendj.ldap.Modification;
import org.forgerock.opendj.ldap.ModificationType;
import org.forgerock.opendj.ldap.RDN;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.controls.Control;
import org.forgerock.opendj.ldap.controls.GenericControl;
import org.forgerock.opendj.ldap.requests.*;
import org.forgerock.opendj.ldap.responses.*;
import org.forgerock.opendj.ldap.requests.AbandonRequest;
import org.forgerock.opendj.ldap.requests.AddRequest;
import org.forgerock.opendj.ldap.requests.CompareRequest;
import org.forgerock.opendj.ldap.requests.DeleteRequest;
import org.forgerock.opendj.ldap.requests.GenericBindRequest;
import org.forgerock.opendj.ldap.requests.GenericExtendedRequest;
import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
import org.forgerock.opendj.ldap.requests.ModifyRequest;
import org.forgerock.opendj.ldap.requests.Request;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.requests.UnbindRequest;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.CompareResult;
import org.forgerock.opendj.ldap.responses.GenericExtendedResult;
import org.forgerock.opendj.ldap.responses.GenericIntermediateResponse;
import org.forgerock.opendj.ldap.responses.Response;
import org.forgerock.opendj.ldap.responses.Responses;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
import org.forgerock.opendj.ldap.schema.Schema;
import com.forgerock.opendj.util.StaticUtils;
/**
 * Static methods for decoding LDAP messages.
 */
final class LDAPReader
{
  static SearchResultEntry decodeEntry(final ASN1Reader reader,
      final DecodeOptions options) throws IOException
  {
    Entry entry;
final class LDAPReader {
    static SearchResultEntry decodeEntry(final ASN1Reader reader, final DecodeOptions options)
            throws IOException {
        Entry entry;
    reader.readStartSequence(OP_TYPE_SEARCH_RESULT_ENTRY);
    try
    {
      final String dnString = reader.readOctetStringAsString();
      final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
      DN dn;
      try
      {
        dn = DN.valueOf(dnString, schema);
      }
      catch (final LocalizedIllegalArgumentException e)
      {
        throw DecodeException.error(e.getMessageObject());
      }
      entry = options.getEntryFactory().newEntry(dn);
      reader.readStartSequence();
      try
      {
        while (reader.hasNextElement())
        {
          reader.readStartSequence();
          try
          {
            final String ads = reader.readOctetStringAsString();
            AttributeDescription ad;
            try
            {
              ad = AttributeDescription.valueOf(ads, schema);
            }
            catch (final LocalizedIllegalArgumentException e)
            {
              throw DecodeException.error(e.getMessageObject());
        reader.readStartSequence(OP_TYPE_SEARCH_RESULT_ENTRY);
        try {
            final String dnString = reader.readOctetStringAsString();
            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
            DN dn;
            try {
                dn = DN.valueOf(dnString, schema);
            } catch (final LocalizedIllegalArgumentException e) {
                throw DecodeException.error(e.getMessageObject());
            }
            final Attribute attribute = options.getAttributeFactory()
                .newAttribute(ad);
            reader.readStartSet();
            try
            {
              while (reader.hasNextElement())
              {
                attribute.add(reader.readOctetString());
              }
              entry.addAttribute(attribute);
            }
            finally
            {
              reader.readEndSet();
            }
          }
          finally
          {
            reader.readEndSequence();
          }
        }
      }
      finally
      {
        reader.readEndSequence();
      }
    }
    finally
    {
      reader.readEndSequence();
    }
    return Responses.newSearchResultEntry(entry);
  }
  private final DecodeOptions options;
  LDAPReader(final DecodeOptions options)
  {
    this.options = options;
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as an LDAP message.
   *
   * @param <P>
   *          The type of {@code param}.
   * @param reader
   *          The ASN.1 reader.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle a decoded
   *          message.
   * @param param
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  <P> void decode(final ASN1Reader reader, final LDAPMessageHandler<P> handler,
      final P param) throws IOException
  {
    reader.readStartSequence();
    try
    {
      final int messageID = (int) reader.readInteger();
      decodeProtocolOp(reader, messageID, handler, param);
    }
    finally
    {
      reader.readEndSequence();
    }
  }
  /**
   * Decodes the elements from the provided ASN.1 read as an LDAP abandon
   * request protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeAbandonRequest(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    final int msgToAbandon = (int) reader.readInteger(OP_TYPE_ABANDON_REQUEST);
    final AbandonRequest message = Requests.newAbandonRequest(msgToAbandon);
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP ABANDON REQUEST(messageID=%d, request=%s)", messageID,
          message));
    }
    handler.abandonRequest(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as an LDAP add request
   * protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeAddRequest(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    Entry entry;
    reader.readStartSequence(OP_TYPE_ADD_REQUEST);
    try
    {
      final String dnString = reader.readOctetStringAsString();
      final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
      final DN dn = decodeDN(dnString, schema);
      entry = options.getEntryFactory().newEntry(dn);
      reader.readStartSequence();
      try
      {
        while (reader.hasNextElement())
        {
          reader.readStartSequence();
          try
          {
            final String ads = reader.readOctetStringAsString();
            final AttributeDescription ad = decodeAttributeDescription(ads,
                schema);
            final Attribute attribute = options.getAttributeFactory()
                .newAttribute(ad);
            reader.readStartSet();
            try
            {
              while (reader.hasNextElement())
              {
                attribute.add(reader.readOctetString());
              }
              entry.addAttribute(attribute);
            }
            finally
            {
              reader.readEndSet();
            }
          }
          finally
          {
            reader.readEndSequence();
          }
        }
      }
      finally
      {
        reader.readEndSequence();
      }
    }
    finally
    {
      reader.readEndSequence();
    }
    final AddRequest message = Requests.newAddRequest(entry);
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP ADD REQUEST(messageID=%d, request=%s)", messageID,
          message));
    }
    handler.addRequest(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as an add response
   * protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeAddResult(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    Result message;
    reader.readStartSequence(OP_TYPE_ADD_RESPONSE);
    try
    {
      final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
      final String matchedDN = reader.readOctetStringAsString();
      final String diagnosticMessage = reader.readOctetStringAsString();
      message = Responses.newResult(resultCode).setMatchedDN(matchedDN)
          .setDiagnosticMessage(diagnosticMessage);
      decodeResponseReferrals(reader, message);
    }
    finally
    {
      reader.readEndSequence();
    }
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String
          .format("DECODE LDAP ADD RESULT(messageID=%d, result=%s)", messageID,
              message));
    }
    handler.addResult(p, messageID, message);
  }
  private AttributeDescription decodeAttributeDescription(
      final String attributeDescription, final Schema schema)
      throws DecodeException
  {
    try
    {
      return AttributeDescription.valueOf(attributeDescription, schema);
    }
    catch (final LocalizedIllegalArgumentException e)
    {
      throw DecodeException.error(e.getMessageObject());
    }
  }
  /**
   * Decodes the elements from the provided ASN.1 read as an LDAP bind request
   * protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeBindRequest(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    reader.readStartSequence(OP_TYPE_BIND_REQUEST);
    try
    {
      final int protocolVersion = (int) reader.readInteger();
      final String authName = reader.readOctetStringAsString();
      final byte authType = reader.peekType();
      final byte[] authBytes = reader.readOctetString(authType).toByteArray();
      final GenericBindRequest request = Requests.newGenericBindRequest(
          authName, authType, authBytes);
      decodeControls(reader, request);
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
      {
        StaticUtils.DEBUG_LOG.finer(String.format(
            "DECODE LDAP BIND REQUEST(messageID=%d, auth=0x%x, request=%s)",
            messageID, request.getAuthenticationType(), request));
      }
      handler.bindRequest(p, messageID, protocolVersion, request);
    }
    finally
    {
      reader.readEndSequence();
    }
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as a bind response
   * protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeBindResult(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    BindResult message;
    reader.readStartSequence(OP_TYPE_BIND_RESPONSE);
    try
    {
      final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
      final String matchedDN = reader.readOctetStringAsString();
      final String diagnosticMessage = reader.readOctetStringAsString();
      message = Responses.newBindResult(resultCode).setMatchedDN(matchedDN)
          .setDiagnosticMessage(diagnosticMessage);
      decodeResponseReferrals(reader, message);
      if (reader.hasNextElement()
          && (reader.peekType() == TYPE_SERVER_SASL_CREDENTIALS))
      {
        message.setServerSASLCredentials(reader
            .readOctetString(TYPE_SERVER_SASL_CREDENTIALS));
      }
    }
    finally
    {
      reader.readEndSequence();
    }
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP BIND RESULT(messageID=%d, result=%s)", messageID,
          message));
    }
    handler.bindResult(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as an LDAP compare
   * request protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeCompareRequest(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    CompareRequest message;
    reader.readStartSequence(OP_TYPE_COMPARE_REQUEST);
    try
    {
      final String dnString = reader.readOctetStringAsString();
      final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
      final DN dn = decodeDN(dnString, schema);
      reader.readStartSequence();
      try
      {
        final String ads = reader.readOctetStringAsString();
        final AttributeDescription ad = decodeAttributeDescription(ads, schema);
        final ByteString assertionValue = reader.readOctetString();
        message = Requests.newCompareRequest(dn, ad, assertionValue);
      }
      finally
      {
        reader.readEndSequence();
      }
    }
    finally
    {
      reader.readEndSequence();
    }
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP COMPARE REQUEST(messageID=%d, request=%s)", messageID,
          message));
    }
    handler.compareRequest(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as a compare response
   * protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeCompareResult(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    CompareResult message;
    reader.readStartSequence(OP_TYPE_COMPARE_RESPONSE);
    try
    {
      final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
      final String matchedDN = reader.readOctetStringAsString();
      final String diagnosticMessage = reader.readOctetStringAsString();
      message = Responses.newCompareResult(resultCode).setMatchedDN(matchedDN)
          .setDiagnosticMessage(diagnosticMessage);
      decodeResponseReferrals(reader, message);
    }
    finally
    {
      reader.readEndSequence();
    }
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP COMPARE RESULT(messageID=%d, result=%s)", messageID,
          message));
    }
    handler.compareResult(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as an LDAP control.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param request
   *          The decoded request to decode controls for.
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private void decodeControl(final ASN1Reader reader, final Request request)
      throws IOException
  {
    String oid;
    boolean isCritical;
    ByteString value;
    reader.readStartSequence();
    try
    {
      oid = reader.readOctetStringAsString();
      isCritical = false;
      value = null;
      if (reader.hasNextElement()
          && (reader.peekType() == UNIVERSAL_BOOLEAN_TYPE))
      {
        isCritical = reader.readBoolean();
      }
      if (reader.hasNextElement()
          && (reader.peekType() == UNIVERSAL_OCTET_STRING_TYPE))
      {
        value = reader.readOctetString();
      }
    }
    finally
    {
      reader.readEndSequence();
    }
    final Control c = GenericControl.newControl(oid, isCritical, value);
    request.addControl(c);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as an LDAP control.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param response
   *          The decoded message to decode controls for.
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private void decodeControl(final ASN1Reader reader, final Response response)
      throws IOException
  {
    String oid;
    boolean isCritical;
    ByteString value;
    reader.readStartSequence();
    try
    {
      oid = reader.readOctetStringAsString();
      isCritical = false;
      value = null;
      if (reader.hasNextElement()
          && (reader.peekType() == UNIVERSAL_BOOLEAN_TYPE))
      {
        isCritical = reader.readBoolean();
      }
      if (reader.hasNextElement()
          && (reader.peekType() == UNIVERSAL_OCTET_STRING_TYPE))
      {
        value = reader.readOctetString();
      }
    }
    finally
    {
      reader.readEndSequence();
    }
    final Control c = GenericControl.newControl(oid, isCritical, value);
    response.addControl(c);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as a set of controls.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param request
   *          The decoded message to decode controls for.
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private void decodeControls(final ASN1Reader reader, final Request request)
      throws IOException
  {
    if (reader.hasNextElement() && (reader.peekType() == TYPE_CONTROL_SEQUENCE))
    {
      reader.readStartSequence(TYPE_CONTROL_SEQUENCE);
      try
      {
        while (reader.hasNextElement())
        {
          decodeControl(reader, request);
        }
      }
      finally
      {
        reader.readEndSequence();
      }
    }
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as a set of controls.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param response
   *          The decoded message to decode controls for.
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private void decodeControls(final ASN1Reader reader, final Response response)
      throws IOException
  {
    if (reader.hasNextElement() && (reader.peekType() == TYPE_CONTROL_SEQUENCE))
    {
      reader.readStartSequence(TYPE_CONTROL_SEQUENCE);
      try
      {
        while (reader.hasNextElement())
        {
          decodeControl(reader, response);
        }
      }
      finally
      {
        reader.readEndSequence();
      }
    }
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as an LDAP delete
   * request protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeDeleteRequest(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    final String dnString = reader
        .readOctetStringAsString(OP_TYPE_DELETE_REQUEST);
    final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
    final DN dn = decodeDN(dnString, schema);
    final DeleteRequest message = Requests.newDeleteRequest(dn);
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP DELETE REQUEST(messageID=%d, request=%s)", messageID,
          message));
    }
    handler.deleteRequest(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as a delete response
   * protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeDeleteResult(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    Result message;
    reader.readStartSequence(OP_TYPE_DELETE_RESPONSE);
    try
    {
      final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
      final String matchedDN = reader.readOctetStringAsString();
      final String diagnosticMessage = reader.readOctetStringAsString();
      message = Responses.newResult(resultCode).setMatchedDN(matchedDN)
          .setDiagnosticMessage(diagnosticMessage);
      decodeResponseReferrals(reader, message);
    }
    finally
    {
      reader.readEndSequence();
    }
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP DELETE RESULT(messageID=%d, result=%s)", messageID,
          message));
    }
    handler.deleteResult(p, messageID, message);
  }
  private DN decodeDN(final String dn, final Schema schema)
      throws DecodeException
  {
    try
    {
      return DN.valueOf(dn, schema);
    }
    catch (final LocalizedIllegalArgumentException e)
    {
      throw DecodeException.error(e.getMessageObject());
    }
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as an LDAP extended
   * request protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeExtendedRequest(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    String oid;
    ByteString value;
    reader.readStartSequence(OP_TYPE_EXTENDED_REQUEST);
    try
    {
      oid = reader.readOctetStringAsString(TYPE_EXTENDED_REQUEST_OID);
      value = null;
      if (reader.hasNextElement()
          && (reader.peekType() == TYPE_EXTENDED_REQUEST_VALUE))
      {
        value = reader.readOctetString(TYPE_EXTENDED_REQUEST_VALUE);
      }
    }
    finally
    {
      reader.readEndSequence();
    }
    final GenericExtendedRequest message = Requests.newGenericExtendedRequest(
        oid, value);
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP EXTENDED REQUEST(messageID=%d, request=%s)", messageID,
          message));
    }
    handler.extendedRequest(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as a extended response
   * protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeExtendedResult(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    GenericExtendedResult message;
    reader.readStartSequence(OP_TYPE_EXTENDED_RESPONSE);
    try
    {
      final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
      final String matchedDN = reader.readOctetStringAsString();
      final String diagnosticMessage = reader.readOctetStringAsString();
      message = Responses.newGenericExtendedResult(resultCode).setMatchedDN(
          matchedDN).setDiagnosticMessage(diagnosticMessage);
      decodeResponseReferrals(reader, message);
      if (reader.hasNextElement()
          && (reader.peekType() == TYPE_EXTENDED_RESPONSE_OID))
      {
        message.setOID(reader
            .readOctetStringAsString(TYPE_EXTENDED_RESPONSE_OID));
      }
      if (reader.hasNextElement()
          && (reader.peekType() == TYPE_EXTENDED_RESPONSE_VALUE))
      {
        message.setValue(reader.readOctetString(TYPE_EXTENDED_RESPONSE_VALUE));
      }
    }
    finally
    {
      reader.readEndSequence();
    }
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP EXTENDED RESULT(messageID=%d, result=%s)", messageID,
          message));
    }
    handler.extendedResult(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as an LDAP intermediate
   * response protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeIntermediateResponse(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    GenericIntermediateResponse message;
    reader.readStartSequence(OP_TYPE_INTERMEDIATE_RESPONSE);
    try
    {
      message = Responses.newGenericIntermediateResponse();
      if (reader.hasNextElement()
          && (reader.peekType() == TYPE_INTERMEDIATE_RESPONSE_OID))
      {
        message.setOID(reader
            .readOctetStringAsString(TYPE_INTERMEDIATE_RESPONSE_OID));
      }
      if (reader.hasNextElement()
          && (reader.peekType() == TYPE_INTERMEDIATE_RESPONSE_VALUE))
      {
        message.setValue(reader
            .readOctetString(TYPE_INTERMEDIATE_RESPONSE_VALUE));
      }
    }
    finally
    {
      reader.readEndSequence();
    }
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP INTERMEDIATE RESPONSE(messageID=%d, response=%s)",
          messageID, message));
    }
    handler.intermediateResponse(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as a modify DN request
   * protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeModifyDNRequest(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    ModifyDNRequest message;
    reader.readStartSequence(OP_TYPE_MODIFY_DN_REQUEST);
    try
    {
      final String dnString = reader.readOctetStringAsString();
      final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
      final DN dn = decodeDN(dnString, schema);
      final String newRDNString = reader.readOctetStringAsString();
      final RDN newRDN = decodeRDN(newRDNString, schema);
      message = Requests.newModifyDNRequest(dn, newRDN);
      message.setDeleteOldRDN(reader.readBoolean());
      if (reader.hasNextElement()
          && (reader.peekType() == TYPE_MODIFY_DN_NEW_SUPERIOR))
      {
        final String newSuperiorString = reader
            .readOctetStringAsString(TYPE_MODIFY_DN_NEW_SUPERIOR);
        final DN newSuperior = decodeDN(newSuperiorString, schema);
        message.setNewSuperior(newSuperior);
      }
    }
    finally
    {
      reader.readEndSequence();
    }
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP MODIFY DN REQUEST(messageID=%d, request=%s)", messageID,
          message));
    }
    handler.modifyDNRequest(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as a modify DN response
   * protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeModifyDNResult(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    Result message;
    reader.readStartSequence(OP_TYPE_MODIFY_DN_RESPONSE);
    try
    {
      final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
      final String matchedDN = reader.readOctetStringAsString();
      final String diagnosticMessage = reader.readOctetStringAsString();
      message = Responses.newResult(resultCode).setMatchedDN(matchedDN)
          .setDiagnosticMessage(diagnosticMessage);
      decodeResponseReferrals(reader, message);
    }
    finally
    {
      reader.readEndSequence();
    }
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP MODIFY DN RESULT(messageID=%d, result=%s)", messageID,
          message));
    }
    handler.modifyDNResult(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as an LDAP modify
   * request protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeModifyRequest(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    ModifyRequest message;
    reader.readStartSequence(OP_TYPE_MODIFY_REQUEST);
    try
    {
      final String dnString = reader.readOctetStringAsString();
      final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
      final DN dn = decodeDN(dnString, schema);
      message = Requests.newModifyRequest(dn);
      reader.readStartSequence();
      try
      {
        while (reader.hasNextElement())
        {
          reader.readStartSequence();
          try
          {
            final int typeIntValue = reader.readEnumerated();
            final ModificationType type = ModificationType
                .valueOf(typeIntValue);
            if (type == null)
            {
              throw DecodeException
                  .error(ERR_LDAP_MODIFICATION_DECODE_INVALID_MOD_TYPE
                      .get(typeIntValue));
            }
            entry = options.getEntryFactory().newEntry(dn);
            reader.readStartSequence();
            try
            {
              final String ads = reader.readOctetStringAsString();
              final AttributeDescription ad = decodeAttributeDescription(ads,
                  schema);
              final Attribute attribute = options.getAttributeFactory()
                  .newAttribute(ad);
            try {
                while (reader.hasNextElement()) {
                    reader.readStartSequence();
                    try {
                        final String ads = reader.readOctetStringAsString();
                        AttributeDescription ad;
                        try {
                            ad = AttributeDescription.valueOf(ads, schema);
                        } catch (final LocalizedIllegalArgumentException e) {
                            throw DecodeException.error(e.getMessageObject());
                        }
              reader.readStartSet();
              try
              {
                while (reader.hasNextElement())
                {
                  attribute.add(reader.readOctetString());
                        final Attribute attribute = options.getAttributeFactory().newAttribute(ad);
                        reader.readStartSet();
                        try {
                            while (reader.hasNextElement()) {
                                attribute.add(reader.readOctetString());
                            }
                            entry.addAttribute(attribute);
                        } finally {
                            reader.readEndSet();
                        }
                    } finally {
                        reader.readEndSequence();
                    }
                }
                message.addModification(new Modification(type, attribute));
              }
              finally
              {
                reader.readEndSet();
              }
            } finally {
                reader.readEndSequence();
            }
            finally
            {
              reader.readEndSequence();
            }
          }
          finally
          {
        } finally {
            reader.readEndSequence();
          }
        }
      }
      finally
      {
        reader.readEndSequence();
      }
    }
    finally
    {
      reader.readEndSequence();
        return Responses.newSearchResultEntry(entry);
    }
    decodeControls(reader, message);
    private final DecodeOptions options;
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP MODIFY REQUEST(messageID=%d, request=%s)", messageID,
          message));
    LDAPReader(final DecodeOptions options) {
        this.options = options;
    }
    handler.modifyRequest(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as a modify response
   * protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeModifyResult(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    Result message;
    reader.readStartSequence(OP_TYPE_MODIFY_RESPONSE);
    try
    {
      final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
      final String matchedDN = reader.readOctetStringAsString();
      final String diagnosticMessage = reader.readOctetStringAsString();
      message = Responses.newResult(resultCode).setMatchedDN(matchedDN)
          .setDiagnosticMessage(diagnosticMessage);
      decodeResponseReferrals(reader, message);
    }
    finally
    {
      reader.readEndSequence();
    }
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP MODIFY RESULT(messageID=%d, result=%s)", messageID,
          message));
    }
    handler.modifyResult(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as an LDAP protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeProtocolOp(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    final byte type = reader.peekType();
    switch (type)
    {
    case OP_TYPE_UNBIND_REQUEST: // 0x42
      decodeUnbindRequest(reader, messageID, handler, p);
      break;
    case 0x43: // 0x43
    case 0x44: // 0x44
    case 0x45: // 0x45
    case 0x46: // 0x46
    case 0x47: // 0x47
    case 0x48: // 0x48
    case 0x49: // 0x49
      handler.unrecognizedMessage(p, messageID, type, reader
          .readOctetString(type));
      break;
    case OP_TYPE_DELETE_REQUEST: // 0x4A
      decodeDeleteRequest(reader, messageID, handler, p);
      break;
    case 0x4B: // 0x4B
    case 0x4C: // 0x4C
    case 0x4D: // 0x4D
    case 0x4E: // 0x4E
    case 0x4F: // 0x4F
      handler.unrecognizedMessage(p, messageID, type, reader
          .readOctetString(type));
      break;
    case OP_TYPE_ABANDON_REQUEST: // 0x50
      decodeAbandonRequest(reader, messageID, handler, p);
      break;
    case 0x51: // 0x51
    case 0x52: // 0x52
    case 0x53: // 0x53
    case 0x54: // 0x54
    case 0x55: // 0x55
    case 0x56: // 0x56
    case 0x57: // 0x57
    case 0x58: // 0x58
    case 0x59: // 0x59
    case 0x5A: // 0x5A
    case 0x5B: // 0x5B
    case 0x5C: // 0x5C
    case 0x5D: // 0x5D
    case 0x5E: // 0x5E
    case 0x5F: // 0x5F
      handler.unrecognizedMessage(p, messageID, type, reader
          .readOctetString(type));
      break;
    case OP_TYPE_BIND_REQUEST: // 0x60
      decodeBindRequest(reader, messageID, handler, p);
      break;
    case OP_TYPE_BIND_RESPONSE: // 0x61
      decodeBindResult(reader, messageID, handler, p);
      break;
    case 0x62: // 0x62
      handler.unrecognizedMessage(p, messageID, type, reader
          .readOctetString(type));
      break;
    case OP_TYPE_SEARCH_REQUEST: // 0x63
      decodeSearchRequest(reader, messageID, handler, p);
      break;
    case OP_TYPE_SEARCH_RESULT_ENTRY: // 0x64
      decodeSearchResultEntry(reader, messageID, handler, p);
      break;
    case OP_TYPE_SEARCH_RESULT_DONE: // 0x65
      decodeSearchResult(reader, messageID, handler, p);
      break;
    case OP_TYPE_MODIFY_REQUEST: // 0x66
      decodeModifyRequest(reader, messageID, handler, p);
      break;
    case OP_TYPE_MODIFY_RESPONSE: // 0x67
      decodeModifyResult(reader, messageID, handler, p);
      break;
    case OP_TYPE_ADD_REQUEST: // 0x68
      decodeAddRequest(reader, messageID, handler, p);
      break;
    case OP_TYPE_ADD_RESPONSE: // 0x69
      decodeAddResult(reader, messageID, handler, p);
      break;
    case 0x6A: // 0x6A
      handler.unrecognizedMessage(p, messageID, type, reader
          .readOctetString(type));
      break;
    case OP_TYPE_DELETE_RESPONSE: // 0x6B
      decodeDeleteResult(reader, messageID, handler, p);
      break;
    case OP_TYPE_MODIFY_DN_REQUEST: // 0x6C
      decodeModifyDNRequest(reader, messageID, handler, p);
      break;
    case OP_TYPE_MODIFY_DN_RESPONSE: // 0x6D
      decodeModifyDNResult(reader, messageID, handler, p);
      break;
    case OP_TYPE_COMPARE_REQUEST: // 0x6E
      decodeCompareRequest(reader, messageID, handler, p);
      break;
    case OP_TYPE_COMPARE_RESPONSE: // 0x6F
      decodeCompareResult(reader, messageID, handler, p);
      break;
    case 0x70: // 0x70
    case 0x71: // 0x71
    case 0x72: // 0x72
      handler.unrecognizedMessage(p, messageID, type, reader
          .readOctetString(type));
      break;
    case OP_TYPE_SEARCH_RESULT_REFERENCE: // 0x73
      decodeSearchResultReference(reader, messageID, handler, p);
      break;
    case 0x74: // 0x74
    case 0x75: // 0x75
    case 0x76: // 0x76
      handler.unrecognizedMessage(p, messageID, type, reader
          .readOctetString(type));
      break;
    case OP_TYPE_EXTENDED_REQUEST: // 0x77
      decodeExtendedRequest(reader, messageID, handler, p);
      break;
    case OP_TYPE_EXTENDED_RESPONSE: // 0x78
      decodeExtendedResult(reader, messageID, handler, p);
      break;
    case OP_TYPE_INTERMEDIATE_RESPONSE: // 0x79
      decodeIntermediateResponse(reader, messageID, handler, p);
      break;
    default:
      handler.unrecognizedMessage(p, messageID, type, reader
          .readOctetString(type));
      break;
    }
  }
  private RDN decodeRDN(final String rdn, final Schema schema)
      throws DecodeException
  {
    try
    {
      return RDN.valueOf(rdn, schema);
    }
    catch (final LocalizedIllegalArgumentException e)
    {
      throw DecodeException.error(e.getMessageObject());
    }
  }
  private void decodeResponseReferrals(final ASN1Reader reader,
      final Result message) throws IOException
  {
    if (reader.hasNextElement()
        && (reader.peekType() == TYPE_REFERRAL_SEQUENCE))
    {
      reader.readStartSequence(TYPE_REFERRAL_SEQUENCE);
      try
      {
        // Should have at least 1.
        do
        {
          message.addReferralURI((reader.readOctetStringAsString()));
        }
        while (reader.hasNextElement());
      }
      finally
      {
        reader.readEndSequence();
      }
    }
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as an LDAP search
   * request protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeSearchRequest(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    SearchRequest message;
    reader.readStartSequence(OP_TYPE_SEARCH_REQUEST);
    try
    {
      final String baseDNString = reader.readOctetStringAsString();
      final Schema schema = options.getSchemaResolver().resolveSchema(
          baseDNString);
      final DN baseDN = decodeDN(baseDNString, schema);
      final int scopeIntValue = reader.readEnumerated();
      final SearchScope scope = SearchScope.valueOf(scopeIntValue);
      if (scope == null)
      {
        throw DecodeException
            .error(ERR_LDAP_SEARCH_REQUEST_DECODE_INVALID_SCOPE
                .get(scopeIntValue));
      }
      final int dereferencePolicyIntValue = reader.readEnumerated();
      final DereferenceAliasesPolicy dereferencePolicy = DereferenceAliasesPolicy
          .valueOf(dereferencePolicyIntValue);
      if (dereferencePolicy == null)
      {
        throw DecodeException
            .error(ERR_LDAP_SEARCH_REQUEST_DECODE_INVALID_DEREF
                .get(dereferencePolicyIntValue));
      }
      final int sizeLimit = (int) reader.readInteger();
      final int timeLimit = (int) reader.readInteger();
      final boolean typesOnly = reader.readBoolean();
      final Filter filter = LDAPUtils.decodeFilter(reader);
      message = Requests.newSearchRequest(baseDN, scope, filter);
      message.setDereferenceAliasesPolicy(dereferencePolicy);
      try
      {
        message.setTimeLimit(timeLimit);
        message.setSizeLimit(sizeLimit);
      }
      catch (final LocalizedIllegalArgumentException e)
      {
        throw DecodeException.error(e.getMessageObject());
      }
      message.setTypesOnly(typesOnly);
      reader.readStartSequence();
      try
      {
        while (reader.hasNextElement())
        {
          message.addAttribute(reader.readOctetStringAsString());
        }
      }
      finally
      {
        reader.readEndSequence();
      }
    }
    finally
    {
      reader.readEndSequence();
    }
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP SEARCH REQUEST(messageID=%d, request=%s)", messageID,
          message));
    }
    handler.searchRequest(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as a search result done
   * protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeSearchResult(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    Result message;
    reader.readStartSequence(OP_TYPE_SEARCH_RESULT_DONE);
    try
    {
      final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
      final String matchedDN = reader.readOctetStringAsString();
      final String diagnosticMessage = reader.readOctetStringAsString();
      message = Responses.newResult(resultCode).setMatchedDN(matchedDN)
          .setDiagnosticMessage(diagnosticMessage);
      decodeResponseReferrals(reader, message);
    }
    finally
    {
      reader.readEndSequence();
    }
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP SEARCH RESULT(messageID=%d, result=%s)", messageID,
          message));
    }
    handler.searchResult(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 reader as an LDAP search
   * result entry protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeSearchResultEntry(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    Entry entry;
    reader.readStartSequence(OP_TYPE_SEARCH_RESULT_ENTRY);
    try
    {
      final String dnString = reader.readOctetStringAsString();
      final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
      final DN dn = decodeDN(dnString, schema);
      entry = options.getEntryFactory().newEntry(dn);
      reader.readStartSequence();
      try
      {
        while (reader.hasNextElement())
        {
          reader.readStartSequence();
          try
          {
            final String ads = reader.readOctetStringAsString();
            final AttributeDescription ad = decodeAttributeDescription(ads,
                schema);
            final Attribute attribute = options.getAttributeFactory()
                .newAttribute(ad);
            reader.readStartSet();
            try
            {
              while (reader.hasNextElement())
              {
                attribute.add(reader.readOctetString());
              }
              entry.addAttribute(attribute);
            }
            finally
            {
              reader.readEndSet();
            }
          }
          finally
          {
    /**
     * Decodes the elements from the provided ASN.1 reader as an LDAP message.
     *
     * @param <P>
     *            The type of {@code param}.
     * @param reader
     *            The ASN.1 reader.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle a decoded
     *            message.
     * @param param
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    <P> void decode(final ASN1Reader reader, final LDAPMessageHandler<P> handler, final P param)
            throws IOException {
        reader.readStartSequence();
        try {
            final int messageID = (int) reader.readInteger();
            decodeProtocolOp(reader, messageID, handler, param);
        } finally {
            reader.readEndSequence();
          }
        }
      }
      finally
      {
        reader.readEndSequence();
      }
    }
    finally
    {
      reader.readEndSequence();
    }
    final SearchResultEntry message = Responses.newSearchResultEntry(entry);
    decodeControls(reader, message);
    /**
     * Decodes the elements from the provided ASN.1 read as an LDAP abandon
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeAbandonRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        final int msgToAbandon = (int) reader.readInteger(OP_TYPE_ABANDON_REQUEST);
        final AbandonRequest message = Requests.newAbandonRequest(msgToAbandon);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP SEARCH RESULT ENTRY(messageID=%d, entry=%s)", messageID,
          message));
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP ABANDON REQUEST(messageID=%d, request=%s)", messageID, message));
        }
        handler.abandonRequest(p, messageID, message);
    }
    handler.searchResultEntry(p, messageID, message);
  }
    /**
     * Decodes the elements from the provided ASN.1 reader as an LDAP add
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeAddRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        Entry entry;
        reader.readStartSequence(OP_TYPE_ADD_REQUEST);
        try {
            final String dnString = reader.readOctetStringAsString();
            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
            final DN dn = decodeDN(dnString, schema);
            entry = options.getEntryFactory().newEntry(dn);
            reader.readStartSequence();
            try {
                while (reader.hasNextElement()) {
                    reader.readStartSequence();
                    try {
                        final String ads = reader.readOctetStringAsString();
                        final AttributeDescription ad = decodeAttributeDescription(ads, schema);
                        final Attribute attribute = options.getAttributeFactory().newAttribute(ad);
  /**
   * Decodes the elements from the provided ASN.1 reader as a search result
   * reference protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeSearchResultReference(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    SearchResultReference message;
                        reader.readStartSet();
                        try {
                            while (reader.hasNextElement()) {
                                attribute.add(reader.readOctetString());
                            }
                            entry.addAttribute(attribute);
                        } finally {
                            reader.readEndSet();
                        }
                    } finally {
                        reader.readEndSequence();
                    }
                }
            } finally {
                reader.readEndSequence();
            }
        } finally {
            reader.readEndSequence();
        }
    reader.readStartSequence(OP_TYPE_SEARCH_RESULT_REFERENCE);
    try
    {
      message = Responses.newSearchResultReference(reader
          .readOctetStringAsString());
      while (reader.hasNextElement())
      {
        message.addURI(reader.readOctetStringAsString());
      }
    }
    finally
    {
      reader.readEndSequence();
        final AddRequest message = Requests.newAddRequest(entry);
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP ADD REQUEST(messageID=%d, request=%s)", messageID, message));
        }
        handler.addRequest(p, messageID, message);
    }
    decodeControls(reader, message);
    /**
     * Decodes the elements from the provided ASN.1 reader as an add response
     * protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeAddResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        Result message;
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP SEARCH RESULT REFERENCE(messageID=%d, reference=%s)",
          messageID, message));
        reader.readStartSequence(OP_TYPE_ADD_RESPONSE);
        try {
            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
            final String matchedDN = reader.readOctetStringAsString();
            final String diagnosticMessage = reader.readOctetStringAsString();
            message =
                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
                            diagnosticMessage);
            decodeResponseReferrals(reader, message);
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP ADD RESULT(messageID=%d, result=%s)", messageID, message));
        }
        handler.addResult(p, messageID, message);
    }
    handler.searchResultReference(p, messageID, message);
  }
  /**
   * Decodes the elements from the provided ASN.1 read as an LDAP unbind request
   * protocol op.
   *
   * @param reader
   *          The ASN.1 reader.
   * @param messageID
   *          The decoded message ID for this message.
   * @param handler
   *          The <code>LDAPMessageHandler</code> that will handle this decoded
   *          message.
   * @param p
   *          The parameter to pass into the <code>LDAPMessageHandler</code>
   * @throws IOException
   *           If an error occurred while reading bytes to decode.
   */
  private <P> void decodeUnbindRequest(final ASN1Reader reader,
      final int messageID, final LDAPMessageHandler<P> handler, final P p)
      throws IOException
  {
    UnbindRequest message;
    reader.readNull(OP_TYPE_UNBIND_REQUEST);
    message = Requests.newUnbindRequest();
    decodeControls(reader, message);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER))
    {
      StaticUtils.DEBUG_LOG.finer(String.format(
          "DECODE LDAP UNBIND REQUEST(messageID=%d, request=%s)", messageID,
          message));
    private AttributeDescription decodeAttributeDescription(final String attributeDescription,
            final Schema schema) throws DecodeException {
        try {
            return AttributeDescription.valueOf(attributeDescription, schema);
        } catch (final LocalizedIllegalArgumentException e) {
            throw DecodeException.error(e.getMessageObject());
        }
    }
    handler.unbindRequest(p, messageID, message);
  }
    /**
     * Decodes the elements from the provided ASN.1 read as an LDAP bind request
     * protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeBindRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        reader.readStartSequence(OP_TYPE_BIND_REQUEST);
        try {
            final int protocolVersion = (int) reader.readInteger();
            final String authName = reader.readOctetStringAsString();
            final byte authType = reader.peekType();
            final byte[] authBytes = reader.readOctetString(authType).toByteArray();
            final GenericBindRequest request =
                    Requests.newGenericBindRequest(authName, authType, authBytes);
            decodeControls(reader, request);
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
                StaticUtils.DEBUG_LOG.finer(String.format(
                        "DECODE LDAP BIND REQUEST(messageID=%d, auth=0x%x, request=%s)", messageID,
                        request.getAuthenticationType(), request));
            }
            handler.bindRequest(p, messageID, protocolVersion, request);
        } finally {
            reader.readEndSequence();
        }
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as a bind response
     * protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeBindResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        BindResult message;
        reader.readStartSequence(OP_TYPE_BIND_RESPONSE);
        try {
            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
            final String matchedDN = reader.readOctetStringAsString();
            final String diagnosticMessage = reader.readOctetStringAsString();
            message =
                    Responses.newBindResult(resultCode).setMatchedDN(matchedDN)
                            .setDiagnosticMessage(diagnosticMessage);
            decodeResponseReferrals(reader, message);
            if (reader.hasNextElement() && (reader.peekType() == TYPE_SERVER_SASL_CREDENTIALS)) {
                message.setServerSASLCredentials(reader
                        .readOctetString(TYPE_SERVER_SASL_CREDENTIALS));
            }
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP BIND RESULT(messageID=%d, result=%s)", messageID, message));
        }
        handler.bindResult(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as an LDAP compare
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeCompareRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        CompareRequest message;
        reader.readStartSequence(OP_TYPE_COMPARE_REQUEST);
        try {
            final String dnString = reader.readOctetStringAsString();
            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
            final DN dn = decodeDN(dnString, schema);
            reader.readStartSequence();
            try {
                final String ads = reader.readOctetStringAsString();
                final AttributeDescription ad = decodeAttributeDescription(ads, schema);
                final ByteString assertionValue = reader.readOctetString();
                message = Requests.newCompareRequest(dn, ad, assertionValue);
            } finally {
                reader.readEndSequence();
            }
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP COMPARE REQUEST(messageID=%d, request=%s)", messageID, message));
        }
        handler.compareRequest(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as a compare response
     * protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeCompareResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        CompareResult message;
        reader.readStartSequence(OP_TYPE_COMPARE_RESPONSE);
        try {
            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
            final String matchedDN = reader.readOctetStringAsString();
            final String diagnosticMessage = reader.readOctetStringAsString();
            message =
                    Responses.newCompareResult(resultCode).setMatchedDN(matchedDN)
                            .setDiagnosticMessage(diagnosticMessage);
            decodeResponseReferrals(reader, message);
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP COMPARE RESULT(messageID=%d, result=%s)", messageID, message));
        }
        handler.compareResult(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as an LDAP control.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param request
     *            The decoded request to decode controls for.
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private void decodeControl(final ASN1Reader reader, final Request request) throws IOException {
        String oid;
        boolean isCritical;
        ByteString value;
        reader.readStartSequence();
        try {
            oid = reader.readOctetStringAsString();
            isCritical = false;
            value = null;
            if (reader.hasNextElement() && (reader.peekType() == UNIVERSAL_BOOLEAN_TYPE)) {
                isCritical = reader.readBoolean();
            }
            if (reader.hasNextElement() && (reader.peekType() == UNIVERSAL_OCTET_STRING_TYPE)) {
                value = reader.readOctetString();
            }
        } finally {
            reader.readEndSequence();
        }
        final Control c = GenericControl.newControl(oid, isCritical, value);
        request.addControl(c);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as an LDAP control.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param response
     *            The decoded message to decode controls for.
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private void decodeControl(final ASN1Reader reader, final Response response) throws IOException {
        String oid;
        boolean isCritical;
        ByteString value;
        reader.readStartSequence();
        try {
            oid = reader.readOctetStringAsString();
            isCritical = false;
            value = null;
            if (reader.hasNextElement() && (reader.peekType() == UNIVERSAL_BOOLEAN_TYPE)) {
                isCritical = reader.readBoolean();
            }
            if (reader.hasNextElement() && (reader.peekType() == UNIVERSAL_OCTET_STRING_TYPE)) {
                value = reader.readOctetString();
            }
        } finally {
            reader.readEndSequence();
        }
        final Control c = GenericControl.newControl(oid, isCritical, value);
        response.addControl(c);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as a set of controls.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param request
     *            The decoded message to decode controls for.
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private void decodeControls(final ASN1Reader reader, final Request request) throws IOException {
        if (reader.hasNextElement() && (reader.peekType() == TYPE_CONTROL_SEQUENCE)) {
            reader.readStartSequence(TYPE_CONTROL_SEQUENCE);
            try {
                while (reader.hasNextElement()) {
                    decodeControl(reader, request);
                }
            } finally {
                reader.readEndSequence();
            }
        }
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as a set of controls.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param response
     *            The decoded message to decode controls for.
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private void decodeControls(final ASN1Reader reader, final Response response)
            throws IOException {
        if (reader.hasNextElement() && (reader.peekType() == TYPE_CONTROL_SEQUENCE)) {
            reader.readStartSequence(TYPE_CONTROL_SEQUENCE);
            try {
                while (reader.hasNextElement()) {
                    decodeControl(reader, response);
                }
            } finally {
                reader.readEndSequence();
            }
        }
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as an LDAP delete
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeDeleteRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        final String dnString = reader.readOctetStringAsString(OP_TYPE_DELETE_REQUEST);
        final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
        final DN dn = decodeDN(dnString, schema);
        final DeleteRequest message = Requests.newDeleteRequest(dn);
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP DELETE REQUEST(messageID=%d, request=%s)", messageID, message));
        }
        handler.deleteRequest(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as a delete response
     * protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeDeleteResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        Result message;
        reader.readStartSequence(OP_TYPE_DELETE_RESPONSE);
        try {
            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
            final String matchedDN = reader.readOctetStringAsString();
            final String diagnosticMessage = reader.readOctetStringAsString();
            message =
                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
                            diagnosticMessage);
            decodeResponseReferrals(reader, message);
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP DELETE RESULT(messageID=%d, result=%s)", messageID, message));
        }
        handler.deleteResult(p, messageID, message);
    }
    private DN decodeDN(final String dn, final Schema schema) throws DecodeException {
        try {
            return DN.valueOf(dn, schema);
        } catch (final LocalizedIllegalArgumentException e) {
            throw DecodeException.error(e.getMessageObject());
        }
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as an LDAP extended
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeExtendedRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        String oid;
        ByteString value;
        reader.readStartSequence(OP_TYPE_EXTENDED_REQUEST);
        try {
            oid = reader.readOctetStringAsString(TYPE_EXTENDED_REQUEST_OID);
            value = null;
            if (reader.hasNextElement() && (reader.peekType() == TYPE_EXTENDED_REQUEST_VALUE)) {
                value = reader.readOctetString(TYPE_EXTENDED_REQUEST_VALUE);
            }
        } finally {
            reader.readEndSequence();
        }
        final GenericExtendedRequest message = Requests.newGenericExtendedRequest(oid, value);
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP EXTENDED REQUEST(messageID=%d, request=%s)", messageID, message));
        }
        handler.extendedRequest(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as a extended
     * response protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeExtendedResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        GenericExtendedResult message;
        reader.readStartSequence(OP_TYPE_EXTENDED_RESPONSE);
        try {
            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
            final String matchedDN = reader.readOctetStringAsString();
            final String diagnosticMessage = reader.readOctetStringAsString();
            message =
                    Responses.newGenericExtendedResult(resultCode).setMatchedDN(matchedDN)
                            .setDiagnosticMessage(diagnosticMessage);
            decodeResponseReferrals(reader, message);
            if (reader.hasNextElement() && (reader.peekType() == TYPE_EXTENDED_RESPONSE_OID)) {
                message.setOID(reader.readOctetStringAsString(TYPE_EXTENDED_RESPONSE_OID));
            }
            if (reader.hasNextElement() && (reader.peekType() == TYPE_EXTENDED_RESPONSE_VALUE)) {
                message.setValue(reader.readOctetString(TYPE_EXTENDED_RESPONSE_VALUE));
            }
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP EXTENDED RESULT(messageID=%d, result=%s)", messageID, message));
        }
        handler.extendedResult(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as an LDAP
     * intermediate response protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeIntermediateResponse(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        GenericIntermediateResponse message;
        reader.readStartSequence(OP_TYPE_INTERMEDIATE_RESPONSE);
        try {
            message = Responses.newGenericIntermediateResponse();
            if (reader.hasNextElement() && (reader.peekType() == TYPE_INTERMEDIATE_RESPONSE_OID)) {
                message.setOID(reader.readOctetStringAsString(TYPE_INTERMEDIATE_RESPONSE_OID));
            }
            if (reader.hasNextElement() && (reader.peekType() == TYPE_INTERMEDIATE_RESPONSE_VALUE)) {
                message.setValue(reader.readOctetString(TYPE_INTERMEDIATE_RESPONSE_VALUE));
            }
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP INTERMEDIATE RESPONSE(messageID=%d, response=%s)", messageID,
                    message));
        }
        handler.intermediateResponse(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as a modify DN
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeModifyDNRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        ModifyDNRequest message;
        reader.readStartSequence(OP_TYPE_MODIFY_DN_REQUEST);
        try {
            final String dnString = reader.readOctetStringAsString();
            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
            final DN dn = decodeDN(dnString, schema);
            final String newRDNString = reader.readOctetStringAsString();
            final RDN newRDN = decodeRDN(newRDNString, schema);
            message = Requests.newModifyDNRequest(dn, newRDN);
            message.setDeleteOldRDN(reader.readBoolean());
            if (reader.hasNextElement() && (reader.peekType() == TYPE_MODIFY_DN_NEW_SUPERIOR)) {
                final String newSuperiorString =
                        reader.readOctetStringAsString(TYPE_MODIFY_DN_NEW_SUPERIOR);
                final DN newSuperior = decodeDN(newSuperiorString, schema);
                message.setNewSuperior(newSuperior);
            }
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP MODIFY DN REQUEST(messageID=%d, request=%s)", messageID, message));
        }
        handler.modifyDNRequest(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as a modify DN
     * response protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeModifyDNResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        Result message;
        reader.readStartSequence(OP_TYPE_MODIFY_DN_RESPONSE);
        try {
            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
            final String matchedDN = reader.readOctetStringAsString();
            final String diagnosticMessage = reader.readOctetStringAsString();
            message =
                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
                            diagnosticMessage);
            decodeResponseReferrals(reader, message);
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP MODIFY DN RESULT(messageID=%d, result=%s)", messageID, message));
        }
        handler.modifyDNResult(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as an LDAP modify
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeModifyRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        ModifyRequest message;
        reader.readStartSequence(OP_TYPE_MODIFY_REQUEST);
        try {
            final String dnString = reader.readOctetStringAsString();
            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
            final DN dn = decodeDN(dnString, schema);
            message = Requests.newModifyRequest(dn);
            reader.readStartSequence();
            try {
                while (reader.hasNextElement()) {
                    reader.readStartSequence();
                    try {
                        final int typeIntValue = reader.readEnumerated();
                        final ModificationType type = ModificationType.valueOf(typeIntValue);
                        if (type == null) {
                            throw DecodeException
                                    .error(ERR_LDAP_MODIFICATION_DECODE_INVALID_MOD_TYPE
                                            .get(typeIntValue));
                        }
                        reader.readStartSequence();
                        try {
                            final String ads = reader.readOctetStringAsString();
                            final AttributeDescription ad = decodeAttributeDescription(ads, schema);
                            final Attribute attribute =
                                    options.getAttributeFactory().newAttribute(ad);
                            reader.readStartSet();
                            try {
                                while (reader.hasNextElement()) {
                                    attribute.add(reader.readOctetString());
                                }
                                message.addModification(new Modification(type, attribute));
                            } finally {
                                reader.readEndSet();
                            }
                        } finally {
                            reader.readEndSequence();
                        }
                    } finally {
                        reader.readEndSequence();
                    }
                }
            } finally {
                reader.readEndSequence();
            }
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP MODIFY REQUEST(messageID=%d, request=%s)", messageID, message));
        }
        handler.modifyRequest(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as a modify response
     * protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeModifyResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        Result message;
        reader.readStartSequence(OP_TYPE_MODIFY_RESPONSE);
        try {
            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
            final String matchedDN = reader.readOctetStringAsString();
            final String diagnosticMessage = reader.readOctetStringAsString();
            message =
                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
                            diagnosticMessage);
            decodeResponseReferrals(reader, message);
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP MODIFY RESULT(messageID=%d, result=%s)", messageID, message));
        }
        handler.modifyResult(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as an LDAP protocol
     * op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeProtocolOp(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        final byte type = reader.peekType();
        switch (type) {
        case OP_TYPE_UNBIND_REQUEST: // 0x42
            decodeUnbindRequest(reader, messageID, handler, p);
            break;
        case 0x43: // 0x43
        case 0x44: // 0x44
        case 0x45: // 0x45
        case 0x46: // 0x46
        case 0x47: // 0x47
        case 0x48: // 0x48
        case 0x49: // 0x49
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            break;
        case OP_TYPE_DELETE_REQUEST: // 0x4A
            decodeDeleteRequest(reader, messageID, handler, p);
            break;
        case 0x4B: // 0x4B
        case 0x4C: // 0x4C
        case 0x4D: // 0x4D
        case 0x4E: // 0x4E
        case 0x4F: // 0x4F
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            break;
        case OP_TYPE_ABANDON_REQUEST: // 0x50
            decodeAbandonRequest(reader, messageID, handler, p);
            break;
        case 0x51: // 0x51
        case 0x52: // 0x52
        case 0x53: // 0x53
        case 0x54: // 0x54
        case 0x55: // 0x55
        case 0x56: // 0x56
        case 0x57: // 0x57
        case 0x58: // 0x58
        case 0x59: // 0x59
        case 0x5A: // 0x5A
        case 0x5B: // 0x5B
        case 0x5C: // 0x5C
        case 0x5D: // 0x5D
        case 0x5E: // 0x5E
        case 0x5F: // 0x5F
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            break;
        case OP_TYPE_BIND_REQUEST: // 0x60
            decodeBindRequest(reader, messageID, handler, p);
            break;
        case OP_TYPE_BIND_RESPONSE: // 0x61
            decodeBindResult(reader, messageID, handler, p);
            break;
        case 0x62: // 0x62
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            break;
        case OP_TYPE_SEARCH_REQUEST: // 0x63
            decodeSearchRequest(reader, messageID, handler, p);
            break;
        case OP_TYPE_SEARCH_RESULT_ENTRY: // 0x64
            decodeSearchResultEntry(reader, messageID, handler, p);
            break;
        case OP_TYPE_SEARCH_RESULT_DONE: // 0x65
            decodeSearchResult(reader, messageID, handler, p);
            break;
        case OP_TYPE_MODIFY_REQUEST: // 0x66
            decodeModifyRequest(reader, messageID, handler, p);
            break;
        case OP_TYPE_MODIFY_RESPONSE: // 0x67
            decodeModifyResult(reader, messageID, handler, p);
            break;
        case OP_TYPE_ADD_REQUEST: // 0x68
            decodeAddRequest(reader, messageID, handler, p);
            break;
        case OP_TYPE_ADD_RESPONSE: // 0x69
            decodeAddResult(reader, messageID, handler, p);
            break;
        case 0x6A: // 0x6A
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            break;
        case OP_TYPE_DELETE_RESPONSE: // 0x6B
            decodeDeleteResult(reader, messageID, handler, p);
            break;
        case OP_TYPE_MODIFY_DN_REQUEST: // 0x6C
            decodeModifyDNRequest(reader, messageID, handler, p);
            break;
        case OP_TYPE_MODIFY_DN_RESPONSE: // 0x6D
            decodeModifyDNResult(reader, messageID, handler, p);
            break;
        case OP_TYPE_COMPARE_REQUEST: // 0x6E
            decodeCompareRequest(reader, messageID, handler, p);
            break;
        case OP_TYPE_COMPARE_RESPONSE: // 0x6F
            decodeCompareResult(reader, messageID, handler, p);
            break;
        case 0x70: // 0x70
        case 0x71: // 0x71
        case 0x72: // 0x72
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            break;
        case OP_TYPE_SEARCH_RESULT_REFERENCE: // 0x73
            decodeSearchResultReference(reader, messageID, handler, p);
            break;
        case 0x74: // 0x74
        case 0x75: // 0x75
        case 0x76: // 0x76
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            break;
        case OP_TYPE_EXTENDED_REQUEST: // 0x77
            decodeExtendedRequest(reader, messageID, handler, p);
            break;
        case OP_TYPE_EXTENDED_RESPONSE: // 0x78
            decodeExtendedResult(reader, messageID, handler, p);
            break;
        case OP_TYPE_INTERMEDIATE_RESPONSE: // 0x79
            decodeIntermediateResponse(reader, messageID, handler, p);
            break;
        default:
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            break;
        }
    }
    private RDN decodeRDN(final String rdn, final Schema schema) throws DecodeException {
        try {
            return RDN.valueOf(rdn, schema);
        } catch (final LocalizedIllegalArgumentException e) {
            throw DecodeException.error(e.getMessageObject());
        }
    }
    private void decodeResponseReferrals(final ASN1Reader reader, final Result message)
            throws IOException {
        if (reader.hasNextElement() && (reader.peekType() == TYPE_REFERRAL_SEQUENCE)) {
            reader.readStartSequence(TYPE_REFERRAL_SEQUENCE);
            try {
                // Should have at least 1.
                do {
                    message.addReferralURI((reader.readOctetStringAsString()));
                } while (reader.hasNextElement());
            } finally {
                reader.readEndSequence();
            }
        }
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as an LDAP search
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeSearchRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        SearchRequest message;
        reader.readStartSequence(OP_TYPE_SEARCH_REQUEST);
        try {
            final String baseDNString = reader.readOctetStringAsString();
            final Schema schema = options.getSchemaResolver().resolveSchema(baseDNString);
            final DN baseDN = decodeDN(baseDNString, schema);
            final int scopeIntValue = reader.readEnumerated();
            final SearchScope scope = SearchScope.valueOf(scopeIntValue);
            if (scope == null) {
                throw DecodeException.error(ERR_LDAP_SEARCH_REQUEST_DECODE_INVALID_SCOPE
                        .get(scopeIntValue));
            }
            final int dereferencePolicyIntValue = reader.readEnumerated();
            final DereferenceAliasesPolicy dereferencePolicy =
                    DereferenceAliasesPolicy.valueOf(dereferencePolicyIntValue);
            if (dereferencePolicy == null) {
                throw DecodeException.error(ERR_LDAP_SEARCH_REQUEST_DECODE_INVALID_DEREF
                        .get(dereferencePolicyIntValue));
            }
            final int sizeLimit = (int) reader.readInteger();
            final int timeLimit = (int) reader.readInteger();
            final boolean typesOnly = reader.readBoolean();
            final Filter filter = LDAPUtils.decodeFilter(reader);
            message = Requests.newSearchRequest(baseDN, scope, filter);
            message.setDereferenceAliasesPolicy(dereferencePolicy);
            try {
                message.setTimeLimit(timeLimit);
                message.setSizeLimit(sizeLimit);
            } catch (final LocalizedIllegalArgumentException e) {
                throw DecodeException.error(e.getMessageObject());
            }
            message.setTypesOnly(typesOnly);
            reader.readStartSequence();
            try {
                while (reader.hasNextElement()) {
                    message.addAttribute(reader.readOctetStringAsString());
                }
            } finally {
                reader.readEndSequence();
            }
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP SEARCH REQUEST(messageID=%d, request=%s)", messageID, message));
        }
        handler.searchRequest(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as a search result
     * done protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeSearchResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        Result message;
        reader.readStartSequence(OP_TYPE_SEARCH_RESULT_DONE);
        try {
            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
            final String matchedDN = reader.readOctetStringAsString();
            final String diagnosticMessage = reader.readOctetStringAsString();
            message =
                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
                            diagnosticMessage);
            decodeResponseReferrals(reader, message);
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP SEARCH RESULT(messageID=%d, result=%s)", messageID, message));
        }
        handler.searchResult(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as an LDAP search
     * result entry protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeSearchResultEntry(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        Entry entry;
        reader.readStartSequence(OP_TYPE_SEARCH_RESULT_ENTRY);
        try {
            final String dnString = reader.readOctetStringAsString();
            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
            final DN dn = decodeDN(dnString, schema);
            entry = options.getEntryFactory().newEntry(dn);
            reader.readStartSequence();
            try {
                while (reader.hasNextElement()) {
                    reader.readStartSequence();
                    try {
                        final String ads = reader.readOctetStringAsString();
                        final AttributeDescription ad = decodeAttributeDescription(ads, schema);
                        final Attribute attribute = options.getAttributeFactory().newAttribute(ad);
                        reader.readStartSet();
                        try {
                            while (reader.hasNextElement()) {
                                attribute.add(reader.readOctetString());
                            }
                            entry.addAttribute(attribute);
                        } finally {
                            reader.readEndSet();
                        }
                    } finally {
                        reader.readEndSequence();
                    }
                }
            } finally {
                reader.readEndSequence();
            }
        } finally {
            reader.readEndSequence();
        }
        final SearchResultEntry message = Responses.newSearchResultEntry(entry);
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP SEARCH RESULT ENTRY(messageID=%d, entry=%s)", messageID, message));
        }
        handler.searchResultEntry(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as a search result
     * reference protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeSearchResultReference(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        SearchResultReference message;
        reader.readStartSequence(OP_TYPE_SEARCH_RESULT_REFERENCE);
        try {
            message = Responses.newSearchResultReference(reader.readOctetStringAsString());
            while (reader.hasNextElement()) {
                message.addURI(reader.readOctetStringAsString());
            }
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP SEARCH RESULT REFERENCE(messageID=%d, reference=%s)", messageID,
                    message));
        }
        handler.searchResultReference(p, messageID, message);
    }
    /**
     * Decodes the elements from the provided ASN.1 read as an LDAP unbind
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 reader.
     * @param messageID
     *            The decoded message ID for this message.
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle this
     *            decoded message.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeUnbindRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
        UnbindRequest message;
        reader.readNull(OP_TYPE_UNBIND_REQUEST);
        message = Requests.newUnbindRequest();
        decodeControls(reader, message);
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINER)) {
            StaticUtils.DEBUG_LOG.finer(String.format(
                    "DECODE LDAP UNBIND REQUEST(messageID=%d, request=%s)", messageID, message));
        }
        handler.unbindRequest(p, messageID, message);
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPSearchFutureResultImpl.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,8 +27,6 @@
package com.forgerock.opendj.ldap;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.IntermediateResponseHandler;
import org.forgerock.opendj.ldap.ResultCode;
@@ -40,106 +37,79 @@
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
/**
 * Search result future implementation.
 */
final class LDAPSearchFutureResultImpl extends
    AbstractLDAPFutureResultImpl<Result> implements SearchResultHandler
{
final class LDAPSearchFutureResultImpl extends AbstractLDAPFutureResultImpl<Result> implements
        SearchResultHandler {
  private SearchResultHandler searchResultHandler;
    private SearchResultHandler searchResultHandler;
  private final SearchRequest request;
    private final SearchRequest request;
  LDAPSearchFutureResultImpl(final int requestID, final SearchRequest request,
      final SearchResultHandler resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler,
      final Connection connection)
  {
    super(requestID, resultHandler, intermediateResponseHandler, connection);
    this.request = request;
    this.searchResultHandler = resultHandler;
  }
  public boolean handleEntry(final SearchResultEntry entry)
  {
    // FIXME: there's a potential race condition here - the future could
    // get cancelled between the isDone() call and the handler
    // invocation. We'd need to add support for intermediate handlers in
    // the synchronizer.
    if (!isDone())
    {
      updateTimestamp();
      if (searchResultHandler != null)
      {
        if (!searchResultHandler.handleEntry(entry))
        {
          searchResultHandler = null;
        }
      }
    LDAPSearchFutureResultImpl(final int requestID, final SearchRequest request,
            final SearchResultHandler resultHandler,
            final IntermediateResponseHandler intermediateResponseHandler,
            final Connection connection) {
        super(requestID, resultHandler, intermediateResponseHandler, connection);
        this.request = request;
        this.searchResultHandler = resultHandler;
    }
    return true;
  }
  public boolean handleReference(final SearchResultReference reference)
  {
    // FIXME: there's a potential race condition here - the future could
    // get cancelled between the isDone() call and the handler
    // invocation. We'd need to add support for intermediate handlers in
    // the synchronizer.
    if (!isDone())
    {
      updateTimestamp();
      if (searchResultHandler != null)
      {
        if (!searchResultHandler.handleReference(reference))
        {
          searchResultHandler = null;
    public boolean handleEntry(final SearchResultEntry entry) {
        // FIXME: there's a potential race condition here - the future could
        // get cancelled between the isDone() call and the handler
        // invocation. We'd need to add support for intermediate handlers in
        // the synchronizer.
        if (!isDone()) {
            updateTimestamp();
            if (searchResultHandler != null) {
                if (!searchResultHandler.handleEntry(entry)) {
                    searchResultHandler = null;
                }
            }
        }
      }
        return true;
    }
    return true;
  }
    public boolean handleReference(final SearchResultReference reference) {
        // FIXME: there's a potential race condition here - the future could
        // get cancelled between the isDone() call and the handler
        // invocation. We'd need to add support for intermediate handlers in
        // the synchronizer.
        if (!isDone()) {
            updateTimestamp();
            if (searchResultHandler != null) {
                if (!searchResultHandler.handleReference(reference)) {
                    searchResultHandler = null;
                }
            }
        }
        return true;
    }
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("LDAPSearchFutureResultImpl(");
        sb.append("request = ");
        sb.append(request);
        super.toString(sb);
        sb.append(")");
        return sb.toString();
    }
  @Override
  public String toString()
  {
    final StringBuilder sb = new StringBuilder();
    sb.append("LDAPSearchFutureResultImpl(");
    sb.append("request = ");
    sb.append(request);
    super.toString(sb);
    sb.append(")");
    return sb.toString();
  }
    SearchRequest getRequest() {
        return request;
    }
  SearchRequest getRequest()
  {
    return request;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  Result newErrorResult(final ResultCode resultCode,
      final String diagnosticMessage, final Throwable cause)
  {
    return Responses.newResult(resultCode).setDiagnosticMessage(
        diagnosticMessage).setCause(cause);
  }
    /**
     * {@inheritDoc}
     */
    @Override
    Result newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
            final Throwable cause) {
        return Responses.newResult(resultCode).setDiagnosticMessage(diagnosticMessage).setCause(
                cause);
    }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPServerFilter.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,8 +27,6 @@
package com.forgerock.opendj.ldap;
import static com.forgerock.opendj.ldap.LDAPConstants.OID_NOTICE_OF_DISCONNECTION;
import java.io.IOException;
@@ -40,16 +37,45 @@
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ConnectionSecurityLayer;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.IntermediateResponseHandler;
import org.forgerock.opendj.ldap.LDAPClientContext;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.SearchResultHandler;
import org.forgerock.opendj.ldap.ServerConnection;
import org.forgerock.opendj.ldap.controls.Control;
import org.forgerock.opendj.ldap.requests.*;
import org.forgerock.opendj.ldap.responses.*;
import org.forgerock.opendj.ldap.requests.AbandonRequest;
import org.forgerock.opendj.ldap.requests.AddRequest;
import org.forgerock.opendj.ldap.requests.CompareRequest;
import org.forgerock.opendj.ldap.requests.DeleteRequest;
import org.forgerock.opendj.ldap.requests.ExtendedRequest;
import org.forgerock.opendj.ldap.requests.GenericBindRequest;
import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
import org.forgerock.opendj.ldap.requests.ModifyRequest;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.requests.UnbindRequest;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.CompareResult;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
import org.forgerock.opendj.ldap.responses.GenericExtendedResult;
import org.forgerock.opendj.ldap.responses.IntermediateResponse;
import org.forgerock.opendj.ldap.responses.Responses;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.attributes.Attribute;
import org.glassfish.grizzly.filterchain.*;
import org.glassfish.grizzly.filterchain.BaseFilter;
import org.glassfish.grizzly.filterchain.Filter;
import org.glassfish.grizzly.filterchain.FilterChain;
import org.glassfish.grizzly.filterchain.FilterChainBuilder;
import org.glassfish.grizzly.filterchain.FilterChainContext;
import org.glassfish.grizzly.filterchain.NextAction;
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
import org.glassfish.grizzly.ssl.SSLFilter;
import org.glassfish.grizzly.ssl.SSLUtils;
@@ -57,1134 +83,777 @@
import com.forgerock.opendj.util.StaticUtils;
import com.forgerock.opendj.util.Validator;
/**
 * Grizzly filter implementation for decoding LDAP requests and handling server
 * side logic for SSL and SASL operations over LDAP.
 */
final class LDAPServerFilter extends BaseFilter
{
  private abstract class AbstractHandler<R extends Result> implements
      IntermediateResponseHandler, ResultHandler<R>
  {
    protected final int messageID;
    protected final Connection<?> connection;
final class LDAPServerFilter extends BaseFilter {
    private abstract class AbstractHandler<R extends Result> implements
            IntermediateResponseHandler, ResultHandler<R> {
        protected final int messageID;
        protected final Connection<?> connection;
    protected AbstractHandler(final int messageID,
        final Connection<?> connection)
    {
      this.messageID = messageID;
      this.connection = connection;
    }
    @Override
    public final boolean handleIntermediateResponse(
        final IntermediateResponse response)
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        LDAP_WRITER.intermediateResponse(asn1Writer, messageID, response);
        connection.write(asn1Writer.getBuffer(), null);
      }
      catch (final IOException ioe)
      {
        notifyConnectionException(connection, ioe);
        return false;
      }
      finally
      {
        asn1Writer.recycle();
      }
      return true;
    }
  }
  private final class AddHandler extends AbstractHandler<Result>
  {
    private AddHandler(final int messageID, final Connection<?> connection)
    {
      super(messageID, connection);
    }
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      handleResult(error.getResult());
    }
    @Override
    public void handleResult(final Result result)
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        LDAP_WRITER.addResult(asn1Writer, messageID, result);
        connection.write(asn1Writer.getBuffer(), null);
      }
      catch (final IOException ioe)
      {
        notifyConnectionException(connection, ioe);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
  }
  private final class BindHandler extends AbstractHandler<BindResult>
  {
    private BindHandler(final int messageID, final Connection<?> connection)
    {
      super(messageID, connection);
    }
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      final Result result = error.getResult();
      if (result instanceof BindResult)
      {
        handleResult((BindResult) result);
      }
      else
      {
        final BindResult newResult = Responses.newBindResult(result
            .getResultCode());
        newResult.setDiagnosticMessage(result.getDiagnosticMessage());
        newResult.setMatchedDN(result.getMatchedDN());
        newResult.setCause(result.getCause());
        for (final Control control : result.getControls())
        {
          newResult.addControl(control);
        }
        handleResult(newResult);
      }
    }
    @Override
    public void handleResult(final BindResult result)
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        LDAP_WRITER.bindResult(asn1Writer, messageID, result);
        connection.write(asn1Writer.getBuffer(), null);
      }
      catch (final IOException ioe)
      {
        notifyConnectionException(connection, ioe);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
  }
  private final class ClientContextImpl implements LDAPClientContext
  {
    private final Connection<?> connection;
    private volatile boolean isClosed = false;
    private ServerConnection<Integer> serverConnection = null;
    private ClientContextImpl(final Connection<?> connection)
    {
      this.connection = connection;
    }
    @Override
    public void disconnect()
    {
      LDAPServerFilter.notifyConnectionDisconnected(connection, null, null);
    }
    @Override
    public void disconnect(final ResultCode resultCode, final String message)
    {
      Validator.ensureNotNull(resultCode);
      final GenericExtendedResult notification = Responses
          .newGenericExtendedResult(resultCode)
          .setOID(OID_NOTICE_OF_DISCONNECTION).setDiagnosticMessage(message);
      sendUnsolicitedNotification(notification);
      LDAPServerFilter.notifyConnectionDisconnected(connection, resultCode,
          message);
    }
    @Override
    public InetSocketAddress getLocalAddress()
    {
      return (InetSocketAddress) connection.getLocalAddress();
    }
    @Override
    public InetSocketAddress getPeerAddress()
    {
      return (InetSocketAddress) connection.getPeerAddress();
    }
    @Override
    public int getSecurityStrengthFactor()
    {
      int ssf = 0;
      final SSLEngine sslEngine = SSLUtils.getSSLEngine(connection);
      if (sslEngine != null)
      {
        final String cipherString = sslEngine.getSession().getCipherSuite();
        for (final Map.Entry<String, Integer> mapEntry : CIPHER_KEY_SIZES
            .entrySet())
        {
          if (cipherString.indexOf(mapEntry.getKey()) >= 0)
          {
            ssf = mapEntry.getValue();
            break;
          }
        }
      }
      return ssf;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isClosed()
    {
      return isClosed;
    }
    @Override
    public void sendUnsolicitedNotification(final ExtendedResult notification)
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        LDAP_WRITER.extendedResult(asn1Writer, 0, notification);
        connection.write(asn1Writer.getBuffer(), null);
      }
      catch (final IOException ioe)
      {
        LDAPServerFilter.notifyConnectionException(connection, ioe);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
    @Override
    public void enableConnectionSecurityLayer(
        final ConnectionSecurityLayer layer)
    {
      synchronized (this)
      {
        installFilter(new ConnectionSecurityLayerFilter(layer, connection
            .getTransport().getMemoryManager()));
      }
    }
    @Override
    public void enableTLS(final SSLContext sslContext,
        final String[] protocols, final String[] suites,
        final boolean wantClientAuth, final boolean needClientAuth)
    {
      Validator.ensureNotNull(sslContext);
      synchronized (this)
      {
        if (isTLSEnabled())
        {
          throw new IllegalStateException("TLS already enabled");
        protected AbstractHandler(final int messageID, final Connection<?> connection) {
            this.messageID = messageID;
            this.connection = connection;
        }
        SSLEngineConfigurator sslEngineConfigurator =
            new SSLEngineConfigurator(sslContext, false, false, false);
        sslEngineConfigurator.setEnabledCipherSuites(suites);
        sslEngineConfigurator.setEnabledProtocols(protocols);
        sslEngineConfigurator.setWantClientAuth(wantClientAuth);
        sslEngineConfigurator.setNeedClientAuth(needClientAuth);
        installFilter(new SSLFilter(sslEngineConfigurator, null));
      }
    }
    /**
     * {@inheritDoc}
     */
    public String toString()
    {
      StringBuilder builder = new StringBuilder();
      builder.append("LDAPClientContext(");
      builder.append(getLocalAddress());
      builder.append(',');
      builder.append(getPeerAddress());
      builder.append(')');
      return builder.toString();
    }
    private void close()
    {
      isClosed = true;
    }
    private ServerConnection<Integer> getServerConnection()
    {
      return serverConnection;
    }
    private void setServerConnection(
        final ServerConnection<Integer> serverConnection)
    {
      this.serverConnection = serverConnection;
    }
    /**
     * Installs a new Grizzly filter (e.g. SSL/SASL) beneath the top-level LDAP
     * filter.
     *
     * @param filter
     *          The filter to be installed.
     */
    private void installFilter(final Filter filter)
    {
      // Determine the index where the filter should be added.
      final FilterChain oldFilterChain = (FilterChain) connection.getProcessor();
      int filterIndex = oldFilterChain.size() - 1;
      if (filter instanceof SSLFilter)
      {
        // Beneath any ConnectionSecurityLayerFilters if present, otherwise
        // beneath the LDAP filter.
        for (int i = oldFilterChain.size() - 2; i >= 0; i--)
        {
          if (!(oldFilterChain.get(i) instanceof ConnectionSecurityLayerFilter))
          {
            filterIndex = i + 1;
            break;
          }
        }
      }
      // Create the new filter chain.
      final FilterChain newFilterChain = FilterChainBuilder.stateless()
          .addAll(oldFilterChain).add(filterIndex, filter).build();
      connection.setProcessor(newFilterChain);
    }
    /**
     * Indicates whether or not TLS is enabled this provided connection.
     *
     * @return {@code true} if TLS is enabled on this connection, otherwise
     *         {@code false}.
     */
    private boolean isTLSEnabled()
    {
      synchronized (this)
      {
        final FilterChain currentFilterChain = (FilterChain) connection
            .getProcessor();
        for (Filter filter : currentFilterChain)
        {
          if (filter instanceof SSLFilter)
          {
        @Override
        public final boolean handleIntermediateResponse(final IntermediateResponse response) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.intermediateResponse(asn1Writer, messageID, response);
                connection.write(asn1Writer.getBuffer(), null);
            } catch (final IOException ioe) {
                notifyConnectionException(connection, ioe);
                return false;
            } finally {
                asn1Writer.recycle();
            }
            return true;
          }
        }
        return false;
      }
    }
  }
  private final class CompareHandler extends AbstractHandler<CompareResult>
  {
    private CompareHandler(final int messageID, final Connection<?> connection)
    {
      super(messageID, connection);
    }
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      final Result result = error.getResult();
      if (result instanceof CompareResult)
      {
        handleResult((CompareResult) result);
      }
      else
      {
        final CompareResult newResult = Responses.newCompareResult(result
            .getResultCode());
        newResult.setDiagnosticMessage(result.getDiagnosticMessage());
        newResult.setMatchedDN(result.getMatchedDN());
        newResult.setCause(result.getCause());
        for (final Control control : result.getControls())
        {
          newResult.addControl(control);
    private final class AddHandler extends AbstractHandler<Result> {
        private AddHandler(final int messageID, final Connection<?> connection) {
            super(messageID, connection);
        }
        handleResult(newResult);
      }
    }
    @Override
    public void handleResult(final CompareResult result)
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        LDAP_WRITER.compareResult(asn1Writer, messageID, result);
        connection.write(asn1Writer.getBuffer(), null);
      }
      catch (final IOException ioe)
      {
        notifyConnectionException(connection, ioe);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
  }
  private final class DeleteHandler extends AbstractHandler<Result>
  {
    private DeleteHandler(final int messageID, final Connection<?> connection)
    {
      super(messageID, connection);
    }
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      handleResult(error.getResult());
    }
    @Override
    public void handleResult(final Result result)
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        LDAP_WRITER.deleteResult(asn1Writer, messageID, result);
        connection.write(asn1Writer.getBuffer(), null);
      }
      catch (final IOException ioe)
      {
        notifyConnectionException(connection, ioe);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
  }
  private final class ExtendedHandler<R extends ExtendedResult> extends
      AbstractHandler<R>
  {
    private ExtendedHandler(final int messageID, final Connection<?> connection)
    {
      super(messageID, connection);
    }
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      final Result result = error.getResult();
      if (result instanceof ExtendedResult)
      {
        handleResult((ExtendedResult) result);
      }
      else
      {
        final ExtendedResult newResult = Responses
            .newGenericExtendedResult(result.getResultCode());
        newResult.setDiagnosticMessage(result.getDiagnosticMessage());
        newResult.setMatchedDN(result.getMatchedDN());
        newResult.setCause(result.getCause());
        for (final Control control : result.getControls())
        {
          newResult.addControl(control);
        @Override
        public void handleErrorResult(final ErrorResultException error) {
            handleResult(error.getResult());
        }
        handleResult(newResult);
      }
        @Override
        public void handleResult(final Result result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.addResult(asn1Writer, messageID, result);
                connection.write(asn1Writer.getBuffer(), null);
            } catch (final IOException ioe) {
                notifyConnectionException(connection, ioe);
            } finally {
                asn1Writer.recycle();
            }
        }
    }
    private final class BindHandler extends AbstractHandler<BindResult> {
        private BindHandler(final int messageID, final Connection<?> connection) {
            super(messageID, connection);
        }
        @Override
        public void handleErrorResult(final ErrorResultException error) {
            final Result result = error.getResult();
            if (result instanceof BindResult) {
                handleResult((BindResult) result);
            } else {
                final BindResult newResult = Responses.newBindResult(result.getResultCode());
                newResult.setDiagnosticMessage(result.getDiagnosticMessage());
                newResult.setMatchedDN(result.getMatchedDN());
                newResult.setCause(result.getCause());
                for (final Control control : result.getControls()) {
                    newResult.addControl(control);
                }
                handleResult(newResult);
            }
        }
        @Override
        public void handleResult(final BindResult result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.bindResult(asn1Writer, messageID, result);
                connection.write(asn1Writer.getBuffer(), null);
            } catch (final IOException ioe) {
                notifyConnectionException(connection, ioe);
            } finally {
                asn1Writer.recycle();
            }
        }
    }
    private final class ClientContextImpl implements LDAPClientContext {
        private final Connection<?> connection;
        private volatile boolean isClosed = false;
        private ServerConnection<Integer> serverConnection = null;
        private ClientContextImpl(final Connection<?> connection) {
            this.connection = connection;
        }
        @Override
        public void disconnect() {
            LDAPServerFilter.notifyConnectionDisconnected(connection, null, null);
        }
        @Override
        public void disconnect(final ResultCode resultCode, final String message) {
            Validator.ensureNotNull(resultCode);
            final GenericExtendedResult notification =
                    Responses.newGenericExtendedResult(resultCode).setOID(
                            OID_NOTICE_OF_DISCONNECTION).setDiagnosticMessage(message);
            sendUnsolicitedNotification(notification);
            LDAPServerFilter.notifyConnectionDisconnected(connection, resultCode, message);
        }
        @Override
        public InetSocketAddress getLocalAddress() {
            return (InetSocketAddress) connection.getLocalAddress();
        }
        @Override
        public InetSocketAddress getPeerAddress() {
            return (InetSocketAddress) connection.getPeerAddress();
        }
        @Override
        public int getSecurityStrengthFactor() {
            int ssf = 0;
            final SSLEngine sslEngine = SSLUtils.getSSLEngine(connection);
            if (sslEngine != null) {
                final String cipherString = sslEngine.getSession().getCipherSuite();
                for (final Map.Entry<String, Integer> mapEntry : CIPHER_KEY_SIZES.entrySet()) {
                    if (cipherString.indexOf(mapEntry.getKey()) >= 0) {
                        ssf = mapEntry.getValue();
                        break;
                    }
                }
            }
            return ssf;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isClosed() {
            return isClosed;
        }
        @Override
        public void sendUnsolicitedNotification(final ExtendedResult notification) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.extendedResult(asn1Writer, 0, notification);
                connection.write(asn1Writer.getBuffer(), null);
            } catch (final IOException ioe) {
                LDAPServerFilter.notifyConnectionException(connection, ioe);
            } finally {
                asn1Writer.recycle();
            }
        }
        @Override
        public void enableConnectionSecurityLayer(final ConnectionSecurityLayer layer) {
            synchronized (this) {
                installFilter(new ConnectionSecurityLayerFilter(layer, connection.getTransport()
                        .getMemoryManager()));
            }
        }
        @Override
        public void enableTLS(final SSLContext sslContext, final String[] protocols,
                final String[] suites, final boolean wantClientAuth, final boolean needClientAuth) {
            Validator.ensureNotNull(sslContext);
            synchronized (this) {
                if (isTLSEnabled()) {
                    throw new IllegalStateException("TLS already enabled");
                }
                SSLEngineConfigurator sslEngineConfigurator =
                        new SSLEngineConfigurator(sslContext, false, false, false);
                sslEngineConfigurator.setEnabledCipherSuites(suites);
                sslEngineConfigurator.setEnabledProtocols(protocols);
                sslEngineConfigurator.setWantClientAuth(wantClientAuth);
                sslEngineConfigurator.setNeedClientAuth(needClientAuth);
                installFilter(new SSLFilter(sslEngineConfigurator, null));
            }
        }
        /**
         * {@inheritDoc}
         */
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("LDAPClientContext(");
            builder.append(getLocalAddress());
            builder.append(',');
            builder.append(getPeerAddress());
            builder.append(')');
            return builder.toString();
        }
        private void close() {
            isClosed = true;
        }
        private ServerConnection<Integer> getServerConnection() {
            return serverConnection;
        }
        private void setServerConnection(final ServerConnection<Integer> serverConnection) {
            this.serverConnection = serverConnection;
        }
        /**
         * Installs a new Grizzly filter (e.g. SSL/SASL) beneath the top-level
         * LDAP filter.
         *
         * @param filter
         *            The filter to be installed.
         */
        private void installFilter(final Filter filter) {
            // Determine the index where the filter should be added.
            final FilterChain oldFilterChain = (FilterChain) connection.getProcessor();
            int filterIndex = oldFilterChain.size() - 1;
            if (filter instanceof SSLFilter) {
                // Beneath any ConnectionSecurityLayerFilters if present,
                // otherwise
                // beneath the LDAP filter.
                for (int i = oldFilterChain.size() - 2; i >= 0; i--) {
                    if (!(oldFilterChain.get(i) instanceof ConnectionSecurityLayerFilter)) {
                        filterIndex = i + 1;
                        break;
                    }
                }
            }
            // Create the new filter chain.
            final FilterChain newFilterChain =
                    FilterChainBuilder.stateless().addAll(oldFilterChain).add(filterIndex, filter)
                            .build();
            connection.setProcessor(newFilterChain);
        }
        /**
         * Indicates whether or not TLS is enabled this provided connection.
         *
         * @return {@code true} if TLS is enabled on this connection, otherwise
         *         {@code false}.
         */
        private boolean isTLSEnabled() {
            synchronized (this) {
                final FilterChain currentFilterChain = (FilterChain) connection.getProcessor();
                for (Filter filter : currentFilterChain) {
                    if (filter instanceof SSLFilter) {
                        return true;
                    }
                }
                return false;
            }
        }
    }
    private final class CompareHandler extends AbstractHandler<CompareResult> {
        private CompareHandler(final int messageID, final Connection<?> connection) {
            super(messageID, connection);
        }
        @Override
        public void handleErrorResult(final ErrorResultException error) {
            final Result result = error.getResult();
            if (result instanceof CompareResult) {
                handleResult((CompareResult) result);
            } else {
                final CompareResult newResult = Responses.newCompareResult(result.getResultCode());
                newResult.setDiagnosticMessage(result.getDiagnosticMessage());
                newResult.setMatchedDN(result.getMatchedDN());
                newResult.setCause(result.getCause());
                for (final Control control : result.getControls()) {
                    newResult.addControl(control);
                }
                handleResult(newResult);
            }
        }
        @Override
        public void handleResult(final CompareResult result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.compareResult(asn1Writer, messageID, result);
                connection.write(asn1Writer.getBuffer(), null);
            } catch (final IOException ioe) {
                notifyConnectionException(connection, ioe);
            } finally {
                asn1Writer.recycle();
            }
        }
    }
    private final class DeleteHandler extends AbstractHandler<Result> {
        private DeleteHandler(final int messageID, final Connection<?> connection) {
            super(messageID, connection);
        }
        @Override
        public void handleErrorResult(final ErrorResultException error) {
            handleResult(error.getResult());
        }
        @Override
        public void handleResult(final Result result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.deleteResult(asn1Writer, messageID, result);
                connection.write(asn1Writer.getBuffer(), null);
            } catch (final IOException ioe) {
                notifyConnectionException(connection, ioe);
            } finally {
                asn1Writer.recycle();
            }
        }
    }
    private final class ExtendedHandler<R extends ExtendedResult> extends AbstractHandler<R> {
        private ExtendedHandler(final int messageID, final Connection<?> connection) {
            super(messageID, connection);
        }
        @Override
        public void handleErrorResult(final ErrorResultException error) {
            final Result result = error.getResult();
            if (result instanceof ExtendedResult) {
                handleResult((ExtendedResult) result);
            } else {
                final ExtendedResult newResult =
                        Responses.newGenericExtendedResult(result.getResultCode());
                newResult.setDiagnosticMessage(result.getDiagnosticMessage());
                newResult.setMatchedDN(result.getMatchedDN());
                newResult.setCause(result.getCause());
                for (final Control control : result.getControls()) {
                    newResult.addControl(control);
                }
                handleResult(newResult);
            }
        }
        @Override
        public void handleResult(final ExtendedResult result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.extendedResult(asn1Writer, messageID, result);
                connection.write(asn1Writer.getBuffer(), null);
            } catch (final IOException ioe) {
                notifyConnectionException(connection, ioe);
            } finally {
                asn1Writer.recycle();
            }
        }
    }
    private final class ModifyDNHandler extends AbstractHandler<Result> {
        private ModifyDNHandler(final int messageID, final Connection<?> connection) {
            super(messageID, connection);
        }
        @Override
        public void handleErrorResult(final ErrorResultException error) {
            handleResult(error.getResult());
        }
        @Override
        public void handleResult(final Result result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.modifyDNResult(asn1Writer, messageID, result);
                connection.write(asn1Writer.getBuffer(), null);
            } catch (final IOException ioe) {
                notifyConnectionException(connection, ioe);
            } finally {
                asn1Writer.recycle();
            }
        }
    }
    private final class ModifyHandler extends AbstractHandler<Result> {
        private ModifyHandler(final int messageID, final Connection<?> connection) {
            super(messageID, connection);
        }
        @Override
        public void handleErrorResult(final ErrorResultException error) {
            handleResult(error.getResult());
        }
        @Override
        public void handleResult(final Result result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.modifyResult(asn1Writer, messageID, result);
                connection.write(asn1Writer.getBuffer(), null);
            } catch (final IOException ioe) {
                notifyConnectionException(connection, ioe);
            } finally {
                asn1Writer.recycle();
            }
        }
    }
    private final class SearchHandler extends AbstractHandler<Result> implements
            SearchResultHandler {
        private SearchHandler(final int messageID, final Connection<?> connection) {
            super(messageID, connection);
        }
        @Override
        public boolean handleEntry(final SearchResultEntry entry) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.searchResultEntry(asn1Writer, messageID, entry);
                connection.write(asn1Writer.getBuffer(), null);
            } catch (final IOException ioe) {
                notifyConnectionException(connection, ioe);
                return false;
            } finally {
                asn1Writer.recycle();
            }
            return true;
        }
        @Override
        public void handleErrorResult(final ErrorResultException error) {
            handleResult(error.getResult());
        }
        @Override
        public boolean handleReference(final SearchResultReference reference) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.searchResultReference(asn1Writer, messageID, reference);
                connection.write(asn1Writer.getBuffer(), null);
            } catch (final IOException ioe) {
                notifyConnectionException(connection, ioe);
                return false;
            } finally {
                asn1Writer.recycle();
            }
            return true;
        }
        @Override
        public void handleResult(final Result result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.searchResult(asn1Writer, messageID, result);
                connection.write(asn1Writer.getBuffer(), null);
            } catch (final IOException ioe) {
                notifyConnectionException(connection, ioe);
            } finally {
                asn1Writer.recycle();
            }
        }
    }
    // Map of cipher phrases to effective key size (bits). Taken from the
    // following RFCs: 5289, 4346, 3268,4132 and 4162.
    private static final Map<String, Integer> CIPHER_KEY_SIZES;
    static {
        CIPHER_KEY_SIZES = new LinkedHashMap<String, Integer>();
        CIPHER_KEY_SIZES.put("_WITH_AES_256_CBC_", 256);
        CIPHER_KEY_SIZES.put("_WITH_CAMELLIA_256_CBC_", 256);
        CIPHER_KEY_SIZES.put("_WITH_AES_256_GCM_", 256);
        CIPHER_KEY_SIZES.put("_WITH_3DES_EDE_CBC_", 112);
        CIPHER_KEY_SIZES.put("_WITH_AES_128_GCM_", 128);
        CIPHER_KEY_SIZES.put("_WITH_SEED_CBC_", 128);
        CIPHER_KEY_SIZES.put("_WITH_CAMELLIA_128_CBC_", 128);
        CIPHER_KEY_SIZES.put("_WITH_AES_128_CBC_", 128);
        CIPHER_KEY_SIZES.put("_WITH_IDEA_CBC_", 128);
        CIPHER_KEY_SIZES.put("_WITH_DES_CBC_", 56);
        CIPHER_KEY_SIZES.put("_WITH_RC2_CBC_40_", 40);
        CIPHER_KEY_SIZES.put("_WITH_RC4_40_", 40);
        CIPHER_KEY_SIZES.put("_WITH_DES40_CBC_", 40);
        CIPHER_KEY_SIZES.put("_WITH_NULL_", 0);
    }
    private static final LDAPWriter LDAP_WRITER = new LDAPWriter();
    private static final Attribute<ClientContextImpl> LDAP_CONNECTION_ATTR =
            Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPServerConnection");
    private static final Attribute<ASN1BufferReader> LDAP_ASN1_READER_ATTR =
            Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPASN1Reader");
    private static void notifyConnectionClosed(final Connection<?> connection, final int messageID,
            final UnbindRequest unbindRequest) {
        final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.remove(connection);
        if (clientContext != null) {
            // Close the connection context.
            clientContext.close();
            // Notify the server connection: it may be null if disconnect is
            // invoked
            // during accept.
            final ServerConnection<Integer> serverConnection = clientContext.getServerConnection();
            if (serverConnection != null) {
                serverConnection.handleConnectionClosed(messageID, unbindRequest);
            }
            // If this close was a result of an unbind request then the
            // connection
            // won't actually be closed yet. To avoid TIME_WAIT TCP state, let
            // the
            // client disconnect.
            if (unbindRequest != null) {
                return;
            }
            // Close the connection.
            try {
                connection.close();
            } catch (final IOException e) {
                StaticUtils.DEBUG_LOG.warning("Error closing connection: " + e);
            }
        }
    }
    private static void notifyConnectionDisconnected(final Connection<?> connection,
            final ResultCode resultCode, final String message) {
        final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.remove(connection);
        if (clientContext != null) {
            // Close the connection context.
            clientContext.close();
            // Notify the server connection: it may be null if disconnect is
            // invoked
            // during accept.
            final ServerConnection<Integer> serverConnection = clientContext.getServerConnection();
            if (serverConnection != null) {
                serverConnection.handleConnectionDisconnected(resultCode, message);
            }
            // Close the connection.
            try {
                connection.close();
            } catch (final IOException e) {
                StaticUtils.DEBUG_LOG.warning("Error closing connection: " + e);
            }
        }
    }
    private static void notifyConnectionException(final Connection<?> connection,
            final Throwable error) {
        final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.remove(connection);
        if (clientContext != null) {
            // Close the connection context.
            clientContext.close();
            // Notify the server connection: it may be null if disconnect is
            // invoked
            // during accept.
            final ServerConnection<Integer> serverConnection = clientContext.getServerConnection();
            if (serverConnection != null) {
                serverConnection.handleConnectionError(error);
            }
            // Close the connection.
            try {
                connection.close();
            } catch (final IOException e) {
                StaticUtils.DEBUG_LOG.warning("Error closing connection: " + e);
            }
        }
    }
    private final AbstractLDAPMessageHandler<FilterChainContext> serverRequestHandler =
            new AbstractLDAPMessageHandler<FilterChainContext>() {
                @Override
                public void abandonRequest(final FilterChainContext ctx, final int messageID,
                        final AbandonRequest request) throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        conn.handleAbandon(messageID, request);
                    }
                }
                @Override
                public void addRequest(final FilterChainContext ctx, final int messageID,
                        final AddRequest request) throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final AddHandler handler = new AddHandler(messageID, ctx.getConnection());
                        conn.handleAdd(messageID, request, handler, handler);
                    }
                }
                @Override
                public void bindRequest(final FilterChainContext ctx, final int messageID,
                        final int version, final GenericBindRequest bindContext)
                        throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final BindHandler handler = new BindHandler(messageID, ctx.getConnection());
                        conn.handleBind(messageID, version, bindContext, handler, handler);
                    }
                }
                @Override
                public void compareRequest(final FilterChainContext ctx, final int messageID,
                        final CompareRequest request) throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final CompareHandler handler =
                                new CompareHandler(messageID, ctx.getConnection());
                        conn.handleCompare(messageID, request, handler, handler);
                    }
                }
                @Override
                public void deleteRequest(final FilterChainContext ctx, final int messageID,
                        final DeleteRequest request) throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final DeleteHandler handler =
                                new DeleteHandler(messageID, ctx.getConnection());
                        conn.handleDelete(messageID, request, handler, handler);
                    }
                }
                @Override
                public <R extends ExtendedResult> void extendedRequest(
                        final FilterChainContext ctx, final int messageID,
                        final ExtendedRequest<R> request) throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final ExtendedHandler<R> handler =
                                new ExtendedHandler<R>(messageID, ctx.getConnection());
                        conn.handleExtendedRequest(messageID, request, handler, handler);
                    }
                }
                @Override
                public void modifyDNRequest(final FilterChainContext ctx, final int messageID,
                        final ModifyDNRequest request) throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final ModifyDNHandler handler =
                                new ModifyDNHandler(messageID, ctx.getConnection());
                        conn.handleModifyDN(messageID, request, handler, handler);
                    }
                }
                @Override
                public void modifyRequest(final FilterChainContext ctx, final int messageID,
                        final ModifyRequest request) throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final ModifyHandler handler =
                                new ModifyHandler(messageID, ctx.getConnection());
                        conn.handleModify(messageID, request, handler, handler);
                    }
                }
                @Override
                public void searchRequest(final FilterChainContext ctx, final int messageID,
                        final SearchRequest request) throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final SearchHandler handler =
                                new SearchHandler(messageID, ctx.getConnection());
                        conn.handleSearch(messageID, request, handler, handler);
                    }
                }
                @Override
                public void unbindRequest(final FilterChainContext ctx, final int messageID,
                        final UnbindRequest request) {
                    notifyConnectionClosed(ctx.getConnection(), messageID, request);
                }
                @Override
                public void unrecognizedMessage(final FilterChainContext ctx, final int messageID,
                        final byte messageTag, final ByteString messageBytes) {
                    notifyConnectionException(ctx.getConnection(), new UnsupportedMessageException(
                            messageID, messageTag, messageBytes));
                }
            };
    private final int maxASN1ElementSize;
    private final LDAPReader ldapReader;
    private final LDAPListenerImpl listener;
    LDAPServerFilter(final LDAPListenerImpl listener, final LDAPReader ldapReader,
            final int maxASN1ElementSize) {
        this.listener = listener;
        this.ldapReader = ldapReader;
        this.maxASN1ElementSize = maxASN1ElementSize;
    }
    @Override
    public void handleResult(final ExtendedResult result)
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        LDAP_WRITER.extendedResult(asn1Writer, messageID, result);
        connection.write(asn1Writer.getBuffer(), null);
      }
      catch (final IOException ioe)
      {
        notifyConnectionException(connection, ioe);
      }
      finally
      {
        asn1Writer.recycle();
      }
    public void exceptionOccurred(final FilterChainContext ctx, final Throwable error) {
        notifyConnectionException(ctx.getConnection(), error);
    }
  }
  private final class ModifyDNHandler extends AbstractHandler<Result>
  {
    private ModifyDNHandler(final int messageID, final Connection<?> connection)
    {
      super(messageID, connection);
    }
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      handleResult(error.getResult());
    public NextAction handleAccept(final FilterChainContext ctx) throws IOException {
        final Connection<?> connection = ctx.getConnection();
        connection.configureBlocking(true);
        try {
            final ClientContextImpl clientContext = new ClientContextImpl(connection);
            final ServerConnection<Integer> serverConn =
                    listener.getConnectionFactory().handleAccept(clientContext);
            clientContext.setServerConnection(serverConn);
            LDAP_CONNECTION_ATTR.set(connection, clientContext);
        } catch (final ErrorResultException e) {
            connection.close();
        }
        return ctx.getStopAction();
    }
    @Override
    public void handleResult(final Result result)
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        LDAP_WRITER.modifyDNResult(asn1Writer, messageID, result);
        connection.write(asn1Writer.getBuffer(), null);
      }
      catch (final IOException ioe)
      {
        notifyConnectionException(connection, ioe);
      }
      finally
      {
        asn1Writer.recycle();
      }
    public NextAction handleClose(final FilterChainContext ctx) throws IOException {
        notifyConnectionClosed(ctx.getConnection(), -1, null);
        return ctx.getStopAction();
    }
  }
  private final class ModifyHandler extends AbstractHandler<Result>
  {
    private ModifyHandler(final int messageID, final Connection<?> connection)
    {
      super(messageID, connection);
    }
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      handleResult(error.getResult());
    public NextAction handleRead(final FilterChainContext ctx) throws IOException {
        final Buffer buffer = (Buffer) ctx.getMessage();
        ASN1BufferReader asn1Reader = LDAP_ASN1_READER_ATTR.get(ctx.getConnection());
        if (asn1Reader == null) {
            asn1Reader =
                    new ASN1BufferReader(maxASN1ElementSize, ctx.getConnection().getTransport()
                            .getMemoryManager());
            LDAP_ASN1_READER_ATTR.set(ctx.getConnection(), asn1Reader);
        }
        asn1Reader.appendBytesRead(buffer);
        try {
            while (asn1Reader.elementAvailable()) {
                ldapReader.decode(asn1Reader, serverRequestHandler, ctx);
            }
        } finally {
            asn1Reader.disposeBytesRead();
        }
        return ctx.getStopAction();
    }
    @Override
    public void handleResult(final Result result)
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        LDAP_WRITER.modifyResult(asn1Writer, messageID, result);
        connection.write(asn1Writer.getBuffer(), null);
      }
      catch (final IOException ioe)
      {
        notifyConnectionException(connection, ioe);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
  }
  private final class SearchHandler extends AbstractHandler<Result> implements
      SearchResultHandler
  {
    private SearchHandler(final int messageID, final Connection<?> connection)
    {
      super(messageID, connection);
    }
    @Override
    public boolean handleEntry(final SearchResultEntry entry)
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        LDAP_WRITER.searchResultEntry(asn1Writer, messageID, entry);
        connection.write(asn1Writer.getBuffer(), null);
      }
      catch (final IOException ioe)
      {
        notifyConnectionException(connection, ioe);
        return false;
      }
      finally
      {
        asn1Writer.recycle();
      }
      return true;
    }
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      handleResult(error.getResult());
    }
    @Override
    public boolean handleReference(final SearchResultReference reference)
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        LDAP_WRITER.searchResultReference(asn1Writer, messageID, reference);
        connection.write(asn1Writer.getBuffer(), null);
      }
      catch (final IOException ioe)
      {
        notifyConnectionException(connection, ioe);
        return false;
      }
      finally
      {
        asn1Writer.recycle();
      }
      return true;
    }
    @Override
    public void handleResult(final Result result)
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
      try
      {
        LDAP_WRITER.searchResult(asn1Writer, messageID, result);
        connection.write(asn1Writer.getBuffer(), null);
      }
      catch (final IOException ioe)
      {
        notifyConnectionException(connection, ioe);
      }
      finally
      {
        asn1Writer.recycle();
      }
    }
  }
  // Map of cipher phrases to effective key size (bits). Taken from the
  // following RFCs: 5289, 4346, 3268,4132 and 4162.
  private static final Map<String, Integer> CIPHER_KEY_SIZES;
  static
  {
    CIPHER_KEY_SIZES = new LinkedHashMap<String, Integer>();
    CIPHER_KEY_SIZES.put("_WITH_AES_256_CBC_", 256);
    CIPHER_KEY_SIZES.put("_WITH_CAMELLIA_256_CBC_", 256);
    CIPHER_KEY_SIZES.put("_WITH_AES_256_GCM_", 256);
    CIPHER_KEY_SIZES.put("_WITH_3DES_EDE_CBC_", 112);
    CIPHER_KEY_SIZES.put("_WITH_AES_128_GCM_", 128);
    CIPHER_KEY_SIZES.put("_WITH_SEED_CBC_", 128);
    CIPHER_KEY_SIZES.put("_WITH_CAMELLIA_128_CBC_", 128);
    CIPHER_KEY_SIZES.put("_WITH_AES_128_CBC_", 128);
    CIPHER_KEY_SIZES.put("_WITH_IDEA_CBC_", 128);
    CIPHER_KEY_SIZES.put("_WITH_DES_CBC_", 56);
    CIPHER_KEY_SIZES.put("_WITH_RC2_CBC_40_", 40);
    CIPHER_KEY_SIZES.put("_WITH_RC4_40_", 40);
    CIPHER_KEY_SIZES.put("_WITH_DES40_CBC_", 40);
    CIPHER_KEY_SIZES.put("_WITH_NULL_", 0);
  }
  private static final LDAPWriter LDAP_WRITER = new LDAPWriter();
  private static final Attribute<ClientContextImpl> LDAP_CONNECTION_ATTR =
    Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPServerConnection");
  private static final Attribute<ASN1BufferReader> LDAP_ASN1_READER_ATTR =
    Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPASN1Reader");
  private static void notifyConnectionClosed(final Connection<?> connection,
      final int messageID, final UnbindRequest unbindRequest)
  {
    final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR
        .remove(connection);
    if (clientContext != null)
    {
      // Close the connection context.
      clientContext.close();
      // Notify the server connection: it may be null if disconnect is invoked
      // during accept.
      final ServerConnection<Integer> serverConnection = clientContext
          .getServerConnection();
      if (serverConnection != null)
      {
        serverConnection.handleConnectionClosed(messageID, unbindRequest);
      }
      // If this close was a result of an unbind request then the connection
      // won't actually be closed yet. To avoid TIME_WAIT TCP state, let the
      // client disconnect.
      if (unbindRequest != null)
      {
        return;
      }
      // Close the connection.
      try
      {
        connection.close();
      }
      catch (final IOException e)
      {
        StaticUtils.DEBUG_LOG.warning("Error closing connection: " + e);
      }
    }
  }
  private static void notifyConnectionDisconnected(
      final Connection<?> connection, final ResultCode resultCode,
      final String message)
  {
    final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR
        .remove(connection);
    if (clientContext != null)
    {
      // Close the connection context.
      clientContext.close();
      // Notify the server connection: it may be null if disconnect is invoked
      // during accept.
      final ServerConnection<Integer> serverConnection = clientContext
          .getServerConnection();
      if (serverConnection != null)
      {
        serverConnection.handleConnectionDisconnected(resultCode, message);
      }
      // Close the connection.
      try
      {
        connection.close();
      }
      catch (final IOException e)
      {
        StaticUtils.DEBUG_LOG.warning("Error closing connection: " + e);
      }
    }
  }
  private static void notifyConnectionException(final Connection<?> connection,
      final Throwable error)
  {
    final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR
        .remove(connection);
    if (clientContext != null)
    {
      // Close the connection context.
      clientContext.close();
      // Notify the server connection: it may be null if disconnect is invoked
      // during accept.
      final ServerConnection<Integer> serverConnection = clientContext
          .getServerConnection();
      if (serverConnection != null)
      {
        serverConnection.handleConnectionError(error);
      }
      // Close the connection.
      try
      {
        connection.close();
      }
      catch (final IOException e)
      {
        StaticUtils.DEBUG_LOG.warning("Error closing connection: " + e);
      }
    }
  }
  private final AbstractLDAPMessageHandler<FilterChainContext> serverRequestHandler =
    new AbstractLDAPMessageHandler<FilterChainContext>()
  {
    @Override
    public void abandonRequest(final FilterChainContext ctx,
        final int messageID, final AbandonRequest request)
        throws UnexpectedRequestException
    {
      final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if (clientContext != null)
      {
        final ServerConnection<Integer> conn = clientContext
            .getServerConnection();
        conn.handleAbandon(messageID, request);
      }
    }
    @Override
    public void addRequest(final FilterChainContext ctx, final int messageID,
        final AddRequest request) throws UnexpectedRequestException
    {
      final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if (clientContext != null)
      {
        final ServerConnection<Integer> conn = clientContext
            .getServerConnection();
        final AddHandler handler = new AddHandler(messageID,
            ctx.getConnection());
        conn.handleAdd(messageID, request, handler, handler);
      }
    }
    @Override
    public void bindRequest(final FilterChainContext ctx, final int messageID,
        final int version, final GenericBindRequest bindContext)
        throws UnexpectedRequestException
    {
      final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if (clientContext != null)
      {
        final ServerConnection<Integer> conn = clientContext
            .getServerConnection();
        final BindHandler handler = new BindHandler(messageID,
            ctx.getConnection());
        conn.handleBind(messageID, version, bindContext, handler, handler);
      }
    }
    @Override
    public void compareRequest(final FilterChainContext ctx,
        final int messageID, final CompareRequest request)
        throws UnexpectedRequestException
    {
      final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if (clientContext != null)
      {
        final ServerConnection<Integer> conn = clientContext
            .getServerConnection();
        final CompareHandler handler = new CompareHandler(messageID,
            ctx.getConnection());
        conn.handleCompare(messageID, request, handler, handler);
      }
    }
    @Override
    public void deleteRequest(final FilterChainContext ctx,
        final int messageID, final DeleteRequest request)
        throws UnexpectedRequestException
    {
      final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if (clientContext != null)
      {
        final ServerConnection<Integer> conn = clientContext
            .getServerConnection();
        final DeleteHandler handler = new DeleteHandler(messageID,
            ctx.getConnection());
        conn.handleDelete(messageID, request, handler, handler);
      }
    }
    @Override
    public <R extends ExtendedResult> void extendedRequest(
        final FilterChainContext ctx, final int messageID,
        final ExtendedRequest<R> request) throws UnexpectedRequestException
    {
      final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if (clientContext != null)
      {
        final ServerConnection<Integer> conn = clientContext
            .getServerConnection();
        final ExtendedHandler<R> handler = new ExtendedHandler<R>(messageID,
            ctx.getConnection());
        conn.handleExtendedRequest(messageID, request, handler, handler);
      }
    }
    @Override
    public void modifyDNRequest(final FilterChainContext ctx,
        final int messageID, final ModifyDNRequest request)
        throws UnexpectedRequestException
    {
      final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if (clientContext != null)
      {
        final ServerConnection<Integer> conn = clientContext
            .getServerConnection();
        final ModifyDNHandler handler = new ModifyDNHandler(messageID,
            ctx.getConnection());
        conn.handleModifyDN(messageID, request, handler, handler);
      }
    }
    @Override
    public void modifyRequest(final FilterChainContext ctx,
        final int messageID, final ModifyRequest request)
        throws UnexpectedRequestException
    {
      final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if (clientContext != null)
      {
        final ServerConnection<Integer> conn = clientContext
            .getServerConnection();
        final ModifyHandler handler = new ModifyHandler(messageID,
            ctx.getConnection());
        conn.handleModify(messageID, request, handler, handler);
      }
    }
    @Override
    public void searchRequest(final FilterChainContext ctx,
        final int messageID, final SearchRequest request)
        throws UnexpectedRequestException
    {
      final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(ctx
          .getConnection());
      if (clientContext != null)
      {
        final ServerConnection<Integer> conn = clientContext
            .getServerConnection();
        final SearchHandler handler = new SearchHandler(messageID,
            ctx.getConnection());
        conn.handleSearch(messageID, request, handler, handler);
      }
    }
    @Override
    public void unbindRequest(final FilterChainContext ctx,
        final int messageID, final UnbindRequest request)
    {
      notifyConnectionClosed(ctx.getConnection(), messageID, request);
    }
    @Override
    public void unrecognizedMessage(final FilterChainContext ctx,
        final int messageID, final byte messageTag,
        final ByteString messageBytes)
    {
      notifyConnectionException(ctx.getConnection(),
          new UnsupportedMessageException(messageID, messageTag, messageBytes));
    }
  };
  private final int maxASN1ElementSize;
  private final LDAPReader ldapReader;
  private final LDAPListenerImpl listener;
  LDAPServerFilter(final LDAPListenerImpl listener,
      final LDAPReader ldapReader, final int maxASN1ElementSize)
  {
    this.listener = listener;
    this.ldapReader = ldapReader;
    this.maxASN1ElementSize = maxASN1ElementSize;
  }
  @Override
  public void exceptionOccurred(final FilterChainContext ctx,
      final Throwable error)
  {
    notifyConnectionException(ctx.getConnection(), error);
  }
  @Override
  public NextAction handleAccept(final FilterChainContext ctx)
      throws IOException
  {
    final Connection<?> connection = ctx.getConnection();
    connection.configureBlocking(true);
    try
    {
      final ClientContextImpl clientContext = new ClientContextImpl(connection);
      final ServerConnection<Integer> serverConn = listener
          .getConnectionFactory().handleAccept(clientContext);
      clientContext.setServerConnection(serverConn);
      LDAP_CONNECTION_ATTR.set(connection, clientContext);
    }
    catch (final ErrorResultException e)
    {
      connection.close();
    }
    return ctx.getStopAction();
  }
  @Override
  public NextAction handleClose(final FilterChainContext ctx)
      throws IOException
  {
    notifyConnectionClosed(ctx.getConnection(), -1, null);
    return ctx.getStopAction();
  }
  @Override
  public NextAction handleRead(final FilterChainContext ctx) throws IOException
  {
    final Buffer buffer = (Buffer) ctx.getMessage();
    ASN1BufferReader asn1Reader = LDAP_ASN1_READER_ATTR
        .get(ctx.getConnection());
    if (asn1Reader == null)
    {
      asn1Reader = new ASN1BufferReader(maxASN1ElementSize, ctx.getConnection()
          .getTransport().getMemoryManager());
      LDAP_ASN1_READER_ATTR.set(ctx.getConnection(), asn1Reader);
    }
    asn1Reader.appendBytesRead(buffer);
    try
    {
      while (asn1Reader.elementAvailable())
      {
        ldapReader.decode(asn1Reader, serverRequestHandler, ctx);
      }
    }
    finally
    {
      asn1Reader.disposeBytesRead();
    }
    return ctx.getStopAction();
  }
}
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPUtils.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -27,8 +26,6 @@
package com.forgerock.opendj.ldap;
import static com.forgerock.opendj.ldap.LDAPConstants.*;
import java.io.IOException;
@@ -38,690 +35,508 @@
import org.forgerock.opendj.asn1.ASN1Reader;
import org.forgerock.opendj.asn1.ASN1Writer;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeOptions;
import org.forgerock.opendj.ldap.Filter;
import org.forgerock.opendj.ldap.FilterVisitor;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
/**
 * Common LDAP utility methods which may be used when implementing new controls
 * and extension.
 */
public final class LDAPUtils
{
public final class LDAPUtils {
  private static final FilterVisitor<IOException, ASN1Writer> ASN1_ENCODER =
    new FilterVisitor<IOException, ASN1Writer>()
  {
    private static final FilterVisitor<IOException, ASN1Writer> ASN1_ENCODER =
            new FilterVisitor<IOException, ASN1Writer>() {
    public IOException visitAndFilter(final ASN1Writer writer,
        final List<Filter> subFilters)
    {
      try
      {
        writer.writeStartSequence(TYPE_FILTER_AND);
        for (final Filter subFilter : subFilters)
        {
          final IOException e = subFilter.accept(this, writer);
          if (e != null)
          {
            return e;
          }
                public IOException visitAndFilter(final ASN1Writer writer,
                        final List<Filter> subFilters) {
                    try {
                        writer.writeStartSequence(TYPE_FILTER_AND);
                        for (final Filter subFilter : subFilters) {
                            final IOException e = subFilter.accept(this, writer);
                            if (e != null) {
                                return e;
                            }
                        }
                        writer.writeEndSequence();
                        return null;
                    } catch (final IOException e) {
                        return e;
                    }
                }
                public IOException visitApproxMatchFilter(final ASN1Writer writer,
                        final String attributeDescription, final ByteString assertionValue) {
                    try {
                        writer.writeStartSequence(TYPE_FILTER_APPROXIMATE);
                        writer.writeOctetString(attributeDescription);
                        writer.writeOctetString(assertionValue);
                        writer.writeEndSequence();
                        return null;
                    } catch (final IOException e) {
                        return e;
                    }
                }
                public IOException visitEqualityMatchFilter(final ASN1Writer writer,
                        final String attributeDescription, final ByteString assertionValue) {
                    try {
                        writer.writeStartSequence(TYPE_FILTER_EQUALITY);
                        writer.writeOctetString(attributeDescription);
                        writer.writeOctetString(assertionValue);
                        writer.writeEndSequence();
                        return null;
                    } catch (final IOException e) {
                        return e;
                    }
                }
                public IOException visitExtensibleMatchFilter(final ASN1Writer writer,
                        final String matchingRule, final String attributeDescription,
                        final ByteString assertionValue, final boolean dnAttributes) {
                    try {
                        writer.writeStartSequence(TYPE_FILTER_EXTENSIBLE_MATCH);
                        if (matchingRule != null) {
                            writer.writeOctetString(TYPE_MATCHING_RULE_ID, matchingRule);
                        }
                        if (attributeDescription != null) {
                            writer.writeOctetString(TYPE_MATCHING_RULE_TYPE, attributeDescription);
                        }
                        writer.writeOctetString(TYPE_MATCHING_RULE_VALUE, assertionValue);
                        if (dnAttributes) {
                            writer.writeBoolean(TYPE_MATCHING_RULE_DN_ATTRIBUTES, true);
                        }
                        writer.writeEndSequence();
                        return null;
                    } catch (final IOException e) {
                        return e;
                    }
                }
                public IOException visitGreaterOrEqualFilter(final ASN1Writer writer,
                        final String attributeDescription, final ByteString assertionValue) {
                    try {
                        writer.writeStartSequence(TYPE_FILTER_GREATER_OR_EQUAL);
                        writer.writeOctetString(attributeDescription);
                        writer.writeOctetString(assertionValue);
                        writer.writeEndSequence();
                        return null;
                    } catch (final IOException e) {
                        return e;
                    }
                }
                public IOException visitLessOrEqualFilter(final ASN1Writer writer,
                        final String attributeDescription, final ByteString assertionValue) {
                    try {
                        writer.writeStartSequence(TYPE_FILTER_LESS_OR_EQUAL);
                        writer.writeOctetString(attributeDescription);
                        writer.writeOctetString(assertionValue);
                        writer.writeEndSequence();
                        return null;
                    } catch (final IOException e) {
                        return e;
                    }
                }
                public IOException visitNotFilter(final ASN1Writer writer, final Filter subFilter) {
                    try {
                        writer.writeStartSequence(TYPE_FILTER_NOT);
                        final IOException e = subFilter.accept(this, writer);
                        if (e != null) {
                            return e;
                        }
                        writer.writeEndSequence();
                        return null;
                    } catch (final IOException e) {
                        return e;
                    }
                }
                public IOException visitOrFilter(final ASN1Writer writer,
                        final List<Filter> subFilters) {
                    try {
                        writer.writeStartSequence(TYPE_FILTER_OR);
                        for (final Filter subFilter : subFilters) {
                            final IOException e = subFilter.accept(this, writer);
                            if (e != null) {
                                return e;
                            }
                        }
                        writer.writeEndSequence();
                        return null;
                    } catch (final IOException e) {
                        return e;
                    }
                }
                public IOException visitPresentFilter(final ASN1Writer writer,
                        final String attributeDescription) {
                    try {
                        writer.writeOctetString(TYPE_FILTER_PRESENCE, attributeDescription);
                        return null;
                    } catch (final IOException e) {
                        return e;
                    }
                }
                public IOException visitSubstringsFilter(final ASN1Writer writer,
                        final String attributeDescription, final ByteString initialSubstring,
                        final List<ByteString> anySubstrings, final ByteString finalSubstring) {
                    try {
                        writer.writeStartSequence(TYPE_FILTER_SUBSTRING);
                        writer.writeOctetString(attributeDescription);
                        writer.writeStartSequence();
                        if (initialSubstring != null) {
                            writer.writeOctetString(TYPE_SUBINITIAL, initialSubstring);
                        }
                        for (final ByteSequence anySubstring : anySubstrings) {
                            writer.writeOctetString(TYPE_SUBANY, anySubstring);
                        }
                        if (finalSubstring != null) {
                            writer.writeOctetString(TYPE_SUBFINAL, finalSubstring);
                        }
                        writer.writeEndSequence();
                        writer.writeEndSequence();
                        return null;
                    } catch (final IOException e) {
                        return e;
                    }
                }
                public IOException visitUnrecognizedFilter(final ASN1Writer writer,
                        final byte filterTag, final ByteString filterBytes) {
                    try {
                        writer.writeOctetString(filterTag, filterBytes);
                        return null;
                    } catch (final IOException e) {
                        return e;
                    }
                }
            };
    /**
     * Reads the next ASN.1 element from the provided {@code ASN1Reader} as a
     * {@code Filter}.
     *
     * @param reader
     *            The {@code ASN1Reader} from which the ASN.1 encoded
     *            {@code Filter} should be read.
     * @return The decoded {@code Filter}.
     * @throws IOException
     *             If an error occurs while reading from {@code reader}.
     */
    public static Filter decodeFilter(final ASN1Reader reader) throws IOException {
        final byte type = reader.peekType();
        switch (type) {
        case TYPE_FILTER_AND:
            return decodeAndFilter(reader);
        case TYPE_FILTER_OR:
            return decodeOrFilter(reader);
        case TYPE_FILTER_NOT:
            return decodeNotFilter(reader);
        case TYPE_FILTER_EQUALITY:
            return decodeEqualityMatchFilter(reader);
        case TYPE_FILTER_GREATER_OR_EQUAL:
            return decodeGreaterOrEqualMatchFilter(reader);
        case TYPE_FILTER_LESS_OR_EQUAL:
            return decodeLessOrEqualMatchFilter(reader);
        case TYPE_FILTER_APPROXIMATE:
            return decodeApproxMatchFilter(reader);
        case TYPE_FILTER_SUBSTRING:
            return decodeSubstringsFilter(reader);
        case TYPE_FILTER_PRESENCE:
            return Filter.newPresentFilter(reader.readOctetStringAsString(type));
        case TYPE_FILTER_EXTENSIBLE_MATCH:
            return decodeExtensibleMatchFilter(reader);
        default:
            return Filter.newUnrecognizedFilter(type, reader.readOctetString(type));
        }
        writer.writeEndSequence();
        return null;
      }
      catch (final IOException e)
      {
        return e;
      }
    }
    public IOException visitApproxMatchFilter(final ASN1Writer writer,
        final String attributeDescription, final ByteString assertionValue)
    {
      try
      {
        writer.writeStartSequence(TYPE_FILTER_APPROXIMATE);
        writer.writeOctetString(attributeDescription);
        writer.writeOctetString(assertionValue);
        writer.writeEndSequence();
        return null;
      }
      catch (final IOException e)
      {
        return e;
      }
    /**
     * Reads the next ASN.1 element from the provided {@code ASN1Reader} as a
     * {@code SearchResultEntry}.
     *
     * @param reader
     *            The {@code ASN1Reader} from which the ASN.1 encoded
     *            {@code SearchResultEntry} should be read.
     * @param options
     *            The decode options to use when decoding the entry.
     * @return The decoded {@code SearchResultEntry}.
     * @throws IOException
     *             If an error occurs while reading from {@code reader}.
     */
    public static SearchResultEntry decodeSearchResultEntry(final ASN1Reader reader,
            final DecodeOptions options) throws IOException {
        return LDAPReader.decodeEntry(reader, options);
    }
    public IOException visitEqualityMatchFilter(final ASN1Writer writer,
        final String attributeDescription, final ByteString assertionValue)
    {
      try
      {
        writer.writeStartSequence(TYPE_FILTER_EQUALITY);
        writer.writeOctetString(attributeDescription);
        writer.writeOctetString(assertionValue);
        writer.writeEndSequence();
        return null;
      }
      catch (final IOException e)
      {
        return e;
      }
    /**
     * Writes the ASN.1 encoding of the provided {@code Filter} to the provided
     * {@code ASN1Writer}.
     *
     * @param writer
     *            The {@code ASN1Writer} to which the ASN.1 encoding of the
     *            provided {@code Filter} should be written.
     * @param filter
     *            The filter to be encoded.
     * @return The updated {@code ASN1Writer}.
     * @throws IOException
     *             If an error occurs while writing to {@code writer}.
     */
    public static ASN1Writer encodeFilter(final ASN1Writer writer, final Filter filter)
            throws IOException {
        final IOException e = filter.accept(ASN1_ENCODER, writer);
        if (e != null) {
            throw e;
        } else {
            return writer;
        }
    }
    /**
     * Writes the ASN.1 encoding of the provided {@code SearchResultEntry} to
     * the provided {@code ASN1Writer}.
     *
     * @param writer
     *            The {@code ASN1Writer} to which the ASN.1 encoding of the
     *            provided {@code SearchResultEntry} should be written.
     * @param entry
     *            The Search Result Entry to be encoded.
     * @return The updated {@code ASN1Writer}.
     * @throws IOException
     *             If an error occurs while writing to {@code writer}.
     */
    public static ASN1Writer encodeSearchResultEntry(final ASN1Writer writer,
            final SearchResultEntry entry) throws IOException {
        // FIXME: this should include Controls.
        LDAPWriter.encodeEntry(writer, entry);
        return writer;
    }
    // Decodes an and filter.
    private static Filter decodeAndFilter(final ASN1Reader reader) throws IOException {
        Filter filter;
    public IOException visitExtensibleMatchFilter(final ASN1Writer writer,
        final String matchingRule, final String attributeDescription,
        final ByteString assertionValue, final boolean dnAttributes)
    {
      try
      {
        writer.writeStartSequence(TYPE_FILTER_EXTENSIBLE_MATCH);
        if (matchingRule != null)
        {
          writer.writeOctetString(TYPE_MATCHING_RULE_ID, matchingRule);
        reader.readStartSequence(TYPE_FILTER_AND);
        try {
            if (reader.hasNextElement()) {
                final List<Filter> subFilters = new LinkedList<Filter>();
                do {
                    subFilters.add(decodeFilter(reader));
                } while (reader.hasNextElement());
                filter = Filter.newAndFilter(subFilters);
            } else {
                // No sub-filters - this is an RFC 4526 absolute true filter.
                filter = Filter.getAbsoluteTrueFilter();
            }
        } finally {
            reader.readEndSequence();
        }
        if (attributeDescription != null)
        {
          writer
              .writeOctetString(TYPE_MATCHING_RULE_TYPE, attributeDescription);
        return filter;
    }
    // Decodes an approximate match filter.
    private static Filter decodeApproxMatchFilter(final ASN1Reader reader) throws IOException {
        String attributeDescription;
        ByteString assertionValue;
        reader.readStartSequence(TYPE_FILTER_APPROXIMATE);
        try {
            attributeDescription = reader.readOctetStringAsString();
            assertionValue = reader.readOctetString();
        } finally {
            reader.readEndSequence();
        }
        writer.writeOctetString(TYPE_MATCHING_RULE_VALUE, assertionValue);
        return Filter.newApproxMatchFilter(attributeDescription, assertionValue);
    }
        if (dnAttributes)
        {
          writer.writeBoolean(TYPE_MATCHING_RULE_DN_ATTRIBUTES, true);
    // Decodes an equality match filter.
    private static Filter decodeEqualityMatchFilter(final ASN1Reader reader) throws IOException {
        String attributeDescription;
        ByteString assertionValue;
        reader.readStartSequence(TYPE_FILTER_EQUALITY);
        try {
            attributeDescription = reader.readOctetStringAsString();
            assertionValue = reader.readOctetString();
        } finally {
            reader.readEndSequence();
        }
        writer.writeEndSequence();
        return null;
      }
      catch (final IOException e)
      {
        return e;
      }
        return Filter.newEqualityMatchFilter(attributeDescription, assertionValue);
    }
    // Decodes an extensible match filter.
    private static Filter decodeExtensibleMatchFilter(final ASN1Reader reader) throws IOException {
        String matchingRule;
        String attributeDescription;
        boolean dnAttributes;
        ByteString assertionValue;
    public IOException visitGreaterOrEqualFilter(final ASN1Writer writer,
        final String attributeDescription, final ByteString assertionValue)
    {
      try
      {
        writer.writeStartSequence(TYPE_FILTER_GREATER_OR_EQUAL);
        writer.writeOctetString(attributeDescription);
        writer.writeOctetString(assertionValue);
        writer.writeEndSequence();
        return null;
      }
      catch (final IOException e)
      {
        return e;
      }
    }
    public IOException visitLessOrEqualFilter(final ASN1Writer writer,
        final String attributeDescription, final ByteString assertionValue)
    {
      try
      {
        writer.writeStartSequence(TYPE_FILTER_LESS_OR_EQUAL);
        writer.writeOctetString(attributeDescription);
        writer.writeOctetString(assertionValue);
        writer.writeEndSequence();
        return null;
      }
      catch (final IOException e)
      {
        return e;
      }
    }
    public IOException visitNotFilter(final ASN1Writer writer,
        final Filter subFilter)
    {
      try
      {
        writer.writeStartSequence(TYPE_FILTER_NOT);
        final IOException e = subFilter.accept(this, writer);
        if (e != null)
        {
          return e;
        }
        writer.writeEndSequence();
        return null;
      }
      catch (final IOException e)
      {
        return e;
      }
    }
    public IOException visitOrFilter(final ASN1Writer writer,
        final List<Filter> subFilters)
    {
      try
      {
        writer.writeStartSequence(TYPE_FILTER_OR);
        for (final Filter subFilter : subFilters)
        {
          final IOException e = subFilter.accept(this, writer);
          if (e != null)
          {
            return e;
          }
        }
        writer.writeEndSequence();
        return null;
      }
      catch (final IOException e)
      {
        return e;
      }
    }
    public IOException visitPresentFilter(final ASN1Writer writer,
        final String attributeDescription)
    {
      try
      {
        writer.writeOctetString(TYPE_FILTER_PRESENCE, attributeDescription);
        return null;
      }
      catch (final IOException e)
      {
        return e;
      }
    }
    public IOException visitSubstringsFilter(final ASN1Writer writer,
        final String attributeDescription, final ByteString initialSubstring,
        final List<ByteString> anySubstrings, final ByteString finalSubstring)
    {
      try
      {
        writer.writeStartSequence(TYPE_FILTER_SUBSTRING);
        writer.writeOctetString(attributeDescription);
        writer.writeStartSequence();
        if (initialSubstring != null)
        {
          writer.writeOctetString(TYPE_SUBINITIAL, initialSubstring);
        reader.readStartSequence(TYPE_FILTER_EXTENSIBLE_MATCH);
        try {
            matchingRule = null;
            if (reader.peekType() == TYPE_MATCHING_RULE_ID) {
                matchingRule = reader.readOctetStringAsString(TYPE_MATCHING_RULE_ID);
            }
            attributeDescription = null;
            if (reader.peekType() == TYPE_MATCHING_RULE_TYPE) {
                attributeDescription = reader.readOctetStringAsString(TYPE_MATCHING_RULE_TYPE);
            }
            dnAttributes = false;
            if (reader.hasNextElement() && (reader.peekType() == TYPE_MATCHING_RULE_DN_ATTRIBUTES)) {
                dnAttributes = reader.readBoolean();
            }
            assertionValue = reader.readOctetString(TYPE_MATCHING_RULE_VALUE);
        } finally {
            reader.readEndSequence();
        }
        for (final ByteSequence anySubstring : anySubstrings)
        {
          writer.writeOctetString(TYPE_SUBANY, anySubstring);
        return Filter.newExtensibleMatchFilter(matchingRule, attributeDescription, assertionValue,
                dnAttributes);
    }
    // Decodes a greater than or equal filter.
    private static Filter decodeGreaterOrEqualMatchFilter(final ASN1Reader reader)
            throws IOException {
        String attributeDescription;
        ByteString assertionValue;
        reader.readStartSequence(TYPE_FILTER_GREATER_OR_EQUAL);
        try {
            attributeDescription = reader.readOctetStringAsString();
            assertionValue = reader.readOctetString();
        } finally {
            reader.readEndSequence();
        }
        return Filter.newGreaterOrEqualFilter(attributeDescription, assertionValue);
    }
    // Decodes a less than or equal filter.
    private static Filter decodeLessOrEqualMatchFilter(final ASN1Reader reader) throws IOException {
        String attributeDescription;
        ByteString assertionValue;
        reader.readStartSequence(TYPE_FILTER_LESS_OR_EQUAL);
        try {
            attributeDescription = reader.readOctetStringAsString();
            assertionValue = reader.readOctetString();
        } finally {
            reader.readEndSequence();
        }
        if (finalSubstring != null)
        {
          writer.writeOctetString(TYPE_SUBFINAL, finalSubstring);
        return Filter.newLessOrEqualFilter(attributeDescription, assertionValue);
    }
    // Decodes a not filter.
    private static Filter decodeNotFilter(final ASN1Reader reader) throws IOException {
        Filter subFilter;
        reader.readStartSequence(TYPE_FILTER_NOT);
        try {
            subFilter = decodeFilter(reader);
        } finally {
            reader.readEndSequence();
        }
        writer.writeEndSequence();
        writer.writeEndSequence();
        return null;
      }
      catch (final IOException e)
      {
        return e;
      }
        return Filter.newNotFilter(subFilter);
    }
    // Decodes an or filter.
    private static Filter decodeOrFilter(final ASN1Reader reader) throws IOException {
        Filter filter;
    public IOException visitUnrecognizedFilter(final ASN1Writer writer,
        final byte filterTag, final ByteString filterBytes)
    {
      try
      {
        writer.writeOctetString(filterTag, filterBytes);
        return null;
      }
      catch (final IOException e)
      {
        return e;
      }
    }
  };
  /**
   * Reads the next ASN.1 element from the provided {@code ASN1Reader} as a
   * {@code Filter}.
   *
   * @param reader
   *          The {@code ASN1Reader} from which the ASN.1 encoded {@code Filter}
   *          should be read.
   * @return The decoded {@code Filter}.
   * @throws IOException
   *           If an error occurs while reading from {@code reader}.
   */
  public static Filter decodeFilter(final ASN1Reader reader) throws IOException
  {
    final byte type = reader.peekType();
    switch (type)
    {
    case TYPE_FILTER_AND:
      return decodeAndFilter(reader);
    case TYPE_FILTER_OR:
      return decodeOrFilter(reader);
    case TYPE_FILTER_NOT:
      return decodeNotFilter(reader);
    case TYPE_FILTER_EQUALITY:
      return decodeEqualityMatchFilter(reader);
    case TYPE_FILTER_GREATER_OR_EQUAL:
      return decodeGreaterOrEqualMatchFilter(reader);
    case TYPE_FILTER_LESS_OR_EQUAL:
      return decodeLessOrEqualMatchFilter(reader);
    case TYPE_FILTER_APPROXIMATE:
      return decodeApproxMatchFilter(reader);
    case TYPE_FILTER_SUBSTRING:
      return decodeSubstringsFilter(reader);
    case TYPE_FILTER_PRESENCE:
      return Filter.newPresentFilter(reader.readOctetStringAsString(type));
    case TYPE_FILTER_EXTENSIBLE_MATCH:
      return decodeExtensibleMatchFilter(reader);
    default:
      return Filter.newUnrecognizedFilter(type, reader.readOctetString(type));
    }
  }
  /**
   * Reads the next ASN.1 element from the provided {@code ASN1Reader} as a
   * {@code SearchResultEntry}.
   *
   * @param reader
   *          The {@code ASN1Reader} from which the ASN.1 encoded {@code
   *          SearchResultEntry} should be read.
   * @param options
   *          The decode options to use when decoding the entry.
   * @return The decoded {@code SearchResultEntry}.
   * @throws IOException
   *           If an error occurs while reading from {@code reader}.
   */
  public static SearchResultEntry decodeSearchResultEntry(
      final ASN1Reader reader, final DecodeOptions options) throws IOException
  {
    return LDAPReader.decodeEntry(reader, options);
  }
  /**
   * Writes the ASN.1 encoding of the provided {@code Filter} to the provided
   * {@code ASN1Writer}.
   *
   * @param writer
   *          The {@code ASN1Writer} to which the ASN.1 encoding of the provided
   *          {@code Filter} should be written.
   * @param filter
   *          The filter to be encoded.
   * @return The updated {@code ASN1Writer}.
   * @throws IOException
   *           If an error occurs while writing to {@code writer}.
   */
  public static ASN1Writer encodeFilter(final ASN1Writer writer,
      final Filter filter) throws IOException
  {
    final IOException e = filter.accept(ASN1_ENCODER, writer);
    if (e != null)
    {
      throw e;
    }
    else
    {
      return writer;
    }
  }
  /**
   * Writes the ASN.1 encoding of the provided {@code SearchResultEntry} to the
   * provided {@code ASN1Writer}.
   *
   * @param writer
   *          The {@code ASN1Writer} to which the ASN.1 encoding of the provided
   *          {@code SearchResultEntry} should be written.
   * @param entry
   *          The Search Result Entry to be encoded.
   * @return The updated {@code ASN1Writer}.
   * @throws IOException
   *           If an error occurs while writing to {@code writer}.
   */
  public static ASN1Writer encodeSearchResultEntry(final ASN1Writer writer,
      final SearchResultEntry entry) throws IOException
  {
    // FIXME: this should include Controls.
    LDAPWriter.encodeEntry(writer, entry);
    return writer;
  }
  // Decodes an and filter.
  private static Filter decodeAndFilter(final ASN1Reader reader)
      throws IOException
  {
    Filter filter;
    reader.readStartSequence(TYPE_FILTER_AND);
    try
    {
      if (reader.hasNextElement())
      {
        final List<Filter> subFilters = new LinkedList<Filter>();
        do
        {
          subFilters.add(decodeFilter(reader));
        reader.readStartSequence(TYPE_FILTER_OR);
        try {
            if (reader.hasNextElement()) {
                final List<Filter> subFilters = new LinkedList<Filter>();
                do {
                    subFilters.add(decodeFilter(reader));
                } while (reader.hasNextElement());
                filter = Filter.newOrFilter(subFilters);
            } else {
                // No sub-filters - this is an RFC 4526 absolute false filter.
                filter = Filter.getAbsoluteFalseFilter();
            }
        } finally {
            reader.readEndSequence();
        }
        while (reader.hasNextElement());
        filter = Filter.newAndFilter(subFilters);
      }
      else
      {
        // No sub-filters - this is an RFC 4526 absolute true filter.
        filter = Filter.getAbsoluteTrueFilter();
      }
    }
    finally
    {
      reader.readEndSequence();
        return filter;
    }
    return filter;
  }
    // Decodes a sub-strings filter.
    private static Filter decodeSubstringsFilter(final ASN1Reader reader) throws IOException {
        ByteString initialSubstring = null;
        List<ByteString> anySubstrings = null;
        ByteString finalSubstring = null;
        String attributeDescription;
  // Decodes an approximate match filter.
  private static Filter decodeApproxMatchFilter(final ASN1Reader reader)
      throws IOException
  {
    String attributeDescription;
    ByteString assertionValue;
    reader.readStartSequence(TYPE_FILTER_APPROXIMATE);
    try
    {
      attributeDescription = reader.readOctetStringAsString();
      assertionValue = reader.readOctetString();
    }
    finally
    {
      reader.readEndSequence();
    }
    return Filter.newApproxMatchFilter(attributeDescription, assertionValue);
  }
  // Decodes an equality match filter.
  private static Filter decodeEqualityMatchFilter(final ASN1Reader reader)
      throws IOException
  {
    String attributeDescription;
    ByteString assertionValue;
    reader.readStartSequence(TYPE_FILTER_EQUALITY);
    try
    {
      attributeDescription = reader.readOctetStringAsString();
      assertionValue = reader.readOctetString();
    }
    finally
    {
      reader.readEndSequence();
    }
    return Filter.newEqualityMatchFilter(attributeDescription, assertionValue);
  }
  // Decodes an extensible match filter.
  private static Filter decodeExtensibleMatchFilter(final ASN1Reader reader)
      throws IOException
  {
    String matchingRule;
    String attributeDescription;
    boolean dnAttributes;
    ByteString assertionValue;
    reader.readStartSequence(TYPE_FILTER_EXTENSIBLE_MATCH);
    try
    {
      matchingRule = null;
      if (reader.peekType() == TYPE_MATCHING_RULE_ID)
      {
        matchingRule = reader.readOctetStringAsString(TYPE_MATCHING_RULE_ID);
      }
      attributeDescription = null;
      if (reader.peekType() == TYPE_MATCHING_RULE_TYPE)
      {
        attributeDescription = reader
            .readOctetStringAsString(TYPE_MATCHING_RULE_TYPE);
      }
      dnAttributes = false;
      if (reader.hasNextElement()
          && (reader.peekType() == TYPE_MATCHING_RULE_DN_ATTRIBUTES))
      {
        dnAttributes = reader.readBoolean();
      }
      assertionValue = reader.readOctetString(TYPE_MATCHING_RULE_VALUE);
    }
    finally
    {
      reader.readEndSequence();
    }
    return Filter.newExtensibleMatchFilter(matchingRule, attributeDescription,
        assertionValue, dnAttributes);
  }
  // Decodes a greater than or equal filter.
  private static Filter decodeGreaterOrEqualMatchFilter(final ASN1Reader reader)
      throws IOException
  {
    String attributeDescription;
    ByteString assertionValue;
    reader.readStartSequence(TYPE_FILTER_GREATER_OR_EQUAL);
    try
    {
      attributeDescription = reader.readOctetStringAsString();
      assertionValue = reader.readOctetString();
    }
    finally
    {
      reader.readEndSequence();
    }
    return Filter.newGreaterOrEqualFilter(attributeDescription, assertionValue);
  }
  // Decodes a less than or equal filter.
  private static Filter decodeLessOrEqualMatchFilter(final ASN1Reader reader)
      throws IOException
  {
    String attributeDescription;
    ByteString assertionValue;
    reader.readStartSequence(TYPE_FILTER_LESS_OR_EQUAL);
    try
    {
      attributeDescription = reader.readOctetStringAsString();
      assertionValue = reader.readOctetString();
    }
    finally
    {
      reader.readEndSequence();
    }
    return Filter.newLessOrEqualFilter(attributeDescription, assertionValue);
  }
  // Decodes a not filter.
  private static Filter decodeNotFilter(final ASN1Reader reader)
      throws IOException
  {
    Filter subFilter;
    reader.readStartSequence(TYPE_FILTER_NOT);
    try
    {
      subFilter = decodeFilter(reader);
    }
    finally
    {
      reader.readEndSequence();
    }
    return Filter.newNotFilter(subFilter);
  }
  // Decodes an or filter.
  private static Filter decodeOrFilter(final ASN1Reader reader)
      throws IOException
  {
    Filter filter;
    reader.readStartSequence(TYPE_FILTER_OR);
    try
    {
      if (reader.hasNextElement())
      {
        final List<Filter> subFilters = new LinkedList<Filter>();
        do
        {
          subFilters.add(decodeFilter(reader));
        reader.readStartSequence(TYPE_FILTER_SUBSTRING);
        try {
            attributeDescription = reader.readOctetStringAsString();
            reader.readStartSequence();
            try {
                // FIXME: There should be at least one element in this substring
                // filter sequence.
                if (reader.peekType() == TYPE_SUBINITIAL) {
                    initialSubstring = reader.readOctetString(TYPE_SUBINITIAL);
                }
                if (reader.hasNextElement() && (reader.peekType() == TYPE_SUBANY)) {
                    anySubstrings = new LinkedList<ByteString>();
                    do {
                        anySubstrings.add(reader.readOctetString(TYPE_SUBANY));
                    } while (reader.hasNextElement() && (reader.peekType() == TYPE_SUBANY));
                }
                if (reader.hasNextElement() && (reader.peekType() == TYPE_SUBFINAL)) {
                    finalSubstring = reader.readOctetString(TYPE_SUBFINAL);
                }
            } finally {
                reader.readEndSequence();
            }
        } finally {
            reader.readEndSequence();
        }
        while (reader.hasNextElement());
        filter = Filter.newOrFilter(subFilters);
      }
      else
      {
        // No sub-filters - this is an RFC 4526 absolute false filter.
        filter = Filter.getAbsoluteFalseFilter();
      }
    }
    finally
    {
      reader.readEndSequence();
    }
    return filter;
  }
  // Decodes a sub-strings filter.
  private static Filter decodeSubstringsFilter(final ASN1Reader reader)
      throws IOException
  {
    ByteString initialSubstring = null;
    List<ByteString> anySubstrings = null;
    ByteString finalSubstring = null;
    String attributeDescription;
    reader.readStartSequence(TYPE_FILTER_SUBSTRING);
    try
    {
      attributeDescription = reader.readOctetStringAsString();
      reader.readStartSequence();
      try
      {
        // FIXME: There should be at least one element in this substring
        // filter sequence.
        if (reader.peekType() == TYPE_SUBINITIAL)
        {
          initialSubstring = reader.readOctetString(TYPE_SUBINITIAL);
        if (anySubstrings == null) {
            anySubstrings = Collections.emptyList();
        }
        if (reader.hasNextElement() && (reader.peekType() == TYPE_SUBANY))
        {
          anySubstrings = new LinkedList<ByteString>();
          do
          {
            anySubstrings.add(reader.readOctetString(TYPE_SUBANY));
          }
          while (reader.hasNextElement() && (reader.peekType() == TYPE_SUBANY));
        }
        if (reader.hasNextElement() && (reader.peekType() == TYPE_SUBFINAL))
        {
          finalSubstring = reader.readOctetString(TYPE_SUBFINAL);
        }
      }
      finally
      {
        reader.readEndSequence();
      }
    }
    finally
    {
      reader.readEndSequence();
        return Filter.newSubstringsFilter(attributeDescription, initialSubstring, anySubstrings,
                finalSubstring);
    }
    if (anySubstrings == null)
    {
      anySubstrings = Collections.emptyList();
    /**
     * Prevent instantiation.
     */
    private LDAPUtils() {
        // Nothing to do.
    }
    return Filter.newSubstringsFilter(attributeDescription, initialSubstring,
        anySubstrings, finalSubstring);
  }
  /**
   * Prevent instantiation.
   */
  private LDAPUtils()
  {
    // Nothing to do.
  }
}
Diff truncated after the above file
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPWriter.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/TimeoutChecker.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/UnexpectedRequestException.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/UnexpectedResponseException.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/UnsupportedMessageException.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/controls/AccountUsabilityRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/controls/AccountUsabilityResponseControl.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/controls/RealAttributesOnlyRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/controls/VirtualAttributesOnlyRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/controls/package-info.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/extensions/GetConnectionIDExtendedRequest.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/extensions/GetConnectionIDExtendedResult.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/extensions/GetSymmetricKeyExtendedRequest.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/extensions/PasswordPolicyStateExtendedRequest.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/extensions/PasswordPolicyStateExtendedResult.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/extensions/PasswordPolicyStateOperation.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/extensions/PasswordPolicyStateOperationContainer.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/extensions/PasswordPolicyStateOperationType.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/extensions/package-info.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/package-info.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/ASCIICharProp.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/AsynchronousFutureResult.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/Base64.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/ByteSequenceOutputStream.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/Collections2.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/CompletedFutureResult.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/ConnectionDecorator.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/Function.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/Functions.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/FutureResultTransformer.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/Iterables.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/Iterators.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/Predicate.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/RecursiveFutureResult.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/SizeLimitInputStream.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/StaticUtils.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/StringPrepProfile.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/SubstringReader.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/Validator.java opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/package-info.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/ASN1.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/ASN1ByteSequenceReader.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/ASN1Constants.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/ASN1InputStreamReader.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/ASN1OutputStreamWriter.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/ASN1Reader.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/ASN1Writer.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/AbstractASN1Reader.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/AbstractASN1Writer.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/package-info.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AVA.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnection.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractAttribute.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractConnection.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractEntry.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractFilterVisitor.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractMapEntry.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Assertion.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AssertionFailureException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Attribute.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AttributeDescription.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AttributeFactory.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Attributes.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AuthenticatedConnectionFactory.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AuthenticationException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AuthorizationException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ByteSequence.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ByteSequenceReader.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ByteString.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ByteStringBuilder.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/CancelRequestListener.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/CancelledResultException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ConditionResult.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Connection.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ConnectionEventListener.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ConnectionException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ConnectionFactory.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ConnectionPool.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ConnectionSecurityLayer.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Connections.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ConstraintViolationException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/DN.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/DecodeException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/DecodeOptions.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/DereferenceAliasesPolicy.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Entries.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Entry.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/EntryFactory.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/EntryNotFoundException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ErrorResultException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ErrorResultIOException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/FailoverLoadBalancingAlgorithm.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Filter.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/FilterVisitor.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/FixedConnectionPool.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/FutureResult.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactory.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/IntermediateResponseHandler.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/InternalConnectionFactory.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/KeyManagers.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/LDAPClientContext.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/LDAPConnectionFactory.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/LDAPListener.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/LDAPListenerOptions.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/LDAPOptions.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/LDAPUrl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/LinkedAttribute.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/LinkedHashMapEntry.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/LoadBalancer.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/LoadBalancingAlgorithm.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Matcher.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Modification.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ModificationType.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/MultipleEntriesFoundException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RDN.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ReferralException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestContext.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestHandler.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactory.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactoryAdapter.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ResultCode.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ResultHandler.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RootDSE.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RoundRobinLoadBalancingAlgorithm.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/SSLContextBuilder.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/SchemaResolver.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/SearchResultHandler.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/SearchResultReferenceIOException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/SearchScope.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ServerConnection.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ServerConnectionFactory.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/SortKey.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/TimeoutResultException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/TreeMapEntry.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/TrustManagers.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/AssertionRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/AuthorizationIdentityRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/AuthorizationIdentityResponseControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/Control.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/ControlDecoder.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/EntryChangeNotificationResponseControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/GenericControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/GetEffectiveRightsRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/ManageDsaITRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/MatchedValuesRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/PasswordExpiredResponseControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/PasswordExpiringResponseControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/PasswordPolicyErrorType.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/PasswordPolicyRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/PasswordPolicyResponseControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/PasswordPolicyWarningType.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/PermissiveModifyRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/PersistentSearchChangeType.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/PersistentSearchRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/PostReadRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/PostReadResponseControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/PreReadRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/PreReadResponseControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/ProxiedAuthV1RequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/ProxiedAuthV2RequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/ServerSideSortRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/ServerSideSortResponseControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/SimplePagedResultsControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/SubentriesRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/SubtreeDeleteRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/VirtualListViewRequestControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/VirtualListViewResponseControl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/controls/package-info.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/package-info.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/AbandonRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/AbandonRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/AbstractBindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/AbstractExtendedRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/AbstractRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/AbstractSASLBindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/AbstractUnmodifiableBindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/AbstractUnmodifiableExtendedRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/AbstractUnmodifiableRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/AbstractUnmodifiableSASLBindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/AddRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/AddRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/AnonymousSASLBindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/AnonymousSASLBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/BindClient.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/BindClientImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/BindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/CancelExtendedRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/CancelExtendedRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/CompareRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/CompareRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/DeleteRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/DeleteRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/ExtendedRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/ExtendedRequestDecoder.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/GenericBindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/GenericBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/GenericExtendedRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/GenericExtendedRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/ModifyDNRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/ModifyDNRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/ModifyRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/ModifyRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/PasswordModifyExtendedRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/PasswordModifyExtendedRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/Request.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/Requests.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/SASLBindClientImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/SASLBindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/SearchRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/SearchRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/SimpleBindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/SimpleBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/StartTLSExtendedRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/StartTLSExtendedRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnbindRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnbindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableAbandonRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableAddRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableAnonymousSASLBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableCRAMMD5SASLBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableCancelExtendedRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableCompareRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableDeleteRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableDigestMD5SASLBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableExternalSASLBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableGSSAPISASLBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableGenericBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableGenericExtendedRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableModifyDNRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableModifyRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiablePasswordModifyExtendedRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiablePlainSASLBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableSearchRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableSimpleBindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableStartTLSExtendedRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableUnbindRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/UnmodifiableWhoAmIExtendedRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/WhoAmIExtendedRequest.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/WhoAmIExtendedRequestImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/package-info.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/AbstractExtendedResult.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/AbstractExtendedResultDecoder.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/AbstractIntermediateResponse.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/AbstractResponseImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/AbstractResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/AbstractUnmodifiableExtendedResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/AbstractUnmodifiableIntermediateResponseImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/AbstractUnmodifiableResponseImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/AbstractUnmodifiableResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/BindResult.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/BindResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/CompareResult.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/CompareResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/ExtendedResult.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/ExtendedResultDecoder.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/GenericExtendedResult.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/GenericExtendedResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/GenericIntermediateResponse.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/GenericIntermediateResponseImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/IntermediateResponse.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/PasswordModifyExtendedResult.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/PasswordModifyExtendedResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/Response.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/Responses.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/Result.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/ResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/SearchResultEntry.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/SearchResultEntryImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/SearchResultReference.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/SearchResultReferenceImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/UnmodifiableBindResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/UnmodifiableCompareResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/UnmodifiableGenericExtendedResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/UnmodifiableGenericIntermediateResponseImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/UnmodifiablePasswordModifyExtendedResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/UnmodifiableResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/UnmodifiableSearchResultEntryImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/UnmodifiableSearchResultReferenceImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/UnmodifiableWhoAmIExtendedResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/WhoAmIExtendedResult.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/WhoAmIExtendedResultImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/package-info.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/AbstractMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/AbstractOrderingMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/AbstractSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/AttributeType.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/AttributeTypeSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/AttributeUsage.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/AuthPasswordExactEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/AuthPasswordSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/BinarySyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/BitStringEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/BitStringSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/BooleanEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/BooleanSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactIA5EqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactIA5SubstringMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactOrderingMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactSubstringMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreIA5EqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreIA5SubstringMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreListEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreListSubstringMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreOrderingMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreSubstringMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CertificateListSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CertificatePairSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CertificateSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/ConflictingSchemaElementException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CoreSchema.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CoreSchemaImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/CountryStringSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRule.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRuleSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRule.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRuleSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/DefaultSchema.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/DeliveryMethodSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/DirectoryStringFirstComponentEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/DirectoryStringSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/DistinguishedNameSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/DoubleMetaphoneApproximateMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/EnhancedGuideSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/EnumOrderingMatchingRule.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/EnumSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/EqualLengthApproximateMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/FacsimileNumberSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/FaxSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/GeneralizedTimeEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/GeneralizedTimeOrderingMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/GeneralizedTimeSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/GenerateCoreSchema.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/GuideSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/IA5StringSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/IntegerEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/IntegerFirstComponentEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/IntegerOrderingMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/IntegerSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/JPEGSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/KeywordEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/LDAPSyntaxDescriptionSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRule.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUse.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUseSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/NameAndOptionalUIDSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/NameForm.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/NameFormSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/NumericStringEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/NumericStringOrderingMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/NumericStringSubstringMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/NumericStringSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/OIDSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClass.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClassSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClassType.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierFirstComponentEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/OctetStringEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/OctetStringOrderingMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/OctetStringSubstringMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/OctetStringSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/OtherMailboxSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/PostalAddressSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/PresentationAddressEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/PresentationAddressSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/PrintableStringSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/ProtocolInformationEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/ProtocolInformationSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/RegexSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SchemaConstants.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElement.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SchemaException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SchemaUtils.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SchemaValidationPolicy.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SubstringAssertionSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SupportedAlgorithmSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/Syntax.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/TelephoneNumberEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/TelephoneNumberSubstringMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/TelephoneNumberSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/TeletexTerminalIdentifierSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/TelexNumberSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/UTCTimeSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/UUIDEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/UUIDOrderingMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/UUIDSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/UniqueMemberEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/UnknownSchemaElementException.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/UserPasswordExactEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/UserPasswordSyntaxImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/WordEqualityMatchingRuleImpl.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/package-info.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/AbstractLDIFReader.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/AbstractLDIFStream.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/AbstractLDIFWriter.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/ChangeRecord.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/ChangeRecordReader.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/ChangeRecordVisitor.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/ChangeRecordVisitorWriter.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/ChangeRecordWriter.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/ConnectionChangeRecordWriter.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/ConnectionEntryReader.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/ConnectionEntryWriter.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/EntryReader.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/EntryWriter.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFChangeRecordReader.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFChangeRecordWriter.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFEntryReader.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFEntryWriter.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedChangeRecordListener.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedLDIFListener.java opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/package-info.java opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core.properties opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core_de.properties opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core_es.properties opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core_fr.properties opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core_ja.properties opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core_ko.properties opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core_zh_CN.properties opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core_zh_TW.properties opendj3/opendj-ldap-sdk/src/test/java/com/forgerock/opendj/ldap/ASN1BufferReaderTestCase.java opendj3/opendj-ldap-sdk/src/test/java/com/forgerock/opendj/ldap/ASN1BufferWriterTestCase.java opendj3/opendj-ldap-sdk/src/test/java/com/forgerock/opendj/ldap/DefaultTCPNIOTransportTestCase.java opendj3/opendj-ldap-sdk/src/test/java/com/forgerock/opendj/ldap/LDAPTestCase.java opendj3/opendj-ldap-sdk/src/test/java/com/forgerock/opendj/ldap/controls/AccountUsabilityRequestControlTestCase.java opendj3/opendj-ldap-sdk/src/test/java/com/forgerock/opendj/ldap/controls/AccountUsabilityResponseControlTestCase.java opendj3/opendj-ldap-sdk/src/test/java/com/forgerock/opendj/util/ASCIICharPropTestCase.java opendj3/opendj-ldap-sdk/src/test/java/com/forgerock/opendj/util/Base64TestCase.java opendj3/opendj-ldap-sdk/src/test/java/com/forgerock/opendj/util/StaticUtilsTestCase.java opendj3/opendj-ldap-sdk/src/test/java/com/forgerock/opendj/util/StringPrepProfileTestCase.java opendj3/opendj-ldap-sdk/src/test/java/com/forgerock/opendj/util/UtilTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/asn1/ASN1ByteSequenceReaderTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/asn1/ASN1InputStreamReaderTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/asn1/ASN1OutputStreamWriterTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/asn1/ASN1ReaderTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/asn1/ASN1WriterTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnectionTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/AttributeDescriptionTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/ByteSequenceTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/ByteStringBuilderTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/ByteStringTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/ConnectionFactoryTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/DNTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/EntriesTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/EntryTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/FilterTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/LDAPListenerTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/LDAPUrlTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/LinkedAttributeTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/RDNTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/SdkTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/TestCaseUtils.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/TypesTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/controls/ControlsTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/AbandonRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/AddRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/AnonymousSASLBindRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/BindRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/CompareRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/DeleteRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/ExtendedRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/GenericBindRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/ModifyDNRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/ModifyRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/RequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/RequestsTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/SimpleBindRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/requests/UnbindRequestTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/responses/ResponsesTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/AbstractSchemaElementTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/ApproximateMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/AttributeTypeSyntaxTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/AttributeTypeTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/BitStringEqualityMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/BitStringSyntaxTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/BooleanEqualityMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/CaseExactEqualityMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/CaseExactIA5EqualityMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/CaseExactIA5SubstringMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/CaseExactOrderingMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/CaseExactSubstringMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/CaseIgnoreEqualityMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/CaseIgnoreIA5EqualityMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/CaseIgnoreIA5SubstringMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/CaseIgnoreOrderingMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/CaseIgnoreSubstringMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/CoreSchemaTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/DITContentRuleSyntaxTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/EntrySchemaCheckingTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/EnumSyntaxTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/GeneralizedTimeSyntaxTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/GuideSyntaxTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/IA5StringSyntaxTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/LDAPSyntaxTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/MatchingRuleSyntaxTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/MatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/MatchingRuleUseSyntaxTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/OrderingMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/OtherMailboxSyntaxTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/RegexSyntaxTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/SchemaCompatTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/SchemaTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/SchemaUtilsTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/SubstitutionSyntaxTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/SubstringMatchingRuleTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/SyntaxTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/TelexSyntaxTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/UTCTimeSyntaxTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/UUIDSyntaxTest.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFChangeRecordReaderTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFEntryReaderTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFEntryWriterTestCase.java opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFTestCase.java opendj3/opendj-ldap-sync/pom.xml opendj3/opendj-ldap-sync/src/main/assembly/bat/ldapsync.bat opendj3/opendj-ldap-sync/src/main/assembly/descriptor.xml opendj3/opendj-ldap-sync/src/main/assembly/libbat/_client-script.bat opendj3/opendj-ldap-sync/src/main/assembly/libbat/_script-util.bat opendj3/opendj-ldap-sync/src/main/assembly/libbat/setcp.bat opendj3/opendj-ldap-sync/src/main/assembly/libbin/_client-script.sh opendj3/opendj-ldap-sync/src/main/assembly/libbin/_script-util.sh opendj3/opendj-ldap-sync/src/main/java/org/forgerock/opendj/sync/Main.java opendj3/opendj-ldap-sync/src/main/java/org/forgerock/opendj/sync/filters/ChangeRecordContext.java opendj3/opendj-ldap-sync/src/main/java/org/forgerock/opendj/sync/filters/Filter.java opendj3/opendj-ldap-sync/src/main/java/org/forgerock/opendj/sync/filters/FilterResult.java opendj3/opendj-ldap-sync/src/main/java/org/forgerock/opendj/sync/filters/Matchers.java opendj3/opendj-ldap-sync/src/main/java/org/forgerock/opendj/sync/filters/Transforms.java opendj3/opendj-ldap-sync/src/main/java/org/forgerock/opendj/sync/filters/package-info.java opendj3/opendj-ldap-sync/src/main/java/org/forgerock/opendj/sync/package-info.java opendj3/opendj-ldap-toolkit/pom.xml opendj3/opendj-ldap-toolkit/src/main/assembly/bat/authrate.bat opendj3/opendj-ldap-toolkit/src/main/assembly/bat/ldapcompare.bat opendj3/opendj-ldap-toolkit/src/main/assembly/bat/ldapmodify.bat opendj3/opendj-ldap-toolkit/src/main/assembly/bat/ldappasswordmodify.bat opendj3/opendj-ldap-toolkit/src/main/assembly/bat/ldapsearch.bat opendj3/opendj-ldap-toolkit/src/main/assembly/bat/ldifdiff.bat opendj3/opendj-ldap-toolkit/src/main/assembly/bat/ldifmodify.bat opendj3/opendj-ldap-toolkit/src/main/assembly/bat/ldifsearch.bat opendj3/opendj-ldap-toolkit/src/main/assembly/bat/modrate.bat opendj3/opendj-ldap-toolkit/src/main/assembly/bat/searchrate.bat opendj3/opendj-ldap-toolkit/src/main/assembly/descriptor.xml opendj3/opendj-ldap-toolkit/src/main/assembly/libbat/_client-script.bat opendj3/opendj-ldap-toolkit/src/main/assembly/libbat/_script-util.bat opendj3/opendj-ldap-toolkit/src/main/assembly/libbat/setcp.bat opendj3/opendj-ldap-toolkit/src/main/assembly/libbin/_client-script.sh opendj3/opendj-ldap-toolkit/src/main/assembly/libbin/_script-util.sh opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ApplicationKeyManager.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/Argument.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ArgumentException.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ArgumentGroup.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ArgumentParser.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthenticatedConnectionFactory.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/BooleanArgument.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/CLIException.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ConnectionFactoryProvider.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ConsoleApplication.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/DataSource.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/FileBasedArgument.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/IntegerArgument.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPCompare.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPModify.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPPasswordModify.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFDiff.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFModify.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFSearch.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MultiChoiceArgument.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MultiColumnPrinter.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PromptingTrustManager.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/StringArgument.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ToolConstants.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/Utils.java opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/package-info.java opendj3/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties opendj3/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools_de.properties opendj3/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools_es.properties opendj3/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools_fr.properties opendj3/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools_ja.properties opendj3/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools_ko.properties opendj3/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools_zh_CN.properties opendj3/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools_zh_TW.properties opendj3/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/ToolsTestCase.java opendj3/opendj-rest2ldap/pom.xml opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompletionHandler.java opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPResource.java opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReadRequest.java opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/RestRequest.java opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Validator.java opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/package-info.java opendj3/pom.xml opendj3/src/site/resources/Example.ldif