From 45141fb11ef698b11c6fb3becca82ca10e11505a Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Mon, 15 Sep 2014 12:50:50 +0000
Subject: [PATCH] OPENDJ-1285 CR-4409 Migrate SDK from Futures to Promises

---
 opendj-core/src/test/java/org/forgerock/opendj/ldap/TestCaseUtils.java                                  |   39 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/Connection.java                                     |  282 +
 opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java                   |  175 
 opendj-core/clirr-ignored-api-changes.xml                                                               |   56 
 opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithmTestCase.java         |   36 
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java                |   34 
 opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java             |   79 
 opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactoryTestCase.java     |  187 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPFutureResultImpl.java               |   72 
 opendj-core/src/main/java/org/forgerock/opendj/ldif/ConnectionEntryReader.java                          |   17 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPConnectionFactory.java                          |    8 
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java                       |   25 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractSynchronousConnection.java                  |   82 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/ResultHandler.java                                  |    9 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/responses/AbstractExtendedResultDecoder.java        |    8 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/RootDSE.java                                        |   29 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnection.java                 |  118 
 opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java                    |  165 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java                                  |   62 
 opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java              |   34 
 opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java             |  270 +-
 opendj-core/src/test/java/org/forgerock/opendj/ldif/ConnectionEntryReaderTestCase.java                  |   60 
 opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPServerFilter.java                         |   20 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResult.java                                   |   87 
 opendj-core/src/test/java/org/forgerock/opendj/ldap/ConnectionPoolTestCase.java                         |   58 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactory.java                     |  533 +--
 opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java                                     |   55 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactoryAdapter.java                   |   50 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java                 |   72 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/ConnectionPool.java                                 |   18 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/InternalConnectionFactory.java                      |   25 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/SearchResultHandler.java                            |    5 
 opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java                              |  117 
 opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ProxyBackend.java                  |  383 +--
 opendj-core/src/main/java/org/forgerock/opendj/ldap/LoadBalancer.java                                   |   16 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResultWrapper.java                            |  281 ++
 opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java                                  |   66 
 opendj-rest2ldap-servlet/src/main/java/org/forgerock/opendj/rest2ldap/servlet/Rest2LDAPAuthnFilter.java |  192 
 opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java                    |   10 
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java                          |   29 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractConnection.java                             |  458 +--
 opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResultImpl.java                               |   64 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/InternalConnection.java                             |   75 
 opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnectionTestCase.java         |  299 +-
 opendj-server3x-adapter/src/main/java/org/forgerock/opendj/adapter/server3x/Adapters.java               |   21 
 pom.xml                                                                                                 |    2 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/Connections.java                                    |   12 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java                           |  263 -
 opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java                      |   10 
 opendj-core/src/test/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactoryTestCase.java             |  158 
 opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaTestCase.java                          |   62 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/CachedConnectionPool.java                           |  229 +
 opendj-server2x-adapter/src/main/java/org/forgerock/opendj/adapter/server2x/Adapters.java               |   23 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPFutureResultImpl.java                       |    8 
 opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/ConnectionFactoryTestCase.java                |  144 
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java                          |   20 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPBindFutureResultImpl.java                   |   11 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPCompareFutureResultImpl.java                |   12 
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java                         |   78 
 opendj-core/src/test/java/org/forgerock/opendj/ldap/spi/BasicLDAPConnectionFactory.java                 |   21 
 opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/RewriterProxy.java                 |   44 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/AuthenticatedConnectionFactory.java                 |  105 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/RequestHandler.java                                 |   13 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/ConnectionFactory.java                              |   17 
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java                       |   18 
 /dev/null                                                                                               |  132 -
 opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractConnectionWrapper.java                      |  179 +
 opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPExtendedFutureResultImpl.java               |   14 
 opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java       |  762 +++---
 opendj-cli/src/main/java/com/forgerock/opendj/cli/AuthenticatedConnectionFactory.java                   |  165 
 opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java                   |   75 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPSearchFutureResultImpl.java                 |   12 
 opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionTestCase.java            |   11 
 73 files changed, 3,441 insertions(+), 3,940 deletions(-)

diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/AuthenticatedConnectionFactory.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/AuthenticatedConnectionFactory.java
index 82c61d5..2e11886 100644
--- a/opendj-cli/src/main/java/com/forgerock/opendj/cli/AuthenticatedConnectionFactory.java
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/AuthenticatedConnectionFactory.java
@@ -26,20 +26,24 @@
  */
 package com.forgerock.opendj.cli;
 
-import org.forgerock.opendj.ldap.Connection;
+import java.util.concurrent.atomic.AtomicReference;
+
 import org.forgerock.opendj.ldap.AbstractConnectionWrapper;
+import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.ConnectionFactory;
 import org.forgerock.opendj.ldap.ErrorResultException;
 import org.forgerock.opendj.ldap.FutureResult;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
-import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.requests.BindRequest;
 import org.forgerock.opendj.ldap.responses.BindResult;
 import org.forgerock.util.Reject;
+import org.forgerock.util.promise.AsyncFunction;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.Function;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.SuccessHandler;
 
-import com.forgerock.opendj.util.FutureResultTransformer;
-import com.forgerock.opendj.util.RecursiveFutureResult;
-
+import static org.forgerock.util.Utils.*;
 /**
  * An authenticated connection factory can be used to create pre-authenticated
  * connections to a Directory Server.
@@ -83,19 +87,21 @@
          * These methods will always throw {@code UnsupportedOperationException}.
          */
         /** {@inheritDoc} */
+        @Override
         public BindResult bind(BindRequest request) throws ErrorResultException {
             throw new UnsupportedOperationException();
         }
 
         /** {@inheritDoc} */
+        @Override
         public BindResult bind(String name, char[] password) throws ErrorResultException {
             throw new UnsupportedOperationException();
         }
 
         /** {@inheritDoc} */
+        @Override
         public FutureResult<BindResult> bindAsync(BindRequest request,
-                IntermediateResponseHandler intermediateResponseHandler,
-                ResultHandler<? super BindResult> resultHandler) {
+            IntermediateResponseHandler intermediateResponseHandler) {
             throw new UnsupportedOperationException();
         }
 
@@ -115,10 +121,6 @@
          * associated with this connection. If re-authentication fails for some
          * reason then this connection will be automatically closed.
          *
-         * @param handler
-         *            A result handler which can be used to asynchronously
-         *            process the operation result when it is received, may be
-         *            {@code null}.
          * @return A future representing the result of the operation.
          * @throws UnsupportedOperationException
          *             If this connection does not support rebind operations.
@@ -126,45 +128,27 @@
          *             If this connection has already been closed, i.e. if
          *             {@code isClosed() == true}.
          */
-        public FutureResult<BindResult> rebindAsync(final ResultHandler<? super BindResult> handler) {
+        public FutureResult<BindResult> rebindAsync() {
             if (request == null) {
                 throw new UnsupportedOperationException();
             }
 
-            /*
-             * Wrap the client handler so that we can update the connection
-             * state.
-             */
-            final ResultHandler<? super BindResult> clientHandler = handler;
-
-            final ResultHandler<BindResult> handlerWrapper = new ResultHandler<BindResult>() {
-
-                /** {@inheritDoc} */
-                public void handleErrorResult(final ErrorResultException error) {
-                    /*
-                     * This connection is now unauthenticated so prevent further
-                     * use.
-                     */
-                    connection.close();
-
-                    if (clientHandler != null) {
-                        clientHandler.handleErrorResult(error);
-                    }
-                }
-
-                /** {@inheritDoc} */
-                public void handleResult(final BindResult result) {
-                    // Save the result.
-                    AuthenticatedConnection.this.result = result;
-
-                    if (clientHandler != null) {
-                        clientHandler.handleResult(result);
-                    }
-                }
-
-            };
-
-            return connection.bindAsync(request, null, handlerWrapper);
+            return (FutureResult<BindResult>) connection.bindAsync(request)
+                    .onSuccess(new SuccessHandler<BindResult>() {
+                        @Override
+                        public void handleResult(final BindResult result) {
+                            // Save the result.
+                            AuthenticatedConnection.this.result = result;
+                        }
+                    }).onFailure(new FailureHandler<ErrorResultException>() {
+                        @Override
+                        public void handleError(final ErrorResultException error) {
+                            /*
+                             * This connection is now unauthenticated so prevent further use.
+                             */
+                            connection.close();
+                        }
+                    });
         }
 
         /**
@@ -172,6 +156,7 @@
          *
          * @return The string representation of this authenticated connection factory.
          */
+        @Override
         public String toString() {
             StringBuilder builder = new StringBuilder();
             builder.append("AuthenticatedConnection(");
@@ -182,56 +167,9 @@
 
     }
 
-    private static final class FutureResultImpl {
-        private final FutureResultTransformer<BindResult, Connection> futureBindResult;
-        private final RecursiveFutureResult<Connection, BindResult> futureConnectionResult;
-        private final BindRequest bindRequest;
-        private Connection connection;
-
-        private FutureResultImpl(final BindRequest request,
-                final ResultHandler<? super Connection> handler) {
-            this.bindRequest = request;
-            this.futureBindResult = new FutureResultTransformer<BindResult, Connection>(handler) {
-
-                @Override
-                protected ErrorResultException transformErrorResult(
-                        final ErrorResultException errorResult) {
-                    // Ensure that the connection is closed.
-                    if (connection != null) {
-                        connection.close();
-                        connection = null;
-                    }
-                    return errorResult;
-                }
-
-                @Override
-                protected AuthenticatedConnection transformResult(final BindResult result)
-                        throws ErrorResultException {
-                    // FIXME: should make the result unmodifiable.
-                    return new AuthenticatedConnection(connection, bindRequest, result);
-                }
-
-            };
-            this.futureConnectionResult =
-                    new RecursiveFutureResult<Connection, BindResult>(futureBindResult) {
-
-                        @Override
-                        protected FutureResult<? extends BindResult> chainResult(
-                                final Connection innerResult,
-                                final ResultHandler<? super BindResult> handler)
-                                throws ErrorResultException {
-                            connection = innerResult;
-                            return connection.bindAsync(bindRequest, null, handler);
-                        }
-                    };
-            futureBindResult.setFutureResult(futureConnectionResult);
-        }
-
-    }
-
     private final BindRequest request;
     private final ConnectionFactory parentFactory;
-    private boolean allowRebinds = false;
+    private boolean allowRebinds;
 
     /**
      * Creates a new authenticated connection factory which will obtain
@@ -260,6 +198,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public Connection getConnection() throws ErrorResultException {
         final Connection connection = parentFactory.getConnection();
         BindResult bindResult = null;
@@ -279,12 +218,35 @@
     }
 
     /** {@inheritDoc} */
-    public FutureResult<Connection> getConnectionAsync(
-            final ResultHandler<? super Connection> handler) {
-        final FutureResultImpl future = new FutureResultImpl(request, handler);
-        future.futureConnectionResult.setFutureResult(parentFactory
-                .getConnectionAsync(future.futureConnectionResult));
-        return future.futureBindResult;
+    @Override
+    public Promise<Connection, ErrorResultException> getConnectionAsync() {
+        final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>();
+        return parentFactory.getConnectionAsync()
+            .thenAsync(
+                    new AsyncFunction<Connection, BindResult, ErrorResultException>() {
+                        @Override
+                        public Promise<BindResult, ErrorResultException> apply(final Connection connection)
+                                throws ErrorResultException {
+                            connectionHolder.set(connection);
+                            return connection.bindAsync(request);
+                        }
+                    }
+            ).then(
+                    new Function<BindResult, Connection, ErrorResultException>() {
+                        @Override
+                        public Connection apply(BindResult result) throws ErrorResultException {
+                            // FIXME: should make the result unmodifiable.
+                            return new AuthenticatedConnection(connectionHolder.get(), request, result);
+                        }
+                    },
+                    new Function<ErrorResultException, Connection, ErrorResultException>() {
+                        @Override
+                        public Connection apply(ErrorResultException errorResult) throws ErrorResultException {
+                            closeSilently(connectionHolder.get());
+                            throw errorResult;
+                        }
+                    }
+            );
     }
 
     /**
@@ -325,10 +287,11 @@
      *
      * @return The string representation of this authenticated connection factory.
      */
+    @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder();
         builder.append("AuthenticatedConnectionFactory(");
-        builder.append(String.valueOf(parentFactory));
+        builder.append(parentFactory);
         builder.append(')');
         return builder.toString();
     }
diff --git a/opendj-core/clirr-ignored-api-changes.xml b/opendj-core/clirr-ignored-api-changes.xml
index e32115d..458dcca 100644
--- a/opendj-core/clirr-ignored-api-changes.xml
+++ b/opendj-core/clirr-ignored-api-changes.xml
@@ -222,5 +222,59 @@
     <method>boolean isIndexingSupported()</method>
     <justification>OPENDJ-1308 Migrate schema support: allows decoupling indexing from a specific backend</justification>
   </difference>
-
+  <difference>
+    <className>org/forgerock/opendj/ldap/*Connection*</className>
+    <differenceType>7004</differenceType>
+    <method>org.forgerock.opendj.ldap.FutureResult *Async(*org.forgerock.opendj.ldap.ResultHandler)</method>
+    <justification>OPENDJ-1285 Migrate SDK from Futures to Promises</justification>
+  </difference>
+  <difference>
+    <className>org/forgerock/opendj/ldap/schema/Schema</className>
+    <differenceType>7004</differenceType>
+    <method>org.forgerock.opendj.ldap.FutureResult readSchema*Async*(org.forgerock.opendj.ldap.Connection, org.forgerock.opendj.ldap.DN, org.forgerock.opendj.ldap.ResultHandler)</method>
+    <justification>OPENDJ-1285 Migrate SDK from Futures to Promises</justification>
+  </difference>
+  <difference>
+    <className>org/forgerock/opendj/ldap/*Connection*</className>
+    <differenceType>7006</differenceType>
+    <method>org.forgerock.opendj.ldap.FutureResult *Async(*org.forgerock.opendj.ldap.ResultHandler)</method>
+    <to>org.forgerock.util.promise.Promise</to>
+    <justification>OPENDJ-1285 Migrate SDK from Futures to Promises</justification>
+  </difference>
+  <difference>
+    <className>org/forgerock/opendj/ldap/Connection</className>
+    <differenceType>7012</differenceType>
+    <method>org.forgerock.opendj.ldap.FutureResult *Async(org.forgerock.opendj.*)</method>
+    <justification>OPENDJ-1285 Migrate SDK from Futures to Promises</justification>
+  </difference>
+  <difference>
+    <className>%regex[org/forgerock/opendj/ldap/(RequestHandler|MemoryBackend)]</className>
+    <differenceType>7004</differenceType>
+    <method>*handleSearch(*)</method>
+    <justification>OPENDJ-1285 Migrate SDK from Futures to Promises</justification>
+  </difference>
+  <difference>
+    <className>org/forgerock/opendj/ldap/ResultHandler</className>
+    <differenceType>7012</differenceType>
+    <method>*handleError(org.forgerock.opendj.ldap.ErrorResultException)</method>
+    <justification>OPENDJ-1285 Migrate SDK from Futures to Promises</justification>
+  </difference>
+  <difference>
+    <className>org/forgerock/opendj/ldap/ResultHandler</className>
+    <differenceType>7002</differenceType>
+    <method>*handleErrorResult(org.forgerock.opendj.ldap.ErrorResultException)</method>
+    <justification>OPENDJ-1285 Migrate SDK from Futures to Promises</justification>
+  </difference>
+  <difference>
+    <className>org/forgerock/opendj/ldap/SearchResultHandler</className>
+    <differenceType>4001</differenceType>
+    <to>org/forgerock/opendj/ldap/ResultHandler</to>
+    <justification>OPENDJ-1285 Migrate SDK from Futures to Promises</justification>
+  </difference>
+  <difference>
+    <className>org/forgerock/opendj/ldap/schema/SchemaBuilder</className>
+    <differenceType>7004</differenceType>
+    <method>org.forgerock.opendj.ldap.FutureResult addSchema*Async(*)</method>
+    <justification>OPENDJ-1285 Migrate SDK from Futures to Promises</justification>
+  </difference>
 </differences>
diff --git a/opendj-core/src/main/java/com/forgerock/opendj/util/AsynchronousFutureResult.java b/opendj-core/src/main/java/com/forgerock/opendj/util/AsynchronousFutureResult.java
deleted file mode 100644
index 3e76e12..0000000
--- a/opendj-core/src/main/java/com/forgerock/opendj/util/AsynchronousFutureResult.java
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at 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 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 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2013-2014 ForgeRock AS.
- */
-
-package com.forgerock.opendj.util;
-
-import static org.forgerock.opendj.ldap.ErrorResultException.*;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.locks.AbstractQueuedSynchronizer;
-
-import org.forgerock.opendj.ldap.CancelledResultException;
-import org.forgerock.opendj.ldap.ErrorResultException;
-import org.forgerock.opendj.ldap.FutureResult;
-import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
-
-/**
- * This class provides a skeletal implementation of the {@code FutureResult}
- * interface, to minimize the effort required to implement this interface.
- * <p>
- * This {@code FutureResult} implementation provides the following features:
- * <ul>
- * <li>The {@link #get} methods throw {@link ErrorResultException}s instead of
- * the more generic {@code ExecutionException}s.
- * <li>The {@link #get} methods never throw {@code CancellationException} since
- * requests in this SDK can usually be cancelled via other external means (e.g.
- * the {@code Cancel} extended operation) for which there are well defined error
- * results. Therefore cancellation is always signalled by throwing a
- * {@link CancelledResultException} in order to be consistent with other error
- * results.
- * <li>A {@link ResultHandler} can be provided to the constructor. The result
- * handler will be invoked immediately after the result or error is received but
- * before threads blocked on {@link #get} are released. More specifically,
- * result handler invocation <i>happens-before</i> a call to {@link #get}.
- * <b>NOTE:</b> a result handler which attempts to call {@link #get} will
- * deadlock.
- * <li>Sub-classes may choose to implement specific cancellation cleanup by
- * implementing the {@link #handleCancelRequest} method.
- * </ul>
- *
- * @param <M>
- *          The type of result returned by this future.
- * @param <H>
- *          The type of {@link ResultHandler} associated to this future.
- */
-public class AsynchronousFutureResult<M, H extends ResultHandler<? super M>> implements
-    FutureResult<M>, ResultHandler<M> {
-
-    @SuppressWarnings("serial")
-    private final class Sync extends AbstractQueuedSynchronizer {
-        // State value representing the initial state before a result has
-        // been received.
-        private static final int WAITING = 0;
-
-        // State value representing that a result has been received and is
-        // being processed.
-        private static final int PENDING = 1;
-
-        // State value representing that the request was cancelled.
-        private static final int CANCELLED = 2;
-
-        // State value representing that the request has failed.
-        private static final int FAIL = 3;
-
-        // State value representing that the request has succeeded.
-        private static final int SUCCESS = 4;
-
-        // These do not need to be volatile since their values are published
-        // by updating the state after they are set and reading the state
-        // immediately before they are read.
-        private ErrorResultException errorResult = null;
-
-        private M result = null;
-
-        /**
-         * Allow all threads to acquire if future has completed.
-         */
-        @Override
-        protected int tryAcquireShared(final int ignore) {
-            return innerIsDone() ? 1 : -1;
-        }
-
-        /**
-         * Signal that the future has completed and threads waiting on get() can
-         * be released.
-         */
-        @Override
-        protected boolean tryReleaseShared(final int finalState) {
-            // Ensures that errorResult/result is published.
-            setState(finalState);
-            return true;
-        }
-
-        boolean innerCancel(final boolean mayInterruptIfRunning) {
-            if (!isCancelable() || !setStatePending()) {
-                return false;
-            }
-
-            // Perform implementation defined cancellation.
-            ErrorResultException errorResult = handleCancelRequest(mayInterruptIfRunning);
-            if (errorResult == null) {
-                errorResult = newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED);
-            }
-            this.errorResult = errorResult;
-
-            try {
-                // Invoke error result completion handler.
-                if (handler != null) {
-                    handler.handleErrorResult(errorResult);
-                }
-            } finally {
-                releaseShared(CANCELLED); // Publishes errorResult.
-            }
-
-            return true;
-        }
-
-        M innerGet() throws ErrorResultException, InterruptedException {
-            acquireSharedInterruptibly(0);
-            return get0();
-        }
-
-        M innerGet(final long nanosTimeout) throws ErrorResultException, TimeoutException,
-                InterruptedException {
-            if (!tryAcquireSharedNanos(0, nanosTimeout)) {
-                throw new TimeoutException();
-            } else {
-                return get0();
-            }
-        }
-
-        boolean innerIsCancelled() {
-            return getState() == CANCELLED;
-        }
-
-        boolean innerIsDone() {
-            return getState() > 1;
-        }
-
-        boolean innerSetErrorResult(final ErrorResultException errorResult) {
-            if (!setStatePending()) {
-                return false;
-            }
-            this.errorResult = errorResult;
-            try {
-                // Invoke error result completion handler.
-                if (handler != null) {
-                    handler.handleErrorResult(errorResult);
-                }
-            } finally {
-                releaseShared(FAIL); // Publishes errorResult.
-            }
-            return true;
-        }
-
-        boolean innerSetResult(final M result) {
-            if (!setStatePending()) {
-                return false;
-            }
-            this.result = result;
-            try {
-                // Invoke result completion handler.
-                if (handler != null) {
-                    handler.handleResult(result);
-                }
-            } finally {
-                releaseShared(SUCCESS); // Publishes result.
-            }
-            return true;
-        }
-
-        private M get0() throws ErrorResultException {
-            if (errorResult != null) {
-                // State must be FAILED or CANCELLED.
-                throw errorResult;
-            } else {
-                // State must be SUCCESS.
-                return result;
-            }
-        }
-
-        private boolean setStatePending() {
-            return compareAndSetState(WAITING, PENDING);
-        }
-    }
-
-    private final Sync sync = new Sync();
-
-    private final H handler;
-
-    private final int requestID;
-
-    /**
-     * Creates a new asynchronous future result with the provided result handler
-     * and a request ID of -1.
-     *
-     * @param handler
-     *            A result handler which will be forwarded the result or error
-     *            when it arrives, may be {@code null}.
-     */
-    public AsynchronousFutureResult(final H handler) {
-        this(handler, -1);
-    }
-
-    /**
-     * Creates a new asynchronous future result with the provided result handler
-     * and request ID.
-     *
-     * @param handler
-     *            A result handler which will be forwarded the result or error
-     *            when it arrives, may be {@code null}.
-     * @param requestID
-     *            The request ID which will be returned by the default
-     *            implementation of {@link #getRequestID}.
-     */
-    public AsynchronousFutureResult(final H handler, final int requestID) {
-        this.handler = handler;
-        this.requestID = requestID;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final boolean cancel(final boolean mayInterruptIfRunning) {
-        return sync.innerCancel(mayInterruptIfRunning);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final M get() throws ErrorResultException, InterruptedException {
-        return sync.innerGet();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final M get(final long timeout, final TimeUnit unit) throws ErrorResultException,
-            TimeoutException, InterruptedException {
-        return sync.innerGet(unit.toNanos(timeout));
-    }
-
-    /**
-     * Returns the result handler associated to this FutureResult.
-     *
-     * @return the result handler associated to this FutureResult.
-     */
-    public H getResultHandler() {
-        return handler;
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * The default implementation returns the request ID passed in during
-     * construction, or -1 if none was provided.
-     */
-    @Override
-    public int getRequestID() {
-        return requestID;
-    }
-
-    /**
-     * Sets the error result associated with this future. If ({@code isDone() ==
-     * true}) then the error result will be ignored, otherwise the result
-     * handler will be invoked if one was provided and, on return, any threads
-     * waiting on {@link #get} will be released and the provided error result
-     * will be thrown.
-     *
-     * @param errorResult
-     *            The error result.
-     */
-    @Override
-    public final void handleErrorResult(final ErrorResultException errorResult) {
-        sync.innerSetErrorResult(errorResult);
-    }
-
-    /**
-     * Sets the result associated with this future. If ({@code isDone() == true}
-     * ) then the result will be ignored, otherwise the result handler will be
-     * invoked if one was provided and, on return, any threads waiting on
-     * {@link #get} will be released and the provided result will be returned.
-     *
-     * @param result
-     *            The result.
-     */
-    @Override
-    public final void handleResult(final M result) {
-        sync.innerSetResult(result);
-    }
-
-    /**
-     * Attempts to set the error result associated with this future. If (i.e.
-     * {@code isDone() == true}) then the error result will be ignored and
-     * {@code false} will be returned, otherwise the result handler will be
-     * invoked if one was provided and, on returning {@code true}, any threads
-     * waiting on {@link #get} will be released and the provided error result
-     * will be thrown.
-     *
-     * @param errorResult
-     *            The error result.
-     * @return {@code false} if this future has already been completed, either
-     *         due to normal termination, an exception, or cancellation (i.e.
-     *         {@code isDone() == true}).
-     */
-    public final boolean tryHandleErrorResult(final ErrorResultException errorResult) {
-        return sync.innerSetErrorResult(errorResult);
-    }
-
-    /**
-     * Attempts to set the result associated with this future. If (i.e.
-     * {@code isDone() == true}) then the result will be ignored and
-     * {@code false} will be returned, otherwise the result handler will be
-     * invoked if one was provided and, on returning {@code true}, any threads
-     * waiting on {@link #get} will be released and the provided result will be
-     * returned.
-     *
-     * @param result
-     *            The result.
-     * @return {@code false} if this future has already been completed, either
-     *         due to normal termination, an exception, or cancellation (i.e.
-     *         {@code isDone() == true}).
-     */
-    public final boolean tryHandleResult(final M result) {
-        return sync.innerSetResult(result);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final boolean isCancelled() {
-        return sync.innerIsCancelled();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final boolean isDone() {
-        return sync.innerIsDone();
-    }
-
-    /**
-     * Invoked when {@link #cancel} is called and {@code isDone() == false} and
-     * immediately before any threads waiting on {@link #get} are released.
-     * Implementations may choose to return a custom error result if needed or
-     * return {@code null} if the following default error result is acceptable:
-     *
-     * <pre>
-     * Result result = Responses.newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED);
-     * </pre>
-     *
-     * In addition, implementations may perform other cleanup, for example, by
-     * issuing an LDAP abandon request. The default implementation is to do
-     * nothing.
-     *
-     * @param mayInterruptIfRunning
-     *            {@code true} if the thread executing executing the response
-     *            handler should be interrupted; otherwise, in-progress response
-     *            handlers are allowed to complete.
-     * @return The custom error result, or {@code null} if the default is
-     *         acceptable.
-     */
-    protected ErrorResultException handleCancelRequest(final boolean mayInterruptIfRunning) {
-        // Do nothing by default.
-        return null;
-    }
-
-    /**
-     * Indicates whether this future result can be canceled.
-     *
-     * @return {@code true} if this future result is cancelable or {@code false}
-     *         otherwise.
-     */
-    protected boolean isCancelable() {
-        // Return true by default.
-        return true;
-    }
-
-    /**
-     * Appends a string representation of this future's state to the provided
-     * builder.
-     *
-     * @param sb
-     *            The string builder.
-     */
-    protected void toString(final StringBuilder sb) {
-        sb.append(" state = ");
-        sb.append(sync);
-    }
-
-}
diff --git a/opendj-core/src/main/java/com/forgerock/opendj/util/CompletedFutureResult.java b/opendj-core/src/main/java/com/forgerock/opendj/util/CompletedFutureResult.java
deleted file mode 100644
index 285e30d..0000000
--- a/opendj-core/src/main/java/com/forgerock/opendj/util/CompletedFutureResult.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at 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 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 2009 Sun Microsystems, Inc.
- *      Portions copyright 2012 ForgeRock AS.
- */
-
-package com.forgerock.opendj.util;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.forgerock.opendj.ldap.ErrorResultException;
-import org.forgerock.opendj.ldap.FutureResult;
-import org.forgerock.util.Reject;
-
-/**
- * An implementation of {@code FutureResult} which can be used in cases where
- * the result is known in advance, for example, if the result is obtained
- * synchronously.
- *
- * @param <S>
- *            The type of result returned by this future.
- */
-public final class CompletedFutureResult<S> implements FutureResult<S> {
-    private final S result;
-
-    private final ErrorResultException errorResult;
-
-    private final int requestID;
-
-    /**
-     * Creates a new completed future which will throw the provided error result
-     * and request ID of {@code -1}.
-     *
-     * @param errorResult
-     *            The error result.
-     * @throws NullPointerException
-     *             If {@code errorResult} was {@code null}.
-     */
-    public CompletedFutureResult(final ErrorResultException errorResult) {
-        this(errorResult, -1);
-    }
-
-    /**
-     * Creates a new completed future which will throw the provided error result
-     * and request ID.
-     *
-     * @param errorResult
-     *            The error result.
-     * @param requestID
-     *            The request ID.
-     * @throws NullPointerException
-     *             If {@code errorResult} was {@code null}.
-     */
-    public CompletedFutureResult(final ErrorResultException errorResult, final int requestID) {
-        Reject.ifNull(errorResult);
-        this.result = null;
-        this.errorResult = errorResult;
-        this.requestID = requestID;
-    }
-
-    /**
-     * Creates a new completed future which will return the provided result and
-     * request ID of {@code -1}.
-     *
-     * @param result
-     *            The result, which may be {@code null}.
-     */
-    public CompletedFutureResult(final S result) {
-        this(result, -1);
-    }
-
-    /**
-     * Creates a new completed future which will return the provided result and
-     * request ID.
-     *
-     * @param result
-     *            The result, which may be {@code null}.
-     * @param requestID
-     *            The request ID.
-     */
-    public CompletedFutureResult(final S result, final int requestID) {
-        this.result = result;
-        this.errorResult = null;
-        this.requestID = requestID;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean cancel(final boolean mayInterruptIfRunning) {
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public S get() throws ErrorResultException, InterruptedException {
-        if (errorResult == null) {
-            // May be null.
-            return result;
-        } else {
-            throw errorResult;
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public S get(final long timeout, final TimeUnit unit) throws ErrorResultException,
-            TimeoutException, InterruptedException {
-        return get();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public int getRequestID() {
-        return requestID;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean isCancelled() {
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean isDone() {
-        return true;
-    }
-
-}
diff --git a/opendj-core/src/main/java/com/forgerock/opendj/util/FutureResultTransformer.java b/opendj-core/src/main/java/com/forgerock/opendj/util/FutureResultTransformer.java
deleted file mode 100644
index 83914d4..0000000
--- a/opendj-core/src/main/java/com/forgerock/opendj/util/FutureResultTransformer.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at 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 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 2009 Sun Microsystems, Inc.
- *      Portions copyright 2013 ForgeRock AS.
- */
-
-package com.forgerock.opendj.util;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.forgerock.opendj.ldap.ErrorResultException;
-import org.forgerock.opendj.ldap.FutureResult;
-import org.forgerock.opendj.ldap.ResultHandler;
-
-/**
- * An implementation of the {@code FutureResult} interface which transforms the
- * result of an asynchronous operation from one type to another. The
- * implementation ensures that the transformed is computed only once.
- *
- * @param <M>
- *            The type of the inner result.
- * @param <N>
- *            The type of the outer result.
- */
-public abstract class FutureResultTransformer<M, N> implements FutureResult<N>, ResultHandler<M> {
-    private final ResultHandler<? super N> handler;
-    private volatile FutureResult<? extends M> future = null;
-
-    // These do not need to be volatile since the future acts as a memory
-    // barrier.
-    private N transformedResult = null;
-    private ErrorResultException transformedErrorResult = null;
-
-    /**
-     * Creates a new result transformer which will transform the results of an
-     * inner asynchronous request.
-     *
-     * @param handler
-     *            The outer result handler.
-     */
-    protected FutureResultTransformer(final ResultHandler<? super N> handler) {
-        this.handler = handler;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public final boolean cancel(final boolean mayInterruptIfRunning) {
-        return future.cancel(mayInterruptIfRunning);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public final N get() throws ErrorResultException, InterruptedException {
-        try {
-            future.get();
-        } catch (ErrorResultException ignored) {
-            // Ignore untransformed error.
-        }
-        // The handlers are guaranteed to have been invoked at this point.
-        return get0();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public final N get(final long timeout, final TimeUnit unit) throws ErrorResultException,
-            TimeoutException, InterruptedException {
-        try {
-            future.get(timeout, unit);
-        } catch (ErrorResultException ignored) {
-            // Ignore untransformed error.
-        }
-        // The handlers are guaranteed to have been invoked at this point.
-        return get0();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public final int getRequestID() {
-        return future.getRequestID();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public final void handleErrorResult(final ErrorResultException error) {
-        transformedErrorResult = transformErrorResult(error);
-        if (handler != null) {
-            handler.handleErrorResult(transformedErrorResult);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public final void handleResult(final M result) {
-        try {
-            transformedResult = transformResult(result);
-            if (handler != null) {
-                handler.handleResult(transformedResult);
-            }
-        } catch (final ErrorResultException e) {
-            transformedErrorResult = e;
-            if (handler != null) {
-                handler.handleErrorResult(transformedErrorResult);
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public final boolean isCancelled() {
-        return future.isCancelled();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public final boolean isDone() {
-        return future.isDone();
-    }
-
-    /**
-     * Sets the inner future for this result transformer. This must be done
-     * before this future is published.
-     *
-     * @param future
-     *            The inner future.
-     */
-    public final void setFutureResult(final FutureResult<? extends M> future) {
-        this.future = future;
-    }
-
-    /**
-     * Transforms the inner error result to an outer error result. The default
-     * implementation is to return the inner error result.
-     *
-     * @param errorResult
-     *            The inner error result.
-     * @return The outer error result.
-     */
-    protected ErrorResultException transformErrorResult(final ErrorResultException errorResult) {
-        return errorResult;
-    }
-
-    /**
-     * Transforms the inner result to an outer result, possibly throwing an
-     * {@code ErrorResultException} if the transformation fails for some reason.
-     *
-     * @param result
-     *            The inner result.
-     * @return The outer result.
-     * @throws ErrorResultException
-     *             If the transformation fails for some reason.
-     */
-    protected abstract N transformResult(M result) throws ErrorResultException;
-
-    private N get0() throws ErrorResultException {
-        if (transformedErrorResult != null) {
-            throw transformedErrorResult;
-        } else {
-            return transformedResult;
-        }
-    }
-
-}
diff --git a/opendj-core/src/main/java/com/forgerock/opendj/util/RecursiveFutureResult.java b/opendj-core/src/main/java/com/forgerock/opendj/util/RecursiveFutureResult.java
deleted file mode 100644
index 9aa3085..0000000
--- a/opendj-core/src/main/java/com/forgerock/opendj/util/RecursiveFutureResult.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at 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 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 2009-2010 Sun Microsystems, Inc.
- */
-
-package com.forgerock.opendj.util;
-
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.forgerock.opendj.ldap.ErrorResultException;
-import org.forgerock.opendj.ldap.FutureResult;
-import org.forgerock.opendj.ldap.ResultHandler;
-
-/**
- * An implementation of the {@code FutureResult} interface which can be used to
- * combine a sequence of two asynchronous operations into a single future
- * result. Implementations should override the methods {@link #chainResult} and
- * {@link #chainErrorResult} in order to define the second asynchronous
- * operation.
- *
- * @param <M>
- *            The type of the inner result.
- * @param <N>
- *            The type of the outer result.
- */
-public abstract class RecursiveFutureResult<M, N> implements FutureResult<N>, ResultHandler<M> {
-
-    private final class FutureResultImpl extends AsynchronousFutureResult<N, ResultHandler<? super N>> {
-
-        private FutureResultImpl(final ResultHandler<? super N> handler) {
-            super(handler);
-        }
-
-        @Override
-        public int getRequestID() {
-            if (innerFuture instanceof FutureResult<?>) {
-                final FutureResult<?> tmp = (FutureResult<?>) innerFuture;
-                return tmp.getRequestID();
-            } else {
-                return -1;
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        protected ErrorResultException handleCancelRequest(final boolean mayInterruptIfRunning) {
-            innerFuture.cancel(mayInterruptIfRunning);
-            if (outerFuture != null) {
-                outerFuture.cancel(mayInterruptIfRunning);
-            }
-            return null;
-        }
-
-    }
-
-    private final FutureResultImpl impl;
-
-    private volatile Future<?> innerFuture = null;
-
-    // This does not need to be volatile since the inner future acts as a
-    // memory barrier.
-    private FutureResult<? extends N> outerFuture = null;
-
-    /**
-     * Creates a new asynchronous result chain which will chain an outer
-     * asynchronous request once the inner asynchronous request completes.
-     *
-     * @param handler
-     *            The outer result handler.
-     */
-    protected RecursiveFutureResult(final ResultHandler<? super N> handler) {
-        this.impl = new FutureResultImpl(handler);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final boolean cancel(final boolean mayInterruptIfRunning) {
-        return impl.cancel(mayInterruptIfRunning);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final N get() throws ErrorResultException, InterruptedException {
-        return impl.get();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final N get(final long timeout, final TimeUnit unit) throws ErrorResultException,
-            TimeoutException, InterruptedException {
-        return impl.get(timeout, unit);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final int getRequestID() {
-        return impl.getRequestID();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final void handleErrorResult(final ErrorResultException error) {
-        try {
-            outerFuture = chainErrorResult(error, impl);
-        } catch (final ErrorResultException e) {
-            impl.handleErrorResult(e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final void handleResult(final M result) {
-        try {
-            outerFuture = chainResult(result, impl);
-        } catch (final ErrorResultException e) {
-            impl.handleErrorResult(e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final boolean isCancelled() {
-        return impl.isCancelled();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final boolean isDone() {
-        return impl.isDone();
-    }
-
-    /**
-     * Sets the inner future for this result chain. This must be done before
-     * this future is published.
-     *
-     * @param future
-     *            The inner future.
-     */
-    public final void setFutureResult(final Future<?> future) {
-        this.innerFuture = future;
-    }
-
-    /**
-     * Invokes the outer request based on the error result of the inner request
-     * and returns a future representing the result of the outer request.
-     * <p>
-     * The default implementation is to terminate further processing by
-     * re-throwing the inner error result.
-     *
-     * @param innerError
-     *            The error result of the inner request.
-     * @param handler
-     *            The result handler to be used for the outer request.
-     * @return A future representing the result of the outer request.
-     * @throws ErrorResultException
-     *             If the outer request could not be invoked and processing
-     *             should terminate.
-     */
-    protected FutureResult<? extends N> chainErrorResult(final ErrorResultException innerError,
-            final ResultHandler<? super N> handler) throws ErrorResultException {
-        throw innerError;
-    }
-
-    /**
-     * Invokes the outer request based on the result of the inner request and
-     * returns a future representing the result of the outer request.
-     *
-     * @param innerResult
-     *            The result of the inner request.
-     * @param handler
-     *            The result handler to be used for the outer request.
-     * @return A future representing the result of the outer request.
-     * @throws ErrorResultException
-     *             If the outer request could not be invoked and processing
-     *             should terminate.
-     */
-    protected abstract FutureResult<? extends N> chainResult(M innerResult,
-            ResultHandler<? super N> handler) throws ErrorResultException;
-
-}
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnection.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnection.java
index 235e173..dfeaac4 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnection.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnection.java
@@ -22,13 +22,11 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2012 ForgeRock AS
+ *      Portions copyright 2011-2014 ForgeRock AS
  */
 
 package org.forgerock.opendj.ldap;
 
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-
 import org.forgerock.opendj.ldap.requests.AddRequest;
 import org.forgerock.opendj.ldap.requests.BindRequest;
 import org.forgerock.opendj.ldap.requests.CompareRequest;
@@ -42,6 +40,8 @@
 import org.forgerock.opendj.ldap.responses.ExtendedResult;
 import org.forgerock.opendj.ldap.responses.Result;
 
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+
 /**
  * An abstract connection whose synchronous methods are implemented in terms of
  * asynchronous methods.
@@ -55,133 +55,61 @@
         // No implementation required.
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public Result add(final AddRequest request) throws ErrorResultException {
-        final FutureResult<Result> future = addAsync(request, null, null);
-        try {
-            return future.get();
-        } catch (InterruptedException e) {
-            throw interrupted(e);
-        } finally {
-            // Cancel the request if it hasn't completed.
-            future.cancel(false);
-        }
+        return blockingGetOrThrow(addAsync(request));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public BindResult bind(final BindRequest request) throws ErrorResultException {
-        final FutureResult<BindResult> future = bindAsync(request, null, null);
-        try {
-            return future.get();
-        } catch (InterruptedException e) {
-            throw interrupted(e);
-        } finally {
-            // Cancel the request if it hasn't completed.
-            future.cancel(false);
-        }
+        return blockingGetOrThrow(bindAsync(request));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public CompareResult compare(final CompareRequest request) throws ErrorResultException {
-        final FutureResult<CompareResult> future = compareAsync(request, null, null);
-        try {
-            return future.get();
-        } catch (InterruptedException e) {
-            throw interrupted(e);
-        } finally {
-            // Cancel the request if it hasn't completed.
-            future.cancel(false);
-        }
+        return blockingGetOrThrow(compareAsync(request));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public Result delete(final DeleteRequest request) throws ErrorResultException {
-        final FutureResult<Result> future = deleteAsync(request, null, null);
-        try {
-            return future.get();
-        } catch (InterruptedException e) {
-            throw interrupted(e);
-        } finally {
-            // Cancel the request if it hasn't completed.
-            future.cancel(false);
-        }
+        return blockingGetOrThrow(deleteAsync(request));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public <R extends ExtendedResult> R extendedRequest(final ExtendedRequest<R> request,
             final IntermediateResponseHandler handler) throws ErrorResultException {
-        final FutureResult<R> future = extendedRequestAsync(request, handler, null);
-        try {
-            return future.get();
-        } catch (InterruptedException e) {
-            throw interrupted(e);
-        } finally {
-            // Cancel the request if it hasn't completed.
-            future.cancel(false);
-        }
+        return blockingGetOrThrow(extendedRequestAsync(request, handler));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public Result modify(final ModifyRequest request) throws ErrorResultException {
-        final FutureResult<Result> future = modifyAsync(request, null, null);
-        try {
-            return future.get();
-        } catch (InterruptedException e) {
-            throw interrupted(e);
-        } finally {
-            // Cancel the request if it hasn't completed.
-            future.cancel(false);
-        }
+        return blockingGetOrThrow(modifyAsync(request));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public Result modifyDN(final ModifyDNRequest request) throws ErrorResultException {
-        final FutureResult<Result> future = modifyDNAsync(request, null, null);
-        try {
-            return future.get();
-        } catch (InterruptedException e) {
-            throw interrupted(e);
-        } finally {
-            // Cancel the request if it hasn't completed.
-            future.cancel(false);
-        }
+        return blockingGetOrThrow(modifyDNAsync(request));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public Result search(final SearchRequest request, final SearchResultHandler handler)
             throws ErrorResultException {
-        final FutureResult<Result> future = searchAsync(request, null, handler);
+        return blockingGetOrThrow(searchAsync(request, handler));
+    }
+
+    private <T extends Result> T blockingGetOrThrow(FutureResult<T> future) throws ErrorResultException {
         try {
-            return future.get();
+            return future.getOrThrow();
         } catch (InterruptedException e) {
             throw interrupted(e);
-        } finally {
-            // Cancel the request if it hasn't completed.
-            future.cancel(false);
         }
     }
 
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractConnection.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractConnection.java
index 2629a53..0da2912 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractConnection.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractConnection.java
@@ -22,23 +22,17 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS
+ *      Portions copyright 2011-2014 ForgeRock AS
  */
 
 package org.forgerock.opendj.ldap;
 
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_NO_SEARCH_RESULT_ENTRIES;
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_UNEXPECTED_SEARCH_RESULT_ENTRIES;
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_UNEXPECTED_SEARCH_RESULT_ENTRIES_NO_COUNT;
-import static com.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;
-import java.util.concurrent.TimeoutException;
 
 import org.forgerock.opendj.ldap.controls.SubtreeDeleteRequestControl;
 import org.forgerock.opendj.ldap.requests.AddRequest;
+import org.forgerock.opendj.ldap.requests.BindRequest;
+import org.forgerock.opendj.ldap.requests.CompareRequest;
 import org.forgerock.opendj.ldap.requests.DeleteRequest;
 import org.forgerock.opendj.ldap.requests.ExtendedRequest;
 import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
@@ -56,6 +50,12 @@
 import org.forgerock.opendj.ldif.ChangeRecordVisitor;
 import org.forgerock.opendj.ldif.ConnectionEntryReader;
 import org.forgerock.util.Reject;
+import org.forgerock.util.promise.Function;
+
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+import static org.forgerock.opendj.ldap.requests.Requests.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
 
 /**
  * This class provides a skeletal implementation of the {@code Connection}
@@ -63,108 +63,10 @@
  */
 public abstract class AbstractConnection implements Connection {
 
-    private static final class SingleEntryFuture implements FutureResult<SearchResultEntry>,
-            SearchResultHandler {
-        private final ResultHandler<? super SearchResultEntry> handler;
-
-        private final SingleEntryHandler singleEntryHandler = new SingleEntryHandler();
-
-        private volatile FutureResult<Result> future = null;
-
-        private SingleEntryFuture(final ResultHandler<? super SearchResultEntry> handler) {
-            this.handler = handler;
-        }
-
-        @Override
-        public boolean cancel(final boolean mayInterruptIfRunning) {
-            return future.cancel(mayInterruptIfRunning);
-        }
-
-        @Override
-        public SearchResultEntry get() throws ErrorResultException, InterruptedException {
-            try {
-                future.get();
-            } catch (ErrorResultException e) {
-                throw singleEntryHandler.filterError(e);
-            }
-            return get0();
-        }
-
-        @Override
-        public SearchResultEntry get(final long timeout, final TimeUnit unit) throws ErrorResultException,
-                TimeoutException, InterruptedException {
-            try {
-                future.get(timeout, unit);
-            } catch (ErrorResultException e) {
-                throw singleEntryHandler.filterError(e);
-            }
-            return get0();
-        }
-
-        @Override
-        public int getRequestID() {
-            return future.getRequestID();
-        }
-
-        @Override
-        public boolean handleEntry(final SearchResultEntry entry) {
-            return singleEntryHandler.handleEntry(entry);
-        }
-
-        @Override
-        public void handleErrorResult(final ErrorResultException error) {
-            if (handler != null) {
-                ErrorResultException finalError = singleEntryHandler.filterError(error);
-                handler.handleErrorResult(finalError);
-            }
-        }
-
-        @Override
-        public boolean handleReference(final SearchResultReference reference) {
-            return singleEntryHandler.handleReference(reference);
-        }
-
-        @Override
-        public void handleResult(final Result result) {
-            if (handler != null) {
-                try {
-                    handler.handleResult(get0());
-                } catch (final ErrorResultException e) {
-                    handler.handleErrorResult(e);
-                }
-            }
-        }
-
-        @Override
-        public boolean isCancelled() {
-            return future.isCancelled();
-        }
-
-        @Override
-        public boolean isDone() {
-            return future.isDone();
-        }
-
-        private SearchResultEntry get0() throws ErrorResultException {
-            ErrorResultException exception = singleEntryHandler.checkForClientSideError();
-            if (exception == null) {
-                return singleEntryHandler.firstEntry;
-            } else {
-                throw exception;
-            }
-        }
-
-        private void setResultFuture(final FutureResult<Result> future) {
-            this.future = future;
-        }
-    }
-
     private static final class SingleEntryHandler implements SearchResultHandler {
-        private volatile SearchResultEntry firstEntry = null;
-
-        private volatile SearchResultReference firstReference = null;
-
-        private volatile int entryCount = 0;
+        private volatile SearchResultEntry firstEntry;
+        private volatile SearchResultReference firstReference;
+        private volatile int entryCount;
 
         @Override
         public boolean handleEntry(final SearchResultEntry entry) {
@@ -175,14 +77,6 @@
             return true;
         }
 
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void handleErrorResult(final ErrorResultException error) {
-            // Ignore
-        }
-
         @Override
         public boolean handleReference(final SearchResultReference reference) {
             if (firstReference == null) {
@@ -192,22 +86,17 @@
         }
 
         /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void handleResult(final Result result) {
-            // Ignore.
-        }
-
-        /**
-         * Filter the provided error in order to transform size limit exceeded error to a client side error,
-         * or leave it as is for any other error.
+         * Filter the provided error in order to transform size limit exceeded
+         * error to a client side error, or leave it as is for any other error.
          *
-         * @param error to filter
-         * @return provided error in most case, or <code>ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED</code>
-         * error if provided error is <code>ResultCode.SIZE_LIMIT_EXCEEDED</code>
+         * @param error
+         *            to filter
+         * @return provided error in most case, or
+         *         <code>ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED</code>
+         *         error if provided error is
+         *         <code>ResultCode.SIZE_LIMIT_EXCEEDED</code>
          */
-        public ErrorResultException filterError(final ErrorResultException error) {
+        private ErrorResultException filterError(final ErrorResultException error) {
             if (error.getResult().getResultCode().equals(ResultCode.SIZE_LIMIT_EXCEEDED)) {
                 return newErrorResult(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED,
                         ERR_UNEXPECTED_SEARCH_RESULT_ENTRIES_NO_COUNT.get().toString());
@@ -217,35 +106,35 @@
         }
 
         /**
-         * Check for any error related to number of search result at client-side level: no result,
-         * too many result, search result reference.
+         * Check for any error related to number of search result at client-side
+         * level: no result, too many result, search result reference. This
+         * method should be called only after search operation is finished.
          *
-         * This method should be called only after search operation is finished.
-         *
-         * @return an <code>ErrorResultException</code> if an error is detected, <code>null</code> otherwise
+         * @return The single search result entry.
+         * @throws ErrorResultException
+         *             If an error is detected.
          */
-        public ErrorResultException checkForClientSideError() {
-            ErrorResultException exception = null;
+        private SearchResultEntry getSingleEntry() throws ErrorResultException {
             if (entryCount == 0) {
                 // Did not find any entries.
-                exception = newErrorResult(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED, ERR_NO_SEARCH_RESULT_ENTRIES
-                        .get().toString());
+                throw newErrorResult(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED,
+                        ERR_NO_SEARCH_RESULT_ENTRIES.get().toString());
             } else if (entryCount > 1) {
                 // Got more entries than expected.
-                exception = newErrorResult(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED,
+                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.
-                exception = newErrorResult(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED,
+                throw newErrorResult(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED,
                         ERR_UNEXPECTED_SEARCH_RESULT_REFERENCES.get(firstReference.getURIs().iterator().next())
-                                .toString());
+                        .toString());
+            } else {
+                return firstEntry;
             }
-            return exception;
         }
-
     }
 
-    // Visitor used for processing synchronous change requests.
+    /** Visitor used for processing synchronous change requests. */
     private static final ChangeRecordVisitor<Object, Connection> SYNC_VISITOR =
             new ChangeRecordVisitor<Object, Connection>() {
 
@@ -268,18 +157,18 @@
                 }
 
                 @Override
-                public Object visitChangeRecord(final Connection p, final ModifyRequest change) {
+                public Object visitChangeRecord(final Connection p, final ModifyDNRequest change) {
                     try {
-                        return p.modify(change);
+                        return p.modifyDN(change);
                     } catch (final ErrorResultException e) {
                         return e;
                     }
                 }
 
                 @Override
-                public Object visitChangeRecord(final Connection p, final ModifyDNRequest change) {
+                public Object visitChangeRecord(final Connection p, final ModifyRequest change) {
                     try {
-                        return p.modifyDN(change);
+                        return p.modify(change);
                     } catch (final ErrorResultException e) {
                         return e;
                     }
@@ -293,25 +182,21 @@
         // No implementation required.
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public Result add(final Entry entry) throws ErrorResultException {
         return add(Requests.newAddRequest(entry));
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public Result add(final String... ldifLines) throws ErrorResultException {
         return add(Requests.newAddRequest(ldifLines));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    @Override
+    public FutureResult<Result> addAsync(final AddRequest request) {
+        return addAsync(request, null);
+    }
+
     @Override
     public Result applyChange(final ChangeRecord request) throws ErrorResultException {
         final Object result = request.accept(SYNC_VISITOR, this);
@@ -322,200 +207,166 @@
         }
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    @Override
+    public FutureResult<Result> applyChangeAsync(ChangeRecord request) {
+        return applyChangeAsync(request, null);
+    }
+
     @Override
     public FutureResult<Result> applyChangeAsync(final ChangeRecord request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final ChangeRecordVisitor<FutureResult<Result>, Connection> visitor =
-                new ChangeRecordVisitor<FutureResult<Result>, Connection>() {
+            new ChangeRecordVisitor<FutureResult<Result>, Connection>() {
 
-                    @Override
-                    public FutureResult<Result> visitChangeRecord(final Connection p,
-                            final AddRequest change) {
-                        return p.addAsync(change, intermediateResponseHandler, resultHandler);
-                    }
+                @Override
+                public FutureResult<Result> visitChangeRecord(final Connection p, final AddRequest change) {
+                    return p.addAsync(change, intermediateResponseHandler);
+                }
 
-                    @Override
-                    public FutureResult<Result> visitChangeRecord(final Connection p,
-                            final DeleteRequest change) {
-                        return p.deleteAsync(change, intermediateResponseHandler, resultHandler);
-                    }
+                @Override
+                public FutureResult<Result> visitChangeRecord(final Connection p, final DeleteRequest change) {
+                    return p.deleteAsync(change, intermediateResponseHandler);
+                }
 
-                    @Override
-                    public FutureResult<Result> visitChangeRecord(final Connection p,
-                            final ModifyRequest change) {
-                        return p.modifyAsync(change, intermediateResponseHandler, resultHandler);
-                    }
+                @Override
+                public FutureResult<Result> visitChangeRecord(final Connection p, final ModifyDNRequest change) {
+                    return p.modifyDNAsync(change, intermediateResponseHandler);
+                }
 
-                    @Override
-                    public FutureResult<Result> visitChangeRecord(final Connection p,
-                            final ModifyDNRequest change) {
-                        return p.modifyDNAsync(change, intermediateResponseHandler, resultHandler);
-                    }
-                };
+                @Override
+                public FutureResult<Result> visitChangeRecord(final Connection p, final ModifyRequest change) {
+                    return p.modifyAsync(change, intermediateResponseHandler);
+                }
+            };
         return request.accept(visitor, this);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public BindResult bind(final String name, final char[] password) throws ErrorResultException {
         return bind(Requests.newSimpleBindRequest(name, password));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    @Override
+    public FutureResult<BindResult> bindAsync(final BindRequest request) {
+        return bindAsync(request, null);
+    }
+
     @Override
     public void close() {
         close(Requests.newUnbindRequest(), null);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public CompareResult compare(final String name, final String attributeDescription,
-            final String assertionValue) throws ErrorResultException {
+    public CompareResult compare(final String name, final String attributeDescription, final String assertionValue)
+            throws ErrorResultException {
         return compare(Requests.newCompareRequest(name, attributeDescription, assertionValue));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    @Override
+    public FutureResult<CompareResult> compareAsync(final CompareRequest request) {
+        return compareAsync(request, null);
+    }
+
     @Override
     public Result delete(final String name) throws ErrorResultException {
         return delete(Requests.newDeleteRequest(name));
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public Result deleteSubtree(final String name) throws ErrorResultException {
-        return delete(Requests.newDeleteRequest(name).addControl(
-                SubtreeDeleteRequestControl.newControl(true)));
+    public FutureResult<Result> deleteAsync(final DeleteRequest request) {
+        return deleteAsync(request, null);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public <R extends ExtendedResult> R extendedRequest(final ExtendedRequest<R> request)
-            throws ErrorResultException {
+    public Result deleteSubtree(final String name) throws ErrorResultException {
+        return delete(Requests.newDeleteRequest(name).addControl(SubtreeDeleteRequestControl.newControl(true)));
+    }
+
+    @Override
+    public <R extends ExtendedResult> R extendedRequest(final ExtendedRequest<R> request) throws ErrorResultException {
         return extendedRequest(request, null);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public GenericExtendedResult extendedRequest(final String requestName,
-            final ByteString requestValue) throws ErrorResultException {
+    public GenericExtendedResult extendedRequest(final String requestName, final ByteString requestValue)
+            throws ErrorResultException {
         return extendedRequest(Requests.newGenericExtendedRequest(requestName, requestValue));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    @Override
+    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request) {
+        return extendedRequestAsync(request, null);
+    }
+
     @Override
     public Result modify(final String... ldifLines) throws ErrorResultException {
         return modify(Requests.newModifyRequest(ldifLines));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    @Override
+    public FutureResult<Result> modifyAsync(final ModifyRequest request) {
+        return modifyAsync(request, null);
+    }
+
     @Override
     public Result modifyDN(final String name, final String newRDN) throws ErrorResultException {
         return modifyDN(Requests.newModifyDNRequest(name, newRDN));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    @Override
+    public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request) {
+        return modifyDNAsync(request, null);
+    }
+
     @Override
     public SearchResultEntry readEntry(final DN baseObject, final String... attributeDescriptions)
             throws ErrorResultException {
         final SearchRequest request =
-                Requests.newSingleEntrySearchRequest(baseObject, SearchScope.BASE_OBJECT, Filter
-                        .objectClassPresent(), attributeDescriptions);
+            Requests.newSingleEntrySearchRequest(baseObject, SearchScope.BASE_OBJECT, Filter.objectClassPresent(),
+                attributeDescriptions);
         return searchSingleEntry(request);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public SearchResultEntry readEntry(final String baseObject,
-            final String... attributeDescriptions) throws ErrorResultException {
+    public SearchResultEntry readEntry(final String baseObject, final String... attributeDescriptions)
+            throws ErrorResultException {
         return readEntry(DN.valueOf(baseObject), attributeDescriptions);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public FutureResult<SearchResultEntry> readEntryAsync(final DN name,
-            final Collection<String> attributeDescriptions,
-            final ResultHandler<? super SearchResultEntry> handler) {
-        final SearchRequest request =
-                Requests.newSingleEntrySearchRequest(
-                        name, SearchScope.BASE_OBJECT,
-                        Filter.objectClassPresent());
+            final Collection<String> attributeDescriptions) {
+        final SearchRequest request = Requests.newSingleEntrySearchRequest(name, SearchScope.BASE_OBJECT,
+                Filter.objectClassPresent());
         if (attributeDescriptions != null) {
             request.getAttributes().addAll(attributeDescriptions);
         }
-        return searchSingleEntryAsync(request, handler);
+        return searchSingleEntryAsync(request);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public ConnectionEntryReader search(final SearchRequest request) {
         return new ConnectionEntryReader(this, request);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public Result search(final SearchRequest request,
-            final Collection<? super SearchResultEntry> entries) throws ErrorResultException {
+    public Result search(final SearchRequest request, final Collection<? super SearchResultEntry> entries)
+            throws ErrorResultException {
         return search(request, entries, null);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public Result search(final SearchRequest request,
-            final Collection<? super SearchResultEntry> entries,
-            final Collection<? super SearchResultReference> references) throws ErrorResultException {
+    public Result search(final SearchRequest request, final Collection<? super SearchResultEntry> entries,
+        final Collection<? super SearchResultReference> references) throws ErrorResultException {
         Reject.ifNull(request, entries);
-
         // FIXME: does this need to be thread safe?
         final SearchResultHandler handler = new SearchResultHandler() {
-
             @Override
             public boolean handleEntry(final SearchResultEntry entry) {
                 entries.add(entry);
                 return true;
             }
 
-            /**
-             * {@inheritDoc}
-             */
-            @Override
-            public void handleErrorResult(final ErrorResultException error) {
-                // Ignore.
-            }
-
             @Override
             public boolean handleReference(final SearchResultReference reference) {
                 if (references != null) {
@@ -523,72 +374,56 @@
                 }
                 return true;
             }
-
-            /**
-             * {@inheritDoc}
-             */
-            @Override
-            public void handleResult(final Result result) {
-                // Ignore.
-            }
         };
 
         return search(request, handler);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public ConnectionEntryReader search(final String baseObject, final SearchScope scope,
-            final String filter, final String... attributeDescriptions) {
-        final SearchRequest request =
-                Requests.newSearchRequest(baseObject, scope, filter, attributeDescriptions);
-        return search(request);
+    public ConnectionEntryReader search(final String baseObject, final SearchScope scope, final String filter,
+        final String... attributeDescriptions) {
+        return search(newSearchRequest(baseObject, scope, filter, attributeDescriptions));
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public SearchResultEntry searchSingleEntry(final SearchRequest request)
-            throws ErrorResultException {
+    public FutureResult<Result> searchAsync(final SearchRequest request, final SearchResultHandler resultHandler) {
+        return searchAsync(request, null, resultHandler);
+    }
+
+    @Override
+    public SearchResultEntry searchSingleEntry(final SearchRequest request) throws ErrorResultException {
         final SingleEntryHandler handler = new SingleEntryHandler();
         try {
             search(enforceSingleEntrySearchRequest(request), handler);
-        } catch (ErrorResultException e) {
+            return handler.getSingleEntry();
+        } catch (final ErrorResultException e) {
             throw handler.filterError(e);
         }
-        ErrorResultException error = handler.checkForClientSideError();
-        if (error == null) {
-            return handler.firstEntry;
-        } else {
-            throw error;
-        }
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public SearchResultEntry searchSingleEntry(final String baseObject, final SearchScope scope,
-            final String filter, final String... attributeDescriptions) throws ErrorResultException {
+    public SearchResultEntry searchSingleEntry(final String baseObject, final SearchScope scope, final String filter,
+        final String... attributeDescriptions) throws ErrorResultException {
         final SearchRequest request =
-                Requests.newSingleEntrySearchRequest(baseObject, scope, filter, attributeDescriptions);
+            Requests.newSingleEntrySearchRequest(baseObject, scope, filter, attributeDescriptions);
         return searchSingleEntry(request);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public FutureResult<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request,
-            final ResultHandler<? super SearchResultEntry> handler) {
-        final SingleEntryFuture innerFuture = new SingleEntryFuture(handler);
-        final FutureResult<Result> future =
-                searchAsync(enforceSingleEntrySearchRequest(request), null, innerFuture);
-        innerFuture.setResultFuture(future);
-        return innerFuture;
+    public FutureResult<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request) {
+        final SingleEntryHandler handler = new SingleEntryHandler();
+        return FutureResultWrapper.asFutureResult(searchAsync(enforceSingleEntrySearchRequest(request), handler).then(
+            new Function<Result, SearchResultEntry, ErrorResultException>() {
+                @Override
+                public SearchResultEntry apply(final Result value) throws ErrorResultException {
+                    return handler.getSingleEntry();
+                }
+            }, new Function<ErrorResultException, SearchResultEntry, ErrorResultException>() {
+                @Override
+                public SearchResultEntry apply(final ErrorResultException error) throws ErrorResultException {
+                    throw handler.filterError(error);
+                }
+            }));
     }
 
     /**
@@ -611,6 +446,7 @@
      * <p>
      * Sub-classes should provide an implementation which returns an appropriate
      * description of the connection which may be used for debugging purposes.
+     * </p>
      */
     @Override
     public abstract String toString();
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractConnectionWrapper.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractConnectionWrapper.java
index dafbd75..1223522 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractConnectionWrapper.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractConnectionWrapper.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS
+ *      Portions copyright 2011-2014 ForgeRock AS
  */
 
 package org.forgerock.opendj.ldap;
@@ -122,10 +122,19 @@
      * The default implementation is to delegate.
      */
     @Override
+    public FutureResult<Result> addAsync(final AddRequest request) {
+        return connection.addAsync(request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * The default implementation is to delegate.
+     */
+    @Override
     public FutureResult<Result> addAsync(final AddRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
-        return connection.addAsync(request, intermediateResponseHandler, resultHandler);
+            final IntermediateResponseHandler intermediateResponseHandler) {
+        return connection.addAsync(request, intermediateResponseHandler);
     }
 
     /**
@@ -154,10 +163,19 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> applyChangeAsync(final ChangeRecord request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
-        return connection.applyChangeAsync(request, intermediateResponseHandler, resultHandler);
+    public FutureResult<Result> applyChangeAsync(ChangeRecord request) {
+        return connection.applyChangeAsync(request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * The default implementation is to delegate.
+     */
+    @Override
+    public FutureResult<Result> applyChangeAsync(ChangeRecord request,
+            IntermediateResponseHandler intermediateResponseHandler) {
+        return connection.applyChangeAsync(request, intermediateResponseHandler);
     }
 
     /**
@@ -186,10 +204,19 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<BindResult> bindAsync(final BindRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super BindResult> resultHandler) {
-        return connection.bindAsync(request, intermediateResponseHandler, resultHandler);
+    public FutureResult<BindResult> bindAsync(final BindRequest request) {
+        return connection.bindAsync(request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * The default implementation is to delegate.
+     */
+    @Override
+    public FutureResult<BindResult> bindAsync(BindRequest request,
+            IntermediateResponseHandler intermediateResponseHandler) {
+        return connection.bindAsync(request, intermediateResponseHandler);
     }
 
     /**
@@ -228,8 +255,8 @@
      * The default implementation is to delegate.
      */
     @Override
-    public CompareResult compare(final String name, final String attributeDescription,
-            final String assertionValue) throws ErrorResultException {
+    public CompareResult compare(final String name, final String attributeDescription, final String assertionValue)
+            throws ErrorResultException {
         return connection.compare(name, attributeDescription, assertionValue);
     }
 
@@ -239,10 +266,19 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<CompareResult> compareAsync(final CompareRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super CompareResult> resultHandler) {
-        return connection.compareAsync(request, intermediateResponseHandler, resultHandler);
+    public FutureResult<CompareResult> compareAsync(final CompareRequest request) {
+        return connection.compareAsync(request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * The default implementation is to delegate.
+     */
+    @Override
+    public FutureResult<CompareResult> compareAsync(CompareRequest request,
+            IntermediateResponseHandler intermediateResponseHandler) {
+        return connection.compareAsync(request, intermediateResponseHandler);
     }
 
     /**
@@ -271,10 +307,19 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> deleteAsync(final DeleteRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
-        return connection.deleteAsync(request, intermediateResponseHandler, resultHandler);
+    public FutureResult<Result> deleteAsync(final DeleteRequest request) {
+        return connection.deleteAsync(request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * The default implementation is to delegate.
+     */
+    @Override
+    public FutureResult<Result> deleteAsync(DeleteRequest request,
+            IntermediateResponseHandler intermediateResponseHandler) {
+        return connection.deleteAsync(request, intermediateResponseHandler);
     }
 
     /**
@@ -293,8 +338,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public <R extends ExtendedResult> R extendedRequest(final ExtendedRequest<R> request)
-            throws ErrorResultException {
+    public <R extends ExtendedResult> R extendedRequest(final ExtendedRequest<R> request) throws ErrorResultException {
         return connection.extendedRequest(request);
     }
 
@@ -315,8 +359,8 @@
      * The default implementation is to delegate.
      */
     @Override
-    public GenericExtendedResult extendedRequest(final String requestName,
-            final ByteString requestValue) throws ErrorResultException {
+    public GenericExtendedResult extendedRequest(final String requestName, final ByteString requestValue)
+            throws ErrorResultException {
         return connection.extendedRequest(requestName, requestValue);
     }
 
@@ -326,11 +370,19 @@
      * The default implementation is to delegate.
      */
     @Override
-    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(
-            final ExtendedRequest<R> request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super R> resultHandler) {
-        return connection.extendedRequestAsync(request, intermediateResponseHandler, resultHandler);
+    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request) {
+        return connection.extendedRequestAsync(request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * The default implementation is to delegate.
+     */
+    @Override
+    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request,
+            IntermediateResponseHandler intermediateResponseHandler) {
+        return connection.extendedRequestAsync(request, intermediateResponseHandler);
     }
 
     /**
@@ -379,10 +431,19 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> modifyAsync(final ModifyRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
-        return connection.modifyAsync(request, intermediateResponseHandler, resultHandler);
+    public FutureResult<Result> modifyAsync(final ModifyRequest request) {
+        return connection.modifyAsync(request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * The default implementation is to delegate.
+     */
+    @Override
+    public FutureResult<Result> modifyAsync(ModifyRequest request,
+            IntermediateResponseHandler intermediateResponseHandler) {
+        return connection.modifyAsync(request, intermediateResponseHandler);
     }
 
     /**
@@ -411,10 +472,19 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
-        return connection.modifyDNAsync(request, intermediateResponseHandler, resultHandler);
+    public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request) {
+        return connection.modifyDNAsync(request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * The default implementation is to delegate.
+     */
+    @Override
+    public FutureResult<Result> modifyDNAsync(ModifyDNRequest request,
+            IntermediateResponseHandler intermediateResponseHandler) {
+        return modifyDNAsync(request, intermediateResponseHandler);
     }
 
     /**
@@ -446,9 +516,8 @@
      */
     @Override
     public FutureResult<SearchResultEntry> readEntryAsync(final DN name,
-            final Collection<String> attributeDescriptions,
-            final ResultHandler<? super SearchResultEntry> handler) {
-        return connection.readEntryAsync(name, attributeDescriptions, handler);
+            final Collection<String> attributeDescriptions) {
+        return connection.readEntryAsync(name, attributeDescriptions);
     }
 
     /**
@@ -523,9 +592,8 @@
      */
     @Override
     public FutureResult<Result> searchAsync(final SearchRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
             final SearchResultHandler resultHandler) {
-        return connection.searchAsync(request, intermediateResponseHandler, resultHandler);
+        return connection.searchAsync(request, resultHandler);
     }
 
     /**
@@ -534,8 +602,18 @@
      * The default implementation is to delegate.
      */
     @Override
-    public SearchResultEntry searchSingleEntry(final SearchRequest request)
-            throws ErrorResultException {
+    public FutureResult<Result> searchAsync(SearchRequest request,
+            IntermediateResponseHandler intermediateResponseHandler, SearchResultHandler entryHandler) {
+        return connection.searchAsync(request, intermediateResponseHandler, entryHandler);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * The default implementation is to delegate.
+     */
+    @Override
+    public SearchResultEntry searchSingleEntry(final SearchRequest request) throws ErrorResultException {
         return connection.searchSingleEntry(request);
     }
 
@@ -545,8 +623,8 @@
      * The default implementation is to delegate.
      */
     @Override
-    public SearchResultEntry searchSingleEntry(final String baseObject, final SearchScope scope,
-            final String filter, final String... attributeDescriptions) throws ErrorResultException {
+    public SearchResultEntry searchSingleEntry(final String baseObject, final SearchScope scope, final String filter,
+            final String... attributeDescriptions) throws ErrorResultException {
         return connection.searchSingleEntry(baseObject, scope, filter, attributeDescriptions);
     }
 
@@ -556,9 +634,8 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request,
-            final ResultHandler<? super SearchResultEntry> handler) {
-        return connection.searchSingleEntryAsync(request, handler);
+    public FutureResult<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request) {
+        return connection.searchSingleEntryAsync(request);
     }
 
     /**
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java
index c65ad4f..37ac6d9 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java
@@ -26,9 +26,6 @@
  */
 package org.forgerock.opendj.ldap;
 
-import static com.forgerock.opendj.util.StaticUtils.DEFAULT_SCHEDULER;
-import static org.forgerock.opendj.ldap.ErrorResultException.*;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -40,10 +37,16 @@
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.util.Reject;
+import org.forgerock.util.promise.AsyncFunction;
+import org.forgerock.util.promise.Promise;
 
-import com.forgerock.opendj.util.AsynchronousFutureResult;
 import com.forgerock.opendj.util.ReferenceCountedObject;
 
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+import static org.forgerock.util.promise.Promises.*;
+
+import static com.forgerock.opendj.util.StaticUtils.*;
+
 /**
  * An abstract load balancing algorithm providing monitoring and failover
  * capabilities.
@@ -58,7 +61,7 @@
 
         private final ConnectionFactory factory;
         private final AtomicBoolean isOperational = new AtomicBoolean(true);
-        private volatile FutureResult<?> pendingConnectFuture = null;
+        private volatile Promise<?, ErrorResultException> pendingConnectPromise;
         private final int index;
 
         private MonitoredConnectionFactory(final ConnectionFactory factory, final int index) {
@@ -90,43 +93,35 @@
         }
 
         @Override
-        public FutureResult<Connection> getConnectionAsync(
-                final ResultHandler<? super Connection> resultHandler) {
-            final AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> future =
-                    new AsynchronousFutureResult<Connection, ResultHandler<? super Connection>>(
-                            resultHandler);
-
-            final ResultHandler<Connection> failoverHandler = new ResultHandler<Connection>() {
-                @Override
-                public void handleErrorResult(final ErrorResultException error) {
-                    // Attempt failed - try next factory.
-                    notifyOffline(error);
-                    final int nextIndex = (index + 1) % monitoredFactories.size();
-                    try {
-                        final MonitoredConnectionFactory nextFactory =
-                                getMonitoredConnectionFactory(nextIndex);
-                        nextFactory.getConnectionAsync(future);
-                    } catch (final ErrorResultException e) {
-                        future.handleErrorResult(e);
+        public Promise<Connection, ErrorResultException> getConnectionAsync() {
+            return factory.getConnectionAsync().thenAsync(
+                new AsyncFunction<Connection, Connection, ErrorResultException>() {
+                    @Override
+                    public Promise<Connection, ErrorResultException> apply(Connection value)
+                            throws ErrorResultException {
+                        notifyOnline();
+                        return newSuccessfulPromise(value);
                     }
-                }
-
-                @Override
-                public void handleResult(final Connection result) {
-                    notifyOnline();
-                    future.handleResult(result);
-                }
-            };
-
-            factory.getConnectionAsync(failoverHandler);
-            return future;
+                },
+                new AsyncFunction<ErrorResultException, Connection, ErrorResultException>() {
+                    @Override
+                    public Promise<Connection, ErrorResultException> apply(ErrorResultException error)
+                            throws ErrorResultException {
+                        // Attempt failed - try next factory.
+                        notifyOffline(error);
+                        final int nextIndex = (index + 1) % monitoredFactories.size();
+                        return getMonitoredConnectionFactory(nextIndex).getConnectionAsync();
+                    }
+                });
         }
 
+
+
         /**
          * Handle monitoring connection request failure.
          */
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
+        public void handleError(final ErrorResultException error) {
             notifyOffline(error);
         }
 
@@ -150,10 +145,9 @@
          * pending monitoring request.
          */
         private synchronized void checkIfAvailable() {
-            if (!isOperational.get()
-                    && (pendingConnectFuture == null || pendingConnectFuture.isDone())) {
+            if (!isOperational.get() && (pendingConnectPromise == null || pendingConnectPromise.isDone())) {
                 logger.debug(LocalizableMessage.raw("Attempting reconnect to offline factory '%s'", this));
-                pendingConnectFuture = factory.getConnectionAsync(this);
+                pendingConnectPromise = factory.getConnectionAsync().onSuccess(this).onFailure(this);
             }
         }
 
@@ -279,7 +273,7 @@
      * Guarded by stateLock.
      */
     private ScheduledFuture<?> monitoringFuture;
-    private AtomicBoolean isClosed = new AtomicBoolean();
+    private final AtomicBoolean isClosed = new AtomicBoolean();
 
     AbstractLoadBalancingAlgorithm(final Collection<? extends ConnectionFactory> factories,
             final LoadBalancerEventListener listener, final long interval, final TimeUnit unit,
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractSynchronousConnection.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractSynchronousConnection.java
index c03967e..db438dd 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractSynchronousConnection.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractSynchronousConnection.java
@@ -21,7 +21,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2012 ForgeRock AS
+ *      Copyright 2012-2014 ForgeRock AS
  */
 
 package org.forgerock.opendj.ldap;
@@ -40,7 +40,7 @@
 import org.forgerock.opendj.ldap.responses.ExtendedResult;
 import org.forgerock.opendj.ldap.responses.Result;
 
-import com.forgerock.opendj.util.CompletedFutureResult;
+import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
 
 /**
  * An abstract connection whose asynchronous methods are implemented in terms of
@@ -74,113 +74,95 @@
      */
     @Override
     public FutureResult<Void> abandonAsync(final AbandonRequest request) {
-        throw new UnsupportedOperationException(
-                "Abandon requests are not supported for synchronous connections");
+        throw new UnsupportedOperationException("Abandon requests are not supported for synchronous connections");
     }
 
     @Override
     public FutureResult<Result> addAsync(final AddRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         try {
-            return onSuccess(add(request), resultHandler);
+            return onSuccess(add(request));
         } catch (final ErrorResultException e) {
-            return onFailure(e, resultHandler);
+            return onFailure(e);
         }
     }
 
     @Override
     public FutureResult<BindResult> bindAsync(final BindRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super BindResult> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         try {
-            return onSuccess(bind(request), resultHandler);
+            return onSuccess(bind(request));
         } catch (final ErrorResultException e) {
-            return onFailure(e, resultHandler);
+            return onFailure(e);
         }
     }
 
     @Override
     public FutureResult<CompareResult> compareAsync(final CompareRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super CompareResult> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         try {
-            return onSuccess(compare(request), resultHandler);
+            return onSuccess(compare(request));
         } catch (final ErrorResultException e) {
-            return onFailure(e, resultHandler);
+            return onFailure(e);
         }
     }
 
     @Override
     public FutureResult<Result> deleteAsync(final DeleteRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         try {
-            return onSuccess(delete(request), resultHandler);
+            return onSuccess(delete(request));
         } catch (final ErrorResultException e) {
-            return onFailure(e, resultHandler);
+            return onFailure(e);
         }
     }
 
     @Override
-    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(
-            final ExtendedRequest<R> request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super R> resultHandler) {
+    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request,
+            final IntermediateResponseHandler intermediateResponseHandler) {
         try {
-            return onSuccess(extendedRequest(request, intermediateResponseHandler), resultHandler);
+            return onSuccess(extendedRequest(request, intermediateResponseHandler));
         } catch (final ErrorResultException e) {
-            return onFailure(e, resultHandler);
+            return onFailure(e);
         }
     }
 
     @Override
     public FutureResult<Result> modifyAsync(final ModifyRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         try {
-            return onSuccess(modify(request), resultHandler);
+            return onSuccess(modify(request));
         } catch (final ErrorResultException e) {
-            return onFailure(e, resultHandler);
+            return onFailure(e);
         }
     }
 
     @Override
     public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         try {
-            return onSuccess(modifyDN(request), resultHandler);
+            return onSuccess(modifyDN(request));
         } catch (final ErrorResultException e) {
-            return onFailure(e, resultHandler);
+            return onFailure(e);
         }
     }
 
     @Override
     public FutureResult<Result> searchAsync(final SearchRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final SearchResultHandler resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) {
         try {
-            return onSuccess(search(request, resultHandler), resultHandler);
+            return onSuccess(search(request, entryHandler));
         } catch (final ErrorResultException e) {
-            return onFailure(e, resultHandler);
+            return onFailure(e);
         }
     }
 
-    private <R extends Result> FutureResult<R> onFailure(final ErrorResultException e,
-            final ResultHandler<? super R> resultHandler) {
-        if (resultHandler != null) {
-            resultHandler.handleErrorResult(e);
-        }
-        return new CompletedFutureResult<R>(e);
+    private <R extends Result> FutureResult<R> onFailure(final ErrorResultException e) {
+        return newFailedFutureResult(e);
     }
 
-    private <R extends Result> FutureResult<R> onSuccess(final R result,
-            final ResultHandler<? super R> resultHandler) {
-        if (resultHandler != null) {
-            resultHandler.handleResult(result);
-        }
-        return new CompletedFutureResult<R>(result);
+    private <R extends Result> FutureResult<R> onSuccess(final R result) {
+        return newSuccessfulFutureResult(result);
     }
 
 }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/AuthenticatedConnectionFactory.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/AuthenticatedConnectionFactory.java
index d0eb99d..e3fa07b 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/AuthenticatedConnectionFactory.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/AuthenticatedConnectionFactory.java
@@ -22,16 +22,20 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap;
 
+import java.util.concurrent.atomic.AtomicReference;
+
 import org.forgerock.opendj.ldap.requests.BindRequest;
 import org.forgerock.opendj.ldap.responses.BindResult;
+import org.forgerock.util.promise.AsyncFunction;
+import org.forgerock.util.promise.Function;
+import org.forgerock.util.promise.Promise;
 
-import com.forgerock.opendj.util.FutureResultTransformer;
-import com.forgerock.opendj.util.RecursiveFutureResult;
+import static org.forgerock.util.Utils.*;
 
 /**
  * An authenticated connection factory can be used to create pre-authenticated
@@ -57,28 +61,27 @@
             super(connection);
         }
 
-        /*
+        /**
          * Bind operations are not supported by pre-authenticated connections.
          * These methods will always throw {@code UnsupportedOperationException}.
          */
-
         public FutureResult<BindResult> bindAsync(final BindRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler,
                 final ResultHandler<? super BindResult> resultHandler) {
             throw new UnsupportedOperationException();
         }
 
-
+        @Override
         public BindResult bind(BindRequest request) throws ErrorResultException {
             throw new UnsupportedOperationException();
         }
 
-
+        @Override
         public BindResult bind(String name, char[] password) throws ErrorResultException {
             throw new UnsupportedOperationException();
         }
 
-
+        @Override
         public String toString() {
             StringBuilder builder = new StringBuilder();
             builder.append("AuthenticatedConnection(");
@@ -89,52 +92,6 @@
 
     }
 
-    private static final class FutureResultImpl {
-        private final FutureResultTransformer<BindResult, Connection> futureBindResult;
-        private final RecursiveFutureResult<Connection, BindResult> futureConnectionResult;
-        private final BindRequest bindRequest;
-        private Connection connection;
-
-        private FutureResultImpl(final BindRequest request,
-                final ResultHandler<? super Connection> handler) {
-            this.bindRequest = request;
-            this.futureBindResult = new FutureResultTransformer<BindResult, Connection>(handler) {
-
-                @Override
-                protected ErrorResultException transformErrorResult(
-                        final ErrorResultException errorResult) {
-                    // Ensure that the connection is closed.
-                    if (connection != null) {
-                        connection.close();
-                        connection = null;
-                    }
-                    return errorResult;
-                }
-
-                @Override
-                protected Connection transformResult(final BindResult result)
-                        throws ErrorResultException {
-                    return new AuthenticatedConnection(connection);
-                }
-
-            };
-            this.futureConnectionResult =
-                    new RecursiveFutureResult<Connection, BindResult>(futureBindResult) {
-
-                        @Override
-                        protected FutureResult<? extends BindResult> chainResult(
-                                final Connection innerResult,
-                                final ResultHandler<? super BindResult> handler)
-                                throws ErrorResultException {
-                            connection = innerResult;
-                            return connection.bindAsync(bindRequest, null, handler);
-                        }
-                    };
-            futureBindResult.setFutureResult(futureConnectionResult);
-        }
-
-    }
-
     private final BindRequest request;
     private final ConnectionFactory parentFactory;
 
@@ -162,6 +119,7 @@
         parentFactory.close();
     }
 
+    @Override
     public Connection getConnection() throws ErrorResultException {
         final Connection connection = parentFactory.getConnection();
         boolean bindSucceeded = false;
@@ -181,25 +139,44 @@
         return new AuthenticatedConnection(connection);
     }
 
-
     @Override
-    public FutureResult<Connection> getConnectionAsync(
-            final ResultHandler<? super Connection> handler) {
-        final FutureResultImpl future = new FutureResultImpl(request, handler);
-        future.futureConnectionResult.setFutureResult(parentFactory
-                .getConnectionAsync(future.futureConnectionResult));
-        return future.futureBindResult;
+    public Promise<Connection, ErrorResultException> getConnectionAsync() {
+        final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>();
+        return parentFactory.getConnectionAsync()
+            .thenAsync(
+                    new AsyncFunction<Connection, BindResult, ErrorResultException>() {
+                        @Override
+                        public Promise<BindResult, ErrorResultException> apply(final Connection connection)
+                                throws ErrorResultException {
+                            connectionHolder.set(connection);
+                            return connection.bindAsync(request);
+                        }
+                    }
+            ).then(
+                    new Function<BindResult, Connection, ErrorResultException>() {
+                        @Override
+                        public Connection apply(BindResult result) throws ErrorResultException {
+                            return new AuthenticatedConnection(connectionHolder.get());
+                        }
+                    },
+                    new Function<ErrorResultException, Connection, ErrorResultException>() {
+                        @Override
+                        public Connection apply(ErrorResultException error) throws ErrorResultException {
+                            closeSilently(connectionHolder.get());
+                            throw error;
+                        }
+                    }
+            );
     }
 
-
+    @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder();
         builder.append("AuthenticatedConnectionFactory(");
-        builder.append(String.valueOf(parentFactory));
+        builder.append(parentFactory);
         builder.append(", ");
         builder.append(request);
         builder.append(')');
         return builder.toString();
     }
-
 }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/CachedConnectionPool.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/CachedConnectionPool.java
index 769b8cf..3d7319b 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/CachedConnectionPool.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/CachedConnectionPool.java
@@ -26,13 +26,6 @@
  */
 package org.forgerock.opendj.ldap;
 
-import static com.forgerock.opendj.util.StaticUtils.DEBUG_ENABLED;
-import static com.forgerock.opendj.util.StaticUtils.DEFAULT_SCHEDULER;
-import static com.forgerock.opendj.util.StaticUtils.getStackTraceIfDebugEnabled;
-import static com.forgerock.opendj.util.StaticUtils.logIfDebugEnabled;
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_CONNECTION_POOL_CLOSING;
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-
 import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
@@ -66,12 +59,19 @@
 import org.forgerock.opendj.ldif.ChangeRecord;
 import org.forgerock.opendj.ldif.ConnectionEntryReader;
 import org.forgerock.util.Reject;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.SuccessHandler;
 
-import com.forgerock.opendj.util.AsynchronousFutureResult;
-import com.forgerock.opendj.util.CompletedFutureResult;
 import com.forgerock.opendj.util.ReferenceCountedObject;
 import com.forgerock.opendj.util.TimeSource;
 
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+import static org.forgerock.util.promise.Promises.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
+import static com.forgerock.opendj.util.StaticUtils.*;
+
 /**
  * A connection pool implementation which maintains a cache of pooled
  * connections with a configurable core pool size, maximum size, and expiration
@@ -80,13 +80,27 @@
 final class CachedConnectionPool implements ConnectionPool {
 
     /**
-     * This result handler is invoked when an attempt to add a new connection to
-     * the pool completes.
+     * This success handler is invoked when an attempt to add a new connection
+     * to the pool completes.
      */
-    private final class ConnectionResultHandler implements ResultHandler<Connection> {
-
+    private final class ConnectionSuccessHandler implements SuccessHandler<Connection> {
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
+        public void handleResult(final Connection connection) {
+            logger.debug(LocalizableMessage.raw(
+                    "Connection attempt succeeded:  availableConnections=%d, maxPoolSize=%d",
+                     currentPoolSize(), maxPoolSize));
+            pendingConnectionAttempts.decrementAndGet();
+            publishConnection(connection);
+        }
+    }
+
+    /**
+     * This failure handler is invoked when an attempt to add a new connection
+     * to the pool ended in error.
+     */
+    private final class ConnectionFailureHandler implements FailureHandler<ErrorResultException> {
+        @Override
+        public void handleError(final ErrorResultException error) {
             // Connection attempt failed, so decrease the pool size.
             pendingConnectionAttempts.decrementAndGet();
             availableConnections.release();
@@ -114,18 +128,9 @@
                 }
             }
             for (QueueElement waitingFuture : waitingFutures) {
-                waitingFuture.getWaitingFuture().handleErrorResult(error);
+                waitingFuture.getWaitingFuture().handleError(error);
             }
         }
-
-        @Override
-        public void handleResult(final Connection connection) {
-            logger.debug(LocalizableMessage.raw(
-                    "Connection attempt succeeded:  availableConnections=%d, maxPoolSize=%d",
-                    currentPoolSize(), maxPoolSize));
-            pendingConnectionAttempts.decrementAndGet();
-            publishConnection(connection);
-        }
     }
 
     /**
@@ -167,10 +172,14 @@
         }
 
         @Override
+        public FutureResult<Result> addAsync(AddRequest request) {
+            return addAsync(request, null);
+        }
+
+        @Override
         public FutureResult<Result> addAsync(final AddRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super Result> resultHandler) {
-            return checkState().addAsync(request, intermediateResponseHandler, resultHandler);
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            return checkState().addAsync(request, intermediateResponseHandler);
         }
 
         @Override
@@ -211,11 +220,14 @@
         }
 
         @Override
+        public FutureResult<Result> applyChangeAsync(final ChangeRecord request) {
+            return checkState().applyChangeAsync(request, null);
+        }
+
+        @Override
         public FutureResult<Result> applyChangeAsync(final ChangeRecord request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super Result> resultHandler) {
-            return checkState().applyChangeAsync(request, intermediateResponseHandler,
-                    resultHandler);
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            return checkState().applyChangeAsync(request, intermediateResponseHandler);
         }
 
         @Override
@@ -230,10 +242,14 @@
         }
 
         @Override
+        public FutureResult<BindResult> bindAsync(BindRequest request) {
+            return bindAsync(request, null);
+        }
+
+        @Override
         public FutureResult<BindResult> bindAsync(final BindRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super BindResult> resultHandler) {
-            return checkState().bindAsync(request, intermediateResponseHandler, resultHandler);
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            return checkState().bindAsync(request, intermediateResponseHandler);
         }
 
         @Override
@@ -268,7 +284,7 @@
                  */
                 connection.close();
                 pendingConnectionAttempts.incrementAndGet();
-                factory.getConnectionAsync(connectionResultHandler);
+                factory.getConnectionAsync().onSuccess(connectionSuccessHandler).onFailure(connectionFailureHandler);
 
                 logger.debug(LocalizableMessage.raw(
                         "Connection no longer valid: availableConnections=%d, maxPoolSize=%d",
@@ -300,10 +316,14 @@
         }
 
         @Override
+        public FutureResult<CompareResult> compareAsync(CompareRequest request) {
+            return compareAsync(request, null);
+        }
+
+        @Override
         public FutureResult<CompareResult> compareAsync(final CompareRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super CompareResult> resultHandler) {
-            return checkState().compareAsync(request, intermediateResponseHandler, resultHandler);
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            return checkState().compareAsync(request, intermediateResponseHandler);
         }
 
         @Override
@@ -317,10 +337,14 @@
         }
 
         @Override
+        public FutureResult<Result> deleteAsync(DeleteRequest request) {
+            return deleteAsync(request, null);
+        }
+
+        @Override
         public FutureResult<Result> deleteAsync(final DeleteRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super Result> resultHandler) {
-            return checkState().deleteAsync(request, intermediateResponseHandler, resultHandler);
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            return checkState().deleteAsync(request, intermediateResponseHandler);
         }
 
         @Override
@@ -347,12 +371,14 @@
         }
 
         @Override
-        public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(
-                final ExtendedRequest<R> request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super R> resultHandler) {
-            return checkState().extendedRequestAsync(request, intermediateResponseHandler,
-                    resultHandler);
+        public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request) {
+            return extendedRequestAsync(request, null);
+        }
+
+        @Override
+        public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request,
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            return checkState().extendedRequestAsync(request, intermediateResponseHandler);
         }
 
         @Override
@@ -367,8 +393,7 @@
         }
 
         @Override
-        public void handleConnectionError(final boolean isDisconnectNotification,
-                final ErrorResultException error) {
+        public void handleConnectionError(final boolean isDisconnectNotification, final ErrorResultException error) {
             final List<ConnectionEventListener> tmpListeners;
             synchronized (stateLock) {
                 tmpListeners = listeners;
@@ -416,10 +441,14 @@
         }
 
         @Override
+        public FutureResult<Result> modifyAsync(ModifyRequest request) {
+            return modifyAsync(request, null);
+        }
+
+        @Override
         public FutureResult<Result> modifyAsync(final ModifyRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super Result> resultHandler) {
-            return checkState().modifyAsync(request, intermediateResponseHandler, resultHandler);
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            return checkState().modifyAsync(request, intermediateResponseHandler);
         }
 
         @Override
@@ -433,10 +462,14 @@
         }
 
         @Override
+        public FutureResult<Result> modifyDNAsync(ModifyDNRequest request) {
+            return modifyDNAsync(request, null);
+        }
+
+        @Override
         public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super Result> resultHandler) {
-            return checkState().modifyDNAsync(request, intermediateResponseHandler, resultHandler);
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            return checkState().modifyDNAsync(request, intermediateResponseHandler);
         }
 
         @Override
@@ -453,9 +486,8 @@
 
         @Override
         public FutureResult<SearchResultEntry> readEntryAsync(final DN name,
-                final Collection<String> attributeDescriptions,
-                final ResultHandler<? super SearchResultEntry> handler) {
-            return checkState().readEntryAsync(name, attributeDescriptions, handler);
+                final Collection<String> attributeDescriptions) {
+            return checkState().readEntryAsync(name, attributeDescriptions);
         }
 
         @Override
@@ -474,16 +506,14 @@
         }
 
         @Override
-        public Result search(final SearchRequest request,
-                final Collection<? super SearchResultEntry> entries) throws ErrorResultException {
+        public Result search(final SearchRequest request, final Collection<? super SearchResultEntry> entries)
+                throws ErrorResultException {
             return checkState().search(request, entries);
         }
 
         @Override
-        public Result search(final SearchRequest request,
-                final Collection<? super SearchResultEntry> entries,
-                final Collection<? super SearchResultReference> references)
-                throws ErrorResultException {
+        public Result search(final SearchRequest request, final Collection<? super SearchResultEntry> entries,
+                final Collection<? super SearchResultReference> references) throws ErrorResultException {
             return checkState().search(request, entries, references);
         }
 
@@ -494,35 +524,36 @@
         }
 
         @Override
-        public ConnectionEntryReader search(final String baseObject, final SearchScope scope,
-                final String filter, final String... attributeDescriptions) {
+        public ConnectionEntryReader search(final String baseObject, final SearchScope scope, final String filter,
+                final String... attributeDescriptions) {
             return checkState().search(baseObject, scope, filter, attributeDescriptions);
         }
 
         @Override
-        public FutureResult<Result> searchAsync(final SearchRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final SearchResultHandler resultHandler) {
-            return checkState().searchAsync(request, intermediateResponseHandler, resultHandler);
+        public FutureResult<Result> searchAsync(SearchRequest request, SearchResultHandler resultHandler) {
+            return searchAsync(request, null, resultHandler);
         }
 
         @Override
-        public SearchResultEntry searchSingleEntry(final SearchRequest request)
-                throws ErrorResultException {
+        public FutureResult<Result> searchAsync(final SearchRequest request,
+                final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) {
+            return checkState().searchAsync(request, intermediateResponseHandler, entryHandler);
+        }
+
+        @Override
+        public SearchResultEntry searchSingleEntry(final SearchRequest request) throws ErrorResultException {
             return checkState().searchSingleEntry(request);
         }
 
         @Override
-        public SearchResultEntry searchSingleEntry(final String baseObject,
-                final SearchScope scope, final String filter, final String... attributeDescriptions)
-                throws ErrorResultException {
+        public SearchResultEntry searchSingleEntry(final String baseObject, final SearchScope scope,
+                final String filter, final String... attributeDescriptions) throws ErrorResultException {
             return checkState().searchSingleEntry(baseObject, scope, filter, attributeDescriptions);
         }
 
         @Override
-        public FutureResult<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request,
-                final ResultHandler<? super SearchResultEntry> handler) {
-            return checkState().searchSingleEntryAsync(request, handler);
+        public FutureResult<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request) {
+            return checkState().searchSingleEntryAsync(request);
         }
 
         @Override
@@ -623,11 +654,9 @@
             this.stack = null;
         }
 
-        QueueElement(final ResultHandler<? super Connection> handler, final long timestampMillis,
-                final StackTraceElement[] stack) {
-            this.value =
-                    new AsynchronousFutureResult<Connection, ResultHandler<? super Connection>>(
-                            handler);
+        QueueElement(final long timestampMillis,
+            final StackTraceElement[] stack) {
+            this.value = new FutureResultImpl<Connection>();
             this.timestampMillis = timestampMillis;
             this.stack = stack;
         }
@@ -650,8 +679,8 @@
         }
 
         @SuppressWarnings("unchecked")
-        AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> getWaitingFuture() {
-            return (AsynchronousFutureResult<Connection, ResultHandler<? super Connection>>) value;
+        FutureResultImpl<Connection> getWaitingFuture() {
+            return (FutureResultImpl<Connection>) value;
         }
 
         boolean hasTimedOut(final long timeLimitMillis) {
@@ -659,7 +688,7 @@
         }
 
         boolean isWaitingFuture() {
-            return value instanceof AsynchronousFutureResult;
+            return value instanceof FutureResultImpl;
         }
     }
 
@@ -672,7 +701,8 @@
     TimeSource timeSource = TimeSource.DEFAULT;
 
     private final Semaphore availableConnections;
-    private final ResultHandler<Connection> connectionResultHandler = new ConnectionResultHandler();
+    private final SuccessHandler<Connection> connectionSuccessHandler = new ConnectionSuccessHandler();
+    private final FailureHandler<ErrorResultException> connectionFailureHandler = new ConnectionFailureHandler();
     private final int corePoolSize;
     private final ConnectionFactory factory;
     private boolean isClosed = false;
@@ -760,15 +790,14 @@
     @Override
     public Connection getConnection() throws ErrorResultException {
         try {
-            return getConnectionAsync(null).get();
+            return getConnectionAsync().getOrThrow();
         } catch (final InterruptedException e) {
             throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, e);
         }
     }
 
     @Override
-    public FutureResult<Connection> getConnectionAsync(
-            final ResultHandler<? super Connection> handler) {
+    public Promise<Connection, ErrorResultException> getConnectionAsync() {
         // Loop while iterating through stale connections (see OPENDJ-590).
         for (;;) {
             final QueueElement holder;
@@ -778,9 +807,7 @@
                 } else if (hasWaitingConnections()) {
                     holder = queue.removeFirst();
                 } else {
-                    holder =
-                            new QueueElement(handler, timeSource.currentTimeMillis(),
-                                    getStackTraceIfDebugEnabled());
+                    holder = new QueueElement(timeSource.currentTimeMillis(), getStackTraceIfDebugEnabled());
                     queue.add(holder);
                 }
             }
@@ -790,7 +817,8 @@
                 final FutureResult<Connection> future = holder.getWaitingFuture();
                 if (!future.isDone() && availableConnections.tryAcquire()) {
                     pendingConnectionAttempts.incrementAndGet();
-                    factory.getConnectionAsync(connectionResultHandler);
+                    factory.getConnectionAsync().onSuccess(connectionSuccessHandler)
+                                                .onFailure(connectionFailureHandler);
                 }
                 return future;
             }
@@ -798,19 +826,14 @@
             // There was a completed connection attempt.
             final Connection connection = holder.getWaitingConnection();
             if (connection.isValid()) {
-                final PooledConnection pooledConnection =
-                        newPooledConnection(connection, getStackTraceIfDebugEnabled());
-                if (handler != null) {
-                    handler.handleResult(pooledConnection);
-                }
-                return new CompletedFutureResult<Connection>(pooledConnection);
+                final Connection pooledConnection = newPooledConnection(connection, getStackTraceIfDebugEnabled());
+                return newSuccessfulPromise(pooledConnection);
             } else {
                 // Close the stale connection and try again.
                 connection.close();
                 availableConnections.release();
 
-                logger.debug(LocalizableMessage.raw(
-                        "Connection no longer valid: availableConnections=%d, poolSize=%d",
+                logger.debug(LocalizableMessage.raw("Connection no longer valid: availableConnections=%d, poolSize=%d",
                         currentPoolSize(), maxPoolSize));
             }
         }
@@ -892,7 +915,7 @@
                 final ErrorResultException e =
                         ErrorResultException.newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED,
                                 ERR_CONNECTION_POOL_CLOSING.get(toString()).toString());
-                holder.getWaitingFuture().handleErrorResult(e);
+                holder.getWaitingFuture().handleError(e);
 
                 logger.debug(LocalizableMessage.raw(
                         "Connection attempt failed: availableConnections=%d, maxPoolSize=%d",
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/Connection.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/Connection.java
index 471bb50..2bd1b48 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/Connection.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/Connection.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS
+ *      Portions copyright 2011-2014 ForgeRock AS
  */
 
 package org.forgerock.opendj.ldap;
@@ -258,13 +258,6 @@
      *
      * @param request
      *            The add request.
-     * @param intermediateResponseHandler
-     *            An intermediate response handler which can be used to process
-     *            any intermediate responses as they are received, may be
-     *            {@code null}.
-     * @param resultHandler
-     *            A result handler which can be used to asynchronously process
-     *            the operation result when it is received, may be {@code null}.
      * @return A future representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support add operations.
@@ -274,9 +267,28 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> addAsync(AddRequest request,
-            IntermediateResponseHandler intermediateResponseHandler,
-            ResultHandler<? super Result> resultHandler);
+    FutureResult<Result> addAsync(AddRequest request);
+
+    /**
+     * Asynchronously adds an entry to the Directory Server using the provided
+     * add request.
+     *
+     * @param request
+     *            The add request.
+     * @param intermediateResponseHandler
+     *            An intermediate response handler which can be used to process
+     *            any intermediate responses as they are received, may be
+     *            {@code null}.
+     * @return A future representing the result of the operation.
+     * @throws UnsupportedOperationException
+     *             If this connection does not support add operations.
+     * @throws IllegalStateException
+     *             If this connection has already been closed, i.e. if
+     *             {@code isClosed() == true}.
+     * @throws NullPointerException
+     *             If {@code request} was {@code null}.
+     */
+    FutureResult<Result> addAsync(AddRequest request, IntermediateResponseHandler intermediateResponseHandler);
 
     /**
      * Registers the provided connection event listener so that it will be
@@ -320,13 +332,28 @@
      *
      * @param request
      *            The change request.
+     * @return A future representing the result of the operation.
+     * @throws UnsupportedOperationException
+     *             If this connection does not support the provided change
+     *             request.
+     * @throws IllegalStateException
+     *             If this connection has already been closed, i.e. if
+     *             {@code isClosed() == true}.
+     * @throws NullPointerException
+     *             If {@code request} was {@code null}.
+     */
+    FutureResult<Result> applyChangeAsync(ChangeRecord request);
+
+    /**
+     * Asynchronously applies the provided change request to the Directory
+     * Server.
+     *
+     * @param request
+     *            The change request.
      * @param intermediateResponseHandler
      *            An intermediate response handler which can be used to process
      *            any intermediate responses as they are received, may be
      *            {@code null}.
-     * @param resultHandler
-     *            A result handler which can be used to asynchronously process
-     *            the operation result when it is received, may be {@code null}.
      * @return A future representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support the provided change
@@ -338,8 +365,7 @@
      *             If {@code request} was {@code null}.
      */
     FutureResult<Result> applyChangeAsync(ChangeRecord request,
-            IntermediateResponseHandler intermediateResponseHandler,
-            ResultHandler<? super Result> resultHandler);
+        IntermediateResponseHandler intermediateResponseHandler);
 
     /**
      * Authenticates to the Directory Server using the provided bind request.
@@ -400,13 +426,6 @@
      *
      * @param request
      *            The bind request.
-     * @param intermediateResponseHandler
-     *            An intermediate response handler which can be used to process
-     *            any intermediate responses as they are received, may be
-     *            {@code null}.
-     * @param resultHandler
-     *            A result handler which can be used to asynchronously process
-     *            the operation result when it is received, may be {@code null}.
      * @return A future representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support bind operations.
@@ -416,9 +435,28 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<BindResult> bindAsync(BindRequest request,
-            IntermediateResponseHandler intermediateResponseHandler,
-            ResultHandler<? super BindResult> resultHandler);
+    FutureResult<BindResult> bindAsync(BindRequest request);
+
+    /**
+     * Asynchronously authenticates to the Directory Server using the provided
+     * bind request.
+     *
+     * @param request
+     *            The bind request.
+     * @param intermediateResponseHandler
+     *            An intermediate response handler which can be used to process
+     *            any intermediate responses as they are received, may be
+     *            {@code null}.
+     * @return A future representing the result of the operation.
+     * @throws UnsupportedOperationException
+     *             If this connection does not support bind operations.
+     * @throws IllegalStateException
+     *             If this connection has already been closed, i.e. if
+     *             {@code isClosed() == true}.
+     * @throws NullPointerException
+     *             If {@code request} was {@code null}.
+     */
+    FutureResult<BindResult> bindAsync(BindRequest request, IntermediateResponseHandler intermediateResponseHandler);
 
     /**
      * Releases any resources associated with this connection. For physical
@@ -530,13 +568,27 @@
      *
      * @param request
      *            The compare request.
+     * @return A future representing the result of the operation.
+     * @throws UnsupportedOperationException
+     *             If this connection does not support compare operations.
+     * @throws IllegalStateException
+     *             If this connection has already been closed, i.e. if
+     *             {@code isClosed() == true}.
+     * @throws NullPointerException
+     *             If {@code request} was {@code null}.
+     */
+    FutureResult<CompareResult> compareAsync(CompareRequest request);
+
+    /**
+     * Asynchronously compares an entry in the Directory Server using the
+     * provided compare request.
+     *
+     * @param request
+     *            The compare request.
      * @param intermediateResponseHandler
      *            An intermediate response handler which can be used to process
      *            any intermediate responses as they are received, may be
      *            {@code null}.
-     * @param resultHandler
-     *            A result handler which can be used to asynchronously process
-     *            the operation result when it is received, may be {@code null}.
      * @return A future representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support compare operations.
@@ -547,8 +599,7 @@
      *             If {@code request} was {@code null}.
      */
     FutureResult<CompareResult> compareAsync(CompareRequest request,
-            IntermediateResponseHandler intermediateResponseHandler,
-            ResultHandler<? super CompareResult> resultHandler);
+        IntermediateResponseHandler intermediateResponseHandler);
 
     /**
      * Deletes an entry from the Directory Server using the provided delete
@@ -636,13 +687,6 @@
      *
      * @param request
      *            The delete request.
-     * @param intermediateResponseHandler
-     *            An intermediate response handler which can be used to process
-     *            any intermediate responses as they are received, may be
-     *            {@code null}.
-     * @param resultHandler
-     *            A result handler which can be used to asynchronously process
-     *            the operation result when it is received, may be {@code null}.
      * @return A future representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support delete operations.
@@ -652,9 +696,28 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> deleteAsync(DeleteRequest request,
-            IntermediateResponseHandler intermediateResponseHandler,
-            ResultHandler<? super Result> resultHandler);
+    FutureResult<Result> deleteAsync(DeleteRequest request);
+
+    /**
+     * Asynchronously deletes an entry from the Directory Server using the
+     * provided delete request.
+     *
+     * @param request
+     *            The delete request.
+     * @param intermediateResponseHandler
+     *            An intermediate response handler which can be used to process
+     *            any intermediate responses as they are received, may be
+     *            {@code null}.
+     * @return A future representing the result of the operation.
+     * @throws UnsupportedOperationException
+     *             If this connection does not support delete operations.
+     * @throws IllegalStateException
+     *             If this connection has already been closed, i.e. if
+     *             {@code isClosed() == true}.
+     * @throws NullPointerException
+     *             If {@code request} was {@code null}.
+     */
+    FutureResult<Result> deleteAsync(DeleteRequest request, IntermediateResponseHandler intermediateResponseHandler);
 
     /**
      * Requests that the Directory Server performs the provided extended
@@ -703,8 +766,8 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    <R extends ExtendedResult> R extendedRequest(ExtendedRequest<R> request,
-            IntermediateResponseHandler handler) throws ErrorResultException;
+    <R extends ExtendedResult> R extendedRequest(ExtendedRequest<R> request, IntermediateResponseHandler handler)
+            throws ErrorResultException;
 
     /**
      * Requests that the Directory Server performs the provided extended
@@ -746,13 +809,29 @@
      *            The type of result returned by the extended request.
      * @param request
      *            The extended request.
+     * @return A future representing the result of the operation.
+     * @throws UnsupportedOperationException
+     *             If this connection does not support extended operations.
+     * @throws IllegalStateException
+     *             If this connection has already been closed, i.e. if
+     *             {@code isClosed() == true}.
+     * @throws NullPointerException
+     *             If {@code request} was {@code null}.
+     */
+    <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request);
+
+    /**
+     * Asynchronously performs the provided extended request in the Directory
+     * Server.
+     *
+     * @param <R>
+     *            The type of result returned by the extended request.
+     * @param request
+     *            The extended request.
      * @param intermediateResponseHandler
      *            An intermediate response handler which can be used to process
      *            any intermediate responses as they are received, may be
      *            {@code null}.
-     * @param resultHandler
-     *            A result handler which can be used to asynchronously process
-     *            the operation result when it is received, may be {@code null}.
      * @return A future representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support extended operations.
@@ -763,8 +842,7 @@
      *             If {@code request} was {@code null}.
      */
     <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request,
-            IntermediateResponseHandler intermediateResponseHandler,
-            ResultHandler<? super R> resultHandler);
+        IntermediateResponseHandler intermediateResponseHandler);
 
     /**
      * Indicates whether or not this connection has been explicitly closed by
@@ -845,13 +923,6 @@
      *
      * @param request
      *            The modify request.
-     * @param intermediateResponseHandler
-     *            An intermediate response handler which can be used to process
-     *            any intermediate responses as they are received, may be
-     *            {@code null}.
-     * @param resultHandler
-     *            A result handler which can be used to asynchronously process
-     *            the operation result when it is received, may be {@code null}.
      * @return A future representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support modify operations.
@@ -861,9 +932,28 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> modifyAsync(ModifyRequest request,
-            IntermediateResponseHandler intermediateResponseHandler,
-            ResultHandler<? super Result> resultHandler);
+    FutureResult<Result> modifyAsync(ModifyRequest request);
+
+    /**
+     * Asynchronously modifies an entry in the Directory Server using the
+     * provided modify request.
+     *
+     * @param request
+     *            The modify request.
+     * @param intermediateResponseHandler
+     *            An intermediate response handler which can be used to process
+     *            any intermediate responses as they are received, may be
+     *            {@code null}.
+     * @return A future representing the result of the operation.
+     * @throws UnsupportedOperationException
+     *             If this connection does not support modify operations.
+     * @throws IllegalStateException
+     *             If this connection has already been closed, i.e. if
+     *             {@code isClosed() == true}.
+     * @throws NullPointerException
+     *             If {@code request} was {@code null}.
+     */
+    FutureResult<Result> modifyAsync(ModifyRequest request, IntermediateResponseHandler intermediateResponseHandler);
 
     /**
      * Renames an entry in the Directory Server using the provided modify DN
@@ -923,13 +1013,27 @@
      *
      * @param request
      *            The modify DN request.
+     * @return A future representing the result of the operation.
+     * @throws UnsupportedOperationException
+     *             If this connection does not support modify DN operations.
+     * @throws IllegalStateException
+     *             If this connection has already been closed, i.e. if
+     *             {@code isClosed() == true}.
+     * @throws NullPointerException
+     *             If {@code request} was {@code null}.
+     */
+    FutureResult<Result> modifyDNAsync(ModifyDNRequest request);
+
+    /**
+     * Asynchronously renames an entry in the Directory Server using the
+     * provided modify DN request.
+     *
+     * @param request
+     *            The modify DN request.
      * @param intermediateResponseHandler
      *            An intermediate response handler which can be used to process
      *            any intermediate responses as they are received, may be
      *            {@code null}.
-     * @param resultHandler
-     *            A result handler which can be used to asynchronously process
-     *            the operation result when it is received, may be {@code null}.
      * @return A future representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support modify DN operations.
@@ -940,8 +1044,7 @@
      *             If {@code request} was {@code null}.
      */
     FutureResult<Result> modifyDNAsync(ModifyDNRequest request,
-            IntermediateResponseHandler intermediateResponseHandler,
-            ResultHandler<? super Result> resultHandler);
+        IntermediateResponseHandler intermediateResponseHandler);
 
     /**
      * Reads the named entry from the Directory Server.
@@ -953,8 +1056,8 @@
      * This method is equivalent to the following code:
      *
      * <pre>
-     * SearchRequest request =
-     *         new SearchRequest(name, SearchScope.BASE_OBJECT, &quot;(objectClass=*)&quot;, attributeDescriptions);
+     * SearchRequest request = new SearchRequest(name, SearchScope.BASE_OBJECT,
+     * &quot;(objectClass=*)&quot;, attributeDescriptions);
      * connection.searchSingleEntry(request);
      * </pre>
      *
@@ -1037,9 +1140,6 @@
      *            The names of the attributes to be included with the entry,
      *            which may be {@code null} or empty indicating that all user
      *            attributes should be returned.
-     * @param handler
-     *            A result handler which can be used to asynchronously process
-     *            the operation result when it is received, may be {@code null}.
      * @return A future representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support search operations.
@@ -1049,9 +1149,7 @@
      * @throws NullPointerException
      *             If the {@code name} was {@code null}.
      */
-    FutureResult<SearchResultEntry> readEntryAsync(DN name,
-            Collection<String> attributeDescriptions,
-            ResultHandler<? super SearchResultEntry> handler);
+    FutureResult<SearchResultEntry> readEntryAsync(DN name, Collection<String> attributeDescriptions);
 
     /**
      * Removes the provided connection event listener from this connection so
@@ -1231,11 +1329,7 @@
      *
      * @param request
      *            The search request.
-     * @param intermediateResponseHandler
-     *            An intermediate response handler which can be used to process
-     *            any intermediate responses as they are received, may be
-     *            {@code null}.
-     * @param resultHandler
+     * @param entryHandler
      *            A search result handler which can be used to asynchronously
      *            process the search result entries and references as they are
      *            received, may be {@code null}.
@@ -1248,9 +1342,33 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> searchAsync(SearchRequest request,
-            IntermediateResponseHandler intermediateResponseHandler,
-            SearchResultHandler resultHandler);
+    FutureResult<Result> searchAsync(SearchRequest request, SearchResultHandler entryHandler);
+
+    /**
+     * Asynchronously searches the Directory Server using the provided search
+     * request.
+     *
+     * @param request
+     *            The search request.
+     * @param intermediateResponseHandler
+     *            An intermediate response handler which can be used to process
+     *            any intermediate responses as they are received, may be
+     *            {@code null}.
+     * @param entryHandler
+     *            A search result handler which can be used to asynchronously
+     *            process the search result entries and references as they are
+     *            received, may be {@code null}.
+     * @return A future representing the result of the operation.
+     * @throws UnsupportedOperationException
+     *             If this connection does not support search operations.
+     * @throws IllegalStateException
+     *             If this connection has already been closed, i.e. if
+     *             {@code isClosed() == true}.
+     * @throws NullPointerException
+     *             If {@code request} was {@code null}.
+     */
+    FutureResult<Result> searchAsync(SearchRequest request, IntermediateResponseHandler intermediateResponseHandler,
+        SearchResultHandler entryHandler);
 
     /**
      * Searches the Directory Server for a single entry using the provided
@@ -1337,9 +1455,6 @@
      *
      * @param request
      *            The search request.
-     * @param handler
-     *            A result handler which can be used to asynchronously process
-     *            the operation result when it is received, may be {@code null}.
      * @return A future representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support search operations.
@@ -1349,6 +1464,5 @@
      * @throws NullPointerException
      *             If the {@code request} was {@code null}.
      */
-    FutureResult<SearchResultEntry> searchSingleEntryAsync(SearchRequest request,
-            ResultHandler<? super SearchResultEntry> handler);
+    FutureResult<SearchResultEntry> searchSingleEntryAsync(SearchRequest request);
 }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/ConnectionFactory.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/ConnectionFactory.java
index af6bf07..33431da 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/ConnectionFactory.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/ConnectionFactory.java
@@ -22,13 +22,15 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap;
 
 import java.io.Closeable;
 
+import org.forgerock.util.promise.Promise;
+
 /**
  * A connection factory provides an interface for obtaining a connection to a
  * Directory Server. Connection factories can be used to wrap other connection
@@ -75,17 +77,12 @@
 
     /**
      * Asynchronously obtains a connection to the Directory Server associated
-     * with this connection factory. The returned {@code FutureResult} can be
-     * used to retrieve the completed connection. Alternatively, if a
-     * {@code ResultHandler} is provided, the handler will be notified when the
-     * connection is available and ready for use.
+     * with this connection factory. The returned {@code Promise} can be used to
+     * retrieve the completed connection.
      *
-     * @param handler
-     *            The completion handler, or {@code null} if no handler is to be
-     *            used.
-     * @return A future which can be used to retrieve the connection.
+     * @return A promise which can be used to retrieve the connection.
      */
-    FutureResult<Connection> getConnectionAsync(ResultHandler<? super Connection> handler);
+    Promise<Connection, ErrorResultException> getConnectionAsync();
 
     /**
      * Returns a connection to the Directory Server associated with this
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/ConnectionPool.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/ConnectionPool.java
index d9fc7f1..580c8b6 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/ConnectionPool.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/ConnectionPool.java
@@ -26,6 +26,8 @@
 
 package org.forgerock.opendj.ldap;
 
+import org.forgerock.util.promise.Promise;
+
 /**
  * A connection factory which maintains and re-uses a pool of connections.
  * Connections obtained from a connection pool are returned to the connection
@@ -51,28 +53,25 @@
      * Calling {@code close} on a connection pool which is already closed has no
      * effect.
      */
+    @Override
     void close();
 
     /**
      * Asynchronously obtains a connection from this connection pool,
      * potentially opening a new connection if needed.
      * <p>
-     * The returned {@code FutureResult} can be used to retrieve the pooled
-     * connection. Alternatively, if a {@code ResultHandler} is provided, the
-     * handler will be notified when the pooled connection is available and
-     * ready for use.
+     * The returned {@code Promise} can be used to retrieve the pooled
+     * connection.
      * <p>
      * Closing the pooled connection will, depending on the connection pool
      * implementation, return the connection to this pool without closing it.
      *
-     * @param handler
-     *            The completion handler, or {@code null} if no handler is to be
-     *            used.
-     * @return A future which can be used to retrieve the pooled connection.
+     * @return A promise which can be used to retrieve the pooled connection.
      * @throws IllegalStateException
      *             If this connection pool has already been closed.
      */
-    FutureResult<Connection> getConnectionAsync(ResultHandler<? super Connection> handler);
+    @Override
+    Promise<Connection, ErrorResultException> getConnectionAsync();
 
     /**
      * Obtains a connection from this connection pool, potentially opening a new
@@ -87,5 +86,6 @@
      * @throws IllegalStateException
      *             If this connection pool has already been closed.
      */
+    @Override
     Connection getConnection() throws ErrorResultException;
 }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/Connections.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/Connections.java
index f8a2fab..d17b8a5 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/Connections.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/Connections.java
@@ -37,6 +37,7 @@
 import org.forgerock.opendj.ldap.requests.BindRequest;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
 import org.forgerock.util.Reject;
+import org.forgerock.util.promise.Promise;
 
 /**
  * This class contains methods for creating and manipulating connection
@@ -547,9 +548,8 @@
             }
 
             @Override
-            public FutureResult<Connection> getConnectionAsync(
-                    final ResultHandler<? super Connection> handler) {
-                return factory.getConnectionAsync(handler);
+            public Promise<Connection, ErrorResultException> getConnectionAsync() {
+                return factory.getConnectionAsync();
             }
 
             @Override
@@ -642,6 +642,7 @@
                 // Do nothing.
             }
 
+            @Override
             public void close(org.forgerock.opendj.ldap.requests.UnbindRequest request,
                     String reason) {
                 // Do nothing.
@@ -662,9 +663,8 @@
         return new ConnectionFactory() {
 
             @Override
-            public FutureResult<Connection> getConnectionAsync(
-                    ResultHandler<? super Connection> handler) {
-                return factory.getConnectionAsync(handler);
+            public Promise<Connection, ErrorResultException> getConnectionAsync() {
+                return factory.getConnectionAsync();
             }
 
             @Override
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResult.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResult.java
index 1776f14..7aaabef 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResult.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResult.java
@@ -22,80 +22,20 @@
  *
  *
  *      Copyright 2009 Sun Microsystems, Inc.
+ *      Portions copyright 2014 ForgeRock AS
  */
 
 package org.forgerock.opendj.ldap;
 
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
+import org.forgerock.util.promise.Promise;
 
 /**
  * A handle which can be used to retrieve the Result of an asynchronous Request.
  *
  * @param <S>
- *            The type of result returned by this future.
+ *            The type of result returned by this future result.
  */
-public interface FutureResult<S> extends Future<S> {
-    /**
-     * Attempts to cancel the request. This attempt will fail if the request has
-     * already completed or has already been cancelled. If successful, then
-     * cancellation results in an abandon or cancel request (if configured)
-     * being sent to the server.
-     * <p>
-     * After this method returns, subsequent calls to {@link #isDone} will
-     * always return {@code true}. Subsequent calls to {@link #isCancelled} will
-     * always return {@code true} if this method returned {@code true}.
-     *
-     * @param mayInterruptIfRunning
-     *            {@code true} if the thread executing executing the response
-     *            handler should be interrupted; otherwise, in-progress response
-     *            handlers are allowed to complete.
-     * @return {@code false} if the request could not be cancelled, typically
-     *         because it has already completed normally; {@code true}
-     *         otherwise.
-     */
-    boolean cancel(boolean mayInterruptIfRunning);
-
-    /**
-     * Waits if necessary for the request to complete, and then returns the
-     * result if the request succeeded. If the request failed (i.e. a
-     * non-successful result code was obtained) then the result is thrown as an
-     * {@link ErrorResultException}.
-     *
-     * @return The result, but only if the result code indicates that the
-     *         request succeeded.
-     * @throws ErrorResultException
-     *             If the result code indicates that the request failed for some
-     *             reason.
-     * @throws InterruptedException
-     *             If the current thread was interrupted while waiting.
-     */
-    S get() throws ErrorResultException, InterruptedException;
-
-    /**
-     * Waits if necessary for at most the given time for the request to
-     * complete, and then returns the result if the request succeeded. If the
-     * request failed (i.e. a non-successful result code was obtained) then the
-     * result is thrown as an {@link ErrorResultException}.
-     *
-     * @param timeout
-     *            The maximum time to wait.
-     * @param unit
-     *            The time unit of the timeout argument.
-     * @return The result, but only if the result code indicates that the
-     *         request succeeded.
-     * @throws ErrorResultException
-     *             If the result code indicates that the request failed for some
-     *             reason.
-     * @throws TimeoutException
-     *             If the wait timed out.
-     * @throws InterruptedException
-     *             If the current thread was interrupted while waiting.
-     */
-    S get(long timeout, TimeUnit unit) throws ErrorResultException, TimeoutException,
-            InterruptedException;
-
+public interface FutureResult<S> extends Promise<S, ErrorResultException> {
     /**
      * Returns the request ID of the request if appropriate.
      *
@@ -103,23 +43,4 @@
      */
     int getRequestID();
 
-    /**
-     * Returns {@code true} if the request was cancelled before it completed
-     * normally.
-     *
-     * @return {@code true} if the request was cancelled before it completed
-     *         normally, otherwise {@code false}.
-     */
-    boolean isCancelled();
-
-    /**
-     * Returns {@code true} if the request has completed.
-     * <p>
-     * Completion may be due to normal termination, an exception, or
-     * cancellation. In all of these cases, this method will return {@code true}.
-     *
-     * @return {@code true} if the request has completed, otherwise
-     *         {@code false}.
-     */
-    boolean isDone();
 }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResultImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResultImpl.java
new file mode 100644
index 0000000..c6d4c82
--- /dev/null
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResultImpl.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 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 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 2014 ForgeRock AS.
+ */
+package org.forgerock.opendj.ldap;
+
+import org.forgerock.util.promise.PromiseImpl;
+
+/**
+ * This class provides an implementation of the {@code FutureResult}.
+ *
+ * @param <R>
+ *            The type of result returned by this future.
+ * @see Promise
+ * @see Promises
+ */
+public class FutureResultImpl<R> extends PromiseImpl<R, ErrorResultException>
+    implements FutureResult<R>, ResultHandler<R> {
+    private final int requestID;
+
+    /**
+     * Creates a new future result with a request ID of -1.
+     */
+    public FutureResultImpl() {
+        this(-1);
+    }
+
+    /**
+     * Creates a future result with the provided request ID.
+     *
+     * @param requestID
+     *            The request ID which will be returned by the default
+     *            implementation of {@link #getRequestID}.
+     */
+    public FutureResultImpl(int requestID) {
+        this.requestID = requestID;
+    }
+
+    @Override
+    public int getRequestID() {
+        return requestID;
+    }
+}
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResultWrapper.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResultWrapper.java
new file mode 100644
index 0000000..0fd5252
--- /dev/null
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResultWrapper.java
@@ -0,0 +1,281 @@
+/*
+ * 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 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 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 2014 ForgeRock AS.
+ */
+package org.forgerock.opendj.ldap;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.forgerock.util.promise.AsyncFunction;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.Function;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.Promises;
+import org.forgerock.util.promise.SuccessHandler;
+
+/**
+ * This class is a {@link Promise} wrapper which implements {@link FutureResult} interface.
+ *
+ * It allows client code to return {@link FutureResult} instance when using {@link Promise}
+ * chaining methods (e.g onSuccess(), then(), thenAsync()).
+ *
+ * Wrapping is specially needed with {@link Promise} method which are not returning
+ * the original promise (i.e this) but a new one.
+ *
+ * It also provides some useful methods to create completed
+ * {@link FutureResult} instance.
+ *
+ *
+ * @param <R>
+ *            The type of the task's result, or {@link Void} if the task does
+ *            not return anything (i.e. it only has side-effects).
+ */
+public final class FutureResultWrapper<R> {
+    private static class LdapPromiseWrapper<R> implements FutureResult<R> {
+        private final Promise<R, ErrorResultException> wrappedPromise;
+        private final int requestID;
+
+        LdapPromiseWrapper(Promise<R, ErrorResultException> wrappedPromise, int requestID) {
+            this.wrappedPromise = wrappedPromise;
+            this.requestID = requestID;
+        }
+
+        @Override
+        public int getRequestID() {
+            return wrappedPromise instanceof FutureResult ? ((FutureResult<R>) wrappedPromise).getRequestID()
+                : requestID;
+        }
+
+        @Override
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            return wrappedPromise.cancel(mayInterruptIfRunning);
+        }
+
+        @Override
+        public R get() throws ExecutionException, InterruptedException {
+            return wrappedPromise.get();
+        }
+
+        @Override
+        public R get(long timeout, TimeUnit unit) throws ExecutionException, TimeoutException, InterruptedException {
+            return wrappedPromise.get(timeout, unit);
+        }
+
+        @Override
+        public R getOrThrow() throws InterruptedException, ErrorResultException {
+            return wrappedPromise.getOrThrow();
+        }
+
+        @Override
+        public R getOrThrow(long timeout, TimeUnit unit) throws InterruptedException, ErrorResultException,
+            TimeoutException {
+            return wrappedPromise.getOrThrow(timeout, unit);
+        }
+
+        @Override
+        public R getOrThrowUninterruptibly() throws ErrorResultException {
+            return wrappedPromise.getOrThrowUninterruptibly();
+        }
+
+        @Override
+        public R getOrThrowUninterruptibly(long timeout, TimeUnit unit) throws ErrorResultException, TimeoutException {
+            return wrappedPromise.getOrThrowUninterruptibly(timeout, unit);
+        }
+
+        @Override
+        public boolean isCancelled() {
+            return wrappedPromise.isCancelled();
+        }
+
+        @Override
+        public boolean isDone() {
+            return wrappedPromise.isDone();
+        }
+
+        @Override
+        public Promise<R, ErrorResultException> onFailure(FailureHandler<? super ErrorResultException> onFailure) {
+            wrappedPromise.onFailure(onFailure);
+            return this;
+        }
+
+        @Override
+        public Promise<R, ErrorResultException> onSuccess(SuccessHandler<? super R> onSuccess) {
+            wrappedPromise.onSuccess(onSuccess);
+            return this;
+        }
+
+        @Override
+        public Promise<R, ErrorResultException> onSuccessOrFailure(Runnable onSuccessOrFailure) {
+            wrappedPromise.onSuccessOrFailure(onSuccessOrFailure);
+            return this;
+        }
+
+        @Override
+        // @Checkstyle:ignore
+        public <VOUT> Promise<VOUT, ErrorResultException> then(
+                Function<? super R, VOUT, ErrorResultException> onSuccess) {
+            return new LdapPromiseWrapper<VOUT>(wrappedPromise.then(onSuccess), getRequestID());
+        }
+
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        @Override
+        // @Checkstyle:ignore
+        public <VOUT, EOUT extends Exception> Promise<VOUT, EOUT> then(Function<? super R, VOUT, EOUT> onSuccess,
+                Function<? super ErrorResultException, VOUT, EOUT> onFailure) {
+            return new LdapPromiseWrapper(wrappedPromise.then(onSuccess, onFailure), getRequestID());
+        }
+
+        @Override
+        public Promise<R, ErrorResultException> then(SuccessHandler<? super R> onSuccess) {
+            wrappedPromise.then(onSuccess);
+            return this;
+        }
+
+        @Override
+        public Promise<R, ErrorResultException> then(SuccessHandler<? super R> onSuccess,
+            FailureHandler<? super ErrorResultException> onFailure) {
+            wrappedPromise.then(onSuccess, onFailure);
+            return this;
+        }
+
+        @Override
+        public Promise<R, ErrorResultException> thenAlways(Runnable onSuccessOrFailure) {
+            wrappedPromise.thenAlways(onSuccessOrFailure);
+            return this;
+        }
+
+        @Override
+        // @Checkstyle:ignore
+        public <VOUT> Promise<VOUT, ErrorResultException> thenAsync(
+                AsyncFunction<? super R, VOUT, ErrorResultException> onSuccess) {
+            return new LdapPromiseWrapper<VOUT>(wrappedPromise.thenAsync(onSuccess), getRequestID());
+        }
+
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        @Override
+        // @Checkstyle:ignore
+        public <VOUT, EOUT extends Exception> Promise<VOUT, EOUT> thenAsync(
+                AsyncFunction<? super R, VOUT, EOUT> onSuccess,
+                AsyncFunction<? super ErrorResultException, VOUT, EOUT> onFailure) {
+            return new LdapPromiseWrapper(wrappedPromise.thenAsync(onSuccess, onFailure), getRequestID());
+        }
+    }
+
+    /**
+     * Returns a {@link FutureResult} representing an asynchronous task which
+     * has already succeeded with the provided result. Attempts to get the
+     * result will immediately return the result.
+     *
+     * @param <R>
+     *            The type of the task's result, or {@link Void} if the task
+     *            does not return anything (i.e. it only has side-effects).
+     * @param result
+     *            The result of the asynchronous task.
+     * @return A {@link FutureResult} representing an asynchronous task which
+     *         has already succeeded with the provided result.
+     */
+    public static <R> FutureResult<R> newSuccessfulFutureResult(final R result) {
+        return new LdapPromiseWrapper<R>(Promises.<R, ErrorResultException> newSuccessfulPromise(result), -1);
+    }
+
+    /**
+     * Returns a {@link FutureResult} representing an asynchronous task,
+     * identified by the provided requestID, which has already succeeded with
+     * the provided result. Attempts to get the result will immediately return
+     * the result.
+     *
+     * @param <R>
+     *            The type of the task's result, or {@link Void} if the task
+     *            does not return anything (i.e. it only has side-effects).
+     * @param result
+     *            The result of the asynchronous task.
+     * @param requestID
+     *            The request ID of the succeeded task.
+     * @return A {@link FutureResult} representing an asynchronous task which
+     *         has already succeeded with the provided result.
+     */
+    public static <R> FutureResult<R> newSuccessfulFutureResult(final R result, int requestID) {
+        return new LdapPromiseWrapper<R>(Promises.<R, ErrorResultException> newSuccessfulPromise(result), requestID);
+    }
+
+    /**
+     * Returns a {@link FutureResult} representing an asynchronous task which
+     * has already failed with the provided error.
+     *
+     * @param <R>
+     *            The type of the task's result, or {@link Void} if the task
+     *            does not return anything (i.e. it only has side-effects).
+     * @param <E>
+     *            The type of the exception thrown by the task if it fails.
+     * @param error
+     *            The exception indicating why the asynchronous task has failed.
+     * @return A {@link FutureResult} representing an asynchronous task which
+     *         has already failed with the provided error.
+     */
+    public static <R, E extends ErrorResultException> FutureResult<R> newFailedFutureResult(final E error) {
+        return new LdapPromiseWrapper<R>(Promises.<R, ErrorResultException> newFailedPromise(error), -1);
+    }
+
+    /**
+     * Returns a {@link FutureResult} representing an asynchronous task,
+     * identified by the provided requestID, which has already failed with the
+     * provided error.
+     *
+     * @param <R>
+     *            The type of the task's result, or {@link Void} if the task
+     *            does not return anything (i.e. it only has side-effects).
+     * @param <E>
+     *            The type of the exception thrown by the task if it fails.
+     * @param error
+     *            The exception indicating why the asynchronous task has failed.
+     * @param requestID
+     *            The request ID of the failed task.
+     * @return A {@link FutureResult} representing an asynchronous task which
+     *         has already failed with the provided error.
+     */
+    public static <R, E extends ErrorResultException> FutureResult<R> newFailedFutureResult(final E error,
+            int requestID) {
+        return new LdapPromiseWrapper<R>(Promises.<R, ErrorResultException> newFailedPromise(error), requestID);
+    }
+
+    /**
+     * Converts a {@link Promise} to a {@link FutureResult}.
+     *
+     * @param <R>
+     *            The type of the task's result, or {@link Void} if the task
+     *            does not return anything (i.e. it only has side-effects).
+     * @param wrappedPromise
+     *            The {@link Promise} to wrap.
+     * @return A {@link FutureResult} representing the same asynchronous task as
+     *         the {@link Promise} provided.
+     */
+    public static <R> FutureResult<R> asFutureResult(Promise<R, ErrorResultException> wrappedPromise) {
+        return new LdapPromiseWrapper<R>(wrappedPromise, -1);
+    }
+
+    private FutureResultWrapper() {
+    }
+}
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactory.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactory.java
index 3340904..831b244 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactory.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactory.java
@@ -26,15 +26,12 @@
  */
 package org.forgerock.opendj.ldap;
 
-import static com.forgerock.opendj.util.StaticUtils.DEFAULT_SCHEDULER;
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
+
 
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
@@ -65,14 +62,20 @@
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
 import org.forgerock.opendj.ldap.spi.ConnectionState;
 import org.forgerock.util.Reject;
+import org.forgerock.util.promise.AsyncFunction;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.Function;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.SuccessHandler;
 
-import com.forgerock.opendj.util.AsynchronousFutureResult;
-import com.forgerock.opendj.util.CompletedFutureResult;
-import com.forgerock.opendj.util.FutureResultTransformer;
-import com.forgerock.opendj.util.RecursiveFutureResult;
 import com.forgerock.opendj.util.ReferenceCountedObject;
 import com.forgerock.opendj.util.TimeSource;
 
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
+import static com.forgerock.opendj.util.StaticUtils.*;
+
 /**
  * An heart beat connection factory can be used to create connections that sends
  * a periodic search request to a Directory Server.
@@ -109,122 +112,58 @@
      * to a {@code ConnectionImpl} and registering it in the table of valid
      * connections.
      */
-    private final class ConnectionFutureResultImpl {
+    private final class ConnectionFutureResultImpl implements Runnable {
+        private Connection connection;
+        private Connection heartBeatConnection;
+        private ErrorResultException heartBeatError;
 
         /**
-         * This class handles the initial heart beat result notification or
-         * timeout. We need to take care to avoid processing multiple results,
-         * which may occur when the heart beat is timed out and a result follows
-         * soon after, or vice versa.
+         * Due to a potential race between the heart beat timing out and the
+         * heart beat completing this atomic ensures that notification only
+         * occurs once.
          */
-        private final class InitialHeartBeatResultHandler implements SearchResultHandler, Runnable {
-            private final ResultHandler<? super Result> handler;
+        private final AtomicBoolean isComplete = new AtomicBoolean();
 
-            /**
-             * Due to a potential race between the heart beat timing out and the
-             * heart beat completing this atomic ensures that notification only
-             * occurs once.
-             */
-            private final AtomicBoolean isComplete = new AtomicBoolean();
+        private final Function<Result, Connection, ErrorResultException> futureSearchSuccess;
+        private final Function<ErrorResultException, Connection, ErrorResultException> futureSearchError;
 
-            private InitialHeartBeatResultHandler(final ResultHandler<? super Result> handler) {
-                this.handler = handler;
-            }
-
-            @Override
-            public boolean handleEntry(final SearchResultEntry entry) {
-                /*
-                 * Depending on the configuration, a heartbeat may return some
-                 * entries. However, we can just ignore them.
-                 */
-                return true;
-            }
-
-            @Override
-            public void handleErrorResult(final ErrorResultException error) {
-                if (isComplete.compareAndSet(false, true)) {
-                    handler.handleErrorResult(error);
-                }
-            }
-
-            @Override
-            public boolean handleReference(final SearchResultReference reference) {
-                /*
-                 * Depending on the configuration, a heartbeat may return some
-                 * references. However, we can just ignore them.
-                 */
-                return true;
-            }
-
-            @Override
-            public void handleResult(final Result result) {
-                if (isComplete.compareAndSet(false, true)) {
-                    handler.handleResult(result);
-                }
-            }
-
-            /*
-             * Invoked by the scheduler when the heart beat times out.
-             */
-            @Override
-            public void run() {
-                handleErrorResult(newHeartBeatTimeoutError());
-            }
-        }
-
-        private Connection connection;
-        private final RecursiveFutureResult<Connection, Result> futureConnectionResult;
-        private final FutureResultTransformer<Result, Connection> futureSearchResult;
-
-        private ConnectionFutureResultImpl(final ResultHandler<? super Connection> handler) {
-            // Create a future which will handle the initial heart beat result.
-            this.futureSearchResult = new FutureResultTransformer<Result, Connection>(handler) {
+        private ConnectionFutureResultImpl() {
+            this.futureSearchSuccess = new Function<Result, Connection, ErrorResultException>() {
 
                 @Override
-                protected ErrorResultException transformErrorResult(
-                        final ErrorResultException errorResult) {
-                    // Ensure that the connection is closed.
-                    if (connection != null) {
-                        connection.close();
-                        connection = null;
+                public Connection apply(Result result) throws ErrorResultException {
+                    if (isComplete.compareAndSet(false, true)) {
+                        heartBeatConnection = adaptConnection(connection);
                     }
-                    releaseScheduler();
-                    return adaptHeartBeatError(errorResult);
-                }
 
-                @Override
-                protected Connection transformResult(final Result result)
-                        throws ErrorResultException {
-                    return adaptConnection(connection);
+                    return heartBeatConnection;
                 }
-
             };
 
-            // Create a future which will handle connection result.
-            this.futureConnectionResult =
-                    new RecursiveFutureResult<Connection, Result>(futureSearchResult) {
-                        @Override
-                        protected FutureResult<? extends Result> chainResult(
-                                final Connection innerResult,
-                                final ResultHandler<? super Result> handler)
-                                throws ErrorResultException {
-                            // Save the connection for later once the heart beat completes.
-                            connection = innerResult;
+            this.futureSearchError = new Function<ErrorResultException, Connection, ErrorResultException>() {
+                @Override
+                public Connection apply(ErrorResultException error) throws ErrorResultException {
+                    manageError(error);
+                    throw heartBeatError;
+                }
+            };
+        }
 
-                            /*
-                             * Send the initial heart beat and schedule a client
-                             * side timeout notification.
-                             */
-                            final InitialHeartBeatResultHandler wrappedHandler =
-                                    new InitialHeartBeatResultHandler(handler);
-                            scheduler.get().schedule(wrappedHandler, timeoutMS,
-                                    TimeUnit.MILLISECONDS);
-                            return connection.searchAsync(heartBeatRequest, null, wrappedHandler);
-                        }
-                    };
+        @Override
+        public void run() {
+            manageError(newHeartBeatTimeoutError());
+        }
 
-            // Link the two futures.
-            futureSearchResult.setFutureResult(futureConnectionResult);
+        private void manageError(ErrorResultException error) {
+            if (isComplete.compareAndSet(false, true)) {
+                // Ensure that the connection is closed.
+                if (connection != null) {
+                    connection.close();
+                    connection = null;
+                }
+                releaseScheduler();
+                heartBeatError = adaptHeartBeatError(error);
+            }
         }
 
     }
@@ -241,105 +180,38 @@
          * completed requests are removed from the {@code pendingResults} queue,
          * as well as ensuring that requests are only completed once.
          */
-        private abstract class AbstractWrappedResultHandler<R, H extends ResultHandler<? super R>>
-                implements ResultHandler<R>, FutureResult<R> {
-            /** The user provided result handler. */
-            protected final H handler;
-
-            private final CountDownLatch completed = new CountDownLatch(1);
-            private ErrorResultException error;
-            private FutureResult<R> innerFuture;
-            private R result;
-
-            AbstractWrappedResultHandler(final H handler) {
-                this.handler = handler;
-            }
+        private abstract class AbstractWrappedResultHandler<R> implements ResultHandler<R> {
+            private final AtomicBoolean completed = new AtomicBoolean();
 
             @Override
-            public boolean cancel(final boolean mayInterruptIfRunning) {
-                return innerFuture.cancel(mayInterruptIfRunning);
-            }
-
-            @Override
-            public R get() throws ErrorResultException, InterruptedException {
-                completed.await();
-                return get0();
-            }
-
-            @Override
-            public R get(final long timeout, final TimeUnit unit) throws ErrorResultException,
-                    TimeoutException, InterruptedException {
-                if (completed.await(timeout, unit)) {
-                    return get0();
-                } else {
-                    throw new TimeoutException();
-                }
-            }
-
-            @Override
-            public int getRequestID() {
-                return innerFuture.getRequestID();
-            }
-
-            @Override
-            public void handleErrorResult(final ErrorResultException error) {
-                if (tryComplete(null, error)) {
-                    if (handler != null) {
-                        handler.handleErrorResult(timestamp(error));
-                    } else {
-                        timestamp(error);
-                    }
+            public void handleError(final ErrorResultException error) {
+                if (tryComplete()) {
+                    timestamp(error);
                 }
             }
 
             @Override
             public void handleResult(final R result) {
-                if (tryComplete(result, null)) {
-                    if (handler != null) {
-                        handler.handleResult(timestamp(result));
-                    } else {
-                        timestamp(result);
-                    }
+                if (tryComplete()) {
+                    timestamp(result);
                 }
             }
 
-            @Override
-            public boolean isCancelled() {
-                return innerFuture.isCancelled();
-            }
-
-            @Override
             public boolean isDone() {
-                return completed.getCount() == 0;
+                return completed.get();
             }
 
             abstract void releaseBindOrStartTLSLockIfNeeded();
 
-            FutureResult<R> setInnerFuture(final FutureResult<R> innerFuture) {
-                this.innerFuture = innerFuture;
-                return this;
-            }
-
-            private R get0() throws ErrorResultException {
-                if (result != null) {
-                    return result;
-                } else {
-                    throw error;
-                }
-            }
-
             /**
              * Attempts to complete this request, returning true if successful.
              * This method is synchronized in order to avoid race conditions
              * with search result processing.
              */
-            private synchronized boolean tryComplete(final R result,
-                    final ErrorResultException error) {
+            private synchronized boolean tryComplete() {
                 if (pendingResults.remove(this)) {
-                    this.result = result;
-                    this.error = error;
-                    completed.countDown();
                     releaseBindOrStartTLSLockIfNeeded();
+                    completed.set(true);
                     return true;
                 } else {
                     return false;
@@ -355,14 +227,9 @@
          * @param <R>
          *            The type of result returned by the request.
          */
-        private abstract class DelayedFuture<R extends Result> extends
-                AsynchronousFutureResult<R, ResultHandler<? super R>> implements Runnable {
+        private abstract class DelayedFuture<R extends Result> extends FutureResultImpl<R> implements Runnable {
             private volatile FutureResult<R> innerFuture = null;
 
-            protected DelayedFuture(final ResultHandler<? super R> handler) {
-                super(handler);
-            }
-
             @Override
             public final int getRequestID() {
                 return innerFuture != null ? innerFuture.getRequestID() : -1;
@@ -382,8 +249,7 @@
             protected abstract FutureResult<R> dispatch();
 
             @Override
-            protected final ErrorResultException handleCancelRequest(
-                    final boolean mayInterruptIfRunning) {
+            protected final ErrorResultException tryCancel(final boolean mayInterruptIfRunning) {
                 if (innerFuture != null) {
                     innerFuture.cancel(mayInterruptIfRunning);
                 }
@@ -395,12 +261,7 @@
          * A result handler wrapper for bind or startTLS requests which releases
          * the bind/startTLS lock on completion.
          */
-        private final class WrappedBindOrStartTLSResultHandler<R> extends
-                AbstractWrappedResultHandler<R, ResultHandler<? super R>> {
-            WrappedBindOrStartTLSResultHandler(final ResultHandler<? super R> handler) {
-                super(handler);
-            }
-
+        private final class WrappedBindOrStartTLSResultHandler<R> extends AbstractWrappedResultHandler<R> {
             @Override
             void releaseBindOrStartTLSLockIfNeeded() {
                 releaseBindOrStartTLSLock();
@@ -411,12 +272,7 @@
          * A result handler wrapper for normal requests which does not release
          * the bind/startTLS lock on completion.
          */
-        private final class WrappedResultHandler<R> extends
-                AbstractWrappedResultHandler<R, ResultHandler<? super R>> {
-            WrappedResultHandler(final ResultHandler<? super R> handler) {
-                super(handler);
-            }
-
+        private final class WrappedResultHandler<R> extends AbstractWrappedResultHandler<R> {
             @Override
             void releaseBindOrStartTLSLockIfNeeded() {
                 // No-op for normal operations.
@@ -428,18 +284,19 @@
          * results are not sent once the request has been completed (see
          * markComplete()).
          */
-        private final class WrappedSearchResultHandler extends
-                AbstractWrappedResultHandler<Result, SearchResultHandler> implements
+        private final class WrappedSearchResultHandler extends AbstractWrappedResultHandler<Result> implements
                 SearchResultHandler {
+            private final SearchResultHandler entryHandler;
+
             WrappedSearchResultHandler(final SearchResultHandler handler) {
-                super(handler);
+                this.entryHandler = handler;
             }
 
             @Override
             public synchronized boolean handleEntry(final SearchResultEntry entry) {
                 if (!isDone()) {
-                    if (handler != null) {
-                        handler.handleEntry(timestamp(entry));
+                    if (entryHandler != null) {
+                        entryHandler.handleEntry(timestamp(entry));
                     } else {
                         timestamp(entry);
                     }
@@ -452,8 +309,8 @@
             @Override
             public synchronized boolean handleReference(final SearchResultReference reference) {
                 if (!isDone()) {
-                    if (handler != null) {
-                        handler.handleReference(timestamp(reference));
+                    if (entryHandler != null) {
+                        entryHandler.handleReference(timestamp(reference));
                     } else {
                         timestamp(reference);
                     }
@@ -475,44 +332,17 @@
         /**
          * Search result handler for processing heart beat responses.
          */
-        private final SearchResultHandler heartBeatHandler = new SearchResultHandler() {
+        private final SearchResultHandler heartBeatEntryHandler = new SearchResultHandler() {
             @Override
             public boolean handleEntry(final SearchResultEntry entry) {
                 timestamp(entry);
                 return true;
             }
-
-            @Override
-            public void handleErrorResult(final ErrorResultException error) {
-                /*
-                 * Connection failure will be handled by connection event
-                 * listener. Ignore cancellation errors since these indicate
-                 * that the heart beat was aborted by a client-side close.
-                 */
-                if (!(error instanceof CancelledResultException)) {
-                    /*
-                     * Log at debug level to avoid polluting the logs with
-                     * benign password policy related errors. See OPENDJ-1168
-                     * and OPENDJ-1167.
-                     */
-                    logger.debug(LocalizableMessage.raw("Heartbeat failed for connection factory '%s'", factory,
-                            error));
-                    timestamp(error);
-                }
-                releaseHeartBeatLock();
-            }
-
             @Override
             public boolean handleReference(final SearchResultReference reference) {
                 timestamp(reference);
                 return true;
             }
-
-            @Override
-            public void handleResult(final Result result) {
-                timestamp(result);
-                releaseHeartBeatLock();
-            }
         };
 
         /**
@@ -527,8 +357,8 @@
          * signalled if no heart beat is detected within the permitted timeout
          * period.
          */
-        private final Queue<AbstractWrappedResultHandler<?, ?>> pendingResults =
-                new ConcurrentLinkedQueue<AbstractWrappedResultHandler<?, ?>>();
+        private final Queue<AbstractWrappedResultHandler<?>> pendingResults =
+                new ConcurrentLinkedQueue<AbstractWrappedResultHandler<?>>();
 
         /** Internal connection state. */
         private final ConnectionState state = new ConnectionState();
@@ -554,11 +384,9 @@
 
         @Override
         public FutureResult<Result> addAsync(final AddRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super Result> resultHandler) {
-            if (checkState(resultHandler)) {
-                final WrappedResultHandler<Result> h = wrap(resultHandler);
-                return checkState(connection.addAsync(request, intermediateResponseHandler, h), h);
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            if (checkState()) {
+                return then(connection.addAsync(request, intermediateResponseHandler), createResultHandler());
             } else {
                 return newConnectionErrorFuture();
             }
@@ -571,31 +399,24 @@
 
         @Override
         public FutureResult<BindResult> bindAsync(final BindRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super BindResult> resultHandler) {
-            if (checkState(resultHandler)) {
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            if (checkState()) {
                 if (sync.tryLockShared()) {
                     // Fast path
-                    final WrappedBindOrStartTLSResultHandler<BindResult> h =
-                            wrapForBindOrStartTLS(resultHandler);
-                    return checkState(
-                            connection.bindAsync(request, intermediateResponseHandler, h), h);
+                    return then(connection.bindAsync(request, intermediateResponseHandler), wrapForBindOrStartTLS());
                 } else {
                     /*
                      * A heart beat must be in progress so create a runnable
                      * task which will be executed when the heart beat
                      * completes.
                      */
-                    final DelayedFuture<BindResult> future =
-                            new DelayedFuture<BindResult>(resultHandler) {
-                                @Override
-                                public FutureResult<BindResult> dispatch() {
-                                    final WrappedBindOrStartTLSResultHandler<BindResult> h =
-                                            wrapForBindOrStartTLS(this);
-                                    return checkState(connection.bindAsync(request,
-                                            intermediateResponseHandler, h), h);
-                                }
-                            };
+                    final DelayedFuture<BindResult> future = new DelayedFuture<BindResult>() {
+                        @Override
+                        public FutureResult<BindResult> dispatch() {
+                            return HeartBeatConnectionFactory.this.then(
+                                connection.bindAsync(request, intermediateResponseHandler), wrapForBindOrStartTLS());
+                        }
+                    };
                     /*
                      * Enqueue and flush if the heart beat has completed in the
                      * mean time.
@@ -623,12 +444,9 @@
 
         @Override
         public FutureResult<CompareResult> compareAsync(final CompareRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super CompareResult> resultHandler) {
-            if (checkState(resultHandler)) {
-                final WrappedResultHandler<CompareResult> h = wrap(resultHandler);
-                return checkState(connection.compareAsync(request, intermediateResponseHandler, h),
-                        h);
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            if (checkState()) {
+                return then(connection.compareAsync(request, intermediateResponseHandler), createResultHandler());
             } else {
                 return newConnectionErrorFuture();
             }
@@ -636,43 +454,35 @@
 
         @Override
         public FutureResult<Result> deleteAsync(final DeleteRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super Result> resultHandler) {
-            if (checkState(resultHandler)) {
-                final WrappedResultHandler<Result> h = wrap(resultHandler);
-                return checkState(connection.deleteAsync(request, intermediateResponseHandler, h),
-                        h);
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            if (checkState()) {
+                return then(connection.deleteAsync(request, intermediateResponseHandler), createResultHandler());
             } else {
                 return newConnectionErrorFuture();
             }
         }
 
         @Override
-        public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(
-                final ExtendedRequest<R> request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super R> resultHandler) {
-            if (checkState(resultHandler)) {
+        public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request,
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            if (checkState()) {
                 if (isStartTLSRequest(request)) {
                     if (sync.tryLockShared()) {
                         // Fast path
-                        final WrappedBindOrStartTLSResultHandler<R> h =
-                                wrapForBindOrStartTLS(resultHandler);
-                        return checkState(connection.extendedRequestAsync(request,
-                                intermediateResponseHandler, h), h);
+                        return then(connection.extendedRequestAsync(request, intermediateResponseHandler),
+                                wrapForBindOrStartTLS());
                     } else {
                         /*
                          * A heart beat must be in progress so create a runnable
                          * task which will be executed when the heart beat
                          * completes.
                          */
-                        final DelayedFuture<R> future = new DelayedFuture<R>(resultHandler) {
+                        final DelayedFuture<R> future = new DelayedFuture<R>() {
                             @Override
                             public FutureResult<R> dispatch() {
-                                final WrappedBindOrStartTLSResultHandler<R> h =
-                                        wrapForBindOrStartTLS(this);
-                                return checkState(connection.extendedRequestAsync(request,
-                                        intermediateResponseHandler, h), h);
+                                return HeartBeatConnectionFactory.this.then(
+                                        connection.extendedRequestAsync(request, intermediateResponseHandler),
+                                        wrapForBindOrStartTLS());
                             }
                         };
 
@@ -685,9 +495,8 @@
                         return future;
                     }
                 } else {
-                    final WrappedResultHandler<R> h = wrap(resultHandler);
-                    return checkState(connection.extendedRequestAsync(request,
-                            intermediateResponseHandler, h), h);
+                    return then(connection.extendedRequestAsync(request, intermediateResponseHandler),
+                            createResultHandler());
                 }
             } else {
                 return newConnectionErrorFuture();
@@ -740,12 +549,9 @@
 
         @Override
         public FutureResult<Result> modifyAsync(final ModifyRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super Result> resultHandler) {
-            if (checkState(resultHandler)) {
-                final WrappedResultHandler<Result> h = wrap(resultHandler);
-                return checkState(connection.modifyAsync(request, intermediateResponseHandler, h),
-                        h);
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            if (checkState()) {
+                return then(connection.modifyAsync(request, intermediateResponseHandler), createResultHandler());
             } else {
                 return newConnectionErrorFuture();
             }
@@ -753,12 +559,9 @@
 
         @Override
         public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final ResultHandler<? super Result> resultHandler) {
-            if (checkState(resultHandler)) {
-                final WrappedResultHandler<Result> h = wrap(resultHandler);
-                return checkState(
-                        connection.modifyDNAsync(request, intermediateResponseHandler, h), h);
+                final IntermediateResponseHandler intermediateResponseHandler) {
+            if (checkState()) {
+                return then(connection.modifyDNAsync(request, intermediateResponseHandler), createResultHandler());
             } else {
                 return newConnectionErrorFuture();
             }
@@ -772,11 +575,11 @@
         @Override
         public FutureResult<Result> searchAsync(final SearchRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler,
-                final SearchResultHandler resultHandler) {
-            if (checkState(resultHandler)) {
-                final WrappedSearchResultHandler h = wrap(resultHandler);
-                return checkState(connection.searchAsync(request, intermediateResponseHandler, h),
-                        h);
+                final SearchResultHandler searchHandler) {
+            if (checkState()) {
+                final WrappedSearchResultHandler entryHandler = wrap(searchHandler);
+                return then(connection.searchAsync(request, intermediateResponseHandler, entryHandler),
+                        createResultHandler());
             } else {
                 return newConnectionErrorFuture();
             }
@@ -809,23 +612,8 @@
             }
         }
 
-        private <R> FutureResult<R> checkState(final FutureResult<R> future,
-                final AbstractWrappedResultHandler<R, ? extends ResultHandler<? super R>> h) {
-            h.setInnerFuture(future);
-            checkState(h);
-            return h;
-        }
-
-        private boolean checkState(final ResultHandler<?> h) {
-            final ErrorResultException error = state.getConnectionError();
-            if (error != null) {
-                if (h != null) {
-                    h.handleErrorResult(error);
-                }
-                return false;
-            } else {
-                return true;
-            }
+        private boolean checkState() {
+            return state.getConnectionError() == null;
         }
 
         private void failPendingResults(final ErrorResultException error) {
@@ -833,9 +621,9 @@
              * Peek instead of pool because notification is responsible for
              * removing the element from the queue.
              */
-            AbstractWrappedResultHandler<?, ?> pendingResult;
+            AbstractWrappedResultHandler<?> pendingResult;
             while ((pendingResult = pendingResults.peek()) != null) {
-                pendingResult.handleErrorResult(error);
+                pendingResult.handleError(error);
             }
         }
 
@@ -864,8 +652,8 @@
             return request.getOID().equals(StartTLSExtendedRequest.OID);
         }
 
-        private <R> CompletedFutureResult<R> newConnectionErrorFuture() {
-            return new CompletedFutureResult<R>(state.getConnectionError());
+        private <R> FutureResult<R> newConnectionErrorFuture() {
+            return FutureResultWrapper.newFailedFutureResult(state.getConnectionError());
         }
 
         private void releaseBindOrStartTLSLock() {
@@ -908,7 +696,39 @@
              */
             if (sync.tryLockExclusively()) {
                 try {
-                    connection.searchAsync(heartBeatRequest, null, heartBeatHandler);
+                    FutureResult<Result> future = connection.searchAsync(heartBeatRequest, heartBeatEntryHandler);
+                    if (future != null) {
+                        future.onSuccess(new SuccessHandler<Result>() {
+                            @Override
+                            public void handleResult(Result result) {
+                                timestamp(result);
+                                releaseHeartBeatLock();
+                            }
+                        }).onFailure(new FailureHandler<ErrorResultException>() {
+                            @Override
+                            public void handleError(ErrorResultException error) {
+                                /*
+                                 * Connection failure will be handled by
+                                 * connection event listener. Ignore
+                                 * cancellation errors since these indicate that
+                                 * the heart beat was aborted by a client-side
+                                 * close.
+                                 */
+                                if (!(error instanceof CancelledResultException)) {
+                                    /*
+                                     * Log at debug level to avoid polluting the
+                                     * logs with benign password policy related
+                                     * errors. See OPENDJ-1168 and OPENDJ-1167.
+                                     */
+                                    logger.debug(LocalizableMessage.raw("Heartbeat failed for connection factory '%s'",
+                                            factory, error));
+                                    timestamp(error);
+                                }
+                                releaseHeartBeatLock();
+
+                            }
+                        });
+                    }
                 } catch (final IllegalStateException e) {
                     /*
                      * This may happen when we attempt to send the heart beat
@@ -938,8 +758,8 @@
             return response;
         }
 
-        private <R> WrappedResultHandler<R> wrap(final ResultHandler<? super R> handler) {
-            final WrappedResultHandler<R> h = new WrappedResultHandler<R>(handler);
+        private <R> WrappedResultHandler<R> createResultHandler() {
+            final WrappedResultHandler<R> h = new WrappedResultHandler<R>();
             pendingResults.add(h);
             return h;
         }
@@ -950,10 +770,8 @@
             return h;
         }
 
-        private <R> WrappedBindOrStartTLSResultHandler<R> wrapForBindOrStartTLS(
-                final ResultHandler<? super R> handler) {
-            final WrappedBindOrStartTLSResultHandler<R> h =
-                    new WrappedBindOrStartTLSResultHandler<R>(handler);
+        private <R> WrappedBindOrStartTLSResultHandler<R> wrapForBindOrStartTLS() {
+            final WrappedBindOrStartTLSResultHandler<R> h = new WrappedBindOrStartTLSResultHandler<R>();
             pendingResults.add(h);
             return h;
         }
@@ -1241,7 +1059,7 @@
         try {
             final Connection connection = factory.getConnection();
             try {
-                connection.searchAsync(heartBeatRequest, null, null).get(timeoutMS,
+                connection.searchAsync(heartBeatRequest, null).getOrThrow(timeoutMS,
                         TimeUnit.MILLISECONDS);
                 succeeded = true;
                 return adaptConnection(connection);
@@ -1260,22 +1078,24 @@
     }
 
     @Override
-    public FutureResult<Connection> getConnectionAsync(
-            final ResultHandler<? super Connection> handler) {
+    public Promise<Connection, ErrorResultException> getConnectionAsync() {
         acquireScheduler(); // Protect scheduler.
 
-        // Create a future responsible for chaining the initial heartbeat search.
-        final ConnectionFutureResultImpl compositeFuture = new ConnectionFutureResultImpl(handler);
+        // Create a future responsible for chaining the initial heartbeat
+        // search.
+        final ConnectionFutureResultImpl compositeFuture = new ConnectionFutureResultImpl();
 
-        // Request a connection.
-        final FutureResult<Connection> connectionFuture =
-                factory.getConnectionAsync(compositeFuture.futureConnectionResult);
-
-        // Set the connection future in the composite so that the returned search future can delegate.
-        compositeFuture.futureConnectionResult.setFutureResult(connectionFuture);
-
-        // Return the future representing the heartbeat.
-        return compositeFuture.futureSearchResult;
+        // Request a connection and return the future representing the
+        // heartbeat.
+        return factory.getConnectionAsync().thenAsync(new AsyncFunction<Connection, Result, ErrorResultException>() {
+            @Override
+            public Promise<Result, ErrorResultException> apply(final Connection connectionResult) {
+                // Save the connection for later once the heart beat completes.
+                compositeFuture.connection = connectionResult;
+                scheduler.get().schedule(compositeFuture, timeoutMS, TimeUnit.MILLISECONDS);
+                return connectionResult.searchAsync(heartBeatRequest, null);
+            }
+        }).then(compositeFuture.futureSearchSuccess, compositeFuture.futureSearchError);
     }
 
     @Override
@@ -1301,6 +1121,11 @@
         }
     }
 
+    private <R extends Result> FutureResult<R> then(FutureResult<R> future,
+            ResultHandler<? super Object> resultHandler) {
+        return (FutureResult<R>) future.onSuccess(resultHandler).onFailure(resultHandler);
+    }
+
     private ErrorResultException adaptHeartBeatError(final Exception error) {
         if (error instanceof ConnectionException) {
             return (ErrorResultException) error;
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/InternalConnection.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/InternalConnection.java
index 319b279..4d753d4 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/InternalConnection.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/InternalConnection.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap;
@@ -51,8 +51,6 @@
 import org.forgerock.opendj.ldap.spi.LDAPExtendedFutureResultImpl;
 import org.forgerock.opendj.ldap.spi.LDAPFutureResultImpl;
 import org.forgerock.opendj.ldap.spi.LDAPSearchFutureResultImpl;
-
-import com.forgerock.opendj.util.CompletedFutureResult;
 import org.forgerock.util.Reject;
 
 /**
@@ -66,10 +64,9 @@
         private final BindRequest bindRequest;
 
         InternalBindFutureResultImpl(final int messageID, final BindRequest bindRequest,
-                final ResultHandler<? super BindResult> resultHandler,
                 final IntermediateResponseHandler intermediateResponseHandler,
                 final Connection connection) {
-            super(messageID, resultHandler, intermediateResponseHandler, connection);
+            super(messageID, intermediateResponseHandler, connection);
             this.bindRequest = bindRequest;
         }
 
@@ -117,7 +114,7 @@
     public FutureResult<Void> abandonAsync(final AbandonRequest request) {
         final int i = messageID.getAndIncrement();
         serverConnection.handleAbandon(i, request);
-        return new CompletedFutureResult<Void>((Void) null, i);
+        return FutureResultWrapper.newSuccessfulFutureResult((Void) null, i);
     }
 
     /**
@@ -125,12 +122,9 @@
      */
     @Override
     public FutureResult<Result> addAsync(final AddRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final int i = messageID.getAndIncrement();
-        final LDAPFutureResultImpl future =
-                new LDAPFutureResultImpl(i, request, resultHandler, intermediateResponseHandler,
-                        this);
+        final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request, intermediateResponseHandler, this);
         serverConnection.handleAdd(i, request, future, future);
         return future;
     }
@@ -149,12 +143,10 @@
      */
     @Override
     public FutureResult<BindResult> bindAsync(final BindRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super BindResult> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final int i = messageID.getAndIncrement();
-        final InternalBindFutureResultImpl future =
-                new InternalBindFutureResultImpl(i, request, resultHandler,
-                        intermediateResponseHandler, this);
+        final InternalBindFutureResultImpl future = new InternalBindFutureResultImpl(i, request,
+                intermediateResponseHandler, this);
         serverConnection.handleBind(i, 3, request, future, future);
         return future;
     }
@@ -173,12 +165,10 @@
      */
     @Override
     public FutureResult<CompareResult> compareAsync(final CompareRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super CompareResult> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final int i = messageID.getAndIncrement();
-        final LDAPCompareFutureResultImpl future =
-                new LDAPCompareFutureResultImpl(i, request, resultHandler,
-                        intermediateResponseHandler, this);
+        final LDAPCompareFutureResultImpl future = new LDAPCompareFutureResultImpl(i, request,
+                intermediateResponseHandler, this);
         serverConnection.handleCompare(i, request, future, future);
         return future;
     }
@@ -188,12 +178,9 @@
      */
     @Override
     public FutureResult<Result> deleteAsync(final DeleteRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final int i = messageID.getAndIncrement();
-        final LDAPFutureResultImpl future =
-                new LDAPFutureResultImpl(i, request, resultHandler, intermediateResponseHandler,
-                        this);
+        final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request, intermediateResponseHandler, this);
         serverConnection.handleDelete(i, request, future, future);
         return future;
     }
@@ -202,14 +189,11 @@
      * {@inheritDoc}
      */
     @Override
-    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(
-            final ExtendedRequest<R> request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super R> resultHandler) {
+    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request,
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final int i = messageID.getAndIncrement();
-        final LDAPExtendedFutureResultImpl<R> future =
-                new LDAPExtendedFutureResultImpl<R>(i, request, resultHandler,
-                        intermediateResponseHandler, this);
+        final LDAPExtendedFutureResultImpl<R> future = new LDAPExtendedFutureResultImpl<R>(i, request,
+                intermediateResponseHandler, this);
         serverConnection.handleExtendedRequest(i, request, future, future);
         return future;
     }
@@ -237,12 +221,9 @@
      */
     @Override
     public FutureResult<Result> modifyAsync(final ModifyRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final int i = messageID.getAndIncrement();
-        final LDAPFutureResultImpl future =
-                new LDAPFutureResultImpl(i, request, resultHandler, intermediateResponseHandler,
-                        this);
+        final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request, intermediateResponseHandler, this);
         serverConnection.handleModify(i, request, future, future);
         return future;
     }
@@ -252,12 +233,9 @@
      */
     @Override
     public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final int i = messageID.getAndIncrement();
-        final LDAPFutureResultImpl future =
-                new LDAPFutureResultImpl(i, request, resultHandler, intermediateResponseHandler,
-                        this);
+        final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request, intermediateResponseHandler, this);
         serverConnection.handleModifyDN(i, request, future, future);
         return future;
     }
@@ -276,19 +254,18 @@
      */
     @Override
     public FutureResult<Result> searchAsync(final SearchRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final SearchResultHandler resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) {
         final int i = messageID.getAndIncrement();
-        final LDAPSearchFutureResultImpl future =
-                new LDAPSearchFutureResultImpl(i, request, resultHandler,
-                        intermediateResponseHandler, this);
-        serverConnection.handleSearch(i, request, future, future);
+        final LDAPSearchFutureResultImpl future = new LDAPSearchFutureResultImpl(i, request, entryHandler,
+                intermediateResponseHandler, this);
+        serverConnection.handleSearch(i, request, future, future, future);
         return future;
     }
 
     /**
      * {@inheritDoc}
      */
+    @Override
     public String toString() {
         StringBuilder builder = new StringBuilder();
         builder.append("InternalConnection(");
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/InternalConnectionFactory.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/InternalConnectionFactory.java
index af979b5..ca78947 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/InternalConnectionFactory.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/InternalConnectionFactory.java
@@ -22,12 +22,16 @@
  *
  *
  *      Copyright 2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap;
 
-import com.forgerock.opendj.util.CompletedFutureResult;
+import org.forgerock.util.promise.Promise;
+
+import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
+import static org.forgerock.util.promise.Promises.*;
+
 
 /**
  * A special {@code ConnectionFactory} which waits for internal connection
@@ -63,30 +67,25 @@
         // Nothing to do.
     }
 
+    @Override
     public Connection getConnection() throws ErrorResultException {
         final ServerConnection<Integer> serverConnection = factory.handleAccept(clientContext);
         return new InternalConnection(serverConnection);
     }
 
-    public FutureResult<Connection> getConnectionAsync(
-            final ResultHandler<? super Connection> handler) {
+    @Override
+    public Promise<Connection, ErrorResultException> getConnectionAsync() {
         final ServerConnection<Integer> serverConnection;
         try {
             serverConnection = factory.handleAccept(clientContext);
         } catch (final ErrorResultException e) {
-            if (handler != null) {
-                handler.handleErrorResult(e);
-            }
-            return new CompletedFutureResult<Connection>(e);
+            return newFailedFutureResult(e);
         }
 
-        final InternalConnection connection = new InternalConnection(serverConnection);
-        if (handler != null) {
-            handler.handleResult(connection);
-        }
-        return new CompletedFutureResult<Connection>(connection);
+        return newSuccessfulPromise((Connection) new InternalConnection(serverConnection));
     }
 
+    @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder();
         builder.append("InternalConnectionFactory(");
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPConnectionFactory.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPConnectionFactory.java
index 1881392..9e7cf6a 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPConnectionFactory.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPConnectionFactory.java
@@ -32,6 +32,7 @@
 import org.forgerock.opendj.ldap.spi.LDAPConnectionFactoryImpl;
 import org.forgerock.opendj.ldap.spi.TransportProvider;
 import org.forgerock.util.Reject;
+import org.forgerock.util.promise.Promise;
 
 /**
  * A factory class which can be used to obtain connections to an LDAP Directory
@@ -47,7 +48,7 @@
     /*
      * Transport provider that provides the implementation of this factory.
      */
-    private TransportProvider provider;
+    private final TransportProvider provider;
 
     /**
      * Creates a new LDAP connection factory which can be used to create LDAP
@@ -96,9 +97,8 @@
     }
 
     @Override
-    public FutureResult<Connection> getConnectionAsync(
-            final ResultHandler<? super Connection> handler) {
-        return impl.getConnectionAsync(handler);
+    public Promise<Connection, ErrorResultException> getConnectionAsync() {
+        return impl.getConnectionAsync();
     }
 
     @Override
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/LoadBalancer.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/LoadBalancer.java
index c3edcdf..dfd8078 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/LoadBalancer.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/LoadBalancer.java
@@ -22,13 +22,15 @@
  *
  *
  *      Copyright 2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap;
 
-import com.forgerock.opendj.util.CompletedFutureResult;
 import org.forgerock.util.Reject;
+import org.forgerock.util.promise.Promise;
+
+import static org.forgerock.util.promise.Promises.*;
 
 /**
  * A load balancing connection factory allocates connections using the provided
@@ -54,20 +56,16 @@
     }
 
     @Override
-    public FutureResult<Connection> getConnectionAsync(
-            final ResultHandler<? super Connection> resultHandler) {
+    public Promise<Connection, ErrorResultException> getConnectionAsync() {
         final ConnectionFactory factory;
 
         try {
             factory = algorithm.getConnectionFactory();
         } catch (final ErrorResultException e) {
-            if (resultHandler != null) {
-                resultHandler.handleErrorResult(e);
-            }
-            return new CompletedFutureResult<Connection>(e);
+            return newFailedPromise(e);
         }
 
-        return factory.getConnectionAsync(resultHandler);
+        return factory.getConnectionAsync();
     }
 
     @Override
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java
index b8065b0..f767725 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java
@@ -20,7 +20,7 @@
  *
  * CDDL HEADER END
  *
- *      Copyright 2013 ForgeRock AS.
+ *      Copyright 2013-2014 ForgeRock AS.
  */
 package org.forgerock.opendj.ldap;
 
@@ -248,7 +248,7 @@
             }
             resultHandler.handleResult(getResult(request, null, request));
         } catch (final ErrorResultException e) {
-            resultHandler.handleErrorResult(e);
+            resultHandler.handleError(e);
         }
     }
 
@@ -279,17 +279,17 @@
             }
             resultHandler.handleResult(getBindResult(request, entry, entry));
         } catch (final LocalizedIllegalArgumentException e) {
-            resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR, e));
+            resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, e));
         } catch (final EntryNotFoundException e) {
             /*
              * Usually you would not include a diagnostic message, but we'll add
              * one here because the memory back-end is not intended for
              * production use.
              */
-            resultHandler.handleErrorResult(newErrorResult(ResultCode.INVALID_CREDENTIALS,
+            resultHandler.handleError(newErrorResult(ResultCode.INVALID_CREDENTIALS,
                     "Unknown user"));
         } catch (final ErrorResultException e) {
-            resultHandler.handleErrorResult(e);
+            resultHandler.handleError(e);
         }
     }
 
@@ -310,7 +310,7 @@
             resultHandler.handleResult(getCompareResult(request, entry, entry.containsAttribute(
                     assertion, null)));
         } catch (final ErrorResultException e) {
-            resultHandler.handleErrorResult(e);
+            resultHandler.handleError(e);
         }
     }
 
@@ -338,9 +338,9 @@
             }
             resultHandler.handleResult(getResult(request, entry, null));
         } catch (final DecodeException e) {
-            resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR, e));
+            resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, e));
         } catch (final ErrorResultException e) {
-            resultHandler.handleErrorResult(e);
+            resultHandler.handleError(e);
         }
     }
 
@@ -349,7 +349,7 @@
             final RequestContext requestContext, final ExtendedRequest<R> request,
             final IntermediateResponseHandler intermediateResponseHandler,
             final ResultHandler<R> resultHandler) {
-        resultHandler.handleErrorResult(newErrorResult(ResultCode.UNWILLING_TO_PERFORM,
+        resultHandler.handleError(newErrorResult(ResultCode.UNWILLING_TO_PERFORM,
                 "Extended request operation not supported"));
     }
 
@@ -368,7 +368,7 @@
             }
             resultHandler.handleResult(getResult(request, entry, newEntry));
         } catch (final ErrorResultException e) {
-            resultHandler.handleErrorResult(e);
+            resultHandler.handleError(e);
         }
     }
 
@@ -376,42 +376,40 @@
     public void handleModifyDN(final RequestContext requestContext, final ModifyDNRequest request,
             final IntermediateResponseHandler intermediateResponseHandler,
             final ResultHandler<Result> resultHandler) {
-        resultHandler.handleErrorResult(newErrorResult(ResultCode.UNWILLING_TO_PERFORM,
+        resultHandler.handleError(newErrorResult(ResultCode.UNWILLING_TO_PERFORM,
                 "ModifyDN request operation not supported"));
     }
 
     @Override
     public void handleSearch(final RequestContext requestContext, final SearchRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final SearchResultHandler resultHandler) {
+        final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler,
+        ResultHandler<Result> resultHandler) {
         try {
             final DN dn = request.getName();
             final SearchScope scope = request.getScope();
             final Filter filter = request.getFilter();
             final Matcher matcher = filter.matcher(schema);
             final AttributeFilter attributeFilter =
-                    new AttributeFilter(request.getAttributes(), schema).typesOnly(request
-                            .isTypesOnly());
+                new AttributeFilter(request.getAttributes(), schema).typesOnly(request.isTypesOnly());
             if (scope.equals(SearchScope.BASE_OBJECT)) {
                 final Entry baseEntry = getRequiredEntry(request, dn);
                 if (matcher.matches(baseEntry).toBoolean()) {
-                    sendEntry(attributeFilter, resultHandler, baseEntry);
+                    sendEntry(attributeFilter, entryHandler, baseEntry);
                 }
                 resultHandler.handleResult(newResult(ResultCode.SUCCESS));
             } else if (scope.equals(SearchScope.SINGLE_LEVEL) || scope.equals(SearchScope.SUBORDINATES)
                 || scope.equals(SearchScope.WHOLE_SUBTREE)) {
-                searchWithSubordinates(requestContext, resultHandler, dn, matcher, attributeFilter,
-                        request.getSizeLimit(), scope, request.getControl(
-                                SimplePagedResultsControl.DECODER, new DecodeOptions()));
+                searchWithSubordinates(requestContext, entryHandler, resultHandler, dn, matcher, attributeFilter,
+                    request.getSizeLimit(), scope,
+                    request.getControl(SimplePagedResultsControl.DECODER, new DecodeOptions()));
             } else {
-                throw newErrorResult(ResultCode.PROTOCOL_ERROR,
-                        "Search request contains an unsupported search scope");
+                throw newErrorResult(ResultCode.PROTOCOL_ERROR, "Search request contains an unsupported search scope");
             }
         } catch (DecodeException e) {
-            resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
-                    ResultCode.PROTOCOL_ERROR, e.getMessage(), e));
+            resultHandler.handleError(ErrorResultException.newErrorResult(ResultCode.PROTOCOL_ERROR, e.getMessage(),
+                e));
         } catch (final ErrorResultException e) {
-            resultHandler.handleErrorResult(e);
+            resultHandler.handleError(e);
         }
     }
 
@@ -490,11 +488,10 @@
      * @throws ErrorResultException
      *           If the request is unsuccessful.
      */
-    private void searchWithSubordinates(final RequestContext requestContext,
-            final SearchResultHandler resultHandler, final DN dn, final Matcher matcher,
+    private void searchWithSubordinates(final RequestContext requestContext, final SearchResultHandler entryHandler,
+            final ResultHandler<Result> resultHandler, final DN dn, final Matcher matcher,
             final AttributeFilter attributeFilter, final int sizeLimit, SearchScope scope,
-            SimplePagedResultsControl pagedResults) throws CancelledResultException,
-            ErrorResultException {
+            SimplePagedResultsControl pagedResults) throws CancelledResultException, ErrorResultException {
         final int pageSize = pagedResults != null ? pagedResults.getSize() : 0;
         final int offset = (pagedResults != null && !pagedResults.getCookie().isEmpty())
                 ? Integer.valueOf(pagedResults.getCookie().toString()) : 0;
@@ -504,7 +501,7 @@
         for (final Entry entry : subtree.values()) {
             requestContext.checkIfCancelled(false);
             if (scope.equals(SearchScope.WHOLE_SUBTREE) || entry.getName().isChildOf(dn)
-                || (scope.equals(SearchScope.SUBORDINATES) && !entry.getName().equals(dn))) {
+                    || (scope.equals(SearchScope.SUBORDINATES) && !entry.getName().equals(dn))) {
                 if (matcher.matches(entry).toBoolean()) {
                     /*
                      * This entry is going to be returned to the client so it
@@ -522,7 +519,7 @@
                     }
 
                     // Send the entry back to the client.
-                    if (!sendEntry(attributeFilter, resultHandler, entry)) {
+                    if (!sendEntry(attributeFilter, entryHandler, entry)) {
                         // Client has disconnected or cancelled.
                         break;
                     }
@@ -538,9 +535,8 @@
         }
         final Result result = newResult(ResultCode.SUCCESS);
         if (pageSize > 0) {
-            final ByteString cookie =
-                    numberOfResults == pageSize ? ByteString.valueOf(String.valueOf(position))
-                            : ByteString.empty();
+            final ByteString cookie = numberOfResults == pageSize ? ByteString.valueOf(String.valueOf(position))
+                    : ByteString.empty();
             result.addControl(SimplePagedResultsControl.newControl(true, 0, cookie));
         }
         resultHandler.handleResult(result);
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/RequestHandler.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/RequestHandler.java
index c94f0ca..94d5a4c 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/RequestHandler.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/RequestHandler.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap;
@@ -213,13 +213,16 @@
      * @param intermediateResponseHandler
      *            The handler which should be used to send back any intermediate
      *            responses to the client.
+     * @param entryHandler
+     *            The entry handler which should be used to send back the search
+     *            entries results to the client.
      * @param resultHandler
-     *            The handler which should be used to send back the search
-     *            results to the client.
+     *            The handler which should be used to send back the result to
+     *            the client.
      * @throws UnsupportedOperationException
      *             If this request handler does not handle search requests.
      */
     void handleSearch(C requestContext, SearchRequest request,
-            IntermediateResponseHandler intermediateResponseHandler,
-            SearchResultHandler resultHandler);
+        IntermediateResponseHandler intermediateResponseHandler, SearchResultHandler entryHandler,
+        ResultHandler<Result> resultHandler);
 }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactoryAdapter.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactoryAdapter.java
index 05422c7..631afdd 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactoryAdapter.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactoryAdapter.java
@@ -21,7 +21,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2011-2013 ForgeRock AS
+ *      Copyright 2011-2014 ForgeRock AS
  */
 
 package org.forgerock.opendj.ldap;
@@ -103,7 +103,7 @@
                 final R cancelResult =
                         request.getResultDecoder().newExtendedErrorResult(ResultCode.TOO_LATE, "",
                                 "");
-                resultHandler.handleErrorResult(ErrorResultException.newErrorResult(cancelResult));
+                resultHandler.handleError(ErrorResultException.newErrorResult(cancelResult));
             }
         }
 
@@ -238,7 +238,7 @@
          * {@inheritDoc}
          */
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
+        public void handleError(final ErrorResultException error) {
             if (clientConnection.removePendingRequest(this)) {
                 if (setResult(error.getResult())) {
                     /*
@@ -248,7 +248,7 @@
                      * not be sent to the client.
                      */
                 }
-                resultHandler.handleErrorResult(error);
+                resultHandler.handleError(error);
             }
         }
 
@@ -293,7 +293,7 @@
                 if (cancelResultHandler != null) {
                     final Result result =
                             Responses.newGenericExtendedResult(ResultCode.CANNOT_CANCEL);
-                    cancelResultHandler.handleErrorResult(newErrorResult(result));
+                    cancelResultHandler.handleError(newErrorResult(result));
                 }
                 return;
             }
@@ -370,7 +370,7 @@
                     cancelResultHandler.handleResult(result);
                 } else {
                     final Result result = Responses.newGenericExtendedResult(ResultCode.TOO_LATE);
-                    cancelResultHandler.handleErrorResult(ErrorResultException
+                    cancelResultHandler.handleError(ErrorResultException
                             .newErrorResult(result));
                 }
             }
@@ -445,13 +445,16 @@
     /**
      * Search request context implementation.
      */
-    private final static class SearchRequestContextImpl extends
-            RequestContextImpl<Result, SearchResultHandler> implements SearchResultHandler {
+    private final static class SearchRequestContextImpl extends RequestContextImpl<Result, ResultHandler<Result>>
+        implements SearchResultHandler {
+
+        private final SearchResultHandler entryHandler;
 
         private SearchRequestContextImpl(final ServerConnectionImpl clientConnection,
-                final SearchResultHandler resultHandler, final int messageID,
-                final boolean isCancelSupported) {
+            final SearchResultHandler entryHandler, final ResultHandler<Result> resultHandler, final int messageID,
+            final boolean isCancelSupported) {
             super(clientConnection, resultHandler, messageID, isCancelSupported);
+            this.entryHandler = entryHandler;
         }
 
         /**
@@ -459,7 +462,7 @@
          */
         @Override
         public boolean handleEntry(final SearchResultEntry entry) {
-            return resultHandler.handleEntry(entry);
+            return entryHandler.handleEntry(entry);
         }
 
         /**
@@ -467,7 +470,7 @@
          */
         @Override
         public boolean handleReference(final SearchResultReference reference) {
-            return resultHandler.handleReference(reference);
+            return entryHandler.handleReference(reference);
         }
     }
 
@@ -604,7 +607,7 @@
                                     new DecodeOptions());
                 } catch (final DecodeException e) {
                     // Couldn't decode a cancel request.
-                    resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR, e
+                    resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, e
                             .getLocalizedMessage()));
                     return;
                 }
@@ -630,8 +633,7 @@
                          * Couldn't find the request. Invoke on context in order
                          * to remove pending request.
                          */
-                        requestContext
-                                .handleErrorResult(newErrorResult(ResultCode.NO_SUCH_OPERATION));
+                        requestContext.handleError(newErrorResult(ResultCode.NO_SUCH_OPERATION));
                     }
                 }
             } else {
@@ -691,13 +693,13 @@
          */
         @Override
         public void handleSearch(final Integer messageID, final SearchRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final SearchResultHandler resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler,
+            final ResultHandler<Result> resultHandler) {
             final SearchRequestContextImpl requestContext =
-                    new SearchRequestContextImpl(this, resultHandler, messageID, true);
+                new SearchRequestContextImpl(this, entryHandler, resultHandler, messageID, true);
             if (addPendingRequest(requestContext)) {
-                requestHandler.handleSearch(requestContext, request, intermediateResponseHandler,
-                        requestContext);
+                requestHandler.handleSearch(requestContext, request, intermediateResponseHandler, entryHandler,
+                    requestContext);
             }
         }
 
@@ -706,14 +708,13 @@
 
             if (isClosed.get()) {
                 final LocalizableMessage message = INFO_CLIENT_CONNECTION_CLOSING.get();
-                requestContext.handleErrorResult(newErrorResult(ResultCode.UNWILLING_TO_PERFORM,
+                requestContext.handleError(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()));
+                requestContext.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, message.toString()));
                 return false;
             } else if (isClosed.get()) {
                 /*
@@ -723,8 +724,7 @@
                 pendingRequests.remove(messageID);
 
                 final LocalizableMessage message = INFO_CLIENT_CONNECTION_CLOSING.get();
-                requestContext.handleErrorResult(newErrorResult(ResultCode.UNWILLING_TO_PERFORM,
-                        message.toString()));
+                requestContext.handleError(newErrorResult(ResultCode.UNWILLING_TO_PERFORM, message.toString()));
                 return false;
             } else {
                 /*
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/ResultHandler.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/ResultHandler.java
index 316c7c9..a7cf7fe 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/ResultHandler.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/ResultHandler.java
@@ -22,11 +22,14 @@
  *
  *
  *      Copyright 2009 Sun Microsystems, Inc.
- *      Portions copyright 2011 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap;
 
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.SuccessHandler;
+
 /**
  * A completion handler for consuming the result of an asynchronous operation or
  * connection attempts.
@@ -46,7 +49,7 @@
  * @param <S>
  *            The type of result handled by this result handler.
  */
-public interface ResultHandler<S> {
+public interface ResultHandler<S> extends SuccessHandler<S>, FailureHandler<ErrorResultException> {
     /**
      * Invoked when the asynchronous operation has failed.
      *
@@ -54,7 +57,7 @@
      *            The error result exception indicating why the asynchronous
      *            operation has failed.
      */
-    void handleErrorResult(ErrorResultException error);
+    void handleError(ErrorResultException error);
 
     /**
      * Invoked when the asynchronous operation has completed successfully.
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/RootDSE.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/RootDSE.java
index 574b7e6..1b0b6bf 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/RootDSE.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/RootDSE.java
@@ -36,8 +36,9 @@
 import org.forgerock.opendj.ldap.schema.CoreSchema;
 
 import com.forgerock.opendj.util.Collections2;
-import com.forgerock.opendj.util.FutureResultTransformer;
+
 import org.forgerock.util.Reject;
+import org.forgerock.util.promise.Function;
 
 /**
  * The root DSE is a DSA-specific Entry (DSE) and not part of any naming context
@@ -151,21 +152,13 @@
      */
     public static FutureResult<RootDSE> readRootDSEAsync(final Connection connection,
             final ResultHandler<? super RootDSE> handler) {
-        final FutureResultTransformer<SearchResultEntry, RootDSE> future =
-                new FutureResultTransformer<SearchResultEntry, RootDSE>(handler) {
-
-                    @Override
-                    protected RootDSE transformResult(final SearchResultEntry result)
-                            throws ErrorResultException {
-                        return valueOf(result);
-                    }
-
-                };
-
-        final FutureResult<SearchResultEntry> innerFuture =
-                connection.searchSingleEntryAsync(SEARCH_REQUEST, future);
-        future.setFutureResult(innerFuture);
-        return future;
+        return FutureResultWrapper.asFutureResult(connection.searchSingleEntryAsync(SEARCH_REQUEST).then(
+            new Function<SearchResultEntry, RootDSE, ErrorResultException>() {
+                @Override
+                public RootDSE apply(SearchResultEntry result) {
+                    return valueOf(result);
+                }
+            }));
     }
 
     /**
@@ -422,7 +415,7 @@
 
     private <N> Collection<N> getMultiValuedAttribute(
             final AttributeDescription attributeDescription,
-            final Function<ByteString, N, Void> function) {
+        final org.forgerock.opendj.ldap.Function<ByteString, N, Void> function) {
         // The returned collection is unmodifiable because we may need to
         // return an empty collection if the attribute does not exist in the
         // underlying entry. If a value is then added to the returned empty
@@ -438,7 +431,7 @@
     }
 
     private <N> N getSingleValuedAttribute(final AttributeDescription attributeDescription,
-            final Function<ByteString, N, Void> function) {
+        final org.forgerock.opendj.ldap.Function<ByteString, N, Void> function) {
         final Attribute attr = entry.getAttribute(attributeDescription);
         if (attr == null || attr.isEmpty()) {
             return null;
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/SearchResultHandler.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/SearchResultHandler.java
index 773681e..3ac2143 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/SearchResultHandler.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/SearchResultHandler.java
@@ -22,12 +22,11 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap;
 
-import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
 
@@ -45,7 +44,7 @@
  * avoid keeping the invoking thread from dispatching to other completion
  * handlers.
  */
-public interface SearchResultHandler extends ResultHandler<Result> {
+public interface SearchResultHandler {
     /**
      * Invoked each time a search result entry is returned from an asynchronous
      * search operation.
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/responses/AbstractExtendedResultDecoder.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/responses/AbstractExtendedResultDecoder.java
index 49d277d..285d937 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/responses/AbstractExtendedResultDecoder.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/responses/AbstractExtendedResultDecoder.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2010 Sun Microsystems, Inc.
- *      Portions copyright 2012 ForgeRock AS.
+ *      Portions copyright 2012-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap.responses;
@@ -68,13 +68,13 @@
         return new ResultHandler<S>() {
 
             @Override
-            public void handleErrorResult(final ErrorResultException error) {
+            public void handleError(final ErrorResultException error) {
                 final Result result = error.getResult();
                 final R adaptedResult =
                         request.getResultDecoder().newExtendedErrorResult(result.getResultCode(),
                                 result.getMatchedDN(), result.getDiagnosticMessage());
                 adaptedResult.setCause(result.getCause());
-                resultHandler.handleErrorResult(newErrorResult(adaptedResult));
+                resultHandler.handleError(newErrorResult(adaptedResult));
             }
 
             @Override
@@ -85,7 +85,7 @@
                     resultHandler.handleResult(adaptedResult);
                 } catch (final DecodeException e) {
                     final R adaptedResult = request.getResultDecoder().adaptDecodeException(e);
-                    resultHandler.handleErrorResult(newErrorResult(adaptedResult));
+                    resultHandler.handleError(newErrorResult(adaptedResult));
                 }
             }
 
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java
index 4d8b361..eff6d99 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java
@@ -27,9 +27,6 @@
  */
 package org.forgerock.opendj.ldap.schema;
 
-import static org.forgerock.opendj.ldap.AttributeDescription.objectClass;
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-
 import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedList;
@@ -51,11 +48,15 @@
 import org.forgerock.opendj.ldap.FutureResult;
 import org.forgerock.opendj.ldap.LinkedAttribute;
 import org.forgerock.opendj.ldap.RDN;
-import org.forgerock.opendj.ldap.ResultHandler;
-
-import com.forgerock.opendj.util.FutureResultTransformer;
-import com.forgerock.opendj.util.StaticUtils;
 import org.forgerock.util.Reject;
+import org.forgerock.util.promise.Function;
+
+import com.forgerock.opendj.util.StaticUtils;
+
+import static org.forgerock.opendj.ldap.AttributeDescription.*;
+import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
 
 /**
  * This class defines a data structure that holds information about the
@@ -996,8 +997,6 @@
      *            read.
      * @param name
      *            The distinguished name of the subschema sub-entry.
-     * @param handler
-     *            A result handler which can be used to asynchronously process
      *            the operation result when it is received, may be {@code null}.
      * @return A future representing the retrieved schema.
      * @throws UnsupportedOperationException
@@ -1008,24 +1007,15 @@
      * @throws NullPointerException
      *             If the {@code connection} or {@code name} was {@code null}.
      */
-    public static FutureResult<Schema> readSchemaAsync(final Connection connection, final DN name,
-            final ResultHandler<? super Schema> handler) {
-        final FutureResultTransformer<SchemaBuilder, Schema> future =
-                new FutureResultTransformer<SchemaBuilder, Schema>(handler) {
-
+    public static FutureResult<Schema> readSchemaAsync(final Connection connection, final DN name) {
+        final SchemaBuilder builder = new SchemaBuilder();
+        return asFutureResult(builder.addSchemaAsync(connection, name, true).then(
+                new Function<SchemaBuilder, Schema, ErrorResultException>() {
                     @Override
-                    protected Schema transformResult(final SchemaBuilder builder)
-                            throws ErrorResultException {
+                    public Schema apply(SchemaBuilder builder) throws ErrorResultException {
                         return builder.toSchema();
                     }
-
-                };
-
-        final SchemaBuilder builder = new SchemaBuilder();
-        final FutureResult<SchemaBuilder> innerFuture =
-                builder.addSchemaAsync(connection, name, future, true);
-        future.setFutureResult(innerFuture);
-        return future;
+                }));
     }
 
     /**
@@ -1085,9 +1075,6 @@
      * @param name
      *            The distinguished name of the entry whose schema is to be
      *            located.
-     * @param handler
-     *            A result handler which can be used to asynchronously process
-     *            the operation result when it is received, may be {@code null}.
      * @return A future representing the retrieved schema.
      * @throws UnsupportedOperationException
      *             If the connection does not support search operations.
@@ -1097,25 +1084,16 @@
      * @throws NullPointerException
      *             If the {@code connection} or {@code name} was {@code null}.
      */
-    public static FutureResult<Schema> readSchemaForEntryAsync(final Connection connection,
-            final DN name, final ResultHandler<? super Schema> handler) {
-        final FutureResultTransformer<SchemaBuilder, Schema> future =
-                new FutureResultTransformer<SchemaBuilder, Schema>(handler) {
-
-                    @Override
-                    protected Schema transformResult(final SchemaBuilder builder)
-                            throws ErrorResultException {
-                        return builder.toSchema();
-                    }
-
-                };
-
+    public static FutureResult<Schema> readSchemaForEntryAsync(final Connection connection, final DN name) {
         final SchemaBuilder builder = new SchemaBuilder();
-        final FutureResult<SchemaBuilder> innerFuture =
-                builder.addSchemaForEntryAsync(connection, name, future, true);
-        future.setFutureResult(innerFuture);
-        return future;
+        return asFutureResult(builder.addSchemaForEntryAsync(connection, name, true).then(
+            new Function<SchemaBuilder, Schema, ErrorResultException>() {
 
+                @Override
+                public Schema apply(SchemaBuilder builder) throws ErrorResultException {
+                    return builder.toSchema();
+                }
+            }));
     }
 
     /**
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
index 7697890..f49cfda 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
@@ -28,18 +28,6 @@
 
 package org.forgerock.opendj.ldap.schema;
 
-import static com.forgerock.opendj.util.StaticUtils.toLowerCase;
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-import static org.forgerock.opendj.ldap.schema.Schema.*;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.EXTENSIBLE_OBJECT_OBJECTCLASS_OID;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.OMR_GENERIC_ENUM_NAME;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.SCHEMA_PROPERTY_APPROX_RULE;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.TOP_OBJECTCLASS_NAME;
-import static org.forgerock.opendj.ldap.schema.SchemaUtils.unmodifiableCopyOfExtraProperties;
-import static org.forgerock.opendj.ldap.schema.SchemaUtils.unmodifiableCopyOfList;
-import static org.forgerock.opendj.ldap.schema.SchemaUtils.unmodifiableCopyOfSet;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -66,17 +54,26 @@
 import org.forgerock.opendj.ldap.Filter;
 import org.forgerock.opendj.ldap.FutureResult;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.SearchScope;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
+import org.forgerock.util.Reject;
+import org.forgerock.util.promise.AsyncFunction;
+import org.forgerock.util.promise.Function;
+import org.forgerock.util.promise.Promise;
 
-import com.forgerock.opendj.util.FutureResultTransformer;
-import com.forgerock.opendj.util.RecursiveFutureResult;
 import com.forgerock.opendj.util.StaticUtils;
 import com.forgerock.opendj.util.SubstringReader;
-import org.forgerock.util.Reject;
+
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
+import static org.forgerock.opendj.ldap.schema.Schema.*;
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
+import static com.forgerock.opendj.util.StaticUtils.*;
 
 /**
  * Schema builders should be used for incremental construction of new schemas.
@@ -94,15 +91,19 @@
     private static final String[] SUBSCHEMA_SUBENTRY_ATTRS =
             new String[] { ATTR_SUBSCHEMA_SUBENTRY };
 
-    // Constructs a search request for retrieving the subschemaSubentry
-    // attribute from the named entry.
+    /**
+     * Constructs a search request for retrieving the subschemaSubentry
+     * attribute from the named entry.
+     */
     private static SearchRequest getReadSchemaForEntrySearchRequest(final DN dn) {
         return Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT, Filter.objectClassPresent(),
                 SUBSCHEMA_SUBENTRY_ATTRS);
     }
 
-    // Constructs a search request for retrieving the named subschema
-    // sub-entry.
+    /**
+     * Constructs a search request for retrieving the named subschema
+     * sub-entry.
+     */
     private static SearchRequest getReadSchemaSearchRequest(final DN dn) {
         return Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT, SUBSCHEMA_FILTER,
                 SUBSCHEMA_ATTRS);
@@ -158,11 +159,13 @@
     private String defaultSyntaxOID;
     private String defaultMatchingRuleOID;
 
-    // A schema which should be copied into this builder on any mutation.
-    private Schema copyOnWriteSchema = null;
+    /** A schema which should be copied into this builder on any mutation. */
+    private Schema copyOnWriteSchema;
 
-    // A unique ID which can be used to uniquely identify schemas
-    // constructed without a name.
+    /**
+     * A unique ID which can be used to uniquely identify schemas
+     * constructed without a name.
+     */
     private static final AtomicInteger NEXT_SCHEMA_ID = new AtomicInteger();
 
     /**
@@ -256,9 +259,8 @@
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_ATTRTYPE_EXPECTED_OPEN_PARENTHESIS.get(definition, (reader
-                                .pos() - 1), String.valueOf(c));
+                final LocalizableMessage message = ERR_ATTR_SYNTAX_ATTRTYPE_EXPECTED_OPEN_PARENTHESIS.get(
+                    definition, reader.pos() - 1, String.valueOf(c));
                 throw new LocalizedIllegalArgumentException(message);
             }
 
@@ -298,39 +300,39 @@
                 if (tokenName == null) {
                     // No more tokens.
                     break;
-                } else if (tokenName.equalsIgnoreCase("name")) {
+                } else if ("name".equalsIgnoreCase(tokenName)) {
                     names = SchemaUtils.readNameDescriptors(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("desc")) {
+                } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the attribute type. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
                     description = SchemaUtils.readQuotedString(reader);
-                } else if (tokenName.equalsIgnoreCase("obsolete")) {
+                } else if ("obsolete".equalsIgnoreCase(tokenName)) {
                     // This indicates whether the attribute type should be
                     // considered obsolete. We do not need to do any more
                     // parsing for this token.
                     isObsolete = true;
-                } else if (tokenName.equalsIgnoreCase("sup")) {
+                } else if ("sup".equalsIgnoreCase(tokenName)) {
                     // This specifies the name or OID of the superior attribute
                     // type from which this attribute type should inherit its
                     // properties.
                     superiorType = SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("equality")) {
+                } else if ("equality".equalsIgnoreCase(tokenName)) {
                     // This specifies the name or OID of the equality matching
                     // rule to use for this attribute type.
                     equalityMatchingRule =
                             SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("ordering")) {
+                } else if ("ordering".equalsIgnoreCase(tokenName)) {
                     // This specifies the name or OID of the ordering matching
                     // rule to use for this attribute type.
                     orderingMatchingRule =
                             SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("substr")) {
+                } else if ("substr".equalsIgnoreCase(tokenName)) {
                     // This specifies the name or OID of the substring matching
                     // rule to use for this attribute type.
                     substringMatchingRule =
                             SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("syntax")) {
+                } else if ("syntax".equalsIgnoreCase(tokenName)) {
                     // This specifies the numeric OID of the syntax for this
                     // matching rule. It may optionally be immediately followed
                     // by an open curly brace, an integer definition, and a close
@@ -340,28 +342,28 @@
                     // does not impose any practical limit on the length of attribute
                     // values.
                     syntax = SchemaUtils.readOIDLen(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("single-definition")) {
+                } else if ("single-definition".equalsIgnoreCase(tokenName)) {
                     // This indicates that attributes of this type are allowed
                     // to have at most one definition. We do not need any more
                     // parsing for this token.
                     isSingleValue = true;
-                } else if (tokenName.equalsIgnoreCase("single-value")) {
+                } else if ("single-value".equalsIgnoreCase(tokenName)) {
                     // This indicates that attributes of this type are allowed
                     // to have at most one value. We do not need any more parsing
                     // for this token.
                     isSingleValue = true;
-                } else if (tokenName.equalsIgnoreCase("collective")) {
+                } else if ("collective".equalsIgnoreCase(tokenName)) {
                     // This indicates that attributes of this type are
                     // collective
                     // (i.e., have their values generated dynamically in some
                     // way). We do not need any more parsing for this token.
                     isCollective = true;
-                } else if (tokenName.equalsIgnoreCase("no-user-modification")) {
+                } else if ("no-user-modification".equalsIgnoreCase(tokenName)) {
                     // This indicates that the values of attributes of this type
                     // are not to be modified by end users. We do not need any
                     // more parsing for this token.
                     isNoUserModification = true;
-                } else if (tokenName.equalsIgnoreCase("usage")) {
+                } else if ("usage".equalsIgnoreCase(tokenName)) {
                     // This specifies the usage string for this attribute type.
                     // It should be followed by one of the strings
                     // "userApplications", "directoryOperation",
@@ -377,18 +379,17 @@
 
                     reader.reset();
                     final String usageStr = reader.read(length);
-                    if (usageStr.equalsIgnoreCase("userapplications")) {
+                    if ("userapplications".equalsIgnoreCase(usageStr)) {
                         attributeUsage = AttributeUsage.USER_APPLICATIONS;
-                    } else if (usageStr.equalsIgnoreCase("directoryoperation")) {
+                    } else if ("directoryoperation".equalsIgnoreCase(usageStr)) {
                         attributeUsage = AttributeUsage.DIRECTORY_OPERATION;
-                    } else if (usageStr.equalsIgnoreCase("distributedoperation")) {
+                    } else if ("distributedoperation".equalsIgnoreCase(usageStr)) {
                         attributeUsage = AttributeUsage.DISTRIBUTED_OPERATION;
-                    } else if (usageStr.equalsIgnoreCase("dsaoperation")) {
+                    } else if ("dsaoperation".equalsIgnoreCase(usageStr)) {
                         attributeUsage = AttributeUsage.DSA_OPERATION;
                     } else {
                         final LocalizableMessage message =
-                                WARN_ATTR_SYNTAX_ATTRTYPE_INVALID_ATTRIBUTE_USAGE1.get(definition,
-                                        usageStr);
+                            WARN_ATTR_SYNTAX_ATTRTYPE_INVALID_ATTRIBUTE_USAGE1.get(definition, usageStr);
                         throw new LocalizedIllegalArgumentException(message);
                     }
                 } else if (tokenName.matches("^X-[A-Za-z_-]+$")) {
@@ -556,9 +557,8 @@
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_DCR_EXPECTED_OPEN_PARENTHESIS.get(definition,
-                                (reader.pos() - 1), String.valueOf(c));
+                final LocalizableMessage message = ERR_ATTR_SYNTAX_DCR_EXPECTED_OPEN_PARENTHESIS.get(
+                    definition, reader.pos() - 1, String.valueOf(c));
                 throw new LocalizedIllegalArgumentException(message);
             }
 
@@ -593,27 +593,27 @@
                 if (tokenName == null) {
                     // No more tokens.
                     break;
-                } else if (tokenName.equalsIgnoreCase("name")) {
+                } else if ("name".equalsIgnoreCase(tokenName)) {
                     names = SchemaUtils.readNameDescriptors(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("desc")) {
+                } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the attribute type. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
                     description = SchemaUtils.readQuotedString(reader);
-                } else if (tokenName.equalsIgnoreCase("obsolete")) {
+                } else if ("obsolete".equalsIgnoreCase(tokenName)) {
                     // This indicates whether the attribute type should be
                     // considered obsolete. We do not need to do any more
                     // parsing for this token.
                     isObsolete = true;
-                } else if (tokenName.equalsIgnoreCase("aux")) {
+                } else if ("aux".equalsIgnoreCase(tokenName)) {
                     auxiliaryClasses = SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("must")) {
+                } else if ("must".equalsIgnoreCase(tokenName)) {
                     requiredAttributes =
                             SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("may")) {
+                } else if ("may".equalsIgnoreCase(tokenName)) {
                     optionalAttributes =
                             SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("not")) {
+                } else if ("not".equalsIgnoreCase(tokenName)) {
                     prohibitedAttributes =
                             SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
                 } else if (tokenName.matches("^X-[A-Za-z_-]+$")) {
@@ -786,9 +786,8 @@
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_DSR_EXPECTED_OPEN_PARENTHESIS.get(definition,
-                                (reader.pos() - 1), String.valueOf(c));
+                final LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_EXPECTED_OPEN_PARENTHESIS.get(
+                    definition, reader.pos() - 1, String.valueOf(c));
                 throw new LocalizedIllegalArgumentException(message);
             }
 
@@ -820,21 +819,21 @@
                 if (tokenName == null) {
                     // No more tokens.
                     break;
-                } else if (tokenName.equalsIgnoreCase("name")) {
+                } else if ("name".equalsIgnoreCase(tokenName)) {
                     names = SchemaUtils.readNameDescriptors(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("desc")) {
+                } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the attribute type. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
                     description = SchemaUtils.readQuotedString(reader);
-                } else if (tokenName.equalsIgnoreCase("obsolete")) {
+                } else if ("obsolete".equalsIgnoreCase(tokenName)) {
                     // This indicates whether the attribute type should be
                     // considered obsolete. We do not need to do any more
                     // parsing for this token.
                     isObsolete = true;
-                } else if (tokenName.equalsIgnoreCase("form")) {
+                } else if ("form".equalsIgnoreCase(tokenName)) {
                     nameForm = SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("sup")) {
+                } else if ("sup".equalsIgnoreCase(tokenName)) {
                     superiorRules = SchemaUtils.readRuleIDs(reader);
                 } else if (tokenName.matches("^X-[A-Za-z_-]+$")) {
                     // This must be a non-standard property and it must be
@@ -959,9 +958,8 @@
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_MR_EXPECTED_OPEN_PARENTHESIS.get(definition,
-                                (reader.pos() - 1), String.valueOf(c));
+                final LocalizableMessage message = ERR_ATTR_SYNTAX_MR_EXPECTED_OPEN_PARENTHESIS.get(
+                    definition, reader.pos() - 1, String.valueOf(c));
                 throw new LocalizedIllegalArgumentException(message);
             }
 
@@ -990,19 +988,19 @@
                 if (tokenName == null) {
                     // No more tokens.
                     break;
-                } else if (tokenName.equalsIgnoreCase("name")) {
+                } else if ("name".equalsIgnoreCase(tokenName)) {
                     matchingRuleBuilder.names(SchemaUtils.readNameDescriptors(reader, allowMalformedNamesAndOptions));
-                } else if (tokenName.equalsIgnoreCase("desc")) {
+                } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the matching rule. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
                     matchingRuleBuilder.description(SchemaUtils.readQuotedString(reader));
-                } else if (tokenName.equalsIgnoreCase("obsolete")) {
+                } else if ("obsolete".equalsIgnoreCase(tokenName)) {
                     // This indicates whether the matching rule should be
                     // considered obsolete. We do not need to do any more
                     // parsing for this token.
                     matchingRuleBuilder.obsolete(true);
-                } else if (tokenName.equalsIgnoreCase("syntax")) {
+                } else if ("syntax".equalsIgnoreCase(tokenName)) {
                     syntax = SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
                     matchingRuleBuilder.syntaxOID(syntax);
                 } else if (tokenName.matches("^X-[A-Za-z_-]+$")) {
@@ -1081,9 +1079,8 @@
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_MRUSE_EXPECTED_OPEN_PARENTHESIS.get(definition, (reader
-                                .pos() - 1), String.valueOf(c));
+                final LocalizableMessage message = ERR_ATTR_SYNTAX_MRUSE_EXPECTED_OPEN_PARENTHESIS.get(
+                    definition, reader.pos() - 1, String.valueOf(c));
                 throw new LocalizedIllegalArgumentException(message);
             }
 
@@ -1114,19 +1111,19 @@
                 if (tokenName == null) {
                     // No more tokens.
                     break;
-                } else if (tokenName.equalsIgnoreCase("name")) {
+                } else if ("name".equalsIgnoreCase(tokenName)) {
                     names = SchemaUtils.readNameDescriptors(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("desc")) {
+                } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the attribute type. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
                     description = SchemaUtils.readQuotedString(reader);
-                } else if (tokenName.equalsIgnoreCase("obsolete")) {
+                } else if ("obsolete".equalsIgnoreCase(tokenName)) {
                     // This indicates whether the attribute type should be
                     // considered obsolete. We do not need to do any more
                     // parsing for this token.
                     isObsolete = true;
-                } else if (tokenName.equalsIgnoreCase("applies")) {
+                } else if ("applies".equalsIgnoreCase(tokenName)) {
                     attributes = SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
                 } else if (tokenName.matches("^X-[A-Za-z_-]+$")) {
                     // This must be a non-standard property and it must be
@@ -1264,9 +1261,8 @@
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_NAME_FORM_EXPECTED_OPEN_PARENTHESIS.get(definition, (reader
-                                .pos() - 1), c);
+                final LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_EXPECTED_OPEN_PARENTHESIS.get(
+                    definition, reader.pos() - 1, c);
                 throw new LocalizedIllegalArgumentException(message);
             }
 
@@ -1298,27 +1294,27 @@
                 if (tokenName == null) {
                     // No more tokens.
                     break;
-                } else if (tokenName.equalsIgnoreCase("name")) {
+                } else if ("name".equalsIgnoreCase(tokenName)) {
                     nameFormBuilder.names(SchemaUtils.readNameDescriptors(reader,
                             allowMalformedNamesAndOptions));
-                } else if (tokenName.equalsIgnoreCase("desc")) {
+                } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the attribute type. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
                     nameFormBuilder.description(SchemaUtils.readQuotedString(reader));
-                } else if (tokenName.equalsIgnoreCase("obsolete")) {
+                } else if ("obsolete".equalsIgnoreCase(tokenName)) {
                     // This indicates whether the attribute type should be
                     // considered obsolete. We do not need to do any more
                     // parsing for this token.
                     nameFormBuilder.obsolete(true);
-                } else if (tokenName.equalsIgnoreCase("oc")) {
+                } else if ("oc".equalsIgnoreCase(tokenName)) {
                     structuralOID = SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
                     nameFormBuilder.structuralObjectClassOID(structuralOID);
-                } else if (tokenName.equalsIgnoreCase("must")) {
+                } else if ("must".equalsIgnoreCase(tokenName)) {
                     requiredAttributes =
                             SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
                     nameFormBuilder.requiredAttributes(requiredAttributes);
-                } else if (tokenName.equalsIgnoreCase("may")) {
+                } else if ("may".equalsIgnoreCase(tokenName)) {
                     nameFormBuilder.optionalAttributes(SchemaUtils.readOIDs(reader,
                             allowMalformedNamesAndOptions));
                 } else if (tokenName.matches("^X-[A-Za-z_-]+$")) {
@@ -1578,9 +1574,8 @@
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_OBJECTCLASS_EXPECTED_OPEN_PARENTHESIS1.get(definition,
-                                (reader.pos() - 1), String.valueOf(c));
+                final LocalizableMessage message =  ERR_ATTR_SYNTAX_OBJECTCLASS_EXPECTED_OPEN_PARENTHESIS1.get(
+                            definition, reader.pos() - 1, String.valueOf(c));
                 throw new LocalizedIllegalArgumentException(message);
             }
 
@@ -1614,38 +1609,38 @@
                 if (tokenName == null) {
                     // No more tokens.
                     break;
-                } else if (tokenName.equalsIgnoreCase("name")) {
+                } else if ("name".equalsIgnoreCase(tokenName)) {
                     names = SchemaUtils.readNameDescriptors(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("desc")) {
+                } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the attribute type. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
                     description = SchemaUtils.readQuotedString(reader);
-                } else if (tokenName.equalsIgnoreCase("obsolete")) {
+                } else if ("obsolete".equalsIgnoreCase(tokenName)) {
                     // This indicates whether the attribute type should be
                     // considered obsolete. We do not need to do any more
                     // parsing for this token.
                     isObsolete = true;
-                } else if (tokenName.equalsIgnoreCase("sup")) {
+                } else if ("sup".equalsIgnoreCase(tokenName)) {
                     superiorClasses = SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("abstract")) {
+                } else if ("abstract".equalsIgnoreCase(tokenName)) {
                     // This indicates that entries must not include this
                     // objectclass unless they also include a non-abstract
                     // objectclass that inherits from this class. We do not need
                     // any more parsing for this token.
                     objectClassType = ObjectClassType.ABSTRACT;
-                } else if (tokenName.equalsIgnoreCase("structural")) {
+                } else if ("structural".equalsIgnoreCase(tokenName)) {
                     // This indicates that this is a structural objectclass. We
                     // do not need any more parsing for this token.
                     objectClassType = ObjectClassType.STRUCTURAL;
-                } else if (tokenName.equalsIgnoreCase("auxiliary")) {
+                } else if ("auxiliary".equalsIgnoreCase(tokenName)) {
                     // This indicates that this is an auxiliary objectclass. We
                     // do not need any more parsing for this token.
                     objectClassType = ObjectClassType.AUXILIARY;
-                } else if (tokenName.equalsIgnoreCase("must")) {
+                } else if ("must".equalsIgnoreCase(tokenName)) {
                     requiredAttributes =
                             SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
-                } else if (tokenName.equalsIgnoreCase("may")) {
+                } else if ("may".equalsIgnoreCase(tokenName)) {
                     optionalAttributes =
                             SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
                 } else if (tokenName.matches("^X-[A-Za-z_-]+$")) {
@@ -1665,7 +1660,7 @@
                 }
             }
 
-            if (oid.equals(EXTENSIBLE_OBJECT_OBJECTCLASS_OID)) {
+            if (EXTENSIBLE_OBJECT_OBJECTCLASS_OID.equals(oid)) {
                 addObjectClass(new ObjectClass(description, extraProperties), overwrite);
             } else {
                 if (objectClassType == ObjectClassType.STRUCTURAL && superiorClasses.isEmpty()) {
@@ -1726,7 +1721,7 @@
             final boolean overwrite) {
         lazyInitBuilder();
 
-        if (oid.equals(EXTENSIBLE_OBJECT_OBJECTCLASS_OID)) {
+        if (EXTENSIBLE_OBJECT_OBJECTCLASS_OID.equals(oid)) {
             addObjectClass(new ObjectClass(description,
                     unmodifiableCopyOfExtraProperties(extraProperties)), overwrite);
         } else {
@@ -1958,8 +1953,6 @@
      *            read.
      * @param name
      *            The distinguished name of the subschema sub-entry.
-     * @param handler
-     *            A result handler which can be used to asynchronously process
      *            the operation result when it is received, may be {@code null}.
      * @param overwrite
      *            {@code true} if existing schema elements with the same
@@ -1974,26 +1967,18 @@
      *             If the {@code connection} or {@code name} was {@code null}.
      */
     public FutureResult<SchemaBuilder> addSchemaAsync(final Connection connection, final DN name,
-            final ResultHandler<? super SchemaBuilder> handler, final boolean overwrite) {
+        final boolean overwrite) {
         // The call to addSchema will perform copyOnWrite.
         final SearchRequest request = getReadSchemaSearchRequest(name);
 
-        final FutureResultTransformer<SearchResultEntry, SchemaBuilder> future =
-                new FutureResultTransformer<SearchResultEntry, SchemaBuilder>(handler) {
-
+        return asFutureResult(connection.searchSingleEntryAsync(request).then(
+                new Function<SearchResultEntry, SchemaBuilder, ErrorResultException>() {
                     @Override
-                    protected SchemaBuilder transformResult(final SearchResultEntry result)
-                            throws ErrorResultException {
+                    public SchemaBuilder apply(SearchResultEntry result) throws ErrorResultException {
                         addSchema(result, overwrite);
                         return SchemaBuilder.this;
                     }
-
-                };
-
-        final FutureResult<SearchResultEntry> innerFuture =
-                connection.searchSingleEntryAsync(request, future);
-        future.setFutureResult(innerFuture);
-        return future;
+                }));
     }
 
     /**
@@ -2058,9 +2043,6 @@
      * @param name
      *            The distinguished name of the entry whose schema is to be
      *            located.
-     * @param handler
-     *            A result handler which can be used to asynchronously process
-     *            the operation result when it is received, may be {@code null}.
      * @param overwrite
      *            {@code true} if existing schema elements with the same
      *            conflicting OIDs should be overwritten.
@@ -2073,29 +2055,19 @@
      * @throws NullPointerException
      *             If the {@code connection} or {@code name} was {@code null}.
      */
-    public FutureResult<SchemaBuilder> addSchemaForEntryAsync(final Connection connection,
-            final DN name, final ResultHandler<? super SchemaBuilder> handler,
-            final boolean overwrite) {
-        // The call to addSchema will perform copyOnWrite.
-        final RecursiveFutureResult<SearchResultEntry, SchemaBuilder> future =
-                new RecursiveFutureResult<SearchResultEntry, SchemaBuilder>(handler) {
-
-                    @Override
-                    protected FutureResult<SchemaBuilder> chainResult(
-                            final SearchResultEntry innerResult,
-                            final ResultHandler<? super SchemaBuilder> handler)
-                            throws ErrorResultException {
-                        final DN subschemaDN = getSubschemaSubentryDN(name, innerResult);
-                        return addSchemaAsync(connection, subschemaDN, handler, overwrite);
-                    }
-
-                };
-
+    public FutureResult<SchemaBuilder> addSchemaForEntryAsync(final Connection connection, final DN name,
+        final boolean overwrite) {
         final SearchRequest request = getReadSchemaForEntrySearchRequest(name);
-        final FutureResult<SearchResultEntry> innerFuture =
-                connection.searchSingleEntryAsync(request, future);
-        future.setFutureResult(innerFuture);
-        return future;
+
+        return asFutureResult(connection.searchSingleEntryAsync(request).thenAsync(
+                new AsyncFunction<SearchResultEntry, SchemaBuilder, ErrorResultException>() {
+                    @Override
+                    public Promise<SchemaBuilder, ErrorResultException> apply(SearchResultEntry result)
+                            throws ErrorResultException {
+                        final DN subschemaDN = getSubschemaSubentryDN(name, result);
+                        return addSchemaAsync(connection, subschemaDN, overwrite);
+                    }
+                }));
     }
 
     /**
@@ -2171,9 +2143,8 @@
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_ATTRSYNTAX_EXPECTED_OPEN_PARENTHESIS.get(definition,
-                                (reader.pos() - 1), String.valueOf(c));
+                final LocalizableMessage message = ERR_ATTR_SYNTAX_ATTRSYNTAX_EXPECTED_OPEN_PARENTHESIS.get(
+                    definition, reader.pos() - 1, String.valueOf(c));
                 throw new LocalizedIllegalArgumentException(message);
             }
 
@@ -2199,7 +2170,7 @@
                 if (tokenName == null) {
                     // No more tokens.
                     break;
-                } else if (tokenName.equalsIgnoreCase("desc")) {
+                } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the syntax. It is an
                     // arbitrary string of characters enclosed in single quotes.
                     syntaxBuilder.description(SchemaUtils.readQuotedString(reader));
@@ -2220,7 +2191,7 @@
 
             // See if it is a enum syntax
             for (final Map.Entry<String, List<String>> property : syntaxBuilder.getExtraProperties().entrySet()) {
-                if (property.getKey().equalsIgnoreCase("x-enum")) {
+                if ("x-enum".equalsIgnoreCase(property.getKey())) {
                     final EnumSyntaxImpl enumImpl = new EnumSyntaxImpl(oid, property.getValue());
                     syntaxBuilder.implementation(enumImpl);
 
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPFutureResultImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPFutureResultImpl.java
index 0e7c691..514761e 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPFutureResultImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPFutureResultImpl.java
@@ -22,21 +22,20 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap.spi;
 
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.ErrorResultException;
+import org.forgerock.opendj.ldap.FutureResultImpl;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.responses.IntermediateResponse;
 import org.forgerock.opendj.ldap.responses.Result;
 
-import com.forgerock.opendj.util.AsynchronousFutureResult;
 
 /**
  * Abstract future result implementation.
@@ -44,9 +43,8 @@
  * @param <S>
  *            The type of result returned by this future.
  */
-public abstract class AbstractLDAPFutureResultImpl<S extends Result> extends
-        AsynchronousFutureResult<S, ResultHandler<? super S>> implements
-        IntermediateResponseHandler {
+public abstract class AbstractLDAPFutureResultImpl<S extends Result> extends FutureResultImpl<S> implements
+    IntermediateResponseHandler {
     private final Connection connection;
     private IntermediateResponseHandler intermediateResponseHandler;
     private volatile long timestamp;
@@ -56,8 +54,6 @@
      *
      * @param requestID
      *            identifier of the request
-     * @param resultHandler
-     *            handler that consumes the result
      * @param intermediateResponseHandler
      *            handler that consumes intermediate responses from extended
      *            operations
@@ -65,10 +61,8 @@
      *            the connection to directory server
      */
     protected AbstractLDAPFutureResultImpl(final int requestID,
-            final ResultHandler<? super S> resultHandler,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final Connection connection) {
-        super(resultHandler, requestID);
+        final IntermediateResponseHandler intermediateResponseHandler, final Connection connection) {
+        super(requestID);
         this.connection = connection;
         this.intermediateResponseHandler = intermediateResponseHandler;
         this.timestamp = System.currentTimeMillis();
@@ -83,40 +77,37 @@
         // the synchronizer.
         if (!isDone()) {
             updateTimestamp();
-            if (intermediateResponseHandler != null) {
-                if (!intermediateResponseHandler.handleIntermediateResponse(response)) {
-                    intermediateResponseHandler = null;
-                }
+            if (intermediateResponseHandler != null
+                && !intermediateResponseHandler.handleIntermediateResponse(response)) {
+                intermediateResponseHandler = null;
             }
         }
         return true;
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
-    protected final ErrorResultException handleCancelRequest(final boolean mayInterruptIfRunning) {
-        /*
-         * This will abandon the request, but will also recursively cancel this
-         * future. There is no risk of an infinite loop because the state of
-         * this future has already been changed.
-         */
-        connection.abandonAsync(Requests.newAbandonRequest(getRequestID()));
-        return null;
-    }
-
-    @Override
-    protected final boolean isCancelable() {
+    protected final ErrorResultException tryCancel(final boolean mayInterruptIfRunning) {
         /*
          * No other operations can be performed while a bind or startTLS
          * operations is active. Therefore it is not possible to cancel bind or
          * startTLS requests, since doing so will leave the connection in a
          * state which prevents other operations from being performed.
          */
-        return !isBindOrStartTLS();
+        if (isBindOrStartTLS()) {
+            return null;
+        }
+
+        /*
+         * This will abandon the request, but will also recursively cancel this
+         * future. There is no risk of an infinite loop because the state of
+         * this future has already been changed.
+         */
+        connection.abandonAsync(Requests.newAbandonRequest(getRequestID()));
+        return ErrorResultException.newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED);
     }
 
+
     /**
      * Returns {@code true} if this future represents the result of a bind or
      * StartTLS request. The default implementation is to return {@code false}.
@@ -128,13 +119,18 @@
         return false;
     }
 
-    @Override
+    /**
+     * Appends a string representation of this future's state to the provided
+     * builder.
+     *
+     * @param sb
+     *            The string builder.
+     */
     protected void toString(final StringBuilder sb) {
         sb.append(" requestID = ");
         sb.append(getRequestID());
         sb.append(" timestamp = ");
         sb.append(timestamp);
-        super.toString(sb);
     }
 
     /**
@@ -145,8 +141,7 @@
      */
     public final void adaptErrorResult(final Result result) {
         final S errorResult =
-                newErrorResult(result.getResultCode(), result.getDiagnosticMessage(), result
-                        .getCause());
+            newErrorResult(result.getResultCode(), result.getDiagnosticMessage(), result.getCause());
         setResultOrError(errorResult);
     }
 
@@ -170,8 +165,7 @@
      *            cause of the error
      * @return the error result
      */
-    protected abstract S newErrorResult(ResultCode resultCode, String diagnosticMessage,
-            Throwable cause);
+    protected abstract S newErrorResult(ResultCode resultCode, String diagnosticMessage, Throwable cause);
 
     /**
      * Sets the result associated to this future.
@@ -181,7 +175,7 @@
      */
     public final void setResultOrError(final S result) {
         if (result.getResultCode().isExceptional()) {
-            handleErrorResult(ErrorResultException.newErrorResult(result));
+            handleError(ErrorResultException.newErrorResult(result));
         } else {
             handleResult(result);
         }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPBindFutureResultImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPBindFutureResultImpl.java
index d068520..c314ad9 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPBindFutureResultImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPBindFutureResultImpl.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap.spi;
@@ -30,7 +30,6 @@
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.requests.BindClient;
 import org.forgerock.opendj.ldap.responses.BindResult;
 import org.forgerock.opendj.ldap.responses.Responses;
@@ -48,8 +47,6 @@
      *            identifier of the request
      * @param bindClient
      *            client that binds to the server
-     * @param resultHandler
-     *            handler that consumes result of bind
      * @param intermediateResponseHandler
      *            handler that consumes intermediate responses from extended
      *            operations
@@ -57,10 +54,9 @@
      *            the connection to directory server
      */
     public LDAPBindFutureResultImpl(final int requestID, final BindClient bindClient,
-            final ResultHandler<? super BindResult> resultHandler,
             final IntermediateResponseHandler intermediateResponseHandler,
             final Connection connection) {
-        super(requestID, resultHandler, intermediateResponseHandler, connection);
+        super(requestID, intermediateResponseHandler, connection);
         this.bindClient = bindClient;
     }
 
@@ -89,9 +85,6 @@
         return bindClient;
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     protected BindResult newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
             final Throwable cause) {
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPCompareFutureResultImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPCompareFutureResultImpl.java
index 209215b..64c8617 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPCompareFutureResultImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPCompareFutureResultImpl.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap.spi;
@@ -30,7 +30,6 @@
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.requests.CompareRequest;
 import org.forgerock.opendj.ldap.responses.CompareResult;
 import org.forgerock.opendj.ldap.responses.Responses;
@@ -48,8 +47,6 @@
      *            identifier of the request
      * @param request
      *            compare request
-     * @param resultHandler
-     *            handler that consumes compare result
      * @param intermediateResponseHandler
      *            handler that consumes intermediate responses from extended
      *            operations
@@ -57,10 +54,9 @@
      *            the connection to directory server
      */
     public LDAPCompareFutureResultImpl(final int requestID, final CompareRequest request,
-            final ResultHandler<? super CompareResult> resultHandler,
             final IntermediateResponseHandler intermediateResponseHandler,
             final Connection connection) {
-        super(requestID, resultHandler, intermediateResponseHandler, connection);
+        super(requestID, intermediateResponseHandler, connection);
         this.request = request;
     }
 
@@ -79,9 +75,7 @@
         return request;
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     protected CompareResult newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
             final Throwable cause) {
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPExtendedFutureResultImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPExtendedFutureResultImpl.java
index 88b81dd..1429491 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPExtendedFutureResultImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPExtendedFutureResultImpl.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap.spi;
@@ -32,7 +32,6 @@
 import org.forgerock.opendj.ldap.DecodeOptions;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.requests.ExtendedRequest;
 import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
 import org.forgerock.opendj.ldap.responses.ExtendedResult;
@@ -54,8 +53,6 @@
      *            identifier of the request
      * @param request
      *            extended request
-     * @param resultHandler
-     *            handler that consumes result
      * @param intermediateResponseHandler
      *            handler that consumes intermediate responses from extended
      *            operations
@@ -63,10 +60,9 @@
      *            the connection to directory server
      */
     public LDAPExtendedFutureResultImpl(final int requestID, final ExtendedRequest<R> request,
-            final ResultHandler<? super R> resultHandler,
             final IntermediateResponseHandler intermediateResponseHandler,
             final Connection connection) {
-        super(requestID, resultHandler, intermediateResponseHandler, connection);
+        super(requestID, intermediateResponseHandler, connection);
         this.request = request;
     }
 
@@ -83,7 +79,7 @@
 
     @Override
     public boolean isBindOrStartTLS() {
-        return request.getOID().equals(StartTLSExtendedRequest.OID);
+        return StartTLSExtendedRequest.OID.equals(request.getOID());
     }
 
     /**
@@ -110,9 +106,7 @@
         return request;
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     protected R newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
             final Throwable cause) {
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPFutureResultImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPFutureResultImpl.java
index 75a40de..d871acd 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPFutureResultImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPFutureResultImpl.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap.spi;
@@ -30,7 +30,6 @@
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.requests.Request;
 import org.forgerock.opendj.ldap.responses.Responses;
 import org.forgerock.opendj.ldap.responses.Result;
@@ -48,8 +47,6 @@
      *            identifier of the request
      * @param request
      *            the request sent to server
-     * @param resultHandler
-     *            handler that consumes the result
      * @param intermediateResponseHandler
      *            handler that consumes intermediate responses from extended
      *            operations
@@ -57,10 +54,9 @@
      *            the connection to directory server
      */
     public LDAPFutureResultImpl(final int requestID, final Request request,
-            final ResultHandler<? super Result> resultHandler,
             final IntermediateResponseHandler intermediateResponseHandler,
             final Connection connection) {
-        super(requestID, resultHandler, intermediateResponseHandler, connection);
+        super(requestID, intermediateResponseHandler, connection);
         this.request = request;
     }
 
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPSearchFutureResultImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPSearchFutureResultImpl.java
index d1a700b..c9edee0 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPSearchFutureResultImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPSearchFutureResultImpl.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap.spi;
@@ -64,13 +64,13 @@
      *            the connection to directory server
      */
     public LDAPSearchFutureResultImpl(final int requestID, final SearchRequest request,
-            final SearchResultHandler resultHandler,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final Connection connection) {
-        super(requestID, resultHandler, intermediateResponseHandler, connection);
+        final SearchResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler,
+        final Connection connection) {
+        super(requestID, intermediateResponseHandler, connection);
         this.request = request;
         this.searchResultHandler = resultHandler;
-        this.isPersistentSearch = request.containsControl(PersistentSearchRequestControl.OID)
+        this.isPersistentSearch =
+            request.containsControl(PersistentSearchRequestControl.OID)
                 || request.containsControl(ADNotificationRequestControl.OID);
     }
 
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldif/ConnectionEntryReader.java b/opendj-core/src/main/java/org/forgerock/opendj/ldif/ConnectionEntryReader.java
index 8b5b56c..8d8de0b 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldif/ConnectionEntryReader.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldif/ConnectionEntryReader.java
@@ -22,13 +22,11 @@
  *
  *
  *      Copyright 2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldif;
 
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-
 import java.util.NoSuchElementException;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -39,6 +37,7 @@
 import org.forgerock.opendj.ldap.ErrorResultIOException;
 import org.forgerock.opendj.ldap.FutureResult;
 import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.SearchResultHandler;
 import org.forgerock.opendj.ldap.SearchResultReferenceIOException;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
@@ -47,9 +46,10 @@
 import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
-
 import org.forgerock.util.Reject;
 
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+
 /**
  * A {@code ConnectionEntryReader} is a bridge from {@code Connection}s to
  * {@code EntryReader}s. A connection entry reader allows applications to
@@ -114,7 +114,7 @@
     /**
      * Result handler that places all responses in a queue.
      */
-    private final static class BufferHandler implements SearchResultHandler {
+    private final static class BufferHandler implements SearchResultHandler, ResultHandler<Result> {
         private final BlockingQueue<Response> responses;
         private volatile boolean isInterrupted = false;
 
@@ -137,7 +137,7 @@
         }
 
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
+        public void handleError(final ErrorResultException error) {
             try {
                 responses.put(error.getResult());
             } catch (final InterruptedException e) {
@@ -209,10 +209,11 @@
      *             If {@code connection} was {@code null}.
      */
     public ConnectionEntryReader(final Connection connection, final SearchRequest searchRequest,
-            final BlockingQueue<Response> entries) {
+        final BlockingQueue<Response> entries) {
         Reject.ifNull(connection);
         buffer = new BufferHandler(entries);
-        future = connection.searchAsync(searchRequest, null, buffer);
+        future = (FutureResult<Result>) connection.searchAsync(searchRequest, buffer)
+                .onSuccess(buffer).onFailure(buffer);
     }
 
     /**
diff --git a/opendj-core/src/test/java/com/forgerock/opendj/util/FutureResultTransformerTestCase.java b/opendj-core/src/test/java/com/forgerock/opendj/util/FutureResultTransformerTestCase.java
deleted file mode 100644
index 5372fd7..0000000
--- a/opendj-core/src/test/java/com/forgerock/opendj/util/FutureResultTransformerTestCase.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at 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 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 2013 ForgeRock AS.
- */
-
-package com.forgerock.opendj.util;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.fest.assertions.Fail.fail;
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import java.util.concurrent.TimeUnit;
-
-import org.forgerock.opendj.ldap.ErrorResultException;
-import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
-import org.testng.annotations.Test;
-
-/**
- * Tests {@link FutureResultTransformer}.
- */
-@SuppressWarnings("javadoc")
-public class FutureResultTransformerTestCase extends UtilTestCase {
-    private static final class TestFuture extends FutureResultTransformer<Integer, String> {
-        public TestFuture(final ResultHandler<? super String> handler) {
-            super(handler);
-        }
-
-        @Override
-        protected ErrorResultException transformErrorResult(final ErrorResultException error) {
-            assertThat(error).isSameAs(UNTRANSFORMED_ERROR);
-            return TRANSFORMED_ERROR;
-        }
-
-        @Override
-        protected String transformResult(final Integer result) throws ErrorResultException {
-            assertThat(result).isSameAs(UNTRANSFORMED_RESULT);
-            return TRANSFORMED_RESULT;
-        }
-    }
-
-    private static final ErrorResultException TRANSFORMED_ERROR = newErrorResult(ResultCode.OTHER,
-            "transformed");
-    private static final String TRANSFORMED_RESULT = "transformed";
-    private static final ErrorResultException UNTRANSFORMED_ERROR = newErrorResult(
-            ResultCode.OTHER, "untransformed");
-    private static final Integer UNTRANSFORMED_RESULT = Integer.valueOf(0);
-
-    @Test
-    public void testGetTransformsError() throws Exception {
-        final TestFuture future = new TestFuture(null);
-        future.setFutureResult(new CompletedFutureResult<Integer>(UNTRANSFORMED_ERROR));
-        future.handleErrorResult(UNTRANSFORMED_ERROR);
-        try {
-            future.get();
-            fail();
-        } catch (final ErrorResultException e) {
-            assertThat(e).isSameAs(TRANSFORMED_ERROR);
-        }
-    }
-
-    @Test
-    public void testGetTransformsResult() throws Exception {
-        final TestFuture future = new TestFuture(null);
-        future.setFutureResult(new CompletedFutureResult<Integer>(UNTRANSFORMED_RESULT));
-        future.handleResult(UNTRANSFORMED_RESULT);
-        assertThat(future.get()).isSameAs(TRANSFORMED_RESULT);
-    }
-
-    @Test
-    public void testGetWithTimeoutTransformsError() throws Exception {
-        final TestFuture future = new TestFuture(null);
-        future.setFutureResult(new CompletedFutureResult<Integer>(UNTRANSFORMED_ERROR));
-        future.handleErrorResult(UNTRANSFORMED_ERROR);
-        try {
-            future.get(100, TimeUnit.SECONDS);
-            fail();
-        } catch (final ErrorResultException e) {
-            assertThat(e).isSameAs(TRANSFORMED_ERROR);
-        }
-    }
-
-    @Test
-    public void testGetWithTimeoutTransformsResult() throws Exception {
-        final TestFuture future = new TestFuture(null);
-        future.setFutureResult(new CompletedFutureResult<Integer>(UNTRANSFORMED_RESULT));
-        future.handleResult(UNTRANSFORMED_RESULT);
-        assertThat(future.get(100, TimeUnit.SECONDS)).isSameAs(TRANSFORMED_RESULT);
-    }
-
-    @Test
-    public void testResultHandlerTransformsError() throws Exception {
-        @SuppressWarnings("unchecked")
-        final ResultHandler<String> handler = mock(ResultHandler.class);
-        final TestFuture future = new TestFuture(handler);
-        future.setFutureResult(new CompletedFutureResult<Integer>(UNTRANSFORMED_ERROR));
-        future.handleErrorResult(UNTRANSFORMED_ERROR);
-        verify(handler).handleErrorResult(TRANSFORMED_ERROR);
-    }
-
-    @Test
-    public void testResultHandlerTransformsResult() throws Exception {
-        @SuppressWarnings("unchecked")
-        final ResultHandler<String> handler = mock(ResultHandler.class);
-        final TestFuture future = new TestFuture(handler);
-        future.setFutureResult(new CompletedFutureResult<Integer>(UNTRANSFORMED_RESULT));
-        future.handleResult(UNTRANSFORMED_RESULT);
-        verify(handler).handleResult(TRANSFORMED_RESULT);
-    }
-}
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnectionTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnectionTestCase.java
index 5adf70a..3f75145 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnectionTestCase.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnectionTestCase.java
@@ -22,16 +22,11 @@
  *
  *
  *      Copyright 2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap;
 
-import static org.fest.assertions.Assertions.assertThat;
-import static org.fest.assertions.Fail.fail;
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-import static org.mockito.Mockito.*;
-
 import java.util.LinkedList;
 import java.util.List;
 
@@ -44,18 +39,26 @@
 import org.forgerock.opendj.ldap.requests.GenericExtendedRequest;
 import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
 import org.forgerock.opendj.ldap.requests.ModifyRequest;
-import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
 import org.forgerock.opendj.ldap.requests.UnbindRequest;
 import org.forgerock.opendj.ldap.responses.BindResult;
 import org.forgerock.opendj.ldap.responses.CompareResult;
 import org.forgerock.opendj.ldap.responses.ExtendedResult;
-import org.forgerock.opendj.ldap.responses.Responses;
 import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.SuccessHandler;
 import org.testng.annotations.Test;
 
-import com.forgerock.opendj.util.CompletedFutureResult;
+import static org.fest.assertions.Assertions.*;
+import static org.fest.assertions.Fail.*;
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
+import static org.forgerock.opendj.ldap.TestCaseUtils.*;
+import static org.forgerock.opendj.ldap.requests.Requests.*;
+import static org.forgerock.opendj.ldap.responses.Responses.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
 
 /**
  * Unit test for AbstractAsynchronousConnection. The tests verify that all
@@ -73,169 +76,116 @@
             this.entries = entries;
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
+        @Override
         public FutureResult<Void> abandonAsync(AbandonRequest request) {
             if (!resultCode.isExceptional()) {
-                return new CompletedFutureResult<Void>((Void) null);
+                return newSuccessfulFutureResult((Void) null);
             } else {
-                return new CompletedFutureResult<Void>(newErrorResult(resultCode));
+                return newFailedFutureResult(newErrorResult(resultCode));
             }
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
+        @Override
         public FutureResult<Result> addAsync(AddRequest request,
-                IntermediateResponseHandler intermediateResponseHandler,
-                ResultHandler<? super Result> resultHandler) {
-            if (!resultCode.isExceptional()) {
-                return new CompletedFutureResult<Result>(Responses.newResult(resultCode));
-            } else {
-                return new CompletedFutureResult<Result>(newErrorResult(resultCode));
-            }
+                IntermediateResponseHandler intermediateResponseHandler) {
+            return getFutureFromResultCode(newResult(resultCode));
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
+        @Override
         public void addConnectionEventListener(ConnectionEventListener listener) {
             // Do nothing.
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
+        @Override
         public FutureResult<BindResult> bindAsync(BindRequest request,
-                IntermediateResponseHandler intermediateResponseHandler,
-                ResultHandler<? super BindResult> resultHandler) {
-            if (!resultCode.isExceptional()) {
-                return new CompletedFutureResult<BindResult>(Responses.newBindResult(resultCode));
-            } else {
-                return new CompletedFutureResult<BindResult>(newErrorResult(resultCode));
-            }
+                IntermediateResponseHandler intermediateResponseHandler) {
+            return getFutureFromResultCode(newBindResult(resultCode));
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
+        @Override
         public void close(UnbindRequest request, String reason) {
             // Do nothing.
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
+        @Override
         public FutureResult<CompareResult> compareAsync(CompareRequest request,
-                IntermediateResponseHandler intermediateResponseHandler,
-                ResultHandler<? super CompareResult> resultHandler) {
-            if (!resultCode.isExceptional()) {
-                return new CompletedFutureResult<CompareResult>(Responses
-                        .newCompareResult(resultCode));
-            } else {
-                return new CompletedFutureResult<CompareResult>(newErrorResult(resultCode));
-            }
+                IntermediateResponseHandler intermediateResponseHandler) {
+            return getFutureFromResultCode(newCompareResult(resultCode));
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
+        @Override
         public FutureResult<Result> deleteAsync(DeleteRequest request,
-                IntermediateResponseHandler intermediateResponseHandler,
-                ResultHandler<? super Result> resultHandler) {
-            if (!resultCode.isExceptional()) {
-                return new CompletedFutureResult<Result>(Responses.newResult(resultCode));
-            } else {
-                return new CompletedFutureResult<Result>(newErrorResult(resultCode));
-            }
+                IntermediateResponseHandler intermediateResponseHandler) {
+            return getFutureFromResultCode(newResult(resultCode));
         }
 
-        /**
-         * {@inheritDoc}
-         */
-        public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(
-                ExtendedRequest<R> request,
-                IntermediateResponseHandler intermediateResponseHandler,
-                ResultHandler<? super R> resultHandler) {
-            if (!resultCode.isExceptional()) {
-                return new CompletedFutureResult<R>(request.getResultDecoder()
-                        .newExtendedErrorResult(resultCode, "", ""));
-            } else {
-                return new CompletedFutureResult<R>(newErrorResult(resultCode));
-            }
+        /** {@inheritDoc} */
+        @Override
+        public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request,
+                IntermediateResponseHandler intermediateResponseHandler) {
+            return getFutureFromResultCode(request.getResultDecoder().newExtendedErrorResult(resultCode, "", ""));
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
+        @Override
         public boolean isClosed() {
             return false;
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
+        @Override
         public boolean isValid() {
             return true;
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
+        @Override
         public FutureResult<Result> modifyAsync(ModifyRequest request,
-                IntermediateResponseHandler intermediateResponseHandler,
-                ResultHandler<? super Result> resultHandler) {
-            if (!resultCode.isExceptional()) {
-                return new CompletedFutureResult<Result>(Responses.newResult(resultCode));
-            } else {
-                return new CompletedFutureResult<Result>(newErrorResult(resultCode));
-            }
+                IntermediateResponseHandler intermediateResponseHandler) {
+            return getFutureFromResultCode(newResult(resultCode));
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
+        @Override
         public FutureResult<Result> modifyDNAsync(ModifyDNRequest request,
-                IntermediateResponseHandler intermediateResponseHandler,
-                ResultHandler<? super Result> resultHandler) {
-            if (!resultCode.isExceptional()) {
-                return new CompletedFutureResult<Result>(Responses.newResult(resultCode));
-            } else {
-                return new CompletedFutureResult<Result>(newErrorResult(resultCode));
-            }
+                IntermediateResponseHandler intermediateResponseHandler) {
+            return getFutureFromResultCode(newResult(resultCode));
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
+        @Override
         public void removeConnectionEventListener(ConnectionEventListener listener) {
             // Do nothing.
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
+        @Override
         public FutureResult<Result> searchAsync(SearchRequest request,
-                IntermediateResponseHandler intermediateResponseHandler,
-                SearchResultHandler resultHandler) {
+                IntermediateResponseHandler intermediateResponseHandler, SearchResultHandler entryHandler) {
             for (SearchResultEntry entry : entries) {
-                resultHandler.handleEntry(entry);
+                entryHandler.handleEntry(entry);
             }
+
+            return getFutureFromResultCode(newResult(resultCode));
+        }
+
+        private <T extends Result> FutureResult<T> getFutureFromResultCode(T correctResult) {
             if (resultCode.isExceptional()) {
-                ErrorResultException errorResult = newErrorResult(resultCode);
-                resultHandler.handleErrorResult(errorResult);
-                return new CompletedFutureResult<Result>(errorResult);
+                return newFailedFutureResult(newErrorResult(resultCode));
             } else {
-                Result result = Responses.newResult(resultCode);
-                resultHandler.handleResult(result);
-                return new CompletedFutureResult<Result>(result);
+                return newSuccessfulFutureResult(correctResult);
             }
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
+        @Override
         public String toString() {
             return "MockConnection";
         }
@@ -245,14 +195,14 @@
     @Test()
     public void testAddRequestSuccess() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.SUCCESS);
-        final AddRequest addRequest = Requests.newAddRequest("cn=test");
+        final AddRequest addRequest = newAddRequest("cn=test");
         assertThat(mockConnection.add(addRequest).getResultCode()).isEqualTo(ResultCode.SUCCESS);
     }
 
     @Test()
     public void testAddRequestFail() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.UNWILLING_TO_PERFORM);
-        final AddRequest addRequest = Requests.newAddRequest("cn=test");
+        final AddRequest addRequest = newAddRequest("cn=test");
         try {
             mockConnection.add(addRequest);
             fail();
@@ -264,14 +214,14 @@
     @Test()
     public void testBindRequestSuccess() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.SUCCESS);
-        final BindRequest bindRequest = Requests.newSimpleBindRequest();
+        final BindRequest bindRequest = newSimpleBindRequest();
         assertThat(mockConnection.bind(bindRequest).getResultCode()).isEqualTo(ResultCode.SUCCESS);
     }
 
     @Test()
     public void testBindRequestFail() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.UNWILLING_TO_PERFORM);
-        final BindRequest bindRequest = Requests.newSimpleBindRequest();
+        final BindRequest bindRequest = newSimpleBindRequest();
         try {
             mockConnection.bind(bindRequest);
             fail();
@@ -283,7 +233,7 @@
     @Test()
     public void testCompareRequestSuccess() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.SUCCESS);
-        final CompareRequest compareRequest = Requests.newCompareRequest("cn=test", "cn", "test");
+        final CompareRequest compareRequest = newCompareRequest("cn=test", "cn", "test");
         assertThat(mockConnection.compare(compareRequest).getResultCode()).isEqualTo(
                 ResultCode.SUCCESS);
     }
@@ -291,7 +241,7 @@
     @Test()
     public void testCompareRequestFail() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.UNWILLING_TO_PERFORM);
-        final CompareRequest compareRequest = Requests.newCompareRequest("cn=test", "cn", "test");
+        final CompareRequest compareRequest = newCompareRequest("cn=test", "cn", "test");
         try {
             mockConnection.compare(compareRequest);
             fail();
@@ -303,7 +253,7 @@
     @Test()
     public void testDeleteRequestSuccess() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.SUCCESS);
-        final DeleteRequest deleteRequest = Requests.newDeleteRequest("cn=test");
+        final DeleteRequest deleteRequest = newDeleteRequest("cn=test");
         assertThat(mockConnection.delete(deleteRequest).getResultCode()).isEqualTo(
                 ResultCode.SUCCESS);
     }
@@ -311,7 +261,7 @@
     @Test()
     public void testDeleteRequestFail() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.UNWILLING_TO_PERFORM);
-        final DeleteRequest deleteRequest = Requests.newDeleteRequest("cn=test");
+        final DeleteRequest deleteRequest = newDeleteRequest("cn=test");
         try {
             mockConnection.delete(deleteRequest);
             fail();
@@ -323,7 +273,7 @@
     @Test()
     public void testExtendedRequestSuccess() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.SUCCESS);
-        final GenericExtendedRequest extendedRequest = Requests.newGenericExtendedRequest("test");
+        final GenericExtendedRequest extendedRequest = newGenericExtendedRequest("test");
         assertThat(mockConnection.extendedRequest(extendedRequest).getResultCode()).isEqualTo(
                 ResultCode.SUCCESS);
     }
@@ -331,7 +281,7 @@
     @Test()
     public void testExtendedRequestFail() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.UNWILLING_TO_PERFORM);
-        final GenericExtendedRequest extendedRequest = Requests.newGenericExtendedRequest("test");
+        final GenericExtendedRequest extendedRequest = newGenericExtendedRequest("test");
         try {
             mockConnection.extendedRequest(extendedRequest);
             fail();
@@ -343,7 +293,7 @@
     @Test()
     public void testModifyRequestSuccess() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.SUCCESS);
-        final ModifyRequest modifyRequest = Requests.newModifyRequest("cn=test");
+        final ModifyRequest modifyRequest = newModifyRequest("cn=test");
         assertThat(mockConnection.modify(modifyRequest).getResultCode()).isEqualTo(
                 ResultCode.SUCCESS);
     }
@@ -351,7 +301,7 @@
     @Test()
     public void testModifyRequestFail() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.UNWILLING_TO_PERFORM);
-        final ModifyRequest modifyRequest = Requests.newModifyRequest("cn=test");
+        final ModifyRequest modifyRequest = newModifyRequest("cn=test");
         try {
             mockConnection.modify(modifyRequest);
             fail();
@@ -363,7 +313,7 @@
     @Test()
     public void testModifyDNRequestSuccess() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.SUCCESS);
-        final ModifyDNRequest modifyDNRequest = Requests.newModifyDNRequest("cn=test", "cn=newrdn");
+        final ModifyDNRequest modifyDNRequest = newModifyDNRequest("cn=test", "cn=newrdn");
         assertThat(mockConnection.modifyDN(modifyDNRequest).getResultCode()).isEqualTo(
                 ResultCode.SUCCESS);
     }
@@ -371,7 +321,7 @@
     @Test()
     public void testModifyDNRequestFail() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.UNWILLING_TO_PERFORM);
-        final ModifyDNRequest modifyDNRequest = Requests.newModifyDNRequest("cn=test", "cn=newrdn");
+        final ModifyDNRequest modifyDNRequest = newModifyDNRequest("cn=test", "cn=newrdn");
         try {
             mockConnection.modifyDN(modifyDNRequest);
             fail();
@@ -382,10 +332,10 @@
 
     @Test()
     public void testSearchRequestSuccess() throws Exception {
-        final SearchResultEntry entry = Responses.newSearchResultEntry("cn=test");
+        final SearchResultEntry entry = newSearchResultEntry("cn=test");
         final Connection mockConnection = new MockConnection(ResultCode.SUCCESS, entry);
         final SearchRequest searchRequest =
-                Requests.newSearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
+                newSearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
         List<SearchResultEntry> entries = new LinkedList<SearchResultEntry>();
         assertThat(mockConnection.search(searchRequest, entries).getResultCode()).isEqualTo(
                 ResultCode.SUCCESS);
@@ -397,11 +347,11 @@
     public void testSearchRequestFail() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.UNWILLING_TO_PERFORM);
         final SearchRequest searchRequest =
-                Requests.newSearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
+                newSearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
         List<SearchResultEntry> entries = new LinkedList<SearchResultEntry>();
         try {
             mockConnection.search(searchRequest, entries);
-            TestCaseUtils.failWasExpected(ErrorResultException.class);
+            failWasExpected(ErrorResultException.class);
         } catch (ErrorResultException e) {
             assertThat(e.getResult().getResultCode()).isEqualTo(ResultCode.UNWILLING_TO_PERFORM);
             assertThat(entries.isEmpty());
@@ -410,36 +360,37 @@
 
     @Test()
     public void testSingleEntrySearchRequestSuccess() throws Exception {
-        final SearchResultEntry entry = Responses.newSearchResultEntry("cn=test");
+        final SearchResultEntry entry = newSearchResultEntry("cn=test");
         final Connection mockConnection = new MockConnection(ResultCode.SUCCESS, entry);
         final SearchRequest request =
-                Requests.newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
+                newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
         assertThat(mockConnection.searchSingleEntry(request)).isEqualTo(entry);
     }
 
     @SuppressWarnings("unchecked")
     @Test()
     public void testSingleEntrySearchAsyncRequestSuccess() throws Exception {
-        final SearchResultEntry entry = Responses.newSearchResultEntry("cn=test");
+        final SearchResultEntry entry = newSearchResultEntry("cn=test");
         final Connection mockConnection = new MockConnection(ResultCode.SUCCESS, entry);
         final SearchRequest request =
-                Requests.newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
-        ResultHandler<SearchResultEntry> handler = mock(ResultHandler.class);
+                newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
+        SuccessHandler<SearchResultEntry> successHandler = mock(SuccessHandler.class);
 
-        FutureResult<SearchResultEntry> futureResult = mockConnection.searchSingleEntryAsync(request, handler);
+        FutureResult<SearchResultEntry> futureResult = (FutureResult<SearchResultEntry>) mockConnection
+                .searchSingleEntryAsync(request).onSuccess(successHandler);
 
         assertThat(futureResult.get()).isEqualTo(entry);
-        verify(handler).handleResult(any(SearchResultEntry.class));
+        verify(successHandler).handleResult(any(SearchResultEntry.class));
     }
 
     @Test()
     public void testSingleEntrySearchRequestNoEntryReturned() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.SUCCESS);
         final SearchRequest request =
-                Requests.newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
+                newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
         try {
             mockConnection.searchSingleEntry(request);
-            TestCaseUtils.failWasExpected(EntryNotFoundException.class);
+            failWasExpected(EntryNotFoundException.class);
         } catch (EntryNotFoundException e) {
             assertThat(e.getResult().getResultCode()).isEqualTo(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED);
         }
@@ -448,12 +399,12 @@
     @Test()
     public void testSingleEntrySearchRequestMultipleEntriesToReturn() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.SIZE_LIMIT_EXCEEDED,
-                Responses.newSearchResultEntry("cn=test"));
+                newSearchResultEntry("cn=test"));
         final SearchRequest request =
-                Requests.newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
+                newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
         try {
             mockConnection.searchSingleEntry(request);
-            TestCaseUtils.failWasExpected(MultipleEntriesFoundException.class);
+            failWasExpected(MultipleEntriesFoundException.class);
         } catch (MultipleEntriesFoundException e) {
             assertThat(e.getResult().getResultCode()).isEqualTo(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED);
         }
@@ -462,14 +413,13 @@
     @Test()
     public void testSingleEntrySearchRequestMultipleEntriesReturnedByServer() throws Exception {
         // could happen if server does not enforce size limit
-        final Connection mockConnection = new MockConnection(ResultCode.SUCCESS,
-                Responses.newSearchResultEntry("cn=test"),
-                Responses.newSearchResultEntry("cn=test,ou=org"));
-        final SearchRequest request =
-                Requests.newSingleEntrySearchRequest("cn=test", SearchScope.WHOLE_SUBTREE, "(objectClass=*)");
+        final Connection mockConnection = new MockConnection(ResultCode.SUCCESS, newSearchResultEntry("cn=test"),
+                newSearchResultEntry("cn=test,ou=org"));
+        final SearchRequest request = newSingleEntrySearchRequest("cn=test", SearchScope.WHOLE_SUBTREE,
+                "(objectClass=*)");
         try {
             mockConnection.searchSingleEntry(request);
-            TestCaseUtils.failWasExpected(MultipleEntriesFoundException.class);
+            failWasExpected(MultipleEntriesFoundException.class);
         } catch (MultipleEntriesFoundException e) {
             assertThat(e.getResult().getResultCode()).isEqualTo(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED);
         }
@@ -479,36 +429,35 @@
     @Test()
     public void testSingleEntrySearchAsyncRequestMultipleEntriesToReturn() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.SIZE_LIMIT_EXCEEDED,
-                Responses.newSearchResultEntry("cn=test"));
+                newSearchResultEntry("cn=test"));
         final SearchRequest request =
-                Requests.newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
-        ResultHandler<SearchResultEntry> handler = mock(ResultHandler.class);
+                newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
+        FailureHandler<ErrorResultException> failureHandler = mock(FailureHandler.class);
 
         try {
-            mockConnection.searchSingleEntryAsync(request, handler).get();
-            TestCaseUtils.failWasExpected(MultipleEntriesFoundException.class);
+            mockConnection.searchSingleEntryAsync(request).onFailure(failureHandler).getOrThrow();
+            failWasExpected(MultipleEntriesFoundException.class);
         } catch (MultipleEntriesFoundException e) {
             assertThat(e.getResult().getResultCode()).isEqualTo(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED);
-            verify(handler).handleErrorResult(any(ErrorResultException.class));
+            verify(failureHandler).handleError(any(ErrorResultException.class));
         }
     }
 
     @Test()
     public void testSingleEntrySearchAsyncRequestMultipleEntriesReturnedByServer() throws Exception {
         // could happen if server does not enfore size limit
-        final Connection mockConnection = new MockConnection(ResultCode.SUCCESS,
-                Responses.newSearchResultEntry("cn=test"),
-                Responses.newSearchResultEntry("cn=test,ou=org"));
-        final SearchRequest request = Requests.newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT,
+        final Connection mockConnection = new MockConnection(ResultCode.SUCCESS, newSearchResultEntry("cn=test"),
+                newSearchResultEntry("cn=test,ou=org"));
+        final SearchRequest request = newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT,
                 "(objectClass=*)");
         @SuppressWarnings("unchecked")
-        ResultHandler<SearchResultEntry> handler = mock(ResultHandler.class);
+        FailureHandler<ErrorResultException> failureHandler = mock(FailureHandler.class);
         try {
-            mockConnection.searchSingleEntryAsync(request, handler).get();
-            TestCaseUtils.failWasExpected(MultipleEntriesFoundException.class);
+            mockConnection.searchSingleEntryAsync(request).onFailure(failureHandler).getOrThrow();
+            failWasExpected(MultipleEntriesFoundException.class);
         } catch (MultipleEntriesFoundException e) {
             assertThat(e.getResult().getResultCode()).isEqualTo(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED);
-            verify(handler).handleErrorResult(any(ErrorResultException.class));
+            verify(failureHandler).handleError(any(ErrorResultException.class));
         }
     }
 
@@ -516,10 +465,10 @@
     public void testSingleEntrySearchRequestFail() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.UNWILLING_TO_PERFORM);
         final SearchRequest request =
-                Requests.newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
+                newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
         try {
             mockConnection.searchSingleEntry(request);
-            TestCaseUtils.failWasExpected(ErrorResultException.class);
+            failWasExpected(ErrorResultException.class);
         } catch (ErrorResultException e) {
             assertThat(e.getResult().getResultCode()).isEqualTo(ResultCode.UNWILLING_TO_PERFORM);
         }
@@ -529,15 +478,15 @@
     public void testSingleEntrySearchAsyncRequestFail() throws Exception {
         final Connection mockConnection = new MockConnection(ResultCode.UNWILLING_TO_PERFORM);
         final SearchRequest request =
-                Requests.newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
+                newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
         @SuppressWarnings("unchecked")
-        ResultHandler<SearchResultEntry> handler = mock(ResultHandler.class);
+        FailureHandler<ErrorResultException> failureHandler = mock(FailureHandler.class);
         try {
-            mockConnection.searchSingleEntryAsync(request, handler).get();
-            TestCaseUtils.failWasExpected(ErrorResultException.class);
+            mockConnection.searchSingleEntryAsync(request).onFailure(failureHandler).getOrThrow();
+            failWasExpected(ErrorResultException.class);
         } catch (ErrorResultException e) {
             assertThat(e.getResult().getResultCode()).isEqualTo(ResultCode.UNWILLING_TO_PERFORM);
-            verify(handler).handleErrorResult(any(ErrorResultException.class));
+            verify(failureHandler).handleError(any(ErrorResultException.class));
         }
     }
 
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithmTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithmTestCase.java
index f0982e2..391ae70 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithmTestCase.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithmTestCase.java
@@ -21,28 +21,26 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2013 ForgeRock AS
+ *      Copyright 2014 ForgeRock AS
  */
 package org.forgerock.opendj.ldap;
 
-import static java.util.Arrays.asList;
-import static org.fest.assertions.Assertions.assertThat;
-import static org.fest.assertions.Fail.fail;
-import static org.forgerock.opendj.ldap.Connections.newLoadBalancer;
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
 
+import org.forgerock.util.promise.Promise;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-import com.forgerock.opendj.util.CompletedFutureResult;
+import static java.util.Arrays.*;
+
+import static org.fest.assertions.Assertions.*;
+import static org.fest.assertions.Fail.*;
+import static org.forgerock.opendj.ldap.Connections.*;
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+import static org.forgerock.util.promise.Promises.*;
+import static org.mockito.Mockito.*;
 
 @SuppressWarnings("javadoc")
 public class AbstractLoadBalancingAlgorithmTestCase extends SdkTestCase {
@@ -59,19 +57,11 @@
             }
 
             @Override
-            public FutureResult<Connection> getConnectionAsync(
-                    final ResultHandler<? super Connection> handler) {
+            public Promise<Connection, ErrorResultException> getConnectionAsync() {
                 try {
-                    final Connection connection = mock.getConnection();
-                    if (handler != null) {
-                        handler.handleResult(connection);
-                    }
-                    return new CompletedFutureResult<Connection>(connection);
+                    return newSuccessfulPromise(mock.getConnection());
                 } catch (final ErrorResultException e) {
-                    if (handler != null) {
-                        handler.handleErrorResult(e);
-                    }
-                    return new CompletedFutureResult<Connection>(e);
+                    return newFailedPromise(e);
                 }
             }
 
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/ConnectionPoolTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/ConnectionPoolTestCase.java
index 6efb8ab..9b302e7 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/ConnectionPoolTestCase.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/ConnectionPoolTestCase.java
@@ -27,18 +27,6 @@
 
 package org.forgerock.opendj.ldap;
 
-import static org.fest.assertions.Assertions.assertThat;
-import static org.forgerock.opendj.ldap.Connections.newFixedConnectionPool;
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-import static org.forgerock.opendj.ldap.TestCaseUtils.mockConnection;
-import static org.forgerock.opendj.ldap.TestCaseUtils.mockConnectionFactory;
-import static org.forgerock.opendj.ldap.TestCaseUtils.mockTimeSource;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isA;
-import static org.mockito.Mockito.*;
-
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
@@ -48,9 +36,20 @@
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.responses.ExtendedResult;
 import org.forgerock.opendj.ldap.responses.Responses;
-import org.mockito.ArgumentCaptor;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.PromiseImpl;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import static org.fest.assertions.Assertions.*;
+import static org.forgerock.opendj.ldap.Connections.*;
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+import static org.forgerock.opendj.ldap.TestCaseUtils.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
 /**
  * Tests the connection pool implementation..
  */
@@ -248,7 +247,7 @@
          * is a connection available immediately then the future will be
          * completed immediately).
          */
-        final FutureResult<Connection> future = pool.getConnectionAsync(null);
+        final Promise<? extends Connection, ErrorResultException> future = pool.getConnectionAsync();
         assertThat(future.isDone()).isFalse();
 
         // Release a connection and verify that it is immediately redeemed by
@@ -521,24 +520,31 @@
         final ConnectionFactory factory = mock(ConnectionFactory.class);
         final int poolSize = 2;
         final ConnectionPool pool = Connections.newFixedConnectionPool(factory, poolSize);
+        doAnswer(new Answer<Promise<Connection, ErrorResultException>>() {
+            @Override
+            public Promise<Connection, ErrorResultException> answer(final InvocationOnMock invocation)
+                    throws Throwable {
+                return PromiseImpl.create();
+            }
+        }).when(factory).getConnectionAsync();
 
-        List<FutureResult<Connection>> futures = new ArrayList<FutureResult<Connection>>();
+        List<Promise<? extends Connection, ErrorResultException>> futures =
+                new ArrayList<Promise<? extends Connection, ErrorResultException>>();
         for (int i = 0; i < poolSize + 1; i++) {
-            futures.add(pool.getConnectionAsync(null));
+            futures.add(pool.getConnectionAsync());
         }
+        // factory.getConnectionAsync() has been called by the pool poolSize times
+        verify(factory, times(poolSize)).getConnectionAsync();
+        final ErrorResultException connectError = ErrorResultException
+                .newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR);
+        for (Promise<? extends Connection, ErrorResultException> future : futures) {
+            // Simulate that an error happened with the created connections
+            ((FutureResultImpl) future).handleError(connectError);
 
-        final ArgumentCaptor<ResultHandler> arg = ArgumentCaptor.forClass(ResultHandler.class);
-        verify(factory, times(poolSize)).getConnectionAsync(arg.capture());
-        final ErrorResultException connectError =
-                ErrorResultException.newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR);
-        for (ResultHandler<Connection> handler : arg.getAllValues()) {
-            handler.handleErrorResult(connectError);
-        }
-
-        for (FutureResult<Connection> future : futures) {
             try {
                 // Before the fix for OPENDJ-1348 the third future.get() would hang.
-                future.get();
+                future.getOrThrow();
+                Assert.fail("ErrorResultException should have been called");
             } catch (ErrorResultException e) {
                 assertThat(e).isSameAs(connectError);
             }
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactoryTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactoryTestCase.java
index 1f64f20..071ccf4 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactoryTestCase.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactoryTestCase.java
@@ -21,7 +21,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2013 ForgeRock AS.
+ *      Copyright 2013-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap;
@@ -33,7 +33,14 @@
 
 import org.forgerock.opendj.ldap.requests.BindRequest;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
+import org.forgerock.opendj.ldap.responses.BindResult;
+import org.forgerock.opendj.ldap.responses.Responses;
 import org.forgerock.opendj.ldap.responses.Result;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.NeverThrowsException;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.PromiseImpl;
+import org.forgerock.util.promise.SuccessHandler;
 import org.mockito.ArgumentCaptor;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
@@ -42,11 +49,10 @@
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-import com.forgerock.opendj.util.CompletedFutureResult;
-
 import static org.fest.assertions.Assertions.*;
 import static org.fest.assertions.Fail.*;
 import static org.forgerock.opendj.ldap.ErrorResultException.*;
+import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
 import static org.forgerock.opendj.ldap.SearchScope.*;
 import static org.forgerock.opendj.ldap.TestCaseUtils.*;
 import static org.forgerock.opendj.ldap.requests.Requests.*;
@@ -125,43 +131,34 @@
         }
     }
 
-    @SuppressWarnings("unchecked")
     @Test
     public void testBindWhileHeartBeatInProgress() throws Exception {
         mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS);
-
+        mockBindAsyncResponse();
         hbc = hbcf.getConnection();
 
         /*
          * Send a heartbeat, trapping the search call-back so that we can send
          * the response once we have attempted a bind.
          */
-        when(
-                connection.searchAsync(any(SearchRequest.class),
-                        any(IntermediateResponseHandler.class), any(SearchResultHandler.class)))
-                .thenReturn(null);
+        when(connection.searchAsync(any(SearchRequest.class), any(SearchResultHandler.class))).thenReturn(
+            FutureResultWrapper.newSuccessfulFutureResult(Responses.newResult(ResultCode.SUCCESS)));
         when(hbcf.timeSource.currentTimeMillis()).thenReturn(11000L);
         scheduler.runAllTasks(); // Send the heartbeat.
 
         // Capture the heartbeat search result handler.
-        final ArgumentCaptor<SearchResultHandler> arg =
-                ArgumentCaptor.forClass(SearchResultHandler.class);
-        verify(connection, times(2)).searchAsync(same(HEARTBEAT),
-                any(IntermediateResponseHandler.class), arg.capture());
+        verify(connection, times(2)).searchAsync(same(HEARTBEAT), any(SearchResultHandler.class));
         assertThat(hbc.isValid()).isTrue(); // Not checked yet.
 
         /*
          * Now attempt a bind request, which should be held in a queue until the
          * heart beat completes.
          */
-        hbc.bindAsync(newSimpleBindRequest(), null, null);
-        verify(connection, times(0)).bindAsync(any(BindRequest.class),
-                any(IntermediateResponseHandler.class), any(ResultHandler.class));
+        hbc.bindAsync(newSimpleBindRequest());
+        verify(connection, times(0)).bindAsync(any(BindRequest.class));
 
         // Send fake heartbeat response, releasing the bind request.
-        arg.getValue().handleResult(newResult(ResultCode.SUCCESS));
-        verify(connection, times(1)).bindAsync(any(BindRequest.class),
-                any(IntermediateResponseHandler.class), any(ResultHandler.class));
+        verify(connection, times(1)).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class));
     }
 
     @Test
@@ -175,34 +172,42 @@
     @Test
     public void testGetConnectionAsync() throws Exception {
         @SuppressWarnings("unchecked")
-        final ResultHandler<Connection> mockResultHandler = mock(ResultHandler.class);
+        final SuccessHandler<Connection> mockSuccessHandler = mock(SuccessHandler.class);
 
         mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS);
-        hbc = hbcf.getConnectionAsync(mockResultHandler).get();
+        hbc = hbcf.getConnectionAsync().onSuccess(mockSuccessHandler).getOrThrow();
         assertThat(hbc).isNotNull();
         assertThat(hbc.isValid()).isTrue();
 
-        verify(mockResultHandler).handleResult(any(Connection.class));
-        verifyNoMoreInteractions(mockResultHandler);
+        verify(mockSuccessHandler).handleResult(any(Connection.class));
+        verifyNoMoreInteractions(mockSuccessHandler);
     }
 
     @Test
     public void testGetConnectionAsyncWithInitialHeartBeatError() throws Exception {
         @SuppressWarnings("unchecked")
-        final ResultHandler<Connection> mockResultHandler = mock(ResultHandler.class);
-        ErrorResultException expectedException = null;
+        final SuccessHandler<Connection> mockSuccessHandler = mock(SuccessHandler.class);
+        final PromiseImpl<ErrorResultException, NeverThrowsException> promisedError = PromiseImpl.create();
 
         mockConnectionWithInitialHeartbeatResult(ResultCode.BUSY);
+        Promise<? extends Connection, ErrorResultException> promise = hbcf.getConnectionAsync();
+        promise.onSuccess(mockSuccessHandler).onFailure(new FailureHandler<ErrorResultException>() {
+            @Override
+            public void handleError(ErrorResultException error) {
+                promisedError.handleResult(error);
+            }
+        });
+
+        checkInitialHeartBeatFailure(promisedError.getOrThrow());
+
         try {
-            hbcf.getConnectionAsync(mockResultHandler).get();
+            promise.getOrThrow();
             fail("Unexpectedly obtained a connection");
         } catch (final ErrorResultException e) {
             checkInitialHeartBeatFailure(e);
-            expectedException = e;
         }
 
-        verify(mockResultHandler).handleErrorResult(expectedException);
-        verifyNoMoreInteractions(mockResultHandler);
+        verifyNoMoreInteractions(mockSuccessHandler);
     }
 
     @Test
@@ -251,11 +256,11 @@
 
         // Attempt to send a new request: it should fail immediately.
         @SuppressWarnings("unchecked")
-        final ResultHandler<Result> mockHandler = mock(ResultHandler.class);
-        hbc.modifyAsync(newModifyRequest(DN.rootDN()), null, mockHandler);
+        final FailureHandler<ErrorResultException> mockHandler = mock(FailureHandler.class);
+        hbc.modifyAsync(newModifyRequest(DN.rootDN())).onFailure(mockHandler);
         final ArgumentCaptor<ErrorResultException> arg =
                 ArgumentCaptor.forClass(ErrorResultException.class);
-        verify(mockHandler).handleErrorResult(arg.capture());
+        verify(mockHandler).handleError(arg.capture());
         assertThat(arg.getValue().getResult().getResultCode()).isEqualTo(
                 ResultCode.CLIENT_SIDE_SERVER_DOWN);
 
@@ -282,10 +287,11 @@
         assertThat(hbc.isClosed()).isFalse();
     }
 
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({ "rawtypes", "unchecked" })
     @Test(description = "OPENDJ-1348")
     public void testBindPreventsHeartBeatTimeout() throws Exception {
         mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS);
+        mockBindAsyncResponse();
         hbc = hbcf.getConnection();
 
         /*
@@ -293,21 +299,18 @@
          * the response once we have attempted a heartbeat.
          */
         when(hbcf.timeSource.currentTimeMillis()).thenReturn(11000L);
-        hbc.bindAsync(newSimpleBindRequest(), null, null);
-        @SuppressWarnings("rawtypes")
-        final ArgumentCaptor<ResultHandler> arg = ArgumentCaptor.forClass(ResultHandler.class);
-        verify(connection, times(1)).bindAsync(any(BindRequest.class),
-                any(IntermediateResponseHandler.class), arg.capture());
+        FutureResult<BindResult> future = hbc.bindAsync(newSimpleBindRequest());
+
+        verify(connection, times(1)).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class));
 
         // Verify no heartbeat is sent because there is a bind in progress.
         when(hbcf.timeSource.currentTimeMillis()).thenReturn(11001L);
         scheduler.runAllTasks(); // Invokes HBCF.ConnectionImpl.sendHeartBeat()
-        verify(connection, times(1)).searchAsync(same(HEARTBEAT),
-                any(IntermediateResponseHandler.class), any(SearchResultHandler.class));
+        verify(connection, times(1)).searchAsync(same(HEARTBEAT), any(SearchResultHandler.class));
 
         // Send fake bind response, releasing the heartbeat.
         when(hbcf.timeSource.currentTimeMillis()).thenReturn(11099L);
-        arg.getValue().handleResult(newResult(ResultCode.SUCCESS));
+        ((ResultHandler) future).handleResult(newResult(ResultCode.SUCCESS));
 
         // Check that bind response acts as heartbeat.
         assertThat(hbc.isValid()).isTrue();
@@ -316,25 +319,21 @@
         assertThat(hbc.isValid()).isTrue();
     }
 
-    @SuppressWarnings("unchecked")
     @Test(description = "OPENDJ-1348")
     public void testBindTriggersHeartBeatTimeoutWhenTooSlow() throws Exception {
         mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS);
+        mockBindAsyncResponse();
         hbc = hbcf.getConnection();
 
         // Send another bind request which will timeout.
         when(hbcf.timeSource.currentTimeMillis()).thenReturn(20000L);
-        hbc.bindAsync(newSimpleBindRequest(), null, null);
-        @SuppressWarnings("rawtypes")
-        final ArgumentCaptor<ResultHandler> arg = ArgumentCaptor.forClass(ResultHandler.class);
-        verify(connection, times(1)).bindAsync(any(BindRequest.class),
-                any(IntermediateResponseHandler.class), arg.capture());
+        hbc.bindAsync(newSimpleBindRequest());
+        verify(connection, times(1)).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class));
 
         // Verify no heartbeat is sent because there is a bind in progress.
         when(hbcf.timeSource.currentTimeMillis()).thenReturn(20001L);
         scheduler.runAllTasks(); // Invokes HBCF.ConnectionImpl.sendHeartBeat()
-        verify(connection, times(1)).searchAsync(same(HEARTBEAT),
-                any(IntermediateResponseHandler.class), any(SearchResultHandler.class));
+        verify(connection, times(1)).searchAsync(same(HEARTBEAT), any(SearchResultHandler.class));
 
         // Check that lack of bind response acts as heartbeat timeout.
         assertThat(hbc.isValid()).isTrue();
@@ -343,41 +342,39 @@
         assertThat(hbc.isValid()).isFalse();
     }
 
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     @Test
     public void testHeartBeatWhileBindInProgress() throws Exception {
         mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS);
+        mockBindAsyncResponse();
         hbc = hbcf.getConnection();
 
         /*
          * Send a bind request, trapping the bind call-back so that we can send
          * the response once we have attempted a heartbeat.
          */
-        hbc.bindAsync(newSimpleBindRequest(), null, null);
+        FutureResult result = hbc.bindAsync(newSimpleBindRequest());
 
         // Capture the bind result handler.
-        @SuppressWarnings("rawtypes")
-        final ArgumentCaptor<ResultHandler> arg = ArgumentCaptor.forClass(ResultHandler.class);
-        verify(connection, times(1)).bindAsync(any(BindRequest.class),
-                any(IntermediateResponseHandler.class), arg.capture());
+        verify(connection, times(1)).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class));
 
         /*
          * Now attempt the heartbeat which should not happen because there is a
          * bind in progress.
          */
         when(hbcf.timeSource.currentTimeMillis()).thenReturn(11000L);
-        scheduler.runAllTasks(); // Attempt to send the heartbeat.
-        verify(connection, times(1)).searchAsync(same(HEARTBEAT),
-                any(IntermediateResponseHandler.class), any(SearchResultHandler.class));
+        // Attempt to send the heartbeat.
+        scheduler.runAllTasks();
+        verify(connection, times(1)).searchAsync(same(HEARTBEAT), any(SearchResultHandler.class));
 
         // Send fake bind response, releasing the heartbeat.
-        arg.getValue().handleResult(newResult(ResultCode.SUCCESS));
+        ((ResultHandler) result).handleResult(newResult(ResultCode.SUCCESS));
 
         // Attempt to send a heartbeat again.
         when(hbcf.timeSource.currentTimeMillis()).thenReturn(16000L);
-        scheduler.runAllTasks(); // Attempt to send the heartbeat.
-        verify(connection, times(2)).searchAsync(same(HEARTBEAT),
-                any(IntermediateResponseHandler.class), any(SearchResultHandler.class));
+        // Attempt to send the heartbeat.
+        scheduler.runAllTasks();
+        verify(connection, times(2)).searchAsync(same(HEARTBEAT), any(SearchResultHandler.class));
     }
 
     @Test
@@ -417,43 +414,46 @@
         hbcf.timeSource = mockTimeSource(0);
     }
 
-    private Connection mockHeartBeatResponse(final Connection mockConnection,
-            final List<ConnectionEventListener> listeners, final ResultCode resultCode) {
+    private void mockBindAsyncResponse() {
         doAnswer(new Answer<FutureResult<Result>>() {
             @Override
             public FutureResult<Result> answer(final InvocationOnMock invocation) throws Throwable {
+                return new FutureResultImpl<Result>();
+            }
+        }).when(connection).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class));
+    }
+
+    private Connection mockHeartBeatResponse(final Connection mockConnection,
+        final List<ConnectionEventListener> listeners, final ResultCode resultCode) {
+        Answer<FutureResult<Result>> answer = new Answer<FutureResult<Result>>() {
+            @Override
+            public FutureResult<Result> answer(final InvocationOnMock invocation) throws Throwable {
                 if (resultCode == null) {
                     return null;
                 }
 
-                final SearchResultHandler handler =
-                        (SearchResultHandler) invocation.getArguments()[2];
                 if (resultCode.isExceptional()) {
                     final ErrorResultException error = newErrorResult(resultCode);
-                    if (handler != null) {
-                        handler.handleErrorResult(error);
-                    }
                     if (error instanceof ConnectionException) {
                         for (final ConnectionEventListener listener : listeners) {
                             listener.handleConnectionError(false, error);
                         }
                     }
-                    return new CompletedFutureResult<Result>(error);
+                    return newFailedFutureResult(error);
                 } else {
-                    final Result result = newResult(resultCode);
-                    if (handler != null) {
-                        handler.handleResult(result);
-                    }
-                    return new CompletedFutureResult<Result>(result);
+                    return newSuccessfulFutureResult(newResult(resultCode));
                 }
             }
-        }).when(mockConnection).searchAsync(any(SearchRequest.class),
-                any(IntermediateResponseHandler.class), any(SearchResultHandler.class));
+        };
+
+        doAnswer(answer).when(mockConnection).searchAsync(any(SearchRequest.class), any(SearchResultHandler.class));
+        doAnswer(answer).when(mockConnection).searchAsync(any(SearchRequest.class),
+            any(IntermediateResponseHandler.class), any(SearchResultHandler.class));
+
         return mockConnection;
     }
 
     private void verifyHeartBeatSent(final Connection connection, final int times) {
-        verify(connection, times(times)).searchAsync(same(HEARTBEAT),
-                any(IntermediateResponseHandler.class), any(SearchResultHandler.class));
+        verify(connection, times(times)).searchAsync(same(HEARTBEAT), any(SearchResultHandler.class));
     }
 }
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
index c212fca..960042e 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
@@ -27,8 +27,6 @@
 
 package org.forgerock.opendj.ldap;
 
-import static org.forgerock.opendj.ldap.TestCaseUtils.findFreeSocketAddress;
-
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.util.HashMap;
@@ -78,6 +76,8 @@
 import com.forgerock.opendj.ldap.controls.AccountUsabilityRequestControl;
 import com.forgerock.opendj.ldap.controls.AccountUsabilityResponseControl;
 
+import static org.forgerock.opendj.ldap.TestCaseUtils.*;
+
 /**
  * A simple ldap server that manages 1000 entries and used for running
  * testcases.
@@ -100,19 +100,23 @@
             this.isCanceled = new AtomicBoolean(false);
         }
 
+        @Override
         public Request addControl(final Control cntrl) {
             return request.addControl(cntrl);
         }
 
+        @Override
         public boolean containsControl(final String oid) {
             return request.containsControl(oid);
         }
 
+        @Override
         public <C extends Control> C getControl(final ControlDecoder<C> decoder,
                 final DecodeOptions options) throws DecodeException {
             return request.getControl(decoder, options);
         }
 
+        @Override
         public List<Control> getControls() {
             return request.getControls();
         }
@@ -147,6 +151,7 @@
             this.clientContext = clientContext;
         }
 
+        @Override
         public void handleAbandon(final Integer context, final AbandonRequest request)
                 throws UnsupportedOperationException {
             // Check if we have any concurrent operation with this message id.
@@ -160,6 +165,7 @@
             // No response is needed.
         }
 
+        @Override
         public void handleAdd(final Integer context, final AddRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler,
                 final ResultHandler<Result> handler) throws UnsupportedOperationException {
@@ -172,7 +178,7 @@
                 // duplicate entry.
                 result = Responses.newResult(ResultCode.ENTRY_ALREADY_EXISTS);
                 final ErrorResultException ere = ErrorResultException.newErrorResult(result);
-                handler.handleErrorResult(ere);
+                handler.handleError(ere);
                 // doesn't matter if it was canceled.
                 requestsInProgress.remove(context);
                 return;
@@ -191,7 +197,7 @@
             if (abReq.isCanceled()) {
                 result = Responses.newResult(ResultCode.CANCELLED);
                 final ErrorResultException ere = ErrorResultException.newErrorResult(result);
-                handler.handleErrorResult(ere);
+                handler.handleError(ere);
                 requestsInProgress.remove(context);
                 return;
             }
@@ -202,6 +208,7 @@
             handler.handleResult(result);
         }
 
+        @Override
         public void handleBind(final Integer context, final int version, final BindRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler,
                 final ResultHandler<BindResult> resultHandler) throws UnsupportedOperationException {
@@ -229,6 +236,7 @@
                                 Sasl.createSaslServer(saslMech, "ldap",
                                         listener.getHostName(), props,
                                         new CallbackHandler() {
+                                            @Override
                                             public void handle(Callback[] callbacks)
                                                     throws IOException,
                                                     UnsupportedCallbackException {
@@ -264,6 +272,7 @@
                                 && (qop.equalsIgnoreCase("auth-int") || qop
                                         .equalsIgnoreCase("auth-conf"))) {
                             ConnectionSecurityLayer csl = new ConnectionSecurityLayer() {
+                                @Override
                                 public void dispose() {
                                     try {
                                         saslServer.dispose();
@@ -272,6 +281,7 @@
                                     }
                                 }
 
+                                @Override
                                 public byte[] unwrap(byte[] incoming, int offset, int len)
                                         throws ErrorResultException {
                                     try {
@@ -283,6 +293,7 @@
                                     }
                                 }
 
+                                @Override
                                 public byte[] wrap(byte[] outgoing, int offset, int len)
                                         throws ErrorResultException {
                                     try {
@@ -304,7 +315,7 @@
                                 ByteString.wrap(challenge)));
                     }
                 } catch (Exception e) {
-                    resultHandler.handleErrorResult(ErrorResultException.newErrorResult(Responses
+                    resultHandler.handleError(ErrorResultException.newErrorResult(Responses
                             .newBindResult(ResultCode.OPERATIONS_ERROR).setCause(e)
                             .setDiagnosticMessage(e.toString())));
                 }
@@ -314,6 +325,7 @@
             requestsInProgress.remove(context);
         }
 
+        @Override
         public void handleConnectionClosed(final Integer context, final UnbindRequest request) {
             close();
         }
@@ -328,14 +340,17 @@
             }
         }
 
+        @Override
         public void handleConnectionDisconnected(ResultCode resultCode, String message) {
             close();
         }
 
+        @Override
         public void handleConnectionError(final Throwable error) {
             close();
         }
 
+        @Override
         public void handleCompare(final Integer context, final CompareRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler,
                 final ResultHandler<CompareResult> resultHandler)
@@ -349,7 +364,7 @@
                 // entry not found.
                 result = Responses.newCompareResult(ResultCode.NO_SUCH_ATTRIBUTE);
                 final ErrorResultException ere = ErrorResultException.newErrorResult(result);
-                resultHandler.handleErrorResult(ere);
+                resultHandler.handleError(ere);
                 // doesn't matter if it was canceled.
                 requestsInProgress.remove(context);
                 return;
@@ -365,7 +380,7 @@
                     if (abReq.isCanceled()) {
                         final Result r = Responses.newResult(ResultCode.CANCELLED);
                         final ErrorResultException ere = ErrorResultException.newErrorResult(r);
-                        resultHandler.handleErrorResult(ere);
+                        resultHandler.handleError(ere);
                         requestsInProgress.remove(context);
                         return;
                     }
@@ -382,6 +397,7 @@
             requestsInProgress.remove(context);
         }
 
+        @Override
         public void handleDelete(final Integer context, final DeleteRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler,
                 final ResultHandler<Result> handler) throws UnsupportedOperationException {
@@ -394,7 +410,7 @@
                 // entry is not found.
                 result = Responses.newResult(ResultCode.NO_SUCH_OBJECT);
                 final ErrorResultException ere = ErrorResultException.newErrorResult(result);
-                handler.handleErrorResult(ere);
+                handler.handleError(ere);
                 // doesn't matter if it was canceled.
                 requestsInProgress.remove(context);
                 return;
@@ -403,7 +419,7 @@
             if (abReq.isCanceled()) {
                 result = Responses.newResult(ResultCode.CANCELLED);
                 final ErrorResultException ere = ErrorResultException.newErrorResult(result);
-                handler.handleErrorResult(ere);
+                handler.handleError(ere);
                 requestsInProgress.remove(context);
                 return;
             }
@@ -414,6 +430,7 @@
             handler.handleResult(result);
         }
 
+        @Override
         public <R extends ExtendedResult> void handleExtendedRequest(final Integer context,
                 final ExtendedRequest<R> request,
                 final IntermediateResponseHandler intermediateResponseHandler,
@@ -428,21 +445,24 @@
             }
         }
 
+        @Override
         public void handleModify(final Integer context, final ModifyRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler,
                 final ResultHandler<Result> resultHandler) throws UnsupportedOperationException {
             // TODO:
         }
 
+        @Override
         public void handleModifyDN(final Integer context, final ModifyDNRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler,
                 final ResultHandler<Result> resultHandler) throws UnsupportedOperationException {
             // TODO
         }
 
+        @Override
         public void handleSearch(final Integer context, final SearchRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final SearchResultHandler resultHandler) throws UnsupportedOperationException {
+            final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler,
+            final ResultHandler<Result> resultHandler) throws UnsupportedOperationException {
             Result result = null;
             final AbandonableRequest abReq = new AbandonableRequest(request);
             requestsInProgress.put(context, abReq);
@@ -452,7 +472,7 @@
                 // Entry not found.
                 result = Responses.newResult(ResultCode.NO_SUCH_OBJECT);
                 final ErrorResultException ere = ErrorResultException.newErrorResult(result);
-                resultHandler.handleErrorResult(ere);
+                resultHandler.handleError(ere);
                 // Should searchResultHandler handle anything?
 
                 // doesn't matter if it was canceled.
@@ -463,21 +483,19 @@
             if (abReq.isCanceled()) {
                 result = Responses.newResult(ResultCode.CANCELLED);
                 final ErrorResultException ere = ErrorResultException.newErrorResult(result);
-                resultHandler.handleErrorResult(ere);
+                resultHandler.handleError(ere);
                 requestsInProgress.remove(context);
                 return;
             }
 
-            final SearchResultEntry e =
-                    Responses.newSearchResultEntry(new LinkedHashMapEntry(entryMap.get(dn)));
+            final SearchResultEntry e = Responses.newSearchResultEntry(new LinkedHashMapEntry(entryMap.get(dn)));
             // Check we have had any controls in the request.
             for (final Control control : request.getControls()) {
                 if (control.getOID().equals(AccountUsabilityRequestControl.OID)) {
-                    e.addControl(AccountUsabilityResponseControl.newControl(false, false, false,
-                            10, false, 0));
+                    e.addControl(AccountUsabilityResponseControl.newControl(false, false, false, 10, false, 0));
                 }
             }
-            resultHandler.handleEntry(e);
+            entryHandler.handleEntry(e);
             result = Responses.newResult(ResultCode.SUCCESS);
             resultHandler.handleResult(result);
             requestsInProgress.remove(context);
@@ -521,6 +539,7 @@
         }
     }
 
+    @Override
     public ServerConnection<Integer> handleAccept(final LDAPClientContext context) {
         return new LDAPServerConnection(context);
     }
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/TestCaseUtils.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/TestCaseUtils.java
index e81a94c..449830e 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/TestCaseUtils.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/TestCaseUtils.java
@@ -26,12 +26,6 @@
  */
 package org.forgerock.opendj.ldap;
 
-import static org.fest.assertions.Fail.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
@@ -42,13 +36,18 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import org.forgerock.util.promise.Promise;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 import org.mockito.stubbing.OngoingStubbing;
 
-import com.forgerock.opendj.util.CompletedFutureResult;
 import com.forgerock.opendj.util.TimeSource;
 
+import static org.fest.assertions.Fail.*;
+import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
 /**
  * This class defines some utility functions which can be used by test cases.
  */
@@ -164,30 +163,20 @@
      *            The remaining connections to return.
      * @return The connection factory.
      */
-    @SuppressWarnings("unchecked")
-    public static ConnectionFactory mockConnectionFactory(final Connection first,
-            final Connection... remaining) {
+    public static ConnectionFactory mockConnectionFactory(final Connection first, final Connection... remaining) {
         final ConnectionFactory factory = mock(ConnectionFactory.class);
         try {
             when(factory.getConnection()).thenReturn(first, remaining);
         } catch (ErrorResultException ignored) {
             // Cannot happen.
         }
-        when(factory.getConnectionAsync(any(ResultHandler.class))).thenAnswer(
-                new Answer<FutureResult<Connection>>() {
-                    @Override
-                    public FutureResult<Connection> answer(final InvocationOnMock invocation)
-                            throws Throwable {
-                        final Connection connection = factory.getConnection();
-                        // Execute handler and return future.
-                        final ResultHandler<? super Connection> handler =
-                                (ResultHandler<? super Connection>) invocation.getArguments()[0];
-                        if (handler != null) {
-                            handler.handleResult(connection);
-                        }
-                        return new CompletedFutureResult<Connection>(connection);
-                    }
-                });
+        when(factory.getConnectionAsync()).thenAnswer(new Answer<Promise<Connection, ErrorResultException>>() {
+            @Override
+            public Promise<Connection, ErrorResultException> answer(final InvocationOnMock invocation)
+                    throws Throwable {
+                return newSuccessfulFutureResult(factory.getConnection());
+            }
+        });
         return factory;
     }
 
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java
index 7cbfaa0..e88de44 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java
@@ -25,31 +25,33 @@
  */
 package org.forgerock.opendj.ldap.schema;
 
-import static org.fest.assertions.Assertions.assertThat;
-import static org.fest.assertions.Fail.fail;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.SCHEMA_PROPERTY_ORIGIN;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.TOP_OBJECTCLASS_NAME;
-
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
 import org.forgerock.i18n.LocalizedIllegalArgumentException;
 import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.DN;
 import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.Entry;
 import org.forgerock.opendj.ldap.EntryNotFoundException;
+import org.forgerock.opendj.ldap.ErrorResultException;
+import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.FutureResultWrapper;
 import org.forgerock.opendj.ldap.LinkedHashMapEntry;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import org.testng.annotations.Test;
-import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
 import org.forgerock.opendj.ldap.responses.Responses;
+import org.forgerock.opendj.ldap.responses.SearchResultEntry;
+import org.forgerock.util.promise.Promise;
+import org.testng.annotations.Test;
+
+import static org.fest.assertions.Assertions.*;
+import static org.fest.assertions.Fail.*;
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.util.promise.Promises.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
 
 /**
  * Test SchemaBuilder.
@@ -1969,6 +1971,55 @@
         connection.close();
     }
 
+    /**
+     * Asynchronously retrieving an LDAP Server's schema.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public final void testSchemaBuilderAddSchemaForEntryAsyncMockConnection() throws Exception {
+        Connection connection = mock(Connection.class);
+        final SchemaBuilder scBuild = new SchemaBuilder(Schema.getCoreSchema());
+
+        // @formatter:off
+        final String[] entry = {
+            "# Search result entry: uid=bjensen,ou=People,dc=example,dc=com",
+            "dn: uid=bjensen,ou=People,dc=example,dc=com",
+            "subschemaSubentry: cn=schema",
+            "entryDN: uid=bjensen,ou=people,dc=example,dc=com",
+            "entryUUID: fc252fd9-b982-3ed6-b42a-c76d2546312c"
+            // N.B : also works with previous example but needs the subschemaSubentry line.
+        };
+
+        // Send a search entry result promise :
+        Promise<SearchResultEntry, ErrorResultException> promise =
+                newSuccessfulPromise(Responses.newSearchResultEntry(entry));
+        FutureResult<SearchResultEntry> result = FutureResultWrapper.asFutureResult(promise);
+        when(connection.searchSingleEntryAsync((SearchRequest) any())).thenReturn(result);
+        DN testDN = DN.valueOf("uid=bjensen,ou=People,dc=example,dc=com");
+        // @formatter:on
+        Schema sc = scBuild.addSchemaForEntryAsync(connection, testDN, false).getOrThrow().toSchema();
+
+        // We retrieve the schema
+        assertThat(sc.getSyntaxes()).isNotNull();
+        assertThat(sc.getAttributeTypes()).isNotNull();
+        assertThat(sc.getAttributeTypes()).isNotEmpty();
+        assertThat(sc.getObjectClasses()).isNotNull();
+        assertThat(sc.getObjectClasses()).isNotEmpty();
+        assertThat(sc.getMatchingRuleUses()).isNotNull();
+        assertThat(sc.getMatchingRuleUses()).isEmpty();
+        assertThat(sc.getMatchingRules()).isNotNull();
+        assertThat(sc.getMatchingRules()).isNotEmpty();
+        assertThat(sc.getDITContentRules()).isNotNull();
+        assertThat(sc.getDITContentRules()).isEmpty();
+        assertThat(sc.getDITStuctureRules()).isNotNull();
+        assertThat(sc.getDITStuctureRules()).isEmpty();
+        assertThat(sc.getNameForms()).isNotNull();
+        assertThat(sc.getNameForms()).isEmpty();
+
+        connection.close();
+    }
+
     @Test
     public void testDefaultSyntax() {
         final Schema schema =
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaTestCase.java
index 7cf4601..0bdc26a 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaTestCase.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaTestCase.java
@@ -26,7 +26,19 @@
 package org.forgerock.opendj.ldap.schema;
 
 import static org.fest.assertions.Assertions.assertThat;
+import static org.forgerock.util.promise.Promises.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
 
+import org.forgerock.opendj.ldap.Connection;
+import org.forgerock.opendj.ldap.DN;
+import org.forgerock.opendj.ldap.ErrorResultException;
+import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.FutureResultWrapper;
+import org.forgerock.opendj.ldap.requests.SearchRequest;
+import org.forgerock.opendj.ldap.responses.Responses;
+import org.forgerock.opendj.ldap.responses.SearchResultEntry;
+import org.forgerock.util.promise.Promise;
 import org.testng.annotations.Test;
 
 /**
@@ -50,4 +62,54 @@
         final Schema strictSchema2 = schema.asStrictSchema().asNonStrictSchema().asStrictSchema();
         assertThat(strictSchema1).isSameAs(strictSchema2);
     }
+
+    /**
+     * Asynchronously retrieving a simple schema.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public final void testReadSchemaAsyncMethodsMockConnection() throws Exception {
+        Connection connection = mock(Connection.class);
+
+        // @formatter:off
+        final String[] entry = {
+            "# Search result entry: uid=bjensen,ou=People,dc=example,dc=com",
+            "dn: uid=bjensen,ou=People,dc=example,dc=com",
+            "subschemaSubentry: cn=schema",
+            "entryDN: uid=bjensen,ou=people,dc=example,dc=com",
+            "entryUUID: fc252fd9-b982-3ed6-b42a-c76d2546312c"
+            // N.B : also works with previous example but needs the subschemaSubentry line.
+        };
+
+        // Send a search entry result promise :
+        Promise<SearchResultEntry, ErrorResultException> promise =
+                newSuccessfulPromise(Responses.newSearchResultEntry(entry));
+        FutureResult<SearchResultEntry> result = FutureResultWrapper.asFutureResult(promise);
+        when(connection.searchSingleEntryAsync((SearchRequest) any())).thenReturn(result);
+        DN testDN = DN.valueOf("uid=bjensen,ou=People,dc=example,dc=com");
+        // @formatter:on
+        Schema[] schemas = new Schema[] {
+                Schema.readSchemaAsync(connection, testDN).getOrThrow(),
+                Schema.readSchemaForEntryAsync(connection, testDN).getOrThrow()
+        };
+
+        // We retrieve the schemas :
+        for (Schema sc : schemas) {
+            assertThat(sc.getSyntaxes()).isNotNull();
+            assertThat(sc.getAttributeTypes()).isNotNull();
+            assertThat(sc.getObjectClasses()).isNotNull();
+            assertThat(sc.getMatchingRuleUses()).isNotNull();
+            assertThat(sc.getMatchingRuleUses()).isEmpty();
+            assertThat(sc.getMatchingRules()).isNotNull();
+            assertThat(sc.getDITContentRules()).isNotNull();
+            assertThat(sc.getDITContentRules()).isEmpty();
+            assertThat(sc.getDITStuctureRules()).isNotNull();
+            assertThat(sc.getDITStuctureRules()).isEmpty();
+            assertThat(sc.getNameForms()).isNotNull();
+            assertThat(sc.getNameForms()).isEmpty();
+        }
+        connection.close();
+    }
+
 }
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/spi/BasicLDAPConnectionFactory.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/spi/BasicLDAPConnectionFactory.java
index 125d926..72e391f 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/spi/BasicLDAPConnectionFactory.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/spi/BasicLDAPConnectionFactory.java
@@ -26,19 +26,17 @@
 
 package org.forgerock.opendj.ldap.spi;
 
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-import static org.mockito.Mockito.mock;
-
 import java.net.InetSocketAddress;
 
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.ErrorResultException;
-import org.forgerock.opendj.ldap.FutureResult;
 import org.forgerock.opendj.ldap.LDAPOptions;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
+import org.forgerock.util.promise.Promise;
 
-import com.forgerock.opendj.util.AsynchronousFutureResult;
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+import static org.forgerock.util.promise.Promises.*;
+import static org.mockito.Mockito.*;
 
 /**
  * Basic LDAP connection factory implementation to use for tests only.
@@ -73,19 +71,15 @@
     @Override
     public Connection getConnection() throws ErrorResultException {
         try {
-            return getConnectionAsync(null).get();
+            return getConnectionAsync().getOrThrow();
         } catch (final InterruptedException e) {
             throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, e);
         }
     }
 
     @Override
-    public FutureResult<Connection> getConnectionAsync(
-            final ResultHandler<? super Connection> handler) {
-        final AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> future =
-                new AsynchronousFutureResult<Connection, ResultHandler<? super Connection>>(handler);
-        future.handleResult(mock(Connection.class));
-        return future;
+    public Promise<Connection, ErrorResultException> getConnectionAsync() {
+        return newSuccessfulPromise(mock(Connection.class));
     }
 
     /**
@@ -93,6 +87,7 @@
      *
      * @return The address of the Directory Server.
      */
+    @Override
     public InetSocketAddress getSocketAddress() {
         return new InetSocketAddress(host, port);
     }
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldif/ConnectionEntryReaderTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldif/ConnectionEntryReaderTestCase.java
index 34882fd..553274a 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldif/ConnectionEntryReaderTestCase.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldif/ConnectionEntryReaderTestCase.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2011 ForgeRock AS
- *      Portions copyright 2012 ForgeRock AS.
+ *      Portions copyright 2012-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldif;
@@ -34,7 +34,6 @@
 import static org.forgerock.opendj.ldap.responses.Responses.newSearchResultEntry;
 import static org.forgerock.opendj.ldap.responses.Responses.newSearchResultReference;
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.isNull;
 import static org.mockito.Matchers.same;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -44,7 +43,7 @@
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.ErrorResultIOException;
 import org.forgerock.opendj.ldap.FutureResult;
-import org.forgerock.opendj.ldap.IntermediateResponseHandler;
+import org.forgerock.opendj.ldap.FutureResultWrapper;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchResultHandler;
 import org.forgerock.opendj.ldap.SearchResultReferenceIOException;
@@ -58,8 +57,6 @@
 import org.mockito.stubbing.Answer;
 import org.testng.annotations.Test;
 
-import com.forgerock.opendj.util.CompletedFutureResult;
-
 /**
  * This class tests the ConnectionEntryReader functionality.
  */
@@ -254,37 +251,30 @@
     private ConnectionEntryReader newReader(final Object... responses) {
         final Connection connection = mock(Connection.class);
         // @formatter:off
-        when(connection.searchAsync(same(SEARCH), (IntermediateResponseHandler) isNull(),
-                any(SearchResultHandler.class))).thenAnswer(
-                        new Answer<FutureResult<Result>>() {
-                            @Override
-                            public FutureResult<Result> answer(final InvocationOnMock invocation)
-                                    throws Throwable {
-                                // Execute handler and return future.
-                                final SearchResultHandler handler =
-                                        (SearchResultHandler) invocation.getArguments()[2];
-                                if (handler != null) {
-                                    for (int i = 0; i < responses.length; i++) {
-                                        final Object response = responses[i];
-                                        if (response instanceof SearchResultEntry) {
-                                            handler.handleEntry((SearchResultEntry) response);
-                                        } else if (response instanceof SearchResultReference) {
-                                            handler.handleReference((SearchResultReference) response);
-                                        } else if (((Result) response).isSuccess()) {
-                                            handler.handleResult((Result) response);
-                                        } else {
-                                            handler.handleErrorResult(newErrorResult((Result) response));
-                                        }
-                                    }
-                                }
-                                final Result result = (Result) responses[responses.length - 1];
-                                if (result.isSuccess()) {
-                                    return new CompletedFutureResult<Result>(result);
-                                } else {
-                                    return new CompletedFutureResult<Result>(newErrorResult(result));
-                                }
+        when(connection.searchAsync(same(SEARCH), any(SearchResultHandler.class))).thenAnswer(
+            new Answer<FutureResult<Result>>() {
+                @Override
+                public FutureResult<Result> answer(final InvocationOnMock invocation) throws Throwable {
+                    // Execute handler and return future.
+                    final SearchResultHandler handler = (SearchResultHandler) invocation.getArguments()[1];
+                    if (handler != null) {
+                        for (int i = 0; i < responses.length; i++) {
+                            final Object response = responses[i];
+                            if (response instanceof SearchResultEntry) {
+                                handler.handleEntry((SearchResultEntry) response);
+                            } else if (response instanceof SearchResultReference) {
+                                handler.handleReference((SearchResultReference) response);
                             }
-                        });
+                        }
+                    }
+                    final Result result = (Result) responses[responses.length - 1];
+                    if (result.isSuccess()) {
+                        return FutureResultWrapper.newSuccessfulFutureResult(result);
+                    } else {
+                        return FutureResultWrapper.newFailedFutureResult(newErrorResult(result));
+                    }
+                }
+            });
         // @formatter:on
         return new ConnectionEntryReader(connection, SEARCH);
     }
diff --git a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java
index 591a660..f1470e4 100644
--- a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java
+++ b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java
@@ -26,9 +26,6 @@
  */
 package org.forgerock.opendj.grizzly;
 
-import static com.forgerock.opendj.grizzly.GrizzlyMessages.*;
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-
 import java.io.IOException;
 import java.security.GeneralSecurityException;
 import java.util.List;
@@ -52,7 +49,6 @@
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.LDAPOptions;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.SSLContextBuilder;
 import org.forgerock.opendj.ldap.SearchResultHandler;
 import org.forgerock.opendj.ldap.TimeoutEventListener;
@@ -81,15 +77,17 @@
 import org.forgerock.opendj.ldap.spi.LDAPExtendedFutureResultImpl;
 import org.forgerock.opendj.ldap.spi.LDAPFutureResultImpl;
 import org.forgerock.opendj.ldap.spi.LDAPSearchFutureResultImpl;
+import org.forgerock.util.Reject;
 import org.glassfish.grizzly.CompletionHandler;
 import org.glassfish.grizzly.filterchain.Filter;
 import org.glassfish.grizzly.filterchain.FilterChain;
 import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
 import org.glassfish.grizzly.ssl.SSLFilter;
 
-import com.forgerock.opendj.util.CompletedFutureResult;
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
+import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
 
-import org.forgerock.util.Reject;
+import static com.forgerock.opendj.grizzly.GrizzlyMessages.*;
 
 /**
  * LDAP connection implementation.
@@ -120,12 +118,12 @@
     private final ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>> pendingRequests =
             new ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>>();
     private final Object stateLock = new Object();
-    // Guarded by stateLock
+    /** Guarded by stateLock. */
     private Result connectionInvalidReason;
-    private boolean failedDueToDisconnect = false;
-    private boolean isClosed = false;
-    private boolean isFailed = false;
-    private List<ConnectionEventListener> listeners = null;
+    private boolean failedDueToDisconnect;
+    private boolean isClosed;
+    private boolean isFailed;
+    private List<ConnectionEventListener> listeners;
 
     /**
      * Create a LDAP Connection with provided Grizzly connection and LDAP
@@ -162,19 +160,18 @@
                 checkBindOrStartTLSInProgress();
             }
         } catch (final ErrorResultException e) {
-            return new CompletedFutureResult<Void>(e);
+            return newFailedFutureResult(e);
         }
 
         // Remove the future associated with the request to be abandoned.
-        final AbstractLDAPFutureResultImpl<?> pendingRequest =
-                pendingRequests.remove(request.getRequestID());
+        final AbstractLDAPFutureResultImpl<?> pendingRequest = pendingRequests.remove(request.getRequestID());
         if (pendingRequest == null) {
             /*
              * There has never been a request with the specified message ID or
              * the response has already been received and handled. We can ignore
              * this abandon request.
              */
-            return new CompletedFutureResult<Void>((Void) null);
+            return newSuccessfulFutureResult((Void) null);
         }
 
         /*
@@ -197,9 +194,9 @@
             final int messageID = nextMsgID.getAndIncrement();
             writer.writeAbandonRequest(messageID, request);
             connection.write(writer.getASN1Writer().getBuffer(), null);
-            return new CompletedFutureResult<Void>((Void) null, messageID);
+            return newSuccessfulFutureResult((Void) null, messageID);
         } catch (final IOException e) {
-            return new CompletedFutureResult<Void>(adaptRequestIOException(e));
+            return newFailedFutureResult(adaptRequestIOException(e));
         } finally {
             GrizzlyUtils.recycleWriter(writer);
         }
@@ -207,12 +204,10 @@
 
     @Override
     public FutureResult<Result> addAsync(final AddRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final int messageID = nextMsgID.getAndIncrement();
         final LDAPFutureResultImpl future =
-                new LDAPFutureResultImpl(messageID, request, resultHandler,
-                        intermediateResponseHandler, this);
+                new LDAPFutureResultImpl(messageID, request, intermediateResponseHandler, this);
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
@@ -264,42 +259,35 @@
 
     @Override
     public FutureResult<BindResult> bindAsync(final BindRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super BindResult> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final int messageID = nextMsgID.getAndIncrement();
         final BindClient context;
         try {
-            context =
-                    request.createBindClient(Connections.getHostString(factory.getSocketAddress()));
+            context = request.createBindClient(Connections.getHostString(factory.getSocketAddress()));
         } catch (final Exception e) {
             // FIXME: I18N need to have a better error message.
             // FIXME: Is this the best result code?
-            final Result errorResult =
-                    Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
-                            "An error occurred while creating a bind context").setCause(e);
+            final Result errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
+                    .setDiagnosticMessage("An error occurred while creating a bind context").setCause(e);
             final ErrorResultException error = ErrorResultException.newErrorResult(errorResult);
-            if (resultHandler != null) {
-                resultHandler.handleErrorResult(error);
-            }
-            return new CompletedFutureResult<BindResult>(error, messageID);
+
+            return newFailedFutureResult(error, messageID);
         }
 
         final LDAPBindFutureResultImpl future =
-                new LDAPBindFutureResultImpl(messageID, context, resultHandler,
-                        intermediateResponseHandler, this);
+                new LDAPBindFutureResultImpl(messageID, context, intermediateResponseHandler, this);
 
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
                 if (!pendingRequests.isEmpty()) {
-                    future.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR)
-                            .setDiagnosticMessage(
-                                    "There are other operations pending on this connection"));
+                    future.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
+                            "There are other operations pending on this connection"));
                     return future;
                 }
                 if (!bindOrStartTLSInProgress.compareAndSet(false, true)) {
-                    future.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR)
-                            .setDiagnosticMessage("Bind or Start TLS operation in progress"));
+                    future.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
+                            "Bind or Start TLS operation in progress"));
                     return future;
                 }
                 pendingRequests.put(messageID, future);
@@ -338,12 +326,10 @@
 
     @Override
     public FutureResult<CompareResult> compareAsync(final CompareRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super CompareResult> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final int messageID = nextMsgID.getAndIncrement();
         final LDAPCompareFutureResultImpl future =
-                new LDAPCompareFutureResultImpl(messageID, request, resultHandler,
-                        intermediateResponseHandler, this);
+                new LDAPCompareFutureResultImpl(messageID, request, intermediateResponseHandler, this);
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
@@ -370,12 +356,10 @@
 
     @Override
     public FutureResult<Result> deleteAsync(final DeleteRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final int messageID = nextMsgID.getAndIncrement();
         final LDAPFutureResultImpl future =
-                new LDAPFutureResultImpl(messageID, request, resultHandler,
-                        intermediateResponseHandler, this);
+                new LDAPFutureResultImpl(messageID, request, intermediateResponseHandler, this);
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
@@ -401,32 +385,26 @@
     }
 
     @Override
-    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(
-            final ExtendedRequest<R> request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super R> resultHandler) {
+    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request,
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final int messageID = nextMsgID.getAndIncrement();
         final LDAPExtendedFutureResultImpl<R> future =
-                new LDAPExtendedFutureResultImpl<R>(messageID, request, resultHandler,
-                        intermediateResponseHandler, this);
+                new LDAPExtendedFutureResultImpl<R>(messageID, request, intermediateResponseHandler, this);
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
-                if (request.getOID().equals(StartTLSExtendedRequest.OID)) {
+                if (StartTLSExtendedRequest.OID.equals(request.getOID())) {
                     if (!pendingRequests.isEmpty()) {
                         future.setResultOrError(request.getResultDecoder().newExtendedErrorResult(
-                                ResultCode.OPERATIONS_ERROR, "",
-                                "There are pending operations on this connection"));
+                                ResultCode.OPERATIONS_ERROR, "", "There are pending operations on this connection"));
                         return future;
                     } else if (isTLSEnabled()) {
                         future.setResultOrError(request.getResultDecoder().newExtendedErrorResult(
-                                ResultCode.OPERATIONS_ERROR, "",
-                                "This connection is already TLS enabled"));
+                                ResultCode.OPERATIONS_ERROR, "", "This connection is already TLS enabled"));
                         return future;
                     } else if (!bindOrStartTLSInProgress.compareAndSet(false, true)) {
                         future.setResultOrError(request.getResultDecoder().newExtendedErrorResult(
-                                ResultCode.OPERATIONS_ERROR, "",
-                                "Bind or Start TLS operation in progress"));
+                                ResultCode.OPERATIONS_ERROR, "", "Bind or Start TLS operation in progress"));
                         return future;
                     }
                 } else {
@@ -469,12 +447,10 @@
 
     @Override
     public FutureResult<Result> modifyAsync(final ModifyRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final int messageID = nextMsgID.getAndIncrement();
         final LDAPFutureResultImpl future =
-                new LDAPFutureResultImpl(messageID, request, resultHandler,
-                        intermediateResponseHandler, this);
+                new LDAPFutureResultImpl(messageID, request, intermediateResponseHandler, this);
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
@@ -501,12 +477,10 @@
 
     @Override
     public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<? super Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler) {
         final int messageID = nextMsgID.getAndIncrement();
         final LDAPFutureResultImpl future =
-                new LDAPFutureResultImpl(messageID, request, resultHandler,
-                        intermediateResponseHandler, this);
+                new LDAPFutureResultImpl(messageID, request, intermediateResponseHandler, this);
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
@@ -541,14 +515,13 @@
         }
     }
 
+    /** {@inheritDoc} */
     @Override
     public FutureResult<Result> searchAsync(final SearchRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final SearchResultHandler resultHandler) {
+        final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) {
         final int messageID = nextMsgID.getAndIncrement();
         final LDAPSearchFutureResultImpl future =
-                new LDAPSearchFutureResultImpl(messageID, request, resultHandler,
-                        intermediateResponseHandler, this);
+            new LDAPSearchFutureResultImpl(messageID, request, entryHandler, intermediateResponseHandler, this);
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
@@ -614,23 +587,18 @@
                  */
                 logger.debug(LocalizableMessage.raw("Failing bind or StartTLS request due to timeout %s"
                         + "(connection will be invalidated): ", future));
-                final Result result =
-                        Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT).setDiagnosticMessage(
-                                LDAP_CONNECTION_BIND_OR_START_TLS_REQUEST_TIMEOUT.get(timeout)
-                                        .toString());
+                final Result result = Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT).setDiagnosticMessage(
+                        LDAP_CONNECTION_BIND_OR_START_TLS_REQUEST_TIMEOUT.get(timeout).toString());
                 future.adaptErrorResult(result);
 
                 // Fail the connection.
-                final Result errorResult =
-                        Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT).setDiagnosticMessage(
-                                LDAP_CONNECTION_BIND_OR_START_TLS_CONNECTION_TIMEOUT.get(timeout)
-                                        .toString());
+                final Result errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT).setDiagnosticMessage(
+                        LDAP_CONNECTION_BIND_OR_START_TLS_CONNECTION_TIMEOUT.get(timeout).toString());
                 connectionErrorOccurred(errorResult);
             } else {
                 logger.debug(LocalizableMessage.raw("Failing request due to timeout: %s", future));
-                final Result result =
-                        Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT).setDiagnosticMessage(
-                                LDAP_CONNECTION_REQUEST_TIMEOUT.get(timeout).toString());
+                final Result result = Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT).setDiagnosticMessage(
+                        LDAP_CONNECTION_REQUEST_TIMEOUT.get(timeout).toString());
                 future.adaptErrorResult(result);
 
                 /*
@@ -640,9 +608,9 @@
                  * request while holding the state lock, since a blocking write
                  * could hang the application.
                  */
-//              if (!bindOrStartTLSInProgress.get()) {
-//                  sendAbandonRequest(newAbandonRequest(future.getRequestID()));
-//              }
+                // if (!bindOrStartTLSInProgress.get()) {
+                // sendAbandonRequest(newAbandonRequest(future.getRequestID()));
+                // }
             }
         }
         return delay;
@@ -813,22 +781,20 @@
         bindOrStartTLSInProgress.set(state);
     }
 
-    void startTLS(final SSLContext sslContext, final List<String> protocols,
-            final List<String> cipherSuites, final CompletionHandler<SSLEngine> completionHandler)
-            throws IOException {
+    void startTLS(final SSLContext sslContext, final List<String> protocols, final List<String> cipherSuites,
+            final CompletionHandler<SSLEngine> completionHandler) throws IOException {
         synchronized (stateLock) {
             if (isTLSEnabled()) {
                 throw new IllegalStateException("TLS already enabled");
             }
 
-            final SSLEngineConfigurator sslEngineConfigurator =
-                    new SSLEngineConfigurator(sslContext, true, false, false);
+            final SSLEngineConfigurator sslEngineConfigurator = new SSLEngineConfigurator(sslContext, true, false,
+                    false);
             sslEngineConfigurator.setEnabledProtocols(protocols.isEmpty() ? null : protocols
                     .toArray(new String[protocols.size()]));
-            sslEngineConfigurator.setEnabledCipherSuites(cipherSuites.isEmpty() ? null
-                    : cipherSuites.toArray(new String[cipherSuites.size()]));
-            final SSLFilter sslFilter =
-                    new SSLFilter(DUMMY_SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
+            sslEngineConfigurator.setEnabledCipherSuites(cipherSuites.isEmpty() ? null : cipherSuites
+                    .toArray(new String[cipherSuites.size()]));
+            final SSLFilter sslFilter = new SSLFilter(DUMMY_SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
             installFilter(sslFilter);
             sslFilter.handshake(connection, completionHandler);
         }
@@ -837,16 +803,14 @@
     private ErrorResultException adaptRequestIOException(final IOException e) {
         // FIXME: what other sort of IOExceptions can be thrown?
         // FIXME: Is this the best result code?
-        final Result errorResult =
-                Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
+        final Result errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
         connectionErrorOccurred(errorResult);
         return newErrorResult(errorResult);
     }
 
     private void checkBindOrStartTLSInProgress() throws ErrorResultException {
         if (bindOrStartTLSInProgress.get()) {
-            throw newErrorResult(ResultCode.OPERATIONS_ERROR,
-                    "Bind or Start TLS operation in progress");
+            throw newErrorResult(ResultCode.OPERATIONS_ERROR, "Bind or Start TLS operation in progress");
         }
     }
 
@@ -861,8 +825,7 @@
                  * this could be misinterpreted as a genuine authentication
                  * failure for subsequent bind requests.
                  */
-                throw newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN,
-                        "Connection closed by server");
+                throw newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN, "Connection closed by server");
             } else {
                 throw newErrorResult(connectionInvalidReason);
             }
diff --git a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java
index 4d3b78f..59a1959 100644
--- a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java
+++ b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java
@@ -27,13 +27,6 @@
 
 package org.forgerock.opendj.grizzly;
 
-import static com.forgerock.opendj.grizzly.GrizzlyMessages.LDAP_CONNECTION_CONNECT_TIMEOUT;
-import static org.forgerock.opendj.grizzly.DefaultTCPNIOTransport.DEFAULT_TRANSPORT;
-import static org.forgerock.opendj.grizzly.GrizzlyUtils.buildFilterChain;
-import static org.forgerock.opendj.grizzly.GrizzlyUtils.configureConnection;
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-import static org.forgerock.opendj.ldap.TimeoutChecker.TIMEOUT_CHECKER;
-
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.util.concurrent.ExecutionException;
@@ -46,16 +39,18 @@
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.ErrorResultException;
-import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.FutureResultImpl;
 import org.forgerock.opendj.ldap.LDAPOptions;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.TimeoutChecker;
 import org.forgerock.opendj.ldap.TimeoutEventListener;
 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.spi.LDAPConnectionFactoryImpl;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.SuccessHandler;
 import org.glassfish.grizzly.CompletionHandler;
 import org.glassfish.grizzly.EmptyCompletionHandler;
 import org.glassfish.grizzly.SocketConnectorHandler;
@@ -63,9 +58,15 @@
 import org.glassfish.grizzly.nio.transport.TCPNIOConnectorHandler;
 import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
 
-import com.forgerock.opendj.util.AsynchronousFutureResult;
 import com.forgerock.opendj.util.ReferenceCountedObject;
 
+import static org.forgerock.opendj.grizzly.DefaultTCPNIOTransport.*;
+import static org.forgerock.opendj.grizzly.GrizzlyUtils.*;
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+import static org.forgerock.opendj.ldap.TimeoutChecker.*;
+
+import static com.forgerock.opendj.grizzly.GrizzlyMessages.*;
+
 /**
  * LDAP connection factory implementation using Grizzly for transport.
  */
@@ -79,11 +80,10 @@
     @SuppressWarnings("rawtypes")
     private final class CompletionHandlerAdapter implements
             CompletionHandler<org.glassfish.grizzly.Connection>, TimeoutEventListener {
-        private final AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> future;
+        private final FutureResultImpl<Connection> future;
         private final long timeoutEndTime;
 
-        private CompletionHandlerAdapter(
-                final AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> future) {
+        private CompletionHandlerAdapter(final FutureResultImpl<Connection> future) {
             this.future = future;
             final long timeoutMS = getTimeout();
             this.timeoutEndTime = timeoutMS > 0 ? System.currentTimeMillis() + timeoutMS : 0;
@@ -120,37 +120,36 @@
                 final StartTLSExtendedRequest startTLS =
                         Requests.newStartTLSExtendedRequest(options.getSSLContext());
                 startTLS.addEnabledCipherSuite(options.getEnabledCipherSuites().toArray(
-                        new String[options.getEnabledCipherSuites().size()]));
+                    new String[options.getEnabledCipherSuites().size()]));
                 startTLS.addEnabledProtocol(options.getEnabledProtocols().toArray(
-                        new String[options.getEnabledProtocols().size()]));
-                final ResultHandler<ExtendedResult> handler = new ResultHandler<ExtendedResult>() {
-                    @Override
-                    public void handleErrorResult(final ErrorResultException error) {
-                        onFailure(connection, error);
-                    }
+                    new String[options.getEnabledProtocols().size()]));
 
+                connection.extendedRequestAsync(startTLS).onSuccess(new SuccessHandler<ExtendedResult>() {
                     @Override
                     public void handleResult(final ExtendedResult result) {
                         onSuccess(connection);
                     }
-                };
-                connection.extendedRequestAsync(startTLS, null, handler);
+                }).onFailure(new FailureHandler<ErrorResultException>() {
+                    @Override
+                    public void handleError(final ErrorResultException error) {
+                        onFailure(connection, error);
+                    }
+                });
             } else {
                 // Install SSL/TLS layer.
                 try {
                     connection.startTLS(options.getSSLContext(), options.getEnabledProtocols(),
-                            options.getEnabledCipherSuites(),
-                            new EmptyCompletionHandler<SSLEngine>() {
-                                @Override
-                                public void completed(final SSLEngine result) {
-                                    onSuccess(connection);
-                                }
+                        options.getEnabledCipherSuites(), new EmptyCompletionHandler<SSLEngine>() {
+                            @Override
+                            public void completed(final SSLEngine result) {
+                                onSuccess(connection);
+                            }
 
-                                @Override
-                                public void failed(final Throwable throwable) {
-                                    onFailure(connection, throwable);
-                                }
-                            });
+                            @Override
+                            public void failed(final Throwable throwable) {
+                                onFailure(connection, throwable);
+                            }
+                        });
                 } catch (final IOException e) {
                     onFailure(connection, e);
                 }
@@ -161,7 +160,7 @@
         public void failed(final Throwable throwable) {
             // Adapt and forward.
             timeoutChecker.get().removeListener(this);
-            future.handleErrorResult(adaptConnectionException(throwable));
+            future.handleError(adaptConnectionException(throwable));
             releaseTransportAndTimeoutChecker();
         }
 
@@ -197,7 +196,7 @@
         private void onFailure(final GrizzlyLDAPConnection connection, final Throwable t) {
             // Abort connection attempt due to error.
             timeoutChecker.get().removeListener(this);
-            future.handleErrorResult(adaptConnectionException(t));
+            future.handleError(adaptConnectionException(t));
             connection.close();
         }
 
@@ -216,7 +215,7 @@
             } else if (timeoutEndTime > currentTime) {
                 return timeoutEndTime - currentTime;
             } else {
-                future.handleErrorResult(newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
+                future.handleError(newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
                         LDAP_CONNECTION_CONNECT_TIMEOUT.get(getSocketAddress(), getTimeout()).toString()));
                 return 0;
             }
@@ -303,21 +302,19 @@
     @Override
     public Connection getConnection() throws ErrorResultException {
         try {
-            return getConnectionAsync(null).get();
+            return getConnectionAsync().getOrThrow();
         } catch (final InterruptedException e) {
             throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, e);
         }
     }
 
     @Override
-    public FutureResult<Connection> getConnectionAsync(
-            final ResultHandler<? super Connection> handler) {
+    public Promise<Connection, ErrorResultException> getConnectionAsync() {
         acquireTransportAndTimeoutChecker(); // Protect resources.
         final SocketConnectorHandler connectorHandler =
                 TCPNIOConnectorHandler.builder(transport.get()).processor(defaultFilterChain)
                         .build();
-        final AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> future =
-                new AsynchronousFutureResult<Connection, ResultHandler<? super Connection>>(handler);
+        final FutureResultImpl<Connection> future = new FutureResultImpl<Connection>();
         connectorHandler.connect(getSocketAddress(), new CompletionHandlerAdapter(future));
         return future;
     }
diff --git a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPServerFilter.java b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPServerFilter.java
index d9893dd..f005273 100644
--- a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPServerFilter.java
+++ b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPServerFilter.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2010 Sun Microsystems, Inc.
- *      Portions copyright 2012-2013 ForgeRock AS.
+ *      Portions copyright 2012-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.grizzly;
@@ -207,7 +207,7 @@
         }
 
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
+        public void handleError(final ErrorResultException error) {
             handleResult(error.getResult());
         }
 
@@ -224,7 +224,7 @@
         }
 
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
+        public void handleError(final ErrorResultException error) {
             final Result result = error.getResult();
             if (result instanceof BindResult) {
                 handleResult((BindResult) result);
@@ -458,7 +458,7 @@
         }
 
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
+        public void handleError(final ErrorResultException error) {
             final Result result = error.getResult();
             if (result instanceof CompareResult) {
                 handleResult((CompareResult) result);
@@ -483,7 +483,7 @@
         }
 
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
+        public void handleError(final ErrorResultException error) {
             handleResult(error.getResult());
         }
 
@@ -500,7 +500,7 @@
         }
 
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
+        public void handleError(final ErrorResultException error) {
             final Result result = error.getResult();
             if (result instanceof ExtendedResult) {
                 handleResult((ExtendedResult) result);
@@ -540,7 +540,7 @@
         }
 
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
+        public void handleError(final ErrorResultException error) {
             handleResult(error.getResult());
         }
 
@@ -557,7 +557,7 @@
         }
 
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
+        public void handleError(final ErrorResultException error) {
             handleResult(error.getResult());
         }
 
@@ -586,7 +586,7 @@
         }
 
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
+        public void handleError(final ErrorResultException error) {
             handleResult(error.getResult());
         }
 
@@ -779,7 +779,7 @@
             if (clientContext != null) {
                 final ServerConnection<Integer> conn = clientContext.getServerConnection();
                 final SearchHandler handler = new SearchHandler(clientContext, messageID);
-                conn.handleSearch(messageID, request, handler, handler);
+                conn.handleSearch(messageID, request, handler, handler, handler);
             }
         }
 
diff --git a/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/ConnectionFactoryTestCase.java b/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/ConnectionFactoryTestCase.java
index 5763cca..9e878f6 100644
--- a/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/ConnectionFactoryTestCase.java
+++ b/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/ConnectionFactoryTestCase.java
@@ -22,20 +22,11 @@
  *
  *
  *      Copyright 2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS
+ *      Portions copyright 2011-2014 ForgeRock AS
  */
 
 package org.forgerock.opendj.grizzly;
 
-import static org.fest.assertions.Assertions.assertThat;
-import static org.forgerock.opendj.ldap.Connections.*;
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-import static org.forgerock.opendj.ldap.TestCaseUtils.*;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.*;
-import static org.testng.Assert.*;
-
 import java.net.InetSocketAddress;
 import java.util.Arrays;
 import java.util.concurrent.Callable;
@@ -83,6 +74,10 @@
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.schema.Schema;
 import org.forgerock.opendj.ldap.schema.SchemaBuilder;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.PromiseImpl;
+import org.forgerock.util.promise.SuccessHandler;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 import org.testng.annotations.AfterClass;
@@ -90,41 +85,24 @@
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
-import com.forgerock.opendj.util.CompletedFutureResult;
+import static org.fest.assertions.Assertions.*;
+import static org.forgerock.opendj.ldap.Connections.*;
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
+import static org.forgerock.opendj.ldap.TestCaseUtils.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+import static org.testng.Assert.*;
 
 /**
  * Tests the {@code ConnectionFactory} classes.
  */
 @SuppressWarnings("javadoc")
 public class ConnectionFactoryTestCase extends SdkTestCase {
-    // Test timeout in ms for tests which need to wait for network events.
+    /** Test timeout in ms for tests which need to wait for network events. */
     private static final long TEST_TIMEOUT = 30L;
     private static final long TEST_TIMEOUT_MS = TEST_TIMEOUT * 1000L;
 
-    class MyResultHandler implements ResultHandler<Connection> {
-        // latch.
-        private final CountDownLatch latch;
-        // invalid flag.
-        private volatile ErrorResultException error;
-
-        MyResultHandler(final CountDownLatch latch) {
-            this.latch = latch;
-        }
-
-        @Override
-        public void handleErrorResult(final ErrorResultException error) {
-            // came here.
-            this.error = error;
-            latch.countDown();
-        }
-
-        @Override
-        public void handleResult(final Connection con) {
-            con.close();
-            latch.countDown();
-        }
-    }
-
     /**
      * Ensures that the LDAP Server is running.
      *
@@ -164,7 +142,7 @@
         // HeartBeatConnectionFactory
         // Use custom search request.
         SearchRequest request =
-                Requests.newSearchRequest("uid=user.0,ou=people,o=test", SearchScope.BASE_OBJECT,
+            Requests.newSearchRequest("uid=user.0,ou=people,o=test", SearchScope.BASE_OBJECT,
                         "objectclass=*", "cn");
 
         InetSocketAddress serverAddress = getServerSocketAddress();
@@ -284,11 +262,11 @@
      */
     @Test(dataProvider = "connectionFactories", timeOut = TEST_TIMEOUT_MS)
     public void testBlockingFutureNoHandler(ConnectionFactory factory) throws Exception {
-        final FutureResult<Connection> future = factory.getConnectionAsync(null);
-        final Connection con = future.get();
+        final Promise<? extends Connection, ErrorResultException> promise = factory.getConnectionAsync();
+        final Connection con = promise.get();
         // quickly check if it is a valid connection.
         // Don't use a result handler.
-        assertNotNull(con.readEntryAsync(DN.rootDN(), null, null).get());
+        assertNotNull(con.readEntryAsync(DN.rootDN(), null).getOrThrow());
         con.close();
     }
 
@@ -300,16 +278,26 @@
     @Test(dataProvider = "connectionFactories", timeOut = TEST_TIMEOUT_MS)
     public void testNonBlockingFutureWithHandler(ConnectionFactory factory) throws Exception {
         // Use the handler to get the result asynchronously.
-        final CountDownLatch latch = new CountDownLatch(1);
-        final MyResultHandler handler = new MyResultHandler(latch);
-        factory.getConnectionAsync(handler);
+        final PromiseImpl<Connection, ErrorResultException> promise = PromiseImpl.create();
+
+        factory.getConnectionAsync().onSuccess(new SuccessHandler<Connection>() {
+            @Override
+            public void handleResult(Connection con) {
+                con.close();
+                promise.handleResult(con);
+            }
+        }).onFailure(new FailureHandler<ErrorResultException>() {
+
+            @Override
+            public void handleError(ErrorResultException error) {
+                promise.handleError(error);
+            }
+
+        });
 
         // Since we don't have anything to do, we would rather
-        // be notified by the latch when the other thread calls our handler.
-        latch.await(); // should do a timed wait rather?
-        if (handler.error != null) {
-            throw handler.error;
-        }
+        // be notified by the promise when the other thread calls our handler.
+        promise.getOrThrow(); // should do a timed wait rather?
     }
 
     /**
@@ -379,7 +367,6 @@
      * @throws Exception
      *             If an unexpected exception occurred.
      */
-    @SuppressWarnings("unchecked")
     @Test
     public void testConnectionPoolClose() throws Exception {
         // We'll use a pool of 4 connections.
@@ -392,39 +379,30 @@
 
         // Mock underlying connection factory which always succeeds.
         final ConnectionFactory mockFactory = mock(ConnectionFactory.class);
-        when(mockFactory.getConnectionAsync(any(ResultHandler.class))).thenAnswer(
-                new Answer<FutureResult<Connection>>() {
+        when(mockFactory.getConnectionAsync()).thenAnswer(new Answer<FutureResult<Connection>>() {
 
+            @Override
+            public FutureResult<Connection> answer(InvocationOnMock invocation) throws Throwable {
+                // Update state.
+                final int connectionID = realConnectionCount.getAndIncrement();
+                realConnectionIsClosed[connectionID] = false;
+
+                // Mock connection decrements counter on close.
+                Connection mockConnection = mock(Connection.class);
+                doAnswer(new Answer<Void>() {
                     @Override
-                    public FutureResult<Connection> answer(InvocationOnMock invocation)
-                            throws Throwable {
-                        // Update state.
-                        final int connectionID = realConnectionCount.getAndIncrement();
-                        realConnectionIsClosed[connectionID] = false;
-
-                        // Mock connection decrements counter on close.
-                        Connection mockConnection = mock(Connection.class);
-                        doAnswer(new Answer<Void>() {
-                            @Override
-                            public Void answer(InvocationOnMock invocation) throws Throwable {
-                                realConnectionCount.decrementAndGet();
-                                realConnectionIsClosed[connectionID] = true;
-                                return null;
-                            }
-                        }).when(mockConnection).close();
-                        when(mockConnection.isValid()).thenReturn(true);
-                        when(mockConnection.toString()).thenReturn(
-                                "Mock connection " + connectionID);
-
-                        // Execute handler and return future.
-                        ResultHandler<? super Connection> handler =
-                                (ResultHandler<? super Connection>) invocation.getArguments()[0];
-                        if (handler != null) {
-                            handler.handleResult(mockConnection);
-                        }
-                        return new CompletedFutureResult<Connection>(mockConnection);
+                    public Void answer(InvocationOnMock invocation) throws Throwable {
+                        realConnectionCount.decrementAndGet();
+                        realConnectionIsClosed[connectionID] = true;
+                        return null;
                     }
-                });
+                }).when(mockConnection).close();
+                when(mockConnection.isValid()).thenReturn(true);
+                when(mockConnection.toString()).thenReturn("Mock connection " + connectionID);
+
+                return newSuccessfulFutureResult(mockConnection);
+            }
+        });
 
         ConnectionPool pool = Connections.newFixedConnectionPool(mockFactory, size);
         Connection[] pooledConnections = new Connection[size];
@@ -483,10 +461,10 @@
     }
 
     private static final class CloseNotify {
-        private boolean closeOnAccept;
-        private boolean doBindFirst;
-        private boolean useEventListener;
-        private boolean sendDisconnectNotification;
+        private final boolean closeOnAccept;
+        private final boolean doBindFirst;
+        private final boolean useEventListener;
+        private final boolean sendDisconnectNotification;
 
         private CloseNotify(boolean closeOnAccept, boolean doBindFirst, boolean useEventListener,
                 boolean sendDisconnectNotification) {
diff --git a/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactoryTestCase.java b/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactoryTestCase.java
index 980e64b..d90ed9d 100644
--- a/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactoryTestCase.java
+++ b/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactoryTestCase.java
@@ -25,20 +25,10 @@
  */
 package org.forgerock.opendj.grizzly;
 
-import static org.fest.assertions.Assertions.assertThat;
-import static org.fest.assertions.Fail.fail;
-import static org.forgerock.opendj.ldap.TestCaseUtils.findFreeSocketAddress;
-import static org.forgerock.opendj.ldap.requests.Requests.newSimpleBindRequest;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.same;
-import static org.mockito.Mockito.*;
-
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.forgerock.opendj.ldap.Connection;
@@ -69,12 +59,23 @@
 import org.forgerock.opendj.ldap.requests.UnbindRequest;
 import org.forgerock.opendj.ldap.responses.BindResult;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.NeverThrowsException;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.PromiseImpl;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 import org.mockito.stubbing.Stubber;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.Test;
 
+import static org.fest.assertions.Assertions.*;
+import static org.fest.assertions.Fail.*;
+import static org.forgerock.opendj.ldap.TestCaseUtils.*;
+import static org.forgerock.opendj.ldap.requests.Requests.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
 /**
  * Tests the {@link LDAPConnectionFactory} class.
  */
@@ -121,21 +122,22 @@
     @Test(description = "OPENDJ-1197")
     public void testClientSideConnectTimeout() throws Exception {
         // Use an non-local unreachable network address.
-        final ConnectionFactory factory =
-                new LDAPConnectionFactory("10.20.30.40", 1389, new LDAPOptions().setConnectTimeout(
-                        1, TimeUnit.MILLISECONDS));
+        final ConnectionFactory factory = new LDAPConnectionFactory("10.20.30.40", 1389,
+                new LDAPOptions().setConnectTimeout(1, TimeUnit.MILLISECONDS));
         try {
             for (int i = 0; i < ITERATIONS; i++) {
-                final ResultHandler<Connection> handler = mock(ResultHandler.class);
-                final FutureResult<Connection> future = factory.getConnectionAsync(handler);
+                final PromiseImpl<ErrorResultException, NeverThrowsException> promise = PromiseImpl.create();
+                final Promise<? extends Connection, ErrorResultException> future = factory.getConnectionAsync();
+                future.onFailure(getFailureHandler(promise));
+
+                ConnectionException e = (ConnectionException) promise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
+                assertThat(e.getResult().getResultCode()).isEqualTo(ResultCode.CLIENT_SIDE_CONNECT_ERROR);
                 // Wait for the connect to timeout.
                 try {
-                    future.get(TEST_TIMEOUT, TimeUnit.SECONDS);
+                    future.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
                     fail("The connect request succeeded unexpectedly");
-                } catch (ConnectionException e) {
-                    assertThat(e.getResult().getResultCode()).isEqualTo(
-                            ResultCode.CLIENT_SIDE_CONNECT_ERROR);
-                    verify(handler).handleErrorResult(same(e));
+                } catch (ConnectionException ce) {
+                    assertThat(ce.getResult().getResultCode()).isEqualTo(ResultCode.CLIENT_SIDE_CONNECT_ERROR);
                 }
             }
         } finally {
@@ -161,32 +163,32 @@
             waitForConnect();
             final MockConnectionEventListener listener = new MockConnectionEventListener();
             connection.addConnectionEventListener(listener);
-
-            final ResultHandler<BindResult> handler = mock(ResultHandler.class);
-            final FutureResult<BindResult> future =
-                    connection.bindAsync(newSimpleBindRequest(), null, handler);
+            final PromiseImpl<ErrorResultException, NeverThrowsException> promise = PromiseImpl.create();
+            final FutureResult<BindResult> future = connection.bindAsync(newSimpleBindRequest());
+            future.onFailure(getFailureHandler(promise));
             waitForBind();
 
+            TimeoutResultException e = (TimeoutResultException) promise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
+            verifyResultCodeIsClientSideTimeout(e);
+
             // Wait for the request to timeout.
             try {
-                future.get(TEST_TIMEOUT, TimeUnit.SECONDS);
+                future.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
                 fail("The bind request succeeded unexpectedly");
-            } catch (TimeoutResultException e) {
-                verifyResultCodeIsClientSideTimeout(e);
-                verify(handler).handleErrorResult(same(e));
-
-                /*
-                 * The connection should no longer be valid, the event listener
-                 * should have been notified, but no abandon should have been
-                 * sent.
-                 */
-                listener.awaitError(TEST_TIMEOUT, TimeUnit.SECONDS);
-                assertThat(connection.isValid()).isFalse();
-                verifyResultCodeIsClientSideTimeout(listener.getError());
-                connection.close();
-                waitForClose();
-                verifyNoAbandonSent();
+            } catch (TimeoutResultException te) {
+                verifyResultCodeIsClientSideTimeout(te);
             }
+
+            /*
+             * The connection should no longer be valid, the event listener
+             * should have been notified, but no abandon should have been sent.
+             */
+            listener.awaitError(TEST_TIMEOUT, TimeUnit.SECONDS);
+            assertThat(connection.isValid()).isFalse();
+            verifyResultCodeIsClientSideTimeout(listener.getError());
+            connection.close();
+            waitForClose();
+            verifyNoAbandonSent();
         } finally {
             connection.close();
         }
@@ -211,39 +213,41 @@
                 connection.addConnectionEventListener(listener);
 
                 // Now bind with timeout.
-                final ResultHandler<BindResult> handler = mock(ResultHandler.class);
-                final FutureResult<BindResult> future =
-                        connection.bindAsync(newSimpleBindRequest(), null, handler);
+                final PromiseImpl<ErrorResultException, NeverThrowsException> promise = PromiseImpl.create();
+                final FutureResult<BindResult> future = connection.bindAsync(newSimpleBindRequest());
+                future.onFailure(getFailureHandler(promise));
                 waitForBind();
 
-                // Wait for the request to timeout.
-                try {
-                    future.get(5, TimeUnit.SECONDS);
-                    fail("The bind request succeeded unexpectedly");
-                } catch (TimeoutException e) {
-                    fail("The bind request future get timed out");
-                } catch (TimeoutResultException e) {
-                    verifyResultCodeIsClientSideTimeout(e);
-                    verify(handler).handleErrorResult(same(e));
+                // Wait for the request to timeout and check the handler was invoked.
+                TimeoutResultException e = (TimeoutResultException) promise.getOrThrow(5, TimeUnit.SECONDS);
+                verifyResultCodeIsClientSideTimeout(e);
 
-                    /*
-                     * The connection should no longer be valid, the event
-                     * listener should have been notified, but no abandon should
-                     * have been sent.
-                     */
-                    listener.awaitError(TEST_TIMEOUT, TimeUnit.SECONDS);
-                    assertThat(connection.isValid()).isFalse();
-                    verifyResultCodeIsClientSideTimeout(listener.getError());
-                    connection.close();
-                    waitForClose();
-                    verifyNoAbandonSent();
+                // Now check the future was completed as expected.
+                try {
+                    future.getOrThrow(5, TimeUnit.SECONDS);
+                    fail("The bind request succeeded unexpectedly");
+                } catch (TimeoutResultException te) {
+                    verifyResultCodeIsClientSideTimeout(te);
                 }
+
+                /*
+                 * The connection should no longer be valid, the event listener
+                 * should have been notified, but no abandon should have been
+                 * sent.
+                 */
+                listener.awaitError(TEST_TIMEOUT, TimeUnit.SECONDS);
+                assertThat(connection.isValid()).isFalse();
+                verifyResultCodeIsClientSideTimeout(listener.getError());
+                connection.close();
+                waitForClose();
+                verifyNoAbandonSent();
             } finally {
                 connection.close();
             }
         }
     }
 
+
     /**
      * Unit test for OPENDJ-1247: a locally timed out request which is not a
      * bind or startTLS should result in a client side timeout error, but the
@@ -262,30 +266,29 @@
                 waitForConnect();
                 final ConnectionEventListener listener = mock(ConnectionEventListener.class);
                 connection.addConnectionEventListener(listener);
-
-                final ResultHandler<SearchResultEntry> handler = mock(ResultHandler.class);
-                final FutureResult<SearchResultEntry> future =
-                        connection.readEntryAsync(DN.valueOf("cn=test"), null, handler);
+                final PromiseImpl<ErrorResultException, NeverThrowsException> promise = PromiseImpl.create();
+                final FutureResult<SearchResultEntry> future = connection.readEntryAsync(DN.valueOf("cn=test"), null);
+                future.onFailure(getFailureHandler(promise));
                 waitForSearch();
 
+                ErrorResultException e = promise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
+                verifyResultCodeIsClientSideTimeout(e);
                 // Wait for the request to timeout.
                 try {
-                    future.get(TEST_TIMEOUT, TimeUnit.SECONDS);
+                    future.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
                     fail("The search request succeeded unexpectedly");
-                } catch (TimeoutResultException e) {
-                    verifyResultCodeIsClientSideTimeout(e);
-                    verify(handler).handleErrorResult(same(e));
-
-                    // The connection should still be valid.
-                    assertThat(connection.isValid()).isTrue();
-                    verifyZeroInteractions(listener);
-
-                    /*
-                     * FIXME: The search should have been abandoned (see comment
-                     * in LDAPConnection for explanation).
-                     */
-                    // waitForAbandon();
+                } catch (TimeoutResultException te) {
+                    verifyResultCodeIsClientSideTimeout(te);
                 }
+
+                // The connection should still be valid.
+                assertThat(connection.isValid()).isTrue();
+                verifyZeroInteractions(listener);
+                /*
+                 * FIXME: The search should have been abandoned (see comment in
+                 * LDAPConnection for explanation).
+                 */
+                // waitForAbandon();
             } finally {
                 connection.close();
             }
@@ -296,8 +299,7 @@
     public void testCreateLDAPConnectionFactory() throws Exception {
         // test no exception is thrown, which means transport provider is correctly loaded
         InetSocketAddress socketAddress = findFreeSocketAddress();
-        LDAPConnectionFactory factory = new LDAPConnectionFactory(socketAddress.getHostName(),
-                socketAddress.getPort());
+        LDAPConnectionFactory factory = new LDAPConnectionFactory(socketAddress.getHostName(), socketAddress.getPort());
         factory.close();
     }
 
@@ -314,9 +316,7 @@
     @Test
     public void testCreateLDAPConnectionFactoryWithCustomClassLoader() throws Exception {
         // test no exception is thrown, which means transport provider is correctly loaded
-        LDAPOptions options =
-                new LDAPOptions().setProviderClassLoader(Thread.currentThread()
-                        .getContextClassLoader());
+        LDAPOptions options = new LDAPOptions().setProviderClassLoader(Thread.currentThread().getContextClassLoader());
         InetSocketAddress socketAddress = findFreeSocketAddress();
         LDAPConnectionFactory factory = new LDAPConnectionFactory(socketAddress.getHostName(),
                 socketAddress.getPort(), options);
@@ -367,6 +367,16 @@
         }
     }
 
+    private FailureHandler<ErrorResultException> getFailureHandler(
+            final PromiseImpl<ErrorResultException, NeverThrowsException> promise) {
+        return new FailureHandler<ErrorResultException>() {
+            @Override
+            public void handleError(ErrorResultException error) {
+                promise.handleResult(error);
+            }
+        };
+    }
+
     private Stubber notifyEvent(final Semaphore latch) {
         return doAnswer(new Answer<Void>() {
             @Override
@@ -394,9 +404,8 @@
     }
 
     private void registerSearchEvent() {
-        notifyEvent(searchLatch).when(serverConnection).handleSearch(any(Integer.class),
-                any(SearchRequest.class), any(IntermediateResponseHandler.class),
-                any(SearchResultHandler.class));
+        notifyEvent(searchLatch).when(serverConnection).handleSearch(any(Integer.class), any(SearchRequest.class),
+            any(IntermediateResponseHandler.class), any(SearchResultHandler.class), any(ResultHandler.class));
     }
 
     private void resetState() {
diff --git a/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionTestCase.java b/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionTestCase.java
index 2ffb2ba..9050ca0 100644
--- a/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionTestCase.java
+++ b/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionTestCase.java
@@ -49,6 +49,7 @@
 import org.forgerock.opendj.ldap.controls.PersistentSearchRequestControl;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
+import org.forgerock.util.promise.FailureHandler;
 import org.mockito.ArgumentCaptor;
 import org.testng.annotations.Test;
 
@@ -100,17 +101,19 @@
             if (isPersistentSearch) {
                 request.addControl(PersistentSearchRequestControl.newControl(true, true, true));
             }
-            SearchResultHandler handler = mock(SearchResultHandler.class);
-            connection.searchAsync(request, null, handler);
+            SearchResultHandler searchHandler = mock(SearchResultHandler.class);
+            @SuppressWarnings("unchecked")
+            FailureHandler<ErrorResultException> failureHandler = mock(FailureHandler.class);
+            connection.searchAsync(request, searchHandler).onFailure(failureHandler);
 
             // Pass in a time which is guaranteed to trigger expiration.
             connection.handleTimeout(System.currentTimeMillis() + 1000000);
             if (isPersistentSearch) {
-                verifyZeroInteractions(handler);
+                verifyZeroInteractions(searchHandler);
             } else {
                 ArgumentCaptor<ErrorResultException> arg =
                         ArgumentCaptor.forClass(ErrorResultException.class);
-                verify(handler).handleErrorResult(arg.capture());
+                verify(failureHandler).handleError(arg.capture());
                 assertThat(arg.getValue()).isInstanceOf(TimeoutResultException.class);
                 assertThat(arg.getValue().getResult().getResultCode()).isEqualTo(
                         ResultCode.CLIENT_SIDE_TIMEOUT);
diff --git a/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java b/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java
index 984af22..ee10161 100644
--- a/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java
+++ b/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java
@@ -22,15 +22,10 @@
  *
  *
  *      Copyright 2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2014 ForgeRock AS.
  */
 package org.forgerock.opendj.grizzly;
 
-import static org.fest.assertions.Assertions.assertThat;
-import static org.fest.assertions.Fail.fail;
-import static org.forgerock.opendj.ldap.TestCaseUtils.findFreeSocketAddress;
-import static org.mockito.Mockito.mock;
-
 import java.net.InetSocketAddress;
 import java.util.Arrays;
 import java.util.concurrent.CountDownLatch;
@@ -43,6 +38,7 @@
 import org.forgerock.opendj.ldap.Connections;
 import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.ErrorResultException;
+import org.forgerock.opendj.ldap.FutureResultImpl;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.LDAPClientContext;
 import org.forgerock.opendj.ldap.LDAPConnectionFactory;
@@ -76,7 +72,10 @@
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-import com.forgerock.opendj.util.AsynchronousFutureResult;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+import static org.forgerock.opendj.ldap.TestCaseUtils.findFreeSocketAddress;
+import static org.mockito.Mockito.mock;
 
 /**
  * Tests the LDAPListener class.
@@ -85,11 +84,8 @@
 public class GrizzlyLDAPListenerTestCase extends SdkTestCase {
 
     private static class MockServerConnection implements ServerConnection<Integer> {
-        final AsynchronousFutureResult<Throwable, ResultHandler<Throwable>> connectionError =
-                new AsynchronousFutureResult<Throwable, ResultHandler<Throwable>>(null);
-        final AsynchronousFutureResult<LDAPClientContext, ResultHandler<LDAPClientContext>> context =
-                new AsynchronousFutureResult<LDAPClientContext, ResultHandler<LDAPClientContext>>(
-                        null);
+        final FutureResultImpl<Throwable> connectionError = new FutureResultImpl<Throwable>();
+        final FutureResultImpl<LDAPClientContext> context = new FutureResultImpl<LDAPClientContext>();
         final CountDownLatch isClosed = new CountDownLatch(1);
 
         MockServerConnection() {
@@ -179,7 +175,7 @@
                 final ExtendedRequest<R> request,
                 final IntermediateResponseHandler intermediateResponseHandler,
                 final ResultHandler<R> resultHandler) throws UnsupportedOperationException {
-            resultHandler.handleErrorResult(ErrorResultException.newErrorResult(request
+            resultHandler.handleError(ErrorResultException.newErrorResult(request
                     .getResultDecoder().newExtendedErrorResult(ResultCode.PROTOCOL_ERROR, "",
                             "Extended operation " + request.getOID() + " not supported")));
         }
@@ -209,8 +205,8 @@
          */
         @Override
         public void handleSearch(final Integer requestContext, final SearchRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final SearchResultHandler resultHandler) throws UnsupportedOperationException {
+            final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler,
+            final ResultHandler<Result> resultHandler) throws UnsupportedOperationException {
             resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
         }
 
@@ -458,7 +454,7 @@
                         resultHandler.handleResult(Responses.newBindResult(ResultCode.SUCCESS));
                     } catch (final Exception e) {
                         // Unexpected.
-                        resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
+                        resultHandler.handleError(ErrorResultException.newErrorResult(
                                 ResultCode.OTHER,
                                 "Unexpected exception when connecting to load balancer", e));
                     }
@@ -615,7 +611,7 @@
                     try {
                         // This is expected to fail.
                         lcf.getConnection().close();
-                        resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
+                        resultHandler.handleError(ErrorResultException.newErrorResult(
                                 ResultCode.OTHER,
                                 "Connection to offline server succeeded unexpectedly"));
                     } catch (final ConnectionException ce) {
@@ -627,13 +623,13 @@
                             resultHandler.handleResult(Responses.newBindResult(ResultCode.SUCCESS));
                         } catch (final Exception e) {
                             // Unexpected.
-                            resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
+                            resultHandler.handleError(ErrorResultException.newErrorResult(
                                     ResultCode.OTHER,
                                     "Unexpected exception when connecting to online server", e));
                         }
                     } catch (final Exception e) {
                         // Unexpected.
-                        resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
+                        resultHandler.handleError(ErrorResultException.newErrorResult(
                                 ResultCode.OTHER,
                                 "Unexpected exception when connecting to offline server", e));
                     }
diff --git a/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java b/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java
index 1bae0e9..799b6c8 100644
--- a/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java
+++ b/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java
@@ -862,16 +862,6 @@
     private static class MySearchResultHandler implements SearchResultHandler {
 
         @Override
-        public void handleErrorResult(ErrorResultException error) {
-            // Ignore.
-        }
-
-        @Override
-        public void handleResult(Result result) {
-            // Ignore.
-        }
-
-        @Override
         public boolean handleEntry(SearchResultEntry entry) {
             final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
             try {
diff --git a/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ProxyBackend.java b/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ProxyBackend.java
index b7e2d56..abdac06 100644
--- a/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ProxyBackend.java
+++ b/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ProxyBackend.java
@@ -22,12 +22,12 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS
+ *      Portions copyright 2011-2014 ForgeRock AS
  */
 
 package org.forgerock.opendj.examples;
 
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.ConnectionFactory;
@@ -54,8 +54,12 @@
 import org.forgerock.opendj.ldap.responses.CompareResult;
 import org.forgerock.opendj.ldap.responses.ExtendedResult;
 import org.forgerock.opendj.ldap.responses.Result;
-import org.forgerock.opendj.ldap.responses.SearchResultEntry;
-import org.forgerock.opendj.ldap.responses.SearchResultReference;
+import org.forgerock.util.promise.AsyncFunction;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.SuccessHandler;
+
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+import static org.forgerock.util.Utils.*;
 
 /**
  * A simple proxy back-end which forwards requests to a connection factory using
@@ -75,330 +79,186 @@
  *
  * <pre>
  * final RequestHandlerFactory&lt;LDAPClientContext, RequestContext&gt; proxyFactory =
- *         new RequestHandlerFactory&lt;LDAPClientContext, RequestContext&gt;() {
- *             &#064;Override
- *             public ProxyBackend handleAccept(LDAPClientContext clientContext)
- *                     throws ErrorResultException {
- *                 return new ProxyBackend(factory, bindFactory);
- *             }
- *         };
+ *     new RequestHandlerFactory&lt;LDAPClientContext, RequestContext&gt;() {
+ *         &#064;Override
+ *         public ProxyBackend handleAccept(LDAPClientContext clientContext) throws ErrorResultException {
+ *             return new ProxyBackend(factory, bindFactory);
+ *         }
+ *     };
  * final ServerConnectionFactory&lt;LDAPClientContext, Integer&gt; connectionHandler = Connections
- *         .newServerConnectionFactory(proxyFactory);
+ *     .newServerConnectionFactory(proxyFactory);
  * </pre>
  */
 final class ProxyBackend implements RequestHandler<RequestContext> {
-    private abstract class AbstractRequestCompletionHandler<R extends Result, H extends ResultHandler<R>>
-            implements ResultHandler<R> {
-        final Connection connection;
-        final H resultHandler;
-
-        AbstractRequestCompletionHandler(final Connection connection, final H resultHandler) {
-            this.connection = connection;
-            this.resultHandler = resultHandler;
-        }
-
-        @Override
-        public final void handleErrorResult(final ErrorResultException error) {
-            connection.close();
-            resultHandler.handleErrorResult(error);
-        }
-
-        @Override
-        public final void handleResult(final R result) {
-            connection.close();
-            resultHandler.handleResult(result);
-        }
-
-    }
-
-    private abstract class ConnectionCompletionHandler<R extends Result> implements
-            ResultHandler<Connection> {
-        private final ResultHandler<R> resultHandler;
-
-        ConnectionCompletionHandler(final ResultHandler<R> resultHandler) {
-            this.resultHandler = resultHandler;
-        }
-
-        @Override
-        public final void handleErrorResult(final ErrorResultException error) {
-            resultHandler.handleErrorResult(error);
-        }
-
-        @Override
-        public abstract void handleResult(Connection connection);
-
-    }
-
-    private final class RequestCompletionHandler<R extends Result> extends
-            AbstractRequestCompletionHandler<R, ResultHandler<R>> {
-        RequestCompletionHandler(final Connection connection, final ResultHandler<R> resultHandler) {
-            super(connection, resultHandler);
-        }
-    }
-
-    private final class SearchRequestCompletionHandler extends
-            AbstractRequestCompletionHandler<Result, SearchResultHandler> implements
-            SearchResultHandler {
-
-        SearchRequestCompletionHandler(final Connection connection,
-                final SearchResultHandler resultHandler) {
-            super(connection, resultHandler);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public final boolean handleEntry(final SearchResultEntry entry) {
-            return resultHandler.handleEntry(entry);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public final boolean handleReference(final SearchResultReference reference) {
-            return resultHandler.handleReference(reference);
-        }
-
-    }
 
     private final ConnectionFactory bindFactory;
     private final ConnectionFactory factory;
-    private volatile ProxiedAuthV2RequestControl proxiedAuthControl = null;
+    private volatile ProxiedAuthV2RequestControl proxiedAuthControl;
 
     ProxyBackend(final ConnectionFactory factory, final ConnectionFactory bindFactory) {
         this.factory = factory;
         this.bindFactory = bindFactory;
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public void handleAdd(final RequestContext requestContext, final AddRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<Result> resultHandler) {
+        final IntermediateResponseHandler intermediateResponseHandler, final ResultHandler<Result> resultHandler) {
+        final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>();
         addProxiedAuthControl(request);
-        final ConnectionCompletionHandler<Result> outerHandler =
-                new ConnectionCompletionHandler<Result>(resultHandler) {
 
-                    @Override
-                    public void handleResult(final Connection connection) {
-                        final RequestCompletionHandler<Result> innerHandler =
-                                new RequestCompletionHandler<Result>(connection, resultHandler);
-                        connection.addAsync(request, intermediateResponseHandler, innerHandler);
-                    }
-
-                };
-
-        factory.getConnectionAsync(outerHandler);
+        factory.getConnectionAsync().thenAsync(new AsyncFunction<Connection, Result, ErrorResultException>() {
+            @Override
+            public Promise<Result, ErrorResultException> apply(Connection connection) throws ErrorResultException {
+                connectionHolder.set(connection);
+                return connection.addAsync(request, intermediateResponseHandler);
+            }
+        }).onSuccess(resultHandler).onFailure(resultHandler).thenAlways(close(connectionHolder));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
-    public void handleBind(final RequestContext requestContext, final int version,
-            final BindRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<BindResult> resultHandler) {
+    public void handleBind(final RequestContext requestContext, final int version, final BindRequest request,
+        final IntermediateResponseHandler intermediateResponseHandler, final ResultHandler<BindResult> resultHandler) {
 
         if (request.getAuthenticationType() != BindRequest.AUTHENTICATION_TYPE_SIMPLE) {
             // TODO: SASL authentication not implemented.
-            resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
+            resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR,
                     "non-SIMPLE authentication not supported: " + request.getAuthenticationType()));
         } else {
             // Authenticate using a separate bind connection pool, because
             // we don't want to change the state of the pooled connection.
-            final ConnectionCompletionHandler<BindResult> outerHandler =
-                    new ConnectionCompletionHandler<BindResult>(resultHandler) {
-
-                        @Override
-                        public void handleResult(final Connection connection) {
-                            final ResultHandler<BindResult> innerHandler =
-                                    new ResultHandler<BindResult>() {
-
-                                        @Override
-                                        public final void handleErrorResult(
-                                                final ErrorResultException error) {
-                                            connection.close();
-                                            resultHandler.handleErrorResult(error);
-
-                                        }
-
-                                        @Override
-                                        public final void handleResult(final BindResult result) {
-                                            connection.close();
-                                            proxiedAuthControl =
-                                                    ProxiedAuthV2RequestControl.newControl("dn:"
-                                                            + request.getName());
-                                            resultHandler.handleResult(result);
-                                        }
-                                    };
-                            connection
-                                    .bindAsync(request, intermediateResponseHandler, innerHandler);
-                        }
-
-                    };
-
+            final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>();
             proxiedAuthControl = null;
-            bindFactory.getConnectionAsync(outerHandler);
+            bindFactory.getConnectionAsync()
+                    .thenAsync(new AsyncFunction<Connection, BindResult, ErrorResultException>() {
+                        @Override
+                        public Promise<BindResult, ErrorResultException> apply(Connection connection)
+                                throws ErrorResultException {
+                            connectionHolder.set(connection);
+                            return connection.bindAsync(request, intermediateResponseHandler);
+                        }
+                    }).onSuccess(new SuccessHandler<BindResult>() {
+                        @Override
+                        public final void handleResult(final BindResult result) {
+                            proxiedAuthControl = ProxiedAuthV2RequestControl.newControl("dn:" + request.getName());
+                            resultHandler.handleResult(result);
+                        }
+                    }).onFailure(resultHandler).thenAlways(close(connectionHolder));
         }
+
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public void handleCompare(final RequestContext requestContext, final CompareRequest request,
             final IntermediateResponseHandler intermediateResponseHandler,
             final ResultHandler<CompareResult> resultHandler) {
         addProxiedAuthControl(request);
-        final ConnectionCompletionHandler<CompareResult> outerHandler =
-                new ConnectionCompletionHandler<CompareResult>(resultHandler) {
 
-                    @Override
-                    public void handleResult(final Connection connection) {
-                        final RequestCompletionHandler<CompareResult> innerHandler =
-                                new RequestCompletionHandler<CompareResult>(connection,
-                                        resultHandler);
-                        connection.compareAsync(request, intermediateResponseHandler, innerHandler);
-                    }
-
-                };
-
-        factory.getConnectionAsync(outerHandler);
+        final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>();
+        factory.getConnectionAsync().thenAsync(new AsyncFunction<Connection, CompareResult, ErrorResultException>() {
+            @Override
+            public Promise<CompareResult, ErrorResultException> apply(Connection connection)
+                    throws ErrorResultException {
+                connectionHolder.set(connection);
+                return connection.compareAsync(request, intermediateResponseHandler);
+            }
+        }).onSuccess(resultHandler).onFailure(resultHandler).thenAlways(close(connectionHolder));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public void handleDelete(final RequestContext requestContext, final DeleteRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler, final ResultHandler<Result> resultHandler) {
         addProxiedAuthControl(request);
-        final ConnectionCompletionHandler<Result> outerHandler =
-                new ConnectionCompletionHandler<Result>(resultHandler) {
 
-                    @Override
-                    public void handleResult(final Connection connection) {
-                        final RequestCompletionHandler<Result> innerHandler =
-                                new RequestCompletionHandler<Result>(connection, resultHandler);
-                        connection.deleteAsync(request, intermediateResponseHandler, innerHandler);
-                    }
-
-                };
-
-        factory.getConnectionAsync(outerHandler);
+        final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>();
+        factory.getConnectionAsync().thenAsync(new AsyncFunction<Connection, Result, ErrorResultException>() {
+            @Override
+            public Promise<Result, ErrorResultException> apply(Connection connection) throws ErrorResultException {
+                connectionHolder.set(connection);
+                return connection.deleteAsync(request, intermediateResponseHandler);
+            }
+        }).onSuccess(resultHandler).onFailure(resultHandler).thenAlways(close(connectionHolder));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
-    public <R extends ExtendedResult> void handleExtendedRequest(
-            final RequestContext requestContext, final ExtendedRequest<R> request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<R> resultHandler) {
-        if (request.getOID().equals(CancelExtendedRequest.OID)) {
+    public <R extends ExtendedResult> void handleExtendedRequest(final RequestContext requestContext,
+        final ExtendedRequest<R> request, final IntermediateResponseHandler intermediateResponseHandler,
+        final ResultHandler<R> resultHandler) {
+        if (CancelExtendedRequest.OID.equals(request.getOID())) {
             // TODO: not implemented.
-            resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
-                    "Cancel extended request operation not supported"));
-        } else if (request.getOID().equals(StartTLSExtendedRequest.OID)) {
+            resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR,
+                "Cancel extended request operation not supported"));
+        } else if (StartTLSExtendedRequest.OID.equals(request.getOID())) {
             // TODO: not implemented.
-            resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
-                    "StartTLS extended request operation not supported"));
+            resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR,
+                "StartTLS extended request operation not supported"));
         } else {
             // Forward all other extended operations.
             addProxiedAuthControl(request);
 
-            final ConnectionCompletionHandler<R> outerHandler =
-                    new ConnectionCompletionHandler<R>(resultHandler) {
-
-                        @Override
-                        public void handleResult(final Connection connection) {
-                            final RequestCompletionHandler<R> innerHandler =
-                                    new RequestCompletionHandler<R>(connection, resultHandler);
-                            connection.extendedRequestAsync(request, intermediateResponseHandler,
-                                    innerHandler);
-                        }
-
-                    };
-
-            factory.getConnectionAsync(outerHandler);
+            final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>();
+            factory.getConnectionAsync().thenAsync(new AsyncFunction<Connection, R, ErrorResultException>() {
+                @Override
+                public Promise<R, ErrorResultException> apply(Connection connection) throws ErrorResultException {
+                    connectionHolder.set(connection);
+                    return connection.extendedRequestAsync(request, intermediateResponseHandler);
+                }
+            }).onSuccess(resultHandler).onFailure(resultHandler)
+                .thenAlways(close(connectionHolder));
         }
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public void handleModify(final RequestContext requestContext, final ModifyRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<Result> resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler, final ResultHandler<Result> resultHandler) {
         addProxiedAuthControl(request);
-        final ConnectionCompletionHandler<Result> outerHandler =
-                new ConnectionCompletionHandler<Result>(resultHandler) {
 
-                    @Override
-                    public void handleResult(final Connection connection) {
-                        final RequestCompletionHandler<Result> innerHandler =
-                                new RequestCompletionHandler<Result>(connection, resultHandler);
-                        connection.modifyAsync(request, intermediateResponseHandler, innerHandler);
-                    }
-
-                };
-
-        factory.getConnectionAsync(outerHandler);
+        final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>();
+        factory.getConnectionAsync().thenAsync(new AsyncFunction<Connection, Result, ErrorResultException>() {
+            @Override
+            public Promise<Result, ErrorResultException> apply(Connection connection) throws ErrorResultException {
+                connectionHolder.set(connection);
+                return connection.modifyAsync(request, intermediateResponseHandler);
+            }
+        }).onSuccess(resultHandler).onFailure(resultHandler).thenAlways(close(connectionHolder));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public void handleModifyDN(final RequestContext requestContext, final ModifyDNRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final ResultHandler<Result> resultHandler) {
+        final IntermediateResponseHandler intermediateResponseHandler, final ResultHandler<Result> resultHandler) {
         addProxiedAuthControl(request);
-        final ConnectionCompletionHandler<Result> outerHandler =
-                new ConnectionCompletionHandler<Result>(resultHandler) {
 
-                    @Override
-                    public void handleResult(final Connection connection) {
-                        final RequestCompletionHandler<Result> innerHandler =
-                                new RequestCompletionHandler<Result>(connection, resultHandler);
-                        connection
-                                .modifyDNAsync(request, intermediateResponseHandler, innerHandler);
-                    }
-
-                };
-
-        factory.getConnectionAsync(outerHandler);
+        final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>();
+        factory.getConnectionAsync().thenAsync(new AsyncFunction<Connection, Result, ErrorResultException>() {
+            @Override
+            public Promise<Result, ErrorResultException> apply(Connection connection) throws ErrorResultException {
+                connectionHolder.set(connection);
+                return connection.modifyDNAsync(request, intermediateResponseHandler);
+            }
+        }).onSuccess(resultHandler).onFailure(resultHandler).thenAlways(close(connectionHolder));
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public void handleSearch(final RequestContext requestContext, final SearchRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final SearchResultHandler resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler,
+            final ResultHandler<Result> resultHandler) {
         addProxiedAuthControl(request);
-        final ConnectionCompletionHandler<Result> outerHandler =
-                new ConnectionCompletionHandler<Result>(resultHandler) {
 
-                    @Override
-                    public void handleResult(final Connection connection) {
-                        final SearchRequestCompletionHandler innerHandler =
-                                new SearchRequestCompletionHandler(connection, resultHandler);
-                        connection.searchAsync(request, intermediateResponseHandler, innerHandler);
-                    }
-
-                };
-
-        factory.getConnectionAsync(outerHandler);
+        final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>();
+        factory.getConnectionAsync().thenAsync(new AsyncFunction<Connection, Result, ErrorResultException>() {
+            @Override
+            public Promise<Result, ErrorResultException> apply(Connection connection) throws ErrorResultException {
+                connectionHolder.set(connection);
+                return connection.searchAsync(request, intermediateResponseHandler, entryHandler);
+            }
+        }).onSuccess(resultHandler).onFailure(resultHandler).thenAlways(close(connectionHolder));
     }
 
     private void addProxiedAuthControl(final Request request) {
@@ -407,4 +267,13 @@
             request.addControl(control);
         }
     }
+
+    private Runnable close(final AtomicReference<Connection> c) {
+        return new Runnable() {
+            @Override
+            public void run() {
+                closeSilently(c.get());
+            }
+        };
+    }
 }
diff --git a/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/RewriterProxy.java b/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/RewriterProxy.java
index dd3a01e..374294b 100644
--- a/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/RewriterProxy.java
+++ b/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/RewriterProxy.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS
+ *      Portions copyright 2011-2014 ForgeRock AS
  */
 
 package org.forgerock.opendj.examples;
@@ -109,11 +109,11 @@
 public final class RewriterProxy {
     private static final class Rewriter implements RequestHandler<RequestContext> {
 
-        // This example hard codes the attribute...
+        /** This example hard codes the attribute... */
         private static final String CLIENT_ATTRIBUTE = "fullname";
         private static final String SERVER_ATTRIBUTE = "cn";
 
-        // ...and DN rewriting configuration.
+        /** ...and DN rewriting configuration. */
         private static final String CLIENT_SUFFIX = "o=example";
         private static final String SERVER_SUFFIX = "dc=example,dc=com";
 
@@ -122,7 +122,7 @@
         private final AttributeDescription serverAttributeDescription = AttributeDescription
                 .valueOf(SERVER_ATTRIBUTE);
 
-        // Next request handler in the chain.
+        /** Next request handler in the chain. */
         private final RequestHandler<RequestContext> nextHandler;
 
         private Rewriter(final RequestHandler<RequestContext> nextHandler) {
@@ -191,32 +191,20 @@
 
         @Override
         public void handleSearch(final RequestContext requestContext, final SearchRequest request,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final SearchResultHandler resultHandler) {
+            final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler,
+            final ResultHandler<Result> resultHandler) {
             nextHandler.handleSearch(requestContext, rewrite(request), intermediateResponseHandler,
-                    new SearchResultHandler() {
+                new SearchResultHandler() {
+                    @Override
+                    public boolean handleReference(SearchResultReference reference) {
+                        return entryHandler.handleReference(reference);
+                    }
 
-                        @Override
-                        public boolean handleEntry(final SearchResultEntry entry) {
-                            return resultHandler.handleEntry(rewrite(entry));
-                        }
-
-                        @Override
-                        public void handleErrorResult(final ErrorResultException error) {
-                            resultHandler.handleErrorResult(error);
-                        }
-
-                        @Override
-                        public boolean handleReference(final SearchResultReference reference) {
-                            return resultHandler.handleReference(reference);
-                        }
-
-                        @Override
-                        public void handleResult(final Result result) {
-                            resultHandler.handleResult(result);
-                        }
-
-                    });
+                    @Override
+                    public boolean handleEntry(SearchResultEntry entry) {
+                        return entryHandler.handleEntry(rewrite(entry));
+                    }
+                }, resultHandler);
         }
 
         private AddRequest rewrite(final AddRequest request) {
diff --git a/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java b/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java
index 8ef61d5..6fdb62f 100644
--- a/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java
+++ b/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2011-2012 ForgeRock AS
+ *      Portions copyright 2011-2014 ForgeRock AS
  */
 
 package org.forgerock.opendj.examples;
@@ -34,21 +34,23 @@
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.ErrorResultException;
 import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.FutureResultWrapper;
 import org.forgerock.opendj.ldap.LDAPConnectionFactory;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.SearchResultHandler;
 import org.forgerock.opendj.ldap.SearchScope;
-import org.forgerock.opendj.ldap.requests.BindRequest;
 import org.forgerock.opendj.ldap.requests.CancelExtendedRequest;
 import org.forgerock.opendj.ldap.requests.Requests;
-import org.forgerock.opendj.ldap.requests.SearchRequest;
 import org.forgerock.opendj.ldap.responses.BindResult;
 import org.forgerock.opendj.ldap.responses.ExtendedResult;
 import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
 import org.forgerock.opendj.ldif.LDIFEntryWriter;
+import org.forgerock.util.promise.AsyncFunction;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.SuccessHandler;
 
 /**
  * An example client application which searches a Directory Server using the
@@ -60,79 +62,32 @@
  * </pre>
  */
 public final class SearchAsync {
-    private static final class BindResultHandlerImpl implements ResultHandler<BindResult> {
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void handleErrorResult(final ErrorResultException error) {
-            System.err.println(error.getMessage());
-            resultCode = error.getResult().getResultCode().intValue();
-            COMPLETION_LATCH.countDown();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void handleResult(final BindResult result) {
-            // Bind succeeded: initiate search.
-            final SearchRequest request =
-                    Requests.newSearchRequest(baseDN, scope, filter, attributes);
-            final FutureResult<Result> futureResult =
-                    connection.searchAsync(request, null, new SearchResultHandlerImpl());
-            requestID = futureResult.getRequestID();
-        }
-
-    }
-
-    private static final class ConnectResultHandlerImpl implements ResultHandler<Connection> {
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void handleErrorResult(final ErrorResultException error) {
-            System.err.println(error.getMessage());
-            resultCode = error.getResult().getResultCode().intValue();
-            COMPLETION_LATCH.countDown();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void handleResult(final Connection connection) {
-            // Connect succeeded: save connection and initiate bind.
-            SearchAsync.connection = connection;
-
-            final BindRequest request =
-                    Requests.newSimpleBindRequest(userName, password.toCharArray());
-            connection.bindAsync(request, null, new BindResultHandlerImpl());
-        }
-
-    }
-
     // --- JCite search result handler ---
     private static final class SearchResultHandlerImpl implements SearchResultHandler {
-
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
         @Override
         public synchronized boolean handleEntry(final SearchResultEntry entry) {
             try {
                 if (entryCount < 10) {
-                    WRITER.writeComment("Search result entry: "
-                            + entry.getName().toString());
+                    WRITER.writeComment("Search result entry: " + entry.getName().toString());
                     WRITER.writeEntry(entry);
                     ++entryCount;
                 } else { // Cancel the search.
-                    CancelExtendedRequest request =
-                            Requests.newCancelExtendedRequest(requestID);
-                    connection.extendedRequestAsync(request, null,
-                            new CancelResultHandlerImpl());
+                    CancelExtendedRequest request = Requests.newCancelExtendedRequest(requestID);
+                    connection.extendedRequestAsync(request).onSuccess(new SuccessHandler<ExtendedResult>() {
+                        @Override
+                        public void handleResult(ExtendedResult result) {
+                            System.err.println("Cancel request succeeded");
+                            CANCEL_LATCH.countDown();
+                        }
+                    }).onFailure(new FailureHandler<ErrorResultException>() {
+                        @Override
+                        public void handleError(ErrorResultException error) {
+                            System.err.println("Cancel request failed with result code: "
+                                + error.getResult().getResultCode().intValue());
+                            CANCEL_LATCH.countDown();
+                        }
+                    });
                     return false;
                 }
             } catch (final IOException e) {
@@ -144,25 +99,11 @@
             return true;
         }
 
-        /**
-         * {@inheritDoc}
-         */
+        /** {@inheritDoc} */
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
-            System.err.println(error.getMessage());
-            resultCode = error.getResult().getResultCode().intValue();
-            COMPLETION_LATCH.countDown();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public synchronized boolean handleReference(
-                final SearchResultReference reference) {
+        public synchronized boolean handleReference(final SearchResultReference reference) {
             try {
-                WRITER.writeComment("Search result reference: "
-                        + reference.getURIs().toString());
+                WRITER.writeComment("Search result reference: " + reference.getURIs().toString());
             } catch (final IOException e) {
                 System.err.println(e.getMessage());
                 resultCode = ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
@@ -172,18 +113,10 @@
             return true;
         }
 
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void handleResult(final Result result) {
-            resultCode = result.getResultCode().intValue();
-            COMPLETION_LATCH.countDown();
-        }
-
     }
     // --- JCite search result handler ---
 
+
     // --- JCite decl1 ---
     private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1);
     private static final CountDownLatch CANCEL_LATCH = new CountDownLatch(1);
@@ -203,26 +136,6 @@
     static int entryCount = 0;
     // --- JCite decl2 ---
 
-    // --- JCite cancel result handler ---
-    private static final class CancelResultHandlerImpl
-            implements ResultHandler<ExtendedResult> {
-
-        @Override
-        public void handleErrorResult(final ErrorResultException error) {
-            System.err.println("Cancel request failed with result code: "
-                    + error.getResult().getResultCode().intValue());
-            CANCEL_LATCH.countDown();
-        }
-
-        @Override
-        public void handleResult(final ExtendedResult result) {
-            System.err.println("Cancel request succeeded");
-            CANCEL_LATCH.countDown();
-        }
-
-    }
-    // --- JCite cancel result handler ---
-
     /**
      * Main method.
      *
@@ -233,8 +146,7 @@
      */
     public static void main(final String[] args) {
         if (args.length < 7) {
-            System.err.println("Usage: host port username password baseDN scope "
-                    + "filter [attribute ...]");
+            System.err.println("Usage: host port username password baseDN scope " + "filter [attribute ...]");
             System.exit(1);
         }
 
@@ -266,9 +178,38 @@
             return;
         }
 
+
         // Initiate the asynchronous connect, bind, and search.
         final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName, port);
-        factory.getConnectionAsync(new ConnectResultHandlerImpl());
+
+        factory.getConnectionAsync().thenAsync(new AsyncFunction<Connection, BindResult, ErrorResultException>() {
+            @Override
+            public Promise<BindResult, ErrorResultException> apply(Connection connection) throws ErrorResultException {
+                SearchAsync.connection = connection;
+                return connection.bindAsync(Requests.newSimpleBindRequest(userName, password.toCharArray()));
+            }
+        }).thenAsync(new AsyncFunction<BindResult, Result, ErrorResultException>() {
+            @Override
+            public Promise<Result, ErrorResultException> apply(BindResult result) throws ErrorResultException {
+                FutureResult<Result> future = FutureResultWrapper.asFutureResult(connection.searchAsync(
+                        Requests.newSearchRequest(baseDN, scope, filter, attributes), new SearchResultHandlerImpl()));
+                requestID = future.getRequestID();
+                return future;
+            }
+        }).onSuccess(new SuccessHandler<Result>() {
+            @Override
+            public void handleResult(Result result) {
+                resultCode = result.getResultCode().intValue();
+                COMPLETION_LATCH.countDown();
+            }
+        }).onFailure(new FailureHandler<ErrorResultException>() {
+            @Override
+            public void handleError(ErrorResultException error) {
+                System.err.println(error.getMessage());
+                resultCode = error.getResult().getResultCode().intValue();
+                COMPLETION_LATCH.countDown();
+            }
+        });
 
         // Await completion.
         try {
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java
index 9b29b04..1c48090 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java
@@ -26,10 +26,6 @@
 
 package com.forgerock.opendj.ldap.tools;
 
-import static com.forgerock.opendj.cli.ArgumentConstants.*;
-import static com.forgerock.opendj.cli.Utils.filterExitCode;
-import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
-
 import java.io.IOException;
 import java.io.PrintStream;
 import java.util.Arrays;
@@ -42,13 +38,14 @@
 import org.forgerock.opendj.ldap.ConnectionFactory;
 import org.forgerock.opendj.ldap.Entry;
 import org.forgerock.opendj.ldap.ErrorResultException;
-import org.forgerock.opendj.ldap.FutureResult;
 import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.requests.AddRequest;
 import org.forgerock.opendj.ldap.requests.DeleteRequest;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldif.EntryGenerator;
+import org.forgerock.util.promise.Promise;
 
 import com.forgerock.opendj.cli.ArgumentException;
 import com.forgerock.opendj.cli.ArgumentParser;
@@ -60,6 +57,10 @@
 import com.forgerock.opendj.cli.MultiChoiceArgument;
 import com.forgerock.opendj.cli.StringArgument;
 
+import static com.forgerock.opendj.cli.ArgumentConstants.*;
+import static com.forgerock.opendj.cli.Utils.*;
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
+
 /**
  * A load generation tool that can be used to load a Directory Server with Add
  * and Delete requests using one or more LDAP connections.
@@ -68,7 +69,7 @@
 
     private static final class AddPerformanceRunner extends PerformanceRunner {
         private final class AddStatsHandler extends UpdateStatsResultHandler<Result> {
-            private String entryDN;
+            private final String entryDN;
 
             private AddStatsHandler(final long currentTime, String entryDN) {
                 super(currentTime);
@@ -146,17 +147,19 @@
             }
 
             @Override
-            public FutureResult<?> performOperation(Connection connection,
-                    DataSource[] dataSources, long currentTime) {
+            public Promise<?, ErrorResultException> performOperation(Connection connection, DataSource[] dataSources,
+                    long currentTime) {
                 if (needsDelete(currentTime)) {
                     DeleteRequest dr = Requests.newDeleteRequest(getDNEntryToRemove());
-                    return connection.deleteAsync(dr, null, new DeleteStatsHandler(currentTime));
+                    ResultHandler<Result> deleteHandler = new DeleteStatsHandler(currentTime);
+
+                    return connection.deleteAsync(dr).onSuccess(deleteHandler).onFailure(deleteHandler);
                 } else {
                     return performAddOperation(connection, currentTime);
                 }
             }
 
-            private FutureResult<?> performAddOperation(Connection connection, long currentTime) {
+            private Promise<Result, ErrorResultException> performAddOperation(Connection connection, long currentTime) {
                 try {
                     Entry entry;
                     synchronized (generator) {
@@ -164,12 +167,12 @@
                     }
 
                     AddRequest ar = Requests.newAddRequest(entry);
-                    return connection
-                        .addAsync(ar, null, new AddStatsHandler(currentTime, entry.getName().toString()));
+                    ResultHandler<Result> addHandler = new AddStatsHandler(currentTime, entry.getName().toString());
+                    return connection.addAsync(ar).onSuccess(addHandler).onFailure(addHandler);
                 } catch (IOException e) {
                     // faking an error result by notifying the Handler
                     UpdateStatsResultHandler<Result> resHandler = new UpdateStatsResultHandler<Result>(currentTime);
-                    resHandler.handleErrorResult(ErrorResultException.newErrorResult(ResultCode.OTHER, e));
+                    resHandler.handleError(ErrorResultException.newErrorResult(ResultCode.OTHER, e));
                     return null;
                 }
             }
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java
index 130e453..762f679 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java
@@ -26,11 +26,6 @@
  */
 package com.forgerock.opendj.ldap.tools;
 
-import static com.forgerock.opendj.cli.ArgumentConstants.*;
-import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
-import static com.forgerock.opendj.ldap.tools.Utils.setDefaultPerfToolProperties;
-import static com.forgerock.opendj.cli.Utils.filterExitCode;
-
 import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.LinkedList;
@@ -40,24 +35,11 @@
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.forgerock.i18n.LocalizableMessage;
-
-import com.forgerock.opendj.cli.BooleanArgument;
-import com.forgerock.opendj.cli.CommonArguments;
-import com.forgerock.opendj.cli.ConnectionFactoryProvider;
-import com.forgerock.opendj.cli.ConsoleApplication;
-import com.forgerock.opendj.cli.ArgumentParser;
-import com.forgerock.opendj.cli.ArgumentException;
-import com.forgerock.opendj.cli.IntegerArgument;
-import com.forgerock.opendj.cli.MultiChoiceArgument;
-import com.forgerock.opendj.cli.StringArgument;
-
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.ConnectionFactory;
 import org.forgerock.opendj.ldap.DereferenceAliasesPolicy;
 import org.forgerock.opendj.ldap.ErrorResultException;
-import org.forgerock.opendj.ldap.FutureResult;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.SearchScope;
 import org.forgerock.opendj.ldap.requests.BindRequest;
 import org.forgerock.opendj.ldap.requests.CRAMMD5SASLBindRequest;
@@ -70,8 +52,23 @@
 import org.forgerock.opendj.ldap.requests.SimpleBindRequest;
 import org.forgerock.opendj.ldap.responses.BindResult;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
+import org.forgerock.util.promise.AsyncFunction;
+import org.forgerock.util.promise.Promise;
 
-import com.forgerock.opendj.util.RecursiveFutureResult;
+import com.forgerock.opendj.cli.ArgumentException;
+import com.forgerock.opendj.cli.ArgumentParser;
+import com.forgerock.opendj.cli.BooleanArgument;
+import com.forgerock.opendj.cli.CommonArguments;
+import com.forgerock.opendj.cli.ConnectionFactoryProvider;
+import com.forgerock.opendj.cli.ConsoleApplication;
+import com.forgerock.opendj.cli.IntegerArgument;
+import com.forgerock.opendj.cli.MultiChoiceArgument;
+import com.forgerock.opendj.cli.StringArgument;
+
+import static com.forgerock.opendj.cli.ArgumentConstants.*;
+import static com.forgerock.opendj.cli.Utils.*;
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
+import static com.forgerock.opendj.ldap.tools.Utils.*;
 
 /**
  * A load generation tool that can be used to load a Directory Server with Bind
@@ -107,8 +104,8 @@
             }
 
             @Override
-            public void handleErrorResult(final ErrorResultException error) {
-                super.handleErrorResult(error);
+            public void handleError(final ErrorResultException error) {
+                super.handleError(error);
 
                 if (error.getResult().getResultCode() == ResultCode.INVALID_CREDENTIALS) {
                     invalidCredRecentCount.getAndIncrement();
@@ -137,7 +134,7 @@
             }
 
             @Override
-            public FutureResult<?> performOperation(final Connection connection,
+            public Promise<?, ErrorResultException> performOperation(final Connection connection,
                     final DataSource[] dataSources, final long startTime) {
                 if (dataSources != null) {
                     data = DataSource.generateData(dataSources, data);
@@ -147,14 +144,15 @@
                         data = newData;
                     }
                 }
+
+                Promise<BindResult, ErrorResultException> returnedPromise;
                 if (filter != null && baseDN != null) {
                     if (sr == null) {
                         if (dataSources == null) {
                             sr = Requests.newSearchRequest(baseDN, scope, filter, attributes);
                         } else {
-                            sr =
-                                    Requests.newSearchRequest(String.format(baseDN, data), scope,
-                                            String.format(filter, data), attributes);
+                            sr = Requests.newSearchRequest(String.format(baseDN, data), scope,
+                                    String.format(filter, data), attributes);
                         }
                         sr.setDereferenceAliasesPolicy(dereferencesAliasesPolicy);
                     } else if (dataSources != null) {
@@ -162,32 +160,30 @@
                         sr.setName(String.format(baseDN, data));
                     }
 
-                    final RecursiveFutureResult<SearchResultEntry, BindResult> future =
-                            new RecursiveFutureResult<SearchResultEntry, BindResult>(
-                                    new BindUpdateStatsResultHandler(startTime)) {
+                    returnedPromise = connection.searchSingleEntryAsync(sr).thenAsync(
+                            new AsyncFunction<SearchResultEntry, BindResult, ErrorResultException>() {
                                 @Override
-                                protected FutureResult<? extends BindResult> chainResult(
-                                        final SearchResultEntry innerResult,
-                                        final ResultHandler<? super BindResult> resultHandler)
+                                public Promise<BindResult, ErrorResultException> apply(SearchResultEntry result)
                                         throws ErrorResultException {
                                     searchWaitRecentTime.getAndAdd(System.nanoTime() - startTime);
                                     if (data == null) {
                                         data = new Object[1];
                                     }
-                                    data[data.length - 1] = innerResult.getName().toString();
-                                    return performBind(connection, data, resultHandler);
+                                    data[data.length - 1] = result.getName().toString();
+
+                                    return performBind(connection, data);
                                 }
-                            };
-                    connection.searchSingleEntryAsync(sr, future);
-                    return future;
+                            });
                 } else {
-                    return performBind(connection, data,
-                            new BindUpdateStatsResultHandler(startTime));
+                    returnedPromise = performBind(connection, data);
                 }
+
+                return returnedPromise.onSuccess(new UpdateStatsResultHandler<BindResult>(startTime)).onFailure(
+                        new BindUpdateStatsResultHandler(startTime));
             }
 
-            private FutureResult<BindResult> performBind(final Connection connection,
-                    final Object[] data, final ResultHandler<? super BindResult> handler) {
+            private Promise<BindResult, ErrorResultException> performBind(final Connection connection,
+                final Object[] data) {
                 final boolean useInvalidPassword;
 
                 // Avoid rng if possible.
@@ -307,7 +303,7 @@
                     }
                 }
 
-                return connection.bindAsync(br, null, handler);
+                return connection.bindAsync(br);
             }
         }
 
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java
index 7cdf527..fb754fa 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDAPSearch.java
@@ -26,12 +26,6 @@
  */
 package com.forgerock.opendj.ldap.tools;
 
-import static com.forgerock.opendj.cli.ArgumentConstants.*;
-import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
-import static com.forgerock.opendj.cli.Utils.filterExitCode;
-import static com.forgerock.opendj.cli.Utils.secondsToTimeString;
-import static org.forgerock.util.Utils.closeSilently;
-
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
@@ -89,6 +83,12 @@
 import com.forgerock.opendj.ldap.controls.AccountUsabilityResponseControl;
 import com.forgerock.opendj.util.StaticUtils;
 
+import static org.forgerock.util.Utils.*;
+
+import static com.forgerock.opendj.cli.ArgumentConstants.*;
+import static com.forgerock.opendj.cli.Utils.*;
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
+
 /**
  * A tool that can be used to issue Search requests to the Directory Server.
  */
@@ -97,6 +97,7 @@
         private int entryCount;
 
         /** {@inheritDoc} */
+        @Override
         public boolean handleEntry(final SearchResultEntry entry) {
             entryCount++;
 
@@ -179,20 +180,11 @@
         }
 
         /** {@inheritDoc} */
+        @Override
         public boolean handleReference(final SearchResultReference reference) {
             println(LocalizableMessage.raw(reference.toString()));
             return true;
         }
-
-        /** {@inheritDoc} */
-        public void handleErrorResult(ErrorResultException error) {
-            // Ignore.
-        }
-
-        /** {@inheritDoc} */
-        public void handleResult(Result result) {
-            // Ignore.
-        }
     }
 
     /**
@@ -226,6 +218,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isInteractive() {
         return false;
     }
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java
index 1058ad1..0eeaa1f 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java
@@ -26,19 +26,17 @@
  */
 package com.forgerock.opendj.ldap.tools;
 
-import static com.forgerock.opendj.cli.ArgumentConstants.*;
-import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
-import static com.forgerock.opendj.cli.Utils.filterExitCode;
-
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.ConnectionFactory;
-import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.ErrorResultException;
 import org.forgerock.opendj.ldap.ModificationType;
 import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.requests.ModifyRequest;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.responses.Result;
+import org.forgerock.util.promise.Promise;
 
 import com.forgerock.opendj.cli.ArgumentException;
 import com.forgerock.opendj.cli.ArgumentParser;
@@ -48,6 +46,10 @@
 import com.forgerock.opendj.cli.ConsoleApplication;
 import com.forgerock.opendj.cli.StringArgument;
 
+import static com.forgerock.opendj.cli.ArgumentConstants.*;
+import static com.forgerock.opendj.cli.Utils.*;
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
+
 /**
  * A load generation tool that can be used to load a Directory Server with
  * Modify requests using one or more LDAP connections.
@@ -64,14 +66,15 @@
             }
 
             @Override
-            public FutureResult<?> performOperation(final Connection connection,
+            public Promise<?, ErrorResultException> performOperation(final Connection connection,
                     final DataSource[] dataSources, final long startTime) {
                 if (dataSources != null) {
                     data = DataSource.generateData(dataSources, data);
                 }
                 mr = newModifyRequest(data);
-                return connection.modifyAsync(mr, null, new UpdateStatsResultHandler<Result>(
-                        startTime));
+                ResultHandler<Result> modRes = new UpdateStatsResultHandler<Result>(startTime);
+
+                return connection.modifyAsync(mr).onSuccess(modRes).onFailure(modRes);
             }
 
             private ModifyRequest newModifyRequest(final Object[] data) {
@@ -139,6 +142,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isInteractive() {
         return false;
     }
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java
index da50960..0f22a0e 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java
@@ -26,9 +26,6 @@
  */
 package com.forgerock.opendj.ldap.tools;
 
-import static org.forgerock.util.Utils.closeSilently;
-import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
-
 import java.io.IOException;
 import java.lang.management.GarbageCollectorMXBean;
 import java.lang.management.ManagementFactory;
@@ -46,21 +43,25 @@
 import org.forgerock.opendj.ldap.ConnectionEventListener;
 import org.forgerock.opendj.ldap.ConnectionFactory;
 import org.forgerock.opendj.ldap.ErrorResultException;
-import org.forgerock.opendj.ldap.FutureResult;
 import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.responses.ExtendedResult;
 import org.forgerock.opendj.ldap.responses.Result;
+import org.forgerock.util.promise.Promise;
 
 import com.forgerock.opendj.cli.ArgumentException;
 import com.forgerock.opendj.cli.ArgumentParser;
+import com.forgerock.opendj.cli.AuthenticatedConnectionFactory.AuthenticatedConnection;
 import com.forgerock.opendj.cli.BooleanArgument;
 import com.forgerock.opendj.cli.ConsoleApplication;
 import com.forgerock.opendj.cli.IntegerArgument;
 import com.forgerock.opendj.cli.MultiColumnPrinter;
 import com.forgerock.opendj.cli.StringArgument;
-import com.forgerock.opendj.cli.AuthenticatedConnectionFactory.AuthenticatedConnection;
 import com.forgerock.opendj.util.StaticUtils;
 
+import static org.forgerock.util.Utils.*;
+
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
+
 /**
  * Benchmark application framework.
  */
@@ -338,7 +339,7 @@
     }
 
     private class TimerThread extends Thread {
-        private long timeToWait;
+        private final long timeToWait;
 
         public TimerThread(long timeToWait) {
             this.timeToWait = timeToWait;
@@ -370,7 +371,7 @@
         }
 
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
+        public void handleError(final ErrorResultException error) {
             failedRecentCount.getAndIncrement();
             updateStats();
             app.errPrintVerboseMessage(LocalizableMessage.raw(error.getResult().toString()));
@@ -410,22 +411,21 @@
             this.connectionFactory = connectionFactory;
         }
 
-        public abstract FutureResult<?> performOperation(Connection connection,
+        public abstract Promise<?, ErrorResultException> performOperation(Connection connection,
                 DataSource[] dataSources, long startTime);
 
         @Override
         public void run() {
-            FutureResult<?> future;
+            Promise<?, ErrorResultException> promise;
             Connection connection;
 
-            final double targetTimeInMS =
-                    1000.0 / (targetThroughput / (double) (numThreads * numConnections));
+            final double targetTimeInMS = 1000.0 / (targetThroughput / (double) (numThreads * numConnections));
             double sleepTimeInMS = 0;
             long start;
             while (!stopRequested && !(maxIterations > 0 && count >= maxIterations)) {
                 if (this.connection == null) {
                     try {
-                        connection = connectionFactory.getConnectionAsync(null).get();
+                        connection = connectionFactory.getConnectionAsync().getOrThrow();
                     } catch (final InterruptedException e) {
                         // Ignore and check stop requested
                         continue;
@@ -442,7 +442,7 @@
                     if (!noRebind && connection instanceof AuthenticatedConnection) {
                         final AuthenticatedConnection ac = (AuthenticatedConnection) connection;
                         try {
-                            ac.rebindAsync(null).get();
+                            ac.rebindAsync().getOrThrow();
                         } catch (final InterruptedException e) {
                             // Ignore and check stop requested
                             continue;
@@ -458,13 +458,13 @@
                 }
 
                 start = System.nanoTime();
-                future = performOperation(connection, dataSources.get(), start);
+                promise = performOperation(connection, dataSources.get(), start);
                 operationRecentCount.getAndIncrement();
                 count++;
                 if (!isAsync) {
                     try {
-                        if (future != null) {
-                            future.get();
+                        if (promise != null) {
+                            promise.getOrThrow();
                         }
                     } catch (final InterruptedException e) {
                         // Ignore and check stop requested
@@ -857,7 +857,7 @@
             isWarmingUp = warmUpDuration > 0;
             for (int i = 0; i < numConnections; i++) {
                 if (keepConnectionsOpen.isPresent() || noRebindArgument.isPresent()) {
-                    connection = connectionFactory.getConnectionAsync(null).get();
+                    connection = connectionFactory.getConnectionAsync().getOrThrow();
                     connection.addConnectionEventListener(this);
                     connections.add(connection);
                 }
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java
index e1d4a5e..c1d6803 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java
@@ -26,10 +26,6 @@
  */
 package com.forgerock.opendj.ldap.tools;
 
-import static com.forgerock.opendj.cli.ArgumentConstants.*;
-import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
-import static com.forgerock.opendj.cli.Utils.filterExitCode;
-
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
@@ -39,7 +35,7 @@
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.ConnectionFactory;
 import org.forgerock.opendj.ldap.DereferenceAliasesPolicy;
-import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.ErrorResultException;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchResultHandler;
 import org.forgerock.opendj.ldap.SearchScope;
@@ -48,6 +44,7 @@
 import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
+import org.forgerock.util.promise.Promise;
 
 import com.forgerock.opendj.cli.ArgumentException;
 import com.forgerock.opendj.cli.ArgumentParser;
@@ -58,6 +55,10 @@
 import com.forgerock.opendj.cli.MultiChoiceArgument;
 import com.forgerock.opendj.cli.StringArgument;
 
+import static com.forgerock.opendj.cli.ArgumentConstants.*;
+import static com.forgerock.opendj.cli.Utils.*;
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
+
 /**
  * A load generation tool that can be used to load a Directory Server with
  * Search requests using one or more LDAP connections.
@@ -117,7 +118,7 @@
             }
 
             @Override
-            public FutureResult<?> performOperation(final Connection connection,
+            public Promise<?, ErrorResultException> performOperation(final Connection connection,
                     final DataSource[] dataSources, final long startTime) {
                 if (sr == null) {
                     if (dataSources == null) {
@@ -134,7 +135,10 @@
                     sr.setFilter(String.format(filter, data));
                     sr.setName(String.format(baseDN, data));
                 }
-                return connection.searchAsync(sr, null, new SearchStatsHandler(startTime));
+
+                final SearchStatsHandler handler = new SearchStatsHandler(startTime);
+
+                return connection.searchAsync(sr, handler).onSuccess(handler).onFailure(handler);
             }
         }
 
diff --git a/opendj-rest2ldap-servlet/src/main/java/org/forgerock/opendj/rest2ldap/servlet/Rest2LDAPAuthnFilter.java b/opendj-rest2ldap-servlet/src/main/java/org/forgerock/opendj/rest2ldap/servlet/Rest2LDAPAuthnFilter.java
index 28b2ef2..45e9194 100644
--- a/opendj-rest2ldap-servlet/src/main/java/org/forgerock/opendj/rest2ldap/servlet/Rest2LDAPAuthnFilter.java
+++ b/opendj-rest2ldap-servlet/src/main/java/org/forgerock/opendj/rest2ldap/servlet/Rest2LDAPAuthnFilter.java
@@ -11,22 +11,11 @@
  * Header, with the fields enclosed by brackets [] replaced by your own identifying
  * information: "Portions copyright [year] [name of copyright owner]".
  *
- * Copyright 2013 ForgeRock AS.
+ * Copyright 2013-2014 ForgeRock AS.
  */
 
 package org.forgerock.opendj.rest2ldap.servlet;
 
-import static org.forgerock.json.resource.SecurityContext.AUTHZID_DN;
-import static org.forgerock.json.resource.SecurityContext.AUTHZID_ID;
-import static org.forgerock.json.resource.servlet.SecurityContextFactory.ATTRIBUTE_AUTHCID;
-import static org.forgerock.json.resource.servlet.SecurityContextFactory.ATTRIBUTE_AUTHZID;
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-import static org.forgerock.opendj.ldap.requests.Requests.newPlainSASLBindRequest;
-import static org.forgerock.opendj.ldap.requests.Requests.newSearchRequest;
-import static org.forgerock.opendj.ldap.requests.Requests.newSimpleBindRequest;
-import static org.forgerock.opendj.rest2ldap.Rest2LDAP.asResourceException;
-import static org.forgerock.opendj.rest2ldap.servlet.Rest2LDAPContextFactory.ATTRIBUTE_AUTHN_CONNECTION;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collections;
@@ -49,8 +38,8 @@
 import org.forgerock.json.fluent.JsonValue;
 import org.forgerock.json.fluent.JsonValueException;
 import org.forgerock.json.resource.ResourceException;
-import org.forgerock.json.resource.servlet.ServletSynchronizer;
 import org.forgerock.json.resource.servlet.ServletApiVersionAdapter;
+import org.forgerock.json.resource.servlet.ServletSynchronizer;
 import org.forgerock.opendj.ldap.AuthenticationException;
 import org.forgerock.opendj.ldap.AuthorizationException;
 import org.forgerock.opendj.ldap.ByteString;
@@ -62,7 +51,6 @@
 import org.forgerock.opendj.ldap.ErrorResultException;
 import org.forgerock.opendj.ldap.MultipleEntriesFoundException;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.SearchScope;
 import org.forgerock.opendj.ldap.requests.BindRequest;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
@@ -70,6 +58,17 @@
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.schema.Schema;
 import org.forgerock.opendj.rest2ldap.Rest2LDAP;
+import org.forgerock.util.promise.AsyncFunction;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.SuccessHandler;
+
+import static org.forgerock.json.resource.SecurityContext.*;
+import static org.forgerock.json.resource.servlet.SecurityContextFactory.*;
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+import static org.forgerock.opendj.ldap.requests.Requests.*;
+import static org.forgerock.opendj.rest2ldap.Rest2LDAP.*;
+import static org.forgerock.opendj.rest2ldap.servlet.Rest2LDAPContextFactory.*;
 
 /**
  * An LDAP based authentication Servlet filter.
@@ -115,8 +114,8 @@
     }
 
     @Override
-    public void doFilter(final ServletRequest request, final ServletResponse response,
-            final FilterChain chain) throws IOException, ServletException {
+    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
+            throws IOException, ServletException {
         // Skip this filter if authentication has not been configured.
         if (!isEnabled) {
             chain.doFilter(request, response);
@@ -149,14 +148,11 @@
         });
 
         try {
-            final String headerUsername =
-                    supportAltAuthentication ? req.getHeader(altAuthenticationUsernameHeader)
-                            : null;
-            final String headerPassword =
-                    supportAltAuthentication ? req.getHeader(altAuthenticationPasswordHeader)
-                            : null;
-            final String headerAuthorization =
-                    supportHTTPBasicAuthentication ? req.getHeader("Authorization") : null;
+            final String headerUsername = supportAltAuthentication ? req.getHeader(altAuthenticationUsernameHeader)
+                    : null;
+            final String headerPassword = supportAltAuthentication ? req.getHeader(altAuthenticationPasswordHeader)
+                    : null;
+            final String headerAuthorization = supportHTTPBasicAuthentication ? req.getHeader("Authorization") : null;
 
             final String username;
             final char[] password;
@@ -194,17 +190,15 @@
                 authzid = new LinkedHashMap<String, Object>(2);
                 authzid.put(AUTHZID_DN, username);
                 authzid.put(AUTHZID_ID, username);
-                doBind(req, res, newSimpleBindRequest(username, password), chain, savedConnection,
-                        sync, username, authzid);
+                doBind(req, res, newSimpleBindRequest(username, password), chain, savedConnection, sync, username,
+                        authzid);
                 break;
             }
             case SASL_PLAIN: {
                 final Map<String, Object> authzid;
                 final String bindId;
                 if (saslAuthzIdTemplate.startsWith("dn:")) {
-                    final String bindDN =
-                            DN.format(saslAuthzIdTemplate.substring(3), schema, username)
-                                    .toString();
+                    final String bindDN = DN.format(saslAuthzIdTemplate.substring(3), schema, username).toString();
                     bindId = "dn:" + bindDN;
                     authzid = new LinkedHashMap<String, Object>(2);
                     authzid.put(AUTHZID_DN, bindDN);
@@ -213,8 +207,8 @@
                     bindId = String.format(saslAuthzIdTemplate, username);
                     authzid = Collections.singletonMap(AUTHZID_ID, (Object) username);
                 }
-                doBind(req, res, newPlainSASLBindRequest(bindId, password), chain, savedConnection,
-                        sync, username, authzid);
+                doBind(req, res, newPlainSASLBindRequest(bindId, password), chain, savedConnection, sync, username,
+                        authzid);
                 break;
             }
             default: // SEARCH_SIMPLE
@@ -223,61 +217,52 @@
                  * First do a search to find the user's entry and then perform a
                  * bind request using the user's DN.
                  */
-                final org.forgerock.opendj.ldap.Filter filter =
-                        org.forgerock.opendj.ldap.Filter.format(searchFilterTemplate, username);
-                final SearchRequest searchRequest =
-                        newSearchRequest(searchBaseDN, searchScope, filter, "1.1");
-                searchLDAPConnectionFactory.getConnectionAsync(new ResultHandler<Connection>() {
-                    @Override
-                    public void handleErrorResult(final ErrorResultException error) {
-                        sync.signalAndComplete(asResourceException(error));
-                    }
-
-                    @Override
-                    public void handleResult(final Connection connection) {
-                        // Do the search.
-                        connection.searchSingleEntryAsync(searchRequest,
-                                new ResultHandler<SearchResultEntry>() {
-
-                                    @Override
-                                    public void handleErrorResult(final ErrorResultException error) {
-                                        connection.close();
-                                        /*
-                                         * The search error should not be passed
-                                         * as-is back to the user.
-                                         */
-                                        final ErrorResultException normalizedError;
-                                        if (error instanceof EntryNotFoundException
-                                                || error instanceof MultipleEntriesFoundException) {
-                                            normalizedError =
-                                                    newErrorResult(ResultCode.INVALID_CREDENTIALS,
-                                                            error);
-                                        } else if (error instanceof AuthenticationException
-                                                || error instanceof AuthorizationException) {
-                                            normalizedError =
-                                                    newErrorResult(
-                                                            ResultCode.CLIENT_SIDE_LOCAL_ERROR,
-                                                            error);
-                                        } else {
-                                            normalizedError = error;
-                                        }
-                                        sync.signalAndComplete(asResourceException(normalizedError));
+                final org.forgerock.opendj.ldap.Filter filter = org.forgerock.opendj.ldap.Filter.format(
+                        searchFilterTemplate, username);
+                final SearchRequest searchRequest = newSearchRequest(searchBaseDN, searchScope, filter, "1.1");
+                searchLDAPConnectionFactory.getConnectionAsync()
+                        .thenAsync(new AsyncFunction<Connection, SearchResultEntry, ErrorResultException>() {
+                            @Override
+                            public Promise<SearchResultEntry, ErrorResultException> apply(Connection connection)
+                                    throws ErrorResultException {
+                                savedConnection.set(connection);
+                                // Do the search.
+                                return connection.searchSingleEntryAsync(searchRequest);
+                            }
+                        }).onSuccess(new SuccessHandler<SearchResultEntry>() {
+                            @Override
+                            public void handleResult(final SearchResultEntry result) {
+                                savedConnection.get().close();
+                                final String bindDN = result.getName().toString();
+                                final Map<String, Object> authzid = new LinkedHashMap<String, Object>(2);
+                                authzid.put(AUTHZID_DN, bindDN);
+                                authzid.put(AUTHZID_ID, username);
+                                doBind(req, res, newSimpleBindRequest(bindDN, password), chain, savedConnection, sync,
+                                        username, authzid);
+                            }
+                        }).onFailure(new FailureHandler<ErrorResultException>() {
+                            @Override
+                            public void handleError(final ErrorResultException error) {
+                                ErrorResultException normalizedError = error;
+                                if (savedConnection.get() != null) {
+                                    savedConnection.get().close();
+                                    /*
+                                     * The search error should not be passed
+                                     * as-is back to the user.
+                                     */
+                                    if (error instanceof EntryNotFoundException
+                                            || error instanceof MultipleEntriesFoundException) {
+                                        normalizedError = newErrorResult(ResultCode.INVALID_CREDENTIALS, error);
+                                    } else if (error instanceof AuthenticationException
+                                            || error instanceof AuthorizationException) {
+                                        normalizedError = newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, error);
+                                    } else {
+                                        normalizedError = error;
                                     }
-
-                                    @Override
-                                    public void handleResult(final SearchResultEntry result) {
-                                        connection.close();
-                                        final String bindDN = result.getName().toString();
-                                        final Map<String, Object> authzid =
-                                                new LinkedHashMap<String, Object>(2);
-                                        authzid.put(AUTHZID_DN, bindDN);
-                                        authzid.put(AUTHZID_ID, username);
-                                        doBind(req, res, newSimpleBindRequest(bindDN, password),
-                                                chain, savedConnection, sync, username, authzid);
-                                    }
-                                });
-                    }
-                });
+                                }
+                                sync.signalAndComplete(asResourceException(normalizedError));
+                            }
+                        });
                 break;
             }
             }
@@ -406,25 +391,17 @@
      * cached connection and authorization credentials on completion.
      */
     private void doBind(final HttpServletRequest request, final ServletResponse response,
-            final BindRequest bindRequest, final FilterChain chain,
-            final AtomicReference<Connection> savedConnection, final ServletSynchronizer sync,
-            final String authcid, final Map<String, Object> authzid) {
-        bindLDAPConnectionFactory.getConnectionAsync(new ResultHandler<Connection>() {
-            @Override
-            public void handleErrorResult(final ErrorResultException error) {
-                sync.signalAndComplete(asResourceException(error));
-            }
-
-            @Override
-            public void handleResult(final Connection connection) {
-                savedConnection.set(connection);
-                connection.bindAsync(bindRequest, null, new ResultHandler<BindResult>() {
-
+            final BindRequest bindRequest, final FilterChain chain, final AtomicReference<Connection> savedConnection,
+            final ServletSynchronizer sync, final String authcid, final Map<String, Object> authzid) {
+        bindLDAPConnectionFactory.getConnectionAsync()
+                .thenAsync(new AsyncFunction<Connection, BindResult, ErrorResultException>() {
                     @Override
-                    public void handleErrorResult(final ErrorResultException error) {
-                        sync.signalAndComplete(asResourceException(error));
+                    public Promise<BindResult, ErrorResultException> apply(Connection connection)
+                            throws ErrorResultException {
+                        savedConnection.set(connection);
+                        return connection.bindAsync(bindRequest);
                     }
-
+                }).onSuccess(new SuccessHandler<BindResult>() {
                     @Override
                     public void handleResult(final BindResult result) {
                         /*
@@ -433,8 +410,8 @@
                          * filter will close it.
                          */
                         if (reuseAuthenticatedConnection) {
-                            request.setAttribute(ATTRIBUTE_AUTHN_CONNECTION, Connections
-                                    .uncloseable(connection));
+                            request.setAttribute(ATTRIBUTE_AUTHN_CONNECTION,
+                                    Connections.uncloseable(savedConnection.get()));
                         }
 
                         // Pass through the authentication ID and authorization principals.
@@ -462,9 +439,12 @@
                             }
                         }
                     }
+                }).onFailure(new FailureHandler<ErrorResultException>() {
+                    @Override
+                    public void handleError(final ErrorResultException error) {
+                        sync.signalAndComplete(asResourceException(error));
+                    }
                 });
-            }
-        });
     }
 
     private AuthenticationMethod parseAuthenticationMethod(final JsonValue configuration) {
diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java
index 498e84a..94a42e4 100644
--- a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java
+++ b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java
@@ -11,14 +11,10 @@
  * Header, with the fields enclosed by brackets [] replaced by your own identifying
  * information: "Portions Copyright [year] [name of copyright owner]".
  *
- * Copyright 2013 ForgeRock AS.
+ * Copyright 2013-2014 ForgeRock AS.
  */
 package org.forgerock.opendj.rest2ldap;
 
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-import static org.forgerock.opendj.rest2ldap.Rest2LDAP.asResourceException;
-import static org.forgerock.opendj.rest2ldap.Utils.i18n;
-
 import java.io.Closeable;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -58,6 +54,11 @@
 import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.SuccessHandler;
+
+import static org.forgerock.opendj.rest2ldap.Rest2LDAP.*;
+import static org.forgerock.opendj.rest2ldap.Utils.*;
 
 /**
  * Common context information passed to containers and mappers. A new context is
@@ -68,7 +69,7 @@
     /*
      * A cached read request - see cachedReads for more information.
      */
-    private static final class CachedRead implements SearchResultHandler {
+    private static final class CachedRead implements SearchResultHandler, ResultHandler<Result> {
         private SearchResultEntry cachedEntry;
         private final String cachedFilterString;
         private FutureResult<Result> cachedFuture; // Guarded by latch.
@@ -91,7 +92,7 @@
         }
 
         @Override
-        public void handleErrorResult(final ErrorResultException error) {
+        public void handleError(final ErrorResultException error) {
             handleResult(error.getResult());
         }
 
@@ -171,14 +172,9 @@
             }
         }
 
-        private void invokeResultHandler(final SearchResultHandler resultHandler) {
+        private void invokeResultHandler(final SearchResultHandler searchResultHandler) {
             if (cachedEntry != null) {
-                resultHandler.handleEntry(cachedEntry);
-            }
-            if (cachedResult.isSuccess()) {
-                resultHandler.handleResult(cachedResult);
-            } else {
-                resultHandler.handleErrorResult(newErrorResult(cachedResult));
+                searchResultHandler.handleEntry(cachedEntry);
             }
         }
 
@@ -214,9 +210,7 @@
          */
         if (config.getAuthorizationPolicy() != AuthorizationPolicy.NONE
                 && context.containsContext(AuthenticatedConnectionContext.class)) {
-            final Connection connection =
-                    context.asContext(AuthenticatedConnectionContext.class).getConnection();
-            this.connection = wrap(connection);
+            this.connection = wrap(context.asContext(AuthenticatedConnectionContext.class).getConnection());
         } else {
             this.connection = null; // We'll allocate the connection.
         }
@@ -268,11 +262,9 @@
         if (connection == null && config.getAuthorizationPolicy() == AuthorizationPolicy.PROXY) {
             if (context.containsContext(SecurityContext.class)) {
                 try {
-                    final SecurityContext securityContext =
-                            context.asContext(SecurityContext.class);
-                    final String authzId =
-                            config.getProxiedAuthorizationTemplate().formatAsAuthzId(
-                                    securityContext.getAuthorizationId(), config.schema());
+                    final SecurityContext securityContext = context.asContext(SecurityContext.class);
+                    final String authzId = config.getProxiedAuthorizationTemplate().formatAsAuthzId(
+                            securityContext.getAuthorizationId(), config.schema());
                     proxiedAuthzControl = ProxiedAuthV2RequestControl.newControl(authzId);
                 } catch (final ResourceException e) {
                     handler.handleError(e);
@@ -280,8 +272,7 @@
                 }
             } else {
                 handler.handleError(new InternalServerErrorException(
-                        i18n("The request could not be authorized because it did "
-                                + "not contain a security context")));
+                        i18n("The request could not be authorized because it did not contain a security context")));
                 return;
             }
         }
@@ -295,22 +286,21 @@
             // Invoke the handler immediately since a connection is available.
             runnable.run();
         } else if (config.connectionFactory() != null) {
-            config.connectionFactory().getConnectionAsync(new ResultHandler<Connection>() {
-                @Override
-                public final void handleErrorResult(final ErrorResultException error) {
-                    handler.handleError(asResourceException(error));
-                }
-
+            config.connectionFactory().getConnectionAsync().onSuccess(new SuccessHandler<Connection>() {
                 @Override
                 public final void handleResult(final Connection result) {
                     connection = wrap(result);
                     runnable.run();
                 }
+            }).onFailure(new FailureHandler<ErrorResultException>() {
+                @Override
+                public final void handleError(final ErrorResultException error) {
+                    handler.handleError(asResourceException(error));
+                }
             });
         } else {
             handler.handleError(new InternalServerErrorException(
-                    i18n("The request could not be processed because there was no LDAP "
-                            + "connection available for use")));
+                    i18n("The request could not be processed because there was no LDAP connection available for use")));
         }
     }
 
@@ -331,10 +321,8 @@
 
             @Override
             public FutureResult<Result> addAsync(final AddRequest request,
-                    final IntermediateResponseHandler intermediateResponseHandler,
-                    final ResultHandler<? super Result> resultHandler) {
-                return connection.addAsync(withControls(request), intermediateResponseHandler,
-                        resultHandler);
+                final IntermediateResponseHandler intermediateResponseHandler) {
+                return connection.addAsync(withControls(request), intermediateResponseHandler);
             }
 
             @Override
@@ -344,14 +332,13 @@
 
             @Override
             public FutureResult<BindResult> bindAsync(final BindRequest request,
-                    final IntermediateResponseHandler intermediateResponseHandler,
-                    final ResultHandler<? super BindResult> resultHandler) {
+                final IntermediateResponseHandler intermediateResponseHandler) {
                 /*
                  * Simple brute force implementation in case the bind operation
                  * modifies an entry: clear the cachedReads.
                  */
                 evictAll();
-                return connection.bindAsync(request, intermediateResponseHandler, resultHandler);
+                return connection.bindAsync(request, intermediateResponseHandler);
             }
 
             @Override
@@ -366,33 +353,26 @@
 
             @Override
             public FutureResult<CompareResult> compareAsync(final CompareRequest request,
-                    final IntermediateResponseHandler intermediateResponseHandler,
-                    final ResultHandler<? super CompareResult> resultHandler) {
-                return connection.compareAsync(withControls(request), intermediateResponseHandler,
-                        resultHandler);
+                final IntermediateResponseHandler intermediateResponseHandler) {
+                return connection.compareAsync(withControls(request), intermediateResponseHandler);
             }
 
             @Override
             public FutureResult<Result> deleteAsync(final DeleteRequest request,
-                    final IntermediateResponseHandler intermediateResponseHandler,
-                    final ResultHandler<? super Result> resultHandler) {
+                final IntermediateResponseHandler intermediateResponseHandler) {
                 evict(request.getName());
-                return connection.deleteAsync(withControls(request), intermediateResponseHandler,
-                        resultHandler);
+                return connection.deleteAsync(withControls(request), intermediateResponseHandler);
             }
 
             @Override
-            public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(
-                    final ExtendedRequest<R> request,
-                    final IntermediateResponseHandler intermediateResponseHandler,
-                    final ResultHandler<? super R> resultHandler) {
+            public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request,
+                final IntermediateResponseHandler intermediateResponseHandler) {
                 /*
                  * Simple brute force implementation in case the extended
                  * operation modifies an entry: clear the cachedReads.
                  */
                 evictAll();
-                return connection.extendedRequestAsync(withControls(request),
-                        intermediateResponseHandler, resultHandler);
+                return connection.extendedRequestAsync(withControls(request), intermediateResponseHandler);
             }
 
             @Override
@@ -407,21 +387,17 @@
 
             @Override
             public FutureResult<Result> modifyAsync(final ModifyRequest request,
-                    final IntermediateResponseHandler intermediateResponseHandler,
-                    final ResultHandler<? super Result> resultHandler) {
+                final IntermediateResponseHandler intermediateResponseHandler) {
                 evict(request.getName());
-                return connection.modifyAsync(withControls(request), intermediateResponseHandler,
-                        resultHandler);
+                return connection.modifyAsync(withControls(request), intermediateResponseHandler);
             }
 
             @Override
             public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
-                    final IntermediateResponseHandler intermediateResponseHandler,
-                    final ResultHandler<? super Result> resultHandler) {
+                final IntermediateResponseHandler intermediateResponseHandler) {
                 // Simple brute force implementation: clear the cachedReads.
                 evictAll();
-                return connection.modifyDNAsync(withControls(request), intermediateResponseHandler,
-                        resultHandler);
+                return connection.modifyDNAsync(withControls(request), intermediateResponseHandler);
             }
 
             @Override
@@ -434,17 +410,14 @@
              */
             @Override
             public FutureResult<Result> searchAsync(final SearchRequest request,
-                    final IntermediateResponseHandler intermediateResponseHandler,
-                    final SearchResultHandler resultHandler) {
+                final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) {
                 /*
                  * Don't attempt caching if this search is not a read (base
                  * object), or if the search request passed in an intermediate
                  * response handler.
                  */
-                if (!request.getScope().equals(SearchScope.BASE_OBJECT)
-                        || intermediateResponseHandler != null) {
-                    return connection.searchAsync(withControls(request),
-                            intermediateResponseHandler, resultHandler);
+                if (!request.getScope().equals(SearchScope.BASE_OBJECT) || intermediateResponseHandler != null) {
+                    return connection.searchAsync(withControls(request), intermediateResponseHandler, entryHandler);
                 }
 
                 // This is a read request and a candidate for caching.
@@ -454,17 +427,17 @@
                 }
                 if (cachedRead != null && cachedRead.isMatchingRead(request)) {
                     // The cached read matches this read request.
-                    cachedRead.addResultHandler(resultHandler);
+                    cachedRead.addResultHandler(entryHandler);
                     return cachedRead.getFutureResult();
                 } else {
                     // Cache the read, possibly evicting a non-matching cached read.
-                    final CachedRead pendingCachedRead = new CachedRead(request, resultHandler);
+                    final CachedRead pendingCachedRead = new CachedRead(request, entryHandler);
                     synchronized (cachedReads) {
                         cachedReads.put(request.getName(), pendingCachedRead);
                     }
-                    final FutureResult<Result> future =
-                            connection.searchAsync(withControls(request),
-                                    intermediateResponseHandler, pendingCachedRead);
+                    final FutureResult<Result> future = (FutureResult<Result>) connection
+                            .searchAsync(withControls(request), intermediateResponseHandler, pendingCachedRead)
+                            .onSuccess(pendingCachedRead).onFailure(pendingCachedRead);
                     pendingCachedRead.setFuture(future);
                     return future;
                 }
diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
index 69742ff..2d2d3e8 100644
--- a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
+++ b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
@@ -11,24 +11,11 @@
  * Header, with the fields enclosed by brackets [] replaced by your own identifying
  * information: "Portions Copyright [year] [name of copyright owner]".
  *
- * Copyright 2012-2013 ForgeRock AS.
+ * Copyright 2012-2014 ForgeRock AS.
  */
 package org.forgerock.opendj.rest2ldap;
 
-import static java.util.Arrays.asList;
-import static org.forgerock.opendj.ldap.Filter.alwaysFalse;
-import static org.forgerock.opendj.ldap.Filter.alwaysTrue;
-import static org.forgerock.opendj.ldap.requests.Requests.newAddRequest;
-import static org.forgerock.opendj.ldap.requests.Requests.newDeleteRequest;
-import static org.forgerock.opendj.ldap.requests.Requests.newModifyRequest;
-import static org.forgerock.opendj.ldap.requests.Requests.newSearchRequest;
-import static org.forgerock.opendj.rest2ldap.ReadOnUpdatePolicy.CONTROLS;
-import static org.forgerock.opendj.rest2ldap.Rest2LDAP.asResourceException;
-import static org.forgerock.opendj.rest2ldap.Utils.accumulate;
-import static org.forgerock.opendj.rest2ldap.Utils.i18n;
-import static org.forgerock.opendj.rest2ldap.Utils.toFilter;
-import static org.forgerock.opendj.rest2ldap.Utils.transform;
-
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
@@ -86,16 +73,48 @@
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
 import org.forgerock.opendj.ldif.ChangeRecord;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.PromiseImpl;
+import org.forgerock.util.promise.Promises;
+import org.forgerock.util.promise.SuccessHandler;
+
+import static java.util.Arrays.*;
+
+import static org.forgerock.opendj.ldap.Filter.*;
+import static org.forgerock.opendj.ldap.requests.Requests.*;
+import static org.forgerock.opendj.rest2ldap.ReadOnUpdatePolicy.*;
+import static org.forgerock.opendj.rest2ldap.Rest2LDAP.*;
+import static org.forgerock.opendj.rest2ldap.Utils.*;
 
 /**
  * A {@code CollectionResourceProvider} implementation which maps a JSON
  * resource collection to LDAP entries beneath a base DN.
  */
 final class LDAPCollectionResourceProvider implements CollectionResourceProvider {
-    // Dummy exception used for signalling search success.
+    private static class ResultHandlerFromPromise<T> implements ResultHandler<T> {
+        private final PromiseImpl<T, ResourceException> promise;
+
+        ResultHandlerFromPromise() {
+            promise = PromiseImpl.create();
+        }
+
+        @Override
+        public void handleError(ResourceException error) {
+            promise.handleError(error);
+
+        }
+
+        @Override
+        public void handleResult(T result) {
+            promise.handleResult(result);
+        }
+    }
+
+    /** Dummy exception used for signalling search success. */
     private static final ResourceException SUCCESS = new UncategorizedException(0, null, null);
 
-    // Empty decode options required for decoding response controls.
+    /** Empty decode options required for decoding response controls. */
     private static final DecodeOptions DECODE_OPTIONS = new DecodeOptions();
 
     private final List<Attribute> additionalLDAPAttributes;
@@ -140,39 +159,37 @@
             public void run() {
                 // Calculate entry content.
                 attributeMapper.create(c, new JsonPointer(), request.getContent(),
-                        new ResultHandler<List<Attribute>>() {
-                            @Override
-                            public void handleError(final ResourceException error) {
-                                h.handleError(error);
-                            }
+                    new ResultHandler<List<Attribute>>() {
+                        @Override
+                        public void handleError(final ResourceException error) {
+                            h.handleError(error);
+                        }
 
-                            @Override
-                            public void handleResult(final List<Attribute> result) {
-                                // Perform add operation.
-                                final AddRequest addRequest = newAddRequest(DN.rootDN());
-                                for (final Attribute attribute : additionalLDAPAttributes) {
-                                    addRequest.addAttribute(attribute);
-                                }
-                                for (final Attribute attribute : result) {
-                                    addRequest.addAttribute(attribute);
-                                }
-                                try {
-                                    nameStrategy.setResourceId(c, getBaseDN(c), request
-                                            .getNewResourceId(), addRequest);
-                                } catch (final ResourceException e) {
-                                    h.handleError(e);
-                                    return;
-                                }
-                                if (config.readOnUpdatePolicy() == CONTROLS) {
-                                    final String[] attributes =
-                                            getLDAPAttributes(c, request.getFields());
-                                    addRequest.addControl(PostReadRequestControl.newControl(false,
-                                            attributes));
-                                }
-                                c.getConnection().applyChangeAsync(addRequest, null,
-                                        postUpdateHandler(c, h));
+                        @Override
+                        public void handleResult(final List<Attribute> result) {
+                            // Perform add operation.
+                            final AddRequest addRequest = newAddRequest(DN.rootDN());
+                            for (final Attribute attribute : additionalLDAPAttributes) {
+                                addRequest.addAttribute(attribute);
                             }
-                        });
+                            for (final Attribute attribute : result) {
+                                addRequest.addAttribute(attribute);
+                            }
+                            try {
+                                nameStrategy.setResourceId(c, getBaseDN(c), request.getNewResourceId(), addRequest);
+                            } catch (final ResourceException e) {
+                                h.handleError(e);
+                                return;
+                            }
+                            if (config.readOnUpdatePolicy() == CONTROLS) {
+                                final String[] attributes = getLDAPAttributes(c, request.getFields());
+                                addRequest.addControl(PostReadRequestControl.newControl(false, attributes));
+                            }
+                            c.getConnection().applyChangeAsync(addRequest)
+                                        .onSuccess(postUpdateSuccessHandler(c, h))
+                                        .onFailure(postUpdateFailureHandler(h));
+                        }
+                    });
             }
         });
     }
@@ -196,15 +213,14 @@
                     final ChangeRecord deleteRequest = newDeleteRequest(dn);
                     if (config.readOnUpdatePolicy() == CONTROLS) {
                         final String[] attributes = getLDAPAttributes(c, request.getFields());
-                        deleteRequest.addControl(PreReadRequestControl
-                                .newControl(false, attributes));
+                        deleteRequest.addControl(PreReadRequestControl.newControl(false, attributes));
                     }
                     if (config.useSubtreeDelete()) {
                         deleteRequest.addControl(SubtreeDeleteRequestControl.newControl(true));
                     }
                     addAssertionControl(deleteRequest, request.getRevision());
-                    c.getConnection()
-                            .applyChangeAsync(deleteRequest, null, postUpdateHandler(c, h));
+                    c.getConnection().applyChangeAsync(deleteRequest).onSuccess(postUpdateSuccessHandler(c, h))
+                            .onFailure(postUpdateFailureHandler(h));
                 } catch (final Exception e) {
                     h.handleError(asResourceException(e));
                 }
@@ -213,31 +229,29 @@
     }
 
     @Override
-    public void patchInstance(final ServerContext context, final String resourceId,
-            final PatchRequest request, final ResultHandler<Resource> handler) {
+    public void patchInstance(final ServerContext context, final String resourceId, final PatchRequest request,
+            final ResultHandler<Resource> handler) {
         final Context c = wrap(context);
         final ResultHandler<Resource> h = wrap(c, handler);
 
         if (request.getPatchOperations().isEmpty()) {
             /*
-             * This patch is a no-op so just read the entry and check its
-             * version.
+             * This patch is a no-op so just read the entry and check its version.
              */
             c.run(h, new Runnable() {
                 @Override
                 public void run() {
                     final String[] attributes = getLDAPAttributes(c, request.getFields());
-                    final SearchRequest searchRequest =
-                            nameStrategy.createSearchRequest(c, getBaseDN(c), resourceId)
-                                    .addAttribute(attributes);
-                    c.getConnection().searchSingleEntryAsync(searchRequest,
-                            postEmptyPatchHandler(c, request, h));
+                    final SearchRequest searchRequest = nameStrategy.createSearchRequest(c, getBaseDN(c), resourceId)
+                            .addAttribute(attributes);
+                    c.getConnection().searchSingleEntryAsync(searchRequest)
+                            .onSuccess(postEmptyPatchSuccessHandler(c, request, h))
+                            .onFailure(postEmptyPatchFailureHandler(h));
                 }
             });
         } else {
             /*
-             * Get the connection, search if needed, then determine
-             * modifications, then perform modify.
+             * Get the connection, search if needed, then determine modifications, then perform modify.
              */
             c.run(h, doUpdate(c, resourceId, request.getRevision(), new ResultHandler<DN>() {
                 @Override
@@ -247,71 +261,62 @@
 
                 @Override
                 public void handleResult(final DN dn) {
-                    //  Convert the patch operations to LDAP modifications.
-                    final ResultHandler<List<Modification>> handler =
-                            accumulate(request.getPatchOperations().size(),
-                                    new ResultHandler<List<List<Modification>>>() {
-                                        @Override
-                                        public void handleError(final ResourceException error) {
-                                            h.handleError(error);
-                                        }
-
-                                        @Override
-                                        public void handleResult(
-                                                final List<List<Modification>> result) {
-                                            //  The patch operations have been converted successfully.
-                                            try {
-                                                final ModifyRequest modifyRequest =
-                                                        newModifyRequest(dn);
-
-                                                // Add the modifications.
-                                                for (final List<Modification> modifications : result) {
-                                                    if (modifications != null) {
-                                                        modifyRequest.getModifications().addAll(
-                                                                modifications);
-                                                    }
-                                                }
-
-                                                final List<String> attributes =
-                                                        asList(getLDAPAttributes(c, request
-                                                                .getFields()));
-                                                if (modifyRequest.getModifications().isEmpty()) {
-                                                    /*
-                                                     * This patch is a no-op so
-                                                     * just read the entry and
-                                                     * check its version.
-                                                     */
-                                                    c.getConnection().readEntryAsync(dn,
-                                                            attributes,
-                                                            postEmptyPatchHandler(c, request, h));
-                                                } else {
-                                                    // Add controls and perform the modify request.
-                                                    if (config.readOnUpdatePolicy() == CONTROLS) {
-                                                        modifyRequest
-                                                                .addControl(PostReadRequestControl
-                                                                        .newControl(false,
-                                                                                attributes));
-                                                    }
-                                                    if (config.usePermissiveModify()) {
-                                                        modifyRequest
-                                                                .addControl(PermissiveModifyRequestControl
-                                                                        .newControl(true));
-                                                    }
-                                                    addAssertionControl(modifyRequest, request
-                                                            .getRevision());
-                                                    c.getConnection().applyChangeAsync(
-                                                            modifyRequest, null,
-                                                            postUpdateHandler(c, h));
-                                                }
-                                            } catch (final Exception e) {
-                                                h.handleError(asResourceException(e));
-                                            }
-                                        }
-                                    });
-
+                    // Convert the patch operations to LDAP modifications.
+                    List<Promise<List<Modification>, ResourceException>> promises =
+                            new ArrayList<Promise<List<Modification>, ResourceException>>(
+                                    request.getPatchOperations().size());
                     for (final PatchOperation operation : request.getPatchOperations()) {
+                        final ResultHandlerFromPromise<List<Modification>> handler =
+                                new ResultHandlerFromPromise<List<Modification>>();
                         attributeMapper.patch(c, new JsonPointer(), operation, handler);
+                        promises.add(handler.promise);
                     }
+
+                    Promises.when(promises).onSuccess(new SuccessHandler<List<List<Modification>>>() {
+                        @Override
+                        public void handleResult(final List<List<Modification>> result) {
+                            // The patch operations have been converted successfully.
+                            try {
+                                final ModifyRequest modifyRequest = newModifyRequest(dn);
+
+                                // Add the modifications.
+                                for (final List<Modification> modifications : result) {
+                                    if (modifications != null) {
+                                        modifyRequest.getModifications().addAll(modifications);
+                                    }
+                                }
+
+                                final List<String> attributes = asList(getLDAPAttributes(c, request.getFields()));
+                                if (modifyRequest.getModifications().isEmpty()) {
+                                    /*
+                                     * This patch is a no-op so just read the entry and check its version.
+                                     */
+                                    c.getConnection().readEntryAsync(dn, attributes)
+                                            .onSuccess(postEmptyPatchSuccessHandler(c, request, h))
+                                            .onFailure(postEmptyPatchFailureHandler(h));
+                                } else {
+                                    // Add controls and perform the modify request.
+                                    if (config.readOnUpdatePolicy() == CONTROLS) {
+                                        modifyRequest.addControl(PostReadRequestControl.newControl(false, attributes));
+                                    }
+                                    if (config.usePermissiveModify()) {
+                                        modifyRequest.addControl(PermissiveModifyRequestControl.newControl(true));
+                                    }
+                                    addAssertionControl(modifyRequest, request.getRevision());
+                                    c.getConnection().applyChangeAsync(modifyRequest)
+                                            .onSuccess(postUpdateSuccessHandler(c, h))
+                                            .onFailure(postUpdateFailureHandler(h));
+                                }
+                            } catch (final Exception e) {
+                                h.handleError(asResourceException(e));
+                            }
+                        }
+                    }).onFailure(new FailureHandler<ResourceException>() {
+                        @Override
+                        public void handleError(ResourceException error) {
+                            h.handleError(asResourceException(error));
+                        }
+                    });
                 }
             }));
         }
@@ -324,14 +329,25 @@
         final QueryResultHandler h = wrap(c, handler);
 
         /*
-         * Get the connection, then calculate the search filter, then perform
-         * the search.
+         * Get the connection, then calculate the search filter, then perform the search.
          */
         c.run(h, new Runnable() {
             @Override
             public void run() {
                 // Calculate the filter (this may require the connection).
                 getLDAPFilter(c, request.getQueryFilter(), new ResultHandler<Filter>() {
+                    /**
+                     * The following fields are guarded by sequenceLock. In
+                     * addition, the sequenceLock ensures that we send one JSON
+                     * resource at a time back to the client.
+                     */
+                    private final Object sequenceLock = new Object();
+                    private String cookie;
+                    private ResourceException pendingResult;
+                    private int pendingResourceCount;
+                    private boolean resultSent;
+                    private int totalResourceCount;
+
                     @Override
                     public void handleError(final ResourceException error) {
                         h.handleError(error);
@@ -340,23 +356,21 @@
                     @Override
                     public void handleResult(final Filter ldapFilter) {
                         /*
-                         * Avoid performing a search if the filter could not be
-                         * mapped or if it will never match.
+                         * Avoid performing a search if the filter could not be mapped or if it will never match.
                          */
                         if (ldapFilter == null || ldapFilter == alwaysFalse()) {
                             h.handleResult(new QueryResult());
                         } else {
                             // Perform the search.
                             final String[] attributes = getLDAPAttributes(c, request.getFields());
+                            final Filter searchFilter =
+                                    ldapFilter == Filter.alwaysTrue() ? Filter.objectClassPresent() : ldapFilter;
                             final SearchRequest searchRequest =
-                                    newSearchRequest(getBaseDN(c), SearchScope.SINGLE_LEVEL,
-                                            ldapFilter == Filter.alwaysTrue() ? Filter
-                                                    .objectClassPresent() : ldapFilter, attributes);
+                                    newSearchRequest(getBaseDN(c), SearchScope.SINGLE_LEVEL, searchFilter, attributes);
 
                             /*
-                             * Add the page results control. We can support the
-                             * page offset by reading the next offset pages, or
-                             * offset x page size resources.
+                             * Add the page results control. We can support the page offset by
+                             * reading the next offset pages, or offset x page size resources.
                              */
                             final int pageResultStartIndex;
                             final int pageSize = request.getPageSize();
@@ -374,35 +388,18 @@
                                                 .valueOfBase64(request.getPagedResultsCookie())
                                                 : ByteString.empty();
                                 final SimplePagedResultsControl control =
-                                        SimplePagedResultsControl.newControl(true,
-                                                pageResultEndIndex, cookie);
+                                        SimplePagedResultsControl.newControl(true, pageResultEndIndex, cookie);
                                 searchRequest.addControl(control);
                             } else {
                                 pageResultStartIndex = 0;
                             }
 
-                            c.getConnection().searchAsync(searchRequest, null, new SearchResultHandler() {
-                                /*
-                                 * The following fields are guarded by
-                                 * sequenceLock. In addition, the sequenceLock
-                                 * ensures that we send one JSON resource at a
-                                 * time back to the client.
-                                 */
-                                private final Object sequenceLock = new Object();
-                                private int pendingResourceCount = 0;
-                                private ResourceException pendingResult = null;
-                                private boolean resultSent = false;
-                                private int totalResourceCount = 0;
-                                private String cookie = null;
-
+                            c.getConnection().searchAsync(searchRequest, new SearchResultHandler() {
                                 @Override
                                 public boolean handleEntry(final SearchResultEntry entry) {
                                     /*
-                                     * Search result entries will be returned
-                                     * before the search result/error so the
-                                     * only reason pendingResult will be
-                                     * non-null is if a mapping error has
-                                     * occurred.
+                                     * Search result entries will be returned before the search result/error so the
+                                     * only reason pendingResult will be non-null is if a mapping error has occurred.
                                      */
                                     synchronized (sequenceLock) {
                                         if (pendingResult != null) {
@@ -416,75 +413,59 @@
                                     }
 
                                     /*
-                                     * FIXME: secondary asynchronous searches
-                                     * will complete in a non-deterministic
-                                     * order and may cause the JSON resources to
-                                     * be returned in a different order to the
-                                     * order in which the primary LDAP search
-                                     * results were received. This is benign at
-                                     * the moment, but will need resolving when
-                                     * we implement server side sorting. A
-                                     * possible fix will be to use a queue of
-                                     * pending resources (futures?). However,
-                                     * the queue cannot be unbounded in case it
-                                     * grows very large, but it cannot be
-                                     * bounded either since that could cause a
-                                     * deadlock between rest2ldap and the LDAP
-                                     * server (imagine the case where the server
-                                     * has a single worker thread which is
+                                     * FIXME: secondary asynchronous searches will complete in a non-deterministic
+                                     * order and may cause the JSON resources to be returned in a different order to the
+                                     * order in which the primary LDAP search results were received. This is benign at
+                                     * the moment, but will need resolving when we implement server side sorting. A
+                                     * possible fix will be to use a queue of pending resources (futures?). However,
+                                     * the queue cannot be unbounded in case it grows very large, but it cannot be
+                                     * bounded either since that could cause a deadlock between rest2ldap and the LDAP
+                                     * server (imagine the case where the server has a single worker thread which is
                                      * occupied processing the primary search).
-                                     * The best solution is probably to process
-                                     * the primary search results in batches
+                                     * The best solution is probably to process the primary search results in batches
                                      * using the paged results control.
                                      */
                                     final String id = nameStrategy.getResourceId(c, entry);
                                     final String revision = getRevisionFromEntry(entry);
-                                    attributeMapper.read(c, new JsonPointer(), entry,
-                                            new ResultHandler<JsonValue>() {
-                                                @Override
-                                                public void handleError(final ResourceException e) {
-                                                    synchronized (sequenceLock) {
-                                                        pendingResourceCount--;
-                                                        completeIfNecessary(e);
-                                                    }
-                                                }
+                                    attributeMapper.read(c, new JsonPointer(), entry, new ResultHandler<JsonValue>() {
+                                        @Override
+                                        public void handleError(final ResourceException e) {
+                                            synchronized (sequenceLock) {
+                                                pendingResourceCount--;
+                                                completeIfNecessary(e);
+                                            }
+                                        }
 
-                                                @Override
-                                                public void handleResult(final JsonValue result) {
-                                                    synchronized (sequenceLock) {
-                                                        pendingResourceCount--;
-                                                        if (!resultSent) {
-                                                            h.handleResource(new Resource(id,
-                                                                    revision, result));
-                                                        }
-                                                        completeIfNecessary();
-                                                    }
+                                        @Override
+                                        public void handleResult(final JsonValue result) {
+                                            synchronized (sequenceLock) {
+                                                pendingResourceCount--;
+                                                if (!resultSent) {
+                                                    h.handleResource(new Resource(id, revision, result));
                                                 }
-                                            });
+                                                completeIfNecessary();
+                                            }
+                                        }
+                                    });
                                     return true;
                                 }
 
                                 @Override
-                                public void handleErrorResult(final ErrorResultException error) {
-                                    synchronized (sequenceLock) {
-                                        completeIfNecessary(asResourceException(error));
-                                    }
-                                }
-
-                                @Override
                                 public boolean handleReference(final SearchResultReference reference) {
                                     // TODO: should this be classed as an error since rest2ldap
                                     // assumes entries are all colocated?
                                     return true;
                                 }
 
+                            }).onSuccess(new SuccessHandler<Result>() {
                                 @Override
-                                public void handleResult(final Result result) {
+                                public void handleResult(Result result) {
                                     synchronized (sequenceLock) {
                                         if (request.getPageSize() > 0) {
                                             try {
-                                                final SimplePagedResultsControl control = result.getControl(
-                                                    SimplePagedResultsControl.DECODER, DECODE_OPTIONS);
+                                                final SimplePagedResultsControl control =
+                                                    result.getControl(SimplePagedResultsControl.DECODER,
+                                                        DECODE_OPTIONS);
                                                 if (control != null && !control.getCookie().isEmpty()) {
                                                     cookie = control.getCookie().toBase64String();
                                                 }
@@ -495,46 +476,50 @@
                                         completeIfNecessary(SUCCESS);
                                     }
                                 }
-
-                                /*
-                                 * This method must be invoked with the
-                                 * sequenceLock held.
-                                 */
-                                private void completeIfNecessary(final ResourceException e) {
-                                    if (pendingResult == null) {
-                                        pendingResult = e;
-                                    }
-                                    completeIfNecessary();
-                                }
-
-                                /*
-                                 * Close out the query result set if there are
-                                 * no more pending resources and the LDAP result
-                                 * has been received. This method must be
-                                 * invoked with the sequenceLock held.
-                                 */
-                                private void completeIfNecessary() {
-                                    if (pendingResourceCount == 0 && pendingResult != null
-                                            && !resultSent) {
-                                        if (pendingResult == SUCCESS) {
-                                            h.handleResult(new QueryResult(cookie, -1));
-                                        } else {
-                                            h.handleError(pendingResult);
-                                        }
-                                        resultSent = true;
+                            }).onFailure(new FailureHandler<ErrorResultException>() {
+                                @Override
+                                public void handleError(ErrorResultException error) {
+                                    synchronized (sequenceLock) {
+                                        completeIfNecessary(asResourceException(error));
                                     }
                                 }
                             });
                         }
                     }
+
+                    /**
+                     * This method must be invoked with the sequenceLock held.
+                     */
+                    private void completeIfNecessary(final ResourceException e) {
+                        if (pendingResult == null) {
+                            pendingResult = e;
+                        }
+                        completeIfNecessary();
+                    }
+
+                    /**
+                     * Close out the query result set if there are no more
+                     * pending resources and the LDAP result has been received.
+                     * This method must be invoked with the sequenceLock held.
+                     */
+                    private void completeIfNecessary() {
+                        if (pendingResourceCount == 0 && pendingResult != null && !resultSent) {
+                            if (pendingResult == SUCCESS) {
+                                h.handleResult(new QueryResult(cookie, -1));
+                            } else {
+                                h.handleError(pendingResult);
+                            }
+                            resultSent = true;
+                        }
+                    }
                 });
             }
         });
     }
 
     @Override
-    public void readInstance(final ServerContext context, final String resourceId,
-            final ReadRequest request, final ResultHandler<Resource> handler) {
+    public void readInstance(final ServerContext context, final String resourceId, final ReadRequest request,
+        final ResultHandler<Resource> handler) {
         final Context c = wrap(context);
         final ResultHandler<Resource> h = wrap(c, handler);
 
@@ -545,27 +530,27 @@
                 // Do the search.
                 final String[] attributes = getLDAPAttributes(c, request.getFields());
                 final SearchRequest request =
-                        nameStrategy.createSearchRequest(c, getBaseDN(c), resourceId).addAttribute(
-                                attributes);
-                c.getConnection().searchSingleEntryAsync(request,
-                        new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
-                            @Override
-                            public void handleErrorResult(final ErrorResultException error) {
-                                h.handleError(asResourceException(error));
-                            }
+                    nameStrategy.createSearchRequest(c, getBaseDN(c), resourceId).addAttribute(attributes);
 
-                            @Override
-                            public void handleResult(final SearchResultEntry entry) {
-                                adaptEntry(c, entry, h);
-                            }
-                        });
-            }
+                c.getConnection().searchSingleEntryAsync(request).onSuccess(new SuccessHandler<SearchResultEntry>() {
+                    @Override
+                    public void handleResult(final SearchResultEntry entry) {
+                        adaptEntry(c, entry, h);
+                    }
+                }).onFailure(new FailureHandler<ErrorResultException>() {
+                    @Override
+                    public void handleError(final ErrorResultException error) {
+                        h.handleError(asResourceException(error));
+                    }
+                });
+
+            };
         });
     }
 
     @Override
-    public void updateInstance(final ServerContext context, final String resourceId,
-            final UpdateRequest request, final ResultHandler<Resource> handler) {
+    public void updateInstance(final ServerContext context, final String resourceId, final UpdateRequest request,
+            final ResultHandler<Resource> handler) {
         /*
          * Update operations are a bit awkward because there is no direct
          * mapping to LDAP. We need to convert the update request into an LDAP
@@ -582,70 +567,53 @@
         c.run(h, new Runnable() {
             @Override
             public void run() {
-                final String[] attributes =
-                        getLDAPAttributes(c, Collections.<JsonPointer> emptyList());
-                final SearchRequest searchRequest =
-                        nameStrategy.createSearchRequest(c, getBaseDN(c), resourceId).addAttribute(
-                                attributes);
-                c.getConnection().searchSingleEntryAsync(searchRequest,
-                        new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
-                            @Override
-                            public void handleErrorResult(final ErrorResultException error) {
-                                h.handleError(asResourceException(error));
-                            }
+                final String[] attributes = getLDAPAttributes(c, Collections.<JsonPointer> emptyList());
+                final SearchRequest searchRequest = nameStrategy.createSearchRequest(c, getBaseDN(c), resourceId)
+                        .addAttribute(attributes);
 
+                c.getConnection().searchSingleEntryAsync(searchRequest)
+                        .onSuccess(new SuccessHandler<SearchResultEntry>() {
                             @Override
                             public void handleResult(final SearchResultEntry entry) {
                                 try {
                                     // Fail-fast if there is a version mismatch.
                                     ensureMVCCVersionMatches(entry, request.getRevision());
 
-                                    //  Create the modify request.
-                                    final ModifyRequest modifyRequest =
-                                            newModifyRequest(entry.getName());
+                                    // Create the modify request.
+                                    final ModifyRequest modifyRequest = newModifyRequest(entry.getName());
                                     if (config.readOnUpdatePolicy() == CONTROLS) {
-                                        final String[] attributes =
-                                                getLDAPAttributes(c, request.getFields());
-                                        modifyRequest.addControl(PostReadRequestControl.newControl(
-                                                false, attributes));
+                                        final String[] attributes = getLDAPAttributes(c, request.getFields());
+                                        modifyRequest.addControl(PostReadRequestControl.newControl(false, attributes));
                                     }
                                     if (config.usePermissiveModify()) {
-                                        modifyRequest.addControl(PermissiveModifyRequestControl
-                                                .newControl(true));
+                                        modifyRequest.addControl(PermissiveModifyRequestControl.newControl(true));
                                     }
                                     addAssertionControl(modifyRequest, request.getRevision());
 
                                     /*
-                                     * Determine the set of changes that need to
-                                     * be performed.
+                                     * Determine the set of changes that need to be performed.
                                      */
-                                    attributeMapper.update(c, new JsonPointer(), entry, request
-                                            .getNewContent(),
+                                    attributeMapper.update(c, new JsonPointer(), entry, request.getNewContent(),
                                             new ResultHandler<List<Modification>>() {
                                                 @Override
-                                                public void handleError(
-                                                        final ResourceException error) {
+                                                public void handleError(final ResourceException error) {
                                                     h.handleError(error);
                                                 }
 
                                                 @Override
-                                                public void handleResult(
-                                                        final List<Modification> result) {
+                                                public void handleResult(final List<Modification> result) {
                                                     // Perform the modify operation.
                                                     if (result.isEmpty()) {
                                                         /*
-                                                         * No changes to be
-                                                         * performed, so just
-                                                         * return the entry that
-                                                         * we read.
+                                                         * No changes to be performed, so just return
+                                                         * the entry that we read.
                                                          */
                                                         adaptEntry(c, entry, h);
                                                     } else {
-                                                        modifyRequest.getModifications().addAll(
-                                                                result);
-                                                        c.getConnection().applyChangeAsync(
-                                                                modifyRequest, null,
-                                                                postUpdateHandler(c, h));
+                                                        modifyRequest.getModifications().addAll(result);
+                                                        c.getConnection().applyChangeAsync(modifyRequest)
+                                                                .onSuccess(postUpdateSuccessHandler(c, h))
+                                                                .onFailure(postUpdateFailureHandler(h));
                                                     }
                                                 }
                                             });
@@ -653,13 +621,17 @@
                                     h.handleError(asResourceException(e));
                                 }
                             }
+                        }).onFailure(new FailureHandler<ErrorResultException>() {
+                            @Override
+                            public void handleError(final ErrorResultException error) {
+                                h.handleError(asResourceException(error));
+                            }
                         });
             }
         });
     }
 
-    private void adaptEntry(final Context c, final Entry entry,
-            final ResultHandler<Resource> handler) {
+    private void adaptEntry(final Context c, final Entry entry, final ResultHandler<Resource> handler) {
         final String actualResourceId = nameStrategy.getResourceId(c, entry);
         final String revision = getRevisionFromEntry(entry);
         attributeMapper.read(c, new JsonPointer(), entry, transform(
@@ -695,13 +667,8 @@
                     // There's no point in doing a search because we already know the DN.
                     updateHandler.handleResult(searchRequest.getName());
                 } else {
-                    c.getConnection().searchSingleEntryAsync(searchRequest,
-                            new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
-                                @Override
-                                public void handleErrorResult(final ErrorResultException error) {
-                                    updateHandler.handleError(asResourceException(error));
-                                }
-
+                    c.getConnection().searchSingleEntryAsync(searchRequest)
+                            .onSuccess(new SuccessHandler<SearchResultEntry>() {
                                 @Override
                                 public void handleResult(final SearchResultEntry entry) {
                                     try {
@@ -714,6 +681,11 @@
                                         updateHandler.handleError(asResourceException(e));
                                     }
                                 }
+                            }).onFailure(new FailureHandler<ErrorResultException>() {
+                                @Override
+                                public void handleError(final ErrorResultException error) {
+                                    updateHandler.handleError(asResourceException(error));
+                                }
                             });
                 }
             }
@@ -727,21 +699,18 @@
         }
     }
 
-    private void ensureMVCCVersionMatches(final Entry entry, final String expectedRevision)
-            throws ResourceException {
+    private void ensureMVCCVersionMatches(final Entry entry, final String expectedRevision) throws ResourceException {
         if (expectedRevision != null) {
             ensureMVCCSupported();
             final String actualRevision = entry.parseAttribute(etagAttribute).asString();
             if (actualRevision == null) {
                 throw new PreconditionFailedException(i18n(
                         "The resource could not be accessed because it did not contain any "
-                                + "version information, when the version '%s' was expected",
-                        expectedRevision));
+                                + "version information, when the version '%s' was expected", expectedRevision));
             } else if (!expectedRevision.equals(actualRevision)) {
                 throw new PreconditionFailedException(i18n(
                         "The resource could not be accessed because the expected version '%s' "
-                                + "does not match the current version '%s'", expectedRevision,
-                        actualRevision));
+                                + "does not match the current version '%s'", expectedRevision, actualRevision));
             }
         }
     }
@@ -785,42 +754,55 @@
         return requestedLDAPAttributes.toArray(new String[requestedLDAPAttributes.size()]);
     }
 
-    private void getLDAPFilter(final Context c, final QueryFilter queryFilter,
-            final ResultHandler<Filter> h) {
+    private void getLDAPFilter(final Context c, final QueryFilter queryFilter, final ResultHandler<Filter> h) {
         final QueryFilterVisitor<Void, ResultHandler<Filter>> visitor =
                 new QueryFilterVisitor<Void, ResultHandler<Filter>>() {
                     @Override
-                    public Void visitAndFilter(final ResultHandler<Filter> p,
-                            final List<QueryFilter> subFilters) {
-                        final ResultHandler<Filter> handler =
-                                accumulate(subFilters.size(), transform(
-                                        new Function<List<Filter>, Filter, Void>() {
-                                            @Override
-                                            public Filter apply(final List<Filter> value,
-                                                    final Void p) {
-                                                // Check for unmapped filter components and optimize.
-                                                final Iterator<Filter> i = value.iterator();
-                                                while (i.hasNext()) {
-                                                    final Filter f = i.next();
-                                                    if (f == alwaysFalse()) {
-                                                        return alwaysFalse();
-                                                    } else if (f == alwaysTrue()) {
-                                                        i.remove();
-                                                    }
-                                                }
-                                                switch (value.size()) {
-                                                case 0:
-                                                    return alwaysTrue();
-                                                case 1:
-                                                    return value.get(0);
-                                                default:
-                                                    return Filter.and(value);
-                                                }
-                                            }
-                                        }, p));
+                    public Void visitAndFilter(final ResultHandler<Filter> p, final List<QueryFilter> subFilters) {
+                        List<Promise<Filter, ResourceException>> promises =
+                                new ArrayList<Promise<Filter, ResourceException>>(subFilters.size());
                         for (final QueryFilter subFilter : subFilters) {
+                            final ResultHandlerFromPromise<Filter> handler = new ResultHandlerFromPromise<Filter>();
                             subFilter.accept(this, handler);
+                            promises.add(handler.promise);
                         }
+
+                        Promises.when(promises)
+                                .then(new org.forgerock.util.promise.Function<List<Filter>, Filter,
+                                        ResourceException>() {
+                                    @Override
+                                    public Filter apply(final List<Filter> value) {
+                                        // Check for unmapped filter components and optimize.
+                                        final Iterator<Filter> i = value.iterator();
+                                        while (i.hasNext()) {
+                                            final Filter f = i.next();
+                                            if (f == alwaysFalse()) {
+                                                return alwaysFalse();
+                                            } else if (f == alwaysTrue()) {
+                                                i.remove();
+                                            }
+                                        }
+                                        switch (value.size()) {
+                                        case 0:
+                                            return alwaysTrue();
+                                        case 1:
+                                            return value.get(0);
+                                        default:
+                                            return Filter.and(value);
+                                        }
+                                    }
+                                }).onSuccess(new SuccessHandler<Filter>() {
+                                    @Override
+                                    public void handleResult(Filter result) {
+                                        p.handleResult(result);
+                                    }
+                                }).onFailure(new FailureHandler<ResourceException>() {
+                                    @Override
+                                    public void handleError(ResourceException error) {
+                                        p.handleError(error);
+                                    }
+                                });
+
                         return null;
                     }
 
@@ -907,37 +889,51 @@
                     }
 
                     @Override
-                    public Void visitOrFilter(final ResultHandler<Filter> p,
-                            final List<QueryFilter> subFilters) {
-                        final ResultHandler<Filter> handler =
-                                accumulate(subFilters.size(), transform(
-                                        new Function<List<Filter>, Filter, Void>() {
-                                            @Override
-                                            public Filter apply(final List<Filter> value,
-                                                    final Void p) {
-                                                // Check for unmapped filter components and optimize.
-                                                final Iterator<Filter> i = value.iterator();
-                                                while (i.hasNext()) {
-                                                    final Filter f = i.next();
-                                                    if (f == alwaysFalse()) {
-                                                        i.remove();
-                                                    } else if (f == alwaysTrue()) {
-                                                        return alwaysTrue();
-                                                    }
-                                                }
-                                                switch (value.size()) {
-                                                case 0:
-                                                    return alwaysFalse();
-                                                case 1:
-                                                    return value.get(0);
-                                                default:
-                                                    return Filter.or(value);
-                                                }
-                                            }
-                                        }, p));
+                    public Void visitOrFilter(final ResultHandler<Filter> p, final List<QueryFilter> subFilters) {
+                        List<Promise<Filter, ResourceException>> promises =
+                                new ArrayList<Promise<Filter, ResourceException>>(subFilters.size());
                         for (final QueryFilter subFilter : subFilters) {
+                            final ResultHandlerFromPromise<Filter> handler = new ResultHandlerFromPromise<Filter>();
                             subFilter.accept(this, handler);
+                            promises.add(handler.promise);
                         }
+
+                        Promises.when(promises)
+                                .then(new org.forgerock.util.promise.Function<List<Filter>, Filter,
+                                        ResourceException>() {
+                                    @Override
+                                    public Filter apply(final List<Filter> value) {
+                                        // Check for unmapped filter components and optimize.
+                                        final Iterator<Filter> i = value.iterator();
+                                        while (i.hasNext()) {
+                                            final Filter f = i.next();
+                                            if (f == alwaysFalse()) {
+                                                i.remove();
+                                            } else if (f == alwaysTrue()) {
+                                                return alwaysTrue();
+                                            }
+                                        }
+                                        switch (value.size()) {
+                                        case 0:
+                                            return alwaysFalse();
+                                        case 1:
+                                            return value.get(0);
+                                        default:
+                                            return Filter.or(value);
+                                        }
+                                    }
+                                }).onSuccess(new SuccessHandler<Filter>() {
+                                    @Override
+                                    public void handleResult(Filter result) {
+                                        p.handleResult(result);
+                                    }
+                                }).onFailure(new FailureHandler<ResourceException>() {
+                                    @Override
+                                    public void handleError(ResourceException error) {
+                                        p.handleError(error);
+                                    }
+                                });
+
                         return null;
                     }
 
@@ -959,8 +955,7 @@
 
                 };
         /*
-         * Note that the returned LDAP filter may be null if it could not be
-         * mapped by any attribute mappers.
+         * Note that the returned LDAP filter may be null if it could not be mapped by any attribute mappers.
          */
         queryFilter.accept(visitor, h);
     }
@@ -969,14 +964,9 @@
         return etagAttribute != null ? entry.parseAttribute(etagAttribute).asString() : null;
     }
 
-    private org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry> postEmptyPatchHandler(
-            final Context c, final PatchRequest request, final ResultHandler<Resource> h) {
-        return new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
-            @Override
-            public void handleErrorResult(final ErrorResultException error) {
-                h.handleError(asResourceException(error));
-            }
-
+    private SuccessHandler<SearchResultEntry> postEmptyPatchSuccessHandler(final Context c,
+            final PatchRequest request, final ResultHandler<Resource> h) {
+        return new SuccessHandler<SearchResultEntry>() {
             @Override
             public void handleResult(final SearchResultEntry entry) {
                 try {
@@ -990,29 +980,30 @@
         };
     }
 
-    private org.forgerock.opendj.ldap.ResultHandler<Result> postUpdateHandler(final Context c,
-            final ResultHandler<Resource> handler) {
-        // The handler which will be invoked for the LDAP add result.
-        return new org.forgerock.opendj.ldap.ResultHandler<Result>() {
+    private FailureHandler<ErrorResultException> postEmptyPatchFailureHandler(final ResultHandler<Resource> h) {
+        return new FailureHandler<ErrorResultException>() {
             @Override
-            public void handleErrorResult(final ErrorResultException error) {
-                handler.handleError(asResourceException(error));
+            public void handleError(final ErrorResultException error) {
+                h.handleError(asResourceException(error));
             }
+        };
+    }
 
+    private SuccessHandler<Result> postUpdateSuccessHandler(final Context c, final ResultHandler<Resource> handler) {
+        // The handler which will be invoked for the LDAP add result.
+        return new SuccessHandler<Result>() {
             @Override
             public void handleResult(final Result result) {
                 // FIXME: handle USE_SEARCH policy.
                 Entry entry;
                 try {
                     final PostReadResponseControl postReadControl =
-                            result.getControl(PostReadResponseControl.DECODER, config
-                                    .decodeOptions());
+                        result.getControl(PostReadResponseControl.DECODER, config.decodeOptions());
                     if (postReadControl != null) {
                         entry = postReadControl.getEntry();
                     } else {
                         final PreReadResponseControl preReadControl =
-                                result.getControl(PreReadResponseControl.DECODER, config
-                                        .decodeOptions());
+                            result.getControl(PreReadResponseControl.DECODER, config.decodeOptions());
                         if (preReadControl != null) {
                             entry = preReadControl.getEntry();
                         } else {
@@ -1026,8 +1017,7 @@
                 if (entry != null) {
                     adaptEntry(c, entry, handler);
                 } else {
-                    final Resource resource =
-                            new Resource(null, null, new JsonValue(Collections.emptyMap()));
+                    final Resource resource = new Resource(null, null, new JsonValue(Collections.emptyMap()));
                     handler.handleResult(resource);
                 }
             }
@@ -1035,6 +1025,16 @@
         };
     }
 
+    private FailureHandler<ErrorResultException> postUpdateFailureHandler(final ResultHandler<Resource> handler) {
+        // The handler which will be invoked for the LDAP add result.
+        return new FailureHandler<ErrorResultException>() {
+            @Override
+            public void handleError(final ErrorResultException error) {
+                handler.handleError(asResourceException(error));
+            }
+        };
+    }
+
     private QueryResultHandler wrap(final Context c, final QueryResultHandler handler) {
         return new QueryResultHandler() {
             @Override
diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java
index 230359a..e8b21b1 100644
--- a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java
+++ b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java
@@ -11,18 +11,10 @@
  * Header, with the fields enclosed by brackets [] replaced by your own identifying
  * information: "Portions Copyright [year] [name of copyright owner]".
  *
- * Copyright 2012-2013 ForgeRock AS.
+ * Copyright 2012-2014 ForgeRock AS.
  */
 package org.forgerock.opendj.rest2ldap;
 
-import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
-import static org.forgerock.opendj.ldap.requests.Requests.newSearchRequest;
-import static org.forgerock.opendj.rest2ldap.Rest2LDAP.asResourceException;
-import static org.forgerock.opendj.rest2ldap.Utils.accumulate;
-import static org.forgerock.opendj.rest2ldap.Utils.ensureNotNull;
-import static org.forgerock.opendj.rest2ldap.Utils.i18n;
-import static org.forgerock.opendj.rest2ldap.Utils.transform;
-
 import java.util.ArrayList;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
@@ -54,13 +46,19 @@
 import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.SuccessHandler;
+
+import static org.forgerock.opendj.ldap.ErrorResultException.*;
+import static org.forgerock.opendj.ldap.requests.Requests.*;
+import static org.forgerock.opendj.rest2ldap.Rest2LDAP.*;
+import static org.forgerock.opendj.rest2ldap.Utils.*;
 
 /**
  * An attribute mapper which provides a mapping from a JSON value to a single DN
  * valued LDAP attribute.
  */
-public final class ReferenceAttributeMapper extends
-        AbstractLDAPAttributeMapper<ReferenceAttributeMapper> {
+public final class ReferenceAttributeMapper extends AbstractLDAPAttributeMapper<ReferenceAttributeMapper> {
     /**
      * The maximum number of candidate references to allow in search filters.
      */
@@ -73,7 +71,7 @@
     private SearchScope scope = SearchScope.WHOLE_SUBTREE;
 
     ReferenceAttributeMapper(final AttributeDescription ldapAttributeName, final DN baseDN,
-            final AttributeDescription primaryKey, final AttributeMapper mapper) {
+        final AttributeDescription primaryKey, final AttributeMapper mapper) {
         super(ldapAttributeName);
         this.baseDN = baseDN;
         this.primaryKey = primaryKey;
@@ -127,65 +125,65 @@
     }
 
     @Override
-    void getLDAPFilter(final Context c, final JsonPointer path, final JsonPointer subPath,
-            final FilterType type, final String operator, final Object valueAssertion,
-            final ResultHandler<Filter> h) {
+    void getLDAPFilter(final Context c, final JsonPointer path, final JsonPointer subPath, final FilterType type,
+        final String operator, final Object valueAssertion, final ResultHandler<Filter> h) {
         // Construct a filter which can be used to find referenced resources.
-        mapper.getLDAPFilter(c, path, subPath, type, operator, valueAssertion,
-                new ResultHandler<Filter>() {
+        mapper.getLDAPFilter(c, path, subPath, type, operator, valueAssertion, new ResultHandler<Filter>() {
+            @Override
+            public void handleError(final ResourceException error) {
+                h.handleError(error); // Propagate.
+            }
+
+            @Override
+            public void handleResult(final Filter result) {
+                // Search for all referenced entries and construct a filter.
+                final SearchRequest request = createSearchRequest(result);
+                final List<Filter> subFilters = new LinkedList<Filter>();
+
+                final FailureHandler<ErrorResultException> failureHandler =
+                    new FailureHandler<ErrorResultException>() {
+                        @Override
+                        public void handleError(ErrorResultException error) {
+                            h.handleError(asResourceException(error)); // Propagate.
+                        }
+                    };
+
+                c.getConnection().searchAsync(request, new SearchResultHandler() {
                     @Override
-                    public void handleError(final ResourceException error) {
-                        h.handleError(error); // Propagate.
+                    public boolean handleEntry(final SearchResultEntry entry) {
+                        if (subFilters.size() < SEARCH_MAX_CANDIDATES) {
+                            subFilters.add(Filter.equality(ldapAttributeName.toString(), entry.getName()));
+                            return true;
+                        } else {
+                            // No point in continuing - maximum candidates reached.
+                            return false;
+                        }
                     }
 
                     @Override
-                    public void handleResult(final Filter result) {
-                        // Search for all referenced entries and construct a filter.
-                        final SearchRequest request = createSearchRequest(result);
-                        c.getConnection().searchAsync(request, null, new SearchResultHandler() {
-                            final List<Filter> subFilters = new LinkedList<Filter>();
-
-                            @Override
-                            public boolean handleEntry(final SearchResultEntry entry) {
-                                if (subFilters.size() < SEARCH_MAX_CANDIDATES) {
-                                    subFilters.add(Filter.equality(ldapAttributeName.toString(),
-                                            entry.getName()));
-                                    return true;
-                                } else {
-                                    // No point in continuing - maximum candidates reached.
-                                    return false;
-                                }
-                            }
-
-                            @Override
-                            public void handleErrorResult(final ErrorResultException error) {
-                                h.handleError(asResourceException(error)); // Propagate.
-                            }
-
-                            @Override
-                            public boolean handleReference(final SearchResultReference reference) {
-                                // Ignore references.
-                                return true;
-                            }
-
-                            @Override
-                            public void handleResult(final Result result) {
-                                if (subFilters.size() >= SEARCH_MAX_CANDIDATES) {
-                                    handleErrorResult(newErrorResult(ResultCode.ADMIN_LIMIT_EXCEEDED));
-                                } else if (subFilters.size() == 1) {
-                                    h.handleResult(subFilters.get(0));
-                                } else {
-                                    h.handleResult(Filter.or(subFilters));
-                                }
-                            }
-                        });
+                    public boolean handleReference(final SearchResultReference reference) {
+                        // Ignore references.
+                        return true;
                     }
-                });
+                }).onSuccess(new SuccessHandler<Result>() {
+                    @Override
+                    public void handleResult(Result result) {
+                        if (subFilters.size() >= SEARCH_MAX_CANDIDATES) {
+                            failureHandler.handleError(newErrorResult(ResultCode.ADMIN_LIMIT_EXCEEDED));
+                        } else if (subFilters.size() == 1) {
+                            h.handleResult(subFilters.get(0));
+                        } else {
+                            h.handleResult(Filter.or(subFilters));
+                        }
+                    }
+                }).onFailure(failureHandler);
+            }
+        });
     }
 
     @Override
-    void getNewLDAPAttributes(final Context c, final JsonPointer path,
-            final List<Object> newValues, final ResultHandler<Attribute> h) {
+    void getNewLDAPAttributes(final Context c, final JsonPointer path, final List<Object> newValues,
+        final ResultHandler<Attribute> h) {
         /*
          * For each value use the subordinate mapper to obtain the LDAP primary
          * key, the perform a search for each one to find the corresponding
@@ -193,8 +191,7 @@
          */
         final Attribute newLDAPAttribute = new LinkedAttribute(ldapAttributeName);
         final AtomicInteger pendingSearches = new AtomicInteger(newValues.size());
-        final AtomicReference<ResourceException> exception =
-                new AtomicReference<ResourceException>();
+        final AtomicReference<ResourceException> exception = new AtomicReference<ResourceException>();
 
         for (final Object value : newValues) {
             mapper.create(c, path, new JsonValue(value), new ResultHandler<List<Attribute>>() {
@@ -216,17 +213,15 @@
 
                     if (primaryKeyAttribute == null || primaryKeyAttribute.isEmpty()) {
                         h.handleError(new BadRequestException(i18n(
-                                "The request cannot be processed because the reference "
-                                        + "field '%s' contains a value which does not contain "
-                                        + "a primary key", path)));
+                            "The request cannot be processed because the reference "
+                                + "field '%s' contains a value which does not contain " + "a primary key", path)));
                         return;
                     }
 
                     if (primaryKeyAttribute.size() > 1) {
                         h.handleError(new BadRequestException(i18n(
-                                "The request cannot be processed because the reference "
-                                        + "field '%s' contains a value which contains multiple "
-                                        + "primary keys", path)));
+                            "The request cannot be processed because the reference "
+                                + "field '%s' contains a value which contains multiple " + "primary keys", path)));
                         return;
                     }
 
@@ -234,45 +229,39 @@
                     final ByteString primaryKeyValue = primaryKeyAttribute.firstValue();
                     final Filter filter = Filter.equality(primaryKey.toString(), primaryKeyValue);
                     final SearchRequest search = createSearchRequest(filter);
-                    c.getConnection().searchSingleEntryAsync(search,
-                            new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
+                    c.getConnection().searchSingleEntryAsync(search).onSuccess(new SuccessHandler<SearchResultEntry>() {
+                        @Override
+                        public void handleResult(final SearchResultEntry result) {
+                            synchronized (newLDAPAttribute) {
+                                newLDAPAttribute.add(result.getName());
+                            }
+                            completeIfNecessary();
+                        }
+                    }).onFailure(new FailureHandler<ErrorResultException>() {
+                        @Override
+                        public void handleError(final ErrorResultException error) {
+                            ResourceException re;
+                            try {
+                                throw error;
+                            } catch (final EntryNotFoundException e) {
+                                re =
+                                    new BadRequestException(i18n("The request cannot be processed "
+                                        + "because the resource '%s' " + "referenced in field '%s' does "
+                                        + "not exist", primaryKeyValue.toString(), path));
+                            } catch (final MultipleEntriesFoundException e) {
+                                re =
+                                    new BadRequestException(i18n(
+                                        "The request cannot be processed " + "because the resource '%s' "
+                                            + "referenced in field '%s' is " + "ambiguous",
+                                        primaryKeyValue.toString(), path));
+                            } catch (final ErrorResultException e) {
+                                re = asResourceException(e);
+                            }
+                            exception.compareAndSet(null, re);
+                            completeIfNecessary();
+                        }
 
-                                @Override
-                                public void handleErrorResult(final ErrorResultException error) {
-                                    ResourceException re;
-                                    try {
-                                        throw error;
-                                    } catch (final EntryNotFoundException e) {
-                                        re =
-                                                new BadRequestException(i18n(
-                                                        "The request cannot be processed "
-                                                                + "because the resource '%s' "
-                                                                + "referenced in field '%s' does "
-                                                                + "not exist", primaryKeyValue
-                                                                .toString(), path));
-                                    } catch (final MultipleEntriesFoundException e) {
-                                        re =
-                                                new BadRequestException(i18n(
-                                                        "The request cannot be processed "
-                                                                + "because the resource '%s' "
-                                                                + "referenced in field '%s' is "
-                                                                + "ambiguous", primaryKeyValue
-                                                                .toString(), path));
-                                    } catch (final ErrorResultException e) {
-                                        re = asResourceException(e);
-                                    }
-                                    exception.compareAndSet(null, re);
-                                    completeIfNecessary();
-                                }
-
-                                @Override
-                                public void handleResult(final SearchResultEntry result) {
-                                    synchronized (newLDAPAttribute) {
-                                        newLDAPAttribute.add(result.getName());
-                                    }
-                                    completeIfNecessary();
-                                }
-                            });
+                    });
                 }
 
                 private void completeIfNecessary() {
@@ -294,8 +283,7 @@
     }
 
     @Override
-    void read(final Context c, final JsonPointer path, final Entry e,
-            final ResultHandler<JsonValue> h) {
+    void read(final Context c, final JsonPointer path, final Entry e, final ResultHandler<JsonValue> h) {
         final Attribute attribute = e.getAttribute(ldapAttributeName);
         if (attribute == null || attribute.isEmpty()) {
             h.handleResult(null);
@@ -309,30 +297,27 @@
             }
         } else {
             try {
-                final Set<DN> dns =
-                        attribute.parse().usingSchema(c.getConfig().schema()).asSetOfDN();
+                final Set<DN> dns = attribute.parse().usingSchema(c.getConfig().schema()).asSetOfDN();
                 final ResultHandler<JsonValue> handler =
-                        accumulate(dns.size(), transform(
-                                new Function<List<JsonValue>, JsonValue, Void>() {
-                                    @Override
-                                    public JsonValue apply(final List<JsonValue> value, final Void p) {
-                                        if (value.isEmpty()) {
-                                            /*
-                                             * No values, so omit the entire
-                                             * JSON object from the resource.
-                                             */
-                                            return null;
-                                        } else {
-                                            // Combine values into a single JSON array.
-                                            final List<Object> result =
-                                                    new ArrayList<Object>(value.size());
-                                            for (final JsonValue e : value) {
-                                                result.add(e.getObject());
-                                            }
-                                            return new JsonValue(result);
-                                        }
-                                    }
-                                }, h));
+                    accumulate(dns.size(), transform(new Function<List<JsonValue>, JsonValue, Void>() {
+                        @Override
+                        public JsonValue apply(final List<JsonValue> value, final Void p) {
+                            if (value.isEmpty()) {
+                                /*
+                                 * No values, so omit the entire JSON object
+                                 * from the resource.
+                                 */
+                                return null;
+                            } else {
+                                // Combine values into a single JSON array.
+                                final List<Object> result = new ArrayList<Object>(value.size());
+                                for (final JsonValue e : value) {
+                                    result.add(e.getObject());
+                                }
+                                return new JsonValue(result);
+                            }
+                        }
+                    }, h));
                 for (final DN dn : dns) {
                     readEntry(c, path, dn, handler);
                 }
@@ -349,14 +334,18 @@
     }
 
     private void readEntry(final Context c, final JsonPointer path, final DN dn,
-            final ResultHandler<JsonValue> handler) {
+        final ResultHandler<JsonValue> handler) {
         final Set<String> requestedLDAPAttributes = new LinkedHashSet<String>();
         mapper.getLDAPAttributes(c, path, new JsonPointer(), requestedLDAPAttributes);
-        c.getConnection().readEntryAsync(dn, requestedLDAPAttributes,
-                new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
-
+        c.getConnection().readEntryAsync(dn, requestedLDAPAttributes)
+                .onSuccess(new SuccessHandler<SearchResultEntry>() {
                     @Override
-                    public void handleErrorResult(final ErrorResultException error) {
+                    public void handleResult(final SearchResultEntry result) {
+                        mapper.read(c, path, result, handler);
+                    }
+                }).onFailure(new FailureHandler<ErrorResultException>() {
+                    @Override
+                    public void handleError(final ErrorResultException error) {
                         if (!(error instanceof EntryNotFoundException)) {
                             handler.handleError(asResourceException(error));
                         } else {
@@ -367,11 +356,6 @@
                             handler.handleResult(null);
                         }
                     }
-
-                    @Override
-                    public void handleResult(final SearchResultEntry result) {
-                        mapper.read(c, path, result, handler);
-                    }
                 });
     }
 
diff --git a/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java b/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java
index ce3b916..16b4f14 100644
--- a/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java
+++ b/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java
@@ -11,7 +11,7 @@
  * Header, with the fields enclosed by brackets [] replaced by your own identifying
  * information: "Portions copyright [year] [name of copyright owner]".
  *
- * Copyright 2013 ForgeRock AS.
+ * Copyright 2013-2014 ForgeRock AS.
  */
 package org.forgerock.opendj.rest2ldap;
 
@@ -779,11 +779,11 @@
 
             @Override
             public void handleSearch(RequestContext requestContext, SearchRequest request,
-                    IntermediateResponseHandler intermediateResponseHandler,
-                    SearchResultHandler resultHandler) {
+                IntermediateResponseHandler intermediateResponseHandler, SearchResultHandler entryHandler,
+                ResultHandler<Result> resultHandler) {
                 requests.add(request);
-                handler.handleSearch(requestContext, request, intermediateResponseHandler,
-                        resultHandler);
+                handler.handleSearch(requestContext, request, intermediateResponseHandler, entryHandler,
+                    resultHandler);
             }
 
         };
diff --git a/opendj-server2x-adapter/src/main/java/org/forgerock/opendj/adapter/server2x/Adapters.java b/opendj-server2x-adapter/src/main/java/org/forgerock/opendj/adapter/server2x/Adapters.java
index c8dadfb..52d100f 100644
--- a/opendj-server2x-adapter/src/main/java/org/forgerock/opendj/adapter/server2x/Adapters.java
+++ b/opendj-server2x-adapter/src/main/java/org/forgerock/opendj/adapter/server2x/Adapters.java
@@ -21,7 +21,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2013 ForgeRock AS.
+ *      Copyright 2013-2014 ForgeRock AS.
  */
 package org.forgerock.opendj.adapter.server2x;
 
@@ -37,10 +37,8 @@
 import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.DecodeOptions;
 import org.forgerock.opendj.ldap.ErrorResultException;
-import org.forgerock.opendj.ldap.FutureResult;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.SearchResultHandler;
 import org.forgerock.opendj.ldap.controls.Control;
 import org.forgerock.opendj.ldap.requests.AddRequest;
@@ -62,6 +60,7 @@
 import org.forgerock.opendj.ldap.responses.GenericExtendedResult;
 import org.forgerock.opendj.ldap.responses.Responses;
 import org.forgerock.opendj.ldap.responses.Result;
+import org.forgerock.util.promise.Promise;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.BindOperation;
 import org.opends.server.core.CompareOperation;
@@ -78,10 +77,9 @@
 import org.opends.server.types.SearchResultEntry;
 import org.opends.server.types.SearchResultReference;
 
-import com.forgerock.opendj.util.CompletedFutureResult;
-
 import static org.forgerock.opendj.adapter.server2x.Converters.*;
 import static org.forgerock.opendj.ldap.ByteString.*;
+import static org.forgerock.util.promise.Promises.*;
 
 /**
  * This class provides a connection factory and an adapter for the OpenDJ 2.x
@@ -141,8 +139,7 @@
      * @return A new SDK connection factory.
      */
     public static ConnectionFactory newConnectionFactory(final InternalClientConnection icc) {
-        final Connection connection = newConnection(icc);
-        ConnectionFactory factory = new ConnectionFactory() {
+        return new ConnectionFactory() {
 
             @Override
             public void close() {
@@ -150,20 +147,16 @@
             }
 
             @Override
-            public FutureResult<Connection> getConnectionAsync(
-                    ResultHandler<? super Connection> handler) {
-                if (handler != null) {
-                    handler.handleResult(connection);
-                } // TODO change the path...
-                return new CompletedFutureResult<Connection>(connection);
+            public Promise<Connection, ErrorResultException> getConnectionAsync() {
+                // TODO change the path...
+                return newSuccessfulPromise(newConnection(icc));
             }
 
             @Override
             public Connection getConnection() throws ErrorResultException {
-                return connection;
+                return newConnection(icc);
             }
         };
-        return factory;
     }
 
     /**
diff --git a/opendj-server3x-adapter/src/main/java/org/forgerock/opendj/adapter/server3x/Adapters.java b/opendj-server3x-adapter/src/main/java/org/forgerock/opendj/adapter/server3x/Adapters.java
index f6abb09..e5b62bb 100644
--- a/opendj-server3x-adapter/src/main/java/org/forgerock/opendj/adapter/server3x/Adapters.java
+++ b/opendj-server3x-adapter/src/main/java/org/forgerock/opendj/adapter/server3x/Adapters.java
@@ -38,10 +38,8 @@
 import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.DecodeOptions;
 import org.forgerock.opendj.ldap.ErrorResultException;
-import org.forgerock.opendj.ldap.FutureResult;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.SearchResultHandler;
 import org.forgerock.opendj.ldap.controls.Control;
 import org.forgerock.opendj.ldap.requests.AddRequest;
@@ -63,6 +61,7 @@
 import org.forgerock.opendj.ldap.responses.GenericExtendedResult;
 import org.forgerock.opendj.ldap.responses.Responses;
 import org.forgerock.opendj.ldap.responses.Result;
+import org.forgerock.util.promise.Promise;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.BindOperation;
 import org.opends.server.core.CompareOperation;
@@ -78,10 +77,9 @@
 import org.opends.server.types.SearchResultEntry;
 import org.opends.server.types.SearchResultReference;
 
-import com.forgerock.opendj.util.CompletedFutureResult;
-
 import static org.forgerock.opendj.adapter.server3x.Converters.*;
 import static org.forgerock.opendj.ldap.ByteString.*;
+import static org.forgerock.util.promise.Promises.*;
 
 /**
  * This class provides a connection factory and an adapter for the OpenDJ 2.x
@@ -141,8 +139,7 @@
      * @return A new SDK connection factory.
      */
     public static ConnectionFactory newConnectionFactory(final InternalClientConnection icc) {
-        final Connection connection = newConnection(icc);
-        ConnectionFactory factory = new ConnectionFactory() {
+        return new ConnectionFactory() {
 
             @Override
             public void close() {
@@ -150,20 +147,16 @@
             }
 
             @Override
-            public FutureResult<Connection> getConnectionAsync(
-                    ResultHandler<? super Connection> handler) {
-                if (handler != null) {
-                    handler.handleResult(connection);
-                } // TODO change the path...
-                return new CompletedFutureResult<Connection>(connection);
+            public Promise<Connection, ErrorResultException> getConnectionAsync() {
+                // TODO change the path...
+                return newSuccessfulPromise(newConnection(icc));
             }
 
             @Override
             public Connection getConnection() throws ErrorResultException {
-                return connection;
+                return newConnection(icc);
             }
         };
-        return factory;
     }
 
     /**
diff --git a/pom.xml b/pom.xml
index 6bc1ffc..2c814859 100644
--- a/pom.xml
+++ b/pom.xml
@@ -438,7 +438,7 @@
       <dependency>
         <groupId>org.forgerock.commons</groupId>
         <artifactId>forgerock-util</artifactId>
-        <version>1.3.0-SNAPSHOT</version>
+        <version>1.3.5-SNAPSHOT</version>
       </dependency>
     </dependencies>
   </dependencyManagement>

--
Gitblit v1.10.0