From cc5a87f6db56c4dc534636abb5a35efc1d15dee7 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Thu, 02 Jun 2011 01:01:18 +0000
Subject: [PATCH] Fix OPENDJ-183: Add support for RequestContext and RequestHandlers

---
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnection.java          |   26 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractConnection.java                      |   28 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java                           |   20 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/ConnectionEntryReader.java                   |    5 
 opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPCompare.java                   |    7 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java     |    7 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core.properties                         |   16 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactoryAdapter.java            |  983 ++++++++++++++++
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java       |    6 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestContext.java                          |  118 +
 opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/server/Main.java                |  864 ++++++-------
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java   |   22 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java                              |   22 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/AbstractLDAPFutureResultImpl.java            |    2 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/AsynchronousFutureResult.java                |    8 
 opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/proxy/Main.java                 |  993 +++++++--------
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java    |    7 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/LDAPListenerTestCase.java                    |    2 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java      |   37 
 opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPPasswordModify.java            |    8 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/CancelRequestListener.java                   |   64 +
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java          |    9 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnectionFactoryImpl.java               |   25 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactory.java                   |   61 +
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ErrorResultException.java                    |   84 +
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Connections.java                             |   77 +
 opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPModify.java                    |   23 
 opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java                    |    7 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnection.java                          |   14 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/AbstractExtendedResultDecoder.java |    8 
 30 files changed, 2,378 insertions(+), 1,175 deletions(-)

diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/proxy/Main.java b/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/proxy/Main.java
index dd99485..2d35f6f 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/proxy/Main.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/proxy/Main.java
@@ -62,540 +62,15 @@
  */
 public final class Main
 {
-  /**
-   * Proxy server connection factory implementation.
-   */
-  private static final class Proxy implements
-      ServerConnectionFactory<LDAPClientContext, Integer>
+  private static final class ProxyBackend implements
+      RequestHandler<RequestContext>
   {
-    private final class ServerConnectionImpl implements
-        ServerConnection<Integer>
-    {
-
-      private abstract class AbstractRequestCompletionHandler<R extends Result,
-                                                              H extends ResultHandler<? super R>>
-          implements ResultHandler<R>
-      {
-        final H resultHandler;
-        final AsynchronousConnection connection;
-
-
-
-        AbstractRequestCompletionHandler(
-            final AsynchronousConnection connection,
-            final H resultHandler)
-        {
-          this.connection = connection;
-          this.resultHandler = resultHandler;
-        }
-
-
-
-        @Override
-        public final void handleErrorResult(
-            final ErrorResultException error)
-        {
-          connection.close();
-          resultHandler.handleErrorResult(error);
-        }
-
-
-
-        @Override
-        public final void handleResult(final R result)
-        {
-          connection.close();
-          resultHandler.handleResult(result);
-        }
-
-      }
-
-
-
-      private abstract class ConnectionCompletionHandler<R extends Result>
-          implements ResultHandler<AsynchronousConnection>
-      {
-        private final ResultHandler<? super R> resultHandler;
-
-
-
-        ConnectionCompletionHandler(
-            final ResultHandler<? super R> resultHandler)
-        {
-          this.resultHandler = resultHandler;
-        }
-
-
-
-        @Override
-        public final void handleErrorResult(
-            final ErrorResultException error)
-        {
-          resultHandler.handleErrorResult(error);
-        }
-
-
-
-        @Override
-        public abstract void handleResult(
-            AsynchronousConnection connection);
-
-      }
-
-
-
-      private final class RequestCompletionHandler<R extends Result>
-          extends
-          AbstractRequestCompletionHandler<R, ResultHandler<? super R>>
-      {
-        RequestCompletionHandler(
-            final AsynchronousConnection connection,
-            final ResultHandler<? super R> resultHandler)
-        {
-          super(connection, resultHandler);
-        }
-      }
-
-
-
-      private final class SearchRequestCompletionHandler
-          extends
-          AbstractRequestCompletionHandler<Result, SearchResultHandler>
-          implements SearchResultHandler
-      {
-
-        SearchRequestCompletionHandler(
-            final AsynchronousConnection connection,
-            final SearchResultHandler resultHandler)
-        {
-          super(connection, resultHandler);
-        }
-
-
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public final boolean handleEntry(final SearchResultEntry entry)
-        {
-          return resultHandler.handleEntry(entry);
-        }
-
-
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public final boolean handleReference(
-            final SearchResultReference reference)
-        {
-          return resultHandler.handleReference(reference);
-        }
-
-      }
-
-
-
-      private volatile ProxiedAuthV2RequestControl proxiedAuthControl = null;
-
-
-
-      private ServerConnectionImpl(
-          final LDAPClientContext clientContext)
-      {
-        // Nothing to do.
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleAbandon(final Integer requestContext,
-          final AbandonRequest request)
-          throws UnsupportedOperationException
-      {
-        // Not implemented.
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleAdd(
-          final Integer requestContext,
-          final AddRequest request,
-          final ResultHandler<? super Result> resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        addProxiedAuthControl(request);
-        final ConnectionCompletionHandler<Result> outerHandler =
-          new ConnectionCompletionHandler<Result>(resultHandler)
-        {
-
-          @Override
-          public void handleResult(
-              final AsynchronousConnection connection)
-          {
-            final RequestCompletionHandler<Result> innerHandler =
-              new RequestCompletionHandler<Result>(connection, resultHandler);
-            connection.add(request, innerHandler,
-                intermediateResponseHandler);
-          }
-
-        };
-
-        factory.getAsynchronousConnection(outerHandler);
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleBind(
-          final Integer requestContext,
-          final int version,
-          final BindRequest request,
-          final ResultHandler<? super BindResult> resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-
-        if (request.getAuthenticationType() != ((byte) 0x80))
-        {
-          // TODO: SASL authentication not implemented.
-          resultHandler.handleErrorResult(newErrorResult(
-              ResultCode.PROTOCOL_ERROR,
-              "non-SIMPLE authentication not supported: "
-                  + request.getAuthenticationType()));
-        }
-        else
-        {
-          // Authenticate using a separate bind connection pool, because we
-          // don't want to change the state of the pooled connection.
-          final ConnectionCompletionHandler<BindResult> outerHandler =
-            new ConnectionCompletionHandler<BindResult>(resultHandler)
-          {
-
-            @Override
-            public void handleResult(
-                final AsynchronousConnection connection)
-            {
-              final ResultHandler<BindResult> innerHandler =
-                new ResultHandler<BindResult>()
-              {
-
-                @Override
-                public final void handleErrorResult(
-                    final ErrorResultException error)
-                {
-                  connection.close();
-                  resultHandler.handleErrorResult(error);
-                }
-
-
-
-                @Override
-                public final void handleResult(final BindResult result)
-                {
-                  connection.close();
-                  proxiedAuthControl = ProxiedAuthV2RequestControl
-                      .newControl("dn:" + request.getName());
-                  resultHandler.handleResult(result);
-                }
-              };
-              connection.bind(request, innerHandler,
-                  intermediateResponseHandler);
-            }
-
-          };
-
-          proxiedAuthControl = null;
-          bindFactory.getAsynchronousConnection(outerHandler);
-        }
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleCompare(
-          final Integer requestContext,
-          final CompareRequest request,
-          final ResultHandler<? super CompareResult> resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        addProxiedAuthControl(request);
-        final ConnectionCompletionHandler<CompareResult> outerHandler =
-          new ConnectionCompletionHandler<CompareResult>(resultHandler)
-        {
-
-          @Override
-          public void handleResult(
-              final AsynchronousConnection connection)
-          {
-            final RequestCompletionHandler<CompareResult> innerHandler =
-              new RequestCompletionHandler<CompareResult>(connection, resultHandler);
-            connection.compare(request, innerHandler,
-                intermediateResponseHandler);
-          }
-
-        };
-
-        factory.getAsynchronousConnection(outerHandler);
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleConnectionClosed(
-          final Integer requestContext, final UnbindRequest request)
-      {
-        // Client connection closed.
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleConnectionDisconnected(
-          final ResultCode resultCode, final String message)
-      {
-        // Client disconnected by server.
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleConnectionError(final Throwable error)
-      {
-        // Client connection failed.
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleDelete(
-          final Integer requestContext,
-          final DeleteRequest request,
-          final ResultHandler<? super Result> resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        addProxiedAuthControl(request);
-        final ConnectionCompletionHandler<Result> outerHandler =
-          new ConnectionCompletionHandler<Result>(resultHandler)
-        {
-
-          @Override
-          public void handleResult(
-              final AsynchronousConnection connection)
-          {
-            final RequestCompletionHandler<Result> innerHandler =
-              new RequestCompletionHandler<Result>(connection, resultHandler);
-            connection.delete(request, innerHandler,
-                intermediateResponseHandler);
-          }
-
-        };
-
-        factory.getAsynchronousConnection(outerHandler);
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public <R extends ExtendedResult> void handleExtendedRequest(
-          final Integer requestContext,
-          final ExtendedRequest<R> request,
-          final ResultHandler<? super R> resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        if (request.getOID().equals(CancelExtendedRequest.OID))
-        {
-          // TODO: not implemented.
-          resultHandler.handleErrorResult(newErrorResult(
-              ResultCode.PROTOCOL_ERROR,
-              "Cancel extended request operation not supported"));
-        }
-        else if (request.getOID().equals(StartTLSExtendedRequest.OID))
-        {
-          // TODO: not implemented.
-          resultHandler.handleErrorResult(newErrorResult(
-              ResultCode.PROTOCOL_ERROR,
-              "StartTLS extended request operation not supported"));
-        }
-        else
-        {
-          // Forward all other extended operations.
-          addProxiedAuthControl(request);
-
-          final ConnectionCompletionHandler<R> outerHandler =
-            new ConnectionCompletionHandler<R>(resultHandler)
-          {
-
-            @Override
-            public void handleResult(
-                final AsynchronousConnection connection)
-            {
-              final RequestCompletionHandler<R> innerHandler =
-                new RequestCompletionHandler<R>(connection, resultHandler);
-              connection.extendedRequest(request, innerHandler,
-                  intermediateResponseHandler);
-            }
-
-          };
-
-          factory.getAsynchronousConnection(outerHandler);
-        }
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleModify(
-          final Integer requestContext,
-          final ModifyRequest request,
-          final ResultHandler<? super Result> resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        addProxiedAuthControl(request);
-        final ConnectionCompletionHandler<Result> outerHandler =
-          new ConnectionCompletionHandler<Result>(resultHandler)
-        {
-
-          @Override
-          public void handleResult(
-              final AsynchronousConnection connection)
-          {
-            final RequestCompletionHandler<Result> innerHandler =
-              new RequestCompletionHandler<Result>(connection, resultHandler);
-            connection.modify(request, innerHandler,
-                intermediateResponseHandler);
-          }
-
-        };
-
-        factory.getAsynchronousConnection(outerHandler);
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleModifyDN(
-          final Integer requestContext,
-          final ModifyDNRequest request,
-          final ResultHandler<? super Result> resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        addProxiedAuthControl(request);
-        final ConnectionCompletionHandler<Result> outerHandler =
-          new ConnectionCompletionHandler<Result>(resultHandler)
-        {
-
-          @Override
-          public void handleResult(
-              final AsynchronousConnection connection)
-          {
-            final RequestCompletionHandler<Result> innerHandler =
-              new RequestCompletionHandler<Result>(connection, resultHandler);
-            connection.modifyDN(request, innerHandler,
-                intermediateResponseHandler);
-          }
-
-        };
-
-        factory.getAsynchronousConnection(outerHandler);
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleSearch(
-          final Integer requestContext,
-          final SearchRequest request,
-          final SearchResultHandler resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        addProxiedAuthControl(request);
-        final ConnectionCompletionHandler<Result> outerHandler =
-          new ConnectionCompletionHandler<Result>(resultHandler)
-        {
-
-          @Override
-          public void handleResult(
-              final AsynchronousConnection connection)
-          {
-            final SearchRequestCompletionHandler innerHandler =
-              new SearchRequestCompletionHandler(connection, resultHandler);
-            connection.search(request, innerHandler,
-                intermediateResponseHandler);
-          }
-
-        };
-
-        factory.getAsynchronousConnection(outerHandler);
-      }
-
-
-
-      private void addProxiedAuthControl(final Request request)
-      {
-        final ProxiedAuthV2RequestControl control = proxiedAuthControl;
-        if (control != null)
-        {
-          request.addControl(control);
-        }
-      }
-
-    }
-
-
-
     private final ConnectionFactory factory;
     private final ConnectionFactory bindFactory;
 
 
 
-    private Proxy(final ConnectionFactory factory,
+    private ProxyBackend(final ConnectionFactory factory,
         final ConnectionFactory bindFactory)
     {
       this.factory = factory;
@@ -604,15 +79,452 @@
 
 
 
+    private abstract class AbstractRequestCompletionHandler
+        <R extends Result, H extends ResultHandler<? super R>>
+        implements ResultHandler<R>
+    {
+      final H resultHandler;
+      final AsynchronousConnection connection;
+
+
+
+      AbstractRequestCompletionHandler(
+          final AsynchronousConnection connection,
+          final H resultHandler)
+      {
+        this.connection = connection;
+        this.resultHandler = resultHandler;
+      }
+
+
+
+      @Override
+      public final void handleErrorResult(
+          final ErrorResultException error)
+      {
+        connection.close();
+        resultHandler.handleErrorResult(error);
+      }
+
+
+
+      @Override
+      public final void handleResult(final R result)
+      {
+        connection.close();
+        resultHandler.handleResult(result);
+      }
+
+    }
+
+
+
+    private abstract class ConnectionCompletionHandler<R extends Result>
+        implements ResultHandler<AsynchronousConnection>
+    {
+      private final ResultHandler<? super R> resultHandler;
+
+
+
+      ConnectionCompletionHandler(
+          final ResultHandler<? super R> resultHandler)
+      {
+        this.resultHandler = resultHandler;
+      }
+
+
+
+      @Override
+      public final void handleErrorResult(
+          final ErrorResultException error)
+      {
+        resultHandler.handleErrorResult(error);
+      }
+
+
+
+      @Override
+      public abstract void handleResult(
+          AsynchronousConnection connection);
+
+    }
+
+
+
+    private final class RequestCompletionHandler<R extends Result>
+        extends
+        AbstractRequestCompletionHandler<R, ResultHandler<? super R>>
+    {
+      RequestCompletionHandler(
+          final AsynchronousConnection connection,
+          final ResultHandler<? super R> resultHandler)
+      {
+        super(connection, resultHandler);
+      }
+    }
+
+
+
+    private final class SearchRequestCompletionHandler extends
+        AbstractRequestCompletionHandler<Result, SearchResultHandler>
+        implements SearchResultHandler
+    {
+
+      SearchRequestCompletionHandler(
+          final AsynchronousConnection connection,
+          final SearchResultHandler resultHandler)
+      {
+        super(connection, resultHandler);
+      }
+
+
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public final boolean handleEntry(final SearchResultEntry entry)
+      {
+        return resultHandler.handleEntry(entry);
+      }
+
+
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public final boolean handleReference(
+          final SearchResultReference reference)
+      {
+        return resultHandler.handleReference(reference);
+      }
+
+    }
+
+
+
+    private volatile ProxiedAuthV2RequestControl proxiedAuthControl = null;
+
+
+
     /**
      * {@inheritDoc}
      */
     @Override
-    public ServerConnection<Integer> handleAccept(
-        final LDAPClientContext clientContext)
-        throws ErrorResultException
+    public void handleAdd(final RequestContext requestContext,
+        final AddRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
     {
-      return new ServerConnectionImpl(clientContext);
+      addProxiedAuthControl(request);
+      final ConnectionCompletionHandler<Result> outerHandler =
+        new ConnectionCompletionHandler<Result>(resultHandler)
+      {
+
+        @Override
+        public void handleResult(
+            final AsynchronousConnection connection)
+        {
+          final RequestCompletionHandler<Result> innerHandler =
+            new RequestCompletionHandler<Result>(connection, resultHandler);
+          connection.add(request, innerHandler,
+              intermediateResponseHandler);
+        }
+
+      };
+
+      factory.getAsynchronousConnection(outerHandler);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleBind(final RequestContext requestContext,
+        final int version, final BindRequest request,
+        final ResultHandler<? super BindResult> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+
+      if (request.getAuthenticationType() != ((byte) 0x80))
+      {
+        // TODO: SASL authentication not implemented.
+        resultHandler.handleErrorResult(newErrorResult(
+            ResultCode.PROTOCOL_ERROR,
+            "non-SIMPLE authentication not supported: "
+                + request.getAuthenticationType()));
+      }
+      else
+      {
+        // Authenticate using a separate bind connection pool, because we
+        // don't want to change the state of the pooled connection.
+        final ConnectionCompletionHandler<BindResult> outerHandler =
+          new ConnectionCompletionHandler<BindResult>(resultHandler)
+        {
+
+          @Override
+          public void handleResult(
+              final AsynchronousConnection connection)
+          {
+            final ResultHandler<BindResult> innerHandler = new ResultHandler<BindResult>()
+            {
+
+              @Override
+              public final void handleErrorResult(
+                  final ErrorResultException error)
+              {
+                connection.close();
+                resultHandler.handleErrorResult(error);
+              }
+
+
+
+              @Override
+              public final void handleResult(final BindResult result)
+              {
+                connection.close();
+                proxiedAuthControl = ProxiedAuthV2RequestControl
+                    .newControl("dn:" + request.getName());
+                resultHandler.handleResult(result);
+              }
+            };
+            connection.bind(request, innerHandler,
+                intermediateResponseHandler);
+          }
+
+        };
+
+        proxiedAuthControl = null;
+        bindFactory.getAsynchronousConnection(outerHandler);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleCompare(final RequestContext requestContext,
+        final CompareRequest request,
+        final ResultHandler<? super CompareResult> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      addProxiedAuthControl(request);
+      final ConnectionCompletionHandler<CompareResult> outerHandler =
+        new ConnectionCompletionHandler<CompareResult>(resultHandler)
+      {
+
+        @Override
+        public void handleResult(
+            final AsynchronousConnection connection)
+        {
+          final RequestCompletionHandler<CompareResult> innerHandler =
+            new RequestCompletionHandler<CompareResult>(connection, resultHandler);
+          connection.compare(request, innerHandler,
+              intermediateResponseHandler);
+        }
+
+      };
+
+      factory.getAsynchronousConnection(outerHandler);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleDelete(final RequestContext requestContext,
+        final DeleteRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      addProxiedAuthControl(request);
+      final ConnectionCompletionHandler<Result> outerHandler =
+        new ConnectionCompletionHandler<Result>(resultHandler)
+      {
+
+        @Override
+        public void handleResult(
+            final AsynchronousConnection connection)
+        {
+          final RequestCompletionHandler<Result> innerHandler =
+            new RequestCompletionHandler<Result>(connection, resultHandler);
+          connection.delete(request, innerHandler,
+              intermediateResponseHandler);
+        }
+
+      };
+
+      factory.getAsynchronousConnection(outerHandler);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <R extends ExtendedResult> void handleExtendedRequest(
+        final RequestContext requestContext,
+        final ExtendedRequest<R> request,
+        final ResultHandler<? super R> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      if (request.getOID().equals(CancelExtendedRequest.OID))
+      {
+        // TODO: not implemented.
+        resultHandler.handleErrorResult(newErrorResult(
+            ResultCode.PROTOCOL_ERROR,
+            "Cancel extended request operation not supported"));
+      }
+      else if (request.getOID().equals(StartTLSExtendedRequest.OID))
+      {
+        // TODO: not implemented.
+        resultHandler.handleErrorResult(newErrorResult(
+            ResultCode.PROTOCOL_ERROR,
+            "StartTLS extended request operation not supported"));
+      }
+      else
+      {
+        // Forward all other extended operations.
+        addProxiedAuthControl(request);
+
+        final ConnectionCompletionHandler<R> outerHandler =
+          new ConnectionCompletionHandler<R>(resultHandler)
+        {
+
+          @Override
+          public void handleResult(
+              final AsynchronousConnection connection)
+          {
+            final RequestCompletionHandler<R> innerHandler =
+              new RequestCompletionHandler<R>(connection, resultHandler);
+            connection.extendedRequest(request, innerHandler,
+                intermediateResponseHandler);
+          }
+
+        };
+
+        factory.getAsynchronousConnection(outerHandler);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleModify(final RequestContext requestContext,
+        final ModifyRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      addProxiedAuthControl(request);
+      final ConnectionCompletionHandler<Result> outerHandler =
+        new ConnectionCompletionHandler<Result>(resultHandler)
+      {
+
+        @Override
+        public void handleResult(
+            final AsynchronousConnection connection)
+        {
+          final RequestCompletionHandler<Result> innerHandler =
+            new RequestCompletionHandler<Result>(connection, resultHandler);
+          connection.modify(request, innerHandler,
+              intermediateResponseHandler);
+        }
+
+      };
+
+      factory.getAsynchronousConnection(outerHandler);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleModifyDN(final RequestContext requestContext,
+        final ModifyDNRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      addProxiedAuthControl(request);
+      final ConnectionCompletionHandler<Result> outerHandler =
+        new ConnectionCompletionHandler<Result>(resultHandler)
+      {
+
+        @Override
+        public void handleResult(
+            final AsynchronousConnection connection)
+        {
+          final RequestCompletionHandler<Result> innerHandler =
+            new RequestCompletionHandler<Result>(connection, resultHandler);
+          connection.modifyDN(request, innerHandler,
+              intermediateResponseHandler);
+        }
+
+      };
+
+      factory.getAsynchronousConnection(outerHandler);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleSearch(final RequestContext requestContext,
+        final SearchRequest request,
+        final SearchResultHandler resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      addProxiedAuthControl(request);
+      final ConnectionCompletionHandler<Result> outerHandler =
+        new ConnectionCompletionHandler<Result>(resultHandler)
+      {
+
+        @Override
+        public void handleResult(
+            final AsynchronousConnection connection)
+        {
+          final SearchRequestCompletionHandler innerHandler =
+            new SearchRequestCompletionHandler(connection, resultHandler);
+          connection.search(request, innerHandler,
+              intermediateResponseHandler);
+        }
+
+      };
+
+      factory.getAsynchronousConnection(outerHandler);
+    }
+
+
+
+    private void addProxiedAuthControl(final Request request)
+    {
+      final ProxiedAuthV2RequestControl control = proxiedAuthControl;
+      if (control != null)
+      {
+        request.addControl(control);
+      }
     }
 
   }
@@ -654,23 +566,28 @@
           new LDAPConnectionFactory(remoteAddress, remotePort),
           Integer.MAX_VALUE));
     }
-    final RoundRobinLoadBalancingAlgorithm algorithm =
-      new RoundRobinLoadBalancingAlgorithm(factories);
-    final RoundRobinLoadBalancingAlgorithm bindAlgorithm =
-      new RoundRobinLoadBalancingAlgorithm(bindFactories);
+    final RoundRobinLoadBalancingAlgorithm algorithm = new RoundRobinLoadBalancingAlgorithm(
+        factories);
+    final RoundRobinLoadBalancingAlgorithm bindAlgorithm = new RoundRobinLoadBalancingAlgorithm(
+        bindFactories);
     final ConnectionFactory factory = Connections
         .newLoadBalancer(algorithm);
     final ConnectionFactory bindFactory = Connections
         .newLoadBalancer(bindAlgorithm);
 
+    // Create a server connection adapter.
+    final ProxyBackend backend = new ProxyBackend(factory, bindFactory);
+    final ServerConnectionFactory<LDAPClientContext, Integer> connectionHandler =
+      Connections.newServerConnectionFactory(backend);
+
     // Create listener.
     final LDAPListenerOptions options = new LDAPListenerOptions()
         .setBacklog(4096);
     LDAPListener listener = null;
     try
     {
-      listener = new LDAPListener(localAddress, localPort, new Proxy(
-          factory, bindFactory), options);
+      listener = new LDAPListener(localAddress, localPort,
+          connectionHandler, options);
       System.out.println("Press any key to stop the server...");
       System.in.read();
     }
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/server/Main.java b/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/server/Main.java
index a00a1a8..98368d2 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/server/Main.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/server/Main.java
@@ -64,442 +64,17 @@
  */
 public final class Main
 {
-  /**
-   * Proxy server connection factory implementation.
-   */
-  private static final class Store implements
-      ServerConnectionFactory<LDAPClientContext, Integer>
+  private static final class MemoryBackend implements
+      RequestHandler<RequestContext>
   {
-    private final class ServerConnectionImpl implements
-        ServerConnection<Integer>
-    {
-
-      private ServerConnectionImpl(
-          final LDAPClientContext clientContext)
-      {
-        // Nothing to do.
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleAbandon(final Integer requestContext,
-          final AbandonRequest request)
-          throws UnsupportedOperationException
-      {
-        // Not implemented.
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleAdd(
-          final Integer requestContext,
-          final AddRequest request,
-          final ResultHandler<? super Result> resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        // TODO: controls.
-        entryLock.writeLock().lock();
-        try
-        {
-          DN dn = request.getName();
-          if (entries.containsKey(dn))
-          {
-            resultHandler
-                .handleErrorResult(ErrorResultException
-                    .newErrorResult(ResultCode.ENTRY_ALREADY_EXISTS,
-                        "The entry " + dn.toString()
-                            + " already exists"));
-          }
-
-          DN parent = dn.parent();
-          if (!entries.containsKey(parent))
-          {
-            resultHandler.handleErrorResult(ErrorResultException
-                .newErrorResult(ResultCode.NO_SUCH_OBJECT,
-                    "The parent entry " + parent.toString()
-                        + " does not exist"));
-          }
-          else
-          {
-            entries.put(dn, request);
-            resultHandler.handleResult(Responses
-                .newResult(ResultCode.SUCCESS));
-          }
-        }
-        finally
-        {
-          entryLock.writeLock().unlock();
-        }
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleBind(
-          final Integer requestContext,
-          final int version,
-          final BindRequest request,
-          final ResultHandler<? super BindResult> resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        if (request.getAuthenticationType() != ((byte) 0x80))
-        {
-          // TODO: SASL authentication not implemented.
-          resultHandler.handleErrorResult(newErrorResult(
-              ResultCode.PROTOCOL_ERROR,
-              "non-SIMPLE authentication not supported: "
-                  + request.getAuthenticationType()));
-        }
-        else
-        {
-          // TODO: always succeed.
-          resultHandler.handleResult(Responses
-              .newBindResult(ResultCode.SUCCESS));
-        }
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleCompare(
-          final Integer requestContext,
-          final CompareRequest request,
-          final ResultHandler<? super CompareResult> resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        // TODO:
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleConnectionClosed(
-          final Integer requestContext, final UnbindRequest request)
-      {
-        // Nothing to do.
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleConnectionDisconnected(
-          final ResultCode resultCode, final String message)
-      {
-        // Nothing to do.
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleConnectionError(final Throwable error)
-      {
-        // Nothing to do.
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleDelete(
-          final Integer requestContext,
-          final DeleteRequest request,
-          final ResultHandler<? super Result> resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        // TODO: controls.
-        entryLock.writeLock().lock();
-        try
-        {
-          // TODO: check for children.
-          DN dn = request.getName();
-          if (!entries.containsKey(dn))
-          {
-            resultHandler
-                .handleErrorResult(ErrorResultException
-                    .newErrorResult(ResultCode.NO_SUCH_OBJECT,
-                        "The entry " + dn.toString()
-                            + " does not exist"));
-          }
-          else
-          {
-            entries.remove(dn);
-            resultHandler.handleResult(Responses
-                .newResult(ResultCode.SUCCESS));
-          }
-        }
-        finally
-        {
-          entryLock.writeLock().unlock();
-        }
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public <R extends ExtendedResult> void handleExtendedRequest(
-          final Integer requestContext,
-          final ExtendedRequest<R> request,
-          final ResultHandler<? super R> resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        // TODO: not implemented.
-        resultHandler.handleErrorResult(newErrorResult(
-            ResultCode.PROTOCOL_ERROR,
-            "Extended request operation not supported"));
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleModify(
-          final Integer requestContext,
-          final ModifyRequest request,
-          final ResultHandler<? super Result> resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        // TODO: controls.
-        // TODO: read lock is not really enough since concurrent updates may
-        // still occur to the same entry.
-        entryLock.readLock().lock();
-        try
-        {
-          DN dn = request.getName();
-          Entry entry = entries.get(dn);
-          if (entry == null)
-          {
-            resultHandler
-                .handleErrorResult(ErrorResultException
-                    .newErrorResult(ResultCode.NO_SUCH_OBJECT,
-                        "The entry " + dn.toString()
-                            + " does not exist"));
-          }
-
-          Entry newEntry = new LinkedHashMapEntry(entry);
-          for (Modification mod : request.getModifications())
-          {
-            ModificationType modType = mod.getModificationType();
-            if (modType.equals(ModificationType.ADD))
-            {
-              // TODO: Reject empty attribute and duplicate values.
-              newEntry.addAttribute(mod.getAttribute(), null);
-            }
-            else if (modType.equals(ModificationType.DELETE))
-            {
-              // TODO: Reject missing values.
-              newEntry.removeAttribute(mod.getAttribute(), null);
-            }
-            else if (modType.equals(ModificationType.REPLACE))
-            {
-              newEntry.replaceAttribute(mod.getAttribute());
-            }
-            else
-            {
-              resultHandler
-                  .handleErrorResult(newErrorResult(
-                      ResultCode.PROTOCOL_ERROR,
-                      "Modify request contains an unsupported modification type"));
-              return;
-            }
-          }
-
-          entries.put(dn, newEntry);
-          resultHandler.handleResult(Responses
-              .newResult(ResultCode.SUCCESS));
-        }
-        finally
-        {
-          entryLock.readLock().unlock();
-        }
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleModifyDN(
-          final Integer requestContext,
-          final ModifyDNRequest request,
-          final ResultHandler<? super Result> resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        // TODO: not implemented.
-        resultHandler.handleErrorResult(newErrorResult(
-            ResultCode.PROTOCOL_ERROR,
-            "ModifyDN request operation not supported"));
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public void handleSearch(
-          final Integer requestContext,
-          final SearchRequest request,
-          final SearchResultHandler resultHandler,
-          final IntermediateResponseHandler intermediateResponseHandler)
-          throws UnsupportedOperationException
-      {
-        // TODO: controls, limits, etc.
-        entryLock.readLock().lock();
-        try
-        {
-          DN dn = request.getName();
-          Entry baseEntry = entries.get(dn);
-          if (baseEntry == null)
-          {
-            resultHandler
-                .handleErrorResult(ErrorResultException
-                    .newErrorResult(ResultCode.NO_SUCH_OBJECT,
-                        "The entry " + dn.toString()
-                            + " does not exist"));
-          }
-
-          SearchScope scope = request.getScope();
-          Filter filter = request.getFilter();
-          Matcher matcher = filter.matcher();
-
-          if (scope.equals(SearchScope.BASE_OBJECT))
-          {
-            if (matcher.matches(baseEntry).toBoolean())
-            {
-              sendEntry(request, resultHandler, baseEntry);
-            }
-          }
-          else if (scope.equals(SearchScope.SINGLE_LEVEL))
-          {
-            sendEntry(request, resultHandler, baseEntry);
-
-            NavigableMap<DN, Entry> subtree = entries.tailMap(dn,
-                false);
-            for (Entry entry : subtree.values())
-            {
-              DN childDN = entry.getName();
-              if (childDN.isChildOf(dn))
-              {
-                if (!matcher.matches(entry).toBoolean())
-                {
-                  continue;
-                }
-
-                if (!sendEntry(request, resultHandler, entry))
-                {
-                  // Caller has asked to stop sending results.
-                  break;
-                }
-              }
-              else if (!childDN.isSubordinateOrEqualTo(dn))
-              {
-                // The remaining entries will be out of scope.
-                break;
-              }
-            }
-          }
-          else if (scope.equals(SearchScope.WHOLE_SUBTREE))
-          {
-            NavigableMap<DN, Entry> subtree = entries.tailMap(dn);
-            for (Entry entry : subtree.values())
-            {
-              DN childDN = entry.getName();
-              if (childDN.isSubordinateOrEqualTo(dn))
-              {
-                if (!matcher.matches(entry).toBoolean())
-                {
-                  continue;
-                }
-
-                if (!sendEntry(request, resultHandler, entry))
-                {
-                  // Caller has asked to stop sending results.
-                  break;
-                }
-              }
-              else
-              {
-                // The remaining entries will be out of scope.
-                break;
-              }
-            }
-          }
-          else
-          {
-            resultHandler
-                .handleErrorResult(newErrorResult(
-                    ResultCode.PROTOCOL_ERROR,
-                    "Search request contains an unsupported search scope"));
-            return;
-          }
-
-          resultHandler.handleResult(Responses
-              .newResult(ResultCode.SUCCESS));
-        }
-        finally
-        {
-          entryLock.readLock().unlock();
-        }
-      }
-
-
-
-      private boolean sendEntry(SearchRequest request,
-          SearchResultHandler resultHandler, Entry entry)
-      {
-        // TODO: check filter, strip attributes.
-        return resultHandler.handleEntry(Responses
-            .newSearchResultEntry(entry));
-      }
-    }
-
-
-
     private final ConcurrentSkipListMap<DN, Entry> entries;
 
     private final ReentrantReadWriteLock entryLock = new ReentrantReadWriteLock();
 
 
 
-    private Store(final ConcurrentSkipListMap<DN, Entry> entries)
+    private MemoryBackend(
+        final ConcurrentSkipListMap<DN, Entry> entries)
     {
       this.entries = entries;
     }
@@ -510,13 +85,354 @@
      * {@inheritDoc}
      */
     @Override
-    public ServerConnection<Integer> handleAccept(
-        final LDAPClientContext clientContext)
-        throws ErrorResultException
+    public void handleAdd(final RequestContext requestContext,
+        final AddRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
     {
-      return new ServerConnectionImpl(clientContext);
+      // TODO: controls.
+      entryLock.writeLock().lock();
+      try
+      {
+        DN dn = request.getName();
+        if (entries.containsKey(dn))
+        {
+          resultHandler.handleErrorResult(ErrorResultException
+              .newErrorResult(ResultCode.ENTRY_ALREADY_EXISTS,
+                  "The entry " + dn.toString() + " already exists"));
+        }
+
+        DN parent = dn.parent();
+        if (!entries.containsKey(parent))
+        {
+          resultHandler.handleErrorResult(ErrorResultException
+              .newErrorResult(ResultCode.NO_SUCH_OBJECT,
+                  "The parent entry " + parent.toString()
+                      + " does not exist"));
+        }
+        else
+        {
+          entries.put(dn, request);
+          resultHandler.handleResult(Responses
+              .newResult(ResultCode.SUCCESS));
+        }
+      }
+      finally
+      {
+        entryLock.writeLock().unlock();
+      }
     }
 
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleBind(final RequestContext requestContext,
+        final int version, final BindRequest request,
+        final ResultHandler<? super BindResult> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      if (request.getAuthenticationType() != ((byte) 0x80))
+      {
+        // TODO: SASL authentication not implemented.
+        resultHandler.handleErrorResult(newErrorResult(
+            ResultCode.PROTOCOL_ERROR,
+            "non-SIMPLE authentication not supported: "
+                + request.getAuthenticationType()));
+      }
+      else
+      {
+        // TODO: always succeed.
+        resultHandler.handleResult(Responses
+            .newBindResult(ResultCode.SUCCESS));
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleCompare(final RequestContext requestContext,
+        final CompareRequest request,
+        final ResultHandler<? super CompareResult> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      // TODO:
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleDelete(final RequestContext requestContext,
+        final DeleteRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      // TODO: controls.
+      entryLock.writeLock().lock();
+      try
+      {
+        // TODO: check for children.
+        DN dn = request.getName();
+        if (!entries.containsKey(dn))
+        {
+          resultHandler.handleErrorResult(ErrorResultException
+              .newErrorResult(ResultCode.NO_SUCH_OBJECT, "The entry "
+                  + dn.toString() + " does not exist"));
+        }
+        else
+        {
+          entries.remove(dn);
+          resultHandler.handleResult(Responses
+              .newResult(ResultCode.SUCCESS));
+        }
+      }
+      finally
+      {
+        entryLock.writeLock().unlock();
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <R extends ExtendedResult> void handleExtendedRequest(
+        final RequestContext requestContext,
+        final ExtendedRequest<R> request,
+        final ResultHandler<? super R> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      // TODO: not implemented.
+      resultHandler.handleErrorResult(newErrorResult(
+          ResultCode.PROTOCOL_ERROR,
+          "Extended request operation not supported"));
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleModify(final RequestContext requestContext,
+        final ModifyRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      // TODO: controls.
+      // TODO: read lock is not really enough since concurrent updates may
+      // still occur to the same entry.
+      entryLock.readLock().lock();
+      try
+      {
+        DN dn = request.getName();
+        Entry entry = entries.get(dn);
+        if (entry == null)
+        {
+          resultHandler.handleErrorResult(ErrorResultException
+              .newErrorResult(ResultCode.NO_SUCH_OBJECT, "The entry "
+                  + dn.toString() + " does not exist"));
+        }
+
+        Entry newEntry = new LinkedHashMapEntry(entry);
+        for (Modification mod : request.getModifications())
+        {
+          ModificationType modType = mod.getModificationType();
+          if (modType.equals(ModificationType.ADD))
+          {
+            // TODO: Reject empty attribute and duplicate values.
+            newEntry.addAttribute(mod.getAttribute(), null);
+          }
+          else if (modType.equals(ModificationType.DELETE))
+          {
+            // TODO: Reject missing values.
+            newEntry.removeAttribute(mod.getAttribute(), null);
+          }
+          else if (modType.equals(ModificationType.REPLACE))
+          {
+            newEntry.replaceAttribute(mod.getAttribute());
+          }
+          else
+          {
+            resultHandler
+                .handleErrorResult(newErrorResult(
+                    ResultCode.PROTOCOL_ERROR,
+                    "Modify request contains an unsupported modification type"));
+            return;
+          }
+        }
+
+        entries.put(dn, newEntry);
+        resultHandler.handleResult(Responses
+            .newResult(ResultCode.SUCCESS));
+      }
+      finally
+      {
+        entryLock.readLock().unlock();
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleModifyDN(final RequestContext requestContext,
+        final ModifyDNRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      // TODO: not implemented.
+      resultHandler.handleErrorResult(newErrorResult(
+          ResultCode.PROTOCOL_ERROR,
+          "ModifyDN request operation not supported"));
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleSearch(final RequestContext requestContext,
+        final SearchRequest request,
+        final SearchResultHandler resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      // TODO: controls, limits, etc.
+      entryLock.readLock().lock();
+      try
+      {
+        DN dn = request.getName();
+        Entry baseEntry = entries.get(dn);
+        if (baseEntry == null)
+        {
+          resultHandler.handleErrorResult(ErrorResultException
+              .newErrorResult(ResultCode.NO_SUCH_OBJECT, "The entry "
+                  + dn.toString() + " does not exist"));
+        }
+
+        SearchScope scope = request.getScope();
+        Filter filter = request.getFilter();
+        Matcher matcher = filter.matcher();
+
+        if (scope.equals(SearchScope.BASE_OBJECT))
+        {
+          if (matcher.matches(baseEntry).toBoolean())
+          {
+            sendEntry(request, resultHandler, baseEntry);
+          }
+        }
+        else if (scope.equals(SearchScope.SINGLE_LEVEL))
+        {
+          sendEntry(request, resultHandler, baseEntry);
+
+          NavigableMap<DN, Entry> subtree = entries
+              .tailMap(dn, false);
+          for (Entry entry : subtree.values())
+          {
+            // Check for cancellation.
+            requestContext.checkIfCancelled(false);
+
+            DN childDN = entry.getName();
+            if (childDN.isChildOf(dn))
+            {
+              if (!matcher.matches(entry).toBoolean())
+              {
+                continue;
+              }
+
+              if (!sendEntry(request, resultHandler, entry))
+              {
+                // Caller has asked to stop sending results.
+                break;
+              }
+            }
+            else if (!childDN.isSubordinateOrEqualTo(dn))
+            {
+              // The remaining entries will be out of scope.
+              break;
+            }
+          }
+        }
+        else if (scope.equals(SearchScope.WHOLE_SUBTREE))
+        {
+          NavigableMap<DN, Entry> subtree = entries.tailMap(dn);
+          for (Entry entry : subtree.values())
+          {
+            // Check for cancellation.
+            requestContext.checkIfCancelled(false);
+
+            DN childDN = entry.getName();
+            if (childDN.isSubordinateOrEqualTo(dn))
+            {
+              if (!matcher.matches(entry).toBoolean())
+              {
+                continue;
+              }
+
+              if (!sendEntry(request, resultHandler, entry))
+              {
+                // Caller has asked to stop sending results.
+                break;
+              }
+            }
+            else
+            {
+              // The remaining entries will be out of scope.
+              break;
+            }
+          }
+        }
+        else
+        {
+          resultHandler.handleErrorResult(newErrorResult(
+              ResultCode.PROTOCOL_ERROR,
+              "Search request contains an unsupported search scope"));
+          return;
+        }
+
+        resultHandler.handleResult(Responses
+            .newResult(ResultCode.SUCCESS));
+      }
+      catch (CancelledResultException e)
+      {
+        resultHandler.handleErrorResult(e);
+      }
+      finally
+      {
+        entryLock.readLock().unlock();
+      }
+    }
+
+
+
+    private boolean sendEntry(SearchRequest request,
+        SearchResultHandler resultHandler, Entry entry)
+    {
+      // TODO: check filter, strip attributes.
+      return resultHandler.handleEntry(Responses
+          .newSearchResultEntry(entry));
+    }
   }
 
 
@@ -538,22 +454,70 @@
     // Parse command line arguments.
     final String localAddress = args[0];
     final int localPort = Integer.parseInt(args[1]);
+    final String ldifFileName = args[2];
 
+    // Create the memory backend.
+    final ConcurrentSkipListMap<DN, Entry> entries = readEntriesFromLDIF(ldifFileName);
+    final MemoryBackend backend = new MemoryBackend(entries);
+
+    // Create a server connection adapter.
+    final ServerConnectionFactory<LDAPClientContext, Integer> connectionHandler =
+      Connections.newServerConnectionFactory(backend);
+
+    // Create listener.
+    final LDAPListenerOptions options = new LDAPListenerOptions()
+        .setBacklog(4096);
+    LDAPListener listener = null;
+    try
+    {
+      listener = new LDAPListener(localAddress, localPort,
+          connectionHandler, options);
+      System.out.println("Press any key to stop the server...");
+      System.in.read();
+    }
+    catch (final IOException e)
+    {
+      System.out.println("Error listening on " + localAddress + ":"
+          + localPort);
+      e.printStackTrace();
+    }
+    finally
+    {
+      if (listener != null)
+      {
+        listener.close();
+      }
+    }
+  }
+
+
+
+  /**
+   * Reads the entries from the named LDIF file.
+   *
+   * @param ldifFileName
+   *          The name of the LDIF file.
+   * @return The entries.
+   */
+  private static ConcurrentSkipListMap<DN, Entry> readEntriesFromLDIF(
+      final String ldifFileName)
+  {
+    final ConcurrentSkipListMap<DN, Entry> entries;
     // Read the LDIF.
     InputStream ldif;
     try
     {
-      ldif = new FileInputStream(args[2]);
+      ldif = new FileInputStream(ldifFileName);
     }
     catch (final FileNotFoundException e)
     {
       System.err.println(e.getMessage());
       System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
-      return;
+      return null; // Satisfy compiler.
     }
 
+    entries = new ConcurrentSkipListMap<DN, Entry>();
     final LDIFEntryReader reader = new LDIFEntryReader(ldif);
-    ConcurrentSkipListMap<DN, Entry> entries = new ConcurrentSkipListMap<DN, Entry>();
     try
     {
       while (reader.hasNext())
@@ -566,7 +530,7 @@
     {
       System.err.println(e.getMessage());
       System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
-      return;
+      return null; // Satisfy compiler.
     }
     finally
     {
@@ -600,31 +564,7 @@
     {
       System.exit(1);
     }
-
-    // Create listener.
-    final LDAPListenerOptions options = new LDAPListenerOptions()
-        .setBacklog(4096);
-    LDAPListener listener = null;
-    try
-    {
-      listener = new LDAPListener(localAddress, localPort, new Store(
-          entries), options);
-      System.out.println("Press any key to stop the server...");
-      System.in.read();
-    }
-    catch (final IOException e)
-    {
-      System.out.println("Error listening on " + localAddress + ":"
-          + localPort);
-      e.printStackTrace();
-    }
-    finally
-    {
-      if (listener != null)
-      {
-        listener.close();
-      }
-    }
+    return entries;
   }
 
 
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/AbstractLDAPFutureResultImpl.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/AbstractLDAPFutureResultImpl.java
index 2a58062..9ba5157 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/AbstractLDAPFutureResultImpl.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/AbstractLDAPFutureResultImpl.java
@@ -156,7 +156,7 @@
   {
     if (result.getResultCode().isExceptional())
     {
-      handleErrorResult(ErrorResultException.wrap(result));
+      handleErrorResult(ErrorResultException.newErrorResult(result));
     }
     else
     {
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnection.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnection.java
index d9e55bc..e0ccf0d 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnection.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnection.java
@@ -29,6 +29,8 @@
 
 
 
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
+
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.util.List;
@@ -122,7 +124,7 @@
       if (connectionInvalidReason != null)
       {
         return new CompletedFutureResult<Void>(
-            ErrorResultException.wrap(connectionInvalidReason), messageID);
+            newErrorResult(connectionInvalidReason), messageID);
       }
       if (bindOrStartTLSInProgress.get())
       {
@@ -130,7 +132,7 @@
             ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
             "Bind or Start TLS operation in progress");
         return new CompletedFutureResult<Void>(
-            ErrorResultException.wrap(errorResult), messageID);
+            newErrorResult(errorResult), messageID);
       }
 
       // First remove the future associated with the request to be abandoned.
@@ -171,7 +173,7 @@
           ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
       connectionErrorOccurred(errorResult);
       return new CompletedFutureResult<Void>(
-          ErrorResultException.wrap(errorResult), messageID);
+          newErrorResult(errorResult), messageID);
     }
   }
 
@@ -273,7 +275,7 @@
           .newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
           .setDiagnosticMessage(
               "An error occurred while creating a bind context").setCause(e);
-      final ErrorResultException error = ErrorResultException.wrap(errorResult);
+      final ErrorResultException error = ErrorResultException.newErrorResult(errorResult);
       if (resultHandler != null)
       {
         resultHandler.handleErrorResult(error);
@@ -783,7 +785,7 @@
     {
       if (connectionInvalidReason != null)
       {
-        throw ErrorResultException.wrap(connectionInvalidReason);
+        throw newErrorResult(connectionInvalidReason);
       }
       pendingRequests.put(newMsgID, request);
     }
@@ -920,7 +922,7 @@
       for (final ConnectionEventListener listener : listeners)
       {
         listener.handleConnectionError(isDisconnectNotification,
-            ErrorResultException.wrap(reason));
+            newErrorResult(reason));
       }
     }
   }
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnectionFactoryImpl.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnectionFactoryImpl.java
index 77b59b6..b3a566f 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnectionFactoryImpl.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnectionFactoryImpl.java
@@ -30,6 +30,8 @@
 
 
 
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
+
 import java.io.IOException;
 import java.net.SocketAddress;
 import java.util.concurrent.ExecutionException;
@@ -40,7 +42,6 @@
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
 import org.forgerock.opendj.ldap.responses.ExtendedResult;
-import org.forgerock.opendj.ldap.responses.Responses;
 import org.forgerock.opendj.ldap.responses.Result;
 import org.glassfish.grizzly.CompletionHandler;
 import org.glassfish.grizzly.Connection;
@@ -156,22 +157,18 @@
                     @Override
                     public void failed(final Throwable throwable)
                     {
-                      final Result errorResult = Responses
-                          .newResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR)
-                          .setCause(throwable)
-                          .setDiagnosticMessage(throwable.getMessage());
-                      handler.handleErrorResult(ErrorResultException
-                          .wrap(errorResult));
+                      handler.handleErrorResult(newErrorResult(
+                          ResultCode.CLIENT_SIDE_CONNECT_ERROR,
+                          throwable.getMessage(), throwable));
                     }
                   });
               return null;
             }
             catch (final IOException ioe)
             {
-              final Result errorResult = Responses
-                  .newResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR)
-                  .setCause(ioe).setDiagnosticMessage(ioe.getMessage());
-              throw ErrorResultException.wrap(errorResult);
+              throw newErrorResult(
+                  ResultCode.CLIENT_SIDE_CONNECT_ERROR,
+                  ioe.getMessage(), ioe);
             }
           }
           handler.handleResult(null);
@@ -345,9 +342,7 @@
       t = t.getCause();
     }
 
-    final Result result = Responses
-        .newResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR).setCause(t)
-        .setDiagnosticMessage(t.getMessage());
-    return ErrorResultException.wrap(result);
+    return newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
+        t.getMessage(), t);
   }
 }
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/AsynchronousFutureResult.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/AsynchronousFutureResult.java
index b29413e..c1fd084 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/AsynchronousFutureResult.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/util/AsynchronousFutureResult.java
@@ -29,13 +29,13 @@
 
 
 
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
+
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.locks.AbstractQueuedSynchronizer;
 
 import org.forgerock.opendj.ldap.*;
-import org.forgerock.opendj.ldap.responses.Responses;
-import org.forgerock.opendj.ldap.responses.Result;
 
 
 
@@ -134,9 +134,7 @@
       ErrorResultException errorResult = handleCancelRequest(mayInterruptIfRunning);
       if (errorResult == null)
       {
-        final Result result = Responses
-            .newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED);
-        errorResult = ErrorResultException.wrap(result);
+        errorResult = newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED);
       }
       this.errorResult = errorResult;
 
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnection.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnection.java
index 12639af..61fad4d 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnection.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnection.java
@@ -32,6 +32,7 @@
 import static org.forgerock.opendj.ldap.CoreMessages.ERR_NO_SEARCH_RESULT_ENTRIES;
 import static org.forgerock.opendj.ldap.CoreMessages.ERR_UNEXPECTED_SEARCH_RESULT_ENTRIES;
 import static org.forgerock.opendj.ldap.CoreMessages.ERR_UNEXPECTED_SEARCH_RESULT_REFERENCES;
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
 
 import java.util.Collection;
 import java.util.concurrent.TimeUnit;
@@ -176,29 +177,26 @@
       if (entryCount == 0)
       {
         // Did not find any entries.
-        final Result result = Responses.newResult(
-            ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED).setDiagnosticMessage(
+        throw newErrorResult(
+            ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED,
             ERR_NO_SEARCH_RESULT_ENTRIES.get().toString());
-        throw ErrorResultException.wrap(result);
       }
       else if (entryCount > 1)
       {
         // Got more entries than expected.
-        final Result result = Responses
-            .newResult(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED)
-            .setDiagnosticMessage(
-                ERR_UNEXPECTED_SEARCH_RESULT_ENTRIES.get(entryCount).toString());
-        throw ErrorResultException.wrap(result);
+        throw newErrorResult(
+            ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED,
+            ERR_UNEXPECTED_SEARCH_RESULT_ENTRIES.get(entryCount)
+                .toString());
       }
       else if (firstReference != null)
       {
         // Got an unexpected search result reference.
-        final Result result = Responses.newResult(
-            ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED)
-            .setDiagnosticMessage(
-                ERR_UNEXPECTED_SEARCH_RESULT_REFERENCES.get(
-                    firstReference.getURIs().iterator().next()).toString());
-        throw ErrorResultException.wrap(result);
+        throw newErrorResult(
+            ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED,
+            ERR_UNEXPECTED_SEARCH_RESULT_REFERENCES.get(
+                firstReference.getURIs().iterator().next())
+                .toString());
       }
       else
       {
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractConnection.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractConnection.java
index 70957e4..46288cb 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractConnection.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractConnection.java
@@ -32,6 +32,7 @@
 import static org.forgerock.opendj.ldap.CoreMessages.ERR_NO_SEARCH_RESULT_ENTRIES;
 import static org.forgerock.opendj.ldap.CoreMessages.ERR_UNEXPECTED_SEARCH_RESULT_ENTRIES;
 import static org.forgerock.opendj.ldap.CoreMessages.ERR_UNEXPECTED_SEARCH_RESULT_REFERENCES;
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
 
 import java.util.Collection;
 import java.util.concurrent.BlockingQueue;
@@ -433,31 +434,26 @@
     if (handler.entryCount == 0)
     {
       // Did not find any entries.
-      final Result result = Responses.newResult(
-          ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED).setDiagnosticMessage(
+      throw newErrorResult(
+          ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED,
           ERR_NO_SEARCH_RESULT_ENTRIES.get().toString());
-      throw ErrorResultException.wrap(result);
     }
     else if (handler.entryCount > 1)
     {
       // Got more entries than expected.
-      final Result result = Responses.newResult(
-          ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED)
-          .setDiagnosticMessage(
-              ERR_UNEXPECTED_SEARCH_RESULT_ENTRIES.get(handler.entryCount)
-                  .toString());
-      throw ErrorResultException.wrap(result);
+      throw newErrorResult(
+          ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED,
+          ERR_UNEXPECTED_SEARCH_RESULT_ENTRIES
+              .get(handler.entryCount).toString());
     }
     else if (handler.firstReference != null)
     {
       // Got an unexpected search result reference.
-      final Result result = Responses.newResult(
-          ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED)
-          .setDiagnosticMessage(
-              ERR_UNEXPECTED_SEARCH_RESULT_REFERENCES.get(
-                  handler.firstReference.getURIs().iterator().next())
-                  .toString());
-      throw ErrorResultException.wrap(result);
+      throw newErrorResult(
+          ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED,
+          ERR_UNEXPECTED_SEARCH_RESULT_REFERENCES.get(
+              handler.firstReference.getURIs().iterator().next())
+              .toString());
     }
     else
     {
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java
index ff9d547..920f712 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java
@@ -29,6 +29,8 @@
 
 
 
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -38,8 +40,6 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 
-import org.forgerock.opendj.ldap.responses.Responses;
-
 import com.forgerock.opendj.util.AsynchronousFutureResult;
 import com.forgerock.opendj.util.StaticUtils;
 import com.forgerock.opendj.util.Validator;
@@ -439,8 +439,7 @@
     // All factories are offline so give up. We could have a
     // configurable policy here such as waiting indefinitely, or for a
     // configurable timeout period.
-    throw ErrorResultException.wrap(Responses.newResult(
-        ResultCode.CLIENT_SIDE_CONNECT_ERROR).setDiagnosticMessage(
-        "No operational connection factories available"));
+    throw newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
+        "No operational connection factories available");
   }
 }
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/CancelRequestListener.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/CancelRequestListener.java
new file mode 100644
index 0000000..ae3e154
--- /dev/null
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/CancelRequestListener.java
@@ -0,0 +1,64 @@
+/*
+ * 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/opendj3/legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2011 ForgeRock AS
+ */
+
+package org.forgerock.opendj.ldap;
+
+
+
+import java.util.EventListener;
+
+import org.forgerock.i18n.LocalizableMessage;
+
+
+
+/**
+ * An object that registers to be notified when a cancellation request has been
+ * received and processing of the request should be aborted if possible.
+ * <p>
+ * Requests may be cancelled as a result of an abandon request or a cancel
+ * extended request sent from the client, or by the server itself (e.g. during
+ * server shutdown).
+ */
+public interface CancelRequestListener extends EventListener
+{
+  /**
+   * Invoked when a cancellation request has been received and processing of the
+   * request should be aborted if possible.
+   * <p>
+   * Requests may be cancelled as a result of an abandon request or a cancel
+   * extended request sent from the client, or by the server itself (e.g. during
+   * server shutdown).
+   * <p>
+   * Implementations should, if possible, abort further processing of the
+   * request and return an appropriate cancellation result.
+   *
+   * @param cancellationReason
+   *          A message describing the reason why the request is being
+   *          cancelled.
+   */
+  void handleCancelRequest(LocalizableMessage cancellationReason);
+}
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Connections.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Connections.java
index 7fe0ca8..7b1c24c 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Connections.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Connections.java
@@ -334,6 +334,83 @@
 
 
 
+  /**
+   * Creates a new server connection factory using the provided
+   * {@link RequestHandler}. The returned factory will manage connection and
+   * request life-cycle, including request cancellation.
+   * <p>
+   * When processing requests, {@link RequestHandler} implementations are passed
+   * a {@link RequestContext} as the first parameter which may be used for
+   * detecting whether or not the request should be aborted due to cancellation
+   * requests or other events, such as connection failure.
+   * <p>
+   * The returned factory maintains state information which includes a table of
+   * active requests. Therefore, {@code RequestHandler} implementations are
+   * required to always return results in order to avoid potential memory leaks.
+   *
+   * @param <C>
+   *          The type of client context.
+   * @param requestHandler
+   *          The request handler which will be used for all client connections.
+   * @return The new server connection factory.
+   * @throws NullPointerException
+   *           If {@code requestHandler} was {@code null}.
+   */
+  public static <C> ServerConnectionFactory<C, Integer> newServerConnectionFactory(
+      final RequestHandler<RequestContext> requestHandler)
+      throws NullPointerException
+  {
+    Validator.ensureNotNull(requestHandler);
+
+    final RequestHandlerFactory<C, RequestContext> factory =
+      new RequestHandlerFactory<C, RequestContext>()
+    {
+
+      public RequestHandler<RequestContext> handleAccept(
+          C clientContext) throws ErrorResultException
+      {
+        return requestHandler;
+      }
+    };
+
+    return new RequestHandlerFactoryAdapter<C>(factory);
+  }
+
+
+
+  /**
+   * Creates a new server connection factory using the provided
+   * {@link RequestHandlerFactory}. The returned factory will manage connection
+   * and request life-cycle, including request cancellation.
+   * <p>
+   * When processing requests, {@link RequestHandler} implementations are passed
+   * a {@link RequestContext} as the first parameter which may be used for
+   * detecting whether or not the request should be aborted due to cancellation
+   * requests or other events, such as connection failure.
+   * <p>
+   * The returned factory maintains state information which includes a table of
+   * active requests. Therefore, {@code RequestHandler} implementations are
+   * required to always return results in order to avoid potential memory leaks.
+   *
+   * @param <C>
+   *          The type of client context.
+   * @param factory
+   *          The request handler factory to use for associating request
+   *          handlers with client connections.
+   * @return The new server connection factory.
+   * @throws NullPointerException
+   *           If {@code factory} was {@code null}.
+   */
+  public static <C> ServerConnectionFactory<C, Integer> newServerConnectionFactory(
+      final RequestHandlerFactory<C, RequestContext> factory)
+      throws NullPointerException
+  {
+    Validator.ensureNotNull(factory);
+    return new RequestHandlerFactoryAdapter<C>(factory);
+  }
+
+
+
   // Prevent instantiation.
   private Connections()
   {
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ErrorResultException.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ErrorResultException.java
index 2b81c13..8e40b92 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ErrorResultException.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ErrorResultException.java
@@ -45,6 +45,27 @@
 {
 
   /**
+   * Creates a new error result exception with the provided result code and an
+   * empty diagnostic message.
+   *
+   * @param resultCode
+   *          The result code.
+   * @return The new error result exception.
+   * @throws IllegalArgumentException
+   *           If the provided result code does not represent a failure.
+   * @throws NullPointerException
+   *           If {@code resultCode} was {@code null}.
+   */
+  public static ErrorResultException newErrorResult(
+      ResultCode resultCode) throws IllegalArgumentException,
+      NullPointerException
+  {
+    return newErrorResult(resultCode, null, null);
+  }
+
+
+
+  /**
    * Creates a new error result exception with the provided result code and
    * diagnostic message.
    *
@@ -59,9 +80,9 @@
    * @throws NullPointerException
    *           If {@code resultCode} was {@code null}.
    */
-  public static ErrorResultException newErrorResult(ResultCode resultCode,
-      String diagnosticMessage) throws IllegalArgumentException,
-      NullPointerException
+  public static ErrorResultException newErrorResult(
+      ResultCode resultCode, String diagnosticMessage)
+      throws IllegalArgumentException, NullPointerException
   {
     return newErrorResult(resultCode, diagnosticMessage, null);
   }
@@ -69,6 +90,30 @@
 
 
   /**
+   * Creates a new error result exception with the provided result code and
+   * cause. The diagnostic message will be taken from the cause, if provided.
+   *
+   * @param resultCode
+   *          The result code.
+   * @param cause
+   *          The throwable cause, which may be {@code null} indicating that
+   *          none was provided.
+   * @return The new error result exception.
+   * @throws IllegalArgumentException
+   *           If the provided result code does not represent a failure.
+   * @throws NullPointerException
+   *           If {@code resultCode} was {@code null}.
+   */
+  public static ErrorResultException newErrorResult(
+      ResultCode resultCode, Throwable cause)
+      throws IllegalArgumentException, NullPointerException
+  {
+    return newErrorResult(resultCode, null, cause);
+  }
+
+
+
+  /**
    * Creates a new error result exception with the provided result code,
    * diagnostic message, and cause.
    *
@@ -78,28 +123,35 @@
    *          The diagnostic message, which may be empty or {@code null}
    *          indicating that none was provided.
    * @param cause
-   *          The throwable cause, which may be null indicating that none was
-   *          provided.
+   *          The throwable cause, which may be {@code null} indicating that
+   *          none was provided.
    * @return The new error result exception.
    * @throws IllegalArgumentException
    *           If the provided result code does not represent a failure.
    * @throws NullPointerException
    *           If {@code resultCode} was {@code null}.
    */
-  public static ErrorResultException newErrorResult(ResultCode resultCode,
-      String diagnosticMessage, Throwable cause)
+  public static ErrorResultException newErrorResult(
+      ResultCode resultCode, String diagnosticMessage, Throwable cause)
       throws IllegalArgumentException, NullPointerException
   {
-    Result result = Responses.newResult(resultCode)
-        .setDiagnosticMessage(diagnosticMessage).setCause(cause);
-    return wrap(result);
+    final Result result = Responses.newResult(resultCode);
+    if (diagnosticMessage != null)
+    {
+      result.setDiagnosticMessage(diagnosticMessage);
+    }
+    else if (cause != null)
+    {
+      result.setDiagnosticMessage(cause.getLocalizedMessage());
+    }
+    result.setCause(cause);
+    return newErrorResult(result);
   }
 
 
 
   /**
-   * Wraps the provided result in an appropriate error result exception. The
-   * type of error result exception used depends on the underlying result code.
+   * Creates a new error result exception using the provided result.
    *
    * @param result
    *          The result whose result code indicates a failure.
@@ -109,8 +161,9 @@
    * @throws NullPointerException
    *           If {@code result} was {@code null}.
    */
-  public static ErrorResultException wrap(final Result result)
-      throws IllegalArgumentException, NullPointerException
+  public static ErrorResultException newErrorResult(
+      final Result result) throws IllegalArgumentException,
+      NullPointerException
   {
     if (!result.getResultCode().isExceptional())
     {
@@ -199,7 +252,8 @@
    */
   ErrorResultException(final Result result)
   {
-    super(result.getResultCode() + ": " + result.getDiagnosticMessage());
+    super(result.getResultCode() + ": "
+        + result.getDiagnosticMessage());
     this.result = result;
   }
 
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestContext.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestContext.java
new file mode 100644
index 0000000..8bf6db9
--- /dev/null
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestContext.java
@@ -0,0 +1,118 @@
+/*
+ * 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/opendj3/legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2011 ForgeRock AS
+ */
+
+package org.forgerock.opendj.ldap;
+
+
+
+/**
+ * The context associated with a request currently being processed by a request
+ * handler. A request context can be used to query state information about the
+ * request, such as whether or not it has been cancelled, as well as registering
+ * to be notified if the request has been cancelled. Implementations may provide
+ * additional information, such as the associated schema, time-stamp
+ * information, etc.
+ */
+public interface RequestContext
+{
+
+  /**
+   * Registers the provided cancellation listener with this request context so
+   * that it can be notified if a cancellation request is received and
+   * processing of the request should be aborted if possible.
+   * <p>
+   * Requests may be cancelled as a result of an abandon request or a cancel
+   * extended request sent from the client, or by the server itself (e.g. during
+   * server shutdown).
+   * <p>
+   * This method provides a event notification mechanism which can be used by
+   * asynchronous request handler implementations to detect cancellation of
+   * requests.
+   *
+   * @param listener
+   *          The listener which wants to be notified if a cancellation request
+   *          is received and processing of the request should be aborted if
+   *          possible.
+   * @throws NullPointerException
+   *           If the {@code listener} was {@code null}.
+   * @see #checkIfCancelled
+   */
+  void addCancelRequestListener(CancelRequestListener listener)
+      throws NullPointerException;
+
+
+
+  /**
+   * Throws {@link CancelledResultException} if a cancellation request has been
+   * received and processing of the request should be aborted if possible.
+   * <p>
+   * Requests may be cancelled as a result of an abandon request or a cancel
+   * extended request sent from the client, or by the server itself (e.g. during
+   * server shutdown).
+   * <p>
+   * This method provides a polling mechanism which can be used by synchronous
+   * request handler implementations to detect cancellation of requests.
+   *
+   * @param signalTooLate
+   *          {@code true} to signal that, after this method returns, processing
+   *          of the request will have proceeded too far for it to be aborted by
+   *          subsequent cancellation requests.
+   * @throws CancelledResultException
+   *           If a cancellation request has been received and processing of the
+   *           request should be aborted if possible.
+   * @see #addCancelRequestListener
+   */
+  void checkIfCancelled(boolean signalTooLate)
+      throws CancelledResultException;
+
+
+
+  /**
+   * Returns the message ID of the request, if available. Protocols, such as
+   * LDAP and internal connections, include a unique message ID with each
+   * request which may be useful for logging and auditing.
+   *
+   * @return The message ID of the request, or {@code -1} if there is no message
+   *         ID associated with the request.
+   */
+  int getMessageID();
+
+
+
+  /**
+   * Removes the provided cancellation listener from this request context so
+   * that it will not be notified if a cancellation request has been received.
+   *
+   * @param listener
+   *          The listener which no longer wants to be notified if a
+   *          cancellation request has been received.
+   * @throws NullPointerException
+   *           If the {@code listener} was {@code null}.
+   */
+  void removeCancelRequestListener(CancelRequestListener listener)
+      throws NullPointerException;
+}
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactory.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactory.java
new file mode 100644
index 0000000..fe745fe
--- /dev/null
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactory.java
@@ -0,0 +1,61 @@
+/*
+ * 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/opendj3/legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2011 ForgeRock AS
+ */
+
+package org.forgerock.opendj.ldap;
+
+
+
+/**
+ * A handler interface for accepting new connections from clients.
+ *
+ * @param <C>
+ *          The type of client context.
+ * @param <R>
+ *          The type of request context.
+ */
+public interface RequestHandlerFactory<C, R extends RequestContext>
+{
+  /**
+   * Invoked when a new client connection is accepted by the associated
+   * listener. Implementations should return a {@code RequestHandler} which will
+   * be used to handle requests from the client connection.
+   *
+   * @param clientContext
+   *          The protocol dependent context information associated with the
+   *          client connection. Depending on the protocol this may contain
+   *          information about the client such as their address and level
+   *          connection security. It may also be used to manage the state of
+   *          the client's connection.
+   * @return A {@code RequestHandler} which will be used to handle requests from
+   *         a client connection.
+   * @throws ErrorResultException
+   *           If this request handler factory cannot accept the client
+   *           connection.
+   */
+  RequestHandler<R> handleAccept(C clientContext)
+      throws ErrorResultException;
+}
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactoryAdapter.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactoryAdapter.java
new file mode 100644
index 0000000..4f5476d
--- /dev/null
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactoryAdapter.java
@@ -0,0 +1,983 @@
+/*
+ * 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/opendj3/legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2011 ForgeRock AS
+ */
+
+package org.forgerock.opendj.ldap;
+
+
+
+import static org.forgerock.opendj.ldap.CoreMessages.*;
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.opendj.ldap.requests.*;
+import org.forgerock.opendj.ldap.responses.*;
+
+import com.forgerock.opendj.util.Validator;
+
+
+
+/**
+ * An adapter which converts a {@code RequestHandlerFactory} into a
+ * {@code ServerConnectionFactory}.
+ *
+ * @param <C>
+ *          The type of client context.
+ */
+final class RequestHandlerFactoryAdapter<C> implements
+    ServerConnectionFactory<C, Integer>
+{
+  /**
+   * Request context implementation.
+   */
+  private static class RequestContextImpl<S extends Result, H extends ResultHandler<? super S>>
+      implements RequestContext, ResultHandler<S>
+  {
+
+    // Adapter class which invokes cancel result handlers with correct result
+    // type.
+    private static final class ExtendedResultHandlerHolder<R extends ExtendedResult>
+    {
+      private final ExtendedRequest<R> request;
+      private final ResultHandler<? super R> resultHandler;
+
+
+
+      private ExtendedResultHandlerHolder(
+          final ExtendedRequest<R> request,
+          final ResultHandler<? super R> resultHandler)
+      {
+        this.request = request;
+        this.resultHandler = resultHandler;
+      }
+
+
+
+      private void handleSuccess()
+      {
+        final R cancelResult = request.getResultDecoder()
+            .newExtendedErrorResult(ResultCode.SUCCESS, "", "");
+        resultHandler.handleResult(cancelResult);
+      }
+
+
+
+      private void handleTooLate()
+      {
+        final R cancelResult = request.getResultDecoder()
+            .newExtendedErrorResult(ResultCode.TOO_LATE, "", "");
+        resultHandler.handleErrorResult(ErrorResultException
+            .newErrorResult(cancelResult));
+      }
+    }
+
+
+
+    private static enum RequestState
+    {
+      // Request active
+      PENDING,
+
+      // Request active, cancel requested
+      CANCEL_REQUESTED,
+
+      // Request active, too late to cancel
+      TOO_LATE,
+
+      // Result sent, not cancelled
+      RESULT_SENT,
+
+      // Result sent, was cancelled
+      CANCELLED;
+    }
+
+
+
+    private final int messageID;
+
+    // Cancellation state guarded by lock.
+    private final Object stateLock = new Object();
+
+    // These should be notified when a cancel request arrives, at most once.
+    private List<CancelRequestListener> cancelRequestListeners = null;
+
+    // These should be notified when the result is set.
+    private List<ExtendedResultHandlerHolder<?>> cancelResultHandlers = null;
+
+    private RequestState state = RequestState.PENDING;
+
+    private LocalizableMessage cancelRequestReason = null;
+
+    private boolean sendResult = true;
+
+    private final boolean isCancelSupported;
+
+    private final ServerConnectionImpl<?> clientConnection;
+
+    protected final H resultHandler;
+
+
+
+    protected RequestContextImpl(
+        final ServerConnectionImpl<?> clientConnection,
+        final H resultHandler, final int messageID,
+        final boolean isCancelSupported)
+    {
+      this.clientConnection = clientConnection;
+      this.resultHandler = resultHandler;
+      this.messageID = messageID;
+      this.isCancelSupported = isCancelSupported;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void addCancelRequestListener(
+        final CancelRequestListener listener)
+        throws NullPointerException
+    {
+      Validator.ensureNotNull(listener);
+
+      boolean invokeImmediately = false;
+      synchronized (stateLock)
+      {
+        switch (state)
+        {
+        case PENDING:
+          if (cancelRequestListeners == null)
+          {
+            cancelRequestListeners = new LinkedList<CancelRequestListener>();
+          }
+          cancelRequestListeners.add(listener);
+          break;
+        case CANCEL_REQUESTED:
+          // Signal immediately outside lock.
+          invokeImmediately = true;
+          break;
+        case TOO_LATE:
+        case RESULT_SENT:
+        case CANCELLED:
+          // No point in registering the callback since the request can never be
+          // cancelled now.
+          break;
+        }
+      }
+
+      if (invokeImmediately)
+      {
+        listener.handleCancelRequest(cancelRequestReason);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void checkIfCancelled(final boolean signalTooLate)
+        throws CancelledResultException
+    {
+      synchronized (stateLock)
+      {
+        switch (state)
+        {
+        case PENDING:
+          // No cancel request, so no handlers, just switch state.
+          if (signalTooLate)
+          {
+            cancelRequestListeners = null;
+            state = RequestState.TOO_LATE;
+          }
+          break;
+        case CANCEL_REQUESTED:
+          // Don't change state: let the handler ack the cancellation request.
+          throw (CancelledResultException) newErrorResult(
+              ResultCode.CANCELLED, cancelRequestReason.toString());
+        case TOO_LATE:
+          // Already too late. Nothing to do.
+          break;
+        case RESULT_SENT:
+        case CANCELLED:
+          // This should not happen - could throw an illegal state exception?
+          break;
+        }
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getMessageID()
+    {
+      return messageID;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleErrorResult(final ErrorResultException error)
+    {
+      if (clientConnection.removePendingRequest(this))
+      {
+        if (setResult(error.getResult()))
+        {
+          // FIXME: we must invoke the result handler even when abandoned so
+          // that chained result handlers may clean up, log, etc. We really need
+          // to signal that the result must not be sent to the client.
+        }
+        resultHandler.handleErrorResult(error);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleResult(final S result)
+    {
+      if (clientConnection.removePendingRequest(this))
+      {
+        if (setResult(result))
+        {
+          // FIXME: we must invoke the result handler even when abandoned so
+          // that chained result handlers may clean up, log, etc. We really need
+          // to signal that the result must not be sent to the client.
+        }
+        resultHandler.handleResult(result);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void removeCancelRequestListener(
+        final CancelRequestListener listener)
+        throws NullPointerException
+    {
+      Validator.ensureNotNull(listener);
+
+      synchronized (stateLock)
+      {
+        if (cancelRequestListeners != null)
+        {
+          cancelRequestListeners.remove(listener);
+        }
+      }
+    }
+
+
+
+    private <R extends ExtendedResult> void cancel(
+        final LocalizableMessage reason,
+        final ExtendedRequest<R> cancelRequest,
+        final ResultHandler<? super R> cancelResultHandler,
+        final boolean sendResult)
+    {
+      Validator.ensureNotNull(reason);
+
+      if (!isCancelSupported)
+      {
+        if (cancelResultHandler != null)
+        {
+          final Result result = Responses
+              .newGenericExtendedResult(ResultCode.CANNOT_CANCEL);
+          cancelResultHandler
+              .handleErrorResult(newErrorResult(result));
+        }
+        return;
+      }
+
+      List<CancelRequestListener> tmpListeners = null;
+      boolean invokeResultHandler = false;
+      boolean resultHandlerIsSuccess = false;
+
+      synchronized (stateLock)
+      {
+        switch (state)
+        {
+        case PENDING:
+          // Switch to CANCEL_REQUESTED state.
+          cancelRequestReason = reason;
+          if (cancelResultHandler != null)
+          {
+            cancelResultHandlers = new LinkedList<ExtendedResultHandlerHolder<?>>();
+            cancelResultHandlers
+                .add(new ExtendedResultHandlerHolder<R>(
+                    cancelRequest, cancelResultHandler));
+          }
+          tmpListeners = cancelRequestListeners;
+          cancelRequestListeners = null;
+          state = RequestState.CANCEL_REQUESTED;
+          this.sendResult &= sendResult;
+          break;
+        case CANCEL_REQUESTED:
+          // Cancel already request so listeners already invoked.
+          if (cancelResultHandler != null)
+          {
+            if (cancelResultHandlers == null)
+            {
+              cancelResultHandlers = new LinkedList<ExtendedResultHandlerHolder<?>>();
+            }
+            cancelResultHandlers
+                .add(new ExtendedResultHandlerHolder<R>(
+                    cancelRequest, cancelResultHandler));
+          }
+          break;
+        case TOO_LATE:
+        case RESULT_SENT:
+          // Cannot cancel, so invoke result handler immediately outside of
+          // lock.
+          if (cancelResultHandler != null)
+          {
+            invokeResultHandler = true;
+            resultHandlerIsSuccess = false;
+          }
+          break;
+        case CANCELLED:
+          // Multiple cancellation attempts. Clients should not do this, but the
+          // cancel will effectively succeed immediately, so invoke result
+          // handler immediately outside of lock.
+          if (cancelResultHandler != null)
+          {
+            invokeResultHandler = true;
+            resultHandlerIsSuccess = true;
+          }
+          break;
+        }
+      }
+
+      // Invoke listeners outside of lock.
+      if (tmpListeners != null)
+      {
+        for (final CancelRequestListener listener : tmpListeners)
+        {
+          listener.handleCancelRequest(reason);
+        }
+      }
+
+      if (invokeResultHandler)
+      {
+        if (resultHandlerIsSuccess)
+        {
+          final R result = cancelRequest.getResultDecoder()
+              .newExtendedErrorResult(ResultCode.SUCCESS, "", "");
+          cancelResultHandler.handleResult(result);
+        }
+        else
+        {
+          final Result result = Responses
+              .newGenericExtendedResult(ResultCode.TOO_LATE);
+          cancelResultHandler.handleErrorResult(ErrorResultException
+              .newErrorResult(result));
+        }
+      }
+    }
+
+
+
+    /**
+     * Sets the result associated with this request context and updates the
+     * state accordingly.
+     *
+     * @param result
+     *          The result.
+     */
+    private boolean setResult(final Result result)
+    {
+      List<ExtendedResultHandlerHolder<?>> tmpHandlers = null;
+      boolean isCancelled = false;
+      boolean maySendResult;
+
+      synchronized (stateLock)
+      {
+        maySendResult = sendResult;
+
+        switch (state)
+        {
+        case PENDING:
+        case TOO_LATE:
+          // Switch to appropriate final state.
+          if (!result.getResultCode().equals(ResultCode.CANCELLED))
+          {
+            state = RequestState.RESULT_SENT;
+          }
+          else
+          {
+            state = RequestState.CANCELLED;
+          }
+          break;
+        case CANCEL_REQUESTED:
+          // Switch to appropriate final state and invoke any cancel request
+          // handlers.
+          if (!result.getResultCode().equals(ResultCode.CANCELLED))
+          {
+            state = RequestState.RESULT_SENT;
+          }
+          else
+          {
+            state = RequestState.CANCELLED;
+          }
+
+          isCancelled = (state == RequestState.CANCELLED);
+          tmpHandlers = cancelResultHandlers;
+          cancelResultHandlers = null;
+          break;
+        case RESULT_SENT:
+        case CANCELLED:
+          // This should not happen - could throw an illegal state exception?
+          maySendResult = false; // Prevent sending multiple results.
+          break;
+        }
+      }
+
+      // Invoke handlers outside of lock.
+      if (tmpHandlers != null)
+      {
+        for (final ExtendedResultHandlerHolder<?> handler : tmpHandlers)
+        {
+          if (isCancelled)
+          {
+            handler.handleSuccess();
+          }
+          else
+          {
+            handler.handleTooLate();
+          }
+        }
+      }
+
+      return maySendResult;
+    }
+  }
+
+
+
+  /**
+   * Search request context implementation.
+   */
+  private final static class SearchRequestContextImpl extends
+      RequestContextImpl<Result, SearchResultHandler> implements
+      SearchResultHandler
+  {
+
+    private SearchRequestContextImpl(
+        final ServerConnectionImpl<?> clientConnection,
+        final SearchResultHandler resultHandler, final int messageID,
+        final boolean isCancelSupported)
+    {
+      super(clientConnection, resultHandler, messageID,
+          isCancelSupported);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean handleEntry(final SearchResultEntry entry)
+    {
+      return resultHandler.handleEntry(entry);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean handleReference(
+        final SearchResultReference reference)
+    {
+      return resultHandler.handleReference(reference);
+    }
+  }
+
+
+
+  private static final class ServerConnectionImpl<C> implements
+      ServerConnection<Integer>
+  {
+    private final RequestHandler<RequestContext> requestHandler;
+
+    private final AtomicBoolean isClosed = new AtomicBoolean();
+
+    private final ConcurrentHashMap<Integer, RequestContextImpl<?, ?>> pendingRequests =
+      new ConcurrentHashMap<Integer, RequestContextImpl<?, ?>>();
+
+
+
+    private ServerConnectionImpl(
+        final RequestHandler<RequestContext> requestHandler)
+    {
+      this.requestHandler = requestHandler;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleAbandon(final Integer messageID,
+        final AbandonRequest request)
+        throws UnsupportedOperationException
+    {
+      final RequestContextImpl<?, ?> abandonedRequest = getPendingRequest(request
+          .getRequestID());
+      if (abandonedRequest != null)
+      {
+        final LocalizableMessage abandonReason = INFO_CANCELED_BY_ABANDON_REQUEST
+            .get(messageID);
+        abandonedRequest.cancel(abandonReason, null, null, false);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleAdd(final Integer messageID,
+        final AddRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      final RequestContextImpl<Result, ResultHandler<? super Result>> requestContext =
+        new RequestContextImpl<Result, ResultHandler<? super Result>>(
+          this, resultHandler, messageID, true);
+      if (addPendingRequest(requestContext))
+      {
+        requestHandler.handleAdd(requestContext, request,
+            requestContext, intermediateResponseHandler);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleBind(final Integer messageID,
+        final int version, final BindRequest request,
+        final ResultHandler<? super BindResult> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      final RequestContextImpl<BindResult, ResultHandler<? super BindResult>> requestContext =
+        new RequestContextImpl<BindResult, ResultHandler<? super BindResult>>(
+          this, resultHandler, messageID, false);
+      if (addPendingRequest(requestContext))
+      {
+        requestHandler.handleBind(requestContext, version, request,
+            requestContext, intermediateResponseHandler);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleCompare(final Integer messageID,
+        final CompareRequest request,
+        final ResultHandler<? super CompareResult> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      final RequestContextImpl<CompareResult, ResultHandler<? super CompareResult>> requestContext =
+        new RequestContextImpl<CompareResult, ResultHandler<? super CompareResult>>(
+          this, resultHandler, messageID, true);
+      if (addPendingRequest(requestContext))
+      {
+        requestHandler.handleCompare(requestContext, request,
+            requestContext, intermediateResponseHandler);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleConnectionClosed(final Integer messageID,
+        final UnbindRequest request)
+    {
+      final LocalizableMessage cancelReason = INFO_CANCELED_BY_CLIENT_DISCONNECT
+          .get();
+      doClose(cancelReason);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleConnectionDisconnected(
+        final ResultCode resultCode, final String message)
+    {
+      final LocalizableMessage cancelReason = INFO_CANCELED_BY_SERVER_DISCONNECT
+          .get();
+      doClose(cancelReason);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleConnectionError(final Throwable error)
+    {
+      final LocalizableMessage cancelReason = INFO_CANCELED_BY_CLIENT_ERROR
+          .get();
+      doClose(cancelReason);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleDelete(final Integer messageID,
+        final DeleteRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      final RequestContextImpl<Result, ResultHandler<? super Result>> requestContext =
+        new RequestContextImpl<Result, ResultHandler<? super Result>>(
+          this, resultHandler, messageID, true);
+      if (addPendingRequest(requestContext))
+      {
+        requestHandler.handleDelete(requestContext, request,
+            requestContext, intermediateResponseHandler);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <R extends ExtendedResult> void handleExtendedRequest(
+        final Integer messageID, final ExtendedRequest<R> request,
+        final ResultHandler<? super R> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      if (request.getOID().equals(CancelExtendedRequest.OID))
+      {
+        // Decode the request as a cancel request.
+        CancelExtendedRequest cancelRequest;
+        try
+        {
+          cancelRequest = CancelExtendedRequest.DECODER
+              .decodeExtendedRequest(request, new DecodeOptions());
+        }
+        catch (final DecodeException e)
+        {
+          // Couldn't decode a cancel request.
+          resultHandler.handleErrorResult(newErrorResult(
+              ResultCode.PROTOCOL_ERROR, e.getLocalizedMessage()));
+          return;
+        }
+
+        // Register the request in the pending requests table. Even though
+        // this request cannot be cancelled, it is important to do this in
+        // order to monitor the number of pending operations.
+        final RequestContextImpl<R, ResultHandler<? super R>> requestContext =
+          new RequestContextImpl<R, ResultHandler<? super R>>(
+            this, resultHandler, messageID, false);
+        if (addPendingRequest(requestContext))
+        {
+          // Find and cancel the request.
+          final RequestContextImpl<?, ?> cancelledRequest = getPendingRequest(cancelRequest
+              .getRequestID());
+          if (cancelledRequest != null)
+          {
+            final LocalizableMessage cancelReason = INFO_CANCELED_BY_CANCEL_REQUEST
+                .get(messageID);
+            cancelledRequest.cancel(cancelReason, request,
+                requestContext, true);
+          }
+          else
+          {
+            // Couldn't find the request. Invoke on context in order to remove
+            // pending request.
+            requestContext
+                .handleErrorResult(newErrorResult(ResultCode.NO_SUCH_OPERATION));
+          }
+        }
+      }
+      else
+      {
+        final RequestContextImpl<R, ResultHandler<? super R>> requestContext;
+        if (request.getOID().equals(StartTLSExtendedRequest.OID))
+        {
+          // StartTLS requests cannot be cancelled.
+          requestContext = new RequestContextImpl<R, ResultHandler<? super R>>(
+              this, resultHandler, messageID, false);
+        }
+        else
+        {
+          requestContext = new RequestContextImpl<R, ResultHandler<? super R>>(
+              this, resultHandler, messageID, true);
+        }
+
+        if (addPendingRequest(requestContext))
+        {
+          requestHandler.handleExtendedRequest(requestContext,
+              request, requestContext, intermediateResponseHandler);
+        }
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleModify(final Integer messageID,
+        final ModifyRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      final RequestContextImpl<Result, ResultHandler<? super Result>> requestContext =
+        new RequestContextImpl<Result, ResultHandler<? super Result>>(
+          this, resultHandler, messageID, true);
+      if (addPendingRequest(requestContext))
+      {
+        requestHandler.handleModify(requestContext, request,
+            requestContext, intermediateResponseHandler);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleModifyDN(final Integer messageID,
+        final ModifyDNRequest request,
+        final ResultHandler<? super Result> resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      final RequestContextImpl<Result, ResultHandler<? super Result>> requestContext =
+        new RequestContextImpl<Result, ResultHandler<? super Result>>(
+          this, resultHandler, messageID, true);
+      if (addPendingRequest(requestContext))
+      {
+        requestHandler.handleModifyDN(requestContext, request,
+            requestContext, intermediateResponseHandler);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handleSearch(final Integer messageID,
+        final SearchRequest request,
+        final SearchResultHandler resultHandler,
+        final IntermediateResponseHandler intermediateResponseHandler)
+        throws UnsupportedOperationException
+    {
+      final SearchRequestContextImpl requestContext = new SearchRequestContextImpl(
+          this, resultHandler, messageID, true);
+      if (addPendingRequest(requestContext))
+      {
+        requestHandler.handleSearch(requestContext, request,
+            requestContext, intermediateResponseHandler);
+      }
+    }
+
+
+
+    private boolean addPendingRequest(
+        final RequestContextImpl<?, ?> requestContext)
+    {
+      final Integer messageID = requestContext.getMessageID();
+
+      if (isClosed.get())
+      {
+        final LocalizableMessage message = INFO_CLIENT_CONNECTION_CLOSING
+            .get();
+        requestContext.handleErrorResult(newErrorResult(
+            ResultCode.UNWILLING_TO_PERFORM, message.toString()));
+        return false;
+      }
+      else if (pendingRequests.putIfAbsent(messageID, requestContext) != null)
+      {
+        final LocalizableMessage message = WARN_CLIENT_DUPLICATE_MESSAGE_ID
+            .get(requestContext.getMessageID());
+        requestContext.handleErrorResult(newErrorResult(
+            ResultCode.PROTOCOL_ERROR, message.toString()));
+        return false;
+      }
+      else if (isClosed.get())
+      {
+        // A concurrent close may have already removed the pending request but
+        // it will have only been notified for cancellation.
+        pendingRequests.remove(messageID);
+
+        final LocalizableMessage message = INFO_CLIENT_CONNECTION_CLOSING
+            .get();
+        requestContext.handleErrorResult(newErrorResult(
+            ResultCode.UNWILLING_TO_PERFORM, message.toString()));
+        return false;
+      }
+      else
+      {
+        // If the connection is closed now then we just have to pay the cost of
+        // invoking the request in the request handler.
+        return true;
+      }
+    }
+
+
+
+    private void doClose(final LocalizableMessage cancelReason)
+    {
+      if (!isClosed.getAndSet(true))
+      {
+        // At this point if any pending requests are added then we may end up
+        // cancelling them, but this does not matter since addPendingRequest
+        // will fail the request immediately.
+        final Iterator<RequestContextImpl<?, ?>> iterator = pendingRequests
+            .values().iterator();
+        while (iterator.hasNext())
+        {
+          final RequestContextImpl<?, ?> pendingRequest = iterator
+              .next();
+          pendingRequest.cancel(cancelReason, null, null, false);
+          iterator.remove();
+        }
+      }
+    }
+
+
+
+    /**
+     * Returns the pending request context having the specified message ID.
+     *
+     * @param messageID
+     *          The message ID associated with the request context.
+     * @return The pending request context.
+     */
+    private RequestContextImpl<?, ?> getPendingRequest(
+        final Integer messageID)
+    {
+      return pendingRequests.get(messageID);
+    }
+
+
+
+    /**
+     * Deregister a request context once it has completed.
+     *
+     * @param requestContext
+     *          The request context.
+     * @return {@code true} if the request context was found and removed.
+     */
+    private boolean removePendingRequest(
+        final RequestContextImpl<?, ?> requestContext)
+    {
+      return pendingRequests.remove(requestContext.getMessageID()) != null;
+    }
+
+  }
+
+
+
+  private final RequestHandlerFactory<C, RequestContext> factory;
+
+
+
+  /**
+   * Creates a new server connection factory using the provided request handler
+   * factory.
+   *
+   * @param factory
+   *          The request handler factory to be adapted into a server connection
+   *          factory.
+   */
+  RequestHandlerFactoryAdapter(
+      final RequestHandlerFactory<C, RequestContext> factory)
+  {
+    this.factory = factory;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public ServerConnection<Integer> handleAccept(final C clientContext)
+      throws ErrorResultException
+  {
+    final RequestHandler<RequestContext> requestHandler = factory
+        .handleAccept(clientContext);
+    return new ServerConnectionImpl<C>(requestHandler);
+  }
+
+}
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java
index d40afe2..6fcadb0 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java
@@ -29,6 +29,8 @@
 
 
 
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
+
 import javax.security.auth.callback.NameCallback;
 import javax.security.auth.callback.PasswordCallback;
 import javax.security.auth.callback.UnsupportedCallbackException;
@@ -85,8 +87,7 @@
       }
       catch (final SaslException e)
       {
-        throw ErrorResultException.wrap(Responses.newResult(
-            ResultCode.CLIENT_SIDE_LOCAL_ERROR).setCause(e));
+        throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
       }
     }
 
@@ -122,7 +123,7 @@
       {
         // FIXME: I18N need to have a better error message.
         // FIXME: Is this the best result code?
-        throw ErrorResultException.wrap(Responses.newResult(
+        throw ErrorResultException.newErrorResult(Responses.newResult(
             ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
             "An error occurred during multi-stage authentication")
             .setCause(e));
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java
index ccc4ed5..3dae736 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java
@@ -32,6 +32,7 @@
 import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage;
 import static com.forgerock.opendj.util.StaticUtils.joinCollection;
 import static org.forgerock.opendj.ldap.CoreMessages.ERR_SASL_PROTOCOL_ERROR;
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
 
 import java.util.*;
 
@@ -49,7 +50,6 @@
 import org.forgerock.opendj.ldap.ErrorResultException;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.responses.BindResult;
-import org.forgerock.opendj.ldap.responses.Responses;
 
 import com.forgerock.opendj.util.Validator;
 
@@ -154,8 +154,7 @@
       }
       catch (final SaslException e)
       {
-        throw ErrorResultException.wrap(Responses.newResult(
-            ResultCode.CLIENT_SIDE_LOCAL_ERROR).setCause(e));
+        throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
       }
     }
 
@@ -191,11 +190,8 @@
       {
         // 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));
+        throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR,
+            "An error occurred during multi-stage authentication", e);
       }
     }
 
@@ -229,9 +225,8 @@
       {
         final LocalizableMessage msg = ERR_SASL_PROTOCOL_ERROR.get(
             SASL_MECHANISM_NAME, getExceptionMessage(e));
-        throw ErrorResultException.wrap(Responses
-            .newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR)
-            .setDiagnosticMessage(msg.toString()).setCause(e));
+        throw newErrorResult(ResultCode.CLIENT_SIDE_DECODING_ERROR,
+            msg.toString(), e);
       }
     }
 
@@ -249,9 +244,8 @@
       {
         final LocalizableMessage msg = ERR_SASL_PROTOCOL_ERROR.get(
             SASL_MECHANISM_NAME, getExceptionMessage(e));
-        throw ErrorResultException.wrap(Responses
-            .newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR)
-            .setDiagnosticMessage(msg.toString()).setCause(e));
+        throw newErrorResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR,
+            msg.toString(), e);
       }
     }
 
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java
index b815c75..654f715 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java
@@ -29,6 +29,8 @@
 
 
 
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
+
 import javax.security.sasl.Sasl;
 import javax.security.sasl.SaslClient;
 import javax.security.sasl.SaslException;
@@ -76,8 +78,7 @@
       }
       catch (final SaslException e)
       {
-        throw ErrorResultException.wrap(Responses.newResult(
-            ResultCode.CLIENT_SIDE_LOCAL_ERROR).setCause(e));
+        throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
       }
     }
 
@@ -113,7 +114,7 @@
       {
         // FIXME: I18N need to have a better error message.
         // FIXME: Is this the best result code?
-        throw ErrorResultException.wrap(Responses.newResult(
+        throw ErrorResultException.newErrorResult(Responses.newResult(
             ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
             "An error occurred during multi-stage authentication")
             .setCause(e));
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java
index c3cb540..86f741d 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java
@@ -32,6 +32,7 @@
 import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage;
 import static com.forgerock.opendj.util.StaticUtils.joinCollection;
 import static org.forgerock.opendj.ldap.CoreMessages.*;
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
 
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
@@ -61,6 +62,7 @@
 /**
  * GSSAPI SASL bind request implementation.
  */
+@SuppressWarnings("restriction")
 final class GSSAPISASLBindRequestImpl extends
     AbstractSASLBindRequest<GSSAPISASLBindRequest> implements
     GSSAPISASLBindRequest
@@ -75,7 +77,7 @@
       {
         // FIXME: I18N need to have a better error message.
         // FIXME: Is this the best result code?
-        throw ErrorResultException.wrap(Responses.newResult(
+        throw ErrorResultException.newErrorResult(Responses.newResult(
             ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
             "No authentication ID specified for GSSAPI SASL authentication"));
       }
@@ -84,7 +86,7 @@
       {
         // FIXME: I18N need to have a better error message.
         // FIXME: Is this the best result code?
-        throw ErrorResultException.wrap(Responses.newResult(
+        throw ErrorResultException.newErrorResult(Responses.newResult(
             ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
             "No password specified for GSSAPI SASL authentication"));
       }
@@ -119,7 +121,7 @@
         // FIXME: Is this the best result code?
         final LocalizableMessage message = ERR_LDAPAUTH_GSSAPI_LOCAL_AUTHENTICATION_FAILED
             .get(StaticUtils.getExceptionMessage(e));
-        throw ErrorResultException.wrap(Responses
+        throw ErrorResultException.newErrorResult(Responses
             .newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
             .setDiagnosticMessage(message.toString()).setCause(e));
       }
@@ -151,7 +153,7 @@
         {
           // FIXME: I18N need to have a better error message.
           // FIXME: Is this the best result code?
-          throw ErrorResultException.wrap(Responses
+          throw ErrorResultException.newErrorResult(Responses
               .newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
               .setDiagnosticMessage(
                   "An error occurred during multi-stage authentication")
@@ -239,8 +241,7 @@
                 }
                 catch (final SaslException e)
                 {
-                  throw ErrorResultException.wrap(Responses.newResult(
-                      ResultCode.CLIENT_SIDE_LOCAL_ERROR).setCause(e));
+                  throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
                 }
               }
             });
@@ -254,11 +255,10 @@
         else
         {
           // This should not happen. Must be a bug.
-          final LocalizableMessage msg = ERR_SASL_CONTEXT_CREATE_ERROR.get(
-              SASL_MECHANISM_NAME, getExceptionMessage(e));
-          throw ErrorResultException.wrap(Responses
-              .newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
-              .setDiagnosticMessage(msg.toString()).setCause(e));
+          final LocalizableMessage msg = ERR_SASL_CONTEXT_CREATE_ERROR
+              .get(SASL_MECHANISM_NAME, getExceptionMessage(e));
+          throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR,
+              msg.toString(), e);
         }
       }
     }
@@ -300,9 +300,8 @@
           // This should not happen. Must be a bug.
           final LocalizableMessage msg = ERR_SASL_PROTOCOL_ERROR.get(
               SASL_MECHANISM_NAME, getExceptionMessage(e));
-          throw ErrorResultException.wrap(Responses
-              .newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
-              .setDiagnosticMessage(msg.toString()).setCause(e));
+          throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR,
+              msg.toString(), e);
         }
       }
     }
@@ -337,9 +336,8 @@
       {
         final LocalizableMessage msg = ERR_SASL_PROTOCOL_ERROR.get(
             SASL_MECHANISM_NAME, getExceptionMessage(e));
-        throw ErrorResultException.wrap(Responses
-            .newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR)
-            .setDiagnosticMessage(msg.toString()).setCause(e));
+        throw newErrorResult(ResultCode.CLIENT_SIDE_DECODING_ERROR,
+            msg.toString(), e);
       }
     }
 
@@ -357,9 +355,8 @@
       {
         final LocalizableMessage msg = ERR_SASL_PROTOCOL_ERROR.get(
             SASL_MECHANISM_NAME, getExceptionMessage(e));
-        throw ErrorResultException.wrap(Responses
-            .newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR)
-            .setDiagnosticMessage(msg.toString()).setCause(e));
+        throw newErrorResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR,
+            msg.toString(), e);
       }
     }
 
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java
index 9c3c64d..3732b8a 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java
@@ -29,6 +29,8 @@
 
 
 
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
+
 import javax.security.auth.callback.NameCallback;
 import javax.security.auth.callback.PasswordCallback;
 import javax.security.auth.callback.UnsupportedCallbackException;
@@ -40,7 +42,6 @@
 import org.forgerock.opendj.ldap.ErrorResultException;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.responses.BindResult;
-import org.forgerock.opendj.ldap.responses.Responses;
 
 import com.forgerock.opendj.util.Validator;
 
@@ -87,8 +88,7 @@
       }
       catch (final SaslException e)
       {
-        throw ErrorResultException.wrap(Responses.newResult(
-            ResultCode.CLIENT_SIDE_LOCAL_ERROR).setCause(e));
+        throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
       }
     }
 
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/AbstractExtendedResultDecoder.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/AbstractExtendedResultDecoder.java
index 3dc865d..07b19b6 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/AbstractExtendedResultDecoder.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/responses/AbstractExtendedResultDecoder.java
@@ -29,6 +29,8 @@
 
 
 
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
+
 import org.forgerock.opendj.ldap.*;
 import org.forgerock.opendj.ldap.requests.ExtendedRequest;
 
@@ -89,8 +91,7 @@
             .newExtendedErrorResult(result.getResultCode(),
                 result.getMatchedDN(), result.getDiagnosticMessage());
         adaptedResult.setCause(result.getCause());
-        resultHandler.handleErrorResult(ErrorResultException
-            .wrap(adaptedResult));
+        resultHandler.handleErrorResult(newErrorResult(adaptedResult));
       }
 
 
@@ -108,8 +109,7 @@
         {
           final R adaptedResult = request.getResultDecoder()
               .adaptDecodeException(e);
-          resultHandler.handleErrorResult(ErrorResultException
-              .wrap(adaptedResult));
+          resultHandler.handleErrorResult(newErrorResult(adaptedResult));
         }
       }
 
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java
index 501e926..6a2d2b9 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java
@@ -29,6 +29,7 @@
 
 
 import static org.forgerock.opendj.ldap.CoreMessages.*;
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -40,8 +41,6 @@
 import org.forgerock.opendj.ldap.*;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
-import org.forgerock.opendj.ldap.responses.Responses;
-import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 
 import com.forgerock.opendj.util.FutureResultTransformer;
@@ -1829,10 +1828,10 @@
     if (subentryAttr == null || subentryAttr.isEmpty())
     {
       // Did not get the subschema sub-entry attribute.
-      final Result result = Responses.newResult(
-          ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED).setDiagnosticMessage(
-          ERR_NO_SUBSCHEMA_SUBENTRY_ATTR.get(name.toString()).toString());
-      throw ErrorResultException.wrap(result);
+      throw newErrorResult(
+          ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED,
+          ERR_NO_SUBSCHEMA_SUBENTRY_ATTR.get(name.toString())
+              .toString());
     }
 
     final String dnString = subentryAttr.iterator().next().toString();
@@ -1843,11 +1842,10 @@
     }
     catch (final LocalizedIllegalArgumentException e)
     {
-      final Result result = Responses.newResult(
-          ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED).setDiagnosticMessage(
-          ERR_INVALID_SUBSCHEMA_SUBENTRY_ATTR.get(name.toString(), dnString,
-              e.getMessageObject()).toString());
-      throw ErrorResultException.wrap(result);
+      throw newErrorResult(
+          ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED,
+          ERR_INVALID_SUBSCHEMA_SUBENTRY_ATTR.get(name.toString(),
+              dnString, e.getMessageObject()).toString());
     }
     return subschemaDN;
   }
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/ConnectionEntryReader.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/ConnectionEntryReader.java
index 34c1f94..b1029ab 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/ConnectionEntryReader.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/ConnectionEntryReader.java
@@ -29,6 +29,8 @@
 
 
 
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
+
 import java.io.InterruptedIOException;
 import java.util.NoSuchElementException;
 import java.util.concurrent.BlockingQueue;
@@ -273,8 +275,7 @@
       return false;
     }
 
-    final ErrorResultException e = ErrorResultException.wrap(result);
-    throw new ErrorResultIOException(e);
+    throw new ErrorResultIOException(newErrorResult(result));
   }
 
 
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core.properties b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core.properties
index 4556fe9..d8a6561 100755
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core.properties
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core.properties
@@ -1269,6 +1269,22 @@
  contained the OID '%s', when '%s' was expected
 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 operation was rejected because there is \
+ already 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 has disconnected from the server
+INFO_CANCELED_BY_SERVER_DISCONNECT=The operation was canceled because the \
+ server has disconnected from the client
+INFO_CANCELED_BY_CLIENT_ERROR=The operation was canceled because the \
+ client connection failed
+INFO_CLIENT_CONNECTION_CLOSING=The operation was rejected because the \
+ client connection is closing
+
 
 
 
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/LDAPListenerTestCase.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/LDAPListenerTestCase.java
index 11ea51c..a98ce81 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/LDAPListenerTestCase.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/LDAPListenerTestCase.java
@@ -223,7 +223,7 @@
         throws UnsupportedOperationException
     {
       resultHandler
-          .handleErrorResult(ErrorResultException.wrap(request
+          .handleErrorResult(ErrorResultException.newErrorResult(request
               .getResultDecoder().newExtendedErrorResult(
                   ResultCode.PROTOCOL_ERROR, "",
                   "Extended operation " + request.getOID() + " not supported")));
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
index 46def3d..322f95d 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
@@ -200,7 +200,7 @@
       {
         // duplicate entry.
         result = Responses.newResult(ResultCode.ENTRY_ALREADY_EXISTS);
-        final ErrorResultException ere = ErrorResultException.wrap(result);
+        final ErrorResultException ere = ErrorResultException.newErrorResult(result);
         handler.handleErrorResult(ere);
         // doesn't matter if it was canceled.
         requestsInProgress.remove(context);
@@ -222,7 +222,7 @@
       if (abReq.isCanceled())
       {
         result = Responses.newResult(ResultCode.CANCELLED);
-        final ErrorResultException ere = ErrorResultException.wrap(result);
+        final ErrorResultException ere = ErrorResultException.newErrorResult(result);
         handler.handleErrorResult(ere);
         requestsInProgress.remove(context);
         return;
@@ -349,7 +349,7 @@
                   }
                   catch (SaslException e)
                   {
-                    throw ErrorResultException.wrap(Responses.newResult(
+                    throw ErrorResultException.newErrorResult(Responses.newResult(
                         ResultCode.OPERATIONS_ERROR).setCause(e));
                   }
                 }
@@ -365,7 +365,7 @@
                   }
                   catch (SaslException e)
                   {
-                    throw ErrorResultException.wrap(Responses.newResult(
+                    throw ErrorResultException.newErrorResult(Responses.newResult(
                         ResultCode.OPERATIONS_ERROR).setCause(e));
                   }
                 }
@@ -384,7 +384,7 @@
         }
         catch (Exception e)
         {
-          resultHandler.handleErrorResult(ErrorResultException.wrap(Responses
+          resultHandler.handleErrorResult(ErrorResultException.newErrorResult(Responses
               .newBindResult(ResultCode.OPERATIONS_ERROR).setCause(e)
               .setDiagnosticMessage(e.toString())));
         }
@@ -469,7 +469,7 @@
       {
         // entry not found.
         result = Responses.newCompareResult(ResultCode.NO_SUCH_ATTRIBUTE);
-        final ErrorResultException ere = ErrorResultException.wrap(result);
+        final ErrorResultException ere = ErrorResultException.newErrorResult(result);
         resultHandler.handleErrorResult(ere);
         // doesn't matter if it was canceled.
         requestsInProgress.remove(context);
@@ -488,7 +488,7 @@
           if (abReq.isCanceled())
           {
             final Result r = Responses.newResult(ResultCode.CANCELLED);
-            final ErrorResultException ere = ErrorResultException.wrap(r);
+            final ErrorResultException ere = ErrorResultException.newErrorResult(r);
             resultHandler.handleErrorResult(ere);
             requestsInProgress.remove(context);
             return;
@@ -529,7 +529,7 @@
       {
         // entry is not found.
         result = Responses.newResult(ResultCode.NO_SUCH_OBJECT);
-        final ErrorResultException ere = ErrorResultException.wrap(result);
+        final ErrorResultException ere = ErrorResultException.newErrorResult(result);
         handler.handleErrorResult(ere);
         // doesn't matter if it was canceled.
         requestsInProgress.remove(context);
@@ -539,7 +539,7 @@
       if (abReq.isCanceled())
       {
         result = Responses.newResult(ResultCode.CANCELLED);
-        final ErrorResultException ere = ErrorResultException.wrap(result);
+        final ErrorResultException ere = ErrorResultException.newErrorResult(result);
         handler.handleErrorResult(ere);
         requestsInProgress.remove(context);
         return;
@@ -633,7 +633,7 @@
       {
         // Entry not found.
         result = Responses.newResult(ResultCode.NO_SUCH_OBJECT);
-        final ErrorResultException ere = ErrorResultException.wrap(result);
+        final ErrorResultException ere = ErrorResultException.newErrorResult(result);
         resultHandler.handleErrorResult(ere);
         // Should searchResultHandler handle anything?
 
@@ -645,7 +645,7 @@
       if (abReq.isCanceled())
       {
         result = Responses.newResult(ResultCode.CANCELLED);
-        final ErrorResultException ere = ErrorResultException.wrap(result);
+        final ErrorResultException ere = ErrorResultException.newErrorResult(result);
         resultHandler.handleErrorResult(ere);
         requestsInProgress.remove(context);
         return;
diff --git a/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPCompare.java b/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPCompare.java
index 3cea6f2..e116bae 100644
--- a/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPCompare.java
+++ b/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPCompare.java
@@ -32,6 +32,7 @@
 import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
 import static com.forgerock.opendj.ldap.tools.ToolConstants.*;
 import static com.forgerock.opendj.ldap.tools.Utils.filterExitCode;
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
 
 import java.io.*;
 import java.util.ArrayList;
@@ -44,7 +45,6 @@
 import org.forgerock.opendj.ldap.controls.ProxiedAuthV2RequestControl;
 import org.forgerock.opendj.ldap.requests.CompareRequest;
 import org.forgerock.opendj.ldap.requests.Requests;
-import org.forgerock.opendj.ldap.responses.Responses;
 import org.forgerock.opendj.ldap.responses.Result;
 
 import com.forgerock.opendj.util.Base64;
@@ -229,9 +229,8 @@
         {
           // This shouldn't happen because there are no other threads to
           // interrupt this one.
-          result = Responses.newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED)
-              .setCause(e).setDiagnosticMessage(e.getLocalizedMessage());
-          throw ErrorResultException.wrap(result);
+          throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED,
+              e.getLocalizedMessage(), e);
         }
 
         if (result.getResultCode() == ResultCode.COMPARE_FALSE)
diff --git a/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPModify.java b/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPModify.java
index 347637c..598df04 100644
--- a/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPModify.java
+++ b/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPModify.java
@@ -32,6 +32,7 @@
 import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
 import static com.forgerock.opendj.ldap.tools.ToolConstants.*;
 import static com.forgerock.opendj.ldap.tools.Utils.filterExitCode;
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
 
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -50,7 +51,6 @@
 import org.forgerock.opendj.ldap.requests.DeleteRequest;
 import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
 import org.forgerock.opendj.ldap.requests.ModifyRequest;
-import org.forgerock.opendj.ldap.responses.Responses;
 import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldif.*;
 
@@ -87,9 +87,9 @@
           {
             // This shouldn't happen because there are no other threads
             // to interrupt this one.
-            r = Responses.newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED)
-                .setCause(e).setDiagnosticMessage(e.getLocalizedMessage());
-            throw ErrorResultException.wrap(r);
+            throw newErrorResult(
+                ResultCode.CLIENT_SIDE_USER_CANCELLED,
+                e.getLocalizedMessage(), e);
           }
           printResult(opType, change.getName().toString(), r);
           return r.getResultCode().intValue();
@@ -127,9 +127,8 @@
           {
             // This shouldn't happen because there are no other threads
             // to interrupt this one.
-            r = Responses.newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED)
-                .setCause(e).setDiagnosticMessage(e.getLocalizedMessage());
-            throw ErrorResultException.wrap(r);
+            throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED,
+                e.getLocalizedMessage(), e);
           }
           printResult(opType, change.getName().toString(), r);
           return r.getResultCode().intValue();
@@ -167,9 +166,8 @@
           {
             // This shouldn't happen because there are no other threads
             // to interrupt this one.
-            r = Responses.newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED)
-                .setCause(e).setDiagnosticMessage(e.getLocalizedMessage());
-            throw ErrorResultException.wrap(r);
+            throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED,
+                e.getLocalizedMessage(), e);
           }
           printResult(opType, change.getName().toString(), r);
           return r.getResultCode().intValue();
@@ -207,9 +205,8 @@
           {
             // This shouldn't happen because there are no other threads
             // to interrupt this one.
-            r = Responses.newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED)
-                .setCause(e).setDiagnosticMessage(e.getLocalizedMessage());
-            throw ErrorResultException.wrap(r);
+            throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED,
+                e.getLocalizedMessage(), e);
           }
           printResult(opType, change.getName().toString(), r);
           return r.getResultCode().intValue();
diff --git a/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPPasswordModify.java b/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPPasswordModify.java
index f872bec..04fc6de 100644
--- a/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPPasswordModify.java
+++ b/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPPasswordModify.java
@@ -32,6 +32,7 @@
 import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
 import static com.forgerock.opendj.ldap.tools.ToolConstants.*;
 import static com.forgerock.opendj.ldap.tools.Utils.filterExitCode;
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
 
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -42,7 +43,6 @@
 import org.forgerock.opendj.ldap.requests.PasswordModifyExtendedRequest;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.responses.PasswordModifyExtendedResult;
-import org.forgerock.opendj.ldap.responses.Responses;
 
 
 
@@ -446,10 +446,8 @@
       {
         // This shouldn't happen because there are no other threads to
         // interrupt this one.
-        result = Responses.newPasswordModifyExtendedResult(
-            ResultCode.CLIENT_SIDE_USER_CANCELLED).setCause(e)
-            .setDiagnosticMessage(e.getLocalizedMessage());
-        throw ErrorResultException.wrap(result);
+        throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED,
+            e.getLocalizedMessage(), e);
       }
     }
     catch (final ErrorResultException e)
diff --git a/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java b/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java
index cd58570..9aa4952 100644
--- a/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java
+++ b/opendj-sdk/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java
@@ -32,6 +32,7 @@
 import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
 import static com.forgerock.opendj.ldap.tools.ToolConstants.*;
 import static com.forgerock.opendj.ldap.tools.Utils.filterExitCode;
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
 
 import java.io.*;
 import java.util.*;
@@ -42,7 +43,6 @@
 import org.forgerock.opendj.ldap.controls.*;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
-import org.forgerock.opendj.ldap.responses.Responses;
 import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
@@ -1168,9 +1168,8 @@
         {
           // This shouldn't happen because there are no other threads to
           // interrupt this one.
-          result = Responses.newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED)
-              .setCause(e).setDiagnosticMessage(e.getLocalizedMessage());
-          throw ErrorResultException.wrap(result);
+          throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED,
+              e.getLocalizedMessage(), e);
         }
 
         try

--
Gitblit v1.10.0