From 8f4c882e39b040cca1210fcc169424f721be16c8 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Wed, 01 Sep 2010 09:04:15 +0000
Subject: [PATCH] Various improvements:

---
 opendj-sdk/sdk/src/org/opends/sdk/AbstractConnectionFactory.java                             |    4 
 opendj-sdk/sdk/src/org/opends/sdk/SearchResultReferenceIOException.java                      |   81 +
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPServerFilter.java                             |  532 +++++-
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java             |    4 
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPSearchFutureResultImpl.java                   |    7 
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java                    |   10 
 opendj-sdk/sdk/src/org/opends/sdk/requests/StartTLSExtendedRequestImpl.java                  |   52 
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/InternalConnection.java                           |   36 
 opendj-sdk/sdk/src/org/opends/sdk/SearchResultHandler.java                                   |    5 
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParser.java                              |    5 
 opendj-sdk/sdk/src/org/opends/sdk/LDAPConnectionFactory.java                                 |    4 
 opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/ConnectionFactoryTestCase.java     |  105 +
 opendj-sdk/sdk/src/org/opends/sdk/LoadBalancingConnectionFactory.java                        |    2 
 opendj-sdk/sdk/src/org/opends/sdk/schema/Schema.java                                         |  251 +-
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/AuthenticatedConnectionFactory.java              |   38 
 opendj-sdk/sdk/src/org/opends/sdk/LDAPOptions.java                                           |   76 +
 opendj-sdk/sdk/src/org/opends/sdk/SynchronousConnection.java                                 |   23 
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PromptingTrustManager.java                       |   11 
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/GlobalTransportFactory.java                       |   32 
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PerformanceRunner.java                           |   19 
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPBindFutureResultImpl.java                     |   12 
 opendj-sdk/sdk/src/org/opends/sdk/AVA.java                                                   |    2 
 opendj-sdk/sdk/src/com/sun/opends/sdk/util/AbstractFutureResult.java                         |   18 
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPFutureResultImpl.java                         |    4 
 opendj-sdk/sdk/src/org/opends/sdk/Connection.java                                            |   62 
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPExtendedFutureResultImpl.java                 |   13 
 opendj-sdk/sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java                            |   66 
 opendj-sdk/sdk/src/org/opends/sdk/ldif/ConnectionEntryReader.java                            |  311 ++++
 opendj-sdk/sdk/src/org/opends/sdk/InternalConnectionFactory.java                             |    2 
 opendj-sdk/sdk/examples/org/opends/sdk/examples/DummyServer.java                             |   80 
 opendj-sdk/sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java                        |    2 
 opendj-sdk/sdk/src/org/opends/sdk/schema/SchemaBuilder.java                                  |  150 ++
 opendj-sdk/sdk/src/org/opends/sdk/ConnectionFactory.java                                     |    4 
 opendj-sdk/sdk/lib/grizzly.jar                                                               |    0 
 opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/SynchronousConnectionTestCase.java |    9 
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/DataSource.java                                  |   28 
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java                               |  140 -
 opendj-sdk/sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java                        |   36 
 opendj-sdk/sdk/src/org/opends/sdk/LDAPClientContext.java                                     |   91 +
 opendj-sdk/sdk/src/org/opends/sdk/AsynchronousConnection.java                                |  155 +-
 opendj-sdk/sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequestImpl.java                 |   34 
 opendj-sdk/sdk/src/org/opends/sdk/requests/StartTLSExtendedRequest.java                      |   56 
 opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPServer.java                    |  883 +++++++----
 opendj-sdk/sdk/src/org/opends/sdk/requests/ExternalSASLBindRequestImpl.java                  |   34 
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/SearchRate.java                                  |    8 
 opendj-sdk/sdk/src/org/opends/sdk/ServerConnection.java                                      |   78 
 opendj-sdk/sdk/src/org/opends/sdk/controls/EntryChangeNotificationResponseControl.java       |   21 
 opendj-sdk/sdk/src/org/opends/sdk/AbstractConnection.java                                    |   59 
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ModRate.java                                     |    6 
 /dev/null                                                                                    |   56 
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPClientFilter.java                             |  407 +++--
 opendj-sdk/sdk/src/org/opends/sdk/requests/CRAMMD5SASLBindRequestImpl.java                   |   35 
 opendj-sdk/sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java                        |   28 
 opendj-sdk/sdk/src/org/opends/sdk/LDAPUrl.java                                               |    2 
 opendj-sdk/sdk/src/org/opends/sdk/RootDSE.java                                               |    4 
 opendj-sdk/sdk/src/com/sun/opends/sdk/messages/messages.properties                           |   12 
 opendj-sdk/sdk/src/org/opends/sdk/ConnectionPool.java                                        |   42 
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPSearch.java                                  |   22 
 opendj-sdk/sdk/src/org/opends/sdk/ConnectionEventListener.java                               |   30 
 59 files changed, 2,887 insertions(+), 1,412 deletions(-)

diff --git a/opendj-sdk/sdk/examples/org/opends/sdk/examples/DummyServer.java b/opendj-sdk/sdk/examples/org/opends/sdk/examples/DummyServer.java
index 4179cec..8c924bc 100644
--- a/opendj-sdk/sdk/examples/org/opends/sdk/examples/DummyServer.java
+++ b/opendj-sdk/sdk/examples/org/opends/sdk/examples/DummyServer.java
@@ -63,15 +63,17 @@
 
 
 
-    public void abandon(final Integer context, final AbandonRequest request)
-        throws UnsupportedOperationException
+    @Override
+    public void handleAbandon(final Integer context,
+        final AbandonRequest request) throws UnsupportedOperationException
     {
     }
 
 
 
-    public void add(final Integer context, final AddRequest request,
-        final ResultHandler<Result> handler,
+    @Override
+    public void handleAdd(final Integer context, final AddRequest request,
+        final ResultHandler<? super Result> handler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException
     {
@@ -79,7 +81,8 @@
 
 
 
-    public void bind(final Integer context, final int version,
+    @Override
+    public void handleBind(final Integer context, final int version,
         final BindRequest request,
         final ResultHandler<? super BindResult> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
@@ -90,21 +93,9 @@
 
 
 
-    public void closed(final Integer context, final UnbindRequest request)
-    {
-      System.out.println(request);
-    }
-
-
-
-    public void closed(final Throwable error)
-    {
-      System.out.println(error);
-    }
-
-
-
-    public void compare(final Integer context, final CompareRequest request,
+    @Override
+    public void handleCompare(final Integer context,
+        final CompareRequest request,
         final ResultHandler<? super CompareResult> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException
@@ -113,8 +104,27 @@
 
 
 
-    public void delete(final Integer context, final DeleteRequest request,
-        final ResultHandler<Result> handler,
+    @Override
+    public void handleConnectionClosed(final Integer context,
+        final UnbindRequest request)
+    {
+      System.out.println(request);
+    }
+
+
+
+    @Override
+    public void handleConnectionException(final Throwable error)
+    {
+      System.out.println(error);
+    }
+
+
+
+    @Override
+    public void handleDelete(final Integer context,
+        final DeleteRequest request,
+        final ResultHandler<? super Result> handler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException
     {
@@ -122,9 +132,10 @@
 
 
 
-    public <R extends ExtendedResult> void extendedRequest(
+    @Override
+    public <R extends ExtendedResult> void handleExtendedRequest(
         final Integer context, final ExtendedRequest<R> request,
-        final ResultHandler<R> resultHandler,
+        final ResultHandler<? super R> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException
     {
@@ -133,14 +144,16 @@
         final R result = request.getResultDecoder().adaptExtendedErrorResult(
             ResultCode.SUCCESS, "", "");
         resultHandler.handleResult(result);
-        clientContext.startTLS(sslContext);
+        clientContext.startTLS(sslContext, null, null, false, false);
       }
     }
 
 
 
-    public void modify(final Integer context, final ModifyRequest request,
-        final ResultHandler<Result> resultHandler,
+    @Override
+    public void handleModify(final Integer context,
+        final ModifyRequest request,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException
     {
@@ -148,8 +161,10 @@
 
 
 
-    public void modifyDN(final Integer context, final ModifyDNRequest request,
-        final ResultHandler<Result> resultHandler,
+    @Override
+    public void handleModifyDN(final Integer context,
+        final ModifyDNRequest request,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException
     {
@@ -157,8 +172,10 @@
 
 
 
-    public void search(final Integer context, final SearchRequest request,
-        final ResultHandler<Result> resultHandler,
+    @Override
+    public void handleSearch(final Integer context,
+        final SearchRequest request,
+        final ResultHandler<? super Result> resultHandler,
         final SearchResultHandler searchResulthandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException
@@ -252,6 +269,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public ServerConnection<Integer> accept(final LDAPClientContext context)
   {
     System.out.println("Connection from: " + context.getPeerAddress());
diff --git a/opendj-sdk/sdk/lib/grizzly.jar b/opendj-sdk/sdk/lib/grizzly.jar
index 30f4360..137fb72 100644
--- a/opendj-sdk/sdk/lib/grizzly.jar
+++ b/opendj-sdk/sdk/lib/grizzly.jar
Binary files differ
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/GlobalTransportFactory.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/GlobalTransportFactory.java
index b3e1279..85d102d 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/GlobalTransportFactory.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/GlobalTransportFactory.java
@@ -76,6 +76,12 @@
 
   private int selectors;
 
+  private int linger = -1;
+
+  private boolean tcpNoDelay = true;
+
+  private boolean reuseAddress = true;
+
   private TCPNIOTransport globalTCPNIOTransport = null;
 
 
@@ -117,12 +123,15 @@
    * @return instance of TCP {@link com.sun.grizzly.Transport}.
    */
   @Override
-  public TCPNIOTransport createTCPTransport()
+  public synchronized TCPNIOTransport createTCPTransport()
   {
     if (globalTCPNIOTransport == null)
     {
       globalTCPNIOTransport = setupTransport(new TCPNIOTransport());
       globalTCPNIOTransport.setSelectorRunnersCount(selectors);
+      globalTCPNIOTransport.setLinger(linger);
+      globalTCPNIOTransport.setTcpNoDelay(tcpNoDelay);
+      globalTCPNIOTransport.setReuseAddress(reuseAddress);
 
       try
       {
@@ -178,6 +187,27 @@
     ThreadPoolConfig.DEFAULT.setMaxPoolSize(threads);
     ThreadPoolConfig.DEFAULT.setPoolName("OpenDS SDK Worker(Grizzly)");
 
+    final String lingerStr = System
+        .getProperty("org.opends.sdk.ldap.transport.linger");
+    if (lingerStr != null)
+    {
+      linger = Integer.parseInt(lingerStr);
+    }
+
+    final String tcpNoDelayStr = System
+        .getProperty("org.opends.sdk.ldap.transport.tcpNoDelay");
+    if (tcpNoDelayStr != null)
+    {
+      tcpNoDelay = Integer.parseInt(tcpNoDelayStr) != 0;
+    }
+
+    final String reuseAddressStr = System
+        .getProperty("org.opends.sdk.ldap.transport.reuseAddress");
+    if (reuseAddressStr != null)
+    {
+      reuseAddress = Integer.parseInt(reuseAddressStr) != 0;
+    }
+
     super.initialize();
   }
 }
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/InternalConnection.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/InternalConnection.java
index aa897ee..00d07e7 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/InternalConnection.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/InternalConnection.java
@@ -49,7 +49,7 @@
  */
 public final class InternalConnection extends AbstractAsynchronousConnection
 {
-  private final class InternalBindFutureResultImpl extends
+  private static final class InternalBindFutureResultImpl extends
       AbstractLDAPFutureResultImpl<BindResult> implements
       FutureResult<BindResult>
   {
@@ -125,7 +125,7 @@
       NullPointerException
   {
     final int i = messageID.getAndIncrement();
-    serverConnection.abandon(i, request);
+    serverConnection.handleAbandon(i, request);
     return new CompletedFutureResult<Void>((Void) null, i);
   }
 
@@ -135,7 +135,7 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> add(final AddRequest request,
-      final ResultHandler<Result> resultHandler,
+      final ResultHandler<? super Result> resultHandler,
       final IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
@@ -143,7 +143,7 @@
     final int i = messageID.getAndIncrement();
     final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request,
         resultHandler, intermediateResponseHandler, this);
-    serverConnection.add(i, request, future, future);
+    serverConnection.handleAdd(i, request, future, future);
     return future;
   }
 
@@ -173,7 +173,7 @@
     final int i = messageID.getAndIncrement();
     final InternalBindFutureResultImpl future = new InternalBindFutureResultImpl(
         i, request, resultHandler, intermediateResponseHandler, this);
-    serverConnection.bind(i, 3, request, future, future);
+    serverConnection.handleBind(i, 3, request, future, future);
     return future;
   }
 
@@ -185,7 +185,7 @@
   public void close(final UnbindRequest request, final String reason)
   {
     final int i = messageID.getAndIncrement();
-    serverConnection.closed(i, request);
+    serverConnection.handleConnectionClosed(i, request);
   }
 
 
@@ -202,7 +202,7 @@
     final int i = messageID.getAndIncrement();
     final LDAPCompareFutureResultImpl future = new LDAPCompareFutureResultImpl(
         i, request, resultHandler, intermediateResponseHandler, this);
-    serverConnection.compare(i, request, future, future);
+    serverConnection.handleCompare(i, request, future, future);
     return future;
   }
 
@@ -212,7 +212,7 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> delete(final DeleteRequest request,
-      final ResultHandler<Result> resultHandler,
+      final ResultHandler<? super Result> resultHandler,
       final IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
@@ -220,7 +220,7 @@
     final int i = messageID.getAndIncrement();
     final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request,
         resultHandler, intermediateResponseHandler, this);
-    serverConnection.delete(i, request, future, future);
+    serverConnection.handleDelete(i, request, future, future);
     return future;
   }
 
@@ -239,7 +239,7 @@
     final int i = messageID.getAndIncrement();
     final LDAPExtendedFutureResultImpl<R> future = new LDAPExtendedFutureResultImpl<R>(
         i, request, resultHandler, intermediateResponseHandler, this);
-    serverConnection.extendedRequest(i, request, future, future);
+    serverConnection.handleExtendedRequest(i, request, future, future);
     return future;
   }
 
@@ -271,7 +271,7 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> modify(final ModifyRequest request,
-      final ResultHandler<Result> resultHandler,
+      final ResultHandler<? super Result> resultHandler,
       final IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
@@ -279,7 +279,7 @@
     final int i = messageID.getAndIncrement();
     final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request,
         resultHandler, intermediateResponseHandler, this);
-    serverConnection.modify(i, request, future, future);
+    serverConnection.handleModify(i, request, future, future);
     return future;
   }
 
@@ -289,7 +289,7 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> modifyDN(final ModifyDNRequest request,
-      final ResultHandler<Result> resultHandler,
+      final ResultHandler<? super Result> resultHandler,
       final IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
@@ -297,7 +297,7 @@
     final int i = messageID.getAndIncrement();
     final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request,
         resultHandler, intermediateResponseHandler, this);
-    serverConnection.modifyDN(i, request, future, future);
+    serverConnection.handleModifyDN(i, request, future, future);
     return future;
   }
 
@@ -319,17 +319,15 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> search(final SearchRequest request,
-      final ResultHandler<Result> resultHandler,
-      final SearchResultHandler searchResulthandler,
+      final SearchResultHandler resultHandler,
       final IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
     final int i = messageID.getAndIncrement();
     final LDAPSearchFutureResultImpl future = new LDAPSearchFutureResultImpl(i,
-        request, resultHandler, searchResulthandler,
-        intermediateResponseHandler, this);
-    serverConnection.search(i, request, future, future, future);
+        request, resultHandler, intermediateResponseHandler, this);
+    serverConnection.handleSearch(i, request, future, future, future);
     return future;
   }
 }
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPBindFutureResultImpl.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPBindFutureResultImpl.java
index 4fda984..9d9b058 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPBindFutureResultImpl.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPBindFutureResultImpl.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.ldap;
@@ -58,6 +58,16 @@
 
 
 
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected boolean isCancelable() {
+    return false;
+  }
+
+
+
   @Override
   public String toString()
   {
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPClientFilter.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPClientFilter.java
index a1e1618..ea42e73 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPClientFilter.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPClientFilter.java
@@ -77,21 +77,25 @@
     {
       final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
           .getConnection());
-      final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
-          .removePendingRequest(messageID);
-
-      if (pendingRequest != null)
+      if(ldapConnection != null)
       {
-        if (pendingRequest instanceof LDAPFutureResultImpl)
+        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
+            .removePendingRequest(messageID);
+
+        if (pendingRequest != null)
         {
-          final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
-          if (future.getRequest() instanceof AddRequest)
+          if (pendingRequest instanceof LDAPFutureResultImpl)
           {
-            future.setResultOrError(result);
-            return;
+            final LDAPFutureResultImpl future =
+                (LDAPFutureResultImpl) pendingRequest;
+            if (future.getRequest() instanceof AddRequest)
+            {
+              future.setResultOrError(result);
+              return;
+            }
           }
+          throw new UnexpectedResponseException(messageID, result);
         }
-        throw new UnexpectedResponseException(messageID, result);
       }
     }
 
@@ -104,73 +108,79 @@
     {
       final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
           .getConnection());
-      final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
-          .removePendingRequest(messageID);
-
-      if (pendingRequest != null)
+      if(ldapConnection != null)
       {
-        if (pendingRequest instanceof LDAPBindFutureResultImpl)
+        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
+            .removePendingRequest(messageID);
+
+        if (pendingRequest != null)
         {
-          final LDAPBindFutureResultImpl future = ((LDAPBindFutureResultImpl) pendingRequest);
-          final BindClient bindClient = future.getBindClient();
-
-          try
+          if (pendingRequest instanceof LDAPBindFutureResultImpl)
           {
-            if (!bindClient.evaluateResult(result))
-            {
-              // The server is expecting a multi stage bind response.
-              final int msgID = ldapConnection
-                  .addPendingRequest(pendingRequest);
+            final LDAPBindFutureResultImpl future =
+                ((LDAPBindFutureResultImpl) pendingRequest);
+            final BindClient bindClient = future.getBindClient();
 
-              final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
-              try
+            try
+            {
+              if (!bindClient.evaluateResult(result))
               {
-                final GenericBindRequest nextRequest = bindClient
-                    .nextBindRequest();
-                new LDAPWriter().bindRequest(asn1Writer, msgID, 3, nextRequest);
-                ctx.write(asn1Writer.getBuffer(), null);
+                // The server is expecting a multi stage bind response.
+                final int msgID = ldapConnection
+                    .addPendingRequest(pendingRequest);
+
+                final ASN1BufferWriter asn1Writer =
+                    ASN1BufferWriter.getWriter();
+                try
+                {
+                  final GenericBindRequest nextRequest = bindClient
+                      .nextBindRequest();
+                  new LDAPWriter().bindRequest(asn1Writer, msgID, 3,
+                      nextRequest);
+                  ctx.write(asn1Writer.getBuffer(), null);
+                }
+                finally
+                {
+                  asn1Writer.close();
+                }
+                return;
               }
-              finally
-              {
-                asn1Writer.close();
-              }
+            }
+            catch (final ErrorResultException e)
+            {
+              future.adaptErrorResult(e.getResult());
               return;
             }
-          }
-          catch (final ErrorResultException e)
-          {
-            future.adaptErrorResult(e.getResult());
-            return;
-          }
-          catch (final IOException e)
-          {
-            // FIXME: I18N need to have a better error message.
-            // FIXME: Is this the best result code?
-            final Result errorResult = Responses.newResult(
-                ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
-                "An error occurred during multi-stage authentication")
-                .setCause(e);
-            future.adaptErrorResult(errorResult);
-            return;
-          }
-
-          if (result.getResultCode() == ResultCode.SUCCESS)
-          {
-            final ConnectionSecurityLayer l = bindClient
-                .getConnectionSecurityLayer();
-            if (l != null)
+            catch (final IOException e)
             {
-              // The connection needs to be secured by the SASL
-              // mechanism.
-              ldapConnection.installFilter(new SASLFilter(l));
+              // FIXME: I18N need to have a better error message.
+              // FIXME: Is this the best result code?
+              final Result errorResult = Responses.newResult(
+                  ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
+                  "An error occurred during multi-stage authentication")
+                  .setCause(e);
+              future.adaptErrorResult(errorResult);
+              return;
             }
-          }
 
-          ldapConnection.setBindOrStartTLSInProgress(false);
-          future.setResultOrError(result);
-          return;
+            if (result.getResultCode() == ResultCode.SUCCESS)
+            {
+              final ConnectionSecurityLayer l = bindClient
+                  .getConnectionSecurityLayer();
+              if (l != null)
+              {
+                // The connection needs to be secured by the SASL
+                // mechanism.
+                ldapConnection.installFilter(new SASLFilter(l));
+              }
+            }
+
+            ldapConnection.setBindOrStartTLSInProgress(false);
+            future.setResultOrError(result);
+            return;
+          }
+          throw new UnexpectedResponseException(messageID, result);
         }
-        throw new UnexpectedResponseException(messageID, result);
       }
     }
 
@@ -183,18 +193,22 @@
     {
       final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
           .getConnection());
-      final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
-          .removePendingRequest(messageID);
-
-      if (pendingRequest != null)
+      if(ldapConnection != null)
       {
-        if (pendingRequest instanceof LDAPCompareFutureResultImpl)
+        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
+            .removePendingRequest(messageID);
+
+        if (pendingRequest != null)
         {
-          final LDAPCompareFutureResultImpl future = (LDAPCompareFutureResultImpl) pendingRequest;
-          future.setResultOrError(result);
-          return;
+          if (pendingRequest instanceof LDAPCompareFutureResultImpl)
+          {
+            final LDAPCompareFutureResultImpl future =
+                (LDAPCompareFutureResultImpl) pendingRequest;
+            future.setResultOrError(result);
+            return;
+          }
+          throw new UnexpectedResponseException(messageID, result);
         }
-        throw new UnexpectedResponseException(messageID, result);
       }
     }
 
@@ -206,21 +220,25 @@
     {
       final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
           .getConnection());
-      final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
-          .removePendingRequest(messageID);
-
-      if (pendingRequest != null)
+      if(ldapConnection != null)
       {
-        if (pendingRequest instanceof LDAPFutureResultImpl)
+        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
+            .removePendingRequest(messageID);
+
+        if (pendingRequest != null)
         {
-          final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
-          if (future.getRequest() instanceof DeleteRequest)
+          if (pendingRequest instanceof LDAPFutureResultImpl)
           {
-            future.setResultOrError(result);
-            return;
+            final LDAPFutureResultImpl future =
+                (LDAPFutureResultImpl) pendingRequest;
+            if (future.getRequest() instanceof DeleteRequest)
+            {
+              future.setResultOrError(result);
+              return;
+            }
           }
+          throw new UnexpectedResponseException(messageID, result);
         }
-        throw new UnexpectedResponseException(messageID, result);
       }
     }
 
@@ -233,48 +251,54 @@
     {
       final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
           .getConnection());
-      if (messageID == 0)
+      if(ldapConnection != null)
       {
-        if ((result.getOID() != null)
-            && result.getOID().equals(OID_NOTICE_OF_DISCONNECTION))
+        if (messageID == 0)
         {
+          if ((result.getOID() != null)
+              && result.getOID().equals(OID_NOTICE_OF_DISCONNECTION))
+          {
 
-          final Result errorResult = Responses
-              .newResult(result.getResultCode()).setDiagnosticMessage(
-                  result.getDiagnosticMessage());
-          ldapConnection.close(null, true, errorResult);
-          return;
+            final Result errorResult = Responses
+                .newResult(result.getResultCode()).setDiagnosticMessage(
+                    result.getDiagnosticMessage());
+            ldapConnection.close(null, true, errorResult);
+            return;
+          }
+          else
+          {
+            // Unsolicited notification received.
+            ldapConnection.handleUnsolicitedNotification(result);
+          }
         }
-        else
-        {
-          // Unsolicited notification received.
-          ldapConnection.handleUnsolicitedNotification(result);
-        }
-      }
 
-      final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
-          .removePendingRequest(messageID);
+        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
+            .removePendingRequest(messageID);
 
-      if (pendingRequest instanceof LDAPExtendedFutureResultImpl<?>)
-      {
-        final LDAPExtendedFutureResultImpl<?> extendedFuture =
-          ((LDAPExtendedFutureResultImpl<?>) pendingRequest);
-        try
+        if(pendingRequest != null)
         {
-          handleExtendedResult0(ldapConnection, extendedFuture, result);
+          if (pendingRequest instanceof LDAPExtendedFutureResultImpl<?>)
+          {
+            final LDAPExtendedFutureResultImpl<?> extendedFuture =
+              ((LDAPExtendedFutureResultImpl<?>) pendingRequest);
+            try
+            {
+              handleExtendedResult0(ldapConnection, extendedFuture, result);
+            }
+            catch (final DecodeException de)
+            {
+              // FIXME: should the connection be closed as well?
+              final Result errorResult = Responses.newResult(
+                  ResultCode.CLIENT_SIDE_DECODING_ERROR).setDiagnosticMessage(
+                  de.getLocalizedMessage()).setCause(de);
+              extendedFuture.adaptErrorResult(errorResult);
+            }
+          }
+          else
+          {
+            throw new UnexpectedResponseException(messageID, result);
+          }
         }
-        catch (final DecodeException de)
-        {
-          // FIXME: should the connection be closed as well?
-          final Result errorResult = Responses.newResult(
-              ResultCode.CLIENT_SIDE_DECODING_ERROR).setDiagnosticMessage(
-              de.getLocalizedMessage()).setCause(de);
-          extendedFuture.adaptErrorResult(errorResult);
-        }
-      }
-      else
-      {
-        throw new UnexpectedResponseException(messageID, result);
       }
     }
 
@@ -287,12 +311,15 @@
     {
       final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
           .getConnection());
-      final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
-          .getPendingRequest(messageID);
-
-      if (pendingRequest != null)
+      if(ldapConnection != null)
       {
-        pendingRequest.handleIntermediateResponse(response);
+        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
+            .getPendingRequest(messageID);
+
+        if (pendingRequest != null)
+        {
+          pendingRequest.handleIntermediateResponse(response);
+        }
       }
     }
 
@@ -305,21 +332,25 @@
     {
       final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
           .getConnection());
-      final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
-          .removePendingRequest(messageID);
-
-      if (pendingRequest != null)
+      if(ldapConnection != null)
       {
-        if (pendingRequest instanceof LDAPFutureResultImpl)
+        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
+            .removePendingRequest(messageID);
+
+        if (pendingRequest != null)
         {
-          final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
-          if (future.getRequest() instanceof ModifyDNRequest)
+          if (pendingRequest instanceof LDAPFutureResultImpl)
           {
-            future.setResultOrError(result);
-            return;
+            final LDAPFutureResultImpl future =
+                (LDAPFutureResultImpl) pendingRequest;
+            if (future.getRequest() instanceof ModifyDNRequest)
+            {
+              future.setResultOrError(result);
+              return;
+            }
           }
+          throw new UnexpectedResponseException(messageID, result);
         }
-        throw new UnexpectedResponseException(messageID, result);
       }
     }
 
@@ -331,21 +362,25 @@
     {
       final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
           .getConnection());
-      final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
-          .removePendingRequest(messageID);
-
-      if (pendingRequest != null)
+      if(ldapConnection != null)
       {
-        if (pendingRequest instanceof LDAPFutureResultImpl)
+        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
+            .removePendingRequest(messageID);
+
+        if (pendingRequest != null)
         {
-          final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
-          if (future.getRequest() instanceof ModifyRequest)
+          if (pendingRequest instanceof LDAPFutureResultImpl)
           {
-            future.setResultOrError(result);
-            return;
+            final LDAPFutureResultImpl future =
+                (LDAPFutureResultImpl) pendingRequest;
+            if (future.getRequest() instanceof ModifyRequest)
+            {
+              future.setResultOrError(result);
+              return;
+            }
           }
+          throw new UnexpectedResponseException(messageID, result);
         }
-        throw new UnexpectedResponseException(messageID, result);
       }
     }
 
@@ -357,19 +392,22 @@
     {
       final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
           .getConnection());
-      final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
-          .removePendingRequest(messageID);
-
-      if (pendingRequest != null)
+      if(ldapConnection != null)
       {
-        if (pendingRequest instanceof LDAPSearchFutureResultImpl)
+        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
+            .removePendingRequest(messageID);
+
+        if (pendingRequest != null)
         {
-          ((LDAPSearchFutureResultImpl) pendingRequest)
-              .setResultOrError(result);
-        }
-        else
-        {
-          throw new UnexpectedResponseException(messageID, result);
+          if (pendingRequest instanceof LDAPSearchFutureResultImpl)
+          {
+            ((LDAPSearchFutureResultImpl) pendingRequest)
+                .setResultOrError(result);
+          }
+          else
+          {
+            throw new UnexpectedResponseException(messageID, result);
+          }
         }
       }
     }
@@ -383,18 +421,21 @@
     {
       final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
           .getConnection());
-      final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
-          .getPendingRequest(messageID);
-
-      if (pendingRequest != null)
+      if(ldapConnection != null)
       {
-        if (pendingRequest instanceof LDAPSearchFutureResultImpl)
+        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
+            .getPendingRequest(messageID);
+
+        if (pendingRequest != null)
         {
-          ((LDAPSearchFutureResultImpl) pendingRequest).handleEntry(entry);
-        }
-        else
-        {
-          throw new UnexpectedResponseException(messageID, entry);
+          if (pendingRequest instanceof LDAPSearchFutureResultImpl)
+          {
+            ((LDAPSearchFutureResultImpl) pendingRequest).handleEntry(entry);
+          }
+          else
+          {
+            throw new UnexpectedResponseException(messageID, entry);
+          }
         }
       }
     }
@@ -408,19 +449,22 @@
     {
       final LDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx
           .getConnection());
-      final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
-          .getPendingRequest(messageID);
-
-      if (pendingRequest != null)
+      if(ldapConnection != null)
       {
-        if (pendingRequest instanceof LDAPSearchFutureResultImpl)
+        final AbstractLDAPFutureResultImpl<?> pendingRequest = ldapConnection
+            .getPendingRequest(messageID);
+
+        if (pendingRequest != null)
         {
-          ((LDAPSearchFutureResultImpl) pendingRequest)
-              .handleReference(reference);
-        }
-        else
-        {
-          throw new UnexpectedResponseException(messageID, reference);
+          if (pendingRequest instanceof LDAPSearchFutureResultImpl)
+          {
+            ((LDAPSearchFutureResultImpl) pendingRequest)
+                .handleReference(reference);
+          }
+          else
+          {
+            throw new UnexpectedResponseException(messageID, reference);
+          }
         }
       }
     }
@@ -445,6 +489,7 @@
             final StartTLSExtendedRequest request = (StartTLSExtendedRequest) future
                 .getRequest();
             conn.startTLS(request.getSSLContext(),
+                request.getEnabledProtocols(), request.getEnabledCipherSuites(),
                 new EmptyCompletionHandler<SSLEngine>()
                 {
                   @Override
@@ -521,7 +566,7 @@
     {
       // FIXME: what other sort of IOExceptions can be thrown?
       // FIXME: Is this the best result code?
-      errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR)
+      errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
           .setCause(error);
     }
     ldapConnection.close(null, false, errorResult);
@@ -568,6 +613,16 @@
         ldapReader.decode(asn1Reader, CLIENT_RESPONSE_HANDLER, ctx);
       }
     }
+    catch(IOException ioe)
+    {
+      final LDAPConnection ldapConnection =
+          LDAP_CONNECTION_ATTR.get(ctx.getConnection());
+      final Result errorResult =
+          Responses.newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR)
+              .setCause(ioe).setDiagnosticMessage(ioe.getMessage());
+      ldapConnection.close(null, false, errorResult);
+      throw ioe;
+    }
     finally
     {
       asn1Reader.disposeBytesRead();
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java
index d3bed34..25f7d84 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.ldap;
@@ -30,9 +30,9 @@
 
 
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.Iterator;
+import java.net.InetSocketAddress;
 import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -79,8 +79,9 @@
 
   private boolean bindOrStartTLSInProgress = false;
 
-  private final HashMap<Integer, AbstractLDAPFutureResultImpl<?>>
-    pendingRequests = new HashMap<Integer, AbstractLDAPFutureResultImpl<?>>();
+  private final ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>>
+    pendingRequests =
+      new ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>>();
 
   private final Object stateLock = new Object();
 
@@ -112,21 +113,7 @@
    */
   public FutureResult<Void> abandon(final AbandonRequest request)
   {
-    // First remove the future associated with the request to be abandoned.
-    final AbstractLDAPFutureResultImpl<?> pendingRequest = pendingRequests
-        .remove(request.getMessageID());
-
-    if (pendingRequest == null)
-    {
-      // There has never been a request with the specified message ID or the
-      // response has already been received and handled. We can ignore this
-      // abandon request.
-
-      // Message ID will be -1 since no request was sent.
-      return new CompletedFutureResult<Void>((Void) null);
-    }
-
-    pendingRequest.cancel(false);
+    final AbstractLDAPFutureResultImpl<?> pendingRequest;
     final int messageID = nextMsgID.getAndIncrement();
 
     synchronized (stateLock)
@@ -144,8 +131,23 @@
         return new CompletedFutureResult<Void>(ErrorResultException
             .wrap(errorResult), messageID);
       }
+
+      // First remove the future associated with the request to be abandoned.
+      pendingRequest = pendingRequests.remove(request.getMessageID());
     }
 
+    if (pendingRequest == null)
+    {
+      // There has never been a request with the specified message ID or the
+      // response has already been received and handled. We can ignore this
+      // abandon request.
+
+      // Message ID will be -1 since no request was sent.
+      return new CompletedFutureResult<Void>((Void) null);
+    }
+
+    pendingRequest.cancel(false);
+
     try
     {
       final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
@@ -178,7 +180,7 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> add(final AddRequest request,
-      final ResultHandler<Result> resultHandler,
+      final ResultHandler<? super Result> resultHandler,
       final IntermediateResponseHandler intermediateResponseHandler)
   {
     final int messageID = nextMsgID.getAndIncrement();
@@ -256,8 +258,10 @@
     BindClient context;
     try
     {
-      context = request
-          .createBindClient(connection.getPeerAddress().toString());
+      context = request.createBindClient(
+          connection.getPeerAddress() instanceof InetSocketAddress ?
+              ((InetSocketAddress)connection.getPeerAddress()).getHostName() :
+              connection.getPeerAddress().toString());
     }
     catch (final Exception e)
     {
@@ -414,7 +418,7 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> delete(final DeleteRequest request,
-      final ResultHandler<Result> resultHandler,
+      final ResultHandler<? super Result> resultHandler,
       final IntermediateResponseHandler intermediateResponseHandler)
   {
     final int messageID = nextMsgID.getAndIncrement();
@@ -568,7 +572,7 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> modify(final ModifyRequest request,
-      final ResultHandler<Result> resultHandler,
+      final ResultHandler<? super Result> resultHandler,
       final IntermediateResponseHandler intermediateResponseHandler)
   {
     final int messageID = nextMsgID.getAndIncrement();
@@ -626,7 +630,7 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> modifyDN(final ModifyDNRequest request,
-      final ResultHandler<Result> resultHandler,
+      final ResultHandler<? super Result> resultHandler,
       final IntermediateResponseHandler intermediateResponseHandler)
   {
     final int messageID = nextMsgID.getAndIncrement();
@@ -696,14 +700,12 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> search(final SearchRequest request,
-      final ResultHandler<Result> resultHandler,
-      final SearchResultHandler searchResultHandler,
+      final SearchResultHandler resultHandler,
       final IntermediateResponseHandler intermediateResponseHandler)
   {
     final int messageID = nextMsgID.getAndIncrement();
     final LDAPSearchFutureResultImpl future = new LDAPSearchFutureResultImpl(
-        messageID, request, resultHandler, searchResultHandler,
-        intermediateResponseHandler, this);
+        messageID, request, resultHandler, intermediateResponseHandler, this);
 
     synchronized (stateLock)
     {
@@ -753,9 +755,17 @@
 
 
   int addPendingRequest(final AbstractLDAPFutureResultImpl<?> request)
+      throws ErrorResultException
   {
     final int newMsgID = nextMsgID.getAndIncrement();
-    pendingRequests.put(newMsgID, request);
+    synchronized(stateLock)
+    {
+      if(connectionInvalidReason != null)
+      {
+        throw ErrorResultException.wrap(connectionInvalidReason);
+      }
+      pendingRequests.put(newMsgID, request);
+    }
     return newMsgID;
   }
 
@@ -767,21 +777,20 @@
     long delay = timeout;
     if (timeout > 0)
     {
-      synchronized (stateLock)
+      for (int requestID : pendingRequests.keySet())
       {
-        for (final Iterator<AbstractLDAPFutureResultImpl<?>> i = pendingRequests
-            .values().iterator(); i.hasNext();)
+        final AbstractLDAPFutureResultImpl<?> future =
+            pendingRequests.get(requestID);
+        if(future != null)
         {
-          final AbstractLDAPFutureResultImpl<?> future = i.next();
           final long diff = (future.getTimestamp() + timeout) - currentTime;
-          if (diff <= 0)
+          if (diff <= 0 && pendingRequests.remove(requestID) != null)
           {
             StaticUtils.DEBUG_LOG.fine("Cancelling expired future result: "
                 + future);
             final Result result = Responses
                 .newResult(ResultCode.CLIENT_SIDE_TIMEOUT);
             future.adaptErrorResult(result);
-            i.remove();
 
             abandon(Requests.newAbandonRequest(future.getRequestID()));
           }
@@ -805,7 +814,7 @@
 
     synchronized (stateLock)
     {
-      if (isClosed)
+      if (isClosed || connectionInvalidReason != null)
       {
         // Already closed.
         return;
@@ -822,51 +831,29 @@
         notifyErrorOccurred = true;
       }
 
-      if (connectionInvalidReason != null)
-      {
-        // Already invalid.
-        return;
-      }
-
       // Mark the connection as invalid.
-      connectionInvalidReason = reason;
+      connectionInvalidReason =
+          reason.getResultCode() == ResultCode.CLIENT_SIDE_USER_CANCELLED ?
+              reason : Responses.newResult(
+              ResultCode.CLIENT_SIDE_USER_CANCELLED).setCause(
+              ErrorResultException.wrap(reason)).setDiagnosticMessage(
+              "Connection closed: " + reason.getDiagnosticMessage());
     }
 
     // First abort all outstanding requests.
-    for (final AbstractLDAPFutureResultImpl<?> future : pendingRequests
-        .values())
+    for (int requestID : pendingRequests.keySet())
     {
-      if (!bindOrStartTLSInProgress)
+      final AbstractLDAPFutureResultImpl<?> future =
+          pendingRequests.remove(requestID);
+      if(future != null)
       {
-        final int messageID = nextMsgID.getAndIncrement();
-        final AbandonRequest abandon = Requests.newAbandonRequest(future
-            .getRequestID());
-        try
-        {
-          final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
-          try
-          {
-            ldapWriter.abandonRequest(asn1Writer, messageID, abandon);
-            connection.write(asn1Writer.getBuffer(), null);
-          }
-          finally
-          {
-            asn1Writer.recycle();
-          }
-        }
-        catch (final IOException e)
-        {
-          // Underlying channel probably blown up. Just ignore.
-        }
+        future.adaptErrorResult(reason);
       }
-
-      future.adaptErrorResult(reason);
     }
-    pendingRequests.clear();
 
     // Now try cleanly closing the connection if possible.
     // Only send unbind if specified.
-    if (unbindRequest != null)
+    if (unbindRequest != null && !isDisconnectNotification)
     {
       try
       {
@@ -902,7 +889,7 @@
     {
       for (final ConnectionEventListener listener : listeners)
       {
-        listener.connectionClosed();
+        listener.handleConnectionClosed();
       }
     }
 
@@ -910,7 +897,7 @@
     {
       for (final ConnectionEventListener listener : listeners)
       {
-        listener.connectionErrorOccurred(isDisconnectNotification,
+        listener.handleConnectionError(isDisconnectNotification,
             ErrorResultException.wrap(reason));
       }
     }
@@ -942,7 +929,7 @@
 
     for (final ConnectionEventListener listener : listeners)
     {
-      listener.connectionReceivedUnsolicitedNotification(result);
+      listener.handleUnsolicitedNotification(result);
     }
   }
 
@@ -993,6 +980,7 @@
 
 
   synchronized void startTLS(final SSLContext sslContext,
+      final String[] protocols, final String[] cipherSuites,
       final CompletionHandler<SSLEngine> completionHandler) throws IOException
   {
     if (isTLSEnabled())
@@ -1005,6 +993,8 @@
 
     sslEngineConfigurator = new SSLEngineConfigurator(sslContext, true, false,
         false);
+    sslEngineConfigurator.setEnabledProtocols(protocols);
+    sslEngineConfigurator.setEnabledCipherSuites(cipherSuites);
     sslFilter = new SSLFilter(null, sslEngineConfigurator);
     installFilter(sslFilter);
     sslFilter.handshake(connection, completionHandler);
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java
index ce244bb..d56efdd 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.ldap;
@@ -62,7 +62,7 @@
     implements ConnectionFactory
 {
 
-  @SuppressWarnings("unchecked")
+  @SuppressWarnings("rawtypes")
   private final class FutureResultImpl implements CompletionHandler<Connection>
   {
     private final FutureResultTransformer<Result, AsynchronousConnection> futureStartTLSResult;
@@ -123,6 +123,8 @@
           {
             final StartTLSExtendedRequest startTLS = Requests
                 .newStartTLSExtendedRequest(options.getSSLContext());
+            startTLS.setEnabledCipherSuites(options.getEnabledCipherSuites());
+            startTLS.setEnabledProtocols(options.getEnabledProtocols());
             return connection.extendedRequest(startTLS, handler);
           }
 
@@ -131,6 +133,8 @@
             try
             {
               connection.startTLS(options.getSSLContext(),
+                  options.getEnabledProtocols(),
+                  options.getEnabledCipherSuites(),
                   new EmptyCompletionHandler<SSLEngine>()
                   {
                     @Override
@@ -269,7 +273,7 @@
    */
   @Override
   public FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      final ResultHandler<AsynchronousConnection> handler)
+      final ResultHandler<? super AsynchronousConnection> handler)
   {
     final FutureResultImpl future = new FutureResultImpl(handler);
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPExtendedFutureResultImpl.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPExtendedFutureResultImpl.java
index 50a786d..1c162a6 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPExtendedFutureResultImpl.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPExtendedFutureResultImpl.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.ldap;
@@ -31,6 +31,7 @@
 
 import org.opends.sdk.*;
 import org.opends.sdk.requests.ExtendedRequest;
+import org.opends.sdk.requests.StartTLSExtendedRequest;
 import org.opends.sdk.responses.ExtendedResult;
 
 
@@ -74,6 +75,16 @@
 
 
 
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected boolean isCancelable() {
+    return !request.getOID().equals(StartTLSExtendedRequest.OID);
+  }
+
+
+
   R decodeResult(final ExtendedResult result, final DecodeOptions options)
       throws DecodeException
   {
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPFutureResultImpl.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPFutureResultImpl.java
index 58a914e..3733164 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPFutureResultImpl.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPFutureResultImpl.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.ldap;
@@ -47,7 +47,7 @@
 
 
   LDAPFutureResultImpl(final int messageID, final Request request,
-      final ResultHandler<Result> resultHandler,
+      final ResultHandler<? super Result> resultHandler,
       final IntermediateResponseHandler intermediateResponseHandler,
       final AsynchronousConnection connection)
   {
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPSearchFutureResultImpl.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPSearchFutureResultImpl.java
index 5385a1c..70a8e2d 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPSearchFutureResultImpl.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPSearchFutureResultImpl.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.ldap;
@@ -53,14 +53,13 @@
 
 
   LDAPSearchFutureResultImpl(final int messageID, final SearchRequest request,
-      final ResultHandler<Result> resultHandler,
-      final SearchResultHandler searchResultHandler,
+      final SearchResultHandler resultHandler,
       final IntermediateResponseHandler intermediateResponseHandler,
       final AsynchronousConnection connection)
   {
     super(messageID, resultHandler, intermediateResponseHandler, connection);
     this.request = request;
-    this.searchResultHandler = searchResultHandler;
+    this.searchResultHandler = resultHandler;
   }
 
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPServerFilter.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPServerFilter.java
index c21c31b..3bd1034 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPServerFilter.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPServerFilter.java
@@ -33,8 +33,7 @@
 
 import java.io.IOException;
 import java.net.InetSocketAddress;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import java.util.*;
 
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
@@ -66,8 +65,8 @@
   private abstract class AbstractHandler<R extends Result> implements
       IntermediateResponseHandler, ResultHandler<R>
   {
-    protected int messageID;
-    protected Connection<?> connection;
+    protected final int messageID;
+    protected final Connection<?> connection;
 
 
 
@@ -80,6 +79,7 @@
 
 
 
+    @Override
     public boolean handleIntermediateResponse(
         final IntermediateResponse response)
     {
@@ -91,7 +91,7 @@
       }
       catch (final IOException ioe)
       {
-        closeConnection(connection, ioe);
+        notifyConnectionException(connection, ioe);
         return false;
       }
       finally
@@ -114,6 +114,7 @@
 
 
 
+    @Override
     public void handleErrorResult(final ErrorResultException error)
     {
       handleResult(error.getResult());
@@ -121,6 +122,7 @@
 
 
 
+    @Override
     public void handleResult(final Result result)
     {
       final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
@@ -131,7 +133,7 @@
       }
       catch (final IOException ioe)
       {
-        closeConnection(connection, ioe);
+        notifyConnectionException(connection, ioe);
       }
       finally
       {
@@ -151,6 +153,7 @@
 
 
 
+    @Override
     public void handleErrorResult(final ErrorResultException error)
     {
       final Result result = error.getResult();
@@ -175,6 +178,7 @@
 
 
 
+    @Override
     public void handleResult(final BindResult result)
     {
       final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
@@ -185,7 +189,7 @@
       }
       catch (final IOException ioe)
       {
-        closeConnection(connection, ioe);
+        notifyConnectionException(connection, ioe);
       }
       finally
       {
@@ -198,7 +202,16 @@
 
   private final class ClientContextImpl implements LDAPClientContext
   {
-    protected Connection<?> connection;
+    private final Connection<?> connection;
+
+    // Connection state guarded by stateLock.
+    private final Object stateLock = new Object();
+    private List<ConnectionEventListener> connectionEventListeners = null;
+    private boolean isClosed = false;
+    private Throwable connectionError = null;
+    private ExtendedResult disconnectNotification = null;
+
+    private ServerConnection<Integer> serverConnection = null;
 
 
 
@@ -209,20 +222,64 @@
 
 
 
-    public void disconnect(final boolean sendNotification)
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void addConnectionEventListener(
+        final ConnectionEventListener listener) throws NullPointerException
     {
-      if (sendNotification)
+      Validator.ensureNotNull(listener);
+
+      boolean invokeImmediately = false;
+      synchronized (stateLock)
       {
-        final GenericExtendedResult notification = Responses
-            .newGenericExtendedResult(ResultCode.SUCCESS);
-        notification.setOID(OID_NOTICE_OF_DISCONNECTION);
-        sendUnsolicitedNotification(notification);
+        if (isClosed)
+        {
+          invokeImmediately = true;
+        }
+        else
+        {
+          if (connectionEventListeners == null)
+          {
+            connectionEventListeners = new LinkedList<ConnectionEventListener>();
+          }
+          connectionEventListeners.add(listener);
+        }
       }
-      closeConnection(connection, -1, null);
+
+      // Invoke listener immediately if this connection is already closed.
+      if (invokeImmediately)
+      {
+        invokeListener(listener);
+      }
     }
 
 
 
+    @Override
+    public void disconnect()
+    {
+      LDAPServerFilter.notifyConnectionClosed(connection, -1, null);
+    }
+
+
+
+    @Override
+    public void disconnect(final ResultCode resultCode, final String message)
+    {
+      Validator.ensureNotNull(resultCode);
+
+      final GenericExtendedResult notification = Responses
+          .newGenericExtendedResult(resultCode)
+          .setOID(OID_NOTICE_OF_DISCONNECTION).setDiagnosticMessage(message);
+      sendUnsolicitedNotification(notification);
+      disconnect();
+    }
+
+
+
+    @Override
     public InetSocketAddress getLocalAddress()
     {
       return (InetSocketAddress) connection.getLocalAddress();
@@ -230,6 +287,7 @@
 
 
 
+    @Override
     public InetSocketAddress getPeerAddress()
     {
       return (InetSocketAddress) connection.getPeerAddress();
@@ -237,6 +295,7 @@
 
 
 
+    @Override
     public int getSecurityStrengthFactor()
     {
       int ssf = 0;
@@ -260,6 +319,43 @@
 
 
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isClosed()
+    {
+      final boolean tmp;
+      synchronized (stateLock)
+      {
+        tmp = isClosed;
+      }
+      return tmp;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void removeConnectionEventListener(
+        final ConnectionEventListener listener) throws NullPointerException
+    {
+      Validator.ensureNotNull(listener);
+
+      synchronized (stateLock)
+      {
+        if (connectionEventListeners != null)
+        {
+          connectionEventListeners.remove(listener);
+        }
+      }
+    }
+
+
+
+    @Override
     public void sendUnsolicitedNotification(final ExtendedResult notification)
     {
       final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
@@ -270,16 +366,49 @@
       }
       catch (final IOException ioe)
       {
-        closeConnection(connection, ioe);
+        LDAPServerFilter.notifyConnectionException(connection, ioe);
       }
       finally
       {
         asn1Writer.recycle();
       }
+
+      // Update state and notify event listeners if necessary, only if the
+      // notification was sent successfully.
+      if (notification.getOID().equals(OID_NOTICE_OF_DISCONNECTION))
+      {
+        // Don't notify listeners yet - wait for disconnect.
+        synchronized (stateLock)
+        {
+          disconnectNotification = notification;
+        }
+      }
+      else
+      {
+        // Notify listeners.
+        List<ConnectionEventListener> tmpList = null;
+        synchronized (stateLock)
+        {
+          if (!isClosed && connectionEventListeners != null)
+          {
+            tmpList = new ArrayList<ConnectionEventListener>(
+                connectionEventListeners);
+          }
+        }
+
+        if (tmpList != null)
+        {
+          for (final ConnectionEventListener listener : tmpList)
+          {
+            listener.handleUnsolicitedNotification(notification);
+          }
+        }
+      }
     }
 
 
 
+    @Override
     public void startSASL(final ConnectionSecurityLayer bindContext)
     {
       installFilter(connection, new SASLFilter(bindContext));
@@ -287,15 +416,119 @@
 
 
 
-    public void startTLS(final SSLContext sslContext)
+    @Override
+    public void startTLS(final SSLContext sslContext, final String[] protocols,
+        final String[] suites, final boolean wantClientAuth,
+        final boolean needClientAuth)
     {
       Validator.ensureNotNull(sslContext);
       SSLEngineConfigurator sslEngineConfigurator;
 
       sslEngineConfigurator = new SSLEngineConfigurator(sslContext, false,
           false, false);
+      sslEngineConfigurator.setEnabledCipherSuites(suites);
+      sslEngineConfigurator.setEnabledProtocols(protocols);
+      sslEngineConfigurator.setWantClientAuth(wantClientAuth);
+      sslEngineConfigurator.setNeedClientAuth(needClientAuth);
       installFilter(connection, new SSLFilter(sslEngineConfigurator, null));
     }
+
+
+
+    private ServerConnection<Integer> getServerConnection()
+    {
+      return serverConnection;
+    }
+
+
+
+    private void invokeListener(final ConnectionEventListener listener)
+    {
+      if (connectionError != null)
+      {
+        final Result result;
+        if (connectionError instanceof DecodeException)
+        {
+          final DecodeException e = (DecodeException) connectionError;
+          result = Responses.newResult(ResultCode.PROTOCOL_ERROR)
+              .setDiagnosticMessage(e.getMessage()).setCause(connectionError);
+        }
+        else
+        {
+          result = Responses.newResult(ResultCode.OTHER)
+              .setDiagnosticMessage(connectionError.getMessage())
+              .setCause(connectionError);
+        }
+        listener
+            .handleConnectionError(false, ErrorResultException.wrap(result));
+      }
+      else if (disconnectNotification != null)
+      {
+        listener.handleConnectionError(true,
+            ErrorResultException.wrap(disconnectNotification));
+      }
+      else
+      {
+        listener.handleConnectionClosed();
+      }
+    }
+
+
+
+    private void notifyConnectionClosed(final int messageID,
+        final UnbindRequest unbindRequest)
+    {
+      final List<ConnectionEventListener> tmpList;
+      synchronized (stateLock)
+      {
+        if (!isClosed)
+        {
+          isClosed = true;
+        }
+        tmpList = connectionEventListeners;
+        connectionEventListeners = null;
+      }
+      if (tmpList != null)
+      {
+        for (final ConnectionEventListener listener : tmpList)
+        {
+          invokeListener(listener);
+        }
+      }
+    }
+
+
+
+    private void notifyConnectionException(final Throwable error)
+    {
+      final List<ConnectionEventListener> tmpList;
+      synchronized (stateLock)
+      {
+        if (!isClosed)
+        {
+          connectionError = error;
+          isClosed = true;
+        }
+        tmpList = connectionEventListeners;
+        connectionEventListeners = null;
+      }
+      if (tmpList != null)
+      {
+        for (final ConnectionEventListener listener : tmpList)
+        {
+          invokeListener(listener);
+        }
+      }
+    }
+
+
+
+    private void setServerConnection(
+        final ServerConnection<Integer> serverConnection)
+    {
+      this.serverConnection = serverConnection;
+    }
+
   }
 
 
@@ -309,6 +542,7 @@
 
 
 
+    @Override
     public void handleErrorResult(final ErrorResultException error)
     {
       final Result result = error.getResult();
@@ -333,6 +567,7 @@
 
 
 
+    @Override
     public void handleResult(final CompareResult result)
     {
       final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
@@ -343,7 +578,7 @@
       }
       catch (final IOException ioe)
       {
-        closeConnection(connection, ioe);
+        notifyConnectionException(connection, ioe);
       }
       finally
       {
@@ -363,6 +598,7 @@
 
 
 
+    @Override
     public void handleErrorResult(final ErrorResultException error)
     {
       handleResult(error.getResult());
@@ -370,6 +606,7 @@
 
 
 
+    @Override
     public void handleResult(final Result result)
     {
       final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
@@ -380,7 +617,7 @@
       }
       catch (final IOException ioe)
       {
-        closeConnection(connection, ioe);
+        notifyConnectionException(connection, ioe);
       }
       finally
       {
@@ -401,6 +638,7 @@
 
 
 
+    @Override
     public void handleErrorResult(final ErrorResultException error)
     {
       final Result result = error.getResult();
@@ -425,6 +663,7 @@
 
 
 
+    @Override
     public void handleResult(final ExtendedResult result)
     {
       final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
@@ -435,7 +674,7 @@
       }
       catch (final IOException ioe)
       {
-        closeConnection(connection, ioe);
+        notifyConnectionException(connection, ioe);
       }
       finally
       {
@@ -455,6 +694,7 @@
 
 
 
+    @Override
     public void handleErrorResult(final ErrorResultException error)
     {
       handleResult(error.getResult());
@@ -462,6 +702,7 @@
 
 
 
+    @Override
     public void handleResult(final Result result)
     {
       final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
@@ -472,7 +713,7 @@
       }
       catch (final IOException ioe)
       {
-        closeConnection(connection, ioe);
+        notifyConnectionException(connection, ioe);
       }
       finally
       {
@@ -492,6 +733,7 @@
 
 
 
+    @Override
     public void handleErrorResult(final ErrorResultException error)
     {
       handleResult(error.getResult());
@@ -499,6 +741,7 @@
 
 
 
+    @Override
     public void handleResult(final Result result)
     {
       final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
@@ -509,7 +752,7 @@
       }
       catch (final IOException ioe)
       {
-        closeConnection(connection, ioe);
+        notifyConnectionException(connection, ioe);
       }
       finally
       {
@@ -530,6 +773,7 @@
 
 
 
+    @Override
     public boolean handleEntry(final SearchResultEntry entry)
     {
       final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
@@ -540,7 +784,7 @@
       }
       catch (final IOException ioe)
       {
-        closeConnection(connection, ioe);
+        notifyConnectionException(connection, ioe);
         return false;
       }
       finally
@@ -552,6 +796,7 @@
 
 
 
+    @Override
     public void handleErrorResult(final ErrorResultException error)
     {
       handleResult(error.getResult());
@@ -559,6 +804,7 @@
 
 
 
+    @Override
     public boolean handleReference(final SearchResultReference reference)
     {
       final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
@@ -569,7 +815,7 @@
       }
       catch (final IOException ioe)
       {
-        closeConnection(connection, ioe);
+        notifyConnectionException(connection, ioe);
         return false;
       }
       finally
@@ -581,6 +827,7 @@
 
 
 
+    @Override
     public void handleResult(final Result result)
     {
       final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
@@ -591,7 +838,7 @@
       }
       catch (final IOException ioe)
       {
-        closeConnection(connection, ioe);
+        notifyConnectionException(connection, ioe);
       }
       finally
       {
@@ -627,24 +874,101 @@
 
   private static final LDAPWriter LDAP_WRITER = new LDAPWriter();
 
-  private static final Attribute<ServerConnection<Integer>>
-    LDAP_CONNECTION_ATTR = Grizzly.DEFAULT_ATTRIBUTE_BUILDER.
-      createAttribute("LDAPServerConnection");
+  private static final Attribute<ClientContextImpl> LDAP_CONNECTION_ATTR =
+    Grizzly.DEFAULT_ATTRIBUTE_BUILDER
+      .createAttribute("LDAPServerConnection");
 
   private static final Attribute<ASN1BufferReader> LDAP_ASN1_READER_ATTR =
-    Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPASN1Reader");
+    Grizzly.DEFAULT_ATTRIBUTE_BUILDER
+      .createAttribute("LDAPASN1Reader");
 
-  private final AbstractLDAPMessageHandler<FilterChainContext>
-    serverRequestHandler = new AbstractLDAPMessageHandler<FilterChainContext>()
+
+
+  private static void notifyConnectionClosed(final Connection<?> connection,
+      final int messageID, final UnbindRequest unbindRequest)
+  {
+    final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR
+        .remove(connection);
+    if (clientContext != null)
+    {
+      // First notify connection event listeners.
+      clientContext.notifyConnectionClosed(messageID, unbindRequest);
+
+      // Notify the server connection: it may be null if disconnect is invoked
+      // during accept.
+      final ServerConnection<Integer> serverConnection = clientContext
+          .getServerConnection();
+      if (serverConnection != null)
+      {
+        serverConnection.handleConnectionClosed(messageID, unbindRequest);
+      }
+
+      // If this close was a result of an unbind request then the connection
+      // won't actually be closed yet. To avoid TIME_WAIT TCP state, let the
+      // client disconnect.
+      if (unbindRequest != null)
+      {
+        return;
+      }
+
+      // Close the connection.
+      try
+      {
+        connection.close();
+      }
+      catch (final IOException e)
+      {
+        StaticUtils.DEBUG_LOG.warning("Error closing connection: " + e);
+      }
+    }
+  }
+
+
+
+  private static void notifyConnectionException(final Connection<?> connection,
+      final Throwable error)
+  {
+    final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR
+        .remove(connection);
+    if (clientContext != null)
+    {
+      // First notify connection event listeners.
+      clientContext.notifyConnectionException(error);
+
+      // Notify the server connection: it may be null if disconnect is invoked
+      // during accept.
+      final ServerConnection<Integer> serverConnection = clientContext
+          .getServerConnection();
+      if (serverConnection != null)
+      {
+        serverConnection.handleConnectionException(error);
+      }
+
+      // Close the connection.
+      try
+      {
+        connection.close();
+      }
+      catch (final IOException e)
+      {
+        StaticUtils.DEBUG_LOG.warning("Error closing connection: " + e);
+      }
+    }
+  }
+
+
+
+  private final AbstractLDAPMessageHandler<FilterChainContext> serverRequestHandler =
+    new AbstractLDAPMessageHandler<FilterChainContext>()
   {
     @Override
     public void abandonRequest(final FilterChainContext ctx,
         final int messageID, final AbandonRequest request)
         throws UnexpectedRequestException
     {
-      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(ctx
-          .getConnection());
-      conn.abandon(messageID, request);
+      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(
+          ctx.getConnection()).getServerConnection();
+      conn.handleAbandon(messageID, request);
     }
 
 
@@ -653,10 +977,10 @@
     public void addRequest(final FilterChainContext ctx, final int messageID,
         final AddRequest request) throws UnexpectedRequestException
     {
-      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(ctx
-          .getConnection());
+      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(
+          ctx.getConnection()).getServerConnection();
       final AddHandler handler = new AddHandler(messageID, ctx.getConnection());
-      conn.add(messageID, request, handler, handler);
+      conn.handleAdd(messageID, request, handler, handler);
     }
 
 
@@ -666,11 +990,11 @@
         final int version, final GenericBindRequest bindContext)
         throws UnexpectedRequestException
     {
-      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(ctx
-          .getConnection());
-      final BindHandler handler = new BindHandler(messageID, ctx
-          .getConnection());
-      conn.bind(messageID, version, bindContext, handler, handler);
+      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(
+          ctx.getConnection()).getServerConnection();
+      final BindHandler handler = new BindHandler(messageID,
+          ctx.getConnection());
+      conn.handleBind(messageID, version, bindContext, handler, handler);
     }
 
 
@@ -680,11 +1004,11 @@
         final int messageID, final CompareRequest request)
         throws UnexpectedRequestException
     {
-      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(ctx
-          .getConnection());
-      final CompareHandler handler = new CompareHandler(messageID, ctx
-          .getConnection());
-      conn.compare(messageID, request, handler, handler);
+      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(
+          ctx.getConnection()).getServerConnection();
+      final CompareHandler handler = new CompareHandler(messageID,
+          ctx.getConnection());
+      conn.handleCompare(messageID, request, handler, handler);
     }
 
 
@@ -694,11 +1018,11 @@
         final int messageID, final DeleteRequest request)
         throws UnexpectedRequestException
     {
-      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(ctx
-          .getConnection());
-      final DeleteHandler handler = new DeleteHandler(messageID, ctx
-          .getConnection());
-      conn.delete(messageID, request, handler, handler);
+      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(
+          ctx.getConnection()).getServerConnection();
+      final DeleteHandler handler = new DeleteHandler(messageID,
+          ctx.getConnection());
+      conn.handleDelete(messageID, request, handler, handler);
     }
 
 
@@ -708,12 +1032,12 @@
         final FilterChainContext ctx, final int messageID,
         final ExtendedRequest<R> request) throws UnexpectedRequestException
     {
-      final ExtendedHandler<R> handler = new ExtendedHandler<R>(messageID, ctx
-          .getConnection());
+      final ExtendedHandler<R> handler = new ExtendedHandler<R>(messageID,
+          ctx.getConnection());
 
-      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(ctx
-          .getConnection());
-      conn.extendedRequest(messageID, request, handler, handler);
+      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(
+          ctx.getConnection()).getServerConnection();
+      conn.handleExtendedRequest(messageID, request, handler, handler);
     }
 
 
@@ -723,11 +1047,11 @@
         final int messageID, final ModifyDNRequest request)
         throws UnexpectedRequestException
     {
-      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(ctx
-          .getConnection());
-      final ModifyDNHandler handler = new ModifyDNHandler(messageID, ctx
-          .getConnection());
-      conn.modifyDN(messageID, request, handler, handler);
+      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(
+          ctx.getConnection()).getServerConnection();
+      final ModifyDNHandler handler = new ModifyDNHandler(messageID,
+          ctx.getConnection());
+      conn.handleModifyDN(messageID, request, handler, handler);
     }
 
 
@@ -737,11 +1061,11 @@
         final int messageID, final ModifyRequest request)
         throws UnexpectedRequestException
     {
-      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(ctx
-          .getConnection());
-      final ModifyHandler handler = new ModifyHandler(messageID, ctx
-          .getConnection());
-      conn.modify(messageID, request, handler, handler);
+      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(
+          ctx.getConnection()).getServerConnection();
+      final ModifyHandler handler = new ModifyHandler(messageID,
+          ctx.getConnection());
+      conn.handleModify(messageID, request, handler, handler);
     }
 
 
@@ -751,11 +1075,11 @@
         final int messageID, final SearchRequest request)
         throws UnexpectedRequestException
     {
-      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(ctx
-          .getConnection());
-      final SearchHandler handler = new SearchHandler(messageID, ctx
-          .getConnection());
-      conn.search(messageID, request, handler, handler, handler);
+      final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR.get(
+          ctx.getConnection()).getServerConnection();
+      final SearchHandler handler = new SearchHandler(messageID,
+          ctx.getConnection());
+      conn.handleSearch(messageID, request, handler, handler, handler);
     }
 
 
@@ -764,7 +1088,7 @@
     public void unbindRequest(final FilterChainContext ctx,
         final int messageID, final UnbindRequest request)
     {
-      closeConnection(ctx.getConnection(), messageID, request);
+      notifyConnectionClosed(ctx.getConnection(), messageID, request);
     }
 
 
@@ -774,13 +1098,14 @@
         final int messageID, final byte messageTag,
         final ByteString messageBytes)
     {
-      closeConnection(ctx.getConnection(), new UnsupportedMessageException(
-          messageID, messageTag, messageBytes));
+      notifyConnectionException(ctx.getConnection(),
+          new UnsupportedMessageException(messageID, messageTag, messageBytes));
     }
   };
-
   private final int maxASN1ElementSize;
+
   private final LDAPReader ldapReader;
+
   private final LDAPListenerImpl listener;
 
 
@@ -799,7 +1124,7 @@
   public void exceptionOccurred(final FilterChainContext ctx,
       final Throwable error)
   {
-    closeConnection(ctx.getConnection(), error);
+    notifyConnectionException(ctx.getConnection(), error);
   }
 
 
@@ -810,12 +1135,13 @@
   {
     final Connection<?> connection = ctx.getConnection();
     connection.configureBlocking(true);
-    ServerConnection<Integer> serverConn;
     try
     {
-      serverConn = listener.getConnectionFactory().accept(
-          new ClientContextImpl(connection));
-      LDAP_CONNECTION_ATTR.set(connection, serverConn);
+      final ClientContextImpl clientContext = new ClientContextImpl(connection);
+      final ServerConnection<Integer> serverConn = listener
+          .getConnectionFactory().accept(clientContext);
+      clientContext.setServerConnection(serverConn);
+      LDAP_CONNECTION_ATTR.set(connection, clientContext);
     }
     catch (final ErrorResultException e)
     {
@@ -831,7 +1157,7 @@
   public NextAction handleClose(final FilterChainContext ctx)
       throws IOException
   {
-    closeConnection(ctx.getConnection(), -1, null);
+    notifyConnectionClosed(ctx.getConnection(), -1, null);
     return ctx.getStopAction();
   }
 
@@ -867,48 +1193,6 @@
 
 
 
-  private void closeConnection(final Connection<?> connection,
-      final int messageID, final UnbindRequest unbindRequest)
-  {
-    final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR
-        .remove(connection);
-    if (conn != null)
-    {
-      conn.closed(messageID, unbindRequest);
-      try
-      {
-        connection.close();
-      }
-      catch (final IOException e)
-      {
-        StaticUtils.DEBUG_LOG.warning("Error closing connection: " + e);
-      }
-    }
-  }
-
-
-
-  private void closeConnection(final Connection<?> connection,
-      final Throwable error)
-  {
-    final ServerConnection<Integer> conn = LDAP_CONNECTION_ATTR
-        .remove(connection);
-    if (conn != null)
-    {
-      conn.closed(error);
-      try
-      {
-        connection.close();
-      }
-      catch (final IOException e)
-      {
-        StaticUtils.DEBUG_LOG.warning("Error closing connection: " + e);
-      }
-    }
-  }
-
-
-
   private synchronized void installFilter(final Connection<?> connection,
       final com.sun.grizzly.filterchain.Filter filter)
   {
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/messages/messages.properties b/opendj-sdk/sdk/src/com/sun/opends/sdk/messages/messages.properties
index 7b8f5c4..5cb6e2b 100755
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/messages/messages.properties
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/messages/messages.properties
@@ -5834,3 +5834,15 @@
  contained the OID '%s', when '%s' was expected
 SEVERE_ERR_VIRTUAL_ATTRS_ONLY_INVALID_CONTROL_VALUE=Cannot decode the provided \
  virtual attributes only control because it contains a value
+WARN_CLIENT_DUPLICATE_MESSAGE_ID=The Directory Server is already processing \
+ another request on the same client connection with the same message ID of %d
+INFO_CANCELED_BY_ABANDON_REQUEST=The operation was canceled because the client \
+ issued an abandon request (message ID %d) for this operation
+INFO_CANCELED_BY_CANCEL_REQUEST=The operation was canceled because the client \
+ issued a cancel request (message ID %d) for this operation
+INFO_CANCELED_BY_CLIENT_DISCONNECT=The operation was canceled because the \
+ client disconnected
+INFO_CANCELED_BY_CLIENT_ERROR=The operation was canceled because the \
+ client connection failed
+
+
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParser.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParser.java
index 546185a..8f60c53 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParser.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParser.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2009 Sun Microsystems, Inc.
+ *      Copyright 2006-2010 Sun Microsystems, Inc.
  */
 package com.sun.opends.sdk.tools;
 
@@ -327,7 +327,8 @@
 
     if (versionArgument != null)
     {
-      if (shortID == versionArgument.getShortIdentifier())
+      if (shortID != null &&
+          shortID.equals(versionArgument.getShortIdentifier()))
       {
         // Update the version argument to not display its short
         // identifier.
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java
index b3ba2d5..78f7329 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.tools;
@@ -332,7 +332,7 @@
    */
   @Override
   public FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      final ResultHandler<AsynchronousConnection> handler)
+      final ResultHandler<? super AsynchronousConnection> handler)
   {
     return connFactory.getAsynchronousConnection(handler);
   }
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/AuthenticatedConnectionFactory.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/AuthenticatedConnectionFactory.java
index 9f1460a..a331e0e 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/AuthenticatedConnectionFactory.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/AuthenticatedConnectionFactory.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.tools;
@@ -103,7 +103,7 @@
 
 
     public FutureResult<Result> add(final AddRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -113,7 +113,7 @@
 
 
     public FutureResult<Result> add(final AddRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -197,7 +197,7 @@
 
 
     public FutureResult<Result> delete(final DeleteRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -207,7 +207,7 @@
 
 
     public FutureResult<Result> delete(final DeleteRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -283,7 +283,7 @@
 
 
     public FutureResult<Result> modify(final ModifyRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -293,7 +293,7 @@
 
 
     public FutureResult<Result> modify(final ModifyRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -305,7 +305,7 @@
 
 
     public FutureResult<Result> modifyDN(final ModifyDNRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -315,7 +315,7 @@
 
 
     public FutureResult<Result> modifyDN(final ModifyDNRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -344,7 +344,7 @@
      * {@inheritDoc}
      */
     public FutureResult<RootDSE> readRootDSE(
-        final ResultHandler<RootDSE> handler)
+        final ResultHandler<? super RootDSE> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
       return connection.readRootDSE(handler);
@@ -356,7 +356,7 @@
      * {@inheritDoc}
      */
     public FutureResult<Schema> readSchema(final DN name,
-        final ResultHandler<Schema> handler)
+        final ResultHandler<? super Schema> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
       return connection.readSchema(name, handler);
@@ -368,7 +368,7 @@
      * {@inheritDoc}
      */
     public FutureResult<Schema> readSchemaForEntry(final DN name,
-        final ResultHandler<Schema> handler)
+        final ResultHandler<? super Schema> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
       return connection.readSchemaForEntry(name, handler);
@@ -448,24 +448,22 @@
 
 
     public FutureResult<Result> search(final SearchRequest request,
-        final ResultHandler<Result> resultHandler,
-        final SearchResultHandler searchResulthandler)
+        final SearchResultHandler handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
-      return connection.search(request, resultHandler, searchResulthandler);
+      return connection.search(request, handler);
     }
 
 
 
     public FutureResult<Result> search(final SearchRequest request,
-        final ResultHandler<Result> resultHandler,
-        final SearchResultHandler searchResulthandler,
+        final SearchResultHandler resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
-      return connection.search(request, resultHandler, searchResulthandler,
+      return connection.search(request, resultHandler,
           intermediateResponseHandler);
     }
 
@@ -592,7 +590,7 @@
 
 
     private FutureResultImpl(final BindRequest request,
-        final ResultHandler<AsynchronousConnection> handler)
+        final ResultHandler<? super AsynchronousConnection> handler)
     {
       this.bindRequest = request;
       this.futureBindResult = new FutureResultTransformer<BindResult, AsynchronousConnection>(
@@ -684,7 +682,7 @@
 
   @Override
   public FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      final ResultHandler<AsynchronousConnection> handler)
+      final ResultHandler<? super AsynchronousConnection> handler)
   {
     final FutureResultImpl future = new FutureResultImpl(request, handler);
     future.futureConnectionResult.setFutureResult(parentFactory
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/DataSource.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/DataSource.java
index 4c4438f..bb529f7 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/DataSource.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/DataSource.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.tools;
@@ -65,10 +65,17 @@
     {
       lines = new ArrayList<String>();
       final BufferedReader in = new BufferedReader(new FileReader(file));
-      String line;
-      while ((line = in.readLine()) != null)
+      try
       {
-        lines.add(line);
+        String line;
+        while ((line = in.readLine()) != null)
+        {
+          lines.add(line);
+        }
+      }
+      finally
+      {
+        in.close();
       }
     }
 
@@ -151,10 +158,17 @@
       lines = new ArrayList<String>();
       random = new Random(seed);
       final BufferedReader in = new BufferedReader(new FileReader(file));
-      String line;
-      while ((line = in.readLine()) != null)
+      try
       {
-        lines.add(line);
+        String line;
+        while ((line = in.readLine()) != null)
+        {
+          lines.add(line);
+        }
+      }
+      finally
+      {
+        in.close();
       }
     }
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPSearch.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPSearch.java
index 1741c99..d8a8316 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPSearch.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPSearch.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.tools;
@@ -180,6 +180,26 @@
       println(LocalizableMessage.raw(reference.toString()));
       return true;
     }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void handleErrorResult(ErrorResultException error)
+    {
+      // Ignore.
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void handleResult(Result result)
+    {
+      // Ignore.
+    }
   }
 
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ModRate.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ModRate.java
index 2e689e8..548180c 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ModRate.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ModRate.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.tools;
@@ -334,6 +334,10 @@
 
     try
     {
+      if(System.getProperty("org.opends.sdk.ldap.transport.linger") == null)
+      {
+        System.setProperty("org.opends.sdk.ldap.transport.linger", "0");
+      }
       connectionFactory = new ArgumentParserConnectionFactory(argParser, this);
       runner = new ModifyPerformanceRunner(argParser, this);
       propertiesFileArgument = new StringArgument("propertiesFilePath", null,
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PerformanceRunner.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PerformanceRunner.java
index 03c0bce..24b5f56 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PerformanceRunner.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PerformanceRunner.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.tools;
@@ -214,8 +214,8 @@
         if (successCount > 0)
         {
           strings[2] = String.format("%.3f",
-              (waitTime - (gcDuration - lastGCDuration)) / successCount
-                  / 1000000.0);
+              (waitTime - (gcDuration - lastGCDuration))
+                  / (double) successCount / 1000000.0);
         }
         else
         {
@@ -224,7 +224,7 @@
         if (totalSuccessCount > 0)
         {
           strings[3] = String.format("%.3f", (totalWaitTime - gcDuration)
-              / totalSuccessCount / 1000000.0);
+              / (double) totalSuccessCount / 1000000.0);
         }
         else
         {
@@ -438,7 +438,8 @@
       R handler;
 
       final double targetTimeInMS =
-        (1.0 / (targetThroughput / (numThreads * numConnections))) * 1000.0;
+        (1.0 / (targetThroughput /
+            (double) (numThreads * numConnections))) * 1000.0;
       double sleepTimeInMS = 0;
       long start;
       while (!stopRequested && !(maxIterations > 0 && count >= maxIterations))
@@ -706,7 +707,7 @@
       int parent;
       while (child > start)
       {
-        parent = (int) Math.floor((child - 1) / 2);
+        parent = (int) Math.floor((child - 1) / 2.0);
         if (get(parent) > get(child))
         {
           swap(parent, child);
@@ -865,14 +866,14 @@
 
 
 
-  public void connectionClosed()
+  public void handleConnectionClosed()
   {
     // Ignore
   }
 
 
 
-  public synchronized void connectionErrorOccurred(
+  public synchronized void handleConnectionError(
       final boolean isDisconnectNotification, final ErrorResultException error)
   {
     if (!stopRequested)
@@ -889,7 +890,7 @@
 
 
 
-  public void connectionReceivedUnsolicitedNotification(
+  public void handleUnsolicitedNotification(
       final ExtendedResult notification)
   {
     // Ignore
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PromptingTrustManager.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PromptingTrustManager.java
index 2274f92..53d55ca 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PromptingTrustManager.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PromptingTrustManager.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.tools;
@@ -161,7 +161,14 @@
     else
     {
       final FileInputStream fos = new FileInputStream(onDiskTrustStorePath);
-      onDiskTrustStore.load(fos, DEFAULT_PASSWORD);
+      try
+      {
+        onDiskTrustStore.load(fos, DEFAULT_PASSWORD);
+      }
+      finally
+      {
+        fos.close();
+      }
     }
     final TrustManagerFactory tmf = TrustManagerFactory
         .getInstance(TrustManagerFactory.getDefaultAlgorithm());
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/SearchRate.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/SearchRate.java
index 830a88a..c30c921 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/SearchRate.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/SearchRate.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.tools;
@@ -169,7 +169,7 @@
           sr.setFilter(String.format(filter, data));
           sr.setName(String.format(baseDN, data));
         }
-        return connection.search(sr, handler, handler);
+        return connection.search(sr, handler);
       }
     }
 
@@ -393,6 +393,10 @@
 
     try
     {
+      if(System.getProperty("org.opends.sdk.ldap.transport.linger") == null)
+      {
+        System.setProperty("org.opends.sdk.ldap.transport.linger", "0");
+      }
       connectionFactory = new ArgumentParserConnectionFactory(argParser, this);
       runner = new SearchPerformanceRunner(argParser, this);
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/util/AbstractFutureResult.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/util/AbstractFutureResult.java
index a0b0120..1d478d0 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/util/AbstractFutureResult.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/util/AbstractFutureResult.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package com.sun.opends.sdk.util;
@@ -125,7 +125,7 @@
 
     boolean innerCancel(final boolean mayInterruptIfRunning)
     {
-      if (!setStatePending())
+      if (!isCancelable() || !setStatePending())
       {
         return false;
       }
@@ -410,6 +410,20 @@
 
 
   /**
+   * Indicates whether this future result can be canceled.
+   *
+   * @return {@code true} if this future result is cancelable or {@code false}
+   *         otherwise.
+   */
+  protected boolean isCancelable()
+  {
+    // Return true by default.
+    return true;
+  }
+
+
+
+  /**
    * Appends a string representation of this future's state to the provided
    * builder.
    *
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/AVA.java b/opendj-sdk/sdk/src/org/opends/sdk/AVA.java
index 3949155..6ed0388 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/AVA.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/AVA.java
@@ -164,7 +164,7 @@
       return;
     }
 
-    if ((length % 2) == 1)
+    if ((length % 2) != 0)
     {
       final LocalizableMessage message = ERR_HEX_DECODE_INVALID_LENGTH
           .get(hexBuffer);
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java b/opendj-sdk/sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java
index 0f2ae0d..56ec077 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
@@ -53,8 +53,7 @@
 {
 
   private static final class SingleEntryFuture implements
-      FutureResult<SearchResultEntry>, ResultHandler<Result>,
-      SearchResultHandler
+      FutureResult<SearchResultEntry>, SearchResultHandler
   {
     private final ResultHandler<? super SearchResultEntry> handler;
 
@@ -231,7 +230,7 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> add(final AddRequest request,
-      final ResultHandler<Result> handler)
+      final ResultHandler<? super Result> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
@@ -280,7 +279,7 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> delete(final DeleteRequest request,
-      final ResultHandler<Result> handler)
+      final ResultHandler<? super Result> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
@@ -316,7 +315,7 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> modify(final ModifyRequest request,
-      final ResultHandler<Result> handler)
+      final ResultHandler<? super Result> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
@@ -329,7 +328,7 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> modifyDN(final ModifyDNRequest request,
-      final ResultHandler<Result> handler)
+      final ResultHandler<? super Result> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
@@ -358,7 +357,8 @@
   /**
    * {@inheritDoc}
    */
-  public FutureResult<RootDSE> readRootDSE(final ResultHandler<RootDSE> handler)
+  public FutureResult<RootDSE> readRootDSE(
+      final ResultHandler<? super RootDSE> handler)
       throws UnsupportedOperationException, IllegalStateException
   {
     return RootDSE.readRootDSE(this, handler);
@@ -370,7 +370,7 @@
    * {@inheritDoc}
    */
   public FutureResult<Schema> readSchema(final DN name,
-      final ResultHandler<Schema> handler)
+      final ResultHandler<? super Schema> handler)
       throws UnsupportedOperationException, IllegalStateException
   {
     return Schema.readSchema(this, name, handler);
@@ -382,7 +382,7 @@
    * {@inheritDoc}
    */
   public FutureResult<Schema> readSchemaForEntry(final DN name,
-      final ResultHandler<Schema> handler)
+      final ResultHandler<? super Schema> handler)
       throws UnsupportedOperationException, IllegalStateException
   {
     return Schema.readSchema(this, name, handler);
@@ -394,12 +394,11 @@
    * {@inheritDoc}
    */
   public FutureResult<Result> search(final SearchRequest request,
-      final ResultHandler<Result> resultHandler,
-      final SearchResultHandler searchResulthandler)
+      final SearchResultHandler handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
-    return search(request, resultHandler, searchResulthandler, null);
+    return search(request, handler, null);
   }
 
 
@@ -414,8 +413,7 @@
       NullPointerException
   {
     final SingleEntryFuture innerFuture = new SingleEntryFuture(handler);
-    final FutureResult<Result> future = search(request, innerFuture,
-        innerFuture);
+    final FutureResult<Result> future = search(request, innerFuture);
     innerFuture.setResultFuture(future);
     return innerFuture;
   }
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/AbstractConnection.java b/opendj-sdk/sdk/src/org/opends/sdk/AbstractConnection.java
index 64576de..c56e848 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/AbstractConnection.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/AbstractConnection.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
@@ -34,9 +34,10 @@
 import static com.sun.opends.sdk.messages.Messages.ERR_UNEXPECTED_SEARCH_RESULT_REFERENCES;
 
 import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
 
+import org.opends.sdk.ldif.ConnectionEntryReader;
 import org.opends.sdk.requests.Requests;
 import org.opends.sdk.requests.SearchRequest;
 import org.opends.sdk.responses.*;
@@ -84,6 +85,26 @@
       return true;
     }
 
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void handleErrorResult(ErrorResultException error)
+    {
+      // Ignore.
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void handleResult(Result result)
+    {
+      // Ignore.
+    }
+
   }
 
 
@@ -353,6 +374,26 @@
         }
         return true;
       }
+
+
+
+      /**
+       * {@inheritDoc}
+       */
+      public void handleErrorResult(ErrorResultException error)
+      {
+        // Ignore.
+      }
+
+
+
+      /**
+       * {@inheritDoc}
+       */
+      public void handleResult(Result result)
+      {
+        // Ignore.
+      }
     };
 
     return search(request, handler);
@@ -363,18 +404,16 @@
   /**
    * {@inheritDoc}
    */
-  public List<SearchResultEntry> search(final String baseObject,
+  public ConnectionEntryReader search(final String baseObject,
       final SearchScope scope, final String filter,
-      final String... attributeDescriptions) throws ErrorResultException,
-      InterruptedException, LocalizedIllegalArgumentException,
-      UnsupportedOperationException, IllegalStateException,
+      final String... attributeDescriptions)
+      throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
-    final List<SearchResultEntry> entries = new LinkedList<SearchResultEntry>();
+    final BlockingQueue<Response> entries = new LinkedBlockingQueue<Response>();
     final SearchRequest request = Requests.newSearchRequest(baseObject, scope,
         filter, attributeDescriptions);
-    search(request, entries);
-    return entries;
+    return search(request, entries);
   }
 
 
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/AbstractConnectionFactory.java b/opendj-sdk/sdk/src/org/opends/sdk/AbstractConnectionFactory.java
index 4805de0..7ae9ee2 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/AbstractConnectionFactory.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/AbstractConnectionFactory.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
@@ -50,7 +50,7 @@
    * {@inheritDoc}
    */
   public abstract FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      ResultHandler<AsynchronousConnection> handler);
+      ResultHandler<? super AsynchronousConnection> handler);
 
 
 
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/AsynchronousConnection.java b/opendj-sdk/sdk/src/org/opends/sdk/AsynchronousConnection.java
index 5651403..4c0f60f 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/AsynchronousConnection.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/AsynchronousConnection.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
@@ -120,10 +120,10 @@
    * Abandons the unfinished operation identified in the provided abandon
    * request.
    * <p>
-   * Since abandon requests do not have a response, invoking the method {@code
-   * get()} on the returned future will not block, nor return anything (it is
-   * {@code Void}), but may throw an exception if a problem occurred while
-   * sending the abandon request.
+   * Since abandon requests do not have a response, invoking the method
+   * {@code get()} on the returned future will not block, nor return anything
+   * (it is {@code Void}), but may throw an exception if a problem occurred
+   * while sending the abandon request.
    * <p>
    * <b>Note:</b> a more convenient approach to abandoning unfinished operations
    * is provided via the {@link FutureResult#cancel(boolean)} method.
@@ -134,8 +134,8 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support abandon operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
@@ -158,12 +158,13 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support add operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
-  FutureResult<Result> add(AddRequest request, ResultHandler<Result> handler)
+  FutureResult<Result> add(AddRequest request,
+      ResultHandler<? super Result> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException;
 
@@ -184,13 +185,13 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support add operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
   FutureResult<Result> add(AddRequest request,
-      ResultHandler<Result> resultHandler,
+      ResultHandler<? super Result> resultHandler,
       IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException;
@@ -206,8 +207,8 @@
    *          The listener which wants to be notified when events occur on this
    *          connection.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If the {@code listener} was {@code null}.
    */
@@ -229,8 +230,8 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support bind operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
@@ -256,8 +257,8 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support bind operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
@@ -329,8 +330,8 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support compare operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
@@ -357,8 +358,8 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support compare operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
@@ -383,14 +384,15 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support delete operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
   FutureResult<Result> delete(DeleteRequest request,
-      ResultHandler<Result> handler) throws UnsupportedOperationException,
-      IllegalStateException, NullPointerException;
+      ResultHandler<? super Result> handler)
+      throws UnsupportedOperationException, IllegalStateException,
+      NullPointerException;
 
 
 
@@ -410,13 +412,13 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support delete operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
   FutureResult<Result> delete(DeleteRequest request,
-      ResultHandler<Result> resultHandler,
+      ResultHandler<? super Result> resultHandler,
       IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException;
@@ -438,8 +440,8 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support extended operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
@@ -467,8 +469,8 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support extended operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
@@ -505,9 +507,9 @@
 
   /**
    * Returns {@code true} if this connection has not been closed and no fatal
-   * errors have been detected. This method is guaranteed to return {@code
-   * false} only when it is called after the method {@code close} has been
-   * called.
+   * errors have been detected. This method is guaranteed to return
+   * {@code false} only when it is called after the method {@code close} has
+   * been called.
    *
    * @return {@code true} if the connection is valid, {@code false} otherwise.
    */
@@ -528,14 +530,15 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support modify operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
   FutureResult<Result> modify(ModifyRequest request,
-      ResultHandler<Result> handler) throws UnsupportedOperationException,
-      IllegalStateException, NullPointerException;
+      ResultHandler<? super Result> handler)
+      throws UnsupportedOperationException, IllegalStateException,
+      NullPointerException;
 
 
 
@@ -555,13 +558,13 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support modify operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
   FutureResult<Result> modify(ModifyRequest request,
-      ResultHandler<Result> resultHandler,
+      ResultHandler<? super Result> resultHandler,
       IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException;
@@ -581,14 +584,15 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support modify DN operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
   FutureResult<Result> modifyDN(ModifyDNRequest request,
-      ResultHandler<Result> handler) throws UnsupportedOperationException,
-      IllegalStateException, NullPointerException;
+      ResultHandler<? super Result> handler)
+      throws UnsupportedOperationException, IllegalStateException,
+      NullPointerException;
 
 
 
@@ -608,13 +612,13 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support modify DN operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
   FutureResult<Result> modifyDN(ModifyDNRequest request,
-      ResultHandler<Result> resultHandler,
+      ResultHandler<? super Result> resultHandler,
       IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException;
@@ -649,8 +653,8 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support search operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If the {@code name} was {@code null}.
    */
@@ -676,10 +680,10 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support search operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    */
-  FutureResult<RootDSE> readRootDSE(ResultHandler<RootDSE> handler)
+  FutureResult<RootDSE> readRootDSE(ResultHandler<? super RootDSE> handler)
       throws UnsupportedOperationException, IllegalStateException;
 
 
@@ -703,10 +707,10 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support search operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    */
-  FutureResult<Schema> readSchema(DN name, ResultHandler<Schema> handler)
+  FutureResult<Schema> readSchema(DN name, ResultHandler<? super Schema> handler)
       throws UnsupportedOperationException, IllegalStateException;
 
 
@@ -734,10 +738,11 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support search operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    */
-  FutureResult<Schema> readSchemaForEntry(DN name, ResultHandler<Schema> handler)
+  FutureResult<Schema> readSchemaForEntry(DN name,
+      ResultHandler<? super Schema> handler)
       throws UnsupportedOperationException, IllegalStateException;
 
 
@@ -765,10 +770,7 @@
    *
    * @param request
    *          The search request.
-   * @param resultHandler
-   *          A result handler which can be used to asynchronously process the
-   *          operation result when it is received, may be {@code null}.
-   * @param searchResulthandler
+   * @param handler
    *          A search result handler which can be used to asynchronously
    *          process the search result entries and references as they are
    *          received, may be {@code null}.
@@ -776,14 +778,13 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support search operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
   FutureResult<Result> search(SearchRequest request,
-      ResultHandler<Result> resultHandler,
-      SearchResultHandler searchResulthandler)
+      SearchResultHandler handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException;
 
@@ -795,9 +796,6 @@
    * @param request
    *          The search request.
    * @param resultHandler
-   *          A result handler which can be used to asynchronously process the
-   *          operation result when it is received, may be {@code null}.
-   * @param searchResulthandler
    *          A search result handler which can be used to asynchronously
    *          process the search result entries and references as they are
    *          received, may be {@code null}.
@@ -808,14 +806,13 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support search operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
   FutureResult<Result> search(SearchRequest request,
-      ResultHandler<Result> resultHandler,
-      SearchResultHandler searchResulthandler,
+      SearchResultHandler resultHandler,
       IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException;
@@ -841,8 +838,8 @@
    * @throws UnsupportedOperationException
    *           If this connection does not support search operations.
    * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if {@code
-   *           isClosed() == true}.
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
    * @throws NullPointerException
    *           If the {@code request} was {@code null}.
    */
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java b/opendj-sdk/sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java
index a0030c0..1027917 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
@@ -85,7 +85,7 @@
 
 
     public FutureResult<Result> add(final AddRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -95,7 +95,7 @@
 
 
     public FutureResult<Result> add(final AddRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -179,7 +179,7 @@
 
 
     public FutureResult<Result> delete(final DeleteRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -189,7 +189,7 @@
 
 
     public FutureResult<Result> delete(final DeleteRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -254,7 +254,7 @@
 
 
     public FutureResult<Result> modify(final ModifyRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -264,7 +264,7 @@
 
 
     public FutureResult<Result> modify(final ModifyRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -276,7 +276,7 @@
 
 
     public FutureResult<Result> modifyDN(final ModifyDNRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -286,7 +286,7 @@
 
 
     public FutureResult<Result> modifyDN(final ModifyDNRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -315,7 +315,7 @@
      * {@inheritDoc}
      */
     public FutureResult<RootDSE> readRootDSE(
-        final ResultHandler<RootDSE> handler)
+        final ResultHandler<? super RootDSE> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
       return connection.readRootDSE(handler);
@@ -327,7 +327,7 @@
      * {@inheritDoc}
      */
     public FutureResult<Schema> readSchema(final DN name,
-        final ResultHandler<Schema> handler)
+        final ResultHandler<? super Schema> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
       return connection.readSchema(name, handler);
@@ -339,7 +339,7 @@
      * {@inheritDoc}
      */
     public FutureResult<Schema> readSchemaForEntry(final DN name,
-        final ResultHandler<Schema> handler)
+        final ResultHandler<? super Schema> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
       return connection.readSchemaForEntry(name, handler);
@@ -356,24 +356,22 @@
 
 
     public FutureResult<Result> search(final SearchRequest request,
-        final ResultHandler<Result> resultHandler,
-        final SearchResultHandler searchResulthandler)
+        final SearchResultHandler handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
-      return connection.search(request, resultHandler, searchResulthandler);
+      return connection.search(request, handler);
     }
 
 
 
     public FutureResult<Result> search(final SearchRequest request,
-        final ResultHandler<Result> resultHandler,
-        final SearchResultHandler searchResulthandler,
+        final SearchResultHandler resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
-      return connection.search(request, resultHandler, searchResulthandler,
+      return connection.search(request, resultHandler,
           intermediateResponseHandler);
     }
 
@@ -496,7 +494,7 @@
    */
   @Override
   public FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      final ResultHandler<AsynchronousConnection> handler)
+      final ResultHandler<? super AsynchronousConnection> handler)
   {
     final FutureResultImpl future = new FutureResultImpl(request, handler);
     future.futureConnectionResult.setFutureResult(parentFactory
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/Connection.java b/opendj-sdk/sdk/src/org/opends/sdk/Connection.java
index 96828e1..c5fab52 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/Connection.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/Connection.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
@@ -31,8 +31,9 @@
 
 import java.io.Closeable;
 import java.util.Collection;
-import java.util.List;
+import java.util.concurrent.BlockingQueue;
 
+import org.opends.sdk.ldif.ConnectionEntryReader;
 import org.opends.sdk.requests.*;
 import org.opends.sdk.responses.*;
 import org.opends.sdk.schema.Schema;
@@ -1102,21 +1103,19 @@
 
   /**
    * Searches the Directory Server using the provided search parameters. Any
-   * matching entries returned by the search will be added to a {@code List}
-   * which is returned if the search succeeds. Search result references will be
-   * discarded.
+   * matching entries returned by the search will be exposed through the
+   * {@code EntryReader} interface.
    * <p>
-   * <b>Warning:</b> Usage of this method is discouraged if the search request
-   * is expected to yield a large number of search results since the entire set
-   * of results will be stored in memory, potentially causing an {@code
-   * OutOfMemoryError}.
+   * <b>Warning:</b> When using a queue with an optional capacity bound,
+   * the connection will stop reading responses and wait if necessary for
+   * space to become available.
    * <p>
    * This method is equivalent to the following code:
    *
    * <pre>
    * SearchRequest request = new SearchRequest(baseDN, scope, filter,
    *     attributeDescriptions);
-   * connection.search(request, new LinkedList&lt;SearchResultEntry&gt;());
+   * connection.search(request, new LinkedBlockingQueue&lt;Response&gt;());
    * </pre>
    *
    * @param baseObject
@@ -1129,16 +1128,7 @@
    *          order for an entry to be returned.
    * @param attributeDescriptions
    *          The names of the attributes to be included with each entry.
-   * @return A list containing any matching entries returned by the search.
-   * @throws ErrorResultException
-   *           If the result code indicates that the request failed for some
-   *           reason.
-   * @throws InterruptedException
-   *           If the current thread was interrupted while waiting.
-   * @throws LocalizedIllegalArgumentException
-   *           If {@code baseObject} could not be decoded using the default
-   *           schema or if {@code filter} is not a valid LDAP string
-   *           representation of a filter.
+   * @return An entry reader exposing the returned entries.
    * @throws UnsupportedOperationException
    *           If this connection does not support search operations.
    * @throws IllegalStateException
@@ -1148,13 +1138,39 @@
    *           If the {@code baseObject}, {@code scope}, or {@code filter} were
    *           {@code null}.
    */
-  List<SearchResultEntry> search(String baseObject, SearchScope scope,
+  ConnectionEntryReader search(String baseObject, SearchScope scope,
       String filter, String... attributeDescriptions)
-      throws ErrorResultException, InterruptedException,
-      LocalizedIllegalArgumentException, UnsupportedOperationException,
+      throws UnsupportedOperationException,
       IllegalStateException, NullPointerException;
 
 
+  /**
+   * Searches the Directory Server using the provided search parameters. Any
+   * matching entries returned by the search will be exposed through the
+   * {@code EntryReader} interface.
+   * <p>
+   * <b>Warning:</b> When using a queue with an optional capacity bound,
+   * the connection will stop reading responses and wait if necessary for
+   * space to become available.
+   *
+   * @param request
+   *          The search request.
+   * @param entries
+   *          The queue to which matching entries should be added.
+   * @return The result of the operation.
+   * @throws UnsupportedOperationException
+   *           If this connection does not support search operations.
+   * @throws IllegalStateException
+   *           If this connection has already been closed, i.e. if {@code
+   *           isClosed() == true}.
+   * @throws NullPointerException
+   *           If {@code request} or {@code entries} was {@code null}.
+   */
+  ConnectionEntryReader search(SearchRequest request,
+                               BlockingQueue<Response> entries)
+      throws UnsupportedOperationException, IllegalStateException,
+      NullPointerException;
+
 
   /**
    * Searches the Directory Server for a single entry using the provided search
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/ConnectionEventListener.java b/opendj-sdk/sdk/src/org/opends/sdk/ConnectionEventListener.java
index 6225618..f94355b 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/ConnectionEventListener.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/ConnectionEventListener.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
@@ -39,16 +39,6 @@
  * An object that registers to be notified when a connection is closed by the
  * application, receives an unsolicited notification, or experiences a fatal
  * error.
- * <p>
- * TODO: isolate fatal connection errors as a sub-type of ErrorResultException.
- * <p>
- * TODO: do we need client initiated close notification as in JCA / JDBC? A
- * simpler approach would be for the connection pool to wrap the underlying
- * physical connection with its own. It can then intercept the close request
- * from the client. This has the disadvantage in that we lose any specialized
- * methods exposed by the underlying physical connection (i.e. if the physical
- * connection extends Connection and provides additional methods) since the
- * connection pool effectively hides them via its wrapper.
  */
 public interface ConnectionEventListener extends EventListener
 {
@@ -58,7 +48,7 @@
    * notified immediately after the application calls the {@code close} method
    * on the associated connection.
    */
-  void connectionClosed();
+  void handleConnectionClosed();
 
 
 
@@ -69,10 +59,10 @@
    * the provided {@link ErrorResultException} to the application.
    * <p>
    * <b>Note:</b> disconnect notifications are treated as fatal connection
-   * errors and are handled by this method. In this case {@code
-   * isDisconnectNotification} will be {@code true} and {@code error} will
-   * contain the result code and any diagnostic information contained in the
-   * notification message.
+   * errors and are handled by this method. In this case
+   * {@code isDisconnectNotification} will be {@code true} and {@code error}
+   * will contain the result code and any diagnostic information contained in
+   * the notification message.
    *
    * @param isDisconnectNotification
    *          {@code true} if the error was triggered by a disconnect
@@ -80,7 +70,7 @@
    * @param error
    *          The exception that is about to be thrown to the application.
    */
-  void connectionErrorOccurred(boolean isDisconnectNotification,
+  void handleConnectionError(boolean isDisconnectNotification,
       ErrorResultException error);
 
 
@@ -90,10 +80,10 @@
    * received the provided unsolicited notification from the server.
    * <p>
    * <b>Note:</b> disconnect notifications are treated as fatal connection
-   * errors and are handled by the {@link #connectionErrorOccurred} method.
+   * errors and are handled by the {@link #handleConnectionError} method.
    *
    * @param notification
-   *          The unsolicited notification
+   *          The unsolicited notification.
    */
-  void connectionReceivedUnsolicitedNotification(ExtendedResult notification);
+  void handleUnsolicitedNotification(ExtendedResult notification);
 }
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/ConnectionFactory.java b/opendj-sdk/sdk/src/org/opends/sdk/ConnectionFactory.java
index 1737262..45417cb 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/ConnectionFactory.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/ConnectionFactory.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
@@ -66,7 +66,7 @@
    * @return A future which can be used to retrieve the asynchronous connection.
    */
   FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      ResultHandler<AsynchronousConnection> handler);
+      ResultHandler<? super AsynchronousConnection> handler);
 
 
 
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/ConnectionPool.java b/opendj-sdk/sdk/src/org/opends/sdk/ConnectionPool.java
index de641ce..1789d1d 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/ConnectionPool.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/ConnectionPool.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
@@ -104,7 +104,7 @@
 
 
     public FutureResult<Result> add(final AddRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -118,7 +118,7 @@
 
 
     public FutureResult<Result> add(final AddRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -254,14 +254,14 @@
 
 
 
-    public void connectionClosed()
+    public void handleConnectionClosed()
     {
       // Ignore - we intercept close via the close method.
     }
 
 
 
-    public void connectionErrorOccurred(final boolean isDisconnectNotification,
+    public void handleConnectionError(final boolean isDisconnectNotification,
         final ErrorResultException error)
     {
       // Remove this connection from the pool if its in there. If not,
@@ -289,7 +289,7 @@
 
 
 
-    public void connectionReceivedUnsolicitedNotification(
+    public void handleUnsolicitedNotification(
         final ExtendedResult notification)
     {
       // Ignore
@@ -298,7 +298,7 @@
 
 
     public FutureResult<Result> delete(final DeleteRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -312,7 +312,7 @@
 
 
     public FutureResult<Result> delete(final DeleteRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -386,7 +386,7 @@
 
 
     public FutureResult<Result> modify(final ModifyRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -400,7 +400,7 @@
 
 
     public FutureResult<Result> modify(final ModifyRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -416,7 +416,7 @@
 
 
     public FutureResult<Result> modifyDN(final ModifyDNRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -430,7 +430,7 @@
 
 
     public FutureResult<Result> modifyDN(final ModifyDNRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -467,7 +467,7 @@
      * {@inheritDoc}
      */
     public FutureResult<RootDSE> readRootDSE(
-        final ResultHandler<RootDSE> handler)
+        final ResultHandler<? super RootDSE> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
       if (isClosed())
@@ -483,7 +483,7 @@
      * {@inheritDoc}
      */
     public FutureResult<Schema> readSchema(final DN name,
-        final ResultHandler<Schema> handler)
+        final ResultHandler<? super Schema> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
       if (isClosed())
@@ -499,7 +499,7 @@
      * {@inheritDoc}
      */
     public FutureResult<Schema> readSchemaForEntry(final DN name,
-        final ResultHandler<Schema> handler)
+        final ResultHandler<? super Schema> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
       if (isClosed())
@@ -523,8 +523,7 @@
 
 
     public FutureResult<Result> search(final SearchRequest request,
-        final ResultHandler<Result> resultHandler,
-        final SearchResultHandler searchResulthandler)
+        final SearchResultHandler handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -532,14 +531,13 @@
       {
         throw new IllegalStateException();
       }
-      return connection.search(request, resultHandler, searchResulthandler);
+      return connection.search(request, handler);
     }
 
 
 
     public FutureResult<Result> search(final SearchRequest request,
-        final ResultHandler<Result> resultHandler,
-        final SearchResultHandler searchResulthandler,
+        final SearchResultHandler resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -548,7 +546,7 @@
       {
         throw new IllegalStateException();
       }
-      return connection.search(request, resultHandler, searchResulthandler,
+      return connection.search(request, resultHandler,
           intermediateResponseHandler);
     }
 
@@ -654,7 +652,7 @@
 
   @Override
   public synchronized FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      final ResultHandler<AsynchronousConnection> handler)
+      final ResultHandler<? super AsynchronousConnection> handler)
   {
     // This entire method is synchronized to ensure new connects are
     // done synchronously to avoid the "pending connect" case.
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java b/opendj-sdk/sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java
index 4843021..4910e44 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java
@@ -78,7 +78,7 @@
 
     @Override
     public FutureResult<AsynchronousConnection> getAsynchronousConnection(
-        final ResultHandler<AsynchronousConnection> resultHandler)
+        final ResultHandler<? super AsynchronousConnection> resultHandler)
     {
       final ResultHandler<AsynchronousConnection> handler =
         new ResultHandler<AsynchronousConnection>()
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java b/opendj-sdk/sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java
index e3fb48f..ccf41df 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
@@ -53,7 +53,7 @@
    * operations.
    */
   private final class AsynchronousConnectionImpl implements
-      AsynchronousConnection, ConnectionEventListener, ResultHandler<Result>
+      AsynchronousConnection, ConnectionEventListener, SearchResultHandler
   {
     private final AsynchronousConnection connection;
 
@@ -80,7 +80,7 @@
 
 
     public FutureResult<Result> add(final AddRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -90,7 +90,7 @@
 
 
     public FutureResult<Result> add(final AddRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -179,14 +179,14 @@
 
 
 
-    public void connectionClosed()
+    public void handleConnectionClosed()
     {
       // Ignore - we intercept close through the close method.
     }
 
 
 
-    public void connectionErrorOccurred(final boolean isDisconnectNotification,
+    public void handleConnectionError(final boolean isDisconnectNotification,
         final ErrorResultException error)
     {
       synchronized (activeConnections)
@@ -198,7 +198,7 @@
 
 
 
-    public void connectionReceivedUnsolicitedNotification(
+    public void handleUnsolicitedNotification(
         final ExtendedResult notification)
     {
       // Do nothing
@@ -207,7 +207,7 @@
 
 
     public FutureResult<Result> delete(final DeleteRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -217,7 +217,7 @@
 
 
     public FutureResult<Result> delete(final DeleteRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -261,6 +261,17 @@
 
 
 
+    /**
+     * {@inheritDoc}
+     */
+    public boolean handleEntry(SearchResultEntry entry)
+    {
+      // Ignore.
+      return true;
+    }
+
+
+
     public void handleErrorResult(final ErrorResultException error)
     {
       connection.close(Requests.newUnbindRequest(), "Heartbeat retured error: "
@@ -269,6 +280,17 @@
 
 
 
+    /**
+     * {@inheritDoc}
+     */
+    public boolean handleReference(SearchResultReference reference)
+    {
+      // Ignore.
+      return true;
+    }
+
+
+
     public void handleResult(final Result result)
     {
       lastSuccessfulPing = System.currentTimeMillis();
@@ -299,7 +321,7 @@
 
 
     public FutureResult<Result> modify(final ModifyRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -309,7 +331,7 @@
 
 
     public FutureResult<Result> modify(final ModifyRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -321,7 +343,7 @@
 
 
     public FutureResult<Result> modifyDN(final ModifyDNRequest request,
-        final ResultHandler<Result> handler)
+        final ResultHandler<? super Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
@@ -331,7 +353,7 @@
 
 
     public FutureResult<Result> modifyDN(final ModifyDNRequest request,
-        final ResultHandler<Result> resultHandler,
+        final ResultHandler<? super Result> resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -360,7 +382,7 @@
      * {@inheritDoc}
      */
     public FutureResult<RootDSE> readRootDSE(
-        final ResultHandler<RootDSE> handler)
+        final ResultHandler<? super RootDSE> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
       return connection.readRootDSE(handler);
@@ -372,7 +394,7 @@
      * {@inheritDoc}
      */
     public FutureResult<Schema> readSchema(final DN name,
-        final ResultHandler<Schema> handler)
+        final ResultHandler<? super Schema> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
       return connection.readSchema(name, handler);
@@ -384,7 +406,7 @@
      * {@inheritDoc}
      */
     public FutureResult<Schema> readSchemaForEntry(final DN name,
-        final ResultHandler<Schema> handler)
+        final ResultHandler<? super Schema> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
       return connection.readSchemaForEntry(name, handler);
@@ -401,24 +423,22 @@
 
 
     public FutureResult<Result> search(final SearchRequest request,
-        final ResultHandler<Result> resultHandler,
-        final SearchResultHandler searchResultHandler)
+        final SearchResultHandler handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
-      return connection.search(request, resultHandler, searchResultHandler);
+      return connection.search(request, handler);
     }
 
 
 
     public FutureResult<Result> search(final SearchRequest request,
-        final ResultHandler<Result> resultHandler,
-        final SearchResultHandler searchResulthandler,
+        final SearchResultHandler resultHandler,
         final IntermediateResponseHandler intermediateResponseHandler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
     {
-      return connection.search(request, resultHandler, searchResulthandler,
+      return connection.search(request, resultHandler,
           intermediateResponseHandler);
     }
 
@@ -590,7 +610,7 @@
 
   @Override
   public FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      final ResultHandler<AsynchronousConnection> handler)
+      final ResultHandler<? super AsynchronousConnection> handler)
   {
     final FutureResultImpl future = new FutureResultImpl(handler);
     future.setFutureResult(parentFactory.getAsynchronousConnection(future));
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/InternalConnectionFactory.java b/opendj-sdk/sdk/src/org/opends/sdk/InternalConnectionFactory.java
index ba990db..8749d7a 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/InternalConnectionFactory.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/InternalConnectionFactory.java
@@ -61,7 +61,7 @@
 
   @Override
   public FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      final ResultHandler<AsynchronousConnection> handler)
+      final ResultHandler<? super AsynchronousConnection> handler)
   {
     final ServerConnection<Integer> serverConnection;
     try
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/LDAPClientContext.java b/opendj-sdk/sdk/src/org/opends/sdk/LDAPClientContext.java
index 194c7c4..878e020 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/LDAPClientContext.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/LDAPClientContext.java
@@ -46,13 +46,45 @@
 public interface LDAPClientContext
 {
   /**
-   * Disconnects the client and optionally sends a disconnect notification.
+   * Registers the provided connection event listener so that it will be
+   * notified when the underlying connection is closed by the client, receives
+   * an unsolicited notification, or experiences a fatal error.
+   * <p>
+   * This method provides a event notification mechanism which can be used by
+   * asynchronous request handler implementations to detect connection
+   * termination.
    *
-   * @param sendNotification
-   *          {@code true} to send a disconnect notification, or {@code false}
-   *          otherwise.
+   * @param listener
+   *          The listener which wants to be notified when events occur on the
+   *          underlying connection.
+   * @throws NullPointerException
+   *           If the {@code listener} was {@code null}.
+   * @see #isClosed
    */
-  void disconnect(boolean sendNotification);
+  void addConnectionEventListener(ConnectionEventListener listener)
+      throws NullPointerException;
+
+
+
+  /**
+   * Disconnects the client without sending a disconnect notification.
+   */
+  void disconnect();
+
+
+
+  /**
+   * Disconnects the client and sends a disconnect notification, if possible,
+   * containing the provided result code and diagnostic message.
+   *
+   * @param resultCode
+   *          The result code which should be included with the disconnect
+   *          notification.
+   * @param message
+   *          The diagnostic message, which may be empty or {@code null}
+   *          indicating that none was provided.
+   */
+  void disconnect(ResultCode resultCode, String message);
 
 
 
@@ -86,6 +118,39 @@
 
 
   /**
+   * Returns {@code true} if the underlying connection is closed by the client,
+   * receives an unsolicited notification, or experiences a fatal error.
+   * <p>
+   * This method provides a polling mechanism which can be used by synchronous
+   * request handler implementations to detect connection termination.
+   *
+   * @return {@code true} if the underlying connection is closed by the client,
+   *         receives an unsolicited notification, or experiences a fatal error,
+   *         otherwise {@code false}.
+   * @see #addConnectionEventListener
+   */
+  boolean isClosed();
+
+
+
+  /**
+   * Removes the provided connection event listener from this client context so
+   * that it will no longer be notified when the underlying connection is closed
+   * by the application, receives an unsolicited notification, or experiences a
+   * fatal error.
+   *
+   * @param listener
+   *          The listener which no longer wants to be notified when events
+   *          occur on the underlying connection.
+   * @throws NullPointerException
+   *           If the {@code listener} was {@code null}.
+   */
+  void removeConnectionEventListener(ConnectionEventListener listener)
+      throws NullPointerException;
+
+
+
+  /**
    * Sends an unsolicited notification to the client.
    *
    * @param notification
@@ -112,7 +177,19 @@
    *
    * @param sslContext
    *          The {@code SSLContext} which should be used to secure the
-   *          connection.
+   * @param protocols
+   *          Names of all the protocols to enable or {@code null} to use the
+   *          default protocols.
+   * @param suites
+   *          Names of all the suites to enable or {@code null} to use the
+   *          default cipher suites.
+   * @param wantClientAuth
+   *          Set to {@code true} if client authentication is requested, or
+   *          {@code false} if no client authentication is desired.
+   * @param needClientAuth
+   *          Set to {@code true} if client authentication is required, or
+   *          {@code false} if no client authentication is desired.
    */
-  void startTLS(SSLContext sslContext);
+  void startTLS(SSLContext sslContext, String[] protocols, String[] suites,
+      boolean wantClientAuth, boolean needClientAuth);
 }
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/LDAPConnectionFactory.java b/opendj-sdk/sdk/src/org/opends/sdk/LDAPConnectionFactory.java
index 25a52e6..5cbde4a 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/LDAPConnectionFactory.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/LDAPConnectionFactory.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
@@ -134,7 +134,7 @@
    * {@inheritDoc}
    */
   public FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      final ResultHandler<AsynchronousConnection> handler)
+      final ResultHandler<? super AsynchronousConnection> handler)
   {
     return impl.getAsynchronousConnection(handler);
   }
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/LDAPOptions.java b/opendj-sdk/sdk/src/org/opends/sdk/LDAPOptions.java
index 0c17217..f58cbfa 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/LDAPOptions.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/LDAPOptions.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
@@ -50,6 +50,16 @@
 
   private DecodeOptions decodeOptions;
 
+  /**
+   * The list of cipher suite
+   */
+  private String[] enabledCipherSuites = null;
+
+  /**
+   * the list of protocols
+   */
+  private String[] enabledProtocols = null;
+
 
 
   /**
@@ -80,6 +90,8 @@
     this.timeoutInMillis = options.timeoutInMillis;
     this.useStartTLS = options.useStartTLS;
     this.decodeOptions = new DecodeOptions(options.decodeOptions);
+    this.enabledCipherSuites = options.enabledCipherSuites;
+    this.enabledProtocols = options.enabledProtocols;
   }
 
 
@@ -224,4 +236,66 @@
     return useStartTLS;
   }
 
+  /**
+   * Set the protocol versions enabled for secure connections with the
+   * Directory Server.
+   *
+   * The protocols must be supported by the SSLContext specified in
+   * {@link #setSSLContext(SSLContext)}. Following a successful call to
+   * this method, only the protocols listed in the protocols parameter are
+   * enabled for use.
+   *
+   * @param protocols Names of all the protocols to enable or {@code null} to
+   *                  use the default protocols.
+   * @return A reference to this LDAP connection options.
+   */
+  public final LDAPOptions setEnabledProtocols(String[] protocols)
+  {
+    this.enabledProtocols = protocols;
+    return this;
+  }
+
+  /**
+   * Set the cipher suites enabled for secure connections with the
+   * Directory Server.
+   *
+   * The suites must be supported by the SSLContext specified in
+   * {@link #setSSLContext(SSLContext)}. Following a successful call to
+   * this method, only the suites listed in the protocols parameter are
+   * enabled for use.
+   *
+   * @param suites Names of all the suites to enable or {@code null} to
+   *                  use the default cipher suites.
+   * @return A reference to this LDAP connection options.
+   */
+  public final LDAPOptions setEnabledCipherSuites(String[] suites)
+  {
+    this.enabledCipherSuites = suites;
+    return this;
+  }
+
+  /**
+   * Returns the names of the protocol versions which are currently enabled
+   * for secure connections with the Directory Server.
+   *
+   * @return an array of protocols or {@code null} if the default protocols
+   * are to be used.
+   */
+  public final String[] getEnabledProtocols()
+  {
+    return this.enabledProtocols;
+  }
+
+  /**
+   * Returns the names of the protocol versions which are currently enabled
+   * for secure connections with the Directory Server.
+   *
+   * @return an array of protocols or {@code null} if the default protocols
+   * are to be used.
+   */
+  public final  String[] getEnabledCipherSuites()
+  {
+    return this.enabledCipherSuites;
+  }
+
 }
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/LDAPUrl.java b/opendj-sdk/sdk/src/org/opends/sdk/LDAPUrl.java
index d7a23fb..37d5938 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/LDAPUrl.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/LDAPUrl.java
@@ -463,7 +463,7 @@
     else
     {
       listenPort = port.intValue();
-      if (listenPort < 1 && listenPort > Integer.MAX_VALUE)
+      if (listenPort < 1 || listenPort > 65535)
       {
         final LocalizableMessage msg = ERR_LDAPURL_BAD_PORT.get(listenPort);
         throw new LocalizedIllegalArgumentException(msg);
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/LoadBalancingConnectionFactory.java b/opendj-sdk/sdk/src/org/opends/sdk/LoadBalancingConnectionFactory.java
index 5e416d7..af4b0b7 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/LoadBalancingConnectionFactory.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/LoadBalancingConnectionFactory.java
@@ -54,7 +54,7 @@
 
   @Override
   public FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      final ResultHandler<AsynchronousConnection> resultHandler)
+      final ResultHandler<? super AsynchronousConnection> resultHandler)
   {
     ConnectionFactory factory;
 
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/RootDSE.java b/opendj-sdk/sdk/src/org/opends/sdk/RootDSE.java
index 9410450..b5762dd 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/RootDSE.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/RootDSE.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
@@ -156,7 +156,7 @@
    */
   public static FutureResult<RootDSE> readRootDSE(
       final AsynchronousConnection connection,
-      final ResultHandler<RootDSE> handler)
+      final ResultHandler<? super RootDSE> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/SearchResultHandler.java b/opendj-sdk/sdk/src/org/opends/sdk/SearchResultHandler.java
index fc059b0..9f870d5 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/SearchResultHandler.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/SearchResultHandler.java
@@ -22,13 +22,14 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
 
 
 
+import org.opends.sdk.responses.Result;
 import org.opends.sdk.responses.SearchResultEntry;
 import org.opends.sdk.responses.SearchResultReference;
 
@@ -48,7 +49,7 @@
  * avoid keeping the invoking thread from dispatching to other completion
  * handlers.
  */
-public interface SearchResultHandler
+public interface SearchResultHandler extends ResultHandler<Result>
 {
   /**
    * Invoked each time a search result entry is returned from an asynchronous
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/SearchResultReferenceIOException.java b/opendj-sdk/sdk/src/org/opends/sdk/SearchResultReferenceIOException.java
new file mode 100644
index 0000000..ea7ba1a
--- /dev/null
+++ b/opendj-sdk/sdk/src/org/opends/sdk/SearchResultReferenceIOException.java
@@ -0,0 +1,81 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2010 Sun Microsystems, Inc.
+ */
+
+package org.opends.sdk;
+
+
+
+import java.io.IOException;
+
+import org.opends.sdk.responses.SearchResultReference;
+
+import com.sun.opends.sdk.util.Validator;
+
+
+
+/**
+ * Thrown when an iteration over a set of search results using a
+ * {@code ConnectionEntryReader} encounters a {@code SearchResultReference}.
+ */
+@SuppressWarnings("serial")
+public final class SearchResultReferenceIOException extends IOException
+{
+  private final SearchResultReference reference;
+
+
+
+  /**
+   * Creates a new referral result IO exception with the provided
+   * {@code SearchResultReference}.
+   *
+   * @param reference
+   *          The {@code SearchResultReference} which may be later retrieved by
+   *          the {@link #getReference} method.
+   * @throws NullPointerException
+   *           If {@code reference} was {@code null}.
+   */
+  public SearchResultReferenceIOException(final SearchResultReference reference)
+      throws NullPointerException
+  {
+    super(Validator.ensureNotNull(reference).toString());
+    this.reference = reference;
+  }
+
+
+
+  /**
+   * Returns the {@code SearchResultReference} which was encountered while
+   * processing the search results.
+   *
+   * @return The {@code SearchResultReference} which was encountered while
+   *         processing the search results.
+   */
+  public SearchResultReference getReference()
+  {
+    return reference;
+  }
+}
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/ServerConnection.java b/opendj-sdk/sdk/src/org/opends/sdk/ServerConnection.java
index ef81e2a..950f6dc 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/ServerConnection.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/ServerConnection.java
@@ -56,7 +56,7 @@
    * @throws UnsupportedOperationException
    *           If this server connection does not handle abandon requests.
    */
-  void abandon(C requestContext, AbandonRequest request)
+  void handleAbandon(C requestContext, AbandonRequest request)
       throws UnsupportedOperationException;
 
 
@@ -77,8 +77,8 @@
    * @throws UnsupportedOperationException
    *           If this server connection does not handle add requests.
    */
-  void add(C requestContext, AddRequest request,
-      ResultHandler<Result> resultHandler,
+  void handleAdd(C requestContext, AddRequest request,
+      ResultHandler<? super Result> resultHandler,
       IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException;
 
@@ -102,7 +102,7 @@
    * @throws UnsupportedOperationException
    *           If this server connection does not handle bind requests.
    */
-  void bind(C requestContext, int version, BindRequest request,
+  void handleBind(C requestContext, int version, BindRequest request,
       ResultHandler<? super BindResult> resultHandler,
       IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException;
@@ -110,30 +110,6 @@
 
 
   /**
-   * Invoked when the client closes the connection, possibly using an unbind
-   * request.
-   *
-   * @param requestContext
-   *          The request context.
-   * @param request
-   *          The unbind request, which may be {@code null} if one was not sent
-   *          before the connection was closed.
-   */
-  void closed(C requestContext, UnbindRequest request);
-
-
-
-  /**
-   * Invoked when an error occurs on the connection and it is no longer usable.
-   *
-   * @param error
-   *          The exception describing the problem that occurred.
-   */
-  void closed(Throwable error);
-
-
-
-  /**
    * Invoked when a compare request is received from a client.
    *
    * @param requestContext
@@ -149,7 +125,7 @@
    * @throws UnsupportedOperationException
    *           If this server connection does not handle compare requests.
    */
-  void compare(C requestContext, CompareRequest request,
+  void handleCompare(C requestContext, CompareRequest request,
       ResultHandler<? super CompareResult> resultHandler,
       IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException;
@@ -157,6 +133,30 @@
 
 
   /**
+   * Invoked when the client closes the connection, possibly using an unbind
+   * request.
+   *
+   * @param requestContext
+   *          The request context.
+   * @param request
+   *          The unbind request, which may be {@code null} if one was not sent
+   *          before the connection was closed.
+   */
+  void handleConnectionClosed(C requestContext, UnbindRequest request);
+
+
+
+  /**
+   * Invoked when an error occurs on the connection and it is no longer usable.
+   *
+   * @param error
+   *          The exception describing the problem that occurred.
+   */
+  void handleConnectionException(Throwable error);
+
+
+
+  /**
    * Invoked when a delete request is received from a client.
    *
    * @param requestContext
@@ -172,8 +172,8 @@
    * @throws UnsupportedOperationException
    *           If this server connection does not handle delete requests.
    */
-  void delete(C requestContext, DeleteRequest request,
-      ResultHandler<Result> resultHandler,
+  void handleDelete(C requestContext, DeleteRequest request,
+      ResultHandler<? super Result> resultHandler,
       IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException;
 
@@ -197,8 +197,8 @@
    * @throws UnsupportedOperationException
    *           If this server connection does not handle extended requests.
    */
-  <R extends ExtendedResult> void extendedRequest(C requestContext,
-      ExtendedRequest<R> request, ResultHandler<R> resultHandler,
+  <R extends ExtendedResult> void handleExtendedRequest(C requestContext,
+      ExtendedRequest<R> request, ResultHandler<? super R> resultHandler,
       IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException;
 
@@ -220,8 +220,8 @@
    * @throws UnsupportedOperationException
    *           If this server connection does not handle modify requests.
    */
-  void modify(C requestContext, ModifyRequest request,
-      ResultHandler<Result> resultHandler,
+  void handleModify(C requestContext, ModifyRequest request,
+      ResultHandler<? super Result> resultHandler,
       IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException;
 
@@ -243,8 +243,8 @@
    * @throws UnsupportedOperationException
    *           If this server connection does not handle modify DN requests.
    */
-  void modifyDN(C requestContext, ModifyDNRequest request,
-      ResultHandler<Result> resultHandler,
+  void handleModifyDN(C requestContext, ModifyDNRequest request,
+      ResultHandler<? super Result> resultHandler,
       IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException;
 
@@ -269,8 +269,8 @@
    * @throws UnsupportedOperationException
    *           If this server connection does not handle search requests.
    */
-  void search(C requestContext, SearchRequest request,
-      ResultHandler<Result> resultHandler,
+  void handleSearch(C requestContext, SearchRequest request,
+      ResultHandler<? super Result> resultHandler,
       SearchResultHandler searchResulthandler,
       IntermediateResponseHandler intermediateResponseHandler)
       throws UnsupportedOperationException;
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/SynchronousConnection.java b/opendj-sdk/sdk/src/org/opends/sdk/SynchronousConnection.java
index 1d82232..b1bac79 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/SynchronousConnection.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/SynchronousConnection.java
@@ -22,22 +22,21 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk;
 
 
 
+import org.opends.sdk.ldif.ConnectionEntryReader;
 import org.opends.sdk.requests.*;
-import org.opends.sdk.responses.BindResult;
-import org.opends.sdk.responses.CompareResult;
-import org.opends.sdk.responses.ExtendedResult;
-import org.opends.sdk.responses.Result;
+import org.opends.sdk.responses.*;
 import org.opends.sdk.schema.Schema;
 
 import com.sun.opends.sdk.util.Validator;
 
+import java.util.concurrent.BlockingQueue;
 
 
 /**
@@ -350,8 +349,7 @@
       InterruptedException, UnsupportedOperationException,
       IllegalStateException, NullPointerException
   {
-    final FutureResult<Result> future = connection.search(request, null,
-        handler);
+    final FutureResult<Result> future = connection.search(request, handler);
     try
     {
       return future.get();
@@ -363,4 +361,15 @@
     }
   }
 
+  /**
+   * {@inheritDoc}
+   */
+  public ConnectionEntryReader search(final SearchRequest request,
+                            BlockingQueue<Response> entries)
+      throws UnsupportedOperationException, IllegalStateException,
+      NullPointerException
+  {
+    return new ConnectionEntryReader(getAsynchronousConnection(),
+        request, entries);
+  }
 }
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/controls/EntryChangeNotificationResponseControl.java b/opendj-sdk/sdk/src/org/opends/sdk/controls/EntryChangeNotificationResponseControl.java
index 1c3a5b4..6c29336 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/controls/EntryChangeNotificationResponseControl.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/controls/EntryChangeNotificationResponseControl.java
@@ -157,16 +157,19 @@
 
       final Schema schema = options.getSchemaResolver().resolveSchema(
           previousDNString);
-      DN previousDN;
-      try
+      DN previousDN = null;
+      if (previousDNString != null)
       {
-        previousDN = DN.valueOf(previousDNString, schema);
-      }
-      catch (final LocalizedIllegalArgumentException e)
-      {
-        final LocalizableMessage message = ERR_ECN_INVALID_PREVIOUS_DN
-            .get(getExceptionMessage(e));
-        throw DecodeException.error(message, e);
+        try
+        {
+          previousDN = DN.valueOf(previousDNString, schema);
+        }
+        catch (final LocalizedIllegalArgumentException e)
+        {
+          final LocalizableMessage message = ERR_ECN_INVALID_PREVIOUS_DN
+              .get(getExceptionMessage(e));
+          throw DecodeException.error(message, e);
+        }
       }
 
       return new EntryChangeNotificationResponseControl(control.isCritical(),
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/ldif/ConnectionEntryReader.java b/opendj-sdk/sdk/src/org/opends/sdk/ldif/ConnectionEntryReader.java
new file mode 100644
index 0000000..17a0be2
--- /dev/null
+++ b/opendj-sdk/sdk/src/org/opends/sdk/ldif/ConnectionEntryReader.java
@@ -0,0 +1,311 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2010 Sun Microsystems, Inc.
+ */
+
+package org.opends.sdk.ldif;
+
+
+
+import java.io.InterruptedIOException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.opends.sdk.*;
+import org.opends.sdk.requests.SearchRequest;
+import org.opends.sdk.responses.*;
+
+import com.sun.opends.sdk.util.Validator;
+
+
+
+/**
+ * A {@code ConnectionEntryReader} is a bridge from
+ * {@code AsynchronousConnection}s to {@code EntryReader}s. A connection entry
+ * reader allows applications to iterate over search results as they are
+ * returned from the server during a search operation.
+ * <p>
+ * The Search operation is performed synchronously, blocking until a search
+ * result entry is received. If a search result indicates that the search
+ * operation has failed for some reason then the error result is propagated to
+ * the caller using an {@code ErrorResultIOException}. If a search result
+ * reference is returned then it is propagated to the caller using a
+ * {@code SearchResultReferenceIOException}.
+ * <p>
+ * The following code illustrates how a {@code ConnectionEntryReader} may be
+ * used:
+ *
+ * <pre>
+ * Connection connection = ...;
+ * ConnectionEntryReader results = connection.search(
+ *     &quot;dc=example,dc=com&quot;,
+ *     SearchScope.WHOLE_SUBTREE,
+ *     &quot;(objectClass=person)&quot;);
+ * SearchResultEntry entry;
+ * try
+ * {
+ *   while ((entry = results.readEntry()) != null)
+ *   {
+ *     // Process search result entry.
+ *   }
+ * }
+ * catch (Exception e)
+ * {
+ *   // Handle exceptions
+ * }
+ * finally
+ * {
+ *   results.close();
+ * }
+ * </pre>
+ */
+public final class ConnectionEntryReader implements EntryReader
+{
+
+  /**
+   * Result handler that places all responses in a queue.
+   */
+  private final static class BufferHandler implements SearchResultHandler
+  {
+    private final BlockingQueue<Response> responses;
+    private volatile boolean isInterrupted = false;
+
+
+
+    private BufferHandler(final BlockingQueue<Response> responses)
+    {
+      this.responses = responses;
+    }
+
+
+
+    @Override
+    public boolean handleEntry(final SearchResultEntry entry)
+    {
+      try
+      {
+        responses.put(entry);
+        return true;
+      }
+      catch (final InterruptedException e)
+      {
+        // Prevent the reader from waiting for a result that will never arrive.
+        isInterrupted = true;
+
+        Thread.currentThread().interrupt();
+        return false;
+      }
+    }
+
+
+
+    @Override
+    public void handleErrorResult(final ErrorResultException error)
+    {
+      try
+      {
+        responses.put(error.getResult());
+      }
+      catch (final InterruptedException e)
+      {
+        // Prevent the reader from waiting for a result that will never arrive.
+        isInterrupted = true;
+
+        Thread.currentThread().interrupt();
+      }
+    }
+
+
+
+    @Override
+    public boolean handleReference(final SearchResultReference reference)
+    {
+      try
+      {
+        responses.put(reference);
+        return true;
+      }
+      catch (final InterruptedException e)
+      {
+        // Prevent the reader from waiting for a result that will never arrive.
+        isInterrupted = true;
+
+        Thread.currentThread().interrupt();
+        return false;
+      }
+    }
+
+
+
+    @Override
+    public void handleResult(final Result result)
+    {
+      try
+      {
+        responses.put(result);
+      }
+      catch (final InterruptedException e)
+      {
+        // Prevent the reader from waiting for a result that will never arrive.
+        isInterrupted = true;
+
+        Thread.currentThread().interrupt();
+      }
+    }
+  }
+
+
+
+  private final BufferHandler buffer;
+  private final FutureResult<Result> future;
+
+
+
+  /**
+   * Creates a new connection entry reader whose destination is the provided
+   * connection using an unbounded {@code LinkedBlockingQueue}.
+   *
+   * @param connection
+   *          The connection to use.
+   * @param searchRequest
+   *          The search request to retrieve entries with.
+   * @throws NullPointerException
+   *           If {@code connection} was {@code null}.
+   */
+  public ConnectionEntryReader(final AsynchronousConnection connection,
+      final SearchRequest searchRequest) throws NullPointerException
+  {
+    this(connection, searchRequest, new LinkedBlockingQueue<Response>());
+  }
+
+
+
+  /**
+   * Creates a new connection entry reader whose destination is the provided
+   * connection.
+   *
+   * @param connection
+   *          The connection to use.
+   * @param searchRequest
+   *          The search request to retrieve entries with.
+   * @param entries
+   *          The {@code BlockingQueue} implementation to use when queuing the
+   *          returned entries.
+   * @throws NullPointerException
+   *           If {@code connection} was {@code null}.
+   */
+  public ConnectionEntryReader(final AsynchronousConnection connection,
+      final SearchRequest searchRequest, final BlockingQueue<Response> entries)
+      throws NullPointerException
+  {
+    Validator.ensureNotNull(connection);
+    buffer = new BufferHandler(entries);
+    future = connection.search(searchRequest, buffer);
+  }
+
+
+
+  /**
+   * Closes this connection entry reader, cancelling the search request if it is
+   * still active.
+   */
+  @Override
+  public void close()
+  {
+    // Cancel the search if it is still running.
+    future.cancel(true);
+  }
+
+
+
+  /**
+   * Returns the next search result entry contained in the search results,
+   * waiting if necessary until one becomes available.
+   *
+   * @return The next search result entry, or {@code null} if there are no more
+   *         entries in the search results.
+   * @throws SearchResultReferenceIOException
+   *           If the next search response was a search result reference. This
+   *           connection entry reader may still contain remaining search
+   *           results and references which can be retrieved using additional
+   *           calls to this method.
+   * @throws ErrorResultIOException
+   *           If the result code indicates that the search operation failed for
+   *           some reason.
+   * @throws InterruptedIOException
+   *           If the current thread was interrupted while waiting.
+   */
+  @Override
+  public SearchResultEntry readEntry() throws SearchResultReferenceIOException,
+      ErrorResultIOException, InterruptedIOException
+  {
+    Response r;
+    try
+    {
+      while ((r = buffer.responses.poll(50, TimeUnit.MILLISECONDS)) == null)
+      {
+        if (buffer.isInterrupted)
+        {
+          // The worker thread processing the result was interrupted so no
+          // result will ever arrive. We don't want to hang this thread forever
+          // while we wait, so terminate now.
+          r = Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR);
+          break;
+        }
+      }
+    }
+    catch (final InterruptedException e)
+    {
+      throw new InterruptedIOException(e.getMessage());
+    }
+
+    if (r instanceof SearchResultEntry)
+    {
+      return (SearchResultEntry) r;
+    }
+    else if (r instanceof SearchResultReference)
+    {
+      throw new SearchResultReferenceIOException((SearchResultReference) r);
+    }
+    else if (r instanceof Result)
+    {
+      final Result result = (Result) r;
+      if (result.isSuccess())
+      {
+        return null;
+      }
+      else
+      {
+        throw new ErrorResultIOException(ErrorResultException.wrap(result));
+      }
+    }
+    else
+    {
+      throw new RuntimeException("Unexpected response type: "
+          + r.getClass().toString());
+    }
+  }
+}
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/requests/CRAMMD5SASLBindRequestImpl.java b/opendj-sdk/sdk/src/org/opends/sdk/requests/CRAMMD5SASLBindRequestImpl.java
index 9b925ac..b959191 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/requests/CRAMMD5SASLBindRequestImpl.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/requests/CRAMMD5SASLBindRequestImpl.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk.requests;
@@ -111,27 +111,22 @@
     public boolean evaluateResult(final BindResult result)
         throws ErrorResultException
     {
-      if (result.getResultCode() == ResultCode.SASL_BIND_IN_PROGRESS
-          && result.getServerSASLCredentials() != null)
+      try
       {
-        try
-        {
-          setNextSASLCredentials(saslClient.evaluateChallenge(result
-              .getServerSASLCredentials().toByteArray()));
-          return false;
-        }
-        catch (final SaslException e)
-        {
-          // FIXME: I18N need to have a better error message.
-          // FIXME: Is this the best result code?
-          throw ErrorResultException.wrap(Responses.newResult(
-              ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
-              "An error occurred during multi-stage authentication")
-              .setCause(e));
-        }
+        setNextSASLCredentials(saslClient.evaluateChallenge(result
+            .getServerSASLCredentials() == null ? new byte[0] :
+            result.getServerSASLCredentials().toByteArray()));
+        return saslClient.isComplete();
       }
-
-      return true;
+      catch (final SaslException e)
+      {
+        // FIXME: I18N need to have a better error message.
+        // FIXME: Is this the best result code?
+        throw ErrorResultException.wrap(Responses.newResult(
+            ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
+            "An error occurred during multi-stage authentication")
+            .setCause(e));
+      }
     }
 
 
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequestImpl.java b/opendj-sdk/sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequestImpl.java
index 3917be4..c81e762 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequestImpl.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequestImpl.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk.requests;
@@ -122,26 +122,22 @@
     public boolean evaluateResult(final BindResult result)
         throws ErrorResultException
     {
-      if (result.getResultCode() == ResultCode.SASL_BIND_IN_PROGRESS
-          && result.getServerSASLCredentials() != null)
+      try
       {
-        try
-        {
-          setNextSASLCredentials(saslClient.evaluateChallenge(result
-              .getServerSASLCredentials().toByteArray()));
-          return false;
-        }
-        catch (final SaslException e)
-        {
-          // FIXME: I18N need to have a better error message.
-          // FIXME: Is this the best result code?
-          throw ErrorResultException.wrap(Responses.newResult(
-              ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
-              "An error occurred during multi-stage authentication")
-              .setCause(e));
-        }
+        setNextSASLCredentials(saslClient.evaluateChallenge(result
+            .getServerSASLCredentials() == null ? new byte[0] :
+            result.getServerSASLCredentials().toByteArray()));
+        return saslClient.isComplete();
       }
-      return true;
+      catch (final SaslException e)
+      {
+        // FIXME: I18N need to have a better error message.
+        // FIXME: Is this the best result code?
+        throw ErrorResultException.wrap(Responses.newResult(
+            ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
+            "An error occurred during multi-stage authentication")
+            .setCause(e));
+      }
     }
 
 
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/requests/ExternalSASLBindRequestImpl.java b/opendj-sdk/sdk/src/org/opends/sdk/requests/ExternalSASLBindRequestImpl.java
index 80acd82..b6afa0d 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/requests/ExternalSASLBindRequestImpl.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/requests/ExternalSASLBindRequestImpl.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.sdk.requests;
@@ -102,26 +102,22 @@
     public boolean evaluateResult(final BindResult result)
         throws ErrorResultException
     {
-      if (result.getResultCode() == ResultCode.SASL_BIND_IN_PROGRESS
-          && result.getServerSASLCredentials() != null)
+      try
       {
-        try
-        {
-          setNextSASLCredentials(saslClient.evaluateChallenge(result
-              .getServerSASLCredentials().toByteArray()));
-          return false;
-        }
-        catch (final SaslException e)
-        {
-          // FIXME: I18N need to have a better error message.
-          // FIXME: Is this the best result code?
-          throw ErrorResultException.wrap(Responses.newResult(
-              ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
-              "An error occurred during multi-stage authentication")
-              .setCause(e));
-        }
+        setNextSASLCredentials(saslClient.evaluateChallenge(result
+            .getServerSASLCredentials() == null ? new byte[0] :
+            result.getServerSASLCredentials().toByteArray()));
+        return saslClient.isComplete();
       }
-      return true;
+      catch (final SaslException e)
+      {
+        // FIXME: I18N need to have a better error message.
+        // FIXME: Is this the best result code?
+        throw ErrorResultException.wrap(Responses.newResult(
+            ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
+            "An error occurred during multi-stage authentication")
+            .setCause(e));
+      }
     }
   }
 
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/requests/StartTLSExtendedRequest.java b/opendj-sdk/sdk/src/org/opends/sdk/requests/StartTLSExtendedRequest.java
index 33d8249..012e294 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/requests/StartTLSExtendedRequest.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/requests/StartTLSExtendedRequest.java
@@ -117,6 +117,62 @@
 
 
   /**
+   * Set the protocol versions enabled for secure connections with the
+   * Directory Server.
+   *
+   * The protocols must be supported by the SSLContext specified in
+   * {@link #setSSLContext(SSLContext)}. Following a successful call to
+   * this method, only the protocols listed in the protocols parameter are
+   * enabled for use.
+   *
+   * @param protocols Names of all the protocols to enable or {@code null} to
+   *                  use the default protocols.
+   * @return A reference to this LDAP connection options.
+   */
+  StartTLSExtendedRequest setEnabledProtocols(String[] protocols);
+
+
+
+  /**
+   * Set the cipher suites enabled for secure connections with the
+   * Directory Server.
+   *
+   * The suites must be supported by the SSLContext specified in
+   * {@link #setSSLContext(SSLContext)}. Following a successful call to
+   * this method, only the suites listed in the protocols parameter are
+   * enabled for use.
+   *
+   * @param suites Names of all the suites to enable or {@code null} to
+   *                  use the default cipher suites.
+   * @return A reference to this LDAP connection options.
+   */
+  StartTLSExtendedRequest setEnabledCipherSuites(String[] suites);
+
+
+
+  /**
+   * Returns the names of the protocol versions which are currently enabled
+   * for secure connections with the Directory Server.
+   *
+   * @return an array of protocols or {@code null} if the default protocols
+   * are to be used.
+   */
+  String[] getEnabledProtocols();
+
+
+
+  /**
+   * Returns the names of the protocol versions which are currently enabled
+   * for secure connections with the Directory Server.
+   *
+   * @return an array of protocols or {@code null} if the default protocols
+   * are to be used.
+   */
+  String[] getEnabledCipherSuites();
+
+
+
+  /**
    * {@inheritDoc}
    */
   ByteString getValue();
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/requests/StartTLSExtendedRequestImpl.java b/opendj-sdk/sdk/src/org/opends/sdk/requests/StartTLSExtendedRequestImpl.java
index 49dd4f6..9032e55 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/requests/StartTLSExtendedRequestImpl.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/requests/StartTLSExtendedRequestImpl.java
@@ -98,6 +98,16 @@
 
   private SSLContext sslContext;
 
+  /**
+   * The list of cipher suite
+   */
+  private String[] enabledCipherSuites = null;
+
+  /**
+   * the list of protocols
+   */
+  private String[] enabledProtocols = null;
+
   // No need to expose this.
   private static final ExtendedResultDecoder<ExtendedResult> RESULT_DECODER = new ResultDecoder();
 
@@ -152,6 +162,48 @@
 
 
   /**
+   * {@inheritDoc}}
+   */
+  public StartTLSExtendedRequest setEnabledProtocols(String[] protocols)
+  {
+    this.enabledProtocols = protocols;
+    return this;
+  }
+
+
+
+  /**
+   * {@inheritDoc}}
+   */
+  public StartTLSExtendedRequest setEnabledCipherSuites(String[] suites)
+  {
+    this.enabledCipherSuites = suites;
+    return this;
+  }
+
+
+
+  /**
+   * {@inheritDoc}}
+   */
+  public String[] getEnabledProtocols()
+  {
+    return this.enabledProtocols;
+  }
+
+
+
+  /**
+   * {@inheritDoc}}
+   */
+  public String[] getEnabledCipherSuites()
+  {
+    return this.enabledCipherSuites;
+  }
+
+
+
+  /**
    * {@inheritDoc}
    */
   @Override
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/schema/Schema.java b/opendj-sdk/sdk/src/org/opends/sdk/schema/Schema.java
index bcd2ea6..d984613 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/schema/Schema.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/schema/Schema.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 package org.opends.sdk.schema;
 
@@ -1462,21 +1462,21 @@
 
   private static volatile Schema defaultSchema = CoreSchemaImpl.getInstance();
 
-  private static final String ATTR_ATTRIBUTE_TYPES = "attributeTypes";
+  static final String ATTR_ATTRIBUTE_TYPES = "attributeTypes";
 
-  private static final String ATTR_DIT_CONTENT_RULES = "dITContentRules";
+  static final String ATTR_DIT_CONTENT_RULES = "dITContentRules";
 
-  private static final String ATTR_DIT_STRUCTURE_RULES = "dITStructureRules";
+  static final String ATTR_DIT_STRUCTURE_RULES = "dITStructureRules";
 
-  private static final String ATTR_LDAP_SYNTAXES = "ldapSyntaxes";
+  static final String ATTR_LDAP_SYNTAXES = "ldapSyntaxes";
 
-  private static final String ATTR_MATCHING_RULE_USE = "matchingRuleUse";
+  static final String ATTR_MATCHING_RULE_USE = "matchingRuleUse";
 
-  private static final String ATTR_MATCHING_RULES = "matchingRules";
+  static final String ATTR_MATCHING_RULES = "matchingRules";
 
-  private static final String ATTR_NAME_FORMS = "nameForms";
+  static final String ATTR_NAME_FORMS = "nameForms";
 
-  private static final String ATTR_OBJECT_CLASSES = "objectClasses";
+  static final String ATTR_OBJECT_CLASSES = "objectClasses";
 
   private static final String ATTR_SUBSCHEMA_SUBENTRY = "subschemaSubentry";
 
@@ -1679,7 +1679,7 @@
    */
   public static FutureResult<Schema> readSchemaForEntry(
       final AsynchronousConnection connection, final DN name,
-      final ResultHandler<Schema> handler)
+      final ResultHandler<? super Schema> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
@@ -1780,137 +1780,7 @@
    */
   public static Schema valueOf(final Entry entry)
   {
-    final SchemaBuilder builder = new SchemaBuilder(entry.getName().toString());
-
-    Attribute attr = entry.getAttribute(ATTR_LDAP_SYNTAXES);
-    if (attr != null)
-    {
-      for (final ByteString def : attr)
-      {
-        try
-        {
-          builder.addSyntax(def.toString(), true);
-        }
-        catch (final LocalizedIllegalArgumentException e)
-        {
-          builder.addWarning(e.getMessageObject());
-        }
-      }
-    }
-
-    attr = entry.getAttribute(ATTR_ATTRIBUTE_TYPES);
-    if (attr != null)
-    {
-      for (final ByteString def : attr)
-      {
-        try
-        {
-          builder.addAttributeType(def.toString(), true);
-        }
-        catch (final LocalizedIllegalArgumentException e)
-        {
-          builder.addWarning(e.getMessageObject());
-        }
-      }
-    }
-
-    attr = entry.getAttribute(ATTR_OBJECT_CLASSES);
-    if (attr != null)
-    {
-      for (final ByteString def : attr)
-      {
-        try
-        {
-          builder.addObjectClass(def.toString(), true);
-        }
-        catch (final LocalizedIllegalArgumentException e)
-        {
-          builder.addWarning(e.getMessageObject());
-        }
-      }
-    }
-
-    attr = entry.getAttribute(ATTR_MATCHING_RULE_USE);
-    if (attr != null)
-    {
-      for (final ByteString def : attr)
-      {
-        try
-        {
-          builder.addMatchingRuleUse(def.toString(), true);
-        }
-        catch (final LocalizedIllegalArgumentException e)
-        {
-          builder.addWarning(e.getMessageObject());
-        }
-      }
-    }
-
-    attr = entry.getAttribute(ATTR_MATCHING_RULES);
-    if (attr != null)
-    {
-      for (final ByteString def : attr)
-      {
-        try
-        {
-          builder.addMatchingRule(def.toString(), true);
-        }
-        catch (final LocalizedIllegalArgumentException e)
-        {
-          builder.addWarning(e.getMessageObject());
-        }
-      }
-    }
-
-    attr = entry.getAttribute(ATTR_DIT_CONTENT_RULES);
-    if (attr != null)
-    {
-      for (final ByteString def : attr)
-      {
-        try
-        {
-          builder.addDITContentRule(def.toString(), true);
-        }
-        catch (final LocalizedIllegalArgumentException e)
-        {
-          builder.addWarning(e.getMessageObject());
-        }
-      }
-    }
-
-    attr = entry.getAttribute(ATTR_DIT_STRUCTURE_RULES);
-    if (attr != null)
-    {
-      for (final ByteString def : attr)
-      {
-        try
-        {
-          builder.addDITStructureRule(def.toString(), true);
-        }
-        catch (final LocalizedIllegalArgumentException e)
-        {
-          builder.addWarning(e.getMessageObject());
-        }
-      }
-    }
-
-    attr = entry.getAttribute(ATTR_NAME_FORMS);
-    if (attr != null)
-    {
-      for (final ByteString def : attr)
-      {
-        try
-        {
-          builder.addNameForm(def.toString(), true);
-        }
-        catch (final LocalizedIllegalArgumentException e)
-        {
-          builder.addWarning(e.getMessageObject());
-        }
-      }
-    }
-
-    return builder.toSchema();
+    return new SchemaBuilder(entry).toSchema();
   }
 
 
@@ -2641,6 +2511,105 @@
 
 
 
+  /**
+   * Adds the definitions of all the schema elements contained in this schema to
+   * the provided subschema subentry. Any existing attributes (including schema
+   * definitions) contained in the provided entry will be preserved.
+   *
+   * @param entry
+   *          The subschema subentry to which all schema definitions should be
+   *          added.
+   * @return The updated subschema subentry.
+   * @throws NullPointerException
+   *           If {@code entry} was {@code null}.
+   */
+  public Entry toEntry(Entry entry) throws NullPointerException
+  {
+    Attribute attr = new LinkedAttribute(Schema.ATTR_LDAP_SYNTAXES);
+    for (Syntax syntax : getSyntaxes())
+    {
+      attr.add(syntax.toString());
+    }
+    if (!attr.isEmpty())
+    {
+      entry.addAttribute(attr);
+    }
+
+    attr = new LinkedAttribute(Schema.ATTR_ATTRIBUTE_TYPES);
+    for (AttributeType attributeType : getAttributeTypes())
+    {
+      attr.add(attributeType.toString());
+    }
+    if (!attr.isEmpty())
+    {
+      entry.addAttribute(attr);
+    }
+
+    attr = new LinkedAttribute(Schema.ATTR_OBJECT_CLASSES);
+    for (ObjectClass objectClass : getObjectClasses())
+    {
+      attr.add(objectClass.toString());
+    }
+    if (!attr.isEmpty())
+    {
+      entry.addAttribute(attr);
+    }
+
+    attr = new LinkedAttribute(Schema.ATTR_MATCHING_RULE_USE);
+    for (MatchingRuleUse matchingRuleUse : getMatchingRuleUses())
+    {
+      attr.add(matchingRuleUse.toString());
+    }
+    if (!attr.isEmpty())
+    {
+      entry.addAttribute(attr);
+    }
+
+    attr = new LinkedAttribute(Schema.ATTR_MATCHING_RULES);
+    for (MatchingRule matchingRule : getMatchingRules())
+    {
+      attr.add(matchingRule.toString());
+    }
+    if (!attr.isEmpty())
+    {
+      entry.addAttribute(attr);
+    }
+
+    attr = new LinkedAttribute(Schema.ATTR_DIT_CONTENT_RULES);
+    for (DITContentRule ditContentRule : getDITContentRules())
+    {
+      attr.add(ditContentRule.toString());
+    }
+    if (!attr.isEmpty())
+    {
+      entry.addAttribute(attr);
+    }
+
+    attr = new LinkedAttribute(Schema.ATTR_DIT_STRUCTURE_RULES);
+    for (DITStructureRule ditStructureRule : getDITStuctureRules())
+    {
+      attr.add(ditStructureRule.toString());
+    }
+    if (!attr.isEmpty())
+    {
+      entry.addAttribute(attr);
+    }
+
+    attr = new LinkedAttribute(Schema.ATTR_NAME_FORMS);
+    for (NameForm nameForm : getNameForms())
+    {
+      attr.add(nameForm.toString());
+    }
+    if (!attr.isEmpty())
+    {
+      entry.addAttribute(attr);
+    }
+
+    return entry;
+  }
+
+
+
   SchemaCompatOptions getSchemaCompatOptions()
   {
     return impl.getSchemaCompatOptions();
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/schema/SchemaBuilder.java b/opendj-sdk/sdk/src/org/opends/sdk/schema/SchemaBuilder.java
index 2dcd5ce..3509ec4 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/schema/SchemaBuilder.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/schema/SchemaBuilder.java
@@ -42,9 +42,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.regex.Pattern;
 
-import org.opends.sdk.DecodeException;
-import org.opends.sdk.LocalizableMessage;
-import org.opends.sdk.LocalizedIllegalArgumentException;
+import org.opends.sdk.*;
 
 import com.sun.opends.sdk.util.StaticUtils;
 import com.sun.opends.sdk.util.SubstringReader;
@@ -116,6 +114,152 @@
 
 
   /**
+   * Creates a new schema builder containing all of the schema elements
+   * contained in the provided a subschema subentry. Any problems encountered
+   * while parsing the entry can be retrieved using the returned schema's
+   * {@link Schema#getWarnings()} method.
+   *
+   * @param entry
+   *          The subschema subentry to be parsed.
+   * @throws NullPointerException
+   *           If {@code entry} was {@code null}.
+   */
+  public SchemaBuilder(final Entry entry) throws NullPointerException
+  {
+    initBuilder(entry.getName().toString());
+
+    Attribute attr = entry.getAttribute(Schema.ATTR_LDAP_SYNTAXES);
+    if (attr != null)
+    {
+      for (final ByteString def : attr)
+      {
+        try
+        {
+          addSyntax(def.toString(), true);
+        }
+        catch (final LocalizedIllegalArgumentException e)
+        {
+          addWarning(e.getMessageObject());
+        }
+      }
+    }
+
+    attr = entry.getAttribute(Schema.ATTR_ATTRIBUTE_TYPES);
+    if (attr != null)
+    {
+      for (final ByteString def : attr)
+      {
+        try
+        {
+          addAttributeType(def.toString(), true);
+        }
+        catch (final LocalizedIllegalArgumentException e)
+        {
+          addWarning(e.getMessageObject());
+        }
+      }
+    }
+
+    attr = entry.getAttribute(Schema.ATTR_OBJECT_CLASSES);
+    if (attr != null)
+    {
+      for (final ByteString def : attr)
+      {
+        try
+        {
+          addObjectClass(def.toString(), true);
+        }
+        catch (final LocalizedIllegalArgumentException e)
+        {
+          addWarning(e.getMessageObject());
+        }
+      }
+    }
+
+    attr = entry.getAttribute(Schema.ATTR_MATCHING_RULE_USE);
+    if (attr != null)
+    {
+      for (final ByteString def : attr)
+      {
+        try
+        {
+          addMatchingRuleUse(def.toString(), true);
+        }
+        catch (final LocalizedIllegalArgumentException e)
+        {
+          addWarning(e.getMessageObject());
+        }
+      }
+    }
+
+    attr = entry.getAttribute(Schema.ATTR_MATCHING_RULES);
+    if (attr != null)
+    {
+      for (final ByteString def : attr)
+      {
+        try
+        {
+          addMatchingRule(def.toString(), true);
+        }
+        catch (final LocalizedIllegalArgumentException e)
+        {
+          addWarning(e.getMessageObject());
+        }
+      }
+    }
+
+    attr = entry.getAttribute(Schema.ATTR_DIT_CONTENT_RULES);
+    if (attr != null)
+    {
+      for (final ByteString def : attr)
+      {
+        try
+        {
+          addDITContentRule(def.toString(), true);
+        }
+        catch (final LocalizedIllegalArgumentException e)
+        {
+          addWarning(e.getMessageObject());
+        }
+      }
+    }
+
+    attr = entry.getAttribute(Schema.ATTR_DIT_STRUCTURE_RULES);
+    if (attr != null)
+    {
+      for (final ByteString def : attr)
+      {
+        try
+        {
+          addDITStructureRule(def.toString(), true);
+        }
+        catch (final LocalizedIllegalArgumentException e)
+        {
+          addWarning(e.getMessageObject());
+        }
+      }
+    }
+
+    attr = entry.getAttribute(Schema.ATTR_NAME_FORMS);
+    if (attr != null)
+    {
+      for (final ByteString def : attr)
+      {
+        try
+        {
+          addNameForm(def.toString(), true);
+        }
+        catch (final LocalizedIllegalArgumentException e)
+        {
+          addWarning(e.getMessageObject());
+        }
+      }
+    }
+  }
+
+
+
+  /**
    * Creates a new schema builder containing all of the schema elements from the
    * provided schema and its compatibility options.
    *
diff --git a/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/AuthenticatedConnectionFactoryTestCase.java b/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/AuthenticatedConnectionFactoryTestCase.java
deleted file mode 100644
index 9ff1cd1..0000000
--- a/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/AuthenticatedConnectionFactoryTestCase.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2010 Sun Microsystems, Inc.
- */
-
-package org.opends.sdk;
-
-
-
-import org.opends.sdk.requests.Requests;
-
-
-
-/**
- * Tests the authenticated connection factory.
- */
-public class AuthenticatedConnectionFactoryTestCase extends
-    ConnectionFactoryTestCase
-{
-  private final AuthenticatedConnectionFactory authFactory = new AuthenticatedConnectionFactory(
-      new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort()),
-      Requests.newSimpleBindRequest("", ""));
-
-
-
-  @Override
-  protected FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      final ResultHandler<AsynchronousConnection> handler)
-  {
-    return authFactory.getAsynchronousConnection(handler);
-  }
-
-
-
-  @Override
-  protected Connection getConnection() throws Exception
-  {
-    return authFactory.getConnection();
-  }
-}
diff --git a/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/ConnectionFactoryTestCase.java b/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/ConnectionFactoryTestCase.java
index 4d85d2e..1bde983 100644
--- a/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/ConnectionFactoryTestCase.java
+++ b/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/ConnectionFactoryTestCase.java
@@ -33,16 +33,21 @@
 import static org.testng.Assert.assertTrue;
 
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
+import org.opends.sdk.requests.Requests;
+import org.opends.sdk.requests.SearchRequest;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
+import org.testng.annotations.DataProvider;
 
+import javax.net.ssl.SSLContext;
 
 
 /**
  * Tests the connectionfactory classes.
  */
-public abstract class ConnectionFactoryTestCase extends SdkTestCase
+public class ConnectionFactoryTestCase extends SdkTestCase
 {
   class MyResultHandler implements ResultHandler<AsynchronousConnection>
   {
@@ -90,7 +95,63 @@
     TestCaseUtils.startServer();
   }
 
+  @DataProvider(name = "connectionFactories")
+  public Object[][] getModifyDNRequests() throws Exception
+  {
+    Object[][] factories = new Object[7][1];
 
+    // HeartBeatConnectionFactory
+    // Use custom search request.
+    SearchRequest request = Requests.newSearchRequest(
+        "uid=user.0,ou=people,o=test", SearchScope.BASE_OBJECT, "objectclass=*",
+        "cn");
+
+    factories[0][0] = new HeartBeatConnectionFactory(
+        new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort()),
+        1000, TimeUnit.MILLISECONDS, request);
+
+    // InternalConnectionFactory
+    factories[1][0] = Connections
+      .newInternalConnectionFactory(LDAPServer.getInstance(), null);
+
+    // AuthenticatedConnectionFactory
+    factories[2][0] = new AuthenticatedConnectionFactory(
+      new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort()),
+      Requests.newSimpleBindRequest("", ""));
+
+    // AuthenticatedConnectionFactory with multi-stage SASL
+    factories[3][0] = new AuthenticatedConnectionFactory(
+      new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort()),
+      Requests.newCRAMMD5SASLBindRequest("id:user",
+            ByteString.valueOf("password")));
+
+    // LDAPConnectionFactory with default options
+    factories[4][0] = new LDAPConnectionFactory(
+      "localhost", TestCaseUtils.getLdapPort());
+
+    // LDAPConnectionFactory with startTLS
+    SSLContext sslContext = new SSLContextBuilder().
+        setTrustManager(TrustManagers.trustAll()).getSSLContext();
+    LDAPOptions options = new LDAPOptions().setSSLContext(sslContext).
+        setUseStartTLS(true).setEnabledCipherSuites(
+        new String[]{"SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
+                         "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
+                         "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
+                         "SSL_DH_anon_WITH_DES_CBC_SHA",
+                         "SSL_DH_anon_WITH_RC4_128_MD5",
+                         "TLS_DH_anon_WITH_AES_128_CBC_SHA",
+                         "TLS_DH_anon_WITH_AES_256_CBC_SHA"});
+    factories[5][0] = new LDAPConnectionFactory(
+        "localhost", TestCaseUtils.getLdapPort(), options);
+
+    // startTLS + SASL confidentiality
+    factories[6][0] = new AuthenticatedConnectionFactory(
+        new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort(),
+            options), Requests.newDigestMD5SASLBindRequest("id:user",
+            ByteString.valueOf("password")));
+
+    return factories;
+  }
 
   /**
    * Tests the async connection in the blocking mode. This is not fully async as
@@ -98,10 +159,12 @@
    *
    * @throws Exception
    */
-  @Test()
-  public void testBlockingFutureNoHandler() throws Exception
+  @Test(dataProvider = "connectionFactories")
+  public void testBlockingFutureNoHandler(ConnectionFactory factory)
+      throws Exception
   {
-    final FutureResult<AsynchronousConnection> future = getAsynchronousConnection(null);
+    final FutureResult<AsynchronousConnection> future =
+        factory.getAsynchronousConnection(null);
     final AsynchronousConnection con = future.get();
     // quickly check if iit is a valid connection.
     // Don't use a result handler.
@@ -116,13 +179,15 @@
    *
    * @throws Exception
    */
-  @Test()
-  public void testNonBlockingFutureWithHandler() throws Exception
+  @Test(dataProvider = "connectionFactories")
+  public void testNonBlockingFutureWithHandler(ConnectionFactory factory)
+      throws Exception
   {
     // Use the handler to get the result asynchronously.
     final CountDownLatch latch = new CountDownLatch(1);
     final MyResultHandler handler = new MyResultHandler(latch);
-    final FutureResult<AsynchronousConnection> future = getAsynchronousConnection(handler);
+    final FutureResult<AsynchronousConnection> future =
+        factory.getAsynchronousConnection(handler);
     // Since we don't have anything to do, we would rather
     // be notified by the latch when the other thread calls our handler.
     latch.await(); // should do a timed wait rather?
@@ -140,32 +205,14 @@
    *
    * @throws Exception
    */
-  @Test()
-  public void testSynchronousConnection() throws Exception
+  @Test(dataProvider = "connectionFactories")
+  public void testSynchronousConnection(ConnectionFactory factory)
+      throws Exception
   {
-    final Connection con = getConnection();
+    final Connection con = factory.getConnection();
     assertNotNull(con);
     // quickly check if iit is a valid connection.
     assertTrue(con.readRootDSE().getEntry().getName().isRootDN());
     con.close();
   }
-
-
-
-  /**
-   * Gets the future result from the implementations.
-   *
-   * @return FutureResult.
-   */
-  protected abstract FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      ResultHandler<AsynchronousConnection> handler) throws Exception;
-
-
-
-  /**
-   * Gets the connection from the implementations.
-   *
-   * @return Connection
-   */
-  protected abstract Connection getConnection() throws Exception;
 }
diff --git a/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/HeartBeatConnectionFactoryTestCase.java b/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/HeartBeatConnectionFactoryTestCase.java
deleted file mode 100644
index d1adf26..0000000
--- a/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/HeartBeatConnectionFactoryTestCase.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2010 Sun Microsystems, Inc.
- */
-
-package org.opends.sdk;
-
-
-
-import java.util.concurrent.TimeUnit;
-
-import org.opends.sdk.requests.Requests;
-import org.opends.sdk.requests.SearchRequest;
-
-
-
-/**
- * Tests the Heart beat connection factory.
- */
-public class HeartBeatConnectionFactoryTestCase extends
-    ConnectionFactoryTestCase
-{
-  // Use custom search request.
-  SearchRequest request = Requests.newSearchRequest(
-      "uid=user.0,ou=people,o=test", SearchScope.BASE_OBJECT, "objectclass=*",
-      "cn");
-
-  // The factory.
-  private final HeartBeatConnectionFactory factory = new HeartBeatConnectionFactory(
-      new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort()),
-      1000, TimeUnit.MILLISECONDS, request);
-
-
-
-  @Override
-  protected FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      final ResultHandler<AsynchronousConnection> handler) throws Exception
-  {
-    return factory.getAsynchronousConnection(handler);
-  }
-
-
-
-  @Override
-  protected Connection getConnection() throws Exception
-  {
-    return factory.getConnection();
-  }
-}
diff --git a/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/InternalConnectionFactoryTestCase.java b/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/InternalConnectionFactoryTestCase.java
deleted file mode 100644
index c462aac..0000000
--- a/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/InternalConnectionFactoryTestCase.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2010 Sun Microsystems, Inc.
- */
-
-package org.opends.sdk;
-
-
-
-/**
- * Tests the internal connection factory.
- */
-public class InternalConnectionFactoryTestCase extends
-    ConnectionFactoryTestCase
-{
-  private final ConnectionFactory factory = Connections
-      .newInternalConnectionFactory(LDAPServer.getInstance(), null);
-
-
-
-  @Override
-  protected FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      final ResultHandler<AsynchronousConnection> handler) throws Exception
-  {
-    return factory.getAsynchronousConnection(handler);
-  }
-
-
-
-  @Override
-  protected Connection getConnection() throws Exception
-  {
-    return factory.getConnection();
-  }
-}
diff --git a/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPConnectionFactoryTestCase.java b/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPConnectionFactoryTestCase.java
deleted file mode 100644
index 1ff4b85..0000000
--- a/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPConnectionFactoryTestCase.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2010 Sun Microsystems, Inc.
- */
-
-package org.opends.sdk;
-
-
-
-/**
- * Tests the LDAP connection factory.
- */
-public class LDAPConnectionFactoryTestCase extends ConnectionFactoryTestCase
-{
-  private final LDAPConnectionFactory factory = new LDAPConnectionFactory(
-      "localhost", TestCaseUtils.getLdapPort());
-
-
-
-  @Override
-  protected FutureResult<AsynchronousConnection> getAsynchronousConnection(
-      final ResultHandler<AsynchronousConnection> handler) throws Exception
-  {
-    return factory.getAsynchronousConnection(handler);
-  }
-
-
-
-  @Override
-  protected Connection getConnection() throws Exception
-  {
-    return factory.getConnection();
-  }
-}
diff --git a/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPServer.java b/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPServer.java
index 08cf276..c6695cc 100644
--- a/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPServer.java
+++ b/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPServer.java
@@ -29,13 +29,19 @@
 
 
 
+import static com.sun.opends.sdk.ldap.LDAPConstants.TYPE_AUTHENTICATION_SASL;
+
 import java.io.IOException;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import javax.net.ssl.SSLContext;
+import javax.security.auth.callback.*;
+import javax.security.sasl.*;
+
+import org.opends.sdk.asn1.ASN1;
+import org.opends.sdk.asn1.ASN1Reader;
 import org.opends.sdk.controls.Control;
 import org.opends.sdk.controls.ControlDecoder;
 import org.opends.sdk.requests.*;
@@ -53,7 +59,7 @@
  * A simple ldap server that manages 1000 entries and used for running
  * testcases. //FIXME: make it MT-safe.
  */
-public class LDAPServer implements ServerConnection<Integer>,
+public class LDAPServer implements
     ServerConnectionFactory<LDAPClientContext, Integer>
 {
   // Creates an abandonable request from the ordinary requests.
@@ -132,6 +138,525 @@
 
 
 
+  private class LDAPServerConnection implements ServerConnection<Integer>
+  {
+
+    private final LDAPClientContext clientContext;
+    private SaslServer saslServer;
+
+
+
+    private LDAPServerConnection(LDAPClientContext clientContext)
+    {
+      this.clientContext = clientContext;
+    }
+
+
+
+    /**
+     * Abandons the request sent by the client.
+     *
+     * @param context
+     * @param request
+     * @throws UnsupportedOperationException
+     */
+    public void handleAbandon(final Integer context,
+        final AbandonRequest request) throws UnsupportedOperationException
+    {
+      // Check if we have any concurrent operation with this message id.
+      final AbandonableRequest req = requestsInProgress.get(context);
+      if (req == null)
+      {
+        // Nothing to do here.
+        return;
+      }
+      // Cancel the request
+      req.cancel();
+      // No response is needed.
+    }
+
+
+
+    /**
+     * Adds the request sent by the client.
+     *
+     * @param context
+     * @param request
+     * @param handler
+     * @param intermediateResponseHandler
+     * @throws UnsupportedOperationException
+     */
+    public void handleAdd(final Integer context, final AddRequest request,
+        final ResultHandler<? super Result> handler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      Result result = null;
+      final AbandonableRequest abReq = new AbandonableRequest(request);
+      requestsInProgress.put(context, abReq);
+      // Get the DN.
+      final DN dn = request.getName();
+      if (entryMap.containsKey(dn))
+      {
+        // duplicate entry.
+        result = Responses.newResult(ResultCode.ENTRY_ALREADY_EXISTS);
+        final ErrorResultException ere = ErrorResultException.wrap(result);
+        handler.handleErrorResult(ere);
+        // doesn't matter if it was canceled.
+        requestsInProgress.remove(context);
+        return;
+      }
+
+      // Create an entry out of this request.
+      final SearchResultEntry entry = Responses.newSearchResultEntry(dn);
+      for (final Control control : request.getControls())
+      {
+        entry.addControl(control);
+      }
+
+      for (final Attribute attr : request.getAllAttributes())
+      {
+        entry.addAttribute(attr);
+      }
+
+      if (abReq.isCanceled())
+      {
+        result = Responses.newResult(ResultCode.CANCELLED);
+        final ErrorResultException ere = ErrorResultException.wrap(result);
+        handler.handleErrorResult(ere);
+        requestsInProgress.remove(context);
+        return;
+      }
+      // Add this to the map.
+      entryMap.put(dn, entry);
+      requestsInProgress.remove(context);
+      result = Responses.newResult(ResultCode.SUCCESS);
+      handler.handleResult(result);
+    }
+
+
+
+    /**
+     * @param context
+     * @param version
+     * @param request
+     * @param resultHandler
+     * @param intermediateResponseHandler
+     * @throws UnsupportedOperationException
+     */
+    public void handleBind(final Integer context, final int version,
+        final BindRequest request,
+        final ResultHandler<? super BindResult> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      // TODO: all bind types.
+      final AbandonableRequest abReq = new AbandonableRequest(request);
+      requestsInProgress.put(context, abReq);
+      if (request.getAuthenticationType() == TYPE_AUTHENTICATION_SASL
+          && request instanceof GenericBindRequest)
+      {
+        ASN1Reader reader = ASN1.getReader(((GenericBindRequest) request)
+            .getAuthenticationValue());
+        try
+        {
+          String saslMech = reader.readOctetStringAsString();
+          ByteString saslCred;
+          if (reader.hasNextElement())
+          {
+            saslCred = reader.readOctetString();
+          }
+          else
+          {
+            saslCred = ByteString.empty();
+          }
+
+          if (saslServer == null
+              || !saslServer.getMechanismName().equalsIgnoreCase(saslMech))
+          {
+            final Map<String, String> props = new HashMap<String, String>();
+            props.put(Sasl.QOP, "auth-conf,auth-int,auth");
+            saslServer = Sasl.createSaslServer(saslMech, "ldap", clientContext
+                .getLocalAddress().getHostName(), props, new CallbackHandler()
+            {
+              public void handle(Callback[] callbacks) throws IOException,
+                  UnsupportedCallbackException
+              {
+                for (final Callback callback : callbacks)
+                {
+                  if (callback instanceof NameCallback)
+                  {
+                    // Do nothing
+                  }
+                  else if (callback instanceof PasswordCallback)
+                  {
+                    ((PasswordCallback) callback).setPassword("password"
+                        .toCharArray());
+                  }
+                  else if (callback instanceof AuthorizeCallback)
+                  {
+                    ((AuthorizeCallback) callback).setAuthorized(true);
+                  }
+                  else if (callback instanceof RealmCallback)
+                  {
+                    // Do nothing
+                  }
+                  else
+                  {
+                    throw new UnsupportedCallbackException(callback);
+
+                  }
+                }
+              }
+            });
+          }
+
+          byte[] challenge = saslServer
+              .evaluateResponse(saslCred.toByteArray());
+          if (saslServer.isComplete())
+          {
+            resultHandler.handleResult(Responses.newBindResult(
+                ResultCode.SUCCESS).setServerSASLCredentials(
+                ByteString.wrap(challenge)));
+
+            String qop = (String) saslServer.getNegotiatedProperty(Sasl.QOP);
+            if (qop != null
+                && (qop.equalsIgnoreCase("auth-int") || qop
+                    .equalsIgnoreCase("auth-conf")))
+            {
+              ConnectionSecurityLayer csl = new ConnectionSecurityLayer()
+              {
+                public void dispose()
+                {
+                  try
+                  {
+                    saslServer.dispose();
+                  }
+                  catch (SaslException e)
+                  {
+                    e.printStackTrace();
+                  }
+                }
+
+
+
+                public byte[] unwrap(byte[] incoming, int offset, int len)
+                    throws ErrorResultException
+                {
+                  try
+                  {
+                    return saslServer.unwrap(incoming, offset, len);
+                  }
+                  catch (SaslException e)
+                  {
+                    throw ErrorResultException.wrap(Responses.newResult(
+                        ResultCode.OPERATIONS_ERROR).setCause(e));
+                  }
+                }
+
+
+
+                public byte[] wrap(byte[] outgoing, int offset, int len)
+                    throws ErrorResultException
+                {
+                  try
+                  {
+                    return saslServer.wrap(outgoing, offset, len);
+                  }
+                  catch (SaslException e)
+                  {
+                    throw ErrorResultException.wrap(Responses.newResult(
+                        ResultCode.OPERATIONS_ERROR).setCause(e));
+                  }
+                }
+              };
+
+              clientContext.startSASL(csl);
+            }
+
+          }
+          else
+          {
+            resultHandler.handleResult(Responses.newBindResult(
+                ResultCode.SASL_BIND_IN_PROGRESS).setServerSASLCredentials(
+                ByteString.wrap(challenge)));
+          }
+        }
+        catch (Exception e)
+        {
+          resultHandler.handleErrorResult(ErrorResultException.wrap(Responses
+              .newBindResult(ResultCode.OPERATIONS_ERROR).setCause(e)
+              .setDiagnosticMessage(e.toString())));
+        }
+      }
+      else
+      {
+        resultHandler.handleResult(Responses.newBindResult(ResultCode.SUCCESS));
+      }
+      requestsInProgress.remove(context);
+    }
+
+
+
+    /**
+     * @param context
+     * @param request
+     */
+    public void handleConnectionClosed(final Integer context,
+        final UnbindRequest request)
+    {
+      if (saslServer != null)
+      {
+        try
+        {
+          saslServer.dispose();
+        }
+        catch (SaslException e)
+        {
+          e.printStackTrace();
+        }
+      }
+    }
+
+
+
+    /**
+     * @param error
+     */
+    public void handleConnectionException(final Throwable error)
+    {
+
+    }
+
+
+
+    /**
+     * @param context
+     * @param request
+     * @param resultHandler
+     * @param intermediateResponseHandler
+     * @throws UnsupportedOperationException
+     */
+    public void handleCompare(final Integer context,
+        final CompareRequest request,
+        final ResultHandler<? super CompareResult> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      CompareResult result = null;
+      final AbandonableRequest abReq = new AbandonableRequest(request);
+      requestsInProgress.put(context, abReq);
+      // Get the DN.
+      final DN dn = request.getName();
+      if (!entryMap.containsKey(dn))
+      {
+        // entry not found.
+        result = Responses.newCompareResult(ResultCode.NO_SUCH_ATTRIBUTE);
+        final ErrorResultException ere = ErrorResultException.wrap(result);
+        resultHandler.handleErrorResult(ere);
+        // doesn't matter if it was canceled.
+        requestsInProgress.remove(context);
+        return;
+      }
+
+      // Get the entry.
+      final Entry entry = entryMap.get(dn);
+      final AttributeDescription attrDesc = request.getAttributeDescription();
+      for (final Attribute attr : entry.getAllAttributes(attrDesc))
+      {
+        final Iterator<ByteString> it = attr.iterator();
+        while (it.hasNext())
+        {
+          final ByteString s = it.next();
+          if (abReq.isCanceled())
+          {
+            final Result r = Responses.newResult(ResultCode.CANCELLED);
+            final ErrorResultException ere = ErrorResultException.wrap(r);
+            resultHandler.handleErrorResult(ere);
+            requestsInProgress.remove(context);
+            return;
+          }
+          if (s.equals(request.getAssertionValue()))
+          {
+            result = Responses.newCompareResult(ResultCode.COMPARE_TRUE);
+            resultHandler.handleResult(result);
+          }
+        }
+      }
+      result = Responses.newCompareResult(ResultCode.COMPARE_FALSE);
+      resultHandler.handleResult(result);
+      requestsInProgress.remove(context);
+    }
+
+
+
+    /**
+     * @param context
+     * @param request
+     * @param handler
+     * @param intermediateResponseHandler
+     * @throws UnsupportedOperationException
+     */
+    public void handleDelete(final Integer context,
+        final DeleteRequest request,
+        final ResultHandler<? super Result> handler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      Result result = null;
+      final AbandonableRequest abReq = new AbandonableRequest(request);
+      requestsInProgress.put(context, abReq);
+      // Get the DN.
+      final DN dn = request.getName();
+      if (!entryMap.containsKey(dn))
+      {
+        // entry is not found.
+        result = Responses.newResult(ResultCode.NO_SUCH_OBJECT);
+        final ErrorResultException ere = ErrorResultException.wrap(result);
+        handler.handleErrorResult(ere);
+        // doesn't matter if it was canceled.
+        requestsInProgress.remove(context);
+        return;
+      }
+
+      if (abReq.isCanceled())
+      {
+        result = Responses.newResult(ResultCode.CANCELLED);
+        final ErrorResultException ere = ErrorResultException.wrap(result);
+        handler.handleErrorResult(ere);
+        requestsInProgress.remove(context);
+        return;
+      }
+      // Remove this from the map.
+      entryMap.remove(dn);
+      requestsInProgress.remove(context);
+    }
+
+
+
+    /**
+     * @param context
+     * @param request
+     * @param resultHandler
+     * @param intermediateResponseHandler
+     * @throws UnsupportedOperationException
+     */
+    public <R extends ExtendedResult> void handleExtendedRequest(
+        final Integer context, final ExtendedRequest<R> request,
+        final ResultHandler<? super R> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      if (request.getOID().equals(StartTLSExtendedRequest.OID))
+      {
+        final R result = request.getResultDecoder().adaptExtendedErrorResult(
+            ResultCode.SUCCESS, "", "");
+        resultHandler.handleResult(result);
+        clientContext.startTLS(sslContext, null, sslContext.getSocketFactory()
+            .getSupportedCipherSuites(), false, false);
+      }
+    }
+
+
+
+    /**
+     * @param context
+     * @param request
+     * @param resultHandler
+     * @param intermediateResponseHandler
+     * @throws UnsupportedOperationException
+     */
+    public void handleModify(final Integer context,
+        final ModifyRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      // TODO:
+    }
+
+
+
+    /**
+     * @param context
+     * @param request
+     * @param resultHandler
+     * @param intermediateResponseHandler
+     * @throws UnsupportedOperationException
+     */
+    public void handleModifyDN(final Integer context,
+        final ModifyDNRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      // TODO
+    }
+
+
+
+    /**
+     * @param context
+     * @param request
+     * @param resultHandler
+     * @param searchResulthandler
+     * @param intermediateResponseHandler
+     * @throws UnsupportedOperationException
+     */
+    public void handleSearch(final Integer context,
+        final SearchRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final SearchResultHandler searchResulthandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      Result result = null;
+      final AbandonableRequest abReq = new AbandonableRequest(request);
+      requestsInProgress.put(context, abReq);
+      // Get the DN.
+      final DN dn = request.getName();
+      if (!entryMap.containsKey(dn))
+      {
+        // Entry not found.
+        result = Responses.newResult(ResultCode.NO_SUCH_OBJECT);
+        final ErrorResultException ere = ErrorResultException.wrap(result);
+        resultHandler.handleErrorResult(ere);
+        // Should searchResultHandler handle anything?
+
+        // doesn't matter if it was canceled.
+        requestsInProgress.remove(context);
+        return;
+      }
+
+      if (abReq.isCanceled())
+      {
+        result = Responses.newResult(ResultCode.CANCELLED);
+        final ErrorResultException ere = ErrorResultException.wrap(result);
+        resultHandler.handleErrorResult(ere);
+        requestsInProgress.remove(context);
+        return;
+      }
+
+      final SearchResultEntry e = Responses
+          .newSearchResultEntry(new LinkedHashMapEntry(entryMap.get(dn)));
+      // Check we have had any controls in the request.
+      for (final Control control : request.getControls())
+      {
+        if (control.getOID().equals(AccountUsabilityRequestControl.OID))
+        {
+          e.addControl(AccountUsabilityResponseControl.newControl(false, false,
+              false, 10, false, 0));
+        }
+      }
+      searchResulthandler.handleEntry(e);
+      result = Responses.newResult(ResultCode.SUCCESS);
+      resultHandler.handleResult(result);
+      requestsInProgress.remove(context);
+    }
+  }
+
+
+
   // The mapping between entry DNs and the corresponding entries.
   private final ConcurrentHashMap<DN, Entry> entryMap = new ConcurrentHashMap<DN, Entry>();
 
@@ -152,6 +677,8 @@
   // The Set used for locking dns.
   private final HashSet<DN> lockedDNs = new HashSet<DN>();
 
+  private SSLContext sslContext;
+
 
 
   private LDAPServer()
@@ -177,258 +704,12 @@
 
 
   /**
-   * Abandons the request sent by the client.
-   *
-   * @param context
-   * @param request
-   * @throws UnsupportedOperationException
-   */
-  public void abandon(final Integer context, final AbandonRequest request)
-      throws UnsupportedOperationException
-  {
-    // Check if we have any concurrent operation with this message id.
-    final AbandonableRequest req = requestsInProgress.get(context);
-    if (req == null)
-    {
-      // Nothing to do here.
-      return;
-    }
-    // Cancel the request
-    req.cancel();
-    // No response is needed.
-  }
-
-
-
-  /**
    * @param context
    * @return
    */
   public ServerConnection<Integer> accept(final LDAPClientContext context)
   {
-    return this;
-  }
-
-
-
-  /**
-   * Adds the request sent by the client.
-   *
-   * @param context
-   * @param request
-   * @param handler
-   * @param intermediateResponseHandler
-   * @throws UnsupportedOperationException
-   */
-  public void add(final Integer context, final AddRequest request,
-      final ResultHandler<Result> handler,
-      final IntermediateResponseHandler intermediateResponseHandler)
-      throws UnsupportedOperationException
-  {
-    Result result = null;
-    final AbandonableRequest abReq = new AbandonableRequest(request);
-    requestsInProgress.put(context, abReq);
-    // Get the DN.
-    final DN dn = request.getName();
-    if (entryMap.containsKey(dn))
-    {
-      // duplicate entry.
-      result = Responses.newResult(ResultCode.ENTRY_ALREADY_EXISTS);
-      final ErrorResultException ere = ErrorResultException.wrap(result);
-      handler.handleErrorResult(ere);
-      // doesn't matter if it was canceled.
-      requestsInProgress.remove(context);
-      return;
-    }
-
-    // Create an entry out of this request.
-    final SearchResultEntry entry = Responses.newSearchResultEntry(dn);
-    for (final Control control : request.getControls())
-    {
-      entry.addControl(control);
-    }
-
-    for (final Attribute attr : request.getAllAttributes())
-    {
-      entry.addAttribute(attr);
-    }
-
-    if (abReq.isCanceled())
-    {
-      result = Responses.newResult(ResultCode.CANCELLED);
-      final ErrorResultException ere = ErrorResultException.wrap(result);
-      handler.handleErrorResult(ere);
-      requestsInProgress.remove(context);
-      return;
-    }
-    // Add this to the map.
-    entryMap.put(dn, entry);
-    requestsInProgress.remove(context);
-    result = Responses.newResult(ResultCode.SUCCESS);
-    handler.handleResult(result);
-  }
-
-
-
-  /**
-   * @param context
-   * @param version
-   * @param request
-   * @param resultHandler
-   * @param intermediateResponseHandler
-   * @throws UnsupportedOperationException
-   */
-  public void bind(final Integer context, final int version,
-      final BindRequest request,
-      final ResultHandler<? super BindResult> resultHandler,
-      final IntermediateResponseHandler intermediateResponseHandler)
-      throws UnsupportedOperationException
-  {
-    // TODO: all bind types.
-    final AbandonableRequest abReq = new AbandonableRequest(request);
-    requestsInProgress.put(context, abReq);
-    resultHandler.handleResult(Responses.newBindResult(ResultCode.SUCCESS));
-    requestsInProgress.remove(context);
-  }
-
-
-
-  /**
-   * @param context
-   * @param request
-   */
-  public void closed(final Integer context, final UnbindRequest request)
-  {
-
-  }
-
-
-
-  /**
-   * @param error
-   */
-  public void closed(final Throwable error)
-  {
-
-  }
-
-
-
-  /**
-   * @param context
-   * @param request
-   * @param resultHandler
-   * @param intermediateResponseHandler
-   * @throws UnsupportedOperationException
-   */
-  public void compare(final Integer context, final CompareRequest request,
-      final ResultHandler<? super CompareResult> resultHandler,
-      final IntermediateResponseHandler intermediateResponseHandler)
-      throws UnsupportedOperationException
-  {
-    CompareResult result = null;
-    final AbandonableRequest abReq = new AbandonableRequest(request);
-    requestsInProgress.put(context, abReq);
-    // Get the DN.
-    final DN dn = request.getName();
-    if (!entryMap.containsKey(dn))
-    {
-      // entry not found.
-      result = Responses.newCompareResult(ResultCode.NO_SUCH_ATTRIBUTE);
-      final ErrorResultException ere = ErrorResultException.wrap(result);
-      resultHandler.handleErrorResult(ere);
-      // doesn't matter if it was canceled.
-      requestsInProgress.remove(context);
-      return;
-    }
-
-    // Get the entry.
-    final Entry entry = entryMap.get(dn);
-    final AttributeDescription attrDesc = request.getAttributeDescription();
-    for (final Attribute attr : entry.getAllAttributes(attrDesc))
-    {
-      final Iterator<ByteString> it = attr.iterator();
-      while (it.hasNext())
-      {
-        final ByteString s = it.next();
-        if (abReq.isCanceled())
-        {
-          final Result r = Responses.newResult(ResultCode.CANCELLED);
-          final ErrorResultException ere = ErrorResultException.wrap(r);
-          resultHandler.handleErrorResult(ere);
-          requestsInProgress.remove(context);
-          return;
-        }
-        if (s.equals(request.getAssertionValue()))
-        {
-          result = Responses.newCompareResult(ResultCode.COMPARE_TRUE);
-          resultHandler.handleResult(result);
-        }
-      }
-    }
-    result = Responses.newCompareResult(ResultCode.COMPARE_FALSE);
-    resultHandler.handleResult(result);
-    requestsInProgress.remove(context);
-  }
-
-
-
-  /**
-   * @param context
-   * @param request
-   * @param handler
-   * @param intermediateResponseHandler
-   * @throws UnsupportedOperationException
-   */
-  public void delete(final Integer context, final DeleteRequest request,
-      final ResultHandler<Result> handler,
-      final IntermediateResponseHandler intermediateResponseHandler)
-      throws UnsupportedOperationException
-  {
-    Result result = null;
-    final AbandonableRequest abReq = new AbandonableRequest(request);
-    requestsInProgress.put(context, abReq);
-    // Get the DN.
-    final DN dn = request.getName();
-    if (!entryMap.containsKey(dn))
-    {
-      // entry is not found.
-      result = Responses.newResult(ResultCode.NO_SUCH_OBJECT);
-      final ErrorResultException ere = ErrorResultException.wrap(result);
-      handler.handleErrorResult(ere);
-      // doesn't matter if it was canceled.
-      requestsInProgress.remove(context);
-      return;
-    }
-
-    if (abReq.isCanceled())
-    {
-      result = Responses.newResult(ResultCode.CANCELLED);
-      final ErrorResultException ere = ErrorResultException.wrap(result);
-      handler.handleErrorResult(ere);
-      requestsInProgress.remove(context);
-      return;
-    }
-    // Remove this from the map.
-    entryMap.remove(dn);
-    requestsInProgress.remove(context);
-  }
-
-
-
-  /**
-   * @param context
-   * @param request
-   * @param resultHandler
-   * @param intermediateResponseHandler
-   * @throws UnsupportedOperationException
-   */
-  public <R extends ExtendedResult> void extendedRequest(final Integer context,
-      final ExtendedRequest<R> request, final ResultHandler<R> resultHandler,
-      final IntermediateResponseHandler intermediateResponseHandler)
-      throws UnsupportedOperationException
-  {
-    // TODO:
+    return new LDAPServerConnection(context);
   }
 
 
@@ -446,113 +727,21 @@
 
 
   /**
-   * @param context
-   * @param request
-   * @param resultHandler
-   * @param intermediateResponseHandler
-   * @throws UnsupportedOperationException
-   */
-  public void modify(final Integer context, final ModifyRequest request,
-      final ResultHandler<Result> resultHandler,
-      final IntermediateResponseHandler intermediateResponseHandler)
-      throws UnsupportedOperationException
-  {
-    // TODO:
-  }
-
-
-
-  /**
-   * @param context
-   * @param request
-   * @param resultHandler
-   * @param intermediateResponseHandler
-   * @throws UnsupportedOperationException
-   */
-  public void modifyDN(final Integer context, final ModifyDNRequest request,
-      final ResultHandler<Result> resultHandler,
-      final IntermediateResponseHandler intermediateResponseHandler)
-      throws UnsupportedOperationException
-  {
-    // TODO
-  }
-
-
-
-  /**
-   * @param context
-   * @param request
-   * @param resultHandler
-   * @param searchResulthandler
-   * @param intermediateResponseHandler
-   * @throws UnsupportedOperationException
-   */
-  public void search(final Integer context, final SearchRequest request,
-      final ResultHandler<Result> resultHandler,
-      final SearchResultHandler searchResulthandler,
-      final IntermediateResponseHandler intermediateResponseHandler)
-      throws UnsupportedOperationException
-  {
-    Result result = null;
-    final AbandonableRequest abReq = new AbandonableRequest(request);
-    requestsInProgress.put(context, abReq);
-    // Get the DN.
-    final DN dn = request.getName();
-    if (!entryMap.containsKey(dn))
-    {
-      // Entry not found.
-      result = Responses.newResult(ResultCode.NO_SUCH_OBJECT);
-      final ErrorResultException ere = ErrorResultException.wrap(result);
-      resultHandler.handleErrorResult(ere);
-      // Should searchResultHandler handle anything?
-
-      // doesn't matter if it was canceled.
-      requestsInProgress.remove(context);
-      return;
-    }
-
-    if (abReq.isCanceled())
-    {
-      result = Responses.newResult(ResultCode.CANCELLED);
-      final ErrorResultException ere = ErrorResultException.wrap(result);
-      resultHandler.handleErrorResult(ere);
-      requestsInProgress.remove(context);
-      return;
-    }
-
-    final SearchResultEntry e = Responses.newSearchResultEntry(
-        new LinkedHashMapEntry(entryMap.get(dn)));
-    // Check we have had any controls in the request.
-    for (final Control control : request.getControls())
-    {
-      if (control.getOID().equals(AccountUsabilityRequestControl.OID))
-      {
-        e.addControl(AccountUsabilityResponseControl.newControl(false, false,
-            false, 10, false, 0));
-      }
-    }
-    searchResulthandler.handleEntry(e);
-    result = Responses.newResult(ResultCode.SUCCESS);
-    resultHandler.handleResult(result);
-    requestsInProgress.remove(context);
-  }
-
-
-
-  /**
    * Starts the server.
    *
    * @param port
    * @exception IOException
    */
-  public synchronized void start(final int port) throws IOException
+  public synchronized void start(final int port) throws Exception
   {
     if (isRunning)
     {
       return;
     }
+    sslContext = new SSLContextBuilder().getSSLContext();
+
     transport.setSelectorRunnersCount(2);
-    listener = new LDAPListener(port, new LDAPServer(),
+    listener = new LDAPListener(port, getInstance(),
         new GrizzlyLDAPListenerOptions().setTCPNIOTransport(transport)
             .setBacklog(4096));
     transport.start();
diff --git a/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/SynchronousConnectionTestCase.java b/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/SynchronousConnectionTestCase.java
index aa38494..8e2e26f 100644
--- a/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/SynchronousConnectionTestCase.java
+++ b/opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/SynchronousConnectionTestCase.java
@@ -29,17 +29,17 @@
 
 
 
-import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
 import java.util.List;
 
+import org.opends.sdk.ldif.EntryReader;
 import org.opends.sdk.requests.Requests;
 import org.opends.sdk.responses.BindResult;
 import org.opends.sdk.responses.CompareResult;
 import org.opends.sdk.responses.Result;
-import org.opends.sdk.responses.SearchResultEntry;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
@@ -155,10 +155,11 @@
   public void testSearchRequest() throws Exception
   {
     final SynchronousConnection con = new SynchronousConnection(asyncCon);
-    final List<SearchResultEntry> entries = con.search(
+    final EntryReader reader = con.search(
         "uid=user.0,ou=people,o=test", SearchScope.BASE_OBJECT,
         "objectclass=*", "cn");
-    assertEquals(entries.size(), 1);
+    reader.readEntry();
+    assertNull(reader.readEntry());
   }
   // TODO: add more tests.
 }

--
Gitblit v1.10.0