From 22094368c2865dcfb6daf8366425212b721a4657 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Thu, 05 Feb 2009 17:42:14 +0000
Subject: [PATCH] Merge ASN1 branch to trunk

---
 opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java | 2814 +++++++++++++++++++++++++----------------------------------
 1 files changed, 1,200 insertions(+), 1,614 deletions(-)

diff --git a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
index df57bde..fa7b7aa 100644
--- a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
+++ b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -22,17 +22,25 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Copyright 2006-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.protocols.ldap;
 
 
 
+import static org.opends.messages.ProtocolMessages.*;
+import static org.opends.server.loggers.AccessLogger.*;
+import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.protocols.ldap.LDAPConstants.*;
+import static org.opends.server.types.OperationType.*;
+import static org.opends.server.util.StaticUtils.*;
+
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.nio.channels.Selector;
 import java.nio.channels.SocketChannel;
-import java.util.ArrayList;
+import java.security.cert.Certificate;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
@@ -44,7 +52,6 @@
 import org.opends.messages.MessageBuilder;
 import org.opends.server.api.ClientConnection;
 import org.opends.server.api.ConnectionHandler;
-import org.opends.server.api.ConnectionSecurityProvider;
 import org.opends.server.core.AbandonOperationBasis;
 import org.opends.server.core.AddOperationBasis;
 import org.opends.server.core.BindOperationBasis;
@@ -60,15 +67,18 @@
 import org.opends.server.core.SearchOperationBasis;
 import org.opends.server.core.UnbindOperationBasis;
 import org.opends.server.core.networkgroups.NetworkGroup;
-import org.opends.server.extensions.NullConnectionSecurityProvider;
+import org.opends.server.extensions.ConnectionSecurityProvider;
+import org.opends.server.extensions.RedirectingByteChannel;
+import org.opends.server.extensions.TLSByteChannel;
 import org.opends.server.extensions.TLSCapableConnection;
-import org.opends.server.extensions.TLSConnectionSecurityProvider;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.monitors.OperationMonitor;
-import org.opends.server.protocols.asn1.ASN1Element;
-import org.opends.server.protocols.asn1.ASN1OctetString;
-import org.opends.server.protocols.asn1.ASN1Sequence;
+import org.opends.server.protocols.asn1.ASN1;
+import org.opends.server.protocols.asn1.ASN1ByteChannelReader;
+import org.opends.server.protocols.asn1.ASN1Writer;
 import org.opends.server.types.AbstractOperation;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ByteStringBuilder;
 import org.opends.server.types.CancelRequest;
 import org.opends.server.types.CancelResult;
 import org.opends.server.types.Control;
@@ -83,151 +93,113 @@
 import org.opends.server.types.SearchResultReference;
 import org.opends.server.util.TimeThread;
 
-import static org.opends.messages.ProtocolMessages.*;
-import static org.opends.server.loggers.AccessLogger.logDisconnect;
-import static org.opends.server.loggers.ErrorLogger.logError;
-import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
-import static org.opends.server.loggers.debug.DebugLogger.getTracer;
-import static org.opends.server.protocols.ldap.LDAPConstants.*;
-import static org.opends.server.util.StaticUtils.getExceptionMessage;
-import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
-import static org.opends.server.types.OperationType.*;
-
 
 
 /**
- * This class defines an LDAP client connection, which is a type of client
- * connection that will be accepted by an instance of the LDAP connection
- * handler and have its requests decoded by an LDAP request handler.
+ * This class defines an LDAP client connection, which is a type of
+ * client connection that will be accepted by an instance of the LDAP
+ * connection handler and have its requests decoded by an LDAP request
+ * handler.
  */
-public class LDAPClientConnection
-       extends ClientConnection
-       implements TLSCapableConnection
+
+public class LDAPClientConnection extends ClientConnection implements
+    TLSCapableConnection
 {
   /**
    * The tracer object for the debug logger.
    */
   private static final DebugTracer TRACER = getTracer();
 
-
-
   // The time that the last operation was completed.
-  private AtomicLong lastCompletionTime;
+  private final AtomicLong lastCompletionTime;
 
   // The next operation ID that should be used for this connection.
-  private AtomicLong nextOperationID;
+  private final AtomicLong nextOperationID;
 
   // The selector that may be used for write operations.
-  private AtomicReference<Selector> writeSelector;
+  private final AtomicReference<Selector> writeSelector;
 
-  // Indicates whether the Directory Server believes this connection to be
-  // valid and available for communication.
+  // Indicates whether the Directory Server believes this connection to
+  // be valid and available for communication.
   private boolean connectionValid;
 
-  // Indicates whether this connection is about to be closed.  This will be used
-  // to prevent accepting new requests while a disconnect is in progress.
+  // Indicates whether this connection is about to be closed. This will
+  // be used to prevent accepting new requests while a disconnect is in
+  // progress.
   private boolean disconnectRequested;
 
-  // Indicates whether the connection should keep statistics regarding the
-  // operations that it is performing.
-  private boolean keepStats;
-
-  // The BER type for the ASN.1 element that is in the process of being read.
-  private byte elementType;
-
-  // The encoded value for the ASN.1 element that is in the process of being
-  // read.
-  private byte[] elementValue;
+  // Indicates whether the connection should keep statistics regarding
+  // the operations that it is performing.
+  private final boolean keepStats;
 
   // The set of all operations currently in progress on this connection.
-  private ConcurrentHashMap<Integer,Operation> operationsInProgress;
+  private final ConcurrentHashMap<Integer, Operation> operationsInProgress;
 
   // The number of operations performed on this connection.
   // Used to compare with the resource limits of the network group.
   private long operationsPerformed;
 
   // Lock on the number of operations
-  private Object operationsPerformedLock;
-
-  // The connection security provider that was in use for the client connection
-  // before switching to a TLS-based provider.
-  private ConnectionSecurityProvider clearSecurityProvider;
-
-  // The connection security provider for this client connection.
-  private ConnectionSecurityProvider securityProvider;
+  private final Object operationsPerformedLock;
 
   // The port on the client from which this connection originated.
-  private int clientPort;
+  private final int clientPort;
 
-  // The number of bytes contained in the value for the ASN.1 element that is in
-  // the process of being read.
-  private int elementLength;
-
-  // The number of bytes in the multi-byte length that are still needed to fully
-  // decode the length of the ASN.1 element in process.
-  private int elementLengthBytesNeeded;
-
-  // The current state for the data read for the ASN.1 element in progress.
-  private int elementReadState;
-
-  // The number of bytes that have already been read for the ASN.1 element
-  // value in progress.
-  private int elementValueBytesRead;
-
-  // The number of bytes that are still needed to fully decode the value of the
-  // ASN.1 element in progress.
-  private int elementValueBytesNeeded;
-
-  // The LDAP version that the client is using to communicate with the server.
+  // The LDAP version that the client is using to communicate with the
+  // server.
   private int ldapVersion;
 
   // The port on the server to which this client has connected.
-  private int serverPort;
+  private final int serverPort;
 
-  // The reference to the connection handler that accepted this connection.
-  private LDAPConnectionHandler connectionHandler;
+  // The reference to the connection handler that accepted this
+  // connection.
+  private final LDAPConnectionHandler connectionHandler;
 
   // The reference to the request handler with which this connection is
   // associated.
-  private LDAPRequestHandler requestHandler;
+  private final LDAPRequestHandler requestHandler;
 
   // The statistics tracker associated with this client connection.
-  private LDAPStatistics statTracker;
+  private final LDAPStatistics statTracker;
 
   // The connectionHandler statistic tracker.
-  private LDAPStatistics parentTracker;
+  private final LDAPStatistics parentTracker;
 
   // The connection ID assigned to this connection.
-  private long connectionID;
+  private final long connectionID;
 
-  // The lock used to provide threadsafe access to the set of operations in
-  // progress.
-  private Object opsInProgressLock;
+  // The lock used to provide threadsafe access to the set of operations
+  // in progress.
+  private final Object opsInProgressLock;
 
-  // The lock used to provide threadsafe access when sending data to the client.
-  private Object transmitLock;
+  // The lock used to provide threadsafe access when sending data to the
+  // client.
+  private final Object transmitLock;
 
   // The socket channel with which this client connection is associated.
-  private SocketChannel clientChannel;
+  private final SocketChannel clientChannel;
 
   // The string representation of the address of the client.
-  private String clientAddress;
+  private final String clientAddress;
 
-  // The name of the protocol that the client is using to communicate with the
-  // server.
-  private String protocol;
+  // The name of the protocol that the client is using to communicate
+  // with the server.
+  private final String protocol;
 
-  // The string representation of the address of the server to which the client
-  // has connected.
-  private String serverAddress;
+  // The string representation of the address of the server to which the
+  // client has connected.
+  private final String serverAddress;
 
-  // The TLS connection security provider that may be used for this connection
-  // if StartTLS is requested.
-  private TLSConnectionSecurityProvider tlsSecurityProvider;
+  private final ThreadLocal<WriterBuffer> cachedBuffers;
 
-  //The SASL connection provider used if confidentiality/integrity is negotiated
-  //during a SASL bind (GSSAPI and DIGEST-MD5 only).
-  private ConnectionSecurityProvider saslSecurityProvider;
+  private ASN1ByteChannelReader asn1Reader;
+
+  private final RedirectingByteChannel saslChannel;
+  private final RedirectingByteChannel tlsChannel;
+  private ConnectionSecurityProvider activeProvider = null;
+  private ConnectionSecurityProvider tlsPendingProvider = null;
+  private ConnectionSecurityProvider saslPendingProvider = null;
 
   // Statistics for the processed operations
   private OperationMonitor addMonitor;
@@ -241,64 +213,67 @@
   private OperationMonitor moddnMonitor;
   private OperationMonitor unbindMonitor;
 
+
+
+  /**
+   * This class wraps the byte string buffer and ASN1 writer.
+   */
+  private static class WriterBuffer
+  {
+    ASN1Writer writer;
+    ByteStringBuilder buffer;
+  }
+
+
+
   /**
    * Creates a new LDAP client connection with the provided information.
    *
-   * @param  connectionHandler  The connection handler that accepted this
-   *                            connection.
-   * @param  clientChannel      The socket channel that may be used to
-   *                            communicate with the client.
+   * @param connectionHandler
+   *          The connection handler that accepted this connection.
+   * @param clientChannel
+   *          The socket channel that may be used to communicate with
+   *          the client.
    */
   public LDAPClientConnection(LDAPConnectionHandler connectionHandler,
-                              SocketChannel clientChannel)
+      SocketChannel clientChannel)
   {
     super();
 
-
-    this.connectionHandler     = connectionHandler;
-    if (connectionHandler.isAdminConnectionHandler()) {
+    this.connectionHandler = connectionHandler;
+    if (connectionHandler.isAdminConnectionHandler())
+    {
       setNetworkGroup(NetworkGroup.getAdminNetworkGroup());
     }
 
-    this.clientChannel         = clientChannel;
-    this.securityProvider      = null;
-    this.clearSecurityProvider = null;
-
+    this.clientChannel = clientChannel;
     opsInProgressLock = new Object();
-    transmitLock      = new Object();
-
-    elementReadState         = ELEMENT_READ_STATE_NEED_TYPE;
-    elementType              = 0x00;
-    elementLength            = 0;
-    elementLengthBytesNeeded = 0;
-    elementValue             = null;
-    elementValueBytesRead    = 0;
-    elementValueBytesNeeded  = 0;
-
-    ldapVersion          = 3;
-    requestHandler       = null;
-    lastCompletionTime   = new AtomicLong(TimeThread.getTime());
-    nextOperationID      = new AtomicLong(0);
-    connectionValid      = true;
-    disconnectRequested  = false;
-    operationsInProgress = new ConcurrentHashMap<Integer,Operation>();
+    transmitLock = new Object();
+    ldapVersion = 3;
+    requestHandler = null;
+    lastCompletionTime = new AtomicLong(TimeThread.getTime());
+    nextOperationID = new AtomicLong(0);
+    connectionValid = true;
+    disconnectRequested = false;
+    operationsInProgress = new ConcurrentHashMap<Integer, Operation>();
     operationsPerformed = 0;
     operationsPerformedLock = new Object();
-    keepStats            = connectionHandler.keepStats();
-    protocol             = "LDAP";
-    writeSelector        = new AtomicReference<Selector>();
-
-    clientAddress = clientChannel.socket().getInetAddress().getHostAddress();
-    clientPort    = clientChannel.socket().getPort();
-    serverAddress = clientChannel.socket().getLocalAddress().getHostAddress();
-    serverPort    = clientChannel.socket().getLocalPort();
-
+    keepStats = connectionHandler.keepStats();
+    protocol = "LDAP";
+    writeSelector = new AtomicReference<Selector>();
+    clientAddress =
+        clientChannel.socket().getInetAddress().getHostAddress();
+    clientPort = clientChannel.socket().getPort();
+    serverAddress =
+        clientChannel.socket().getLocalAddress().getHostAddress();
+    serverPort = clientChannel.socket().getLocalPort();
     parentTracker = connectionHandler.getStatTracker();
-    String         instanceName  = parentTracker.getMonitorInstanceName() +
-                                   " for " + toString();
+    String instanceName =
+        parentTracker.getMonitorInstanceName() + " for " + toString();
     this.initializeOperationMonitors();
-    statTracker = new LDAPStatistics(connectionHandler,
-            instanceName, parentTracker);
+    statTracker =
+        new LDAPStatistics(connectionHandler, instanceName,
+            parentTracker);
 
     if (keepStats)
     {
@@ -309,8 +284,16 @@
     if (connectionID < 0)
     {
       disconnect(DisconnectReason.ADMIN_LIMIT_EXCEEDED, true,
-                 ERR_LDAP_CONNHANDLER_REJECTED_BY_SERVER.get());
+          ERR_LDAP_CONNHANDLER_REJECTED_BY_SERVER.get());
     }
+    cachedBuffers = new ThreadLocal<WriterBuffer>();
+    tlsChannel =
+        RedirectingByteChannel.getRedirectingByteChannel(clientChannel);
+    saslChannel =
+        RedirectingByteChannel.getRedirectingByteChannel(tlsChannel);
+    this.asn1Reader =
+        ASN1.getReader(saslChannel, 4096, connectionHandler
+            .getMaxRequestSize());
   }
 
 
@@ -318,8 +301,9 @@
   /**
    * Retrieves the connection ID assigned to this connection.
    *
-   * @return  The connection ID assigned to this connection.
+   * @return The connection ID assigned to this connection.
    */
+  @Override
   public long getConnectionID()
   {
     return connectionID;
@@ -328,10 +312,13 @@
 
 
   /**
-   * Retrieves the connection handler that accepted this client connection.
+   * Retrieves the connection handler that accepted this client
+   * connection.
    *
-   * @return  The connection handler that accepted this client connection.
+   * @return The connection handler that accepted this client
+   *         connection.
    */
+  @Override
   public ConnectionHandler<?> getConnectionHandler()
   {
     return connectionHandler;
@@ -340,11 +327,12 @@
 
 
   /**
-   * Retrieves the request handler that will read requests for this client
-   * connection.
+   * Retrieves the request handler that will read requests for this
+   * client connection.
    *
-   * @return  The request handler that will read requests for this client
-   *          connection, or <CODE>null</CODE> if none has been assigned yet.
+   * @return The request handler that will read requests for this client
+   *         connection, or <CODE>null</CODE> if none has been assigned
+   *         yet.
    */
   public LDAPRequestHandler getRequestHandler()
   {
@@ -354,25 +342,11 @@
 
 
   /**
-   * Specifies the request handler that will read requests for this client
-   * connection.
+   * Retrieves the socket channel that can be used to communicate with
+   * the client.
    *
-   * @param  requestHandler  The request handler that will read requests for
-   *                         this client connection.
-   */
-  public void setRequestHandler(LDAPRequestHandler requestHandler)
-  {
-    this.requestHandler = requestHandler;
-  }
-
-
-
-  /**
-   * Retrieves the socket channel that can be used to communicate with the
-   * client.
-   *
-   * @return  The socket channel that can be used to communicate with the
-   *          client.
+   * @return The socket channel that can be used to communicate with the
+   *         client.
    */
   public SocketChannel getSocketChannel()
   {
@@ -382,12 +356,13 @@
 
 
   /**
-   * Retrieves the protocol that the client is using to communicate with the
-   * Directory Server.
+   * Retrieves the protocol that the client is using to communicate with
+   * the Directory Server.
    *
-   * @return  The protocol that the client is using to communicate with the
-   *          Directory Server.
+   * @return The protocol that the client is using to communicate with
+   *         the Directory Server.
    */
+  @Override
   public String getProtocol()
   {
     return protocol;
@@ -398,8 +373,9 @@
   /**
    * Retrieves a string representation of the address of the client.
    *
-   * @return  A string representation of the address of the client.
+   * @return A string representation of the address of the client.
    */
+  @Override
   public String getClientAddress()
   {
     return clientAddress;
@@ -410,8 +386,9 @@
   /**
    * Retrieves the port number for this connection on the client system.
    *
-   * @return  The port number for this connection on the client system.
+   * @return The port number for this connection on the client system.
    */
+  @Override
   public int getClientPort()
   {
     return clientPort;
@@ -420,12 +397,13 @@
 
 
   /**
-   * Retrieves a string representation of the address on the server to which the
-   * client connected.
+   * Retrieves a string representation of the address on the server to
+   * which the client connected.
    *
-   * @return  A string representation of the address on the server to which the
-   *          client connected.
+   * @return A string representation of the address on the server to
+   *         which the client connected.
    */
+  @Override
   public String getServerAddress()
   {
     return serverAddress;
@@ -436,8 +414,9 @@
   /**
    * Retrieves the port number for this connection on the server system.
    *
-   * @return  The port number for this connection on the server system.
+   * @return The port number for this connection on the server system.
    */
+  @Override
   public int getServerPort()
   {
     return serverPort;
@@ -446,13 +425,14 @@
 
 
   /**
-   * Retrieves the <CODE>java.net.InetAddress</CODE> associated with the remote
-   * client system.
+   * Retrieves the <CODE>java.net.InetAddress</CODE> associated with the
+   * remote client system.
    *
-   * @return  The <CODE>java.net.InetAddress</CODE> associated with the remote
-   *          client system.  It may be <CODE>null</CODE> if the client is not
-   *          connected over an IP-based connection.
+   * @return The <CODE>java.net.InetAddress</CODE> associated with the
+   *         remote client system. It may be <CODE>null</CODE> if the
+   *         client is not connected over an IP-based connection.
    */
+  @Override
   public InetAddress getRemoteAddress()
   {
     return clientChannel.socket().getInetAddress();
@@ -461,14 +441,15 @@
 
 
   /**
-   * Retrieves the <CODE>java.net.InetAddress</CODE> for the Directory Server
-   * system to which the client has established the connection.
+   * Retrieves the <CODE>java.net.InetAddress</CODE> for the Directory
+   * Server system to which the client has established the connection.
    *
-   * @return  The <CODE>java.net.InetAddress</CODE> for the Directory Server
-   *          system to which the client has established the connection.  It may
-   *          be <CODE>null</CODE> if the client is not connected over an
-   *          IP-based connection.
+   * @return The <CODE>java.net.InetAddress</CODE> for the Directory
+   *         Server system to which the client has established the
+   *         connection. It may be <CODE>null</CODE> if the client is
+   *         not connected over an IP-based connection.
    */
+  @Override
   public InetAddress getLocalAddress()
   {
     return clientChannel.socket().getLocalAddress();
@@ -477,92 +458,34 @@
 
 
   /**
-   * Indicates whether this client connection is currently using a secure
-   * mechanism to communicate with the server.  Note that this may change over
-   * time based on operations performed by the client or server (e.g., it may go
-   * from <CODE>false</CODE> to <CODE>true</CODE> if the client uses the
-   * StartTLS extended operation).
+   * Indicates whether this client connection is currently using a
+   * secure mechanism to communicate with the server. Note that this may
+   * change over time based on operations performed by the client or
+   * server (e.g., it may go from <CODE>false</CODE> to
+   * <CODE>true</CODE> if the client uses the StartTLS extended
+   * operation).
    *
-   * @return  <CODE>true</CODE> if the client connection is currently using a
-   *          secure mechanism to communicate with the server, or
-   *          <CODE>false</CODE> if not.
+   * @return <CODE>true</CODE> if the client connection is currently
+   *         using a secure mechanism to communicate with the server, or
+   *         <CODE>false</CODE> if not.
    */
+  @Override
   public boolean isSecure()
   {
-    return securityProvider.isSecure();
-  }
-
-
-
-  /**
-   * Retrieves the connection security provider for this client connection.
-   *
-   * @return  The connection security provider for this client connection.
-   */
-  public ConnectionSecurityProvider getConnectionSecurityProvider()
-  {
-      if(saslSecurityProvider != null && saslSecurityProvider.isActive())
-          securityProvider =  saslSecurityProvider;
-      return securityProvider;
-  }
-
-
-
-  /**
-   * Set the security provider to be used to process SASL (DIGEST-MD5, GSSAPI)
-   * confidentiality/integrity messages.
-   *
-   * @param secProvider The security provider to use.
-   */
-    public void
-    setSASLConnectionSecurityProvider(ConnectionSecurityProvider secProvider) {
-        saslSecurityProvider = secProvider;
-    }
-
-
-
-  /**
-   * Specifies the connection security provider for this client connection.
-   *
-   * @param  securityProvider  The connection security provider to use for
-   *                           communication on this client connection.
-   */
-  public void setConnectionSecurityProvider(ConnectionSecurityProvider
-                                                 securityProvider)
-  {
-    this.securityProvider = securityProvider;
-
-    if (securityProvider.isSecure())
-    {
-      protocol = "LDAP+" + securityProvider.getSecurityMechanismName();
-    }
+    if (activeProvider != null)
+      return activeProvider.isSecure();
     else
-    {
-      protocol = "LDAP";
-    }
+      return false;
   }
 
 
 
   /**
-   * Retrieves the human-readable name of the security mechanism that is used to
-   * protect communication with this client.
+   * Retrieves the next operation ID that should be used for this
+   * connection.
    *
-   * @return  The human-readable name of the security mechanism that is used to
-   *          protect communication with this client, or <CODE>null</CODE> if no
-   *          security is in place.
-   */
-  public String getSecurityMechanism()
-  {
-    return securityProvider.getSecurityMechanismName();
-  }
-
-
-
-  /**
-   * Retrieves the next operation ID that should be used for this connection.
-   *
-   * @return  The next operation ID that should be used for this connection.
+   * @return The next operation ID that should be used for this
+   *         connection.
    */
   public long nextOperationID()
   {
@@ -572,57 +495,56 @@
 
 
   /**
-   * Sends a response to the client based on the information in the provided
-   * operation.
+   * Sends a response to the client based on the information in the
+   * provided operation.
    *
-   * @param  operation  The operation for which to send the response.
+   * @param operation
+   *          The operation for which to send the response.
    */
+  @Override
   public void sendResponse(Operation operation)
   {
-    // Since this is the final response for this operation, we can go ahead and
-    // remove it from the "operations in progress" list.  It can't be canceled
-    // after this point, and this will avoid potential race conditions in which
-    // the client immediately sends another request with the same message ID as
-    // was used for this operation.
+    // Since this is the final response for this operation, we can go
+    // ahead and remove it from the "operations in progress" list. It
+    // can't be canceled after this point, and this will avoid potential
+    // race conditions in which the client immediately sends another
+    // request with the same message ID as was used for this operation.
     removeOperationInProgress(operation.getMessageID());
 
     LDAPMessage message = operationToResponseLDAPMessage(operation);
     if (message != null)
     {
-      sendLDAPMessage(securityProvider, message);
+      sendLDAPMessage(message);
     }
   }
 
 
 
   /**
-   * Retrieves an LDAPMessage containing a response generated from the provided
-   * operation.
+   * Retrieves an LDAPMessage containing a response generated from the
+   * provided operation.
    *
-   * @param  operation  The operation to use to generate the response
-   *                    LDAPMessage.
-   *
-   * @return  An LDAPMessage containing a response generated from the provided
-   *          operation.
+   * @param operation
+   *          The operation to use to generate the response LDAPMessage.
+   * @return An LDAPMessage containing a response generated from the
+   *         provided operation.
    */
   private LDAPMessage operationToResponseLDAPMessage(Operation operation)
   {
     ResultCode resultCode = operation.getResultCode();
     if (resultCode == null)
     {
-      // This must mean that the operation has either not yet completed or that
-      // it completed without a result for some reason.  In any case, log a
-      // message and set the response to "operations error".
-      logError(ERR_LDAP_CLIENT_SEND_RESPONSE_NO_RESULT_CODE.
-          get(operation.getOperationType().toString(),
-              operation.getConnectionID(), operation.getOperationID()));
+      // This must mean that the operation has either not yet completed
+      // or that it completed without a result for some reason. In any
+      // case, log a message and set the response to "operations error".
+      logError(ERR_LDAP_CLIENT_SEND_RESPONSE_NO_RESULT_CODE.get(
+          operation.getOperationType().toString(), operation
+              .getConnectionID(), operation.getOperationID()));
       resultCode = DirectoryServer.getServerErrorResultCode();
     }
 
-
     MessageBuilder errorMessage = operation.getErrorMessage();
-    DN             matchedDN    = operation.getMatchedDN();
-
+    DN matchedDN = operation.getMatchedDN();
 
     // Referrals are not allowed for LDAPv2 clients.
     List<String> referralURLs;
@@ -637,7 +559,7 @@
       }
 
       List<String> opReferrals = operation.getReferralURLs();
-      if ((opReferrals != null) && (! opReferrals.isEmpty()))
+      if ((opReferrals != null) && (!opReferrals.isEmpty()))
       {
         StringBuilder referralsStr = new StringBuilder();
         Iterator<String> iterator = opReferrals.iterator();
@@ -649,8 +571,8 @@
           referralsStr.append(iterator.next());
         }
 
-        errorMessage.append(ERR_LDAPV2_REFERRALS_OMITTED.get(
-                String.valueOf(referralsStr)));
+        errorMessage.append(ERR_LDAPV2_REFERRALS_OMITTED.get(String
+            .valueOf(referralsStr)));
       }
     }
     else
@@ -661,94 +583,82 @@
     ProtocolOp protocolOp;
     switch (operation.getOperationType())
     {
-      case ADD:
-        protocolOp = new AddResponseProtocolOp(resultCode.getIntValue(),
-                                               errorMessage.toMessage(),
-                                               matchedDN, referralURLs);
-        break;
-      case BIND:
-        ASN1OctetString serverSASLCredentials =
-             ((BindOperationBasis) operation).getServerSASLCredentials();
-        protocolOp = new BindResponseProtocolOp(resultCode.getIntValue(),
-                              errorMessage.toMessage(), matchedDN,
-                              referralURLs, serverSASLCredentials);
-        break;
-      case COMPARE:
-        protocolOp = new CompareResponseProtocolOp(resultCode.getIntValue(),
-                                                   errorMessage.toMessage(),
-                                                   matchedDN, referralURLs);
-        break;
-      case DELETE:
-        protocolOp = new DeleteResponseProtocolOp(resultCode.getIntValue(),
-                                                  errorMessage.toMessage(),
-                                                  matchedDN, referralURLs);
-        break;
-      case EXTENDED:
-        // If this an LDAPv2 client, then we can't send this.
-        if (ldapVersion == 2)
-        {
-          logError(ERR_LDAPV2_SKIPPING_EXTENDED_RESPONSE.get(
-              getConnectionID(), operation.getOperationID(),
-                  String.valueOf(operation)));
-          return null;
-        }
-
-        ExtendedOperationBasis extOp = (ExtendedOperationBasis) operation;
-        protocolOp = new ExtendedResponseProtocolOp(resultCode.getIntValue(),
-                              errorMessage.toMessage(), matchedDN, referralURLs,
-                              extOp.getResponseOID(), extOp.getResponseValue());
-        break;
-      case MODIFY:
-        protocolOp = new ModifyResponseProtocolOp(resultCode.getIntValue(),
-                                                  errorMessage.toMessage(),
-                                                  matchedDN, referralURLs);
-        break;
-      case MODIFY_DN:
-        protocolOp = new ModifyDNResponseProtocolOp(resultCode.getIntValue(),
-                                                    errorMessage.toMessage(),
-                                                    matchedDN, referralURLs);
-        break;
-      case SEARCH:
-        protocolOp = new SearchResultDoneProtocolOp(resultCode.getIntValue(),
-                                                    errorMessage.toMessage(),
-                                                    matchedDN, referralURLs);
-        break;
-      default:
-        // This must be a type of operation that doesn't have a response.  This
-        // shouldn't happen, so log a message and return.
-        logError(ERR_LDAP_CLIENT_SEND_RESPONSE_INVALID_OP.get(
-                String.valueOf(operation.getOperationType()),
-                getConnectionID(),
-                operation.getOperationID(),
-                String.valueOf(operation)));
+    case ADD:
+      protocolOp =
+          new AddResponseProtocolOp(resultCode.getIntValue(),
+              errorMessage.toMessage(), matchedDN, referralURLs);
+      break;
+    case BIND:
+      ByteString serverSASLCredentials =
+          ((BindOperationBasis) operation).getServerSASLCredentials();
+      protocolOp =
+          new BindResponseProtocolOp(resultCode.getIntValue(),
+              errorMessage.toMessage(), matchedDN, referralURLs,
+              serverSASLCredentials);
+      break;
+    case COMPARE:
+      protocolOp =
+          new CompareResponseProtocolOp(resultCode.getIntValue(),
+              errorMessage.toMessage(), matchedDN, referralURLs);
+      break;
+    case DELETE:
+      protocolOp =
+          new DeleteResponseProtocolOp(resultCode.getIntValue(),
+              errorMessage.toMessage(), matchedDN, referralURLs);
+      break;
+    case EXTENDED:
+      // If this an LDAPv2 client, then we can't send this.
+      if (ldapVersion == 2)
+      {
+        logError(ERR_LDAPV2_SKIPPING_EXTENDED_RESPONSE.get(
+            getConnectionID(), operation.getOperationID(), String
+                .valueOf(operation)));
         return null;
+      }
+
+      ExtendedOperationBasis extOp = (ExtendedOperationBasis) operation;
+      protocolOp =
+          new ExtendedResponseProtocolOp(resultCode.getIntValue(),
+              errorMessage.toMessage(), matchedDN, referralURLs, extOp
+                  .getResponseOID(), extOp.getResponseValue());
+      break;
+    case MODIFY:
+      protocolOp =
+          new ModifyResponseProtocolOp(resultCode.getIntValue(),
+              errorMessage.toMessage(), matchedDN, referralURLs);
+      break;
+    case MODIFY_DN:
+      protocolOp =
+          new ModifyDNResponseProtocolOp(resultCode.getIntValue(),
+              errorMessage.toMessage(), matchedDN, referralURLs);
+      break;
+    case SEARCH:
+      protocolOp =
+          new SearchResultDoneProtocolOp(resultCode.getIntValue(),
+              errorMessage.toMessage(), matchedDN, referralURLs);
+      break;
+    default:
+      // This must be a type of operation that doesn't have a response.
+      // This shouldn't happen, so log a message and return.
+      logError(ERR_LDAP_CLIENT_SEND_RESPONSE_INVALID_OP.get(String
+          .valueOf(operation.getOperationType()), getConnectionID(),
+          operation.getOperationID(), String.valueOf(operation)));
+      return null;
     }
 
-
     // Controls are not allowed for LDAPv2 clients.
-    ArrayList<LDAPControl> controls;
+    List<Control> controls;
     if (ldapVersion == 2)
     {
       controls = null;
     }
     else
     {
-      List<Control> responseControls = operation.getResponseControls();
-      if ((responseControls == null) || responseControls.isEmpty())
-      {
-        controls = null;
-      }
-      else
-      {
-        controls = new ArrayList<LDAPControl>(responseControls.size());
-        for (Control c : responseControls)
-        {
-          controls.add(new LDAPControl(c));
-        }
-      }
+      controls = operation.getResponseControls();
     }
 
-    return new LDAPMessage(operation.getMessageID(), protocolOp, controls);
+    return new LDAPMessage(operation.getMessageID(), protocolOp,
+        controls);
   }
 
 
@@ -756,34 +666,20 @@
   /**
    * Sends the provided search result entry to the client.
    *
-   * @param  searchOperation  The search operation with which the entry is
-   *                          associated.
-   * @param  searchEntry      The search result entry to be sent to the client.
+   * @param searchOperation
+   *          The search operation with which the entry is associated.
+   * @param searchEntry
+   *          The search result entry to be sent to the client.
    */
+  @Override
   public void sendSearchEntry(SearchOperation searchOperation,
-                              SearchResultEntry searchEntry)
+      SearchResultEntry searchEntry)
   {
     SearchResultEntryProtocolOp protocolOp =
-         new SearchResultEntryProtocolOp(searchEntry,ldapVersion);
+        new SearchResultEntryProtocolOp(searchEntry, ldapVersion);
 
-    List<Control> entryControls = searchEntry.getControls();
-    ArrayList<LDAPControl> controls;
-    if ((entryControls == null) || entryControls.isEmpty())
-    {
-      controls = null;
-    }
-    else
-    {
-      controls = new ArrayList<LDAPControl>(entryControls.size());
-      for (Control c : entryControls)
-      {
-        controls.add(new LDAPControl(c));
-      }
-    }
-
-    sendLDAPMessage(securityProvider,
-                    new LDAPMessage(searchOperation.getMessageID(), protocolOp,
-                                    controls));
+    sendLDAPMessage(new LDAPMessage(searchOperation.getMessageID(),
+        protocolOp, searchEntry.getControls()));
   }
 
 
@@ -791,92 +687,69 @@
   /**
    * Sends the provided search result reference to the client.
    *
-   * @param  searchOperation  The search operation with which the reference is
-   *                          associated.
-   * @param  searchReference  The search result reference to be sent to the
-   *                          client.
-   *
-   * @return  <CODE>true</CODE> if the client is able to accept referrals, or
-   *          <CODE>false</CODE> if the client cannot handle referrals and no
-   *          more attempts should be made to send them for the associated
-   *          search operation.
+   * @param searchOperation
+   *          The search operation with which the reference is
+   *          associated.
+   * @param searchReference
+   *          The search result reference to be sent to the client.
+   * @return <CODE>true</CODE> if the client is able to accept
+   *         referrals, or <CODE>false</CODE> if the client cannot
+   *         handle referrals and no more attempts should be made to
+   *         send them for the associated search operation.
    */
+  @Override
   public boolean sendSearchReference(SearchOperation searchOperation,
-                                     SearchResultReference searchReference)
+      SearchResultReference searchReference)
   {
-    // Make sure this is not an LDAPv2 client.  If it is, then they can't see
-    // referrals so we'll not send anything.  Also, throw an exception so that
-    // the core server will know not to try sending any more referrals to this
-    // client for the rest of the operation.
+    // Make sure this is not an LDAPv2 client. If it is, then they can't
+    // see referrals so we'll not send anything. Also, throw an
+    // exception so that the core server will know not to try sending
+    // any more referrals to this client for the rest of the operation.
     if (ldapVersion == 2)
     {
-      Message message = ERR_LDAPV2_SKIPPING_SEARCH_REFERENCE.
-          get(getConnectionID(), searchOperation.getOperationID(),
-              String.valueOf(searchReference));
+      Message message =
+          ERR_LDAPV2_SKIPPING_SEARCH_REFERENCE.get(getConnectionID(),
+              searchOperation.getOperationID(), String
+                  .valueOf(searchReference));
       logError(message);
       return false;
     }
 
     SearchResultReferenceProtocolOp protocolOp =
-         new SearchResultReferenceProtocolOp(searchReference);
+        new SearchResultReferenceProtocolOp(searchReference);
 
-    List<Control> referenceControls = searchReference.getControls();
-    ArrayList<LDAPControl> controls;
-    if ((referenceControls == null) || referenceControls.isEmpty())
-    {
-      controls = null;
-    }
-    else
-    {
-      controls = new ArrayList<LDAPControl>(referenceControls.size());
-      for (Control c : referenceControls)
-      {
-        controls.add(new LDAPControl(c));
-      }
-    }
-
-    sendLDAPMessage(securityProvider,
-                    new LDAPMessage(searchOperation.getMessageID(), protocolOp,
-                                    controls));
+    sendLDAPMessage(new LDAPMessage(searchOperation.getMessageID(),
+        protocolOp, searchReference.getControls()));
     return true;
   }
 
 
 
-
   /**
    * Sends the provided intermediate response message to the client.
    *
-   * @param  intermediateResponse  The intermediate response message to be sent.
-   *
-   * @return  <CODE>true</CODE> if processing on the associated operation should
-   *          continue, or <CODE>false</CODE> if not.
+   * @param intermediateResponse
+   *          The intermediate response message to be sent.
+   * @return <CODE>true</CODE> if processing on the associated operation
+   *         should continue, or <CODE>false</CODE> if not.
    */
+  @Override
   protected boolean sendIntermediateResponseMessage(
-                         IntermediateResponse intermediateResponse)
+      IntermediateResponse intermediateResponse)
   {
     IntermediateResponseProtocolOp protocolOp =
-         new IntermediateResponseProtocolOp(intermediateResponse.getOID(),
-                                            intermediateResponse.getValue());
+        new IntermediateResponseProtocolOp(intermediateResponse
+            .getOID(), intermediateResponse.getValue());
 
     Operation operation = intermediateResponse.getOperation();
 
-    List<Control> controls = intermediateResponse.getControls();
-    ArrayList<LDAPControl> ldapControls =
-         new ArrayList<LDAPControl>(controls.size());
-    for (Control c : controls)
-    {
-      ldapControls.add(new LDAPControl(c));
-    }
+    LDAPMessage message =
+        new LDAPMessage(operation.getMessageID(), protocolOp,
+            intermediateResponse.getControls());
+    sendLDAPMessage(message);
 
-
-    LDAPMessage message = new LDAPMessage(operation.getMessageID(), protocolOp,
-                                          ldapControls);
-    sendLDAPMessage(securityProvider, message);
-
-
-    // The only reason we shouldn't continue processing is if the connection is
-    // closed.
+    // The only reason we shouldn't continue processing is if the
+    // connection is closed.
     return connectionValid;
   }
 
@@ -885,95 +758,92 @@
   /**
    * Sends the provided LDAP message to the client.
    *
-   * @param  secProvider  The connection security provider to use to handle any
-   *                      necessary security translation.
-   * @param  message      The LDAP message to send to the client.
+   * @param message
+   *          The LDAP message to send to the client.
    */
-  public void sendLDAPMessage(ConnectionSecurityProvider secProvider,
-                              LDAPMessage message)
+  public void sendLDAPMessage(LDAPMessage message)
   {
-    ASN1Element messageElement = message.encode();
-
-    ByteBuffer messageBuffer = ByteBuffer.wrap(messageElement.encode());
-
-
-    // Make sure that we can only send one message at a time.  This locking will
-    // not have any impact on the ability to read requests from the client.
-    synchronized (transmitLock)
+    // Get the buffer used by this thread.
+    WriterBuffer writerBuffer = cachedBuffers.get();
+    if (writerBuffer == null)
     {
-      try
+      writerBuffer = new WriterBuffer();
+      // TODO SASLPhase2 maybe don't want to cache these
+      if (isSecure())
       {
-        try
+        int appBufSize = activeProvider.getAppBufSize();
+        writerBuffer.writer = ASN1.getWriter(saslChannel, appBufSize);
+      }
+      else
+        writerBuffer.writer = ASN1.getWriter(saslChannel, 4096);
+      cachedBuffers.set(writerBuffer);
+    }
+    try
+    {
+      // Make sure that we can only send one message at a time. This
+      // locking will not have any impact on the ability to read
+      // requests from the client.
+      synchronized (transmitLock)
+      {
+        message.write(writerBuffer.writer);
+        writerBuffer.writer.flush();
+        if(debugEnabled())
         {
-          int bytesWritten = messageBuffer.limit() - messageBuffer.position();
-          if (! secProvider.writeData(messageBuffer))
-          {
-            return;
-          }
+          TRACER.debugProtocolElement(DebugLogLevel.VERBOSE,
+              message.toString());
 
-          TRACER.debugProtocolElement(DebugLogLevel.VERBOSE, message);
-          TRACER.debugProtocolElement(DebugLogLevel.VERBOSE, messageElement);
-
-          messageBuffer.rewind();
-          if (debugEnabled())
-          {
-            TRACER.debugData(DebugLogLevel.VERBOSE, messageBuffer);
-          }
-
-          if (keepStats)
-          {
-            statTracker.updateMessageWritten(message, bytesWritten);
-          }
+          // TODO SASLPhase2 message buffer?
+          // TRACER.debugData(DebugLogLevel.VERBOSE, messageBuffer);
         }
-        catch (@Deprecated Exception e)
-        {
-          if (debugEnabled())
-          {
-            TRACER.debugCaught(DebugLogLevel.ERROR, e);
-          }
 
-          // We were unable to send the message due to some other internal
-          // problem.  Disconnect from the client and return.
-          disconnect(DisconnectReason.SERVER_ERROR, true, null);
-          return;
+        if (keepStats)
+        {
+          // TODO SASLPhase2 hard-coded for now, flush probably needs to
+          // return how many bytes were flushed.
+          statTracker.updateMessageWritten(message, 4096);
         }
       }
-      catch (Exception e)
+      // writerBuffer.buffer.clear();
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
       {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, e);
-        }
-
-        // FIXME -- Log a message or something
-        disconnect(DisconnectReason.SERVER_ERROR, true, null);
-        return;
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
       }
+
+      // FIXME -- Log a message or something
+      disconnect(DisconnectReason.SERVER_ERROR, true, null);
+      return;
     }
   }
 
 
 
   /**
-   * Closes the connection to the client, optionally sending it a message
-   * indicating the reason for the closure.  Note that the ability to send a
-   * notice of disconnection may not be available for all protocols or under all
-   * circumstances.
+   * Closes the connection to the client, optionally sending it a
+   * message indicating the reason for the closure. Note that the
+   * ability to send a notice of disconnection may not be available for
+   * all protocols or under all circumstances.
    *
-   * @param  disconnectReason  The disconnect reason that provides the generic
-   *                           cause for the disconnect.
-   * @param  sendNotification  Indicates whether to try to provide notification
-   *                           to the client that the connection will be closed.
-   * @param  message           The message to include in the disconnect
-   *                           notification response.  It may be
-   *                           <CODE>null</CODE> if no message is to be sent.
+   * @param disconnectReason
+   *          The disconnect reason that provides the generic cause for
+   *          the disconnect.
+   * @param sendNotification
+   *          Indicates whether to try to provide notification to the
+   *          client that the connection will be closed.
+   * @param message
+   *          The message to include in the disconnect notification
+   *          response. It may be <CODE>null</CODE> if no message is to
+   *          be sent.
    */
+  @Override
   public void disconnect(DisconnectReason disconnectReason,
-                         boolean sendNotification,
-                         Message message)
+      boolean sendNotification, Message message)
   {
-    // Set a flag indicating that the connection is being terminated so that no
-    // new requests will be accepted.  Also cancel all operations in progress.
+    // Set a flag indicating that the connection is being terminated so
+    // that no new requests will be accepted. Also cancel all operations
+    // in progress.
     synchronized (opsInProgressLock)
     {
       // If we are already in the middle of a disconnect, then don't
@@ -986,7 +856,6 @@
       disconnectRequested = true;
     }
 
-
     if (keepStats)
     {
       statTracker.updateDisconnect();
@@ -997,26 +866,25 @@
       DirectoryServer.connectionClosed(this);
     }
 
-
     // Indicate that this connection is no longer valid.
     connectionValid = false;
 
-    if(message != null)
+    if (message != null)
     {
       MessageBuilder msgBuilder = new MessageBuilder();
       msgBuilder.append(disconnectReason.getClosureMessage());
       msgBuilder.append(": ");
       msgBuilder.append(message);
-      cancelAllOperations(new CancelRequest(true, msgBuilder.toMessage()));
+      cancelAllOperations(new CancelRequest(true, msgBuilder
+          .toMessage()));
     }
     else
     {
-      cancelAllOperations(new CancelRequest(true,
-          disconnectReason.getClosureMessage()));
+      cancelAllOperations(new CancelRequest(true, disconnectReason
+          .getClosureMessage()));
     }
     finalizeConnectionInternal();
 
-
     // If there is a write selector for this connection, then close it.
     Selector selector = writeSelector.get();
     if (selector != null)
@@ -1024,13 +892,16 @@
       try
       {
         selector.close();
-      } catch (Exception e) {}
+      }
+      catch (Exception e)
+      {
+      }
     }
 
-
-    // See if we should send a notification to the client.  If so, then
-    // construct and send a notice of disconnection unsolicited response.
-    // Note that we cannot send this notification to an LDAPv2 client.
+    // See if we should send a notification to the client. If so, then
+    // construct and send a notice of disconnection unsolicited
+    // response. Note that we cannot send this notification to an LDAPv2
+    // client.
     if (sendNotification && (ldapVersion != 2))
     {
       try
@@ -1038,70 +909,63 @@
         int resultCode;
         switch (disconnectReason)
         {
-          case PROTOCOL_ERROR:
-            resultCode = LDAPResultCode.PROTOCOL_ERROR;
-            break;
-          case SERVER_SHUTDOWN:
-            resultCode = LDAPResultCode.UNAVAILABLE;
-            break;
-          case SERVER_ERROR:
-            resultCode =
-                 DirectoryServer.getServerErrorResultCode().getIntValue();
-            break;
-          case ADMIN_LIMIT_EXCEEDED:
-          case IDLE_TIME_LIMIT_EXCEEDED:
-          case MAX_REQUEST_SIZE_EXCEEDED:
-          case IO_TIMEOUT:
-            resultCode = LDAPResultCode.ADMIN_LIMIT_EXCEEDED;
-            break;
-          case CONNECTION_REJECTED:
-            resultCode = LDAPResultCode.CONSTRAINT_VIOLATION;
-            break;
-          default:
-            resultCode = LDAPResultCode.OTHER;
-            break;
+        case PROTOCOL_ERROR:
+          resultCode = LDAPResultCode.PROTOCOL_ERROR;
+          break;
+        case SERVER_SHUTDOWN:
+          resultCode = LDAPResultCode.UNAVAILABLE;
+          break;
+        case SERVER_ERROR:
+          resultCode =
+              DirectoryServer.getServerErrorResultCode().getIntValue();
+          break;
+        case ADMIN_LIMIT_EXCEEDED:
+        case IDLE_TIME_LIMIT_EXCEEDED:
+        case MAX_REQUEST_SIZE_EXCEEDED:
+        case IO_TIMEOUT:
+          resultCode = LDAPResultCode.ADMIN_LIMIT_EXCEEDED;
+          break;
+        case CONNECTION_REJECTED:
+          resultCode = LDAPResultCode.CONSTRAINT_VIOLATION;
+          break;
+        default:
+          resultCode = LDAPResultCode.OTHER;
+          break;
         }
 
-
         Message errMsg;
         if (message == null)
         {
-          errMsg = INFO_LDAP_CLIENT_GENERIC_NOTICE_OF_DISCONNECTION.get();
+          errMsg =
+              INFO_LDAP_CLIENT_GENERIC_NOTICE_OF_DISCONNECTION.get();
         }
         else
         {
           errMsg = message;
         }
 
-
         ExtendedResponseProtocolOp notificationOp =
-             new ExtendedResponseProtocolOp(resultCode, errMsg, null, null,
-                                            OID_NOTICE_OF_DISCONNECTION, null);
-        byte[] messageBytes =
-                    new LDAPMessage(0, notificationOp, null).encode().encode();
-        ByteBuffer buffer = ByteBuffer.wrap(messageBytes);
-        try
-        {
-          securityProvider.writeData(buffer);
-        } catch (Exception e) {}
+            new ExtendedResponseProtocolOp(resultCode, errMsg, null,
+                null, OID_NOTICE_OF_DISCONNECTION, null);
+
+        sendLDAPMessage(new LDAPMessage(0, notificationOp, null));
       }
       catch (Exception e)
       {
-        // NYI -- Log a message indicating that we couldn't send the notice of
-        // disconnection.
+        // NYI -- Log a message indicating that we couldn't send the
+        // notice of disconnection.
       }
     }
 
-
     // Close the connection to the client.
     try
     {
-      securityProvider.disconnect(sendNotification);
+      asn1Reader.close();
     }
     catch (Exception e)
     {
-      // In general, we don't care about any exception that might be thrown
-      // here.
+      // In general, we don't care about any exception that might be
+      // thrown here.
       if (debugEnabled())
       {
         TRACER.debugCaught(DebugLogLevel.ERROR, e);
@@ -1114,29 +978,29 @@
     }
     catch (Exception e)
     {
-      // In general, we don't care about any exception that might be thrown
-      // here.
+      // In general, we don't care about any exception that might be
+      // thrown here.
       if (debugEnabled())
       {
         TRACER.debugCaught(DebugLogLevel.ERROR, e);
       }
     }
 
+    // Remove the thread local buffers.
+    cachedBuffers.remove();
 
     // NYI -- Deregister the client connection from any server components that
     // might know about it.
 
-
     // Log a disconnect message.
     logDisconnect(this, disconnectReason, message);
 
-
     try
     {
       PluginConfigManager pluginManager =
-           DirectoryServer.getPluginConfigManager();
+          DirectoryServer.getPluginConfigManager();
       pluginManager.invokePostDisconnectPlugins(this, disconnectReason,
-              message);
+          message);
     }
     catch (Exception e)
     {
@@ -1150,11 +1014,13 @@
 
 
   /**
-   * Retrieves the set of operations in progress for this client connection.
-   * This list must not be altered by any caller.
+   * Retrieves the set of operations in progress for this client
+   * connection. This list must not be altered by any caller.
    *
-   * @return  The set of operations in progress for this client connection.
+   * @return The set of operations in progress for this client
+   *         connection.
    */
+  @Override
   public Collection<Operation> getOperationsInProgress()
   {
     return operationsInProgress.values();
@@ -1165,11 +1031,12 @@
   /**
    * Retrieves the operation in progress with the specified message ID.
    *
-   * @param  messageID  The message ID for the operation to retrieve.
-   *
-   * @return  The operation in progress with the specified message ID, or
-   *          <CODE>null</CODE> if no such operation could be found.
+   * @param messageID
+   *          The message ID for the operation to retrieve.
+   * @return The operation in progress with the specified message ID, or
+   *         <CODE>null</CODE> if no such operation could be found.
    */
+  @Override
   public Operation getOperationInProgress(int messageID)
   {
     return operationsInProgress.get(messageID);
@@ -1178,56 +1045,59 @@
 
 
   /**
-   * Adds the provided operation to the set of operations in progress for this
-   * client connection.
+   * Adds the provided operation to the set of operations in progress
+   * for this client connection.
    *
-   * @param  operation  The operation to add to the set of operations in
-   *                    progress for this client connection.
-   *
-   * @throws  DirectoryException  If the operation is not added for some reason
-   *                              (e.g., the client already has reached the
-   *                              maximum allowed concurrent requests).
+   * @param operation
+   *          The operation to add to the set of operations in progress
+   *          for this client connection.
+   * @throws DirectoryException
+   *           If the operation is not added for some reason (e.g., the
+   *           client already has reached the maximum allowed concurrent
+   *           requests).
    */
   public void addOperationInProgress(AbstractOperation operation)
-         throws DirectoryException
+      throws DirectoryException
   {
     int messageID = operation.getMessageID();
 
-    // We need to grab a lock to ensure that no one else can add operations to
-    // the queue while we are performing some preliminary checks.
+    // We need to grab a lock to ensure that no one else can add
+    // operations to the queue while we are performing some preliminary
+    // checks.
     synchronized (opsInProgressLock)
     {
       try
       {
-        // If we're already in the process of disconnecting the client, then
-        // reject the operation.
+        // If we're already in the process of disconnecting the client,
+        // then reject the operation.
         if (disconnectRequested)
         {
-          Message message = WARN_LDAP_CLIENT_DISCONNECT_IN_PROGRESS.get();
+          Message message =
+              WARN_LDAP_CLIENT_DISCONNECT_IN_PROGRESS.get();
           throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
-                                       message);
+              message);
         }
 
-
-        // See if there is already an operation in progress with the same
-        // message ID.  If so, then we can't allow it.
+        // See if there is already an operation in progress with the
+        // same message ID. If so, then we can't allow it.
         Operation op = operationsInProgress.get(messageID);
         if (op != null)
         {
           Message message =
-               WARN_LDAP_CLIENT_DUPLICATE_MESSAGE_ID.get(messageID);
-          throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
+              WARN_LDAP_CLIENT_DUPLICATE_MESSAGE_ID.get(messageID);
+          throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
+              message);
         }
 
-
-        // Add the operation to the list of operations in progress for this
-        // connection.
+        // Add the operation to the list of operations in progress for
+        // this connection.
         operationsInProgress.put(messageID, operation);
 
-
         // Try to add the operation to the work queue,
-        // or run it synchronously (typically for the administration connector)
-        connectionHandler.getQueueingStrategy().enqueueRequest(operation);
+        // or run it synchronously (typically for the administration
+        // connector)
+        connectionHandler.getQueueingStrategy().enqueueRequest(
+            operation);
       }
       catch (DirectoryException de)
       {
@@ -1250,8 +1120,8 @@
 
         Message message =
             WARN_LDAP_CLIENT_CANNOT_ENQUEUE.get(getExceptionMessage(e));
-        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-                                     message, e);
+        throw new DirectoryException(DirectoryServer
+            .getServerErrorResultCode(), message, e);
       }
     }
   }
@@ -1259,16 +1129,19 @@
 
 
   /**
-   * Removes the provided operation from the set of operations in progress for
-   * this client connection.  Note that this does not make any attempt to
-   * cancel any processing that may already be in progress for the operation.
+   * Removes the provided operation from the set of operations in
+   * progress for this client connection. Note that this does not make
+   * any attempt to cancel any processing that may already be in
+   * progress for the operation.
    *
-   * @param  messageID  The message ID of the operation to remove from the set
-   *                    of operations in progress.
-   *
-   * @return  <CODE>true</CODE> if the operation was found and removed from the
-   *          set of operations in progress, or <CODE>false</CODE> if not.
+   * @param messageID
+   *          The message ID of the operation to remove from the set of
+   *          operations in progress.
+   * @return <CODE>true</CODE> if the operation was found and removed
+   *         from the set of operations in progress, or
+   *         <CODE>false</CODE> if not.
    */
+  @Override
   public boolean removeOperationInProgress(int messageID)
   {
     Operation operation = operationsInProgress.remove(messageID);
@@ -1288,15 +1161,17 @@
   /**
    * Attempts to cancel the specified operation.
    *
-   * @param  messageID      The message ID of the operation to cancel.
-   * @param  cancelRequest  An object providing additional information about how
-   *                        the cancel should be processed.
-   *
-   * @return  A cancel result that either indicates that the cancel was
-   *          successful or provides a reason that it was not.
+   * @param messageID
+   *          The message ID of the operation to cancel.
+   * @param cancelRequest
+   *          An object providing additional information about how the
+   *          cancel should be processed.
+   * @return A cancel result that either indicates that the cancel was
+   *         successful or provides a reason that it was not.
    */
+  @Override
   public CancelResult cancelOperation(int messageID,
-                                      CancelRequest cancelRequest)
+      CancelRequest cancelRequest)
   {
     Operation op = operationsInProgress.get(messageID);
     if (op == null)
@@ -1331,9 +1206,11 @@
   /**
    * Attempts to cancel all operations in progress on this connection.
    *
-   * @param  cancelRequest  An object providing additional information about how
-   *                        the cancel should be processed.
+   * @param cancelRequest
+   *          An object providing additional information about how the
+   *          cancel should be processed.
    */
+  @Override
   public void cancelAllOperations(CancelRequest cancelRequest)
   {
     // Make sure that no one can add any new operations.
@@ -1362,15 +1239,14 @@
           }
         }
 
-        if (! (operationsInProgress.isEmpty() &&
-               getPersistentSearches().isEmpty()))
+        if (!(operationsInProgress.isEmpty() && getPersistentSearches()
+            .isEmpty()))
         {
           lastCompletionTime.set(TimeThread.getTime());
         }
 
         operationsInProgress.clear();
 
-
         for (PersistentSearch persistentSearch : getPersistentSearches())
         {
           persistentSearch.cancel();
@@ -1389,16 +1265,19 @@
 
 
   /**
-   * Attempts to cancel all operations in progress on this connection except the
-   * operation with the specified message ID.
+   * Attempts to cancel all operations in progress on this connection
+   * except the operation with the specified message ID.
    *
-   * @param  cancelRequest  An object providing additional information about how
-   *                        the cancel should be processed.
-   * @param  messageID      The message ID of the operation that should not be
-   *                        canceled.
+   * @param cancelRequest
+   *          An object providing additional information about how the
+   *          cancel should be processed.
+   * @param messageID
+   *          The message ID of the operation that should not be
+   *          canceled.
    */
+  @Override
   public void cancelAllOperationsExcept(CancelRequest cancelRequest,
-                                        int messageID)
+      int messageID)
   {
     // Make sure that no one can add any new operations.
     synchronized (opsInProgressLock)
@@ -1438,7 +1317,6 @@
           lastCompletionTime.set(TimeThread.getTime());
         }
 
-
         for (PersistentSearch persistentSearch : getPersistentSearches())
         {
           if (persistentSearch.getMessageID() == messageID)
@@ -1474,7 +1352,7 @@
       try
       {
         selector = Selector.open();
-        if (! writeSelector.compareAndSet(null, selector))
+        if (!writeSelector.compareAndSet(null, selector))
         {
           selector.close();
           selector = writeSelector.get();
@@ -1506,308 +1384,109 @@
 
 
   /**
-   * Returns the total number of operations initiated on this connection.
+   * Returns the total number of operations initiated on this
+   * connection.
    *
    * @return the total number of operations on this connection
    */
-  public long getNumberOfOperations() {
+  @Override
+  public long getNumberOfOperations()
+  {
     long tmpNumberOfOperations;
-    synchronized (operationsPerformedLock) {
+    synchronized (operationsPerformedLock)
+    {
       tmpNumberOfOperations = operationsPerformed;
     }
     return tmpNumberOfOperations;
   }
 
 
+
   /**
-   * Process the information contained in the provided byte buffer as an ASN.1
-   * element.  It may take several calls to this method in order to get all the
-   * information necessary to decode a single ASN.1 element, but it may also be
-   * possible that there are multiple elements (or at least fragments of
-   * multiple elements) in a single buffer.  This will fully process whatever
-   * the client provided and set up the appropriate state information to make it
-   * possible to pick up in the right place the next time around.
+   * Process data read.
    *
-   * @param  buffer  The buffer containing the data to be processed.  It must be
-   *                 ready for reading (i.e., it should have been flipped by the
-   *                 caller), and the data provided must be unencrypted (e.g.,
-   *                 if the client is communicating over SSL, then the
-   *                 decryption should happen before calling this method).
-   *
-   * @return  <CODE>true</CODE> if all the data in the provided buffer was
-   *          processed and the client connection can remain established, or
-   *          <CODE>false</CODE> if a decoding error occurred and requests from
-   *          this client should no longer be processed.  Note that if this
-   *          method does return <CODE>false</CODE>, then it must have already
-   *          disconnected the client, and upon returning the request handler
-   *          should remove it from the associated selector.
+   * @return {@code true} if this connection is still valid.
    */
-  public boolean processDataRead(ByteBuffer buffer)
+  public boolean processDataRead()
   {
-    if (debugEnabled())
+    if (this.saslPendingProvider != null)
     {
-      TRACER.debugData(DebugLogLevel.VERBOSE, buffer);
+      enableSASL();
     }
-
-
-    int bytesAvailable = buffer.limit() - buffer.position();
-
-    if (keepStats)
+    while (true)
     {
-      statTracker.updateBytesRead(bytesAvailable);
-    }
-
-    while (bytesAvailable > 0)
-    {
-      switch (elementReadState)
+      try
       {
-        case ELEMENT_READ_STATE_NEED_TYPE:
-          // Read just the type and then loop again to see if there is more.
-          elementType = buffer.get();
-          bytesAvailable--;
-          elementReadState = ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE;
-          continue;
-
-
-        case ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE:
-          // Get the first length byte and see if it is a single-byte or
-          // multi-byte length.
-          byte firstLengthByte = buffer.get();
-          bytesAvailable--;
-          elementLengthBytesNeeded = (firstLengthByte & 0x7F);
-          if (elementLengthBytesNeeded == firstLengthByte)
-          {
-            elementLength = firstLengthByte;
-
-            // If the length is zero, then it cannot be a valid LDAP message.
-            if (elementLength == 0)
-            {
-              disconnect(DisconnectReason.PROTOCOL_ERROR, true,
-                         ERR_LDAP_CLIENT_DECODE_ZERO_BYTE_VALUE.get());
-              return false;
-            }
-
-            // Make sure that the element is not larger than the maximum allowed
-            // message size.
-            if ((connectionHandler.getMaxRequestSize() > 0) &&
-                (elementLength > connectionHandler.getMaxRequestSize()))
-            {
-              Message m = ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED.get(
-                elementLength, connectionHandler.getMaxRequestSize());
-              disconnect(DisconnectReason.MAX_REQUEST_SIZE_EXCEEDED, true, m);
-              return false;
-            }
-
-            elementValue            = new byte[elementLength];
-            elementValueBytesRead   = 0;
-            elementValueBytesNeeded = elementLength;
-            elementReadState        = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
-            continue;
-          }
-          else
-          {
-            if (elementLengthBytesNeeded > 4)
-            {
-              // We cannot handle multi-byte lengths in which more than four
-              // bytes are used to encode the length.
-              Message m = ERR_LDAP_CLIENT_DECODE_INVALID_MULTIBYTE_LENGTH.get(
-                elementLengthBytesNeeded);
-              disconnect(DisconnectReason.PROTOCOL_ERROR, true, m);
-              return false;
-            }
-
-            elementLength = 0x00;
-            if (elementLengthBytesNeeded <= bytesAvailable)
-            {
-              // We can read the entire length, so do it.
-              while (elementLengthBytesNeeded > 0)
-              {
-                elementLength = (elementLength << 8) | (buffer.get() & 0xFF);
-                bytesAvailable--;
-                elementLengthBytesNeeded--;
-              }
-
-              // If the length is zero, then it cannot be a valid LDAP message.
-              if (elementLength == 0)
-              {
-                disconnect(DisconnectReason.PROTOCOL_ERROR, true,
-                           ERR_LDAP_CLIENT_DECODE_ZERO_BYTE_VALUE.get());
-                return false;
-              }
-
-              // Make sure that the element is not larger than the maximum
-              // allowed message size.
-              if ((connectionHandler.getMaxRequestSize() > 0) &&
-                  (elementLength > connectionHandler.getMaxRequestSize()))
-              {
-                disconnect(DisconnectReason.MAX_REQUEST_SIZE_EXCEEDED, true,
-                           ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED.get(
-                                   elementLength,
-                                   connectionHandler.getMaxRequestSize()));
-                return false;
-              }
-
-              elementValue            = new byte[elementLength];
-              elementValueBytesRead   = 0;
-              elementValueBytesNeeded = elementLength;
-              elementReadState        = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
-              continue;
-            }
-            else
-            {
-              // We can't read the entire length, so just read what is
-              // available.
-              while (bytesAvailable > 0)
-              {
-                elementLength = (elementLength << 8) | (buffer.get() & 0xFF);
-                bytesAvailable--;
-                elementLengthBytesNeeded--;
-              }
-
-              return true;
-            }
-          }
-
-
-        case ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES:
-          if (bytesAvailable >= elementLengthBytesNeeded)
-          {
-            // We have enough data available to be able to read the entire
-            // length.  Do so.
-            while (elementLengthBytesNeeded > 0)
-            {
-              elementLength = (elementLength << 8) | (buffer.get() & 0xFF);
-              bytesAvailable--;
-              elementLengthBytesNeeded--;
-            }
-
-            // If the length is zero, then it cannot be a valid LDAP message.
-            if (elementLength == 0)
-            {
-              disconnect(DisconnectReason.PROTOCOL_ERROR, true,
-                         ERR_LDAP_CLIENT_DECODE_ZERO_BYTE_VALUE.get());
-              return false;
-            }
-
-            // Make sure that the element is not larger than the maximum allowed
-            // message size.
-            if ((connectionHandler.getMaxRequestSize() > 0) &&
-                (elementLength > connectionHandler.getMaxRequestSize()))
-            {
-              disconnect(DisconnectReason.MAX_REQUEST_SIZE_EXCEEDED, true,
-                         ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED.get(
-                                 elementLength,
-                                 connectionHandler.getMaxRequestSize()));
-              return false;
-            }
-
-            elementValue            = new byte[elementLength];
-            elementValueBytesRead   = 0;
-            elementValueBytesNeeded = elementLength;
-            elementReadState        = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
-            continue;
-          }
-          else
-          {
-            // We still don't have enough data to complete the length, so just
-            // read as much as possible.
-            while (bytesAvailable > 0)
-            {
-              elementLength = (elementLength << 8) | (buffer.get() & 0xFF);
-              bytesAvailable--;
-              elementLengthBytesNeeded--;
-            }
-
-            return true;
-          }
-
-
-        case ELEMENT_READ_STATE_NEED_VALUE_BYTES:
-          if (bytesAvailable >= elementValueBytesNeeded)
-          {
-            // We have enough data available to fully read the value.  Finish
-            // reading the information and convert it to an ASN.1 element.  Then
-            // decode that as an LDAP message.
-            buffer.get(elementValue, elementValueBytesRead,
-                       elementValueBytesNeeded);
-            elementValueBytesRead += elementValueBytesNeeded;
-            bytesAvailable -= elementValueBytesNeeded;
-            elementReadState = ELEMENT_READ_STATE_NEED_TYPE;
-
-            ASN1Sequence requestSequence;
-            try
-            {
-              requestSequence = ASN1Sequence.decodeAsSequence(elementType,
-                                                              elementValue);
-              TRACER.debugProtocolElement(DebugLogLevel.VERBOSE,
-                                          requestSequence);
-            }
-            catch (Exception e)
-            {
-              if (debugEnabled())
-              {
-                TRACER.debugCaught(DebugLogLevel.ERROR, e);
-              }
-              Message m = ERR_LDAP_CLIENT_DECODE_ASN1_FAILED.get(
-                String.valueOf(e));
-              disconnect(DisconnectReason.PROTOCOL_ERROR, true, m);
-              return false;
-            }
-
-            LDAPMessage requestMessage;
-            try
-            {
-              requestMessage = LDAPMessage.decode(requestSequence);
-              TRACER.debugProtocolElement(DebugLogLevel.VERBOSE,
-                                          requestMessage);
-            }
-            catch (Exception e)
-            {
-              if (debugEnabled())
-              {
-                TRACER.debugCaught(DebugLogLevel.ERROR, e);
-              }
-              Message m = ERR_LDAP_CLIENT_DECODE_LDAP_MESSAGE_FAILED.get(
-                String.valueOf(e));
-              disconnect(DisconnectReason.PROTOCOL_ERROR, true, m);
-              return false;
-            }
-
-            if (processLDAPMessage(requestMessage))
-            {
-              continue;
-            }
-            else
-            {
-              return false;
-            }
-          }
-          else
-          {
-            // We can't read all the value, so just read as much as we have
-            // available and pick it up again the next time around.
-            buffer.get(elementValue, elementValueBytesRead, bytesAvailable);
-            elementValueBytesRead   += bytesAvailable;
-            elementValueBytesNeeded -= bytesAvailable;
-            return true;
-          }
-
-
-        default:
-          // This should never happen.  There is an invalid internal read state.
-          // The only recourse that we have is to log a message and disconnect
-          // the client.
-          Message message =
-              ERR_LDAP_CLIENT_INVALID_DECODE_STATE.get(elementReadState);
-          logError(message);
-          disconnect(DisconnectReason.SERVER_ERROR, true, message);
+        int result = asn1Reader.processChannelData();
+        if (result < 0)
+        {
+          // The connection has been closed by the client. Disconnect
+          // and return.
+          disconnect(DisconnectReason.CLIENT_DISCONNECT, false, null);
           return false;
+        }
+        if (result == 0)
+        {
+          // We have read all the data that there is to read right now
+          // (or there wasn't any in the first place). Just return and
+          // wait for future notification.
+          return true;
+        }
+        else
+        {
+          // Decode all complete elements from the last read
+          while (asn1Reader.elementAvailable())
+          {
+            processLDAPMessage(LDAPReader.readMessage(asn1Reader));
+          }
+        }
+      }
+      catch (Exception e)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+        Message m =
+            ERR_LDAP_CLIENT_DECODE_LDAP_MESSAGE_FAILED.get(String
+                .valueOf(e));
+        disconnect(DisconnectReason.PROTOCOL_ERROR, true, m);
+        return false;
       }
     }
+  }
 
 
-    // If we've gotten here, then all of the data must have been processed
-    // properly so we can return true.
+
+  /**
+   * Process the information contained in the provided byte buffer as an
+   * ASN.1 element. It may take several calls to this method in order to
+   * get all the information necessary to decode a single ASN.1 element,
+   * but it may also be possible that there are multiple elements (or at
+   * least fragments of multiple elements) in a single buffer. This will
+   * fully process whatever the client provided and set up the
+   * appropriate state information to make it possible to pick up in the
+   * right place the next time around.
+   *
+   * @param buffer
+   *          The buffer containing the data to be processed. It must be
+   *          ready for reading (i.e., it should have been flipped by
+   *          the caller), and the data provided must be unencrypted
+   *          (e.g., if the client is communicating over SSL, then the
+   *          decryption should happen before calling this method).
+   * @return <CODE>true</CODE> if all the data in the provided buffer
+   *         was processed and the client connection can remain
+   *         established, or <CODE>false</CODE> if a decoding error
+   *         occurred and requests from this client should no longer be
+   *         processed. Note that if this method does return
+   *         <CODE>false</CODE>, then it must have already disconnected
+   *         the client, and upon returning the request handler should
+   *         remove it from the associated selector.
+   */
+  @Override
+  public boolean processDataRead(ByteBuffer buffer)
+  {
+    // this is no longer used.
     return true;
   }
 
@@ -1815,16 +1494,16 @@
 
   /**
    * Processes the provided LDAP message read from the client and takes
-   * whatever action is appropriate.  For most requests, this will include
-   * placing the operation in the work queue.  Certain requests (in particular,
-   * abandons and unbinds) will be processed directly.
+   * whatever action is appropriate. For most requests, this will
+   * include placing the operation in the work queue. Certain requests
+   * (in particular, abandons and unbinds) will be processed directly.
    *
-   * @param  message  The LDAP message to process.
-   *
-   * @return  <CODE>true</CODE> if the appropriate action was taken for the
-   *          request, or <CODE>false</CODE> if there was a fatal error and
-   *          the client has been disconnected as a result, or if the client
-   *          unbound from the server.
+   * @param message
+   *          The LDAP message to process.
+   * @return <CODE>true</CODE> if the appropriate action was taken for
+   *         the request, or <CODE>false</CODE> if there was a fatal
+   *         error and the client has been disconnected as a result, or
+   *         if the client unbound from the server.
    */
   private boolean processLDAPMessage(LDAPMessage message)
   {
@@ -1833,126 +1512,124 @@
       statTracker.updateMessageRead(message);
       this.getNetworkGroup().updateMessageRead(message);
     }
-    synchronized (operationsPerformedLock) {
+    synchronized (operationsPerformedLock)
+    {
       operationsPerformed++;
     }
 
-    ArrayList<Control> opControls;
-    ArrayList<LDAPControl> ldapControls = message.getControls();
-    if ((ldapControls == null) || ldapControls.isEmpty())
-    {
-      opControls = null;
-    }
-    else
-    {
-      opControls = new ArrayList<Control>(ldapControls.size());
-      for (LDAPControl c : ldapControls)
-      {
-        opControls.add(c.getControl());
-      }
-    }
+    List<Control> opControls = message.getControls();
 
+    // FIXME -- See if there is a bind in progress. If so, then deny
+    // most kinds of operations.
 
-    // FIXME -- See if there is a bind in progress.  If so, then deny most
-    // kinds of operations.
-
-
-    // Figure out what type of operation we're dealing with based on the LDAP
-    // message.  Abandon and unbind requests will be processed here.  All other
-    // types of requests will be encapsulated into operations and put into the
-    // work queue to be picked up by a worker thread.  Any other kinds of
-    // LDAP messages (e.g., response messages) are illegal and will result in
-    // the connection being terminated.
+    // Figure out what type of operation we're dealing with based on the
+    // LDAP message. Abandon and unbind requests will be processed here.
+    // All other types of requests will be encapsulated into operations
+    // and append into the work queue to be picked up by a worker
+    // thread. Any other kinds of LDAP messages (e.g., response
+    // messages) are illegal and will result in the connection being
+    // terminated.
     try
     {
       boolean result;
       switch (message.getProtocolOpType())
       {
-        case OP_TYPE_ABANDON_REQUEST:
-          if (keepStats) this.abandonMonitor.start();
-          result=processAbandonRequest(message, opControls);
-          if (keepStats) {
-              this.abandonMonitor.stop();
-              this.abandonMonitor.updateMonitorProvider(statTracker);
-          }
-          return result;
-        case OP_TYPE_ADD_REQUEST:
-          if (keepStats) this.addMonitor.start();
-          result=processAddRequest(message, opControls);
-          if (keepStats) {
-              this.addMonitor.stop();
-              this.addMonitor.updateMonitorProvider(statTracker);
-          }
-          return result;
-        case OP_TYPE_BIND_REQUEST:
-          if (keepStats) this.bindMonitor.start();
-          result=processBindRequest(message, opControls);
-          if (keepStats) {
-              this.bindMonitor.stop();
-              this.bindMonitor.updateMonitorProvider(statTracker);
-          }
-          return result;
-        case OP_TYPE_COMPARE_REQUEST:
-          if (keepStats) this.compareMonitor.start();
-          result=processCompareRequest(message, opControls);
-          if (keepStats) {
-              this.compareMonitor.stop();
-              this.compareMonitor.updateMonitorProvider(statTracker);
-          }
-          return result;
-        case OP_TYPE_DELETE_REQUEST:
-          if (keepStats) this.delMonitor.start();
-          result=processDeleteRequest(message, opControls);
-          if (keepStats) {
-              this.delMonitor.stop();
-              this.delMonitor.updateMonitorProvider(statTracker);
-          }
-          return result;
-        case OP_TYPE_EXTENDED_REQUEST:
-          if (keepStats) this.extendedMonitor.start();
-          result=processExtendedRequest(message, opControls);
-          if (keepStats) {
-              this.extendedMonitor.stop();
-              this.extendedMonitor.updateMonitorProvider(statTracker);
-          }
-          return result;
-        case OP_TYPE_MODIFY_REQUEST:
-          if (keepStats) this.modMonitor.start();
-          result=processModifyRequest(message, opControls);
-          if (keepStats) {
-              this.modMonitor.stop();
-              this.modMonitor.updateMonitorProvider(statTracker);
-          }
-          return result;
-        case OP_TYPE_MODIFY_DN_REQUEST:
-          if (keepStats) this.moddnMonitor.start();
-          result=processModifyDNRequest(message, opControls);
-          if (keepStats) {
-              this.moddnMonitor.stop();
-              this.moddnMonitor.updateMonitorProvider(statTracker);
-          }
-          return result;
-        case OP_TYPE_SEARCH_REQUEST:
-          if (keepStats) this.searchMonitor.start();
-          result=processSearchRequest(message, opControls);
-          if (keepStats) {
-              this.searchMonitor.stop();
-              this.searchMonitor.updateMonitorProvider(statTracker);
-          }
-          return result;
-        case OP_TYPE_UNBIND_REQUEST:
-          if (keepStats) this.unbindMonitor.start();
-          result=processUnbindRequest(message, opControls);
-          if (keepStats) {
-              this.unbindMonitor.stop();
-              this.unbindMonitor.updateMonitorProvider(statTracker);
-          }
-          return result;
-        default:
-          Message msg = ERR_LDAP_DISCONNECT_DUE_TO_INVALID_REQUEST_TYPE.get(
-                  message.getProtocolOpName(), message.getMessageID());
-          disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg);
-          return false;
+      case OP_TYPE_ABANDON_REQUEST:
+        if (keepStats) this.abandonMonitor.start();
+        result = processAbandonRequest(message, opControls);
+        if (keepStats)
+        {
+          this.abandonMonitor.stop();
+          this.abandonMonitor.updateMonitorProvider(statTracker);
+        }
+        return result;
+      case OP_TYPE_ADD_REQUEST:
+        if (keepStats) this.addMonitor.start();
+        result = processAddRequest(message, opControls);
+        if (keepStats)
+        {
+          this.addMonitor.stop();
+          this.addMonitor.updateMonitorProvider(statTracker);
+        }
+        return result;
+      case OP_TYPE_BIND_REQUEST:
+        if (keepStats) this.bindMonitor.start();
+        result = processBindRequest(message, opControls);
+        if (keepStats)
+        {
+          this.bindMonitor.stop();
+          this.bindMonitor.updateMonitorProvider(statTracker);
+        }
+        return result;
+      case OP_TYPE_COMPARE_REQUEST:
+        if (keepStats) this.compareMonitor.start();
+        result = processCompareRequest(message, opControls);
+        if (keepStats)
+        {
+          this.compareMonitor.stop();
+          this.compareMonitor.updateMonitorProvider(statTracker);
+        }
+        return result;
+      case OP_TYPE_DELETE_REQUEST:
+        if (keepStats) this.delMonitor.start();
+        result = processDeleteRequest(message, opControls);
+        if (keepStats)
+        {
+          this.delMonitor.stop();
+          this.delMonitor.updateMonitorProvider(statTracker);
+        }
+        return result;
+      case OP_TYPE_EXTENDED_REQUEST:
+        if (keepStats) this.extendedMonitor.start();
+        result = processExtendedRequest(message, opControls);
+        if (keepStats)
+        {
+          this.extendedMonitor.stop();
+          this.extendedMonitor.updateMonitorProvider(statTracker);
+        }
+        return result;
+      case OP_TYPE_MODIFY_REQUEST:
+        if (keepStats) this.modMonitor.start();
+        result = processModifyRequest(message, opControls);
+        if (keepStats)
+        {
+          this.modMonitor.stop();
+          this.modMonitor.updateMonitorProvider(statTracker);
+        }
+        return result;
+      case OP_TYPE_MODIFY_DN_REQUEST:
+        if (keepStats) this.moddnMonitor.start();
+        result = processModifyDNRequest(message, opControls);
+        if (keepStats)
+        {
+          this.moddnMonitor.stop();
+          this.moddnMonitor.updateMonitorProvider(statTracker);
+        }
+        return result;
+      case OP_TYPE_SEARCH_REQUEST:
+        if (keepStats) this.searchMonitor.start();
+        result = processSearchRequest(message, opControls);
+        if (keepStats)
+        {
+          this.searchMonitor.stop();
+          this.searchMonitor.updateMonitorProvider(statTracker);
+        }
+        return result;
+      case OP_TYPE_UNBIND_REQUEST:
+        if (keepStats) this.unbindMonitor.start();
+        result = processUnbindRequest(message, opControls);
+        if (keepStats)
+        {
+          this.unbindMonitor.stop();
+          this.unbindMonitor.updateMonitorProvider(statTracker);
+        }
+        return result;
+      default:
+        Message msg =
+            ERR_LDAP_DISCONNECT_DUE_TO_INVALID_REQUEST_TYPE.get(message
+                .getProtocolOpName(), message.getMessageID());
+        disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg);
+        return false;
       }
     }
     catch (Exception e)
@@ -1962,9 +1639,10 @@
         TRACER.debugCaught(DebugLogLevel.ERROR, e);
       }
 
-      Message msg = ERR_LDAP_DISCONNECT_DUE_TO_PROCESSING_FAILURE.get(
-              message.getProtocolOpName(),
-              message.getMessageID(), String.valueOf(e));
+      Message msg =
+          ERR_LDAP_DISCONNECT_DUE_TO_PROCESSING_FAILURE.get(message
+              .getProtocolOpName(), message.getMessageID(), String
+              .valueOf(e));
       disconnect(DisconnectReason.SERVER_ERROR, true, msg);
       return false;
     }
@@ -1975,24 +1653,26 @@
   /**
    * Processes the provided LDAP message as an abandon request.
    *
-   * @param  message   The LDAP message containing the abandon request to
-   *                   process.
-   * @param  controls  The set of pre-decoded request controls contained in the
-   *                   message.
-   *
-   * @return  <CODE>true</CODE> if the request was processed successfully, or
-   *          <CODE>false</CODE> if not and the connection has been closed as a
-   *          result (it is the responsibility of this method to close the
-   *          connection).
+   * @param message
+   *          The LDAP message containing the abandon request to
+   *          process.
+   * @param controls
+   *          The set of pre-decoded request controls contained in the
+   *          message.
+   * @return <CODE>true</CODE> if the request was processed
+   *         successfully, or <CODE>false</CODE> if not and the
+   *         connection has been closed as a result (it is the
+   *         responsibility of this method to close the connection).
    */
   private boolean processAbandonRequest(LDAPMessage message,
-                                        ArrayList<Control> controls)
+      List<Control> controls)
   {
-    AbandonRequestProtocolOp protocolOp = message.getAbandonRequestProtocolOp();
+    AbandonRequestProtocolOp protocolOp =
+        message.getAbandonRequestProtocolOp();
     AbandonOperationBasis abandonOp =
-         new AbandonOperationBasis(this, nextOperationID.getAndIncrement(),
-                              message.getMessageID(), controls,
-                              protocolOp.getIDToAbandon());
+        new AbandonOperationBasis(this, nextOperationID
+            .getAndIncrement(), message.getMessageID(), controls,
+            protocolOp.getIDToAbandon());
 
     abandonOp.run();
     if (keepStats && (abandonOp.getResultCode() == ResultCode.CANCELED))
@@ -2008,37 +1688,39 @@
   /**
    * Processes the provided LDAP message as an add request.
    *
-   * @param  message   The LDAP message containing the add request to process.
-   * @param  controls  The set of pre-decoded request controls contained in the
-   *                   message.
-   *
-   * @return  <CODE>true</CODE> if the request was processed successfully, or
-   *          <CODE>false</CODE> if not and the connection has been closed as a
-   *          result (it is the responsibility of this method to close the
-   *          connection).
+   * @param message
+   *          The LDAP message containing the add request to process.
+   * @param controls
+   *          The set of pre-decoded request controls contained in the
+   *          message.
+   * @return <CODE>true</CODE> if the request was processed
+   *         successfully, or <CODE>false</CODE> if not and the
+   *         connection has been closed as a result (it is the
+   *         responsibility of this method to close the connection).
    */
   private boolean processAddRequest(LDAPMessage message,
-                                    ArrayList<Control> controls)
+      List<Control> controls)
   {
-    if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty()))
+    if ((ldapVersion == 2) && (controls != null)
+        && (!controls.isEmpty()))
     {
       // LDAPv2 clients aren't allowed to send controls.
       AddResponseProtocolOp responseOp =
-           new AddResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
-                    ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
-      sendLDAPMessage(securityProvider,
-                      new LDAPMessage(message.getMessageID(), responseOp));
+          new AddResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
+              ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
+      sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+          responseOp));
       disconnect(DisconnectReason.PROTOCOL_ERROR, false,
-                 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
+          ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
       return false;
     }
 
     // Create the add operation and add it into the work queue.
     AddRequestProtocolOp protocolOp = message.getAddRequestProtocolOp();
     AddOperationBasis addOp =
-         new AddOperationBasis(this, nextOperationID.getAndIncrement(),
-                          message.getMessageID(), controls, protocolOp.getDN(),
-                          protocolOp.getAttributes());
+        new AddOperationBasis(this, nextOperationID.getAndIncrement(),
+            message.getMessageID(), controls, protocolOp.getDN(),
+            protocolOp.getAttributes());
 
     try
     {
@@ -2052,24 +1734,14 @@
       }
 
       AddResponseProtocolOp responseOp =
-           new AddResponseProtocolOp(de.getResultCode().getIntValue(),
-                                     de.getMessageObject(), de.getMatchedDN(),
-                                     de.getReferralURLs());
+          new AddResponseProtocolOp(de.getResultCode().getIntValue(),
+              de.getMessageObject(), de.getMatchedDN(), de
+                  .getReferralURLs());
 
-      List<Control> responseControls = addOp.getResponseControls();
-      ArrayList<LDAPControl> responseLDAPControls =
-           new ArrayList<LDAPControl>(responseControls.size());
-      for (Control c : responseControls)
-      {
-        responseLDAPControls.add(new LDAPControl(c));
-      }
-
-      sendLDAPMessage(securityProvider,
-                      new LDAPMessage(message.getMessageID(), responseOp,
-                                      responseLDAPControls));
+      sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+          responseOp, addOp.getResponseControls()));
     }
 
-
     return connectionValid;
   }
 
@@ -2078,91 +1750,92 @@
   /**
    * Processes the provided LDAP message as a bind request.
    *
-   * @param  message   The LDAP message containing the bind request to process.
-   * @param  controls  The set of pre-decoded request controls contained in the
-   *                   message.
-   *
-   * @return  <CODE>true</CODE> if the request was processed successfully, or
-   *          <CODE>false</CODE> if not and the connection has been closed as a
-   *          result (it is the responsibility of this method to close the
-   *          connection).
+   * @param message
+   *          The LDAP message containing the bind request to process.
+   * @param controls
+   *          The set of pre-decoded request controls contained in the
+   *          message.
+   * @return <CODE>true</CODE> if the request was processed
+   *         successfully, or <CODE>false</CODE> if not and the
+   *         connection has been closed as a result (it is the
+   *         responsibility of this method to close the connection).
    */
   private boolean processBindRequest(LDAPMessage message,
-                                     ArrayList<Control> controls)
+      List<Control> controls)
   {
-    BindRequestProtocolOp protocolOp = message.getBindRequestProtocolOp();
+    BindRequestProtocolOp protocolOp =
+        message.getBindRequestProtocolOp();
 
-    // See if this is an LDAPv2 bind request, and if so whether that should be
-    // allowed.
+    // See if this is an LDAPv2 bind request, and if so whether that
+    // should be allowed.
     String versionString;
     switch (ldapVersion = protocolOp.getProtocolVersion())
     {
-      case 2:
-        versionString = "2";
+    case 2:
+      versionString = "2";
 
-        if (! connectionHandler.allowLDAPv2())
-        {
-          BindResponseProtocolOp responseOp =
-               new BindResponseProtocolOp(
-                        LDAPResultCode.INAPPROPRIATE_AUTHENTICATION,
-                        ERR_LDAPV2_CLIENTS_NOT_ALLOWED.get());
-          sendLDAPMessage(securityProvider,
-                          new LDAPMessage(message.getMessageID(), responseOp));
-          disconnect(DisconnectReason.PROTOCOL_ERROR, false,
-                     ERR_LDAPV2_CLIENTS_NOT_ALLOWED.get());
-          return false;
-        }
+      if (!connectionHandler.allowLDAPv2())
+      {
+        BindResponseProtocolOp responseOp =
+            new BindResponseProtocolOp(
+                LDAPResultCode.INAPPROPRIATE_AUTHENTICATION,
+                ERR_LDAPV2_CLIENTS_NOT_ALLOWED.get());
+        sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+            responseOp));
+        disconnect(DisconnectReason.PROTOCOL_ERROR, false,
+            ERR_LDAPV2_CLIENTS_NOT_ALLOWED.get());
+        return false;
+      }
 
-        if ((controls != null) && (! controls.isEmpty()))
-        {
-          // LDAPv2 clients aren't allowed to send controls.
-          BindResponseProtocolOp responseOp =
-               new BindResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
-                        ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
-          sendLDAPMessage(securityProvider,
-                          new LDAPMessage(message.getMessageID(), responseOp));
-          disconnect(DisconnectReason.PROTOCOL_ERROR, false,
-                     ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
-          return false;
-        }
+      if ((controls != null) && (!controls.isEmpty()))
+      {
+        // LDAPv2 clients aren't allowed to send controls.
+        BindResponseProtocolOp responseOp =
+            new BindResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
+                ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
+        sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+            responseOp));
+        disconnect(DisconnectReason.PROTOCOL_ERROR, false,
+            ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
+        return false;
+      }
 
-        break;
-      case 3:
-        versionString = "3";
-        break;
-      default:
-        versionString = String.valueOf(ldapVersion);
-        break;
+      break;
+    case 3:
+      versionString = "3";
+      break;
+    default:
+      versionString = String.valueOf(ldapVersion);
+      break;
     }
 
-
-    ASN1OctetString bindDN = protocolOp.getDN();
+    ByteString bindDN = protocolOp.getDN();
 
     BindOperationBasis bindOp;
     switch (protocolOp.getAuthenticationType())
     {
-      case SIMPLE:
-        bindOp = new BindOperationBasis(this, nextOperationID.getAndIncrement(),
-                                   message.getMessageID(), controls,
-                                   versionString, bindDN,
-                                   protocolOp.getSimplePassword());
-        break;
-      case SASL:
-        bindOp = new BindOperationBasis(this, nextOperationID.getAndIncrement(),
-                                   message.getMessageID(), controls,
-                                   versionString, bindDN,
-                                   protocolOp.getSASLMechanism(),
-                                   protocolOp.getSASLCredentials());
-        break;
-      default:
-        // This is an invalid authentication type, and therefore a protocol
-        // error.  As per RFC 2251, a protocol error in a bind request must
-        // result in terminating the connection.
-        Message msg =
-                ERR_LDAP_INVALID_BIND_AUTH_TYPE.get(message.getMessageID(),
-                          String.valueOf(protocolOp.getAuthenticationType()));
-        disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg);
-        return false;
+    case SIMPLE:
+      bindOp =
+          new BindOperationBasis(this, nextOperationID
+              .getAndIncrement(), message.getMessageID(), controls,
+              versionString, bindDN, protocolOp.getSimplePassword());
+      break;
+    case SASL:
+      bindOp =
+          new BindOperationBasis(this, nextOperationID
+              .getAndIncrement(), message.getMessageID(), controls,
+              versionString, bindDN, protocolOp.getSASLMechanism(),
+              protocolOp.getSASLCredentials());
+      break;
+    default:
+      // This is an invalid authentication type, and therefore a
+      // protocol error. As per RFC 2251, a protocol error in a bind
+      // request must result in terminating the connection.
+      Message msg =
+          ERR_LDAP_INVALID_BIND_AUTH_TYPE.get(message.getMessageID(),
+              String.valueOf(protocolOp.getAuthenticationType()));
+      disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg);
+      return false;
     }
 
     // Add the operation into the work queue.
@@ -2178,32 +1851,23 @@
       }
 
       BindResponseProtocolOp responseOp =
-           new BindResponseProtocolOp(de.getResultCode().getIntValue(),
-                                      de.getMessageObject(), de.getMatchedDN(),
-                                      de.getReferralURLs());
+          new BindResponseProtocolOp(de.getResultCode().getIntValue(),
+              de.getMessageObject(), de.getMatchedDN(), de
+                  .getReferralURLs());
 
-      List<Control> responseControls = bindOp.getResponseControls();
-      ArrayList<LDAPControl> responseLDAPControls =
-           new ArrayList<LDAPControl>(responseControls.size());
-      for (Control c : responseControls)
-      {
-        responseLDAPControls.add(new LDAPControl(c));
-      }
-
-      sendLDAPMessage(securityProvider,
-                      new LDAPMessage(message.getMessageID(), responseOp,
-                                      responseLDAPControls));
+      sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+          responseOp, bindOp.getResponseControls()));
 
       // If it was a protocol error, then terminate the connection.
       if (de.getResultCode() == ResultCode.PROTOCOL_ERROR)
       {
-        Message msg = ERR_LDAP_DISCONNECT_DUE_TO_BIND_PROTOCOL_ERROR.get(
-                message.getMessageID(), de.getMessageObject());
+        Message msg =
+            ERR_LDAP_DISCONNECT_DUE_TO_BIND_PROTOCOL_ERROR.get(message
+                .getMessageID(), de.getMessageObject());
         disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg);
       }
     }
 
-
     return connectionValid;
   }
 
@@ -2212,38 +1876,41 @@
   /**
    * Processes the provided LDAP message as a compare request.
    *
-   * @param  message   The LDAP message containing the compare request to
-   *                   process.
-   * @param  controls  The set of pre-decoded request controls contained in the
-   *                   message.
-   *
-   * @return  <CODE>true</CODE> if the request was processed successfully, or
-   *          <CODE>false</CODE> if not and the connection has been closed as a
-   *          result (it is the responsibility of this method to close the
-   *          connection).
+   * @param message
+   *          The LDAP message containing the compare request to
+   *          process.
+   * @param controls
+   *          The set of pre-decoded request controls contained in the
+   *          message.
+   * @return <CODE>true</CODE> if the request was processed
+   *         successfully, or <CODE>false</CODE> if not and the
+   *         connection has been closed as a result (it is the
+   *         responsibility of this method to close the connection).
    */
   private boolean processCompareRequest(LDAPMessage message,
-                                        ArrayList<Control> controls)
+      List<Control> controls)
   {
-    if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty()))
+    if ((ldapVersion == 2) && (controls != null)
+        && (!controls.isEmpty()))
     {
       // LDAPv2 clients aren't allowed to send controls.
       CompareResponseProtocolOp responseOp =
-           new CompareResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
-                    ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
-      sendLDAPMessage(securityProvider,
-                      new LDAPMessage(message.getMessageID(), responseOp));
+          new CompareResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
+              ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
+      sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+          responseOp));
       disconnect(DisconnectReason.PROTOCOL_ERROR, false,
-                 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
+          ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
       return false;
     }
 
-    CompareRequestProtocolOp protocolOp = message.getCompareRequestProtocolOp();
+    CompareRequestProtocolOp protocolOp =
+        message.getCompareRequestProtocolOp();
     CompareOperationBasis compareOp =
-         new CompareOperationBasis(this, nextOperationID.getAndIncrement(),
-                              message.getMessageID(), controls,
-                              protocolOp.getDN(), protocolOp.getAttributeType(),
-                              protocolOp.getAssertionValue());
+        new CompareOperationBasis(this, nextOperationID
+            .getAndIncrement(), message.getMessageID(), controls,
+            protocolOp.getDN(), protocolOp.getAttributeType(),
+            protocolOp.getAssertionValue());
 
     // Add the operation into the work queue.
     try
@@ -2258,25 +1925,14 @@
       }
 
       CompareResponseProtocolOp responseOp =
-           new CompareResponseProtocolOp(de.getResultCode().getIntValue(),
-                                         de.getMessageObject(),
-                                         de.getMatchedDN(),
-                                         de.getReferralURLs());
+          new CompareResponseProtocolOp(de.getResultCode()
+              .getIntValue(), de.getMessageObject(), de.getMatchedDN(),
+              de.getReferralURLs());
 
-      List<Control> responseControls = compareOp.getResponseControls();
-      ArrayList<LDAPControl> responseLDAPControls =
-           new ArrayList<LDAPControl>(responseControls.size());
-      for (Control c : responseControls)
-      {
-        responseLDAPControls.add(new LDAPControl(c));
-      }
-
-      sendLDAPMessage(securityProvider,
-                      new LDAPMessage(message.getMessageID(), responseOp,
-                                      responseLDAPControls));
+      sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+          responseOp, compareOp.getResponseControls()));
     }
 
-
     return connectionValid;
   }
 
@@ -2285,37 +1941,39 @@
   /**
    * Processes the provided LDAP message as a delete request.
    *
-   * @param  message   The LDAP message containing the delete request to
-   *                   process.
-   * @param  controls  The set of pre-decoded request controls contained in the
-   *                   message.
-   *
-   * @return  <CODE>true</CODE> if the request was processed successfully, or
-   *          <CODE>false</CODE> if not and the connection has been closed as a
-   *          result (it is the responsibility of this method to close the
-   *          connection).
+   * @param message
+   *          The LDAP message containing the delete request to process.
+   * @param controls
+   *          The set of pre-decoded request controls contained in the
+   *          message.
+   * @return <CODE>true</CODE> if the request was processed
+   *         successfully, or <CODE>false</CODE> if not and the
+   *         connection has been closed as a result (it is the
+   *         responsibility of this method to close the connection).
    */
   private boolean processDeleteRequest(LDAPMessage message,
-                                       ArrayList<Control> controls)
+      List<Control> controls)
   {
-    if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty()))
+    if ((ldapVersion == 2) && (controls != null)
+        && (!controls.isEmpty()))
     {
       // LDAPv2 clients aren't allowed to send controls.
       DeleteResponseProtocolOp responseOp =
-           new DeleteResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
-                    ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
-      sendLDAPMessage(securityProvider,
-                      new LDAPMessage(message.getMessageID(), responseOp));
+          new DeleteResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
+              ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
+      sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+          responseOp));
       disconnect(DisconnectReason.PROTOCOL_ERROR, false,
-                 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
+          ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
       return false;
     }
 
-    DeleteRequestProtocolOp protocolOp = message.getDeleteRequestProtocolOp();
+    DeleteRequestProtocolOp protocolOp =
+        message.getDeleteRequestProtocolOp();
     DeleteOperationBasis deleteOp =
-         new DeleteOperationBasis(this, nextOperationID.getAndIncrement(),
-                             message.getMessageID(), controls,
-                             protocolOp.getDN());
+        new DeleteOperationBasis(this, nextOperationID
+            .getAndIncrement(), message.getMessageID(), controls,
+            protocolOp.getDN());
 
     // Add the operation into the work queue.
     try
@@ -2330,25 +1988,14 @@
       }
 
       DeleteResponseProtocolOp responseOp =
-           new DeleteResponseProtocolOp(de.getResultCode().getIntValue(),
-                                        de.getMessageObject(),
-                                        de.getMatchedDN(),
-                                        de.getReferralURLs());
+          new DeleteResponseProtocolOp(
+              de.getResultCode().getIntValue(), de.getMessageObject(),
+              de.getMatchedDN(), de.getReferralURLs());
 
-      List<Control> responseControls = deleteOp.getResponseControls();
-      ArrayList<LDAPControl> responseLDAPControls =
-           new ArrayList<LDAPControl>(responseControls.size());
-      for (Control c : responseControls)
-      {
-        responseLDAPControls.add(new LDAPControl(c));
-      }
-
-      sendLDAPMessage(securityProvider,
-                      new LDAPMessage(message.getMessageID(), responseOp,
-                                      responseLDAPControls));
+      sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+          responseOp, deleteOp.getResponseControls()));
     }
 
-
     return connectionValid;
   }
 
@@ -2357,43 +2004,44 @@
   /**
    * Processes the provided LDAP message as an extended request.
    *
-   * @param  message   The LDAP message containing the extended request to
-   *                   process.
-   * @param  controls  The set of pre-decoded request controls contained in the
-   *                   message.
-   *
-   * @return  <CODE>true</CODE> if the request was processed successfully, or
-   *          <CODE>false</CODE> if not and the connection has been closed as a
-   *          result (it is the responsibility of this method to close the
-   *          connection).
+   * @param message
+   *          The LDAP message containing the extended request to
+   *          process.
+   * @param controls
+   *          The set of pre-decoded request controls contained in the
+   *          message.
+   * @return <CODE>true</CODE> if the request was processed
+   *         successfully, or <CODE>false</CODE> if not and the
+   *         connection has been closed as a result (it is the
+   *         responsibility of this method to close the connection).
    */
   private boolean processExtendedRequest(LDAPMessage message,
-                                         ArrayList<Control> controls)
+      List<Control> controls)
   {
-    // See if this is an LDAPv2 client.  If it is, then they should not be
-    // issuing extended requests.  We can't send a response that we can be sure
-    // they can understand, so we have no choice but to close the connection.
+    // See if this is an LDAPv2 client. If it is, then they should not
+    // be issuing extended requests. We can't send a response that we
+    // can be sure they can understand, so we have no choice but to
+    // close the connection.
     if (ldapVersion == 2)
     {
-      Message msg = ERR_LDAPV2_EXTENDED_REQUEST_NOT_ALLOWED.get(
-          getConnectionID(), message.getMessageID());
+      Message msg =
+          ERR_LDAPV2_EXTENDED_REQUEST_NOT_ALLOWED.get(
+              getConnectionID(), message.getMessageID());
       logError(msg);
       disconnect(DisconnectReason.PROTOCOL_ERROR, false, msg);
       return false;
     }
 
-
     // FIXME -- Do we need to handle certain types of request here?
     // -- StartTLS requests
     // -- Cancel requests
 
-
     ExtendedRequestProtocolOp protocolOp =
-         message.getExtendedRequestProtocolOp();
+        message.getExtendedRequestProtocolOp();
     ExtendedOperationBasis extendedOp =
-         new ExtendedOperationBasis(this, nextOperationID.getAndIncrement(),
-                               message.getMessageID(), controls,
-                               protocolOp.getOID(), protocolOp.getValue());
+        new ExtendedOperationBasis(this, nextOperationID
+            .getAndIncrement(), message.getMessageID(), controls,
+            protocolOp.getOID(), protocolOp.getValue());
 
     // Add the operation into the work queue.
     try
@@ -2408,25 +2056,14 @@
       }
 
       ExtendedResponseProtocolOp responseOp =
-           new ExtendedResponseProtocolOp(de.getResultCode().getIntValue(),
-                                          de.getMessageObject(),
-                                          de.getMatchedDN(),
-                                          de.getReferralURLs());
+          new ExtendedResponseProtocolOp(de.getResultCode()
+              .getIntValue(), de.getMessageObject(), de.getMatchedDN(),
+              de.getReferralURLs());
 
-      List<Control> responseControls = extendedOp.getResponseControls();
-      ArrayList<LDAPControl> responseLDAPControls =
-           new ArrayList<LDAPControl>(responseControls.size());
-      for (Control c : responseControls)
-      {
-        responseLDAPControls.add(new LDAPControl(c));
-      }
-
-      sendLDAPMessage(securityProvider,
-                      new LDAPMessage(message.getMessageID(), responseOp,
-                                      responseLDAPControls));
+      sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+          responseOp, extendedOp.getResponseControls()));
     }
 
-
     return connectionValid;
   }
 
@@ -2435,37 +2072,39 @@
   /**
    * Processes the provided LDAP message as a modify request.
    *
-   * @param  message   The LDAP message containing the modify request to
-   *                   process.
-   * @param  controls  The set of pre-decoded request controls contained in the
-   *                   message.
-   *
-   * @return  <CODE>true</CODE> if the request was processed successfully, or
-   *          <CODE>false</CODE> if not and the connection has been closed as a
-   *          result (it is the responsibility of this method to close the
-   *          connection).
+   * @param message
+   *          The LDAP message containing the modify request to process.
+   * @param controls
+   *          The set of pre-decoded request controls contained in the
+   *          message.
+   * @return <CODE>true</CODE> if the request was processed
+   *         successfully, or <CODE>false</CODE> if not and the
+   *         connection has been closed as a result (it is the
+   *         responsibility of this method to close the connection).
    */
   private boolean processModifyRequest(LDAPMessage message,
-                                       ArrayList<Control> controls)
+      List<Control> controls)
   {
-    if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty()))
+    if ((ldapVersion == 2) && (controls != null)
+        && (!controls.isEmpty()))
     {
       // LDAPv2 clients aren't allowed to send controls.
       ModifyResponseProtocolOp responseOp =
-           new ModifyResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
-                    ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
-      sendLDAPMessage(securityProvider,
-                      new LDAPMessage(message.getMessageID(), responseOp));
+          new ModifyResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
+              ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
+      sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+          responseOp));
       disconnect(DisconnectReason.PROTOCOL_ERROR, false,
-                 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
+          ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
       return false;
     }
 
-    ModifyRequestProtocolOp protocolOp = message.getModifyRequestProtocolOp();
+    ModifyRequestProtocolOp protocolOp =
+        message.getModifyRequestProtocolOp();
     ModifyOperationBasis modifyOp =
-         new ModifyOperationBasis(this, nextOperationID.getAndIncrement(),
-                             message.getMessageID(), controls,
-                             protocolOp.getDN(), protocolOp.getModifications());
+        new ModifyOperationBasis(this, nextOperationID
+            .getAndIncrement(), message.getMessageID(), controls,
+            protocolOp.getDN(), protocolOp.getModifications());
 
     // Add the operation into the work queue.
     try
@@ -2480,25 +2119,14 @@
       }
 
       ModifyResponseProtocolOp responseOp =
-           new ModifyResponseProtocolOp(de.getResultCode().getIntValue(),
-                                        de.getMessageObject(),
-                                        de.getMatchedDN(),
-                                        de.getReferralURLs());
+          new ModifyResponseProtocolOp(
+              de.getResultCode().getIntValue(), de.getMessageObject(),
+              de.getMatchedDN(), de.getReferralURLs());
 
-      List<Control> responseControls = modifyOp.getResponseControls();
-      ArrayList<LDAPControl> responseLDAPControls =
-           new ArrayList<LDAPControl>(responseControls.size());
-      for (Control c : responseControls)
-      {
-        responseLDAPControls.add(new LDAPControl(c));
-      }
-
-      sendLDAPMessage(securityProvider,
-                      new LDAPMessage(message.getMessageID(), responseOp,
-                                      responseLDAPControls));
+      sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+          responseOp, modifyOp.getResponseControls()));
     }
 
-
     return connectionValid;
   }
 
@@ -2507,40 +2135,41 @@
   /**
    * Processes the provided LDAP message as a modify DN request.
    *
-   * @param  message   The LDAP message containing the modify DN request to
-   *                   process.
-   * @param  controls  The set of pre-decoded request controls contained in the
-   *                   message.
-   *
-   * @return  <CODE>true</CODE> if the request was processed successfully, or
-   *          <CODE>false</CODE> if not and the connection has been closed as a
-   *          result (it is the responsibility of this method to close the
-   *          connection).
+   * @param message
+   *          The LDAP message containing the modify DN request to
+   *          process.
+   * @param controls
+   *          The set of pre-decoded request controls contained in the
+   *          message.
+   * @return <CODE>true</CODE> if the request was processed
+   *         successfully, or <CODE>false</CODE> if not and the
+   *         connection has been closed as a result (it is the
+   *         responsibility of this method to close the connection).
    */
   private boolean processModifyDNRequest(LDAPMessage message,
-                                         ArrayList<Control> controls)
+      List<Control> controls)
   {
-    if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty()))
+    if ((ldapVersion == 2) && (controls != null)
+        && (!controls.isEmpty()))
     {
       // LDAPv2 clients aren't allowed to send controls.
       ModifyDNResponseProtocolOp responseOp =
-           new ModifyDNResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
-                    ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
-      sendLDAPMessage(securityProvider,
-                      new LDAPMessage(message.getMessageID(), responseOp));
+          new ModifyDNResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
+              ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
+      sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+          responseOp));
       disconnect(DisconnectReason.PROTOCOL_ERROR, false,
-                 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
+          ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
       return false;
     }
 
     ModifyDNRequestProtocolOp protocolOp =
-         message.getModifyDNRequestProtocolOp();
+        message.getModifyDNRequestProtocolOp();
     ModifyDNOperationBasis modifyDNOp =
-         new ModifyDNOperationBasis(this, nextOperationID.getAndIncrement(),
-                               message.getMessageID(), controls,
-                               protocolOp.getEntryDN(), protocolOp.getNewRDN(),
-                               protocolOp.deleteOldRDN(),
-                               protocolOp.getNewSuperior());
+        new ModifyDNOperationBasis(this, nextOperationID
+            .getAndIncrement(), message.getMessageID(), controls,
+            protocolOp.getEntryDN(), protocolOp.getNewRDN(), protocolOp
+                .deleteOldRDN(), protocolOp.getNewSuperior());
 
     // Add the operation into the work queue.
     try
@@ -2555,25 +2184,14 @@
       }
 
       ModifyDNResponseProtocolOp responseOp =
-           new ModifyDNResponseProtocolOp(de.getResultCode().getIntValue(),
-                                          de.getMessageObject(),
-                                          de.getMatchedDN(),
-                                          de.getReferralURLs());
+          new ModifyDNResponseProtocolOp(de.getResultCode()
+              .getIntValue(), de.getMessageObject(), de.getMatchedDN(),
+              de.getReferralURLs());
 
-      List<Control> responseControls = modifyDNOp.getResponseControls();
-      ArrayList<LDAPControl> responseLDAPControls =
-           new ArrayList<LDAPControl>(responseControls.size());
-      for (Control c : responseControls)
-      {
-        responseLDAPControls.add(new LDAPControl(c));
-      }
-
-      sendLDAPMessage(securityProvider,
-                      new LDAPMessage(message.getMessageID(), responseOp,
-                                      responseLDAPControls));
+      sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+          responseOp, modifyDNOp.getResponseControls()));
     }
 
-
     return connectionValid;
   }
 
@@ -2582,42 +2200,42 @@
   /**
    * Processes the provided LDAP message as a search request.
    *
-   * @param  message   The LDAP message containing the search request to
-   *                   process.
-   * @param  controls  The set of pre-decoded request controls contained in the
-   *                   message.
-   *
-   * @return  <CODE>true</CODE> if the request was processed successfully, or
-   *          <CODE>false</CODE> if not and the connection has been closed as a
-   *          result (it is the responsibility of this method to close the
-   *          connection).
+   * @param message
+   *          The LDAP message containing the search request to process.
+   * @param controls
+   *          The set of pre-decoded request controls contained in the
+   *          message.
+   * @return <CODE>true</CODE> if the request was processed
+   *         successfully, or <CODE>false</CODE> if not and the
+   *         connection has been closed as a result (it is the
+   *         responsibility of this method to close the connection).
    */
   private boolean processSearchRequest(LDAPMessage message,
-                                       ArrayList<Control> controls)
+      List<Control> controls)
   {
-    if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty()))
+    if ((ldapVersion == 2) && (controls != null)
+        && (!controls.isEmpty()))
     {
       // LDAPv2 clients aren't allowed to send controls.
       SearchResultDoneProtocolOp responseOp =
-           new SearchResultDoneProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
-                    ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
-      sendLDAPMessage(securityProvider,
-                      new LDAPMessage(message.getMessageID(), responseOp));
+          new SearchResultDoneProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
+              ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
+      sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+          responseOp));
       disconnect(DisconnectReason.PROTOCOL_ERROR, false,
-                 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
+          ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
       return false;
     }
 
-    SearchRequestProtocolOp protocolOp = message.getSearchRequestProtocolOp();
+    SearchRequestProtocolOp protocolOp =
+        message.getSearchRequestProtocolOp();
     SearchOperationBasis searchOp =
-         new SearchOperationBasis(this, nextOperationID.getAndIncrement(),
-                             message.getMessageID(), controls,
-                             protocolOp.getBaseDN(), protocolOp.getScope(),
-                             protocolOp.getDereferencePolicy(),
-                             protocolOp.getSizeLimit(),
-                             protocolOp.getTimeLimit(),
-                             protocolOp.getTypesOnly(), protocolOp.getFilter(),
-                             protocolOp.getAttributes());
+        new SearchOperationBasis(this, nextOperationID
+            .getAndIncrement(), message.getMessageID(), controls,
+            protocolOp.getBaseDN(), protocolOp.getScope(), protocolOp
+                .getDereferencePolicy(), protocolOp.getSizeLimit(),
+            protocolOp.getTimeLimit(), protocolOp.getTypesOnly(),
+            protocolOp.getFilter(), protocolOp.getAttributes());
 
     // Add the operation into the work queue.
     try
@@ -2632,25 +2250,14 @@
       }
 
       SearchResultDoneProtocolOp responseOp =
-           new SearchResultDoneProtocolOp(de.getResultCode().getIntValue(),
-                                          de.getMessageObject(),
-                                          de.getMatchedDN(),
-                                          de.getReferralURLs());
+          new SearchResultDoneProtocolOp(de.getResultCode()
+              .getIntValue(), de.getMessageObject(), de.getMatchedDN(),
+              de.getReferralURLs());
 
-      List<Control> responseControls = searchOp.getResponseControls();
-      ArrayList<LDAPControl> responseLDAPControls =
-           new ArrayList<LDAPControl>(responseControls.size());
-      for (Control c : responseControls)
-      {
-        responseLDAPControls.add(new LDAPControl(c));
-      }
-
-      sendLDAPMessage(securityProvider,
-                      new LDAPMessage(message.getMessageID(), responseOp,
-                                      responseLDAPControls));
+      sendLDAPMessage(new LDAPMessage(message.getMessageID(),
+          responseOp, searchOp.getResponseControls()));
     }
 
-
     return connectionValid;
   }
 
@@ -2659,22 +2266,22 @@
   /**
    * Processes the provided LDAP message as an unbind request.
    *
-   * @param  message   The LDAP message containing the unbind request to
-   *                   process.
-   * @param  controls  The set of pre-decoded request controls contained in the
-   *                   message.
-   *
-   * @return  <CODE>true</CODE> if the request was processed successfully, or
-   *          <CODE>false</CODE> if not and the connection has been closed as a
-   *          result (it is the responsibility of this method to close the
-   *          connection).
+   * @param message
+   *          The LDAP message containing the unbind request to process.
+   * @param controls
+   *          The set of pre-decoded request controls contained in the
+   *          message.
+   * @return <CODE>true</CODE> if the request was processed
+   *         successfully, or <CODE>false</CODE> if not and the
+   *         connection has been closed as a result (it is the
+   *         responsibility of this method to close the connection).
    */
   private boolean processUnbindRequest(LDAPMessage message,
-                                       ArrayList<Control> controls)
+      List<Control> controls)
   {
     UnbindOperationBasis unbindOp =
-         new UnbindOperationBasis(this, nextOperationID.getAndIncrement(),
-                              message.getMessageID(), controls);
+        new UnbindOperationBasis(this, nextOperationID
+            .getAndIncrement(), message.getMessageID(), controls);
 
     unbindOp.run();
 
@@ -2687,6 +2294,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public String getMonitorSummary()
   {
     StringBuilder buffer = new StringBuilder();
@@ -2713,9 +2321,9 @@
     }
 
     buffer.append("\" security=\"");
-    if (securityProvider.isSecure())
+    if (isSecure())
     {
-      buffer.append(securityProvider.getSecurityMechanismName());
+      buffer.append(activeProvider.getName());
     }
     else
     {
@@ -2732,11 +2340,13 @@
 
 
   /**
-   * Appends a string representation of this client connection to the provided
-   * buffer.
+   * Appends a string representation of this client connection to the
+   * provided buffer.
    *
-   * @param  buffer  The buffer to which the information should be appended.
+   * @param buffer
+   *          The buffer to which the information should be appended.
    */
+  @Override
   public void toString(StringBuilder buffer)
   {
     buffer.append("LDAP client connection from ");
@@ -2752,218 +2362,90 @@
 
 
   /**
-   * Indicates whether TLS protection is actually available for the underlying
-   * client connection.  If there is any reason that TLS protection cannot be
-   * enabled on this client connection, then it should be appended to the
-   * provided buffer.
+   * Indicates whether TLS protection is actually available for the
+   * underlying client connection. If there is any reason that TLS
+   * protection cannot be enabled on this client connection, then it
+   * should be appended to the provided buffer.
    *
-   * @param  unavailableReason  The buffer used to hold the reason that TLS is
-   *                            not available on the underlying client
-   *                            connection.
-   *
-   * @return  <CODE>true</CODE> if TLS is available on the underlying client
-   *          connection, or <CODE>false</CODE> if it is not.
+   * @param unavailableReason
+   *          The buffer used to hold the reason that TLS is not
+   *          available on the underlying client connection.
+   * @return <CODE>true</CODE> if TLS is available on the underlying
+   *         client connection, or <CODE>false</CODE> if it is not.
    */
-  public boolean tlsProtectionAvailable(MessageBuilder unavailableReason)
+  public boolean isTLSAvailable(MessageBuilder unavailableReason)
   {
-    // Make sure that this client connection does not already have some other
-    // security provider enabled.
-    if (! (securityProvider instanceof NullConnectionSecurityProvider))
+    if (isSecure() && activeProvider.getName().equals("TLS"))
     {
-
-      unavailableReason.append(ERR_LDAP_TLS_EXISTING_SECURITY_PROVIDER.get(
-              securityProvider.getSecurityMechanismName()));
+      // TODO SASLPhase2 more general message
+      unavailableReason.append(ERR_LDAP_TLS_EXISTING_SECURITY_PROVIDER
+          .get(activeProvider.getName()));
       return false;
     }
-
-
-    // Make sure that the connection handler allows the use of the StartTLS
-    // operation.
-    if (! connectionHandler.allowStartTLS())
+    // Make sure that the connection handler allows the use of the
+    // StartTLS operation.
+    if (!connectionHandler.allowStartTLS())
     {
-
       unavailableReason.append(ERR_LDAP_TLS_STARTTLS_NOT_ALLOWED.get());
       return false;
     }
-
-
-    // Make sure that the TLS security provider is available.
-    if (tlsSecurityProvider == null)
+    try
     {
-      try
-      {
-        TLSConnectionSecurityProvider tlsProvider =
-             new TLSConnectionSecurityProvider();
-        tlsProvider.initializeConnectionSecurityProvider(null);
-        tlsProvider.setSSLClientAuthPolicy(
-             connectionHandler.getSSLClientAuthPolicy());
-        tlsProvider.setEnabledProtocols(
-             connectionHandler.getEnabledSSLProtocols());
-        tlsProvider.setEnabledCipherSuites(
-             connectionHandler.getEnabledSSLCipherSuites());
-
-        tlsSecurityProvider = (TLSConnectionSecurityProvider)
-                              tlsProvider.newInstance(this, clientChannel);
-      }
-      catch (Exception e)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, e);
-        }
-
-        tlsSecurityProvider = null;
-
-
-        unavailableReason.append(ERR_LDAP_TLS_CANNOT_CREATE_TLS_PROVIDER.get(
-                stackTraceToSingleLineString(e)));
-        return false;
-      }
+      TLSByteChannel tlsByteChannel =
+          connectionHandler.getTLSByteChannel(this, clientChannel);
+      setTLSPendingProvider(tlsByteChannel);
     }
-
-
-    // If we've gotten here, then everything looks OK.
+    catch (DirectoryException de)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, de);
+      }
+      unavailableReason.append(ERR_LDAP_TLS_CANNOT_CREATE_TLS_PROVIDER
+          .get(stackTraceToSingleLineString(de)));
+      return false;
+    }
     return true;
   }
 
 
 
   /**
-   * Installs the TLS connection security provider on this client connection.
-   * If an error occurs in the process, then the underlying client connection
-   * must be terminated and an exception must be thrown to indicate the
-   * underlying cause.
-   *
-   * @throws  DirectoryException  If the TLS connection security provider could
-   *                              not be enabled and the underlying connection
-   *                              has been closed.
-   */
-  public void enableTLSConnectionSecurityProvider()
-         throws DirectoryException
-  {
-    if (tlsSecurityProvider == null)
-    {
-      Message message = ERR_LDAP_TLS_NO_PROVIDER.get();
-
-      disconnect(DisconnectReason.OTHER, false, message);
-      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-                                   message);
-    }
-
-    clearSecurityProvider = securityProvider;
-    setConnectionSecurityProvider(tlsSecurityProvider);
-  }
-
-
-
-  /**
-   * Disables the TLS connection security provider on this client connection.
-   * This must also eliminate any authentication that had been performed on the
-   * client connection so that it is in an anonymous state.  If a problem occurs
-   * while attempting to revert the connection to a non-TLS-protected state,
-   * then an exception must be thrown and the client connection must be
-   * terminated.
-   *
-   * @throws  DirectoryException  If TLS protection cannot be reverted and the
-   *                              underlying client connection has been closed.
-   */
-  public void disableTLSConnectionSecurityProvider()
-         throws DirectoryException
-  {
-    Message message = ERR_LDAP_TLS_CLOSURE_NOT_ALLOWED.get();
-
-    disconnect(DisconnectReason.OTHER, false, message);
-    throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-                                 message);
-  }
-
-
-
-  /**
    * Sends a response to the client in the clear rather than through the
-   * encrypted channel.  This should only be used when processing the StartTLS
-   * extended operation to send the response in the clear after the TLS
-   * negotiation has already been initiated.
+   * encrypted channel. This should only be used when processing the
+   * StartTLS extended operation to send the response in the clear after
+   * the TLS negotiation has already been initiated.
    *
-   * @param  operation  The operation for which to send the response in the
-   *                    clear.
-   *
-   *
-   * @throws  DirectoryException  If a problem occurs while sending the response
-   *                              in the clear.
+   * @param operation
+   *          The operation for which to send the response in the clear.
+   * @throws DirectoryException
+   *           If a problem occurs while sending the response in the
+   *           clear.
    */
   public void sendClearResponse(Operation operation)
-         throws DirectoryException
+      throws DirectoryException
   {
-    if (clearSecurityProvider == null)
-    {
-      Message message = ERR_LDAP_NO_CLEAR_SECURITY_PROVIDER.get(toString());
-      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-                                   message);
-    }
-
-    sendLDAPMessage(clearSecurityProvider,
-                    operationToResponseLDAPMessage(operation));
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public DN getKeyManagerProviderDN()
-  {
-    return connectionHandler.getKeyManagerProviderDN();
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public DN getTrustManagerProviderDN()
-  {
-    return connectionHandler.getTrustManagerProviderDN();
-  }
-
-
-
-  /**
-   * Retrieves the alias of the server certificate that should be used
-   * for operations requiring a server certificate.  The default
-   * implementation returns {@code null} to indicate that any alias is
-   * acceptable.
-   *
-   * @return  The alias of the server certificate that should be used
-   *          for operations requring a server certificate, or
-   *          {@code null} if any alias is acceptable.
-   */
-  @Override
-  public String getCertificateAlias()
-  {
-    return connectionHandler.getSSLServerCertNickname();
+    sendLDAPMessage(operationToResponseLDAPMessage(operation));
   }
 
 
 
   /**
    * Retrieves the length of time in milliseconds that this client
-   * connection has been idle.
-   * <BR><BR>
+   * connection has been idle. <BR>
+   * <BR>
    * Note that the default implementation will always return zero.
-   * Subclasses associated with connection handlers should override
-   * this method if they wish to provided idle time limit
-   * functionality.
+   * Subclasses associated with connection handlers should override this
+   * method if they wish to provided idle time limit functionality.
    *
-   * @return  The length of time in milliseconds that this client
-   *          connection has been idle.
+   * @return The length of time in milliseconds that this client
+   *         connection has been idle.
    */
   @Override
   public long getIdleTime()
   {
-    if (operationsInProgress.isEmpty() && getPersistentSearches().isEmpty())
+    if (operationsInProgress.isEmpty()
+        && getPersistentSearches().isEmpty())
     {
       return (TimeThread.getTime() - lastCompletionTime.get());
     }
@@ -2974,27 +2456,131 @@
     }
   }
 
-  private void initializeOperationMonitors() {
-    this.addMonitor = OperationMonitor.getOperationMonitor(
-            ADD);
-    this.searchMonitor = OperationMonitor.getOperationMonitor(
-            SEARCH);
-    this.abandonMonitor = OperationMonitor.getOperationMonitor(
-            ABANDON);
-    this.bindMonitor = OperationMonitor.getOperationMonitor(
-            BIND);
-    this.compareMonitor = OperationMonitor.getOperationMonitor(
-            COMPARE);
-    this.delMonitor = OperationMonitor.getOperationMonitor(
-            DELETE);
-    this.extendedMonitor = OperationMonitor.getOperationMonitor(
-            EXTENDED);
-    this.modMonitor = OperationMonitor.getOperationMonitor(
-            MODIFY);
-    this.moddnMonitor = OperationMonitor.getOperationMonitor(
-            MODIFY_DN);
-    this.unbindMonitor = OperationMonitor.getOperationMonitor(
-            UNBIND);
+
+
+  /**
+   * Set the connection provider that is not in use yet. Used in TLS
+   * negotiation when a clear response is needed before the connection
+   * provider is active.
+   *
+   * @param provider
+   *          The provider that needs to be activated.
+   */
+  public void setTLSPendingProvider(ConnectionSecurityProvider provider)
+  {
+    tlsPendingProvider = provider;
+
+  }
+
+
+
+  /**
+   * Set the connection provider that is not in use. Used in SASL
+   * negotiation when a clear response is needed before the connection
+   * provider is active.
+   *
+   * @param provider
+   *          The provider that needs to be activated.
+   */
+  public void setSASLPendingProvider(ConnectionSecurityProvider provider)
+  {
+    saslPendingProvider = provider;
+
+  }
+
+
+
+  /**
+   * Enable the provider that is inactive.
+   */
+  public void enableTLS()
+  {
+    this.asn1Reader =
+        ASN1.getReader(saslChannel, tlsPendingProvider.getAppBufSize(),
+            connectionHandler.getMaxRequestSize());
+    activeProvider = tlsPendingProvider;
+    tlsChannel.redirect(tlsPendingProvider);
+    tlsPendingProvider = null;
+  }
+
+
+
+  /**
+   * Set the security provider to the specified provider.
+   *
+   * @param sslProvider
+   *          The provider to set the security provider to.
+   */
+  public void enableSSL(ConnectionSecurityProvider sslProvider)
+  {
+    this.asn1Reader =
+        ASN1.getReader(saslChannel, sslProvider.getAppBufSize(),
+            connectionHandler.getMaxRequestSize());
+    activeProvider = sslProvider;
+    tlsChannel.redirect(sslProvider);
+  }
+
+
+
+  /**
+   * Enable the SASL provider that is currently inactive or pending.
+   */
+  public void enableSASL()
+  {
+    activeProvider = saslPendingProvider;
+    saslChannel.redirect(saslPendingProvider);
+    asn1Reader =
+        ASN1.getReader(saslChannel,
+            saslPendingProvider.getAppBufSize(), connectionHandler
+                .getMaxRequestSize());
+    saslPendingProvider = null;
+  }
+
+
+
+  /**
+   * Return the certificate chain array associated with a connection.
+   *
+   * @return The array of certificates associated with a connection.
+   */
+  public Certificate[] getClientCertificateChain()
+  {
+    if (activeProvider != null)
+    {
+      return activeProvider.getClientCertificateChain();
+    }
+    else
+      return new Certificate[0];
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int getSSF()
+  {
+    if (activeProvider != null)
+      return activeProvider.getSSF();
+    else
+      return 0;
+  }
+
+
+
+  private void initializeOperationMonitors()
+  {
+    this.addMonitor = OperationMonitor.getOperationMonitor(ADD);
+    this.searchMonitor = OperationMonitor.getOperationMonitor(SEARCH);
+    this.abandonMonitor = OperationMonitor.getOperationMonitor(ABANDON);
+    this.bindMonitor = OperationMonitor.getOperationMonitor(BIND);
+    this.compareMonitor = OperationMonitor.getOperationMonitor(COMPARE);
+    this.delMonitor = OperationMonitor.getOperationMonitor(DELETE);
+    this.extendedMonitor =
+        OperationMonitor.getOperationMonitor(EXTENDED);
+    this.modMonitor = OperationMonitor.getOperationMonitor(MODIFY);
+    this.moddnMonitor = OperationMonitor.getOperationMonitor(MODIFY_DN);
+    this.unbindMonitor = OperationMonitor.getOperationMonitor(UNBIND);
   }
 }
-

--
Gitblit v1.10.0