From 600f6cb4356f2355a004604b9353505f0c2f7f49 Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Mon, 29 Sep 2014 10:04:51 +0000
Subject: [PATCH] OPENDJ-1536 OPENDJ-1285 Rename FutureResult classes hierarchy in the SDK to enhance code consistency

---
 opendj-core/src/test/java/org/forgerock/opendj/ldap/TestCaseUtils.java                                  |    4 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/Connection.java                                     |  112 
 opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java                   |   21 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/ExtendedResultLdapPromiseImpl.java              |   99 +
 opendj-core/clirr-ignored-api-changes.xml                                                               |   32 
 opendj-core/src/test/java/org/forgerock/opendj/ldif/ConnectionChangeRecordWriterTestCase.java           |    2 
 opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithmTestCase.java         |    8 
 opendj-server/src/main/java/org/forgerock/opendj/server/core/ExportableDataProvider.java                |    6 
 opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java             |   35 
 opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactoryTestCase.java     |   29 
 opendj-config/src/test/java/org/forgerock/opendj/config/client/ldap/LDAPClientTest.java                 |    5 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/BindResultLdapPromiseImpl.java                  |   51 
 opendj-core/src/main/java/org/forgerock/opendj/ldif/ConnectionEntryReader.java                          |   13 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/ResultLdapPromiseImpl.java                      |  191 +++
 opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/SearchResultLdapPromiseImpl.java                |   59 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractSynchronousConnection.java                  |   28 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/ResultHandler.java                                  |    2 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/responses/AbstractExtendedResultDecoder.java        |    6 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/RootDSE.java                                        |   15 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnection.java                 |    6 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/LdapException.java                                  |   40 
 opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java                    |  218 +-
 opendj-core/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java                                  |   41 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/Entries.java                                        |   16 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java          |   10 
 opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java              |   34 
 opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java             |    2 
 opendj-core/src/test/java/org/forgerock/opendj/ldif/ConnectionEntryReaderTestCase.java                  |   30 
 opendj-core/src/test/java/org/forgerock/opendj/ldap/ConnectionPoolTestCase.java                         |   33 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactory.java                     |  637 ++++------
 opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java                                     |   23 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactoryAdapter.java                   |   20 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/TransportProvider.java                          |    3 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java                 |    4 
 opendj-server2x-adapter/src/main/java/org/forgerock/opendj/adapter/server2x/Converters.java             |    4 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/InternalConnectionFactory.java                      |    4 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java            |   10 
 opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java                              |   45 
 opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPClientFilter.java                         |  227 +--
 opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ProxyBackend.java                  |    6 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java                                  |   25 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/LdapPromise.java                                    |   75 +
 opendj-rest2ldap-servlet/src/main/java/org/forgerock/opendj/rest2ldap/servlet/Rest2LDAPAuthnFilter.java |    4 
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java                          |    4 
 opendj-core/src/test/java/org/forgerock/opendj/ldap/spi/ConnectionStateTest.java                        |    6 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractConnection.java                             |   69 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/InternalConnection.java                             |  128 -
 opendj-server/src/main/java/org/forgerock/opendj/server/core/ImportableDataProvider.java                |    6 
 opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnectionTestCase.java         |   53 
 opendj-server3x-adapter/src/main/java/org/forgerock/opendj/adapter/server3x/Adapters.java               |    8 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java                           |   36 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java           |    6 
 opendj-core/src/test/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactoryTestCase.java             |   56 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromiseImpl.java                            |   92 +
 opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaTestCase.java                          |   19 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/CachedConnectionPool.java                           |  117 -
 opendj-server2x-adapter/src/main/java/org/forgerock/opendj/adapter/server2x/Adapters.java               |    8 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java              |    4 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromiseWrapper.java                         |  184 +++
 opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/ConnectionFactoryTestCase.java                |   25 
 opendj-server3x-adapter/src/main/java/org/forgerock/opendj/adapter/server3x/Converters.java             |    4 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java             |   20 
 opendj-core/src/test/java/org/forgerock/opendj/ldap/spi/BasicLDAPConnectionFactory.java                 |    2 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/AuthenticatedConnectionFactory.java                 |    2 
 opendj-server/src/main/java/org/forgerock/opendj/server/core/ArchivableDataProvider.java                |   10 
 /dev/null                                                                                               |   92 -
 opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractConnectionWrapper.java                      |   42 
 opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java       |    2 
 opendj-cli/src/main/java/com/forgerock/opendj/cli/AuthenticatedConnectionFactory.java                   |   10 
 opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java                   |   12 
 opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyTransportProvider.java                 |    3 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromises.java                               |  273 ++++
 opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/EntrySchemaCheckingTestCase.java             |    3 
 73 files changed, 2,038 insertions(+), 1,493 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 6b2e17f..db64344 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
@@ -32,7 +32,7 @@
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.ConnectionFactory;
 import org.forgerock.opendj.ldap.LdapException;
-import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.requests.BindRequest;
 import org.forgerock.opendj.ldap.responses.BindResult;
@@ -99,7 +99,7 @@
 
         /** {@inheritDoc} */
         @Override
-        public FutureResult<BindResult> bindAsync(BindRequest request,
+        public LdapPromise<BindResult> bindAsync(BindRequest request,
             IntermediateResponseHandler intermediateResponseHandler) {
             throw new UnsupportedOperationException();
         }
@@ -120,19 +120,19 @@
          * associated with this connection. If re-authentication fails for some
          * reason then this connection will be automatically closed.
          *
-         * @return A future representing the result of the operation.
+         * @return A promise representing the result of the operation.
          * @throws UnsupportedOperationException
          *             If this connection does not support rebind operations.
          * @throws IllegalStateException
          *             If this connection has already been closed, i.e. if
          *             {@code isClosed() == true}.
          */
-        public FutureResult<BindResult> rebindAsync() {
+        public LdapPromise<BindResult> rebindAsync() {
             if (request == null) {
                 throw new UnsupportedOperationException();
             }
 
-            return (FutureResult<BindResult>) connection.bindAsync(request)
+            return (LdapPromise<BindResult>) connection.bindAsync(request)
                     .onSuccess(new SuccessHandler<BindResult>() {
                         @Override
                         public void handleResult(final BindResult result) {
diff --git a/opendj-config/src/test/java/org/forgerock/opendj/config/client/ldap/LDAPClientTest.java b/opendj-config/src/test/java/org/forgerock/opendj/config/client/ldap/LDAPClientTest.java
index 4ec05aa..6b15ab3 100644
--- a/opendj-config/src/test/java/org/forgerock/opendj/config/client/ldap/LDAPClientTest.java
+++ b/opendj-config/src/test/java/org/forgerock/opendj/config/client/ldap/LDAPClientTest.java
@@ -28,6 +28,7 @@
 
 import static org.fest.assertions.Assertions.assertThat;
 import static org.forgerock.opendj.ldap.Connections.newInternalConnection;
+import static org.forgerock.opendj.ldap.LdapException.*;
 
 import java.util.Arrays;
 import java.util.Collections;
@@ -271,7 +272,7 @@
         Connection c = new AbstractConnectionWrapper<Connection>(newInternalConnection(backend)) {
             @Override
             public Result add(Entry entry) throws LdapException {
-                throw LdapException.newErrorResult(resultCodeOfThrownException);
+                throw newLdapException(resultCodeOfThrownException);
             }
         };
         ManagementContext ctx =
@@ -387,7 +388,7 @@
         Connection c = new AbstractConnectionWrapper<Connection>(newInternalConnection(backend)) {
             @Override
             public SearchResultEntry readEntry(DN name, String... attributeDescriptions) throws LdapException {
-                throw LdapException.newErrorResult(resultCodeOfThrownException);
+                throw newLdapException(resultCodeOfThrownException);
             }
         };
         ManagementContext ctx =
diff --git a/opendj-core/clirr-ignored-api-changes.xml b/opendj-core/clirr-ignored-api-changes.xml
index af48c4f..a45b6c7 100644
--- a/opendj-core/clirr-ignored-api-changes.xml
+++ b/opendj-core/clirr-ignored-api-changes.xml
@@ -307,4 +307,36 @@
     <differenceType>8001</differenceType>
     <justification>OPENDJ-1536 Rename FutureResult and ErrorResultException classes hierarchy in the SDK to enhance code consistency</justification>
   </difference>
+  <difference>
+    <className>org/forgerock/opendj/ldap/*Connection*</className>
+    <differenceType>7006</differenceType>
+    <method>*Async*</method>
+    <to>org.forgerock.opendj.ldap.LdapPromise</to>
+    <justification>OPENDJ-1536 Rename FutureResult and ErrorResultException classes hierarchy in the SDK to enhance code consistency</justification>
+  </difference>
+  <difference>
+    <className>org/forgerock/opendj/ldap/RootDSE</className>
+    <differenceType>7006</differenceType>
+    <method>*Async*</method>
+    <to>org.forgerock.opendj.ldap.LdapPromise</to>
+    <justification>OPENDJ-1536 Rename FutureResult and ErrorResultException classes hierarchy in the SDK to enhance code consistency</justification>
+  </difference>
+  <difference>
+    <className>org/forgerock/opendj/ldap/schema/Schema*</className>
+    <differenceType>7006</differenceType>
+    <method>*Async*</method>
+    <to>org.forgerock.opendj.ldap.LdapPromise</to>
+    <justification>OPENDJ-1536 Rename FutureResult and ErrorResultException classes hierarchy in the SDK to enhance code consistency</justification>
+  </difference>
+  <difference>
+    <className>org/forgerock/opendj/ldap/Connection</className>
+    <differenceType>7012</differenceType>
+    <method>org.forgerock.opendj.ldap.LdapPromise *Async(*)</method>
+    <justification>OPENDJ-1536 Rename FutureResult and ErrorResultException classes hierarchy in the SDK to enhance code consistency</justification>
+  </difference>
+  <difference>
+    <className>org/forgerock/opendj/ldap/FutureResult</className>
+    <differenceType>8001</differenceType>
+    <justification>OPENDJ-1536 Rename FutureResult and ErrorResultException classes hierarchy in the SDK to enhance code consistency</justification>
+  </difference>
 </differences>
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 a48881c..5d46ca2 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
@@ -104,9 +104,9 @@
         return blockingGetOrThrow(searchAsync(request, handler));
     }
 
-    private <T extends Result> T blockingGetOrThrow(FutureResult<T> future) throws LdapException {
+    private <T extends Result> T blockingGetOrThrow(LdapPromise<T> promise) throws LdapException {
         try {
-            return future.getOrThrow();
+            return promise.getOrThrow();
         } catch (InterruptedException e) {
             throw interrupted(e);
         }
@@ -114,6 +114,6 @@
 
     // Handle thread interruption.
     private LdapException interrupted(InterruptedException e) {
-        return newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, e);
+        return newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, e);
     }
 }
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 b030e0a..9f56c8c 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
@@ -54,6 +54,7 @@
 
 import static org.forgerock.opendj.ldap.LdapException.*;
 import static org.forgerock.opendj.ldap.requests.Requests.*;
+import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
 
 import static com.forgerock.opendj.ldap.CoreMessages.*;
 
@@ -98,7 +99,7 @@
          */
         private LdapException filterError(final LdapException error) {
             if (error.getResult().getResultCode().equals(ResultCode.SIZE_LIMIT_EXCEEDED)) {
-                return newErrorResult(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED,
+                return newLdapException(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED,
                         ERR_UNEXPECTED_SEARCH_RESULT_ENTRIES_NO_COUNT.get().toString());
             } else {
                 return error;
@@ -117,15 +118,15 @@
         private SearchResultEntry getSingleEntry() throws LdapException {
             if (entryCount == 0) {
                 // Did not find any entries.
-                throw newErrorResult(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED,
+                throw newLdapException(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED,
                         ERR_NO_SEARCH_RESULT_ENTRIES.get().toString());
             } else if (entryCount > 1) {
                 // Got more entries than expected.
-                throw newErrorResult(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED,
+                throw newLdapException(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED,
                         ERR_UNEXPECTED_SEARCH_RESULT_ENTRIES.get(entryCount).toString());
             } else if (firstReference != null) {
                 // Got an unexpected search result reference.
-                throw newErrorResult(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED,
+                throw newLdapException(ResultCode.CLIENT_SIDE_UNEXPECTED_RESULTS_RETURNED,
                         ERR_UNEXPECTED_SEARCH_RESULT_REFERENCES.get(firstReference.getURIs().iterator().next())
                         .toString());
             } else {
@@ -193,7 +194,7 @@
     }
 
     @Override
-    public FutureResult<Result> addAsync(final AddRequest request) {
+    public LdapPromise<Result> addAsync(final AddRequest request) {
         return addAsync(request, null);
     }
 
@@ -208,33 +209,33 @@
     }
 
     @Override
-    public FutureResult<Result> applyChangeAsync(ChangeRecord request) {
+    public LdapPromise<Result> applyChangeAsync(ChangeRecord request) {
         return applyChangeAsync(request, null);
     }
 
     @Override
-    public FutureResult<Result> applyChangeAsync(final ChangeRecord request,
+    public LdapPromise<Result> applyChangeAsync(final ChangeRecord request,
             final IntermediateResponseHandler intermediateResponseHandler) {
-        final ChangeRecordVisitor<FutureResult<Result>, Connection> visitor =
-            new ChangeRecordVisitor<FutureResult<Result>, Connection>() {
+        final ChangeRecordVisitor<LdapPromise<Result>, Connection> visitor =
+            new ChangeRecordVisitor<LdapPromise<Result>, Connection>() {
 
                 @Override
-                public FutureResult<Result> visitChangeRecord(final Connection p, final AddRequest change) {
+                public LdapPromise<Result> visitChangeRecord(final Connection p, final AddRequest change) {
                     return p.addAsync(change, intermediateResponseHandler);
                 }
 
                 @Override
-                public FutureResult<Result> visitChangeRecord(final Connection p, final DeleteRequest change) {
+                public LdapPromise<Result> visitChangeRecord(final Connection p, final DeleteRequest change) {
                     return p.deleteAsync(change, intermediateResponseHandler);
                 }
 
                 @Override
-                public FutureResult<Result> visitChangeRecord(final Connection p, final ModifyDNRequest change) {
+                public LdapPromise<Result> visitChangeRecord(final Connection p, final ModifyDNRequest change) {
                     return p.modifyDNAsync(change, intermediateResponseHandler);
                 }
 
                 @Override
-                public FutureResult<Result> visitChangeRecord(final Connection p, final ModifyRequest change) {
+                public LdapPromise<Result> visitChangeRecord(final Connection p, final ModifyRequest change) {
                     return p.modifyAsync(change, intermediateResponseHandler);
                 }
             };
@@ -247,7 +248,7 @@
     }
 
     @Override
-    public FutureResult<BindResult> bindAsync(final BindRequest request) {
+    public LdapPromise<BindResult> bindAsync(final BindRequest request) {
         return bindAsync(request, null);
     }
 
@@ -263,7 +264,7 @@
     }
 
     @Override
-    public FutureResult<CompareResult> compareAsync(final CompareRequest request) {
+    public LdapPromise<CompareResult> compareAsync(final CompareRequest request) {
         return compareAsync(request, null);
     }
 
@@ -273,7 +274,7 @@
     }
 
     @Override
-    public FutureResult<Result> deleteAsync(final DeleteRequest request) {
+    public LdapPromise<Result> deleteAsync(final DeleteRequest request) {
         return deleteAsync(request, null);
     }
 
@@ -294,7 +295,7 @@
     }
 
     @Override
-    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request) {
+    public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request) {
         return extendedRequestAsync(request, null);
     }
 
@@ -304,7 +305,7 @@
     }
 
     @Override
-    public FutureResult<Result> modifyAsync(final ModifyRequest request) {
+    public LdapPromise<Result> modifyAsync(final ModifyRequest request) {
         return modifyAsync(request, null);
     }
 
@@ -314,7 +315,7 @@
     }
 
     @Override
-    public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request) {
+    public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request) {
         return modifyDNAsync(request, null);
     }
 
@@ -334,7 +335,7 @@
     }
 
     @Override
-    public FutureResult<SearchResultEntry> readEntryAsync(final DN name,
+    public LdapPromise<SearchResultEntry> readEntryAsync(final DN name,
             final Collection<String> attributeDescriptions) {
         final SearchRequest request = Requests.newSingleEntrySearchRequest(name, SearchScope.BASE_OBJECT,
                 Filter.objectClassPresent());
@@ -386,7 +387,7 @@
     }
 
     @Override
-    public FutureResult<Result> searchAsync(final SearchRequest request, final SearchResultHandler resultHandler) {
+    public LdapPromise<Result> searchAsync(final SearchRequest request, final SearchResultHandler resultHandler) {
         return searchAsync(request, null, resultHandler);
     }
 
@@ -410,20 +411,20 @@
     }
 
     @Override
-    public FutureResult<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request) {
+    public LdapPromise<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request) {
         final SingleEntryHandler handler = new SingleEntryHandler();
-        return FutureResultWrapper.asFutureResult(searchAsync(enforceSingleEntrySearchRequest(request), handler).then(
-            new Function<Result, SearchResultEntry, LdapException>() {
-                @Override
-                public SearchResultEntry apply(final Result value) throws LdapException {
-                    return handler.getSingleEntry();
-                }
-            }, new Function<LdapException, SearchResultEntry, LdapException>() {
-                @Override
-                public SearchResultEntry apply(final LdapException error) throws LdapException {
-                    throw handler.filterError(error);
-                }
-            }));
+        return asPromise(searchAsync(enforceSingleEntrySearchRequest(request), handler).then(
+                new Function<Result, SearchResultEntry, LdapException>() {
+                    @Override
+                    public SearchResultEntry apply(final Result value) throws LdapException {
+                        return handler.getSingleEntry();
+                    }
+                }, new Function<LdapException, SearchResultEntry, LdapException>() {
+                    @Override
+                    public SearchResultEntry apply(final LdapException error) throws LdapException {
+                        throw handler.filterError(error);
+                    }
+                }));
     }
 
     /**
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 259e044..f492b02 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
@@ -82,7 +82,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Void> abandonAsync(final AbandonRequest request) {
+    public LdapPromise<Void> abandonAsync(final AbandonRequest request) {
         return connection.abandonAsync(request);
     }
 
@@ -122,7 +122,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> addAsync(final AddRequest request) {
+    public LdapPromise<Result> addAsync(final AddRequest request) {
         return connection.addAsync(request);
     }
 
@@ -132,7 +132,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> addAsync(final AddRequest request,
+    public LdapPromise<Result> addAsync(final AddRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         return connection.addAsync(request, intermediateResponseHandler);
     }
@@ -163,7 +163,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> applyChangeAsync(ChangeRecord request) {
+    public LdapPromise<Result> applyChangeAsync(ChangeRecord request) {
         return connection.applyChangeAsync(request);
     }
 
@@ -173,7 +173,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> applyChangeAsync(ChangeRecord request,
+    public LdapPromise<Result> applyChangeAsync(ChangeRecord request,
             IntermediateResponseHandler intermediateResponseHandler) {
         return connection.applyChangeAsync(request, intermediateResponseHandler);
     }
@@ -204,7 +204,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<BindResult> bindAsync(final BindRequest request) {
+    public LdapPromise<BindResult> bindAsync(final BindRequest request) {
         return connection.bindAsync(request);
     }
 
@@ -214,7 +214,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<BindResult> bindAsync(BindRequest request,
+    public LdapPromise<BindResult> bindAsync(BindRequest request,
             IntermediateResponseHandler intermediateResponseHandler) {
         return connection.bindAsync(request, intermediateResponseHandler);
     }
@@ -266,7 +266,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<CompareResult> compareAsync(final CompareRequest request) {
+    public LdapPromise<CompareResult> compareAsync(final CompareRequest request) {
         return connection.compareAsync(request);
     }
 
@@ -276,7 +276,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<CompareResult> compareAsync(CompareRequest request,
+    public LdapPromise<CompareResult> compareAsync(CompareRequest request,
             IntermediateResponseHandler intermediateResponseHandler) {
         return connection.compareAsync(request, intermediateResponseHandler);
     }
@@ -307,7 +307,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> deleteAsync(final DeleteRequest request) {
+    public LdapPromise<Result> deleteAsync(final DeleteRequest request) {
         return connection.deleteAsync(request);
     }
 
@@ -317,7 +317,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> deleteAsync(DeleteRequest request,
+    public LdapPromise<Result> deleteAsync(DeleteRequest request,
             IntermediateResponseHandler intermediateResponseHandler) {
         return connection.deleteAsync(request, intermediateResponseHandler);
     }
@@ -370,7 +370,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request) {
+    public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request) {
         return connection.extendedRequestAsync(request);
     }
 
@@ -380,7 +380,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request,
+    public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(ExtendedRequest<R> request,
             IntermediateResponseHandler intermediateResponseHandler) {
         return connection.extendedRequestAsync(request, intermediateResponseHandler);
     }
@@ -431,7 +431,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> modifyAsync(final ModifyRequest request) {
+    public LdapPromise<Result> modifyAsync(final ModifyRequest request) {
         return connection.modifyAsync(request);
     }
 
@@ -441,7 +441,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> modifyAsync(ModifyRequest request,
+    public LdapPromise<Result> modifyAsync(ModifyRequest request,
             IntermediateResponseHandler intermediateResponseHandler) {
         return connection.modifyAsync(request, intermediateResponseHandler);
     }
@@ -472,7 +472,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request) {
+    public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request) {
         return connection.modifyDNAsync(request);
     }
 
@@ -482,7 +482,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> modifyDNAsync(ModifyDNRequest request,
+    public LdapPromise<Result> modifyDNAsync(ModifyDNRequest request,
             IntermediateResponseHandler intermediateResponseHandler) {
         return modifyDNAsync(request, intermediateResponseHandler);
     }
@@ -513,7 +513,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<SearchResultEntry> readEntryAsync(final DN name,
+    public LdapPromise<SearchResultEntry> readEntryAsync(final DN name,
             final Collection<String> attributeDescriptions) {
         return connection.readEntryAsync(name, attributeDescriptions);
     }
@@ -588,7 +588,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> searchAsync(final SearchRequest request,
+    public LdapPromise<Result> searchAsync(final SearchRequest request,
             final SearchResultHandler resultHandler) {
         return connection.searchAsync(request, resultHandler);
     }
@@ -599,7 +599,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<Result> searchAsync(SearchRequest request,
+    public LdapPromise<Result> searchAsync(SearchRequest request,
             IntermediateResponseHandler intermediateResponseHandler, SearchResultHandler entryHandler) {
         return connection.searchAsync(request, intermediateResponseHandler, entryHandler);
     }
@@ -631,7 +631,7 @@
      * The default implementation is to delegate.
      */
     @Override
-    public FutureResult<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request) {
+    public LdapPromise<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 f974d77..be7d001 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
@@ -71,7 +71,7 @@
 
         @Override
         public void close() {
-            // Should we cancel the future?
+            // Should we cancel the promise?
             factory.close();
         }
 
@@ -362,7 +362,7 @@
          * policy here such as waiting indefinitely, or for a configurable
          * timeout period.
          */
-        throw newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
+        throw newLdapException(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
                 "No operational connection factories available", lastFailure);
     }
 }
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 4ac781e..d8e45a3 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
@@ -40,7 +40,7 @@
 import org.forgerock.opendj.ldap.responses.ExtendedResult;
 import org.forgerock.opendj.ldap.responses.Result;
 
-import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
+import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
 
 /**
  * An abstract connection whose asynchronous methods are implemented in terms of
@@ -73,12 +73,12 @@
      *             synchronous connections.
      */
     @Override
-    public FutureResult<Void> abandonAsync(final AbandonRequest request) {
+    public LdapPromise<Void> abandonAsync(final AbandonRequest request) {
         throw new UnsupportedOperationException("Abandon requests are not supported for synchronous connections");
     }
 
     @Override
-    public FutureResult<Result> addAsync(final AddRequest request,
+    public LdapPromise<Result> addAsync(final AddRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         try {
             return onSuccess(add(request));
@@ -88,7 +88,7 @@
     }
 
     @Override
-    public FutureResult<BindResult> bindAsync(final BindRequest request,
+    public LdapPromise<BindResult> bindAsync(final BindRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         try {
             return onSuccess(bind(request));
@@ -98,7 +98,7 @@
     }
 
     @Override
-    public FutureResult<CompareResult> compareAsync(final CompareRequest request,
+    public LdapPromise<CompareResult> compareAsync(final CompareRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         try {
             return onSuccess(compare(request));
@@ -108,7 +108,7 @@
     }
 
     @Override
-    public FutureResult<Result> deleteAsync(final DeleteRequest request,
+    public LdapPromise<Result> deleteAsync(final DeleteRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         try {
             return onSuccess(delete(request));
@@ -118,7 +118,7 @@
     }
 
     @Override
-    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request,
+    public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         try {
             return onSuccess(extendedRequest(request, intermediateResponseHandler));
@@ -128,7 +128,7 @@
     }
 
     @Override
-    public FutureResult<Result> modifyAsync(final ModifyRequest request,
+    public LdapPromise<Result> modifyAsync(final ModifyRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         try {
             return onSuccess(modify(request));
@@ -138,7 +138,7 @@
     }
 
     @Override
-    public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
+    public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         try {
             return onSuccess(modifyDN(request));
@@ -148,7 +148,7 @@
     }
 
     @Override
-    public FutureResult<Result> searchAsync(final SearchRequest request,
+    public LdapPromise<Result> searchAsync(final SearchRequest request,
             final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) {
         try {
             return onSuccess(search(request, entryHandler));
@@ -157,12 +157,12 @@
         }
     }
 
-    private <R extends Result> FutureResult<R> onFailure(final LdapException e) {
-        return newFailedFutureResult(e);
+    private <R extends Result> LdapPromise<R> onFailure(final LdapException e) {
+        return newFailedLdapPromise(e);
     }
 
-    private <R extends Result> FutureResult<R> onSuccess(final R result) {
-        return newSuccessfulFutureResult(result);
+    private <R extends Result> LdapPromise<R> onSuccess(final R result) {
+        return newSuccessfulLdapPromise(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 93d0399..d15fb31 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
@@ -64,7 +64,7 @@
          * Bind operations are not supported by pre-authenticated connections.
          * These methods will always throw {@code UnsupportedOperationException}.
          */
-        public FutureResult<BindResult> bindAsync(final BindRequest request,
+        public LdapPromise<BindResult> bindAsync(final BindRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler,
                 final ResultHandler<? super BindResult> resultHandler) {
             throw new UnsupportedOperationException();
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 a1e47fc..ad51842 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
@@ -61,6 +61,7 @@
 import org.forgerock.util.Reject;
 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 com.forgerock.opendj.util.ReferenceCountedObject;
@@ -110,25 +111,24 @@
                     currentPoolSize(), maxPoolSize, error));
 
             /*
-             * There may be many pending futures waiting for a connection
+             * There may be many pending promises waiting for a connection
              * attempt to succeed. In some situations the number of pending
-             * futures may exceed the pool size and the number of outstanding
-             * connection attempts. If only one pending future is resolved per
-             * failed connection attempt then some pending futures will be left
+             * promises may exceed the pool size and the number of outstanding
+             * connection attempts. If only one pending promises is resolved per
+             * failed connection attempt then some pending promises will be left
              * unresolved. Therefore, a failed connection attempt must fail all
-             * pending futures, even if some of the subsequent connection
+             * pending promises, even if some of the subsequent connection
              * attempts succeed, which is unlikely (if one fails, then they are
              * all likely to fail).
              */
-            final List<QueueElement> waitingFutures =
-                    new LinkedList<CachedConnectionPool.QueueElement>();
+            final List<QueueElement> waitingPromises = new LinkedList<CachedConnectionPool.QueueElement>();
             synchronized (queue) {
-                while (hasWaitingFutures()) {
-                    waitingFutures.add(queue.removeFirst());
+                while (hasWaitingPromises()) {
+                    waitingPromises.add(queue.removeFirst());
                 }
             }
-            for (QueueElement waitingFuture : waitingFutures) {
-                waitingFuture.getWaitingFuture().handleError(error);
+            for (QueueElement waitingPromise : waitingPromises) {
+                waitingPromise.getWaitingPromise().handleError(error);
             }
         }
     }
@@ -152,7 +152,7 @@
         }
 
         @Override
-        public FutureResult<Void> abandonAsync(final AbandonRequest request) {
+        public LdapPromise<Void> abandonAsync(final AbandonRequest request) {
             return checkState().abandonAsync(request);
         }
 
@@ -172,12 +172,12 @@
         }
 
         @Override
-        public FutureResult<Result> addAsync(AddRequest request) {
+        public LdapPromise<Result> addAsync(AddRequest request) {
             return addAsync(request, null);
         }
 
         @Override
-        public FutureResult<Result> addAsync(final AddRequest request,
+        public LdapPromise<Result> addAsync(final AddRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             return checkState().addAsync(request, intermediateResponseHandler);
         }
@@ -220,12 +220,12 @@
         }
 
         @Override
-        public FutureResult<Result> applyChangeAsync(final ChangeRecord request) {
+        public LdapPromise<Result> applyChangeAsync(final ChangeRecord request) {
             return checkState().applyChangeAsync(request, null);
         }
 
         @Override
-        public FutureResult<Result> applyChangeAsync(final ChangeRecord request,
+        public LdapPromise<Result> applyChangeAsync(final ChangeRecord request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             return checkState().applyChangeAsync(request, intermediateResponseHandler);
         }
@@ -241,12 +241,12 @@
         }
 
         @Override
-        public FutureResult<BindResult> bindAsync(BindRequest request) {
+        public LdapPromise<BindResult> bindAsync(BindRequest request) {
             return bindAsync(request, null);
         }
 
         @Override
-        public FutureResult<BindResult> bindAsync(final BindRequest request,
+        public LdapPromise<BindResult> bindAsync(final BindRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             return checkState().bindAsync(request, intermediateResponseHandler);
         }
@@ -277,7 +277,7 @@
                 /*
                  * The connection may have been disconnected by the remote
                  * server, but the server may still be available. In order to
-                 * avoid leaving pending futures hanging indefinitely, we should
+                 * avoid leaving pending promises hanging indefinitely, we should
                  * try to reconnect immediately. No need to release/acquire
                  * availableConnections.
                  */
@@ -315,12 +315,12 @@
         }
 
         @Override
-        public FutureResult<CompareResult> compareAsync(CompareRequest request) {
+        public LdapPromise<CompareResult> compareAsync(CompareRequest request) {
             return compareAsync(request, null);
         }
 
         @Override
-        public FutureResult<CompareResult> compareAsync(final CompareRequest request,
+        public LdapPromise<CompareResult> compareAsync(final CompareRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             return checkState().compareAsync(request, intermediateResponseHandler);
         }
@@ -336,12 +336,12 @@
         }
 
         @Override
-        public FutureResult<Result> deleteAsync(DeleteRequest request) {
+        public LdapPromise<Result> deleteAsync(DeleteRequest request) {
             return deleteAsync(request, null);
         }
 
         @Override
-        public FutureResult<Result> deleteAsync(final DeleteRequest request,
+        public LdapPromise<Result> deleteAsync(final DeleteRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             return checkState().deleteAsync(request, intermediateResponseHandler);
         }
@@ -369,12 +369,12 @@
         }
 
         @Override
-        public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request) {
+        public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(ExtendedRequest<R> request) {
             return extendedRequestAsync(request, null);
         }
 
         @Override
-        public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request,
+        public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             return checkState().extendedRequestAsync(request, intermediateResponseHandler);
         }
@@ -439,12 +439,12 @@
         }
 
         @Override
-        public FutureResult<Result> modifyAsync(ModifyRequest request) {
+        public LdapPromise<Result> modifyAsync(ModifyRequest request) {
             return modifyAsync(request, null);
         }
 
         @Override
-        public FutureResult<Result> modifyAsync(final ModifyRequest request,
+        public LdapPromise<Result> modifyAsync(final ModifyRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             return checkState().modifyAsync(request, intermediateResponseHandler);
         }
@@ -460,12 +460,12 @@
         }
 
         @Override
-        public FutureResult<Result> modifyDNAsync(ModifyDNRequest request) {
+        public LdapPromise<Result> modifyDNAsync(ModifyDNRequest request) {
             return modifyDNAsync(request, null);
         }
 
         @Override
-        public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
+        public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             return checkState().modifyDNAsync(request, intermediateResponseHandler);
         }
@@ -483,7 +483,7 @@
         }
 
         @Override
-        public FutureResult<SearchResultEntry> readEntryAsync(final DN name,
+        public LdapPromise<SearchResultEntry> readEntryAsync(final DN name,
                 final Collection<String> attributeDescriptions) {
             return checkState().readEntryAsync(name, attributeDescriptions);
         }
@@ -528,12 +528,12 @@
         }
 
         @Override
-        public FutureResult<Result> searchAsync(SearchRequest request, SearchResultHandler resultHandler) {
+        public LdapPromise<Result> searchAsync(SearchRequest request, SearchResultHandler resultHandler) {
             return searchAsync(request, null, resultHandler);
         }
 
         @Override
-        public FutureResult<Result> searchAsync(final SearchRequest request,
+        public LdapPromise<Result> searchAsync(final SearchRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) {
             return checkState().searchAsync(request, intermediateResponseHandler, entryHandler);
         }
@@ -550,7 +550,7 @@
         }
 
         @Override
-        public FutureResult<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request) {
+        public LdapPromise<SearchResultEntry> searchSingleEntryAsync(final SearchRequest request) {
             return checkState().searchSingleEntryAsync(request);
         }
 
@@ -612,9 +612,8 @@
             }
         }
 
-        private boolean isTimedOutQueuedConnection(final QueueElement holder,
-                final long timeoutMillis) {
-            return holder != null && !holder.isWaitingFuture() && holder.hasTimedOut(timeoutMillis);
+        private boolean isTimedOutQueuedConnection(final QueueElement holder, final long timeoutMillis) {
+            return holder != null && !holder.isWaitingPromise() && holder.hasTimedOut(timeoutMillis);
         }
     }
 
@@ -630,14 +629,13 @@
         @Override
         protected void finalize() throws Throwable {
             if (!isClosed()) {
-                logIfDebugEnabled("CONNECTION POOL: connection leaked! It was allocated here: ",
-                        stackTrace);
+                logIfDebugEnabled("CONNECTION POOL: connection leaked! It was allocated here: ", stackTrace);
             }
         }
     }
 
     /**
-     * A queue element is either a pending connection request future awaiting an
+     * A queue element is either a pending connection request promise awaiting an
      * {@code Connection} or it is an unused {@code Connection} awaiting a
      * connection request.
      */
@@ -652,9 +650,8 @@
             this.stack = null;
         }
 
-        QueueElement(final long timestampMillis,
-            final StackTraceElement[] stack) {
-            this.value = new FutureResultImpl<Connection>();
+        QueueElement(final long timestampMillis, final StackTraceElement[] stack) {
+            this.value = PromiseImpl.create();
             this.timestampMillis = timestampMillis;
             this.stack = stack;
         }
@@ -677,16 +674,16 @@
         }
 
         @SuppressWarnings("unchecked")
-        FutureResultImpl<Connection> getWaitingFuture() {
-            return (FutureResultImpl<Connection>) value;
+        PromiseImpl<Connection, LdapException> getWaitingPromise() {
+            return (PromiseImpl<Connection, LdapException>) value;
         }
 
         boolean hasTimedOut(final long timeLimitMillis) {
             return timestampMillis < timeLimitMillis;
         }
 
-        boolean isWaitingFuture() {
-            return value instanceof FutureResultImpl;
+        boolean isWaitingPromise() {
+            return value instanceof PromiseImpl;
         }
     }
 
@@ -790,7 +787,7 @@
         try {
             return getConnectionAsync().getOrThrow();
         } catch (final InterruptedException e) {
-            throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, e);
+            throw newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, e);
         }
     }
 
@@ -810,15 +807,15 @@
                 }
             }
 
-            if (holder.isWaitingFuture()) {
+            if (holder.isWaitingPromise()) {
                 // Grow the pool if needed.
-                final FutureResult<Connection> future = holder.getWaitingFuture();
-                if (!future.isDone() && availableConnections.tryAcquire()) {
+                final Promise<Connection, LdapException> promise = holder.getWaitingPromise();
+                if (!promise.isDone() && availableConnections.tryAcquire()) {
                     pendingConnectionAttempts.incrementAndGet();
                     factory.getConnectionAsync().onSuccess(connectionSuccessHandler)
                                                 .onFailure(connectionFailureHandler);
                 }
-                return future;
+                return promise;
             }
 
             // There was a completed connection attempt.
@@ -845,7 +842,7 @@
         int blocked = 0;
         synchronized (queue) {
             for (QueueElement qe : queue) {
-                if (qe.isWaitingFuture()) {
+                if (qe.isWaitingPromise()) {
                     blocked++;
                 } else {
                     in++;
@@ -874,11 +871,11 @@
     }
 
     private boolean hasWaitingConnections() {
-        return !queue.isEmpty() && !queue.getFirst().isWaitingFuture();
+        return !queue.isEmpty() && !queue.getFirst().isWaitingPromise();
     }
 
-    private boolean hasWaitingFutures() {
-        return !queue.isEmpty() && queue.getFirst().isWaitingFuture();
+    private boolean hasWaitingPromises() {
+        return !queue.isEmpty() && queue.getFirst().isWaitingPromise();
     }
 
     private void publishConnection(final Connection connection) {
@@ -886,7 +883,7 @@
         boolean connectionPoolIsClosing = false;
 
         synchronized (queue) {
-            if (hasWaitingFutures()) {
+            if (hasWaitingPromises()) {
                 connectionPoolIsClosing = isClosed;
                 holder = queue.removeFirst();
             } else if (isClosed) {
@@ -899,7 +896,7 @@
             }
         }
 
-        // There was waiting future, so complete it.
+        // There was waiting promise, so complete it.
         if (connectionPoolIsClosing) {
             // The connection will be closed, so decrease the pool size.
             availableConnections.release();
@@ -911,16 +908,16 @@
 
             if (holder != null) {
                 final LdapException e =
-                        LdapException.newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED,
+                        newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED,
                                 ERR_CONNECTION_POOL_CLOSING.get(toString()).toString());
-                holder.getWaitingFuture().handleError(e);
+                holder.getWaitingPromise().handleError(e);
 
                 logger.debug(LocalizableMessage.raw(
                         "Connection attempt failed: availableConnections=%d, maxPoolSize=%d",
                         currentPoolSize(), maxPoolSize, e));
             }
         } else {
-            holder.getWaitingFuture().handleResult(
+            holder.getWaitingPromise().handleResult(
                     newPooledConnection(connection, holder.getStackTrace()));
         }
     }
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 a9db6e9..1271689 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
@@ -78,12 +78,12 @@
  * <h4>Performing operations asynchronously</h4>
  * <p>
  * Asynchronous methods, identified by their {@code Async} suffix, are
- * non-blocking, returning a {@link FutureResult} or sub-type thereof which can
- * be used for retrieving the result using the {@link FutureResult#get} method.
- * Operation failures, for whatever reason, are signalled by the
- * {@link FutureResult#get()} method throwing an {@link LdapException}.
+ * non-blocking, returning a {@link LdapPromise} or sub-type thereof which can
+ * be used for retrieving the result using the {@link LdapPromise#get} method.
+ * Operation failures, for whatever reason, are signaled by the
+ * {@link LdapPromise#get()} method throwing an {@link LdapException}.
  * <p>
- * In addition to returning a {@code FutureResult}, all asynchronous methods
+ * In addition to returning a {@link LdapPromise}, all asynchronous methods
  * accept a {@link ResultHandler} which will be notified upon completion of the
  * operation.
  * <p>
@@ -106,12 +106,12 @@
  * Connection connection2 = ...;
  * AddRequest request = ...;
  * // Add the entry to the first server (don't block).
- * FutureResult future1 = connection1.add(request);
+ * LdapPromise promise1 = connection1.add(request);
  * // Add the entry to the second server (in parallel).
- * FutureResult future2 = connection2.add(request);
+ * LdapPromise promise2 = connection2.add(request);
  * // Total time = is O(1) instead of O(n).
- * future1.get();
- * future2.get();
+ * promise1.get();
+ * promise2.get();
  * </pre>
  *
  * More complex client applications can take advantage of a fully asynchronous
@@ -155,18 +155,18 @@
      * request.
      * <p>
      * Abandon requests do not have a response, so invoking the method get() on
-     * the returned future will not block, nor return anything (it is Void), but
+     * the returned promise will not block, nor return anything (it is Void), but
      * may throw an exception if a problem occurred while sending the abandon
-     * request. In addition the returned future may be used in order to
+     * request. In addition the returned promise may be used in order to
      * determine the message ID of the abandon request.
      * <p>
      * <b>Note:</b> a more convenient approach to abandoning unfinished
      * asynchronous operations is provided via the
-     * {@link FutureResult#cancel(boolean)} method.
+     * {@link LdapPromise#cancel(boolean)} method.
      *
      * @param request
      *            The request identifying the operation to be abandoned.
-     * @return A future whose result is Void.
+     * @return A promise whose result is Void.
      * @throws UnsupportedOperationException
      *             If this connection does not support abandon operations.
      * @throws IllegalStateException
@@ -175,7 +175,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Void> abandonAsync(AbandonRequest request);
+    LdapPromise<Void> abandonAsync(AbandonRequest request);
 
     /**
      * Adds an entry to the Directory Server using the provided add request.
@@ -258,7 +258,7 @@
      *
      * @param request
      *            The add request.
-     * @return A future representing the result of the operation.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support add operations.
      * @throws IllegalStateException
@@ -267,7 +267,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> addAsync(AddRequest request);
+    LdapPromise<Result> addAsync(AddRequest request);
 
     /**
      * Asynchronously adds an entry to the Directory Server using the provided
@@ -279,7 +279,7 @@
      *            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.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support add operations.
      * @throws IllegalStateException
@@ -288,7 +288,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> addAsync(AddRequest request, IntermediateResponseHandler intermediateResponseHandler);
+    LdapPromise<Result> addAsync(AddRequest request, IntermediateResponseHandler intermediateResponseHandler);
 
     /**
      * Registers the provided connection event listener so that it will be
@@ -332,7 +332,7 @@
      *
      * @param request
      *            The change request.
-     * @return A future representing the result of the operation.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support the provided change
      *             request.
@@ -342,7 +342,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> applyChangeAsync(ChangeRecord request);
+    LdapPromise<Result> applyChangeAsync(ChangeRecord request);
 
     /**
      * Asynchronously applies the provided change request to the Directory
@@ -354,7 +354,7 @@
      *            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.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support the provided change
      *             request.
@@ -364,7 +364,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> applyChangeAsync(ChangeRecord request,
+    LdapPromise<Result> applyChangeAsync(ChangeRecord request,
         IntermediateResponseHandler intermediateResponseHandler);
 
     /**
@@ -426,7 +426,7 @@
      *
      * @param request
      *            The bind request.
-     * @return A future representing the result of the operation.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support bind operations.
      * @throws IllegalStateException
@@ -435,7 +435,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<BindResult> bindAsync(BindRequest request);
+    LdapPromise<BindResult> bindAsync(BindRequest request);
 
     /**
      * Asynchronously authenticates to the Directory Server using the provided
@@ -447,7 +447,7 @@
      *            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.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support bind operations.
      * @throws IllegalStateException
@@ -456,7 +456,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<BindResult> bindAsync(BindRequest request, IntermediateResponseHandler intermediateResponseHandler);
+    LdapPromise<BindResult> bindAsync(BindRequest request, IntermediateResponseHandler intermediateResponseHandler);
 
     /**
      * Releases any resources associated with this connection. For physical
@@ -568,7 +568,7 @@
      *
      * @param request
      *            The compare request.
-     * @return A future representing the result of the operation.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support compare operations.
      * @throws IllegalStateException
@@ -577,7 +577,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<CompareResult> compareAsync(CompareRequest request);
+    LdapPromise<CompareResult> compareAsync(CompareRequest request);
 
     /**
      * Asynchronously compares an entry in the Directory Server using the
@@ -589,7 +589,7 @@
      *            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.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support compare operations.
      * @throws IllegalStateException
@@ -598,7 +598,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<CompareResult> compareAsync(CompareRequest request,
+    LdapPromise<CompareResult> compareAsync(CompareRequest request,
         IntermediateResponseHandler intermediateResponseHandler);
 
     /**
@@ -687,7 +687,7 @@
      *
      * @param request
      *            The delete request.
-     * @return A future representing the result of the operation.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support delete operations.
      * @throws IllegalStateException
@@ -696,7 +696,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> deleteAsync(DeleteRequest request);
+    LdapPromise<Result> deleteAsync(DeleteRequest request);
 
     /**
      * Asynchronously deletes an entry from the Directory Server using the
@@ -708,7 +708,7 @@
      *            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.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support delete operations.
      * @throws IllegalStateException
@@ -717,7 +717,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> deleteAsync(DeleteRequest request, IntermediateResponseHandler intermediateResponseHandler);
+    LdapPromise<Result> deleteAsync(DeleteRequest request, IntermediateResponseHandler intermediateResponseHandler);
 
     /**
      * Requests that the Directory Server performs the provided extended
@@ -807,7 +807,7 @@
      *            The type of result returned by the extended request.
      * @param request
      *            The extended request.
-     * @return A future representing the result of the operation.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support extended operations.
      * @throws IllegalStateException
@@ -816,7 +816,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request);
+    <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(ExtendedRequest<R> request);
 
     /**
      * Asynchronously performs the provided extended request in the Directory
@@ -830,7 +830,7 @@
      *            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.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support extended operations.
      * @throws IllegalStateException
@@ -839,7 +839,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request,
+    <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(ExtendedRequest<R> request,
         IntermediateResponseHandler intermediateResponseHandler);
 
     /**
@@ -921,7 +921,7 @@
      *
      * @param request
      *            The modify request.
-     * @return A future representing the result of the operation.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support modify operations.
      * @throws IllegalStateException
@@ -930,7 +930,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> modifyAsync(ModifyRequest request);
+    LdapPromise<Result> modifyAsync(ModifyRequest request);
 
     /**
      * Asynchronously modifies an entry in the Directory Server using the
@@ -942,7 +942,7 @@
      *            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.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support modify operations.
      * @throws IllegalStateException
@@ -951,7 +951,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> modifyAsync(ModifyRequest request, IntermediateResponseHandler intermediateResponseHandler);
+    LdapPromise<Result> modifyAsync(ModifyRequest request, IntermediateResponseHandler intermediateResponseHandler);
 
     /**
      * Renames an entry in the Directory Server using the provided modify DN
@@ -1011,7 +1011,7 @@
      *
      * @param request
      *            The modify DN request.
-     * @return A future representing the result of the operation.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support modify DN operations.
      * @throws IllegalStateException
@@ -1020,7 +1020,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> modifyDNAsync(ModifyDNRequest request);
+    LdapPromise<Result> modifyDNAsync(ModifyDNRequest request);
 
     /**
      * Asynchronously renames an entry in the Directory Server using the
@@ -1032,7 +1032,7 @@
      *            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.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support modify DN operations.
      * @throws IllegalStateException
@@ -1041,7 +1041,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> modifyDNAsync(ModifyDNRequest request,
+    LdapPromise<Result> modifyDNAsync(ModifyDNRequest request,
         IntermediateResponseHandler intermediateResponseHandler);
 
     /**
@@ -1120,7 +1120,7 @@
      * <p>
      * If the requested entry is not returned by the Directory Server then the
      * request will fail with an {@link EntryNotFoundException}. More
-     * specifically, the returned future will never return {@code null}.
+     * specifically, the returned promise will never return {@code null}.
      * <p>
      * This method is equivalent to the following code:
      *
@@ -1136,7 +1136,7 @@
      *            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.
-     * @return A future representing the result of the operation.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support search operations.
      * @throws IllegalStateException
@@ -1145,7 +1145,7 @@
      * @throws NullPointerException
      *             If the {@code name} was {@code null}.
      */
-    FutureResult<SearchResultEntry> readEntryAsync(DN name, Collection<String> attributeDescriptions);
+    LdapPromise<SearchResultEntry> readEntryAsync(DN name, Collection<String> attributeDescriptions);
 
     /**
      * Removes the provided connection event listener from this connection so
@@ -1328,7 +1328,7 @@
      *            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.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support search operations.
      * @throws IllegalStateException
@@ -1337,7 +1337,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> searchAsync(SearchRequest request, SearchResultHandler entryHandler);
+    LdapPromise<Result> searchAsync(SearchRequest request, SearchResultHandler entryHandler);
 
     /**
      * Asynchronously searches the Directory Server using the provided search
@@ -1353,7 +1353,7 @@
      *            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.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support search operations.
      * @throws IllegalStateException
@@ -1362,7 +1362,7 @@
      * @throws NullPointerException
      *             If {@code request} was {@code null}.
      */
-    FutureResult<Result> searchAsync(SearchRequest request, IntermediateResponseHandler intermediateResponseHandler,
+    LdapPromise<Result> searchAsync(SearchRequest request, IntermediateResponseHandler intermediateResponseHandler,
         SearchResultHandler entryHandler);
 
     /**
@@ -1444,13 +1444,13 @@
      * <p>
      * If the requested entry is not returned by the Directory Server then the
      * request will fail with an {@link EntryNotFoundException}. More
-     * specifically, the returned future will never return {@code null}. If
+     * specifically, the returned promise will never return {@code null}. If
      * multiple matching entries are returned by the Directory Server then the
      * request will fail with an {@link MultipleEntriesFoundException}.
      *
      * @param request
      *            The search request.
-     * @return A future representing the result of the operation.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If this connection does not support search operations.
      * @throws IllegalStateException
@@ -1459,5 +1459,5 @@
      * @throws NullPointerException
      *             If the {@code request} was {@code null}.
      */
-    FutureResult<SearchResultEntry> searchSingleEntryAsync(SearchRequest request);
+    LdapPromise<SearchResultEntry> searchSingleEntryAsync(SearchRequest request);
 }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/Entries.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/Entries.java
index 5dbdc67..c15ada6 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/Entries.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/Entries.java
@@ -29,7 +29,7 @@
 
 import static com.forgerock.opendj.ldap.CoreMessages.*;
 import static org.forgerock.opendj.ldap.AttributeDescription.objectClass;
-import static org.forgerock.opendj.ldap.LdapException.newErrorResult;
+import static org.forgerock.opendj.ldap.LdapException.newLdapException;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -1084,21 +1084,21 @@
         // First parse the change.
         final AttributeDescription deltaAd = change.getAttributeDescription();
         if (change.size() != 1) {
-            throw newErrorResult(ResultCode.CONSTRAINT_VIOLATION,
+            throw newLdapException(ResultCode.CONSTRAINT_VIOLATION,
                     ERR_ENTRY_INCREMENT_INVALID_VALUE_COUNT.get(deltaAd.toString()).toString());
         }
         final long delta;
         try {
             delta = change.parse().asLong();
         } catch (final Exception e) {
-            throw newErrorResult(ResultCode.CONSTRAINT_VIOLATION,
+            throw newLdapException(ResultCode.CONSTRAINT_VIOLATION,
                     ERR_ENTRY_INCREMENT_CANNOT_PARSE_AS_INT.get(deltaAd.toString()).toString());
         }
 
         // Now apply the increment to the attribute.
         final Attribute oldAttribute = entry.getAttribute(deltaAd);
         if (oldAttribute == null) {
-            throw newErrorResult(ResultCode.NO_SUCH_ATTRIBUTE,
+            throw newLdapException(ResultCode.NO_SUCH_ATTRIBUTE,
                     ERR_ENTRY_INCREMENT_NO_SUCH_ATTRIBUTE.get(deltaAd.toString()).toString());
         }
 
@@ -1109,7 +1109,7 @@
                 newAttribute.add(value + delta);
             }
         } catch (final Exception e) {
-            throw newErrorResult(ResultCode.CONSTRAINT_VIOLATION,
+            throw newLdapException(ResultCode.CONSTRAINT_VIOLATION,
                     ERR_ENTRY_INCREMENT_CANNOT_PARSE_AS_INT.get(deltaAd.toString()).toString());
         }
         entry.replaceAttribute(newAttribute);
@@ -1133,7 +1133,7 @@
             entry.addAttribute(change.getAttribute(), conflictingValues);
             if (!isPermissive && !conflictingValues.isEmpty()) {
                 // Duplicate values.
-                throw newErrorResult(ResultCode.ATTRIBUTE_OR_VALUE_EXISTS,
+                throw newLdapException(ResultCode.ATTRIBUTE_OR_VALUE_EXISTS,
                         ERR_ENTRY_DUPLICATE_VALUES.get(
                                 change.getAttribute().getAttributeDescriptionAsString()).toString());
             }
@@ -1142,7 +1142,7 @@
                     entry.removeAttribute(change.getAttribute(), conflictingValues);
             if (!isPermissive && (!hasChanged || !conflictingValues.isEmpty())) {
                 // Missing attribute or values.
-                throw newErrorResult(ResultCode.NO_SUCH_ATTRIBUTE, ERR_ENTRY_NO_SUCH_VALUE.get(
+                throw newLdapException(ResultCode.NO_SUCH_ATTRIBUTE, ERR_ENTRY_NO_SUCH_VALUE.get(
                         change.getAttribute().getAttributeDescriptionAsString()).toString());
             }
         } else if (modType.equals(ModificationType.REPLACE)) {
@@ -1150,7 +1150,7 @@
         } else if (modType.equals(ModificationType.INCREMENT)) {
             incrementAttribute(entry, change.getAttribute());
         } else {
-            throw newErrorResult(ResultCode.UNWILLING_TO_PERFORM,
+            throw newLdapException(ResultCode.UNWILLING_TO_PERFORM,
                     ERR_ENTRY_UNKNOWN_MODIFICATION_TYPE.get(String.valueOf(modType)).toString());
         }
         return entry;
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
deleted file mode 100644
index 27f68aa..0000000
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResult.java
+++ /dev/null
@@ -1,46 +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 2014 ForgeRock AS
- */
-
-package org.forgerock.opendj.ldap;
-
-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 result.
- */
-public interface FutureResult<S> extends Promise<S, LdapException> {
-    /**
-     * Returns the request ID of the request if appropriate.
-     *
-     * @return The request ID, or {@code -1} if there is no request ID.
-     */
-    int getRequestID();
-
-}
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
deleted file mode 100644
index dd40ad6..0000000
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResultImpl.java
+++ /dev/null
@@ -1,64 +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 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, LdapException>
-    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
deleted file mode 100644
index 858cfcc..0000000
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResultWrapper.java
+++ /dev/null
@@ -1,278 +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 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, LdapException> wrappedPromise;
-        private final int requestID;
-
-        LdapPromiseWrapper(Promise<R, LdapException> 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, LdapException {
-            return wrappedPromise.getOrThrow();
-        }
-
-        @Override
-        public R getOrThrow(long timeout, TimeUnit unit) throws InterruptedException, LdapException,
-            TimeoutException {
-            return wrappedPromise.getOrThrow(timeout, unit);
-        }
-
-        @Override
-        public R getOrThrowUninterruptibly() throws LdapException {
-            return wrappedPromise.getOrThrowUninterruptibly();
-        }
-
-        @Override
-        public R getOrThrowUninterruptibly(long timeout, TimeUnit unit) throws LdapException, TimeoutException {
-            return wrappedPromise.getOrThrowUninterruptibly(timeout, unit);
-        }
-
-        @Override
-        public boolean isCancelled() {
-            return wrappedPromise.isCancelled();
-        }
-
-        @Override
-        public boolean isDone() {
-            return wrappedPromise.isDone();
-        }
-
-        @Override
-        public Promise<R, LdapException> onFailure(FailureHandler<? super LdapException> onFailure) {
-            wrappedPromise.onFailure(onFailure);
-            return this;
-        }
-
-        @Override
-        public Promise<R, LdapException> onSuccess(SuccessHandler<? super R> onSuccess) {
-            wrappedPromise.onSuccess(onSuccess);
-            return this;
-        }
-
-        @Override
-        public Promise<R, LdapException> onSuccessOrFailure(Runnable onSuccessOrFailure) {
-            wrappedPromise.onSuccessOrFailure(onSuccessOrFailure);
-            return this;
-        }
-
-        @Override
-        // @Checkstyle:ignore
-        public <VOUT> Promise<VOUT, LdapException> then(Function<? super R, VOUT, LdapException> 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 LdapException, VOUT, EOUT> onFailure) {
-            return new LdapPromiseWrapper(wrappedPromise.then(onSuccess, onFailure), getRequestID());
-        }
-
-        @Override
-        public Promise<R, LdapException> then(SuccessHandler<? super R> onSuccess) {
-            wrappedPromise.then(onSuccess);
-            return this;
-        }
-
-        @Override
-        public Promise<R, LdapException> then(SuccessHandler<? super R> onSuccess,
-            FailureHandler<? super LdapException> onFailure) {
-            wrappedPromise.then(onSuccess, onFailure);
-            return this;
-        }
-
-        @Override
-        public Promise<R, LdapException> thenAlways(Runnable onSuccessOrFailure) {
-            wrappedPromise.thenAlways(onSuccessOrFailure);
-            return this;
-        }
-
-        @Override
-        // @Checkstyle:ignore
-        public <VOUT> Promise<VOUT, LdapException> thenAsync(AsyncFunction<? super R, VOUT, LdapException> 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 LdapException, 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, LdapException> 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, LdapException> 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 LdapException> FutureResult<R> newFailedFutureResult(final E error) {
-        return new LdapPromiseWrapper<R>(Promises.<R, LdapException> 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 LdapException> FutureResult<R> newFailedFutureResult(final E error, int requestID) {
-        return new LdapPromiseWrapper<R>(Promises.<R, LdapException> 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, LdapException> 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 ba25d85..e38daf8 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
@@ -38,6 +38,7 @@
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.AbstractQueuedSynchronizer;
 
 import org.forgerock.i18n.LocalizableMessage;
@@ -61,17 +62,21 @@
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
 import org.forgerock.opendj.ldap.spi.ConnectionState;
+import org.forgerock.opendj.ldap.spi.LdapPromiseImpl;
 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.PromiseImpl;
 import org.forgerock.util.promise.SuccessHandler;
 
 import com.forgerock.opendj.util.ReferenceCountedObject;
 import com.forgerock.opendj.util.TimeSource;
 
 import static org.forgerock.opendj.ldap.LdapException.*;
+import static org.forgerock.opendj.ldap.spi.LdapPromiseImpl.*;
+import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
+import static org.forgerock.util.Utils.*;
 
 import static com.forgerock.opendj.ldap.CoreMessages.*;
 import static com.forgerock.opendj.util.StaticUtils.*;
@@ -105,224 +110,25 @@
  * while there are pending bind or startTLS requests.
  */
 final class HeartBeatConnectionFactory implements ConnectionFactory {
-
-    /**
-     * This class is responsible for performing an initial heart beat once a new
-     * connection has been obtained and, if the heart beat succeeds, adapting it
-     * to a {@code ConnectionImpl} and registering it in the table of valid
-     * connections.
-     */
-    private final class ConnectionFutureResultImpl implements Runnable {
-        private Connection connection;
-        private Connection heartBeatConnection;
-        private LdapException heartBeatError;
-
-        /**
-         * 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, LdapException> futureSearchSuccess;
-        private final Function<LdapException, Connection, LdapException> futureSearchError;
-
-        private ConnectionFutureResultImpl() {
-            this.futureSearchSuccess = new Function<Result, Connection, LdapException>() {
-
-                @Override
-                public Connection apply(Result result) throws LdapException {
-                    if (isComplete.compareAndSet(false, true)) {
-                        heartBeatConnection = adaptConnection(connection);
-                    }
-
-                    return heartBeatConnection;
-                }
-            };
-
-            this.futureSearchError = new Function<LdapException, Connection, LdapException>() {
-                @Override
-                public Connection apply(LdapException error) throws LdapException {
-                    manageError(error);
-                    throw heartBeatError;
-                }
-            };
-        }
-
-        @Override
-        public void run() {
-            manageError(newHeartBeatTimeoutError());
-        }
-
-        private void manageError(LdapException error) {
-            if (isComplete.compareAndSet(false, true)) {
-                // Ensure that the connection is closed.
-                if (connection != null) {
-                    connection.close();
-                    connection = null;
-                }
-                releaseScheduler();
-                heartBeatError = adaptHeartBeatError(error);
-            }
-        }
-
-    }
-
     /**
      * A connection that sends heart beats and supports all operations.
      */
-    private final class ConnectionImpl extends AbstractAsynchronousConnection implements
-            ConnectionEventListener {
+    private final class ConnectionImpl extends AbstractAsynchronousConnection implements ConnectionEventListener {
 
-        /**
-         * A result handler wrapper for operations which timestamps the
-         * connection for each response received. The wrapper ensures that
-         * completed requests are removed from the {@code pendingResults} queue,
-         * as well as ensuring that requests are only completed once.
-         */
-        private abstract class AbstractWrappedResultHandler<R> implements ResultHandler<R> {
-            private final AtomicBoolean completed = new AtomicBoolean();
-
-            @Override
-            public void handleError(final LdapException error) {
-                if (tryComplete()) {
-                    timestamp(error);
-                }
-            }
-
-            @Override
-            public void handleResult(final R result) {
-                if (tryComplete()) {
-                    timestamp(result);
-                }
-            }
-
-            public boolean isDone() {
-                return completed.get();
-            }
-
-            abstract void releaseBindOrStartTLSLockIfNeeded();
-
-            /**
-             * 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() {
-                if (pendingResults.remove(this)) {
-                    releaseBindOrStartTLSLockIfNeeded();
-                    completed.set(true);
-                    return true;
-                } else {
-                    return false;
-                }
-            }
-
-        }
-
-        /**
-         * Runs pending request once the shared lock becomes available (when no
-         * heart beat is in progress).
-         *
-         * @param <R>
-         *            The type of result returned by the request.
-         */
-        private abstract class DelayedFuture<R extends Result> extends FutureResultImpl<R> implements Runnable {
-            private volatile FutureResult<R> innerFuture = null;
-
-            @Override
-            public final int getRequestID() {
-                return innerFuture != null ? innerFuture.getRequestID() : -1;
-            }
-
-            @Override
-            public final void run() {
-                if (!isCancelled()) {
-                    sync.lockShared(); // Will not block.
-                    innerFuture = dispatch();
-                    if (isCancelled() && !innerFuture.isCancelled()) {
-                        innerFuture.cancel(false);
+        private class LdapPromiseImplWrapper<R> extends LdapPromiseImpl<R> {
+            protected LdapPromiseImplWrapper(final LdapPromise<R> wrappedPromise) {
+                super(new PromiseImpl<R, LdapException>() {
+                    @Override
+                    protected LdapException tryCancel(boolean mayInterruptIfRunning) {
+                        /*
+                         * FIXME: if the inner cancel succeeds then this promise will be
+                         * completed and we can never indicate that this cancel request
+                         * has succeeded.
+                         */
+                        wrappedPromise.cancel(mayInterruptIfRunning);
+                        return null;
                     }
-                }
-            }
-
-            protected abstract FutureResult<R> dispatch();
-
-            @Override
-            protected final LdapException tryCancel(final boolean mayInterruptIfRunning) {
-                if (innerFuture != null) {
-                    innerFuture.cancel(mayInterruptIfRunning);
-                }
-                return null;
-            }
-        }
-
-        /**
-         * 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> {
-            @Override
-            void releaseBindOrStartTLSLockIfNeeded() {
-                releaseBindOrStartTLSLock();
-            }
-        }
-
-        /**
-         * 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> {
-            @Override
-            void releaseBindOrStartTLSLockIfNeeded() {
-                // No-op for normal operations.
-            }
-        }
-
-        /**
-         * A result handler wrapper for search operations. Ensures that search
-         * results are not sent once the request has been completed (see
-         * markComplete()).
-         */
-        private final class WrappedSearchResultHandler extends AbstractWrappedResultHandler<Result> implements
-                SearchResultHandler {
-            private final SearchResultHandler entryHandler;
-
-            WrappedSearchResultHandler(final SearchResultHandler handler) {
-                this.entryHandler = handler;
-            }
-
-            @Override
-            public synchronized boolean handleEntry(final SearchResultEntry entry) {
-                if (!isDone()) {
-                    if (entryHandler != null) {
-                        entryHandler.handleEntry(timestamp(entry));
-                    } else {
-                        timestamp(entry);
-                    }
-                    return true;
-                } else {
-                    return false;
-                }
-            }
-
-            @Override
-            public synchronized boolean handleReference(final SearchResultReference reference) {
-                if (!isDone()) {
-                    if (entryHandler != null) {
-                        entryHandler.handleReference(timestamp(reference));
-                    } else {
-                        timestamp(reference);
-                    }
-                    return true;
-                } else {
-                    return false;
-                }
-            }
-
-            @Override
-            void releaseBindOrStartTLSLockIfNeeded() {
-                // No-op for normal operations.
+                }, wrappedPromise.getRequestID());
             }
         }
 
@@ -330,22 +136,6 @@
         private final Connection connection;
 
         /**
-         * Search result handler for processing heart beat responses.
-         */
-        private final SearchResultHandler heartBeatEntryHandler = new SearchResultHandler() {
-            @Override
-            public boolean handleEntry(final SearchResultEntry entry) {
-                timestamp(entry);
-                return true;
-            }
-            @Override
-            public boolean handleReference(final SearchResultReference reference) {
-                timestamp(reference);
-                return true;
-            }
-        };
-
-        /**
          * List of pending Bind or StartTLS requests which must be invoked once
          * the current heart beat completes.
          */
@@ -357,8 +147,8 @@
          * signalled if no heart beat is detected within the permitted timeout
          * period.
          */
-        private final Queue<AbstractWrappedResultHandler<?>> pendingResults =
-                new ConcurrentLinkedQueue<AbstractWrappedResultHandler<?>>();
+        private final Queue<ResultHandler<?>> pendingResults =
+                new ConcurrentLinkedQueue<ResultHandler<?>>();
 
         /** Internal connection state. */
         private final ConnectionState state = new ConnectionState();
@@ -378,17 +168,17 @@
         }
 
         @Override
-        public FutureResult<Void> abandonAsync(final AbandonRequest request) {
+        public LdapPromise<Void> abandonAsync(final AbandonRequest request) {
             return connection.abandonAsync(request);
         }
 
         @Override
-        public FutureResult<Result> addAsync(final AddRequest request,
+        public LdapPromise<Result> addAsync(final AddRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             if (checkState()) {
-                return then(connection.addAsync(request, intermediateResponseHandler), createResultHandler());
+                return timestampPromise(connection.addAsync(request, intermediateResponseHandler));
             } else {
-                return newConnectionErrorFuture();
+                return newConnectionErrorPromise();
             }
         }
 
@@ -398,35 +188,23 @@
         }
 
         @Override
-        public FutureResult<BindResult> bindAsync(final BindRequest request,
+        public LdapPromise<BindResult> bindAsync(final BindRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             if (checkState()) {
                 if (sync.tryLockShared()) {
                     // Fast path
-                    return then(connection.bindAsync(request, intermediateResponseHandler), wrapForBindOrStartTLS());
+                    return timestampBindOrStartTLSPromise(connection.bindAsync(request, intermediateResponseHandler));
                 } 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>() {
+                    return enqueueBindOrStartTLSPromise(new AsyncFunction<Void, BindResult, LdapException>() {
                         @Override
-                        public FutureResult<BindResult> dispatch() {
-                            return HeartBeatConnectionFactory.this.then(
-                                connection.bindAsync(request, intermediateResponseHandler), wrapForBindOrStartTLS());
+                        public Promise<BindResult, LdapException> apply(Void value) throws LdapException {
+                            return timestampBindOrStartTLSPromise(connection.bindAsync(request,
+                                    intermediateResponseHandler));
                         }
-                    };
-                    /*
-                     * Enqueue and flush if the heart beat has completed in the
-                     * mean time.
-                     */
-                    pendingBindOrStartTLSRequests.offer(future);
-                    flushPendingBindOrStartTLSRequests();
-                    return future;
+                    });
                 }
             } else {
-                return newConnectionErrorFuture();
+                return newConnectionErrorPromise();
             }
         }
 
@@ -443,79 +221,61 @@
         }
 
         @Override
-        public FutureResult<CompareResult> compareAsync(final CompareRequest request,
+        public LdapPromise<CompareResult> compareAsync(final CompareRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             if (checkState()) {
-                return then(connection.compareAsync(request, intermediateResponseHandler), createResultHandler());
+                return timestampPromise(connection.compareAsync(request, intermediateResponseHandler));
             } else {
-                return newConnectionErrorFuture();
+                return newConnectionErrorPromise();
             }
         }
 
         @Override
-        public FutureResult<Result> deleteAsync(final DeleteRequest request,
+        public LdapPromise<Result> deleteAsync(final DeleteRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             if (checkState()) {
-                return then(connection.deleteAsync(request, intermediateResponseHandler), createResultHandler());
+                return timestampPromise(connection.deleteAsync(request, intermediateResponseHandler));
             } else {
-                return newConnectionErrorFuture();
+                return newConnectionErrorPromise();
             }
         }
 
         @Override
-        public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request,
+        public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             if (checkState()) {
                 if (isStartTLSRequest(request)) {
                     if (sync.tryLockShared()) {
                         // Fast path
-                        return then(connection.extendedRequestAsync(request, intermediateResponseHandler),
-                                wrapForBindOrStartTLS());
+                        return timestampBindOrStartTLSPromise(connection.extendedRequestAsync(request,
+                                intermediateResponseHandler));
                     } 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>() {
+                        return enqueueBindOrStartTLSPromise(new AsyncFunction<Void, R, LdapException>() {
                             @Override
-                            public FutureResult<R> dispatch() {
-                                return HeartBeatConnectionFactory.this.then(
-                                        connection.extendedRequestAsync(request, intermediateResponseHandler),
-                                        wrapForBindOrStartTLS());
+                            public Promise<R, LdapException> apply(Void value) throws LdapException {
+                                return timestampBindOrStartTLSPromise(connection.extendedRequestAsync(
+                                        request, intermediateResponseHandler));
                             }
-                        };
-
-                        /*
-                         * Enqueue and flush if the heart beat has completed in
-                         * the mean time.
-                         */
-                        pendingBindOrStartTLSRequests.offer(future);
-                        flushPendingBindOrStartTLSRequests();
-                        return future;
+                        });
                     }
                 } else {
-                    return then(connection.extendedRequestAsync(request, intermediateResponseHandler),
-                            createResultHandler());
+                    return timestampPromise(connection.extendedRequestAsync(request, intermediateResponseHandler));
                 }
             } else {
-                return newConnectionErrorFuture();
+                return newConnectionErrorPromise();
             }
         }
 
         @Override
         public void handleConnectionClosed() {
             if (state.notifyConnectionClosed()) {
-                failPendingResults(newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED,
+                failPendingResults(newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED,
                         HBCF_CONNECTION_CLOSED_BY_CLIENT.get()));
                 synchronized (validConnections) {
                     connection.removeConnectionEventListener(this);
                     validConnections.remove(this);
                     if (validConnections.isEmpty()) {
-                        /*
-                         * This is the last active connection, so stop the
-                         * heartbeat.
-                         */
+                        // This is the last active connection, so stop the heartbeat.
                         heartBeatFuture.cancel(false);
                     }
                 }
@@ -547,22 +307,22 @@
         }
 
         @Override
-        public FutureResult<Result> modifyAsync(final ModifyRequest request,
+        public LdapPromise<Result> modifyAsync(final ModifyRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             if (checkState()) {
-                return then(connection.modifyAsync(request, intermediateResponseHandler), createResultHandler());
+                return timestampPromise(connection.modifyAsync(request, intermediateResponseHandler));
             } else {
-                return newConnectionErrorFuture();
+                return newConnectionErrorPromise();
             }
         }
 
         @Override
-        public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
+        public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
             if (checkState()) {
-                return then(connection.modifyDNAsync(request, intermediateResponseHandler), createResultHandler());
+                return timestampPromise(connection.modifyDNAsync(request, intermediateResponseHandler));
             } else {
-                return newConnectionErrorFuture();
+                return newConnectionErrorPromise();
             }
         }
 
@@ -572,15 +332,44 @@
         }
 
         @Override
-        public FutureResult<Result> searchAsync(final SearchRequest request,
+        public LdapPromise<Result> searchAsync(final SearchRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler,
                 final SearchResultHandler searchHandler) {
             if (checkState()) {
-                final WrappedSearchResultHandler entryHandler = wrap(searchHandler);
-                return then(connection.searchAsync(request, intermediateResponseHandler, entryHandler),
-                        createResultHandler());
+                final AtomicBoolean searchDone = new AtomicBoolean();
+                final SearchResultHandler entryHandler = new SearchResultHandler() {
+                    @Override
+                    public synchronized boolean handleEntry(SearchResultEntry entry) {
+                        if (!searchDone.get()) {
+                            timestamp(entry);
+                            if (searchHandler != null) {
+                                searchHandler.handleEntry(entry);
+                            }
+                        }
+                        return true;
+                    }
+
+                    @Override
+                    public synchronized boolean handleReference(SearchResultReference reference) {
+                        if (!searchDone.get()) {
+                            timestamp(reference);
+                            if (searchHandler != null) {
+                                searchHandler.handleReference(reference);
+                            }
+                        }
+                        return true;
+                    }
+                };
+
+                return timestampPromise(connection.searchAsync(request,
+                        intermediateResponseHandler, entryHandler).onSuccessOrFailure(new Runnable() {
+                            @Override
+                            public void run() {
+                                searchDone.getAndSet(true);
+                            }
+                        }));
             } else {
-                return newConnectionErrorFuture();
+                return newConnectionErrorPromise();
             }
         }
 
@@ -620,7 +409,7 @@
              * Peek instead of pool because notification is responsible for
              * removing the element from the queue.
              */
-            AbstractWrappedResultHandler<?> pendingResult;
+            ResultHandler<?> pendingResult;
             while ((pendingResult = pendingResults.peek()) != null) {
                 pendingResult.handleError(error);
             }
@@ -629,9 +418,8 @@
         private void flushPendingBindOrStartTLSRequests() {
             if (!pendingBindOrStartTLSRequests.isEmpty()) {
                 /*
-                 * The pending requests will acquire the shared lock, but we
-                 * take it here anyway to ensure that pending requests do not
-                 * get blocked.
+                 * The pending requests will acquire the shared lock, but we take
+                 * it here anyway to ensure that pending requests do not get blocked.
                  */
                 if (sync.tryLockShared()) {
                     try {
@@ -651,8 +439,8 @@
             return request.getOID().equals(StartTLSExtendedRequest.OID);
         }
 
-        private <R> FutureResult<R> newConnectionErrorFuture() {
-            return FutureResultWrapper.newFailedFutureResult(state.getConnectionError());
+        private <R> LdapPromise<R> newConnectionErrorPromise() {
+            return newFailedLdapPromise(state.getConnectionError());
         }
 
         private void releaseBindOrStartTLSLock() {
@@ -667,8 +455,7 @@
         /**
          * Sends a heart beat on this connection if required to do so.
          *
-         * @return {@code true} if a heart beat was sent, otherwise
-         *         {@code false}.
+         * @return {@code true} if a heart beat was sent, otherwise {@code false}.
          */
         private boolean sendHeartBeat() {
             /*
@@ -695,39 +482,46 @@
              */
             if (sync.tryLockExclusively()) {
                 try {
-                    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<LdapException>() {
-                            @Override
-                            public void handleError(LdapException 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();
+                    connection.searchAsync(heartBeatRequest, new SearchResultHandler() {
+                        @Override
+                        public boolean handleEntry(final SearchResultEntry entry) {
+                            timestamp(entry);
+                            return true;
+                        }
 
+                        @Override
+                        public boolean handleReference(final SearchResultReference reference) {
+                            timestamp(reference);
+                            return true;
+                        }
+                    }).onSuccess(new SuccessHandler<Result>() {
+                        @Override
+                        public void handleResult(Result result) {
+                            timestamp(result);
+                            releaseHeartBeatLock();
+                        }
+                    }).onFailure(new FailureHandler<LdapException>() {
+                        @Override
+                        public void handleError(LdapException 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
@@ -757,22 +551,66 @@
             return response;
         }
 
-        private <R> WrappedResultHandler<R> createResultHandler() {
-            final WrappedResultHandler<R> h = new WrappedResultHandler<R>();
-            pendingResults.add(h);
-            return h;
+        private <R extends Result> LdapPromise<R> enqueueBindOrStartTLSPromise(
+                AsyncFunction<Void, R, LdapException> doRequest) {
+            /*
+             * A heart beat must be in progress so create a runnable task which
+             * will be executed when the heart beat completes.
+             */
+            final LdapPromiseImpl<Void> promise = newLdapPromiseImpl();
+            final LdapPromise<R> result = promise.thenAsync(doRequest);
+
+            // Enqueue and flush if the heart beat has completed in the mean
+            // time.
+            pendingBindOrStartTLSRequests.offer(new Runnable() {
+                @Override
+                public void run() {
+                    // FIXME: Handle cancel chaining.
+                    if (!result.isCancelled()) {
+                        sync.lockShared(); // Will not block.
+                        promise.handleResult((Void) null);
+                    }
+                }
+            });
+            flushPendingBindOrStartTLSRequests();
+            return result;
         }
 
-        private WrappedSearchResultHandler wrap(final SearchResultHandler handler) {
-            final WrappedSearchResultHandler h = new WrappedSearchResultHandler(handler);
-            pendingResults.add(h);
-            return h;
+        private <R extends Result> LdapPromise<R> timestampPromise(LdapPromise<R> wrappedPromise) {
+            final LdapPromiseImpl<R> outerPromise = new LdapPromiseImplWrapper<R>(wrappedPromise);
+            pendingResults.add(outerPromise);
+            wrappedPromise.onSuccess(new SuccessHandler<R>() {
+                @Override
+                public void handleResult(R result) {
+                    outerPromise.handleResult(result);
+                    timestamp(result);
+                }
+            }).onFailure(new FailureHandler<LdapException>() {
+                @Override
+                public void handleError(LdapException error) {
+                    outerPromise.handleError(error);
+                    timestamp(error);
+                }
+            });
+            outerPromise.onSuccessOrFailure(new Runnable() {
+                @Override
+                public void run() {
+                    pendingResults.remove(outerPromise);
+                }
+            });
+            if (!checkState()) {
+                outerPromise.handleError(state.getConnectionError());
+            }
+            return outerPromise;
         }
 
-        private <R> WrappedBindOrStartTLSResultHandler<R> wrapForBindOrStartTLS() {
-            final WrappedBindOrStartTLSResultHandler<R> h = new WrappedBindOrStartTLSResultHandler<R>();
-            pendingResults.add(h);
-            return h;
+        private <R extends Result> LdapPromise<R> timestampBindOrStartTLSPromise(LdapPromise<R> wrappedPromise) {
+            return timestampPromise(wrappedPromise).onSuccessOrFailure(new Runnable() {
+                @Override
+                public void run() {
+                    releaseBindOrStartTLSLock();
+                }
+            });
         }
     }
 
@@ -1054,25 +892,15 @@
          * server is responding.
          */
         acquireScheduler(); // Protect scheduler.
-        boolean succeeded = false;
+        Connection connection = null;
         try {
-            final Connection connection = factory.getConnection();
-            try {
-                connection.searchAsync(heartBeatRequest, null).getOrThrow(timeoutMS,
-                        TimeUnit.MILLISECONDS);
-                succeeded = true;
-                return adaptConnection(connection);
-            } catch (final Exception e) {
-                throw adaptHeartBeatError(e);
-            } finally {
-                if (!succeeded) {
-                    connection.close();
-                }
-            }
-        } finally {
-            if (!succeeded) {
-                releaseScheduler();
-            }
+            connection = factory.getConnection();
+            connection.searchAsync(heartBeatRequest, null).getOrThrow(timeoutMS, TimeUnit.MILLISECONDS);
+            return registerConnection(new ConnectionImpl(connection));
+        } catch (final Exception e) {
+            closeSilently(connection);
+            releaseScheduler();
+            throw adaptHeartBeatError(e);
         }
     }
 
@@ -1080,21 +908,46 @@
     public Promise<Connection, LdapException> getConnectionAsync() {
         acquireScheduler(); // Protect scheduler.
 
-        // Create a future responsible for chaining the initial heartbeat
-        // search.
-        final ConnectionFutureResultImpl compositeFuture = new ConnectionFutureResultImpl();
+        final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>();
+        final PromiseImpl<Connection, LdapException> promise = PromiseImpl.create();
 
-        // Request a connection and return the future representing the
-        // heartbeat.
-        return factory.getConnectionAsync().thenAsync(new AsyncFunction<Connection, Result, LdapException>() {
+        // Request a connection and return the promise representing the heartbeat.
+        factory.getConnectionAsync().thenAsync(new AsyncFunction<Connection, Result, LdapException>() {
             @Override
-            public Promise<Result, LdapException> apply(final Connection connectionResult) {
+            public Promise<Result, LdapException> apply(final Connection connection) {
                 // 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);
+                connectionHolder.set(connection);
+                scheduler.get().schedule(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (promise.tryHandleError(newHeartBeatTimeoutError())) {
+                            closeSilently(connectionHolder.get());
+                            releaseScheduler();
+                        }
+                    }
+                }, timeoutMS, TimeUnit.MILLISECONDS);
+                return connection.searchAsync(heartBeatRequest, null);
             }
-        }).then(compositeFuture.futureSearchSuccess, compositeFuture.futureSearchError);
+        }).onSuccess(new SuccessHandler<Result>() {
+            @Override
+            public void handleResult(Result result) {
+                final Connection connection = connectionHolder.get();
+                final ConnectionImpl connectionImpl = new ConnectionImpl(connection);
+                if (!promise.tryHandleResult(registerConnection(connectionImpl))) {
+                    connectionImpl.close();
+                }
+            }
+        }).onFailure(new FailureHandler<LdapException>() {
+            @Override
+            public void handleError(LdapException error) {
+                if (promise.tryHandleError(adaptHeartBeatError(error))) {
+                    closeSilently(connectionHolder.get());
+                    releaseScheduler();
+                }
+            }
+        });
+
+        return promise;
     }
 
     @Override
@@ -1106,35 +959,27 @@
         return builder.toString();
     }
 
-    private Connection adaptConnection(final Connection connection) {
+    private Connection registerConnection(final ConnectionImpl heartBeatConnection) {
         synchronized (validConnections) {
-            final ConnectionImpl heartBeatConnection = new ConnectionImpl(connection);
             if (validConnections.isEmpty()) {
                 /* This is the first active connection, so start the heart beat. */
                 heartBeatFuture =
-                        scheduler.get().scheduleWithFixedDelay(sendHeartBeatRunnable, 0, interval,
-                                intervalUnit);
+                        scheduler.get().scheduleWithFixedDelay(sendHeartBeatRunnable, 0, interval, intervalUnit);
             }
             validConnections.add(heartBeatConnection);
             return heartBeatConnection;
         }
     }
 
-    private <R extends Result> FutureResult<R> then(FutureResult<R> future,
-            ResultHandler<? super Object> resultHandler) {
-        return (FutureResult<R>) future.onSuccess(resultHandler).onFailure(resultHandler);
-    }
-
     private LdapException adaptHeartBeatError(final Exception error) {
         if (error instanceof ConnectionException) {
             return (LdapException) error;
         } else if (error instanceof TimeoutResultException || error instanceof TimeoutException) {
             return newHeartBeatTimeoutError();
         } else if (error instanceof InterruptedException) {
-            return newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, error);
+            return newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, error);
         } else {
-            return newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN, HBCF_HEARTBEAT_FAILED.get(),
-                    error);
+            return newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN, HBCF_HEARTBEAT_FAILED.get(), error);
         }
     }
 
@@ -1147,7 +992,7 @@
     }
 
     private LdapException newHeartBeatTimeoutError() {
-        return newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN, HBCF_HEARTBEAT_TIMEOUT
+        return newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN, HBCF_HEARTBEAT_TIMEOUT
                 .get(timeoutMS));
     }
 }
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 4d753d4..9e8dd16 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
@@ -44,57 +44,23 @@
 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.spi.AbstractLDAPFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.LDAPCompareFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.LDAPExtendedFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.LDAPFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.LDAPSearchFutureResultImpl;
+import org.forgerock.opendj.ldap.spi.BindResultLdapPromiseImpl;
+import org.forgerock.opendj.ldap.spi.ExtendedResultLdapPromiseImpl;
+import org.forgerock.opendj.ldap.spi.ResultLdapPromiseImpl;
+import org.forgerock.opendj.ldap.spi.SearchResultLdapPromiseImpl;
 import org.forgerock.util.Reject;
 
+import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
+
 /**
  * This class defines a pseudo-connection object that can be used for performing
  * internal operations directly against a {@code ServerConnection}
  * implementation.
  */
 final class InternalConnection extends AbstractAsynchronousConnection {
-    private static final class InternalBindFutureResultImpl extends
-            AbstractLDAPFutureResultImpl<BindResult> {
-        private final BindRequest bindRequest;
-
-        InternalBindFutureResultImpl(final int messageID, final BindRequest bindRequest,
-                final IntermediateResponseHandler intermediateResponseHandler,
-                final Connection connection) {
-            super(messageID, intermediateResponseHandler, connection);
-            this.bindRequest = bindRequest;
-        }
-
-        @Override
-        public String toString() {
-            final StringBuilder sb = new StringBuilder();
-            sb.append("InternalBindFutureResultImpl(");
-            sb.append("bindRequest = ");
-            sb.append(bindRequest);
-            super.toString(sb);
-            sb.append(")");
-            return sb.toString();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        protected BindResult newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
-                final Throwable cause) {
-            return Responses.newBindResult(resultCode).setDiagnosticMessage(diagnosticMessage)
-                    .setCause(cause);
-        }
-    }
-
     private final ServerConnection<Integer> serverConnection;
-    private final List<ConnectionEventListener> listeners =
-            new CopyOnWriteArrayList<ConnectionEventListener>();
+    private final List<ConnectionEventListener> listeners = new CopyOnWriteArrayList<ConnectionEventListener>();
     private final AtomicInteger messageID = new AtomicInteger();
 
     /**
@@ -111,22 +77,23 @@
      * {@inheritDoc}
      */
     @Override
-    public FutureResult<Void> abandonAsync(final AbandonRequest request) {
+    public LdapPromise<Void> abandonAsync(final AbandonRequest request) {
         final int i = messageID.getAndIncrement();
         serverConnection.handleAbandon(i, request);
-        return FutureResultWrapper.newSuccessfulFutureResult((Void) null, i);
+        return newSuccessfulLdapPromise((Void) null, i);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public FutureResult<Result> addAsync(final AddRequest request,
+    public LdapPromise<Result> addAsync(final AddRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         final int i = messageID.getAndIncrement();
-        final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request, intermediateResponseHandler, this);
-        serverConnection.handleAdd(i, request, future, future);
-        return future;
+        final ResultLdapPromiseImpl<AddRequest, Result> promise =
+                newResultLdapPromise(i, request, intermediateResponseHandler, this);
+        serverConnection.handleAdd(i, request, promise, promise);
+        return promise;
     }
 
     /**
@@ -142,13 +109,13 @@
      * {@inheritDoc}
      */
     @Override
-    public FutureResult<BindResult> bindAsync(final BindRequest request,
+    public LdapPromise<BindResult> bindAsync(final BindRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         final int i = messageID.getAndIncrement();
-        final InternalBindFutureResultImpl future = new InternalBindFutureResultImpl(i, request,
-                intermediateResponseHandler, this);
-        serverConnection.handleBind(i, 3, request, future, future);
-        return future;
+        final BindResultLdapPromiseImpl promise =
+                newBindLdapPromise(i, request, null, intermediateResponseHandler, this);
+        serverConnection.handleBind(i, 3, request, promise, promise);
+        return promise;
     }
 
     /**
@@ -164,38 +131,39 @@
      * {@inheritDoc}
      */
     @Override
-    public FutureResult<CompareResult> compareAsync(final CompareRequest request,
+    public LdapPromise<CompareResult> compareAsync(final CompareRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         final int i = messageID.getAndIncrement();
-        final LDAPCompareFutureResultImpl future = new LDAPCompareFutureResultImpl(i, request,
-                intermediateResponseHandler, this);
-        serverConnection.handleCompare(i, request, future, future);
-        return future;
+        final ResultLdapPromiseImpl<CompareRequest, CompareResult> promise =
+                newCompareLdapPromise(i, request, intermediateResponseHandler, this);
+        serverConnection.handleCompare(i, request, promise, promise);
+        return promise;
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public FutureResult<Result> deleteAsync(final DeleteRequest request,
+    public LdapPromise<Result> deleteAsync(final DeleteRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         final int i = messageID.getAndIncrement();
-        final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request, intermediateResponseHandler, this);
-        serverConnection.handleDelete(i, request, future, future);
-        return future;
+        final ResultLdapPromiseImpl<DeleteRequest, Result> promise =
+                newResultLdapPromise(i, request, intermediateResponseHandler, this);
+        serverConnection.handleDelete(i, request, promise, promise);
+        return promise;
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request,
+    public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         final int i = messageID.getAndIncrement();
-        final LDAPExtendedFutureResultImpl<R> future = new LDAPExtendedFutureResultImpl<R>(i, request,
-                intermediateResponseHandler, this);
-        serverConnection.handleExtendedRequest(i, request, future, future);
-        return future;
+        final ExtendedResultLdapPromiseImpl<R> promise = newExtendedLdapPromise(i, request, intermediateResponseHandler,
+                this);
+        serverConnection.handleExtendedRequest(i, request, promise, promise);
+        return promise;
     }
 
     /**
@@ -220,24 +188,26 @@
      * {@inheritDoc}
      */
     @Override
-    public FutureResult<Result> modifyAsync(final ModifyRequest request,
+    public LdapPromise<Result> modifyAsync(final ModifyRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         final int i = messageID.getAndIncrement();
-        final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request, intermediateResponseHandler, this);
-        serverConnection.handleModify(i, request, future, future);
-        return future;
+        final ResultLdapPromiseImpl<ModifyRequest, Result> promise =
+                newResultLdapPromise(i, request, intermediateResponseHandler, this);
+        serverConnection.handleModify(i, request, promise, promise);
+        return promise;
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
+    public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         final int i = messageID.getAndIncrement();
-        final LDAPFutureResultImpl future = new LDAPFutureResultImpl(i, request, intermediateResponseHandler, this);
-        serverConnection.handleModifyDN(i, request, future, future);
-        return future;
+        final ResultLdapPromiseImpl<ModifyDNRequest, Result> promise =
+                newResultLdapPromise(i, request, intermediateResponseHandler, this);
+        serverConnection.handleModifyDN(i, request, promise, promise);
+        return promise;
     }
 
     /**
@@ -253,13 +223,13 @@
      * {@inheritDoc}
      */
     @Override
-    public FutureResult<Result> searchAsync(final SearchRequest request,
+    public LdapPromise<Result> searchAsync(final SearchRequest request,
             final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) {
         final int i = messageID.getAndIncrement();
-        final LDAPSearchFutureResultImpl future = new LDAPSearchFutureResultImpl(i, request, entryHandler,
-                intermediateResponseHandler, this);
-        serverConnection.handleSearch(i, request, future, future, future);
-        return future;
+        final SearchResultLdapPromiseImpl promise =
+                newSearchLdapPromise(i, request, entryHandler, intermediateResponseHandler, this);
+        serverConnection.handleSearch(i, request, promise, promise, promise);
+        return promise;
     }
 
     /**
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 ba2414b..a369367 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
@@ -29,7 +29,7 @@
 
 import org.forgerock.util.promise.Promise;
 
-import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
+import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
 import static org.forgerock.util.promise.Promises.*;
 
 
@@ -79,7 +79,7 @@
         try {
             serverConnection = factory.handleAccept(clientContext);
         } catch (final LdapException e) {
-            return newFailedFutureResult(e);
+            return newFailedLdapPromise(e);
         }
 
         return newSuccessfulPromise((Connection) new InternalConnection(serverConnection));
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/LdapException.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/LdapException.java
index b54bf1d..81a35f4 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/LdapException.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/LdapException.java
@@ -41,23 +41,23 @@
 public class LdapException extends IOException {
 
     /**
-     * Creates a new error result exception with the provided result code and an
+     * Creates a new LDAP exception with the provided result code and an
      * empty diagnostic message.
      *
      * @param resultCode
      *            The result code.
-     * @return The new error result exception.
+     * @return The new LDAP exception.
      * @throws IllegalArgumentException
      *             If the provided result code does not represent a failure.
      * @throws NullPointerException
      *             If {@code resultCode} was {@code null}.
      */
-    public static LdapException newErrorResult(ResultCode resultCode) {
-        return newErrorResult(resultCode, null, null);
+    public static LdapException newLdapException(ResultCode resultCode) {
+        return newLdapException(resultCode, null, null);
     }
 
     /**
-     * Creates a new error result exception with the provided result code and
+     * Creates a new LDAP exception with the provided result code and
      * diagnostic message.
      *
      * @param resultCode
@@ -65,19 +65,19 @@
      * @param diagnosticMessage
      *            The diagnostic message, which may be empty or {@code null}
      *            indicating that none was provided.
-     * @return The new error result exception.
+     * @return The new LDAP exception.
      * @throws IllegalArgumentException
      *             If the provided result code does not represent a failure.
      * @throws NullPointerException
      *             If {@code resultCode} was {@code null}.
      */
-    public static LdapException newErrorResult(ResultCode resultCode,
+    public static LdapException newLdapException(ResultCode resultCode,
             CharSequence diagnosticMessage) {
-        return newErrorResult(resultCode, diagnosticMessage, null);
+        return newLdapException(resultCode, diagnosticMessage, null);
     }
 
     /**
-     * Creates a new error result exception with the provided result code and
+     * Creates a new LDAP exception with the provided result code and
      * cause. The diagnostic message will be taken from the cause, if provided.
      *
      * @param resultCode
@@ -85,18 +85,18 @@
      * @param cause
      *            The throwable cause, which may be {@code null} indicating that
      *            none was provided.
-     * @return The new error result exception.
+     * @return The new LDAP exception.
      * @throws IllegalArgumentException
      *             If the provided result code does not represent a failure.
      * @throws NullPointerException
      *             If {@code resultCode} was {@code null}.
      */
-    public static LdapException newErrorResult(ResultCode resultCode, Throwable cause) {
-        return newErrorResult(resultCode, null, cause);
+    public static LdapException newLdapException(ResultCode resultCode, Throwable cause) {
+        return newLdapException(resultCode, null, cause);
     }
 
     /**
-     * Creates a new error result exception with the provided result code,
+     * Creates a new LDAP exception with the provided result code,
      * diagnostic message, and cause.
      *
      * @param resultCode
@@ -107,13 +107,13 @@
      * @param cause
      *            The throwable cause, which may be {@code null} indicating that
      *            none was provided.
-     * @return The new error result exception.
+     * @return The new LDAP exception.
      * @throws IllegalArgumentException
      *             If the provided result code does not represent a failure.
      * @throws NullPointerException
      *             If {@code resultCode} was {@code null}.
      */
-    public static LdapException newErrorResult(ResultCode resultCode,
+    public static LdapException newLdapException(ResultCode resultCode,
             CharSequence diagnosticMessage, Throwable cause) {
         final Result result = Responses.newResult(resultCode);
         if (diagnosticMessage != null) {
@@ -122,21 +122,21 @@
             result.setDiagnosticMessage(cause.getLocalizedMessage());
         }
         result.setCause(cause);
-        return newErrorResult(result);
+        return newLdapException(result);
     }
 
     /**
-     * Creates a new error result exception using the provided result.
+     * Creates a new LDAP exception using the provided result.
      *
      * @param result
      *            The result whose result code indicates a failure.
-     * @return The error result exception wrapping the provided result.
+     * @return The LDAP exception wrapping the provided result.
      * @throws IllegalArgumentException
      *             If the provided result does not represent a failure.
      * @throws NullPointerException
      *             If {@code result} was {@code null}.
      */
-    public static LdapException newErrorResult(final Result result) {
+    public static LdapException newLdapException(final Result result) {
         if (!result.getResultCode().isExceptional()) {
             throw new IllegalArgumentException("Attempted to wrap a successful result: " + result);
         }
@@ -196,7 +196,7 @@
     private final Result result;
 
     /**
-     * Creates a new error result exception using the provided result.
+     * Creates a new LDAP exception using the provided result.
      *
      * @param result
      *            The error result.
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/LdapPromise.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/LdapPromise.java
new file mode 100644
index 0000000..7d6c59d
--- /dev/null
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/LdapPromise.java
@@ -0,0 +1,75 @@
+/*
+ * 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 2014 ForgeRock AS
+ */
+
+package org.forgerock.opendj.ldap;
+
+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;
+
+/**
+ * A handle which can be used to retrieve the Result of an asynchronous Request.
+ *
+ * @param <S>
+ *            The type of result returned by this promise.
+ */
+public interface LdapPromise<S> extends Promise<S, LdapException> {
+    /**
+     * Returns the request ID of the request if appropriate.
+     *
+     * @return The request ID, or {@code -1} if there is no request ID.
+     */
+    int getRequestID();
+
+    @Override
+    LdapPromise<S> onSuccess(SuccessHandler<? super S> onSuccess);
+
+    @Override
+    LdapPromise<S> onFailure(FailureHandler<? super LdapException> onFailure);
+
+    @Override
+    LdapPromise<S> onSuccessOrFailure(Runnable onSuccessOrFailure);
+
+    @Override
+    // @Checkstyle:ignore
+    <VOUT> LdapPromise<VOUT> then(Function<? super S, VOUT, LdapException> onSuccess);
+
+    @Override
+    LdapPromise<S> then(SuccessHandler<? super S> onSuccess);
+
+    @Override
+    LdapPromise<S> then(SuccessHandler<? super S> onSuccess, FailureHandler<? super LdapException> onFailure);
+
+    @Override
+    LdapPromise<S> thenAlways(Runnable onSuccessOrFailure);
+
+    @Override
+    // @Checkstyle:ignore
+    <VOUT> LdapPromise<VOUT> thenAsync(AsyncFunction<? super S, VOUT, LdapException> onSuccess);
+}
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 851767e..da6cc2a 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
@@ -26,7 +26,7 @@
 
 import static org.forgerock.opendj.ldap.Attributes.singletonAttribute;
 import static org.forgerock.opendj.ldap.Entries.modifyEntry;
-import static org.forgerock.opendj.ldap.LdapException.newErrorResult;
+import static org.forgerock.opendj.ldap.LdapException.newLdapException;
 import static org.forgerock.opendj.ldap.responses.Responses.newBindResult;
 import static org.forgerock.opendj.ldap.responses.Responses.newCompareResult;
 import static org.forgerock.opendj.ldap.responses.Responses.newResult;
@@ -238,7 +238,7 @@
                 final DN dn = request.getName();
                 final DN parent = dn.parent();
                 if (entries.containsKey(dn)) {
-                    throw newErrorResult(ResultCode.ENTRY_ALREADY_EXISTS, "The entry '"
+                    throw newLdapException(ResultCode.ENTRY_ALREADY_EXISTS, "The entry '"
                             + dn.toString() + "' already exists");
                 } else if (!entries.containsKey(parent)) {
                     noSuchObject(parent);
@@ -268,25 +268,25 @@
                         && request.getAuthenticationType() == BindRequest.AUTHENTICATION_TYPE_SIMPLE) {
                     password = ((GenericBindRequest) request).getAuthenticationValue();
                 } else {
-                    throw newErrorResult(ResultCode.PROTOCOL_ERROR,
+                    throw newLdapException(ResultCode.PROTOCOL_ERROR,
                             "non-SIMPLE authentication not supported: "
                                     + request.getAuthenticationType());
                 }
                 entry = getRequiredEntry(null, username);
                 if (!entry.containsAttribute("userPassword", password)) {
-                    throw newErrorResult(ResultCode.INVALID_CREDENTIALS, "Wrong password");
+                    throw newLdapException(ResultCode.INVALID_CREDENTIALS, "Wrong password");
                 }
             }
             resultHandler.handleResult(getBindResult(request, entry, entry));
         } catch (final LocalizedIllegalArgumentException e) {
-            resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, e));
+            resultHandler.handleError(newLdapException(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.handleError(newErrorResult(ResultCode.INVALID_CREDENTIALS,
+            resultHandler.handleError(newLdapException(ResultCode.INVALID_CREDENTIALS,
                     "Unknown user"));
         } catch (final LdapException e) {
             resultHandler.handleError(e);
@@ -332,13 +332,13 @@
                     if (next == null || !next.isChildOf(dn)) {
                         entries.remove(dn);
                     } else {
-                        throw newErrorResult(ResultCode.NOT_ALLOWED_ON_NONLEAF);
+                        throw newLdapException(ResultCode.NOT_ALLOWED_ON_NONLEAF);
                     }
                 }
             }
             resultHandler.handleResult(getResult(request, entry, null));
         } catch (final DecodeException e) {
-            resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, e));
+            resultHandler.handleError(newLdapException(ResultCode.PROTOCOL_ERROR, e));
         } catch (final LdapException e) {
             resultHandler.handleError(e);
         }
@@ -349,7 +349,7 @@
             final RequestContext requestContext, final ExtendedRequest<R> request,
             final IntermediateResponseHandler intermediateResponseHandler,
             final ResultHandler<R> resultHandler) {
-        resultHandler.handleError(newErrorResult(ResultCode.UNWILLING_TO_PERFORM,
+        resultHandler.handleError(newLdapException(ResultCode.UNWILLING_TO_PERFORM,
                 "Extended request operation not supported"));
     }
 
@@ -376,7 +376,7 @@
     public void handleModifyDN(final RequestContext requestContext, final ModifyDNRequest request,
             final IntermediateResponseHandler intermediateResponseHandler,
             final ResultHandler<Result> resultHandler) {
-        resultHandler.handleError(newErrorResult(ResultCode.UNWILLING_TO_PERFORM,
+        resultHandler.handleError(newLdapException(ResultCode.UNWILLING_TO_PERFORM,
                 "ModifyDN request operation not supported"));
     }
 
@@ -403,10 +403,11 @@
                     request.getSizeLimit(), scope,
                     request.getControl(SimplePagedResultsControl.DECODER, new DecodeOptions()));
             } else {
-                throw newErrorResult(ResultCode.PROTOCOL_ERROR, "Search request contains an unsupported search scope");
+                throw newLdapException(ResultCode.PROTOCOL_ERROR,
+                        "Search request contains an unsupported search scope");
             }
         } catch (DecodeException e) {
-            resultHandler.handleError(LdapException.newErrorResult(ResultCode.PROTOCOL_ERROR, e.getMessage(), e));
+            resultHandler.handleError(newLdapException(ResultCode.PROTOCOL_ERROR, e.getMessage(), e));
         } catch (final LdapException e) {
             resultHandler.handleError(e);
         }
@@ -445,7 +446,7 @@
                         final Entry entry = reader.readEntry();
                         final DN dn = entry.getName();
                         if (!overwrite && entries.containsKey(dn)) {
-                            throw newErrorResult(ResultCode.ENTRY_ALREADY_EXISTS,
+                            throw newLdapException(ResultCode.ENTRY_ALREADY_EXISTS,
                                     "Attempted to add the entry '" + dn + "' multiple times");
                         } else {
                             entries.put(dn, entry);
@@ -508,7 +509,7 @@
 
                     // Check size limit.
                     if (sizeLimit > 0 && numberOfResults >= sizeLimit) {
-                        throw newErrorResult(newResult(ResultCode.SIZE_LIMIT_EXCEEDED));
+                        throw newLdapException(newResult(ResultCode.SIZE_LIMIT_EXCEEDED));
                     }
 
                     // Ignore this entry if we haven't reached the first page yet.
@@ -548,7 +549,7 @@
                     request.getControl(PreReadRequestControl.DECODER, decodeOptions);
             if (preRead != null) {
                 if (preRead.isCritical() && before == null) {
-                    throw newErrorResult(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
+                    throw newLdapException(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
                 } else {
                     final AttributeFilter filter =
                             new AttributeFilter(preRead.getAttributes(), schema);
@@ -562,7 +563,7 @@
                     request.getControl(PostReadRequestControl.DECODER, decodeOptions);
             if (postRead != null) {
                 if (postRead.isCritical() && after == null) {
-                    throw newErrorResult(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
+                    throw newLdapException(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
                 } else {
                     final AttributeFilter filter =
                             new AttributeFilter(postRead.getAttributes(), schema);
@@ -572,7 +573,7 @@
             }
             return result;
         } catch (final DecodeException e) {
-            throw newErrorResult(ResultCode.PROTOCOL_ERROR, e);
+            throw newLdapException(ResultCode.PROTOCOL_ERROR, e);
         }
     }
 
@@ -599,13 +600,13 @@
             try {
                 control = request.getControl(AssertionRequestControl.DECODER, decodeOptions);
             } catch (final DecodeException e) {
-                throw newErrorResult(ResultCode.PROTOCOL_ERROR, e);
+                throw newLdapException(ResultCode.PROTOCOL_ERROR, e);
             }
             if (control != null) {
                 final Filter filter = control.getFilter();
                 final Matcher matcher = filter.matcher(schema);
                 if (!matcher.matches(entry).toBoolean()) {
-                    throw newErrorResult(ResultCode.ASSERTION_FAILED, "The filter '"
+                    throw newLdapException(ResultCode.ASSERTION_FAILED, "The filter '"
                             + filter.toString() + "' did not match the entry '"
                             + entry.getName().toString() + "'");
                 }
@@ -619,7 +620,7 @@
     }
 
     private void noSuchObject(final DN dn) throws LdapException {
-        throw newErrorResult(ResultCode.NO_SUCH_OBJECT, "The entry '" + dn.toString()
+        throw newLdapException(ResultCode.NO_SUCH_OBJECT, "The entry '" + dn.toString()
                 + "' does not exist");
     }
 
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 994eeac..93dafbe 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
@@ -33,7 +33,7 @@
 import static com.forgerock.opendj.ldap.CoreMessages.INFO_CANCELED_BY_SERVER_DISCONNECT;
 import static com.forgerock.opendj.ldap.CoreMessages.INFO_CLIENT_CONNECTION_CLOSING;
 import static com.forgerock.opendj.ldap.CoreMessages.WARN_CLIENT_DUPLICATE_MESSAGE_ID;
-import static org.forgerock.opendj.ldap.LdapException.newErrorResult;
+import static org.forgerock.opendj.ldap.LdapException.newLdapException;
 
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -103,7 +103,7 @@
                 final R cancelResult =
                         request.getResultDecoder().newExtendedErrorResult(ResultCode.TOO_LATE, "",
                                 "");
-                resultHandler.handleError(LdapException.newErrorResult(cancelResult));
+                resultHandler.handleError(newLdapException(cancelResult));
             }
         }
 
@@ -210,7 +210,7 @@
                      * Don't change state: let the handler ack the cancellation
                      * request.
                      */
-                    throw (CancelledResultException) newErrorResult(ResultCode.CANCELLED,
+                    throw (CancelledResultException) newLdapException(ResultCode.CANCELLED,
                             cancelRequestReason.toString());
                 case TOO_LATE:
                     /* Already too late. Nothing to do. */
@@ -293,7 +293,7 @@
                 if (cancelResultHandler != null) {
                     final Result result =
                             Responses.newGenericExtendedResult(ResultCode.CANNOT_CANCEL);
-                    cancelResultHandler.handleError(newErrorResult(result));
+                    cancelResultHandler.handleError(newLdapException(result));
                 }
                 return;
             }
@@ -370,7 +370,7 @@
                     cancelResultHandler.handleResult(result);
                 } else {
                     final Result result = Responses.newGenericExtendedResult(ResultCode.TOO_LATE);
-                    cancelResultHandler.handleError(LdapException.newErrorResult(result));
+                    cancelResultHandler.handleError(newLdapException(result));
                 }
             }
         }
@@ -606,7 +606,7 @@
                                     new DecodeOptions());
                 } catch (final DecodeException e) {
                     // Couldn't decode a cancel request.
-                    resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, e
+                    resultHandler.handleError(newLdapException(ResultCode.PROTOCOL_ERROR, e
                             .getLocalizedMessage()));
                     return;
                 }
@@ -632,7 +632,7 @@
                          * Couldn't find the request. Invoke on context in order
                          * to remove pending request.
                          */
-                        requestContext.handleError(newErrorResult(ResultCode.NO_SUCH_OPERATION));
+                        requestContext.handleError(newLdapException(ResultCode.NO_SUCH_OPERATION));
                     }
                 }
             } else {
@@ -707,13 +707,13 @@
 
             if (isClosed.get()) {
                 final LocalizableMessage message = INFO_CLIENT_CONNECTION_CLOSING.get();
-                requestContext.handleError(newErrorResult(ResultCode.UNWILLING_TO_PERFORM,
+                requestContext.handleError(newLdapException(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.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR, message.toString()));
+                requestContext.handleError(newLdapException(ResultCode.PROTOCOL_ERROR, message.toString()));
                 return false;
             } else if (isClosed.get()) {
                 /*
@@ -723,7 +723,7 @@
                 pendingRequests.remove(messageID);
 
                 final LocalizableMessage message = INFO_CLIENT_CONNECTION_CLOSING.get();
-                requestContext.handleError(newErrorResult(ResultCode.UNWILLING_TO_PERFORM, message.toString()));
+                requestContext.handleError(newLdapException(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 f0c025f..a5f9f81 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
@@ -39,7 +39,7 @@
  * asynchronously to a remote Directory Server using an
  * {@link ConnectionFactory}. The {@link #handleResult} method is invoked when
  * the operation or connection attempt completes successfully. The
- * {@link #handleErrorResult} method is invoked if the operation or connection
+ * {@link #handleError(LdapException)} method is invoked if the operation or connection
  * attempt fails.
  * <p>
  * Implementations of these methods should complete in a timely manner so as to
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 aee8fff..946fceb 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
@@ -34,11 +34,12 @@
 import org.forgerock.opendj.ldap.requests.SearchRequest;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.schema.CoreSchema;
+import org.forgerock.util.Reject;
+import org.forgerock.util.promise.Function;
 
 import com.forgerock.opendj.util.Collections2;
 
-import org.forgerock.util.Reject;
-import org.forgerock.util.promise.Function;
+import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
 
 /**
  * The root DSE is a DSA-specific Entry (DSE) and not part of any naming context
@@ -133,7 +134,7 @@
      * <p>
      * If the Root DSE is not returned by the Directory Server then the request
      * will fail with an {@link EntryNotFoundException}. More specifically, the
-     * returned future will never return {@code null}.
+     * returned promise will never return {@code null}.
      *
      * @param connection
      *            A connection to the Directory Server whose Root DSE is to be
@@ -141,7 +142,7 @@
      * @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.
+     * @return A promise representing the result of the operation.
      * @throws UnsupportedOperationException
      *             If the connection does not support search operations.
      * @throws IllegalStateException
@@ -150,15 +151,15 @@
      * @throws NullPointerException
      *             If the {@code connection} was {@code null}.
      */
-    public static FutureResult<RootDSE> readRootDSEAsync(final Connection connection,
+    public static LdapPromise<RootDSE> readRootDSEAsync(final Connection connection,
             final ResultHandler<? super RootDSE> handler) {
-        return FutureResultWrapper.asFutureResult(connection.searchSingleEntryAsync(SEARCH_REQUEST).then(
+        return connection.searchSingleEntryAsync(SEARCH_REQUEST).then(
             new Function<SearchResultEntry, RootDSE, LdapException>() {
                 @Override
                 public RootDSE apply(SearchResultEntry result) {
                     return valueOf(result);
                 }
-            }));
+            });
     }
 
     /**
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java
index b6689b5..513223a 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java
@@ -28,7 +28,9 @@
 package org.forgerock.opendj.ldap.requests;
 
 import static com.forgerock.opendj.util.StaticUtils.copyOfBytes;
-import static org.forgerock.opendj.ldap.LdapException.newErrorResult;
+
+import static org.forgerock.opendj.ldap.LdapException.newLdapException;
+import static org.forgerock.opendj.ldap.responses.Responses.*;
 
 import javax.security.auth.callback.NameCallback;
 import javax.security.auth.callback.PasswordCallback;
@@ -41,7 +43,6 @@
 import org.forgerock.opendj.ldap.LdapException;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.responses.BindResult;
-import org.forgerock.opendj.ldap.responses.Responses;
 import org.forgerock.util.Reject;
 
 import com.forgerock.opendj.util.StaticUtils;
@@ -73,7 +74,7 @@
                     setNextSASLCredentials((ByteString) null);
                 }
             } catch (final SaslException e) {
-                throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
+                throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
             }
         }
 
@@ -100,8 +101,7 @@
             } catch (final SaslException e) {
                 // FIXME: I18N need to have a better error message.
                 // FIXME: Is this the best result code?
-                throw LdapException.newErrorResult(Responses.newResult(
-                        ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
+                throw newLdapException(newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
                         "An error occurred during multi-stage authentication").setCause(e));
             }
         }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java
index cf4f5f2..a1e1a84 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java
@@ -30,7 +30,7 @@
 import static com.forgerock.opendj.ldap.CoreMessages.ERR_SASL_PROTOCOL_ERROR;
 import static com.forgerock.opendj.util.StaticUtils.copyOfBytes;
 import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage;
-import static org.forgerock.opendj.ldap.LdapException.newErrorResult;
+import static org.forgerock.opendj.ldap.LdapException.newLdapException;
 
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -133,7 +133,7 @@
                     setNextSASLCredentials((ByteString) null);
                 }
             } catch (final SaslException e) {
-                throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
+                throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
             }
         }
 
@@ -160,7 +160,7 @@
             } catch (final SaslException e) {
                 // FIXME: I18N need to have a better error message.
                 // FIXME: Is this the best result code?
-                throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR,
+                throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR,
                         "An error occurred during multi-stage authentication", e);
             }
         }
@@ -182,7 +182,7 @@
             } catch (final SaslException e) {
                 final LocalizableMessage msg =
                         ERR_SASL_PROTOCOL_ERROR.get(SASL_MECHANISM_NAME, getExceptionMessage(e));
-                throw newErrorResult(ResultCode.CLIENT_SIDE_DECODING_ERROR, msg.toString(), e);
+                throw newLdapException(ResultCode.CLIENT_SIDE_DECODING_ERROR, msg.toString(), e);
             }
         }
 
@@ -193,7 +193,7 @@
             } catch (final SaslException e) {
                 final LocalizableMessage msg =
                         ERR_SASL_PROTOCOL_ERROR.get(SASL_MECHANISM_NAME, getExceptionMessage(e));
-                throw newErrorResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR, msg.toString(), e);
+                throw newLdapException(ResultCode.CLIENT_SIDE_ENCODING_ERROR, msg.toString(), e);
             }
         }
 
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java
index 10711e6..9ddaeca 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java
@@ -27,7 +27,7 @@
 
 package org.forgerock.opendj.ldap.requests;
 
-import static org.forgerock.opendj.ldap.LdapException.newErrorResult;
+import static org.forgerock.opendj.ldap.LdapException.newLdapException;
 
 import javax.security.sasl.Sasl;
 import javax.security.sasl.SaslClient;
@@ -62,7 +62,7 @@
                     setNextSASLCredentials((ByteString) null);
                 }
             } catch (final SaslException e) {
-                throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
+                throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
             }
         }
 
@@ -89,7 +89,7 @@
             } catch (final SaslException e) {
                 // FIXME: I18N need to have a better error message.
                 // FIXME: Is this the best result code?
-                throw LdapException.newErrorResult(Responses.newResult(
+                throw newLdapException(Responses.newResult(
                         ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
                         "An error occurred during multi-stage authentication").setCause(e));
             }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java
index e83e43b..33ced89 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java
@@ -32,7 +32,7 @@
 import static com.forgerock.opendj.ldap.CoreMessages.ERR_SASL_PROTOCOL_ERROR;
 import static com.forgerock.opendj.util.StaticUtils.copyOfBytes;
 import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage;
-import static org.forgerock.opendj.ldap.LdapException.newErrorResult;
+import static org.forgerock.opendj.ldap.LdapException.newLdapException;
 
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
@@ -74,7 +74,7 @@
             if (authenticationID == null) {
                 // FIXME: I18N need to have a better error message.
                 // FIXME: Is this the best result code?
-                throw LdapException.newErrorResult(Responses.newResult(
+                throw newLdapException(Responses.newResult(
                         ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
                         "No authentication ID specified for GSSAPI SASL authentication"));
             }
@@ -82,7 +82,7 @@
             if (password == null) {
                 // FIXME: I18N need to have a better error message.
                 // FIXME: Is this the best result code?
-                throw LdapException.newErrorResult(Responses.newResult(
+                throw newLdapException(Responses.newResult(
                         ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
                         "No password specified for GSSAPI SASL authentication"));
             }
@@ -113,7 +113,7 @@
                 final LocalizableMessage message =
                         ERR_LDAPAUTH_GSSAPI_LOCAL_AUTHENTICATION_FAILED.get(StaticUtils
                                 .getExceptionMessage(e));
-                throw LdapException.newErrorResult(Responses.newResult(
+                throw newLdapException(Responses.newResult(
                         ResultCode.CLIENT_SIDE_LOCAL_ERROR)
                         .setDiagnosticMessage(message.toString()).setCause(e));
             }
@@ -137,7 +137,7 @@
                         } catch (final SaslException e) {
                             // FIXME: I18N need to have a better error message.
                             // FIXME: Is this the best result code?
-                            throw LdapException.newErrorResult(Responses.newResult(
+                            throw newLdapException(Responses.newResult(
                                     ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
                                     "An error occurred during multi-stage authentication")
                                     .setCause(e));
@@ -215,7 +215,7 @@
                                     }
                                     return saslClient;
                                 } catch (final SaslException e) {
-                                    throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
+                                    throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
                                 }
                             }
                         });
@@ -227,7 +227,7 @@
                     final LocalizableMessage msg =
                             ERR_SASL_CONTEXT_CREATE_ERROR.get(SASL_MECHANISM_NAME,
                                     getExceptionMessage(e));
-                    throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, msg.toString(), e);
+                    throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, msg.toString(), e);
                 }
             }
         }
@@ -254,7 +254,7 @@
                     final LocalizableMessage msg =
                             ERR_SASL_PROTOCOL_ERROR
                                     .get(SASL_MECHANISM_NAME, getExceptionMessage(e));
-                    throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, msg.toString(), e);
+                    throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, msg.toString(), e);
                 }
             }
         }
@@ -276,7 +276,7 @@
             } catch (final SaslException e) {
                 final LocalizableMessage msg =
                         ERR_SASL_PROTOCOL_ERROR.get(SASL_MECHANISM_NAME, getExceptionMessage(e));
-                throw newErrorResult(ResultCode.CLIENT_SIDE_DECODING_ERROR, msg.toString(), e);
+                throw newLdapException(ResultCode.CLIENT_SIDE_DECODING_ERROR, msg.toString(), e);
             }
         }
 
@@ -287,7 +287,7 @@
             } catch (final SaslException e) {
                 final LocalizableMessage msg =
                         ERR_SASL_PROTOCOL_ERROR.get(SASL_MECHANISM_NAME, getExceptionMessage(e));
-                throw newErrorResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR, msg.toString(), e);
+                throw newLdapException(ResultCode.CLIENT_SIDE_ENCODING_ERROR, msg.toString(), e);
             }
         }
 
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java
index 583fc09..903ff5f 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java
@@ -27,7 +27,7 @@
 
 package org.forgerock.opendj.ldap.requests;
 
-import static org.forgerock.opendj.ldap.LdapException.newErrorResult;
+import static org.forgerock.opendj.ldap.LdapException.newLdapException;
 
 import javax.security.auth.callback.NameCallback;
 import javax.security.auth.callback.PasswordCallback;
@@ -73,7 +73,7 @@
                     setNextSASLCredentials((ByteString) null);
                 }
             } catch (final SaslException e) {
-                throw newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
+                throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, e);
             }
         }
 
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 443a524..12016bb 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
@@ -27,7 +27,7 @@
 
 package org.forgerock.opendj.ldap.responses;
 
-import static org.forgerock.opendj.ldap.LdapException.newErrorResult;
+import static org.forgerock.opendj.ldap.LdapException.newLdapException;
 
 import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.DecodeOptions;
@@ -74,7 +74,7 @@
                         request.getResultDecoder().newExtendedErrorResult(result.getResultCode(),
                                 result.getMatchedDN(), result.getDiagnosticMessage());
                 adaptedResult.setCause(result.getCause());
-                resultHandler.handleError(newErrorResult(adaptedResult));
+                resultHandler.handleError(newLdapException(adaptedResult));
             }
 
             @Override
@@ -85,7 +85,7 @@
                     resultHandler.handleResult(adaptedResult);
                 } catch (final DecodeException e) {
                     final R adaptedResult = request.getResultDecoder().adaptDecodeException(e);
-                    resultHandler.handleError(newErrorResult(adaptedResult));
+                    resultHandler.handleError(newLdapException(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 be8fe03..170b8ad 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
@@ -45,7 +45,7 @@
 import org.forgerock.opendj.ldap.Entry;
 import org.forgerock.opendj.ldap.EntryNotFoundException;
 import org.forgerock.opendj.ldap.LdapException;
-import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.LinkedAttribute;
 import org.forgerock.opendj.ldap.RDN;
 import org.forgerock.util.Reject;
@@ -54,7 +54,7 @@
 import com.forgerock.opendj.util.StaticUtils;
 
 import static org.forgerock.opendj.ldap.AttributeDescription.*;
-import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
+import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
 
 import static com.forgerock.opendj.ldap.CoreMessages.*;
 
@@ -989,7 +989,7 @@
      * <p>
      * If the requested schema is not returned by the Directory Server then the
      * request will fail with an {@link EntryNotFoundException}. More
-     * specifically, the returned future will never return {@code null}.
+     * specifically, the returned promise will never return {@code null}.
      *
      * @param connection
      *            A connection to the Directory Server whose schema is to be
@@ -997,7 +997,7 @@
      * @param name
      *            The distinguished name of the subschema sub-entry.
      *            the operation result when it is received, may be {@code null}.
-     * @return A future representing the retrieved schema.
+     * @return A promise representing the retrieved schema.
      * @throws UnsupportedOperationException
      *             If the connection does not support search operations.
      * @throws IllegalStateException
@@ -1006,15 +1006,15 @@
      * @throws NullPointerException
      *             If the {@code connection} or {@code name} was {@code null}.
      */
-    public static FutureResult<Schema> readSchemaAsync(final Connection connection, final DN name) {
+    public static LdapPromise<Schema> readSchemaAsync(final Connection connection, final DN name) {
         final SchemaBuilder builder = new SchemaBuilder();
-        return asFutureResult(builder.addSchemaAsync(connection, name, true).then(
+        return builder.addSchemaAsync(connection, name, true).then(
                 new Function<SchemaBuilder, Schema, LdapException>() {
                     @Override
                     public Schema apply(SchemaBuilder builder) throws LdapException {
                         return builder.toSchema();
                     }
-                }));
+                });
     }
 
     /**
@@ -1060,7 +1060,7 @@
      * <p>
      * If the requested entry or its associated schema are not returned by the
      * Directory Server then the request will fail with an
-     * {@link EntryNotFoundException}. More specifically, the returned future
+     * {@link EntryNotFoundException}. More specifically, the returned promise
      * will never return {@code null}.
      * <p>
      * This implementation first reads the {@code subschemaSubentry} attribute
@@ -1074,7 +1074,7 @@
      * @param name
      *            The distinguished name of the entry whose schema is to be
      *            located.
-     * @return A future representing the retrieved schema.
+     * @return A promise representing the retrieved schema.
      * @throws UnsupportedOperationException
      *             If the connection does not support search operations.
      * @throws IllegalStateException
@@ -1083,16 +1083,15 @@
      * @throws NullPointerException
      *             If the {@code connection} or {@code name} was {@code null}.
      */
-    public static FutureResult<Schema> readSchemaForEntryAsync(final Connection connection, final DN name) {
+    public static LdapPromise<Schema> readSchemaForEntryAsync(final Connection connection, final DN name) {
         final SchemaBuilder builder = new SchemaBuilder();
-        return asFutureResult(builder.addSchemaForEntryAsync(connection, name, true).then(
+        return builder.addSchemaForEntryAsync(connection, name, true).then(
             new Function<SchemaBuilder, Schema, LdapException>() {
-
                 @Override
                 public Schema apply(SchemaBuilder builder) throws LdapException {
                     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 c78f1e6..133a476 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
@@ -52,7 +52,7 @@
 import org.forgerock.opendj.ldap.EntryNotFoundException;
 import org.forgerock.opendj.ldap.LdapException;
 import org.forgerock.opendj.ldap.Filter;
-import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchScope;
 import org.forgerock.opendj.ldap.requests.Requests;
@@ -67,10 +67,10 @@
 import com.forgerock.opendj.util.SubstringReader;
 
 import static org.forgerock.opendj.ldap.LdapException.*;
-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 org.forgerock.opendj.ldap.spi.LdapPromises.*;
 
 import static com.forgerock.opendj.ldap.CoreMessages.*;
 import static com.forgerock.opendj.util.StaticUtils.*;
@@ -114,7 +114,7 @@
 
         if (subentryAttr == null || subentryAttr.isEmpty()) {
             // Did not get the subschema sub-entry attribute.
-            throw newErrorResult(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED,
+            throw newLdapException(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED,
                     ERR_NO_SUBSCHEMA_SUBENTRY_ATTR.get(name.toString()).toString());
         }
 
@@ -123,7 +123,7 @@
         try {
             subschemaDN = DN.valueOf(dnString);
         } catch (final LocalizedIllegalArgumentException e) {
-            throw newErrorResult(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED,
+            throw newLdapException(ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED,
                     ERR_INVALID_SUBSCHEMA_SUBENTRY_ATTR.get(name.toString(), dnString,
                             e.getMessageObject()).toString());
         }
@@ -1956,7 +1956,7 @@
      * @param overwrite
      *            {@code true} if existing schema elements with the same
      *            conflicting OIDs should be overwritten.
-     * @return A future representing the updated schema builder.
+     * @return A promise representing the updated schema builder.
      * @throws UnsupportedOperationException
      *             If the connection does not support search operations.
      * @throws IllegalStateException
@@ -1965,19 +1965,17 @@
      * @throws NullPointerException
      *             If the {@code connection} or {@code name} was {@code null}.
      */
-    public FutureResult<SchemaBuilder> addSchemaAsync(final Connection connection, final DN name,
+    public LdapPromise<SchemaBuilder> addSchemaAsync(final Connection connection, final DN name,
         final boolean overwrite) {
         // The call to addSchema will perform copyOnWrite.
-        final SearchRequest request = getReadSchemaSearchRequest(name);
-
-        return asFutureResult(connection.searchSingleEntryAsync(request).then(
+        return connection.searchSingleEntryAsync(getReadSchemaSearchRequest(name)).then(
                 new Function<SearchResultEntry, SchemaBuilder, LdapException>() {
                     @Override
                     public SchemaBuilder apply(SearchResultEntry result) throws LdapException {
                         addSchema(result, overwrite);
                         return SchemaBuilder.this;
                     }
-                }));
+                });
     }
 
     /**
@@ -2033,8 +2031,7 @@
      * <p>
      * This implementation first reads the {@code subschemaSubentry} attribute
      * of the entry in order to identify the schema and then invokes
-     * {@link #addSchemaAsync(Connection, DN, ResultHandler, boolean)} to read
-     * the schema.
+     * {@link #addSchemaAsync(Connection, DN, boolean)} to read the schema.
      *
      * @param connection
      *            A connection to the Directory Server whose schema is to be
@@ -2045,7 +2042,7 @@
      * @param overwrite
      *            {@code true} if existing schema elements with the same
      *            conflicting OIDs should be overwritten.
-     * @return A future representing the updated schema builder.
+     * @return A promise representing the updated schema builder.
      * @throws UnsupportedOperationException
      *             If the connection does not support search operations.
      * @throws IllegalStateException
@@ -2054,19 +2051,16 @@
      * @throws NullPointerException
      *             If the {@code connection} or {@code name} was {@code null}.
      */
-    public FutureResult<SchemaBuilder> addSchemaForEntryAsync(final Connection connection, final DN name,
-        final boolean overwrite) {
-        final SearchRequest request = getReadSchemaForEntrySearchRequest(name);
-
-        return asFutureResult(connection.searchSingleEntryAsync(request).thenAsync(
+    public LdapPromise<SchemaBuilder> addSchemaForEntryAsync(final Connection connection, final DN name,
+            final boolean overwrite) {
+        return connection.searchSingleEntryAsync(getReadSchemaForEntrySearchRequest(name)).thenAsync(
                 new AsyncFunction<SearchResultEntry, SchemaBuilder, LdapException>() {
                     @Override
-                    public Promise<SchemaBuilder, LdapException> apply(SearchResultEntry result)
-                            throws LdapException {
+                    public Promise<SchemaBuilder, LdapException> apply(SearchResultEntry result) throws LdapException {
                         final DN subschemaDN = getSubschemaSubentryDN(name, result);
                         return addSchemaAsync(connection, subschemaDN, overwrite);
                     }
-                }));
+                });
     }
 
     /**
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
deleted file mode 100644
index eec7582..0000000
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPFutureResultImpl.java
+++ /dev/null
@@ -1,200 +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 2011-2014 ForgeRock AS.
- */
-
-package org.forgerock.opendj.ldap.spi;
-
-import org.forgerock.opendj.ldap.Connection;
-import org.forgerock.opendj.ldap.LdapException;
-import org.forgerock.opendj.ldap.FutureResultImpl;
-import org.forgerock.opendj.ldap.IntermediateResponseHandler;
-import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.requests.Requests;
-import org.forgerock.opendj.ldap.responses.IntermediateResponse;
-import org.forgerock.opendj.ldap.responses.Result;
-
-
-/**
- * Abstract future result implementation.
- *
- * @param <S>
- *            The type of result returned by this future.
- */
-public abstract class AbstractLDAPFutureResultImpl<S extends Result> extends FutureResultImpl<S> implements
-    IntermediateResponseHandler {
-    private final Connection connection;
-    private IntermediateResponseHandler intermediateResponseHandler;
-    private volatile long timestamp;
-
-    /**
-     * Creates a future result.
-     *
-     * @param requestID
-     *            identifier of the request
-     * @param intermediateResponseHandler
-     *            handler that consumes intermediate responses from extended
-     *            operations
-     * @param connection
-     *            the connection to directory server
-     */
-    protected AbstractLDAPFutureResultImpl(final int requestID,
-        final IntermediateResponseHandler intermediateResponseHandler, final Connection connection) {
-        super(requestID);
-        this.connection = connection;
-        this.intermediateResponseHandler = intermediateResponseHandler;
-        this.timestamp = System.currentTimeMillis();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public final boolean handleIntermediateResponse(final IntermediateResponse response) {
-        // FIXME: there's a potential race condition here - the future could
-        // get cancelled between the isDone() call and the handler
-        // invocation. We'd need to add support for intermediate handlers in
-        // the synchronizer.
-        if (!isDone()) {
-            updateTimestamp();
-            if (intermediateResponseHandler != null
-                && !intermediateResponseHandler.handleIntermediateResponse(response)) {
-                intermediateResponseHandler = null;
-            }
-        }
-        return true;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected final LdapException 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.
-         */
-        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 LdapException.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}.
-     *
-     * @return {@code true} if this future represents the result of a bind or
-     *         StartTLS request.
-     */
-    public boolean isBindOrStartTLS() {
-        return false;
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * Sets the result associated to this future as an error result.
-     *
-     * @param result
-     *            result of an operation
-     */
-    public final void adaptErrorResult(final Result result) {
-        final S errorResult =
-            newErrorResult(result.getResultCode(), result.getDiagnosticMessage(), result.getCause());
-        setResultOrError(errorResult);
-    }
-
-    /**
-     * Returns the creation time of this future.
-     *
-     * @return the timestamp indicating creation time of this future
-     */
-    public final long getTimestamp() {
-        return timestamp;
-    }
-
-    /**
-     * Create a new error result.
-     *
-     * @param resultCode
-     *            operation result code
-     * @param diagnosticMessage
-     *            message associated to the error
-     * @param cause
-     *            cause of the error
-     * @return the error result
-     */
-    protected abstract S newErrorResult(ResultCode resultCode, String diagnosticMessage, Throwable cause);
-
-    /**
-     * Sets the result associated to this future.
-     *
-     * @param result
-     *            the result of operation
-     */
-    public final void setResultOrError(final S result) {
-        if (result.getResultCode().isExceptional()) {
-            handleError(LdapException.newErrorResult(result));
-        } else {
-            handleResult(result);
-        }
-    }
-
-    final void updateTimestamp() {
-        timestamp = System.currentTimeMillis();
-    }
-
-    /**
-     * Returns {@code true} if this request should be canceled once the timeout
-     * period expires. The default implementation is to return {@code true}
-     * which will be appropriate for nearly all requests, the one obvious
-     * exception being persistent searches.
-     *
-     * @return {@code true} if this request should be canceled once the timeout
-     *         period expires.
-     */
-    public boolean checkForTimeout() {
-        return true;
-    }
-}
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/BindResultLdapPromiseImpl.java
similarity index 61%
rename from opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPBindFutureResultImpl.java
rename to opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/BindResultLdapPromiseImpl.java
index c314ad9..be7c33d 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/BindResultLdapPromiseImpl.java
@@ -29,34 +29,34 @@
 
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
+import org.forgerock.opendj.ldap.LdapException;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.requests.BindClient;
+import org.forgerock.opendj.ldap.requests.BindRequest;
 import org.forgerock.opendj.ldap.responses.BindResult;
 import org.forgerock.opendj.ldap.responses.Responses;
+import org.forgerock.util.promise.PromiseImpl;
 
 /**
- * Bind result future implementation.
+ * Bind result promise implementation.
  */
-public final class LDAPBindFutureResultImpl extends AbstractLDAPFutureResultImpl<BindResult> {
+public final class BindResultLdapPromiseImpl extends ResultLdapPromiseImpl<BindRequest, BindResult> {
     private final BindClient bindClient;
 
-    /**
-     * Creates an bind future result.
-     *
-     * @param requestID
-     *            identifier of the request
-     * @param bindClient
-     *            client that binds to the server
-     * @param intermediateResponseHandler
-     *            handler that consumes intermediate responses from extended
-     *            operations
-     * @param connection
-     *            the connection to directory server
-     */
-    public LDAPBindFutureResultImpl(final int requestID, final BindClient bindClient,
+    BindResultLdapPromiseImpl(final int requestID, final BindRequest request, final BindClient bindClient,
             final IntermediateResponseHandler intermediateResponseHandler,
             final Connection connection) {
-        super(requestID, intermediateResponseHandler, connection);
+        super(new PromiseImpl<BindResult, LdapException>() {
+            protected LdapException tryCancel(boolean mayInterruptIfRunning) {
+                /*
+                 * No other operations can be performed while a bind is active.
+                 * Therefore it is not possible to cancel bind or requests,
+                 * since doing so will leave the connection in a state which
+                 * prevents other operations from being performed.
+                 */
+                return null;
+            }
+        }, requestID, request, intermediateResponseHandler, connection);
         this.bindClient = bindClient;
     }
 
@@ -65,28 +65,17 @@
         return true;
     }
 
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("LDAPBindFutureResultImpl(");
-        sb.append("bindClient = ");
-        sb.append(bindClient);
-        super.toString(sb);
-        sb.append(")");
-        return sb.toString();
-    }
-
     /**
-     * Returns the client.
+     * Returns the bind client.
      *
-     * @return the bind client
+     * @return The bind client.
      */
     public BindClient getBindClient() {
         return bindClient;
     }
 
     @Override
-    protected BindResult newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
+    BindResult newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
             final Throwable cause) {
         return Responses.newBindResult(resultCode).setDiagnosticMessage(diagnosticMessage)
                 .setCause(cause);
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/ExtendedResultLdapPromiseImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/ExtendedResultLdapPromiseImpl.java
new file mode 100644
index 0000000..4c3e6a2
--- /dev/null
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/ExtendedResultLdapPromiseImpl.java
@@ -0,0 +1,99 @@
+/*
+ * 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 2011-2014 ForgeRock AS.
+ */
+
+package org.forgerock.opendj.ldap.spi;
+
+import org.forgerock.opendj.ldap.Connection;
+import org.forgerock.opendj.ldap.DecodeException;
+import org.forgerock.opendj.ldap.DecodeOptions;
+import org.forgerock.opendj.ldap.IntermediateResponseHandler;
+import org.forgerock.opendj.ldap.LdapException;
+import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.requests.ExtendedRequest;
+import org.forgerock.opendj.ldap.requests.Requests;
+import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
+import org.forgerock.opendj.ldap.responses.ExtendedResult;
+import org.forgerock.util.promise.PromiseImpl;
+
+import static org.forgerock.opendj.ldap.LdapException.*;
+
+/**
+ * Extended result promise implementation.
+ *
+ * @param <S>
+ *            The type of result returned by this promise.
+ */
+public final class ExtendedResultLdapPromiseImpl<S extends ExtendedResult> extends
+        ResultLdapPromiseImpl<ExtendedRequest<S>, S> {
+    ExtendedResultLdapPromiseImpl(final int requestID, final ExtendedRequest<S> request,
+            final IntermediateResponseHandler intermediateResponseHandler,
+            final Connection connection) {
+        super(new PromiseImpl<S, LdapException>() {
+            @Override
+            protected LdapException tryCancel(boolean mayInterruptIfRunning) {
+                if (!StartTLSExtendedRequest.OID.equals(request.getOID())) {
+                    connection.abandonAsync(Requests.newAbandonRequest(requestID));
+                    return newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED);
+                }
+
+                /*
+                 * No other operations can be performed while a startTLS is active.
+                 * Therefore it is not possible to cancel startTLS requests,
+                 * since doing so will leave the connection in a state which
+                 * prevents other operations from being performed.
+                 */
+                return null;
+            }
+        }, requestID, request, intermediateResponseHandler, connection);
+    }
+
+    @Override
+    public boolean isBindOrStartTLS() {
+        return StartTLSExtendedRequest.OID.equals(getRequest().getOID());
+    }
+
+    /**
+     * Decode an extended result.
+     *
+     * @param result
+     *            Extended result to decode.
+     * @param options
+     *            Decoding options.
+     * @return The decoded extended result.
+     * @throws DecodeException
+     *             If a problem occurs during decoding.
+     */
+    public S decodeResult(final ExtendedResult result, final DecodeOptions options) throws DecodeException {
+        return getRequest().getResultDecoder().decodeExtendedResult(result, options);
+    }
+
+    @Override
+    S newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
+            final Throwable cause) {
+        return getRequest().getResultDecoder().newExtendedErrorResult(resultCode, "", diagnosticMessage);
+    }
+}
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
deleted file mode 100644
index 64c8617..0000000
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPCompareFutureResultImpl.java
+++ /dev/null
@@ -1,85 +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 2011-2014 ForgeRock AS.
- */
-
-package org.forgerock.opendj.ldap.spi;
-
-import org.forgerock.opendj.ldap.Connection;
-import org.forgerock.opendj.ldap.IntermediateResponseHandler;
-import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.requests.CompareRequest;
-import org.forgerock.opendj.ldap.responses.CompareResult;
-import org.forgerock.opendj.ldap.responses.Responses;
-
-/**
- * Compare result future implementation.
- */
-public final class LDAPCompareFutureResultImpl extends AbstractLDAPFutureResultImpl<CompareResult> {
-    private final CompareRequest request;
-
-    /**
-     * Creates a compare future result.
-     *
-     * @param requestID
-     *            identifier of the request
-     * @param request
-     *            compare request
-     * @param intermediateResponseHandler
-     *            handler that consumes intermediate responses from extended
-     *            operations
-     * @param connection
-     *            the connection to directory server
-     */
-    public LDAPCompareFutureResultImpl(final int requestID, final CompareRequest request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final Connection connection) {
-        super(requestID, intermediateResponseHandler, connection);
-        this.request = request;
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("LDAPCompareFutureResultImpl(");
-        sb.append("request = ");
-        sb.append(request);
-        super.toString(sb);
-        sb.append(")");
-        return sb.toString();
-    }
-
-    CompareRequest getRequest() {
-        return request;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected CompareResult newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
-            final Throwable cause) {
-        return Responses.newCompareResult(resultCode).setDiagnosticMessage(diagnosticMessage)
-                .setCause(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
deleted file mode 100644
index 1429491..0000000
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPExtendedFutureResultImpl.java
+++ /dev/null
@@ -1,115 +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 2011-2014 ForgeRock AS.
- */
-
-package org.forgerock.opendj.ldap.spi;
-
-import org.forgerock.opendj.ldap.Connection;
-import org.forgerock.opendj.ldap.DecodeException;
-import org.forgerock.opendj.ldap.DecodeOptions;
-import org.forgerock.opendj.ldap.IntermediateResponseHandler;
-import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.requests.ExtendedRequest;
-import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
-import org.forgerock.opendj.ldap.responses.ExtendedResult;
-
-/**
- * Extended result future implementation.
- *
- * @param <R>
- *            The type of result returned by this future.
- */
-public final class LDAPExtendedFutureResultImpl<R extends ExtendedResult> extends
-        AbstractLDAPFutureResultImpl<R> {
-    private final ExtendedRequest<R> request;
-
-    /**
-     * Creates an extended future result.
-     *
-     * @param requestID
-     *            identifier of the request
-     * @param request
-     *            extended request
-     * @param intermediateResponseHandler
-     *            handler that consumes intermediate responses from extended
-     *            operations
-     * @param connection
-     *            the connection to directory server
-     */
-    public LDAPExtendedFutureResultImpl(final int requestID, final ExtendedRequest<R> request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final Connection connection) {
-        super(requestID, intermediateResponseHandler, connection);
-        this.request = request;
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("LDAPExtendedFutureResultImpl(");
-        sb.append("request = ");
-        sb.append(request);
-        super.toString(sb);
-        sb.append(")");
-        return sb.toString();
-    }
-
-    @Override
-    public boolean isBindOrStartTLS() {
-        return StartTLSExtendedRequest.OID.equals(request.getOID());
-    }
-
-    /**
-     * Decode an extended result.
-     *
-     * @param result
-     *            extended result to decode
-     * @param options
-     *            decoding options
-     * @return the decoded extended result
-     * @throws DecodeException
-     *             if a problem occurs during decoding
-     */
-    public R decodeResult(final ExtendedResult result, final DecodeOptions options) throws DecodeException {
-        return request.getResultDecoder().decodeExtendedResult(result, options);
-    }
-
-    /**
-     * Returns the extended request.
-     *
-     * @return the extended request
-     */
-    public ExtendedRequest<R> getRequest() {
-        return request;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected R newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
-            final Throwable cause) {
-        return request.getResultDecoder().newExtendedErrorResult(resultCode, "", diagnosticMessage);
-    }
-}
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
deleted file mode 100644
index d871acd..0000000
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPFutureResultImpl.java
+++ /dev/null
@@ -1,92 +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 2011-2014 ForgeRock AS.
- */
-
-package org.forgerock.opendj.ldap.spi;
-
-import org.forgerock.opendj.ldap.Connection;
-import org.forgerock.opendj.ldap.IntermediateResponseHandler;
-import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.requests.Request;
-import org.forgerock.opendj.ldap.responses.Responses;
-import org.forgerock.opendj.ldap.responses.Result;
-
-/**
- * Result future implementation.
- */
-public final class LDAPFutureResultImpl extends AbstractLDAPFutureResultImpl<Result> {
-    private final Request request;
-
-    /**
-     * Creates a future result.
-     *
-     * @param requestID
-     *            identifier of the request
-     * @param request
-     *            the request sent to server
-     * @param intermediateResponseHandler
-     *            handler that consumes intermediate responses from extended
-     *            operations
-     * @param connection
-     *            the connection to directory server
-     */
-    public LDAPFutureResultImpl(final int requestID, final Request request,
-            final IntermediateResponseHandler intermediateResponseHandler,
-            final Connection connection) {
-        super(requestID, intermediateResponseHandler, connection);
-        this.request = request;
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("LDAPFutureResultImpl(");
-        sb.append("request = ");
-        sb.append(request);
-        super.toString(sb);
-        sb.append(")");
-        return sb.toString();
-    }
-
-    /**
-     * Returns the initial request.
-     *
-     * @return the request
-     */
-    public Request getRequest() {
-        return request;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected Result newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
-            final Throwable cause) {
-        return Responses.newResult(resultCode).setDiagnosticMessage(diagnosticMessage).setCause(
-                cause);
-    }
-}
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromiseImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromiseImpl.java
new file mode 100644
index 0000000..fb19c1f
--- /dev/null
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromiseImpl.java
@@ -0,0 +1,92 @@
+/*
+ * 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.spi;
+
+import org.forgerock.opendj.ldap.LdapException;
+import org.forgerock.opendj.ldap.LdapPromise;
+import org.forgerock.opendj.ldap.ResultHandler;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.PromiseImpl;
+import org.forgerock.util.promise.Promises;
+
+/**
+ * This class provides an implementation of the {@link LdapPromise}.
+ *
+ * @param <S>
+ *            The type of result returned by this promise.
+ * @see Promise
+ * @see Promises
+ * @see LdapPromise
+ */
+public class LdapPromiseImpl<S> extends LdapPromiseWrapper<S, PromiseImpl<S, LdapException>> implements
+        LdapPromise<S>, ResultHandler<S> {
+
+    /**
+     * Creates a new {@link LdapPromiseImpl} from a wrapped existing {@link PromiseImpl}.
+     *
+     * @param wrappedPromise
+     *      The {@link Promise} to wrap.
+     * @param requestID
+     *      Identifier of the request.
+     */
+    protected LdapPromiseImpl(PromiseImpl<S, LdapException> wrappedPromise, int requestID) {
+        super(wrappedPromise, requestID);
+    }
+
+    /**
+     * Creates a new {@link LdapPromiseImpl}.
+     *
+     * @param <S>
+     *            The type of result of the promise.
+     * @return a new {@link LdapPromiseImpl}
+     */
+    public static <S> LdapPromiseImpl<S> newLdapPromiseImpl() {
+        return newLdapPromiseImpl(-1);
+    }
+
+    /**
+     * Creates a new {@link LdapPromiseImpl} with a requestID.
+     *
+     * @param <S>
+     *            The type of result of the promise.
+     * @param requestID
+     *            Identifier of the request.
+     * @return a new {@link LdapPromiseImpl}
+     */
+    public static <S> LdapPromiseImpl<S> newLdapPromiseImpl(int requestID) {
+        return new LdapPromiseImpl<S>(PromiseImpl.<S, LdapException> create(), requestID);
+    }
+
+    @Override
+    public void handleError(LdapException error) {
+        getWrappedPromise().handleError(error);
+    }
+
+    @Override
+    public void handleResult(S result) {
+        getWrappedPromise().handleResult(result);
+    }
+}
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromiseWrapper.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromiseWrapper.java
new file mode 100644
index 0000000..be80aa4
--- /dev/null
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromiseWrapper.java
@@ -0,0 +1,184 @@
+/*
+ * 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.spi;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.forgerock.opendj.ldap.LdapException;
+import org.forgerock.opendj.ldap.LdapPromise;
+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 static org.forgerock.opendj.ldap.spi.LdapPromises.*;
+
+/**
+ * Provides a {@link Promise} wrapper and a {@link LdapPromise} implementation.
+ *
+ *
+ * This wrapper allows client code to return {@link LdapPromise} 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.
+ *
+ * @param <R>
+ *       The type of the task's result.
+ * @param <P>
+ *       The wrapped promise.
+ */
+class LdapPromiseWrapper<R, P extends Promise<R, LdapException>> implements LdapPromise<R> {
+    private final P wrappedPromise;
+    private final int requestID;
+
+    public LdapPromiseWrapper(P wrappedPromise, int requestID) {
+        this.wrappedPromise = wrappedPromise;
+        this.requestID = requestID;
+    }
+
+    @Override
+    public int getRequestID() {
+        return wrappedPromise instanceof LdapPromise ? ((LdapPromise<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, LdapException {
+        return wrappedPromise.getOrThrow();
+    }
+
+    @Override
+    public R getOrThrow(long timeout, TimeUnit unit) throws InterruptedException, LdapException, TimeoutException {
+        return wrappedPromise.getOrThrow(timeout, unit);
+    }
+
+    @Override
+    public R getOrThrowUninterruptibly() throws LdapException {
+        return wrappedPromise.getOrThrowUninterruptibly();
+    }
+
+    @Override
+    public R getOrThrowUninterruptibly(long timeout, TimeUnit unit) throws LdapException, TimeoutException {
+        return wrappedPromise.getOrThrowUninterruptibly(timeout, unit);
+    }
+
+    @Override
+    public boolean isCancelled() {
+        return wrappedPromise.isCancelled();
+    }
+
+    @Override
+    public boolean isDone() {
+        return wrappedPromise.isDone();
+    }
+
+    @Override
+    public LdapPromise<R> onFailure(FailureHandler<? super LdapException> onFailure) {
+        wrappedPromise.onFailure(onFailure);
+        return this;
+    }
+
+    @Override
+    public LdapPromise<R> onSuccess(SuccessHandler<? super R> onSuccess) {
+        wrappedPromise.onSuccess(onSuccess);
+        return this;
+    }
+
+    @Override
+    public LdapPromise<R> onSuccessOrFailure(Runnable onSuccessOrFailure) {
+        wrappedPromise.onSuccessOrFailure(onSuccessOrFailure);
+        return this;
+    }
+
+    @Override
+    // @Checkstyle:ignore
+    public <VOUT> LdapPromise<VOUT> then(Function<? super R, VOUT, LdapException> onSuccess) {
+        return LdapPromises.wrap(wrappedPromise.then(onSuccess), getRequestID());
+    }
+
+    @Override
+    // @Checkstyle:ignore
+    public <VOUT, EOUT extends Exception> Promise<VOUT, EOUT> then(Function<? super R, VOUT, EOUT> onSuccess,
+            Function<? super LdapException, VOUT, EOUT> onFailure) {
+        return wrappedPromise.then(onSuccess, onFailure);
+    }
+
+    @Override
+    public LdapPromise<R> then(SuccessHandler<? super R> onSuccess) {
+        wrappedPromise.then(onSuccess);
+        return this;
+    }
+
+    @Override
+    public LdapPromise<R> then(SuccessHandler<? super R> onSuccess,
+            FailureHandler<? super LdapException> onFailure) {
+        wrappedPromise.then(onSuccess, onFailure);
+        return this;
+    }
+
+    @Override
+    public LdapPromise<R> thenAlways(Runnable onSuccessOrFailure) {
+        wrappedPromise.thenAlways(onSuccessOrFailure);
+        return this;
+    }
+
+    @Override
+    // @Checkstyle:ignore
+    public <VOUT> LdapPromise<VOUT> thenAsync(AsyncFunction<? super R, VOUT, LdapException> onSuccess) {
+        return wrap(wrappedPromise.thenAsync(onSuccess), getRequestID());
+    }
+
+    @Override
+    // @Checkstyle:ignore
+    public <VOUT, EOUT extends Exception> Promise<VOUT, EOUT> thenAsync(
+            AsyncFunction<? super R, VOUT, EOUT> onSuccess,
+            AsyncFunction<? super LdapException, VOUT, EOUT> onFailure) {
+        return wrappedPromise.thenAsync(onSuccess, onFailure);
+    }
+
+    public P getWrappedPromise() {
+        return wrappedPromise;
+    }
+
+}
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromises.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromises.java
new file mode 100644
index 0000000..4ca5cb8
--- /dev/null
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromises.java
@@ -0,0 +1,273 @@
+/*
+ * 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.spi;
+
+import org.forgerock.opendj.ldap.Connection;
+import org.forgerock.opendj.ldap.IntermediateResponseHandler;
+import org.forgerock.opendj.ldap.LdapException;
+import org.forgerock.opendj.ldap.LdapPromise;
+import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.SearchResultHandler;
+import org.forgerock.opendj.ldap.requests.BindClient;
+import org.forgerock.opendj.ldap.requests.BindRequest;
+import org.forgerock.opendj.ldap.requests.CompareRequest;
+import org.forgerock.opendj.ldap.requests.ExtendedRequest;
+import org.forgerock.opendj.ldap.requests.Request;
+import org.forgerock.opendj.ldap.requests.SearchRequest;
+import org.forgerock.opendj.ldap.responses.CompareResult;
+import org.forgerock.opendj.ldap.responses.ExtendedResult;
+import org.forgerock.opendj.ldap.responses.Result;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.Promises;
+
+import static org.forgerock.opendj.ldap.responses.Responses.*;
+
+/**
+ * Utility methods for creating and composing {@link LdapPromise}s.
+ */
+public final class LdapPromises {
+    /**
+     * Returns a {@link LdapPromise} 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 LdapPromise} representing an asynchronous task which has
+     *         already succeeded with the provided result.
+     */
+    public static <R> LdapPromise<R> newSuccessfulLdapPromise(final R result) {
+        return wrap(Promises.<R, LdapException> newSuccessfulPromise(result), -1);
+    }
+
+    /**
+     * Returns a {@link LdapPromise} 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 LdapPromise} representing an asynchronous task which has
+     *         already succeeded with the provided result.
+     */
+    public static <R> LdapPromise<R> newSuccessfulLdapPromise(final R result, int requestID) {
+        return wrap(Promises.<R, LdapException> newSuccessfulPromise(result), requestID);
+    }
+
+    /**
+     * Returns a {@link LdapPromise} 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 LdapPromise} representing an asynchronous task which has
+     *         already failed with the provided error.
+     */
+    public static <R, E extends LdapException> LdapPromise<R> newFailedLdapPromise(final E error) {
+        return wrap(Promises.<R, LdapException> newFailedPromise(error), -1);
+    }
+
+    /**
+     * Returns a {@link LdapPromise} 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 LdapPromise} representing an asynchronous task which has
+     *         already failed with the provided error.
+     */
+    public static <R, E extends LdapException> LdapPromise<R> newFailedLdapPromise(final E error, int requestID) {
+        return wrap(Promises.<R, LdapException> newFailedPromise(error), requestID);
+    }
+
+    /**
+     * Creates a new {@link ResultLdapPromiseImpl} to handle  a standard request (add, delete, modify and modidyDN).
+     *
+     * @param <R>
+     *           The type of the task's request.
+     *
+     * @param requestID
+     *            Identifier of the request.
+     * @param request
+     *            The request sent to the server.
+     * @param intermediateResponseHandler
+     *            Handler that consumes intermediate responses from extended operations.
+     * @param connection
+     *            The connection to directory server.
+     *
+     * @return The new {@link ResultLdapPromiseImpl}.
+     */
+    public static <R extends Request> ResultLdapPromiseImpl<R, Result> newResultLdapPromise(final int requestID,
+            final R request, final IntermediateResponseHandler intermediateResponseHandler,
+            final Connection connection) {
+        return new ResultLdapPromiseImpl<R, Result>(requestID, request, intermediateResponseHandler, connection) {
+            @Override
+            protected Result newErrorResult(ResultCode resultCode, String diagnosticMessage, Throwable cause) {
+                return newResult(resultCode).setDiagnosticMessage(diagnosticMessage).setCause(cause);
+            }
+        };
+    }
+
+    /**
+     * Creates a new bind {@link BindResultLdapPromiseImpl}.
+     *
+     * @param requestID
+     *            Identifier of the request.
+     * @param request
+     *            The bind request sent to server.
+     * @param bindClient
+     *            Client that binds to the server.
+     * @param intermediateResponseHandler
+     *            Handler that consumes intermediate responses from extended operations.
+     * @param connection
+     *            The connection to directory server.
+     *
+     * @return The new {@link BindResultLdapPromiseImpl}.
+     */
+    public static BindResultLdapPromiseImpl newBindLdapPromise(final int requestID,
+            final BindRequest request, final BindClient bindClient,
+            final IntermediateResponseHandler intermediateResponseHandler, final Connection connection) {
+        return new BindResultLdapPromiseImpl(requestID, request, bindClient, intermediateResponseHandler, connection);
+    }
+
+    /**
+     * Creates a new compare {@link ResultLdapPromiseImpl}.
+     *
+     * @param requestID
+     *            Identifier of the request.
+     * @param request
+     *            The compare request sent to the server.
+     * @param intermediateResponseHandler
+     *            Handler that consumes intermediate responses from extended operations.
+     * @param connection
+     *            The connection to directory server.
+     *
+     * @return The new {@link ResultLdapPromiseImpl}.
+     */
+    public static ResultLdapPromiseImpl<CompareRequest, CompareResult> newCompareLdapPromise(final int requestID,
+            final CompareRequest request, final IntermediateResponseHandler intermediateResponseHandler,
+            final Connection connection) {
+        return new ResultLdapPromiseImpl<CompareRequest, CompareResult>(requestID, request, intermediateResponseHandler,
+                connection) {
+            @Override
+            protected CompareResult newErrorResult(ResultCode resultCode, String diagnosticMessage, Throwable cause) {
+                return newCompareResult(resultCode).setDiagnosticMessage(diagnosticMessage).setCause(cause);
+            }
+        };
+    }
+
+    /**
+     * Creates a new extended {@link ExtendedResultLdapPromiseImpl}.
+     *
+     * @param <S>
+     *            The type of result returned by this promise.
+     * @param requestID
+     *            Identifier of the request.
+     * @param request
+     *            The extended request sent to the server.
+     * @param intermediateResponseHandler
+     *            Handler that consumes intermediate responses from extended operations.
+     * @param connection
+     *            The connection to directory server.
+     *
+     * @return The new {@link ExtendedResultLdapPromiseImpl}.
+     */
+    public static <S extends ExtendedResult> ExtendedResultLdapPromiseImpl<S> newExtendedLdapPromise(
+            final int requestID, final ExtendedRequest<S> request,
+            final IntermediateResponseHandler intermediateResponseHandler, final Connection connection) {
+        return new ExtendedResultLdapPromiseImpl<S>(requestID, request, intermediateResponseHandler, connection);
+    }
+
+    /**
+     * Creates a new search {@link SearchResultLdapPromiseImpl}.
+     *
+     * @param requestID
+     *            Identifier of the request.
+     * @param request
+     *            The search request sent to the server.
+     * @param resultHandler
+     *            Handler that consumes search result.
+     * @param intermediateResponseHandler
+     *            Handler that consumes intermediate responses from extended operations.
+     * @param connection
+     *            The connection to directory server.
+     *
+     * @return The new {@link SearchResultLdapPromiseImpl}.
+     */
+    public static SearchResultLdapPromiseImpl newSearchLdapPromise(final int requestID, final SearchRequest request,
+            final SearchResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler,
+            final Connection connection) {
+        return new SearchResultLdapPromiseImpl(requestID, request, resultHandler, intermediateResponseHandler,
+                connection);
+    }
+
+
+
+    /**
+     * Converts a {@link Promise} to a {@link LdapPromise}.
+     *
+     * @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 LdapPromise} representing the same asynchronous task as
+     *         the {@link Promise} provided.
+     */
+    public static <R> LdapPromise<R> asPromise(Promise<R, LdapException> wrappedPromise) {
+        return wrap(wrappedPromise, -1);
+    }
+
+    static <R> LdapPromise<R> wrap(Promise<R, LdapException> wrappedPromise, int requestID) {
+        return new LdapPromiseWrapper<R, Promise<R, LdapException>>(wrappedPromise, requestID);
+    }
+
+    private LdapPromises() {
+    }
+
+}
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/ResultLdapPromiseImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/ResultLdapPromiseImpl.java
new file mode 100644
index 0000000..8a738d8
--- /dev/null
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/ResultLdapPromiseImpl.java
@@ -0,0 +1,191 @@
+/*
+ * 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.spi;
+
+import org.forgerock.opendj.ldap.Connection;
+import org.forgerock.opendj.ldap.IntermediateResponseHandler;
+import org.forgerock.opendj.ldap.LdapException;
+import org.forgerock.opendj.ldap.LdapPromise;
+import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.requests.Request;
+import org.forgerock.opendj.ldap.requests.Requests;
+import org.forgerock.opendj.ldap.responses.IntermediateResponse;
+import org.forgerock.opendj.ldap.responses.Result;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.PromiseImpl;
+import org.forgerock.util.promise.Promises;
+
+import static org.forgerock.opendj.ldap.LdapException.*;
+
+/**
+ * This class provides an implementation of the {@link LdapPromise}.
+ *
+ * @param <R>
+ *            The type of the associated {@link Request}.
+ * @param <S>
+ *            The type of result returned by this promise.
+ * @see Promise
+ * @see Promises
+ * @see LdapPromise
+ */
+public abstract class ResultLdapPromiseImpl<R extends Request, S extends Result> extends LdapPromiseImpl<S>
+        implements LdapPromise<S>, IntermediateResponseHandler {
+    private final R request;
+    private IntermediateResponseHandler intermediateResponseHandler;
+    private volatile long timestamp;
+
+    ResultLdapPromiseImpl(final int requestID, final R request,
+            final IntermediateResponseHandler intermediateResponseHandler, final Connection connection) {
+        this(new PromiseImpl<S, LdapException>() {
+            @Override
+            protected final LdapException tryCancel(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(requestID));
+                return newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED);
+            }
+        }, requestID, request, intermediateResponseHandler, connection);
+    }
+
+    ResultLdapPromiseImpl(final PromiseImpl<S, LdapException> impl, final int requestID, final R request,
+            final IntermediateResponseHandler intermediateResponseHandler, final Connection connection) {
+        super(impl, requestID);
+        this.request = request;
+        this.intermediateResponseHandler = intermediateResponseHandler;
+        this.timestamp = System.currentTimeMillis();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean handleIntermediateResponse(final IntermediateResponse response) {
+        // FIXME: there's a potential race condition here - the promise could
+        // get cancelled between the isDone() call and the handler
+        // invocation. We'd need to add support for intermediate handlers in
+        // the synchronizer.
+        if (!isDone()) {
+            updateTimestamp();
+            if (intermediateResponseHandler != null
+                    && !intermediateResponseHandler.handleIntermediateResponse(response)) {
+                intermediateResponseHandler = null;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns {@code true} if this promise represents the result of a bind or
+     * StartTLS request. The default implementation is to return {@code false}.
+     *
+     * @return {@code true} if this promise represents the result of a bind or
+     *         StartTLS request.
+     */
+    public boolean isBindOrStartTLS() {
+        return false;
+    }
+
+    /**
+     * Returns a string representation of this promise's state.
+     *
+     * @return String representation of this promise's state.
+     */
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("( requestID = ");
+        sb.append(getRequestID());
+        sb.append(" timestamp = ");
+        sb.append(timestamp);
+        sb.append(" request = ");
+        sb.append(getRequest());
+        sb.append(" )");
+        return sb.toString();
+    }
+
+    /**
+     * Sets the result associated to this promise as an error result.
+     *
+     * @param result
+     *            result of an operation
+     */
+    public final void adaptErrorResult(final Result result) {
+        final S errorResult = newErrorResult(result.getResultCode(), result.getDiagnosticMessage(), result.getCause());
+        setResultOrError(errorResult);
+    }
+
+    /**
+     * Returns the creation time of this promise.
+     *
+     * @return the timestamp indicating creation time of this promise
+     */
+    public final long getTimestamp() {
+        return timestamp;
+    }
+
+    abstract S newErrorResult(ResultCode resultCode, String diagnosticMessage, Throwable cause);
+
+    /**
+     * Sets the result associated to this promise.
+     *
+     * @param result
+     *            the result of operation
+     */
+    public final void setResultOrError(final S result) {
+        if (result.getResultCode().isExceptional()) {
+            getWrappedPromise().handleError(newLdapException(result));
+        } else {
+            getWrappedPromise().handleResult(result);
+        }
+    }
+
+    /**
+     * Returns the attached request.
+     *
+     * @return The request.
+     */
+    public R getRequest() {
+        return request;
+    }
+
+    final void updateTimestamp() {
+        timestamp = System.currentTimeMillis();
+    }
+
+    /**
+     * Returns {@code true} if this request should be canceled once the timeout
+     * period expires. The default implementation is to return {@code true}
+     * which will be appropriate for nearly all requests, the one obvious
+     * exception being persistent searches.
+     *
+     * @return {@code true} if this request should be canceled once the timeout
+     *         period expires.
+     */
+    public boolean checkForTimeout() {
+        return true;
+    }
+}
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/SearchResultLdapPromiseImpl.java
similarity index 71%
rename from opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPSearchFutureResultImpl.java
rename to opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/SearchResultLdapPromiseImpl.java
index c9edee0..6b06732 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/SearchResultLdapPromiseImpl.java
@@ -40,34 +40,17 @@
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
 
 /**
- * Search result future implementation.
+ * Search result promise implementation.
  */
-public final class LDAPSearchFutureResultImpl extends AbstractLDAPFutureResultImpl<Result> implements
+public final class SearchResultLdapPromiseImpl extends ResultLdapPromiseImpl<SearchRequest, Result> implements
         SearchResultHandler {
     private SearchResultHandler searchResultHandler;
-    private final SearchRequest request;
     private final boolean isPersistentSearch;
 
-    /**
-     * Creates a search future result.
-     *
-     * @param requestID
-     *            identifier of the request
-     * @param request
-     *            search request
-     * @param resultHandler
-     *            handler that consumes search result
-     * @param intermediateResponseHandler
-     *            handler that consumes intermediate responses from extended
-     *            operations
-     * @param connection
-     *            the connection to directory server
-     */
-    public LDAPSearchFutureResultImpl(final int requestID, final SearchRequest request,
+    SearchResultLdapPromiseImpl(final int requestID, final SearchRequest request,
         final SearchResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler,
         final Connection connection) {
-        super(requestID, intermediateResponseHandler, connection);
-        this.request = request;
+        super(requestID, request, intermediateResponseHandler, connection);
         this.searchResultHandler = resultHandler;
         this.isPersistentSearch =
             request.containsControl(PersistentSearchRequestControl.OID)
@@ -75,8 +58,9 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean handleEntry(final SearchResultEntry entry) {
-        // FIXME: there's a potential race condition here - the future could
+        // FIXME: there's a potential race condition here - the promise could
         // get cancelled between the isDone() call and the handler
         // invocation. We'd need to add support for intermediate handlers in
         // the synchronizer.
@@ -92,8 +76,9 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean handleReference(final SearchResultReference reference) {
-        // FIXME: there's a potential race condition here - the future could
+        // FIXME: there's a potential race condition here - the promise could
         // get cancelled between the isDone() call and the handler
         // invocation. We'd need to add support for intermediate handlers in
         // the synchronizer.
@@ -109,38 +94,16 @@
     }
 
     @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("LDAPSearchFutureResultImpl(");
-        sb.append("request = ");
-        sb.append(request);
-        super.toString(sb);
-        sb.append(")");
-        return sb.toString();
-    }
-
-    /**
-     * Returns the search request.
-     *
-     * @return the search request
-     */
-    SearchRequest getRequest() {
-        return request;
-    }
-
-    @Override
-    protected Result newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
+    Result newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
             final Throwable cause) {
         return Responses.newResult(resultCode).setDiagnosticMessage(diagnosticMessage).setCause(
                 cause);
     }
 
-
-    /** {@inheritDoc} */
     @Override
-    public
-    boolean checkForTimeout() {
+    public boolean checkForTimeout() {
         // Persistent searches should not time out.
         return !isPersistentSearch;
     }
+
 }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/TransportProvider.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/TransportProvider.java
index d4cb0bc..a9c87a7 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/TransportProvider.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/TransportProvider.java
@@ -57,8 +57,7 @@
      *            The LDAP options to use when creating connections.
      * @return an implementation of {@code LDAPConnectionFactory}
      */
-    LDAPConnectionFactoryImpl getLDAPConnectionFactory(String host, int port,
-            LDAPOptions options);
+    LDAPConnectionFactoryImpl getLDAPConnectionFactory(String host, int port, LDAPOptions options);
 
   /**
      * Returns an implementation of {@code LDAPListener}.
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 e52f1ef..c405edb 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
@@ -34,7 +34,7 @@
 
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.LdapException;
-import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.SearchResultHandler;
@@ -175,7 +175,7 @@
     }
 
     private final BufferHandler buffer;
-    private final FutureResult<Result> future;
+    private final LdapPromise<Result> promise;
     private Response nextResponse = null;
 
     /**
@@ -211,8 +211,7 @@
         final BlockingQueue<Response> entries) {
         Reject.ifNull(connection);
         buffer = new BufferHandler(entries);
-        future = (FutureResult<Result>) connection.searchAsync(searchRequest, buffer)
-                .onSuccess(buffer).onFailure(buffer);
+        promise = connection.searchAsync(searchRequest, buffer).onSuccess(buffer).onFailure(buffer);
     }
 
     /**
@@ -222,7 +221,7 @@
     @Override
     public void close() {
         // Cancel the search if it is still running.
-        future.cancel(true);
+        promise.cancel(true);
     }
 
     /**
@@ -243,7 +242,7 @@
             return false;
         }
 
-        throw newErrorResult(result);
+        throw newLdapException(result);
     }
 
     /**
@@ -400,7 +399,7 @@
             try {
                 nextResponse = buffer.responses.poll(50, TimeUnit.MILLISECONDS);
             } catch (final InterruptedException e) {
-                throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, e);
+                throw newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, e);
             }
 
             if (nextResponse == null && buffer.isInterrupted) {
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 2c63d76..2d94714 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
@@ -53,10 +53,10 @@
 import static org.fest.assertions.Assertions.*;
 import static org.fest.assertions.Fail.*;
 import static org.forgerock.opendj.ldap.LdapException.*;
-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.forgerock.opendj.ldap.spi.LdapPromises.*;
 import static org.mockito.Matchers.*;
 import static org.mockito.Mockito.*;
 
@@ -78,19 +78,19 @@
 
         /** {@inheritDoc} */
         @Override
-        public FutureResult<Void> abandonAsync(AbandonRequest request) {
+        public LdapPromise<Void> abandonAsync(AbandonRequest request) {
             if (!resultCode.isExceptional()) {
-                return newSuccessfulFutureResult((Void) null);
+                return newSuccessfulLdapPromise((Void) null);
             } else {
-                return newFailedFutureResult(newErrorResult(resultCode));
+                return newFailedLdapPromise(newLdapException(resultCode));
             }
         }
 
         /** {@inheritDoc} */
         @Override
-        public FutureResult<Result> addAsync(AddRequest request,
+        public LdapPromise<Result> addAsync(AddRequest request,
                 IntermediateResponseHandler intermediateResponseHandler) {
-            return getFutureFromResultCode(newResult(resultCode));
+            return getPromiseFromResultCode(newResult(resultCode));
         }
 
         /** {@inheritDoc} */
@@ -101,9 +101,9 @@
 
         /** {@inheritDoc} */
         @Override
-        public FutureResult<BindResult> bindAsync(BindRequest request,
+        public LdapPromise<BindResult> bindAsync(BindRequest request,
                 IntermediateResponseHandler intermediateResponseHandler) {
-            return getFutureFromResultCode(newBindResult(resultCode));
+            return getPromiseFromResultCode(newBindResult(resultCode));
         }
 
         /** {@inheritDoc} */
@@ -114,23 +114,23 @@
 
         /** {@inheritDoc} */
         @Override
-        public FutureResult<CompareResult> compareAsync(CompareRequest request,
+        public LdapPromise<CompareResult> compareAsync(CompareRequest request,
                 IntermediateResponseHandler intermediateResponseHandler) {
-            return getFutureFromResultCode(newCompareResult(resultCode));
+            return getPromiseFromResultCode(newCompareResult(resultCode));
         }
 
         /** {@inheritDoc} */
         @Override
-        public FutureResult<Result> deleteAsync(DeleteRequest request,
+        public LdapPromise<Result> deleteAsync(DeleteRequest request,
                 IntermediateResponseHandler intermediateResponseHandler) {
-            return getFutureFromResultCode(newResult(resultCode));
+            return getPromiseFromResultCode(newResult(resultCode));
         }
 
         /** {@inheritDoc} */
         @Override
-        public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(ExtendedRequest<R> request,
+        public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(ExtendedRequest<R> request,
                 IntermediateResponseHandler intermediateResponseHandler) {
-            return getFutureFromResultCode(request.getResultDecoder().newExtendedErrorResult(resultCode, "", ""));
+            return getPromiseFromResultCode(request.getResultDecoder().newExtendedErrorResult(resultCode, "", ""));
         }
 
         /** {@inheritDoc} */
@@ -147,16 +147,16 @@
 
         /** {@inheritDoc} */
         @Override
-        public FutureResult<Result> modifyAsync(ModifyRequest request,
+        public LdapPromise<Result> modifyAsync(ModifyRequest request,
                 IntermediateResponseHandler intermediateResponseHandler) {
-            return getFutureFromResultCode(newResult(resultCode));
+            return getPromiseFromResultCode(newResult(resultCode));
         }
 
         /** {@inheritDoc} */
         @Override
-        public FutureResult<Result> modifyDNAsync(ModifyDNRequest request,
+        public LdapPromise<Result> modifyDNAsync(ModifyDNRequest request,
                 IntermediateResponseHandler intermediateResponseHandler) {
-            return getFutureFromResultCode(newResult(resultCode));
+            return getPromiseFromResultCode(newResult(resultCode));
         }
 
         /** {@inheritDoc} */
@@ -167,20 +167,20 @@
 
         /** {@inheritDoc} */
         @Override
-        public FutureResult<Result> searchAsync(SearchRequest request,
+        public LdapPromise<Result> searchAsync(SearchRequest request,
                 IntermediateResponseHandler intermediateResponseHandler, SearchResultHandler entryHandler) {
             for (SearchResultEntry entry : entries) {
                 entryHandler.handleEntry(entry);
             }
 
-            return getFutureFromResultCode(newResult(resultCode));
+            return getPromiseFromResultCode(newResult(resultCode));
         }
 
-        private <T extends Result> FutureResult<T> getFutureFromResultCode(T correctResult) {
+        private <T extends Result> LdapPromise<T> getPromiseFromResultCode(T correctResult) {
             if (resultCode.isExceptional()) {
-                return newFailedFutureResult(newErrorResult(resultCode));
+                return newFailedLdapPromise(newLdapException(resultCode));
             } else {
-                return newSuccessfulFutureResult(correctResult);
+                return newSuccessfulLdapPromise(correctResult);
             }
         }
 
@@ -375,11 +375,8 @@
         final SearchRequest request =
                 newSingleEntrySearchRequest("cn=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
         SuccessHandler<SearchResultEntry> successHandler = mock(SuccessHandler.class);
-
-        FutureResult<SearchResultEntry> futureResult = (FutureResult<SearchResultEntry>) mockConnection
-                .searchSingleEntryAsync(request).onSuccess(successHandler);
-
-        assertThat(futureResult.get()).isEqualTo(entry);
+        SearchResultEntry resultEntry = mockConnection.searchSingleEntryAsync(request).onSuccess(successHandler).get();
+        assertThat(resultEntry).isEqualTo(entry);
         verify(successHandler).handleResult(any(SearchResultEntry.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 b8a3b58..26504a7 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
@@ -98,11 +98,11 @@
          * Create load-balancer with two failed connection factories.
          */
         final ConnectionFactory first = mock(ConnectionFactory.class, "first");
-        final LdapException firstError = newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN);
+        final LdapException firstError = newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN);
         when(first.getConnection()).thenThrow(firstError);
 
         final ConnectionFactory second = mock(ConnectionFactory.class, "second");
-        final LdapException secondError = newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN);
+        final LdapException secondError = newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN);
         when(second.getConnection()).thenThrow(secondError);
 
         final ConnectionFactory loadBalancer =
@@ -137,12 +137,12 @@
          */
         final ConnectionFactory first = mock(ConnectionFactory.class, "first");
         final ConnectionFactory firstAsync = mockAsync(first);
-        final LdapException firstError = newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN);
+        final LdapException firstError = newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN);
         when(first.getConnection()).thenThrow(firstError).thenReturn(mock(Connection.class));
 
         final ConnectionFactory second = mock(ConnectionFactory.class, "second");
         final ConnectionFactory secondAsync = mockAsync(second);
-        final LdapException secondError = newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN);
+        final LdapException secondError = newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN);
         when(second.getConnection()).thenThrow(secondError);
 
         final LoadBalancerEventListener listener = mock(LoadBalancerEventListener.class);
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 77eece7..d9bd16c 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
@@ -102,7 +102,7 @@
         connection.addConnectionEventListener(listener);
         assertThat(listeners).hasSize(1);
         listeners.get(0).handleConnectionError(false,
-                newErrorResult(ResultCode.CLIENT_SIDE_SERVER_DOWN));
+                newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN));
         verify(listener, times(0)).handleConnectionClosed();
         verify(listener).handleConnectionError(eq(false), isA(ConnectionException.class));
         verify(listener, times(0)).handleUnsolicitedNotification(any(ExtendedResult.class));
@@ -242,19 +242,18 @@
 
         /*
          * Grab another connection and check that this attempt blocks (if there
-         * is a connection available immediately then the future will be
+         * is a connection available immediately then the promise will be
          * completed immediately).
          */
-        final Promise<? extends Connection, LdapException> future = pool.getConnectionAsync();
-        assertThat(future.isDone()).isFalse();
+        final Promise<? extends Connection, LdapException> promise = pool.getConnectionAsync();
+        assertThat(promise.isDone()).isFalse();
 
-        // Release a connection and verify that it is immediately redeemed by
-        // the future.
+        // Release a connection and verify that it is immediately redeemed by the promise.
         pc2.close();
-        assertThat(future.isDone()).isTrue();
+        assertThat(promise.isDone()).isTrue();
 
         // Check that returned connection routes request to released connection.
-        final Connection pc3 = future.get();
+        final Connection pc3 = promise.get();
         assertThat(pc3.bind(bind2).getResultCode()).isEqualTo(ResultCode.SUCCESS);
         verify(factory, times(2)).getConnection();
         verify(connection2).bind(bind2);
@@ -509,12 +508,12 @@
     }
 
     /**
-     * Test that all outstanding pending connection futures are completed when a
+     * Test that all outstanding pending connection promises are completed when a
      * connection request fails.
      */
     @SuppressWarnings({ "rawtypes", "unchecked" })
     @Test(description = "OPENDJ-1348", timeOut = 10000)
-    public void testNewConnectionFailureFlushesAllPendingFutures() throws Exception {
+    public void testNewConnectionFailureFlushesAllPendingPromises() throws Exception {
         final ConnectionFactory factory = mock(ConnectionFactory.class);
         final int poolSize = 2;
         final ConnectionPool pool = Connections.newFixedConnectionPool(factory, poolSize);
@@ -526,21 +525,21 @@
             }
         }).when(factory).getConnectionAsync();
 
-        List<Promise<? extends Connection, LdapException>> futures =
+        List<Promise<? extends Connection, LdapException>> promises =
                 new ArrayList<Promise<? extends Connection, LdapException>>();
         for (int i = 0; i < poolSize + 1; i++) {
-            futures.add(pool.getConnectionAsync());
+            promises.add(pool.getConnectionAsync());
         }
         // factory.getConnectionAsync() has been called by the pool poolSize times
         verify(factory, times(poolSize)).getConnectionAsync();
-        final LdapException connectError = LdapException.newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR);
-        for (Promise<? extends Connection, LdapException> future : futures) {
+        final LdapException connectError = newLdapException(ResultCode.CLIENT_SIDE_CONNECT_ERROR);
+        for (Promise<? extends Connection, LdapException> promise : promises) {
             // Simulate that an error happened with the created connections
-            ((FutureResultImpl) future).handleError(connectError);
+            ((PromiseImpl) promise).handleError(connectError);
 
             try {
-                // Before the fix for OPENDJ-1348 the third future.get() would hang.
-                future.getOrThrow();
+                // Before the fix for OPENDJ-1348 the third promise.get() would hang.
+                promise.getOrThrow();
                 Assert.fail("Expected an exception to be thrown");
             } catch (LdapException 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 fd334c8..1fcd356 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
@@ -34,8 +34,8 @@
 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.opendj.ldap.spi.BindResultLdapPromiseImpl;
 import org.forgerock.util.promise.FailureHandler;
 import org.forgerock.util.promise.NeverThrowsException;
 import org.forgerock.util.promise.Promise;
@@ -52,11 +52,13 @@
 import static org.fest.assertions.Assertions.*;
 import static org.fest.assertions.Fail.*;
 import static org.forgerock.opendj.ldap.LdapException.*;
-import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
+import static org.forgerock.opendj.ldap.ResultCode.*;
 import static org.forgerock.opendj.ldap.SearchScope.*;
 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.forgerock.opendj.ldap.spi.LdapPromiseImpl.*;
+import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
 import static org.mockito.Matchers.*;
 import static org.mockito.Mockito.*;
 
@@ -142,7 +144,7 @@
          * the response once we have attempted a bind.
          */
         when(connection.searchAsync(any(SearchRequest.class), any(SearchResultHandler.class))).thenReturn(
-            FutureResultWrapper.newSuccessfulFutureResult(Responses.newResult(ResultCode.SUCCESS)));
+            newSuccessfulLdapPromise(newResult(SUCCESS)));
         when(hbcf.timeSource.currentTimeMillis()).thenReturn(11000L);
         scheduler.runAllTasks(); // Send the heartbeat.
 
@@ -290,7 +292,7 @@
     @Test(description = "OPENDJ-1348")
     public void testBindPreventsHeartBeatTimeout() throws Exception {
         mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS);
-        mockBindAsyncResponse();
+        BindResultLdapPromiseImpl promise = mockBindAsyncResponse();
         hbc = hbcf.getConnection();
 
         /*
@@ -298,7 +300,7 @@
          * the response once we have attempted a heartbeat.
          */
         when(hbcf.timeSource.currentTimeMillis()).thenReturn(11000L);
-        FutureResult<BindResult> future = hbc.bindAsync(newSimpleBindRequest());
+        hbc.bindAsync(newSimpleBindRequest());
 
         verify(connection, times(1)).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class));
 
@@ -309,7 +311,7 @@
 
         // Send fake bind response, releasing the heartbeat.
         when(hbcf.timeSource.currentTimeMillis()).thenReturn(11099L);
-        ((ResultHandler) future).handleResult(newResult(ResultCode.SUCCESS));
+        ((PromiseImpl) promise.getWrappedPromise()).handleResult(newResult(SUCCESS));
 
         // Check that bind response acts as heartbeat.
         assertThat(hbc.isValid()).isTrue();
@@ -345,16 +347,11 @@
     @Test
     public void testHeartBeatWhileBindInProgress() throws Exception {
         mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS);
-        mockBindAsyncResponse();
+        //Trapping the callback of mockedConnection.bindAsync
+        BindResultLdapPromiseImpl promise = mockBindAsyncResponse();
         hbc = hbcf.getConnection();
+        hbc.bindAsync(newSimpleBindRequest());
 
-        /*
-         * Send a bind request, trapping the bind call-back so that we can send
-         * the response once we have attempted a heartbeat.
-         */
-        FutureResult result = hbc.bindAsync(newSimpleBindRequest());
-
-        // Capture the bind result handler.
         verify(connection, times(1)).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class));
 
         /*
@@ -367,7 +364,7 @@
         verify(connection, times(1)).searchAsync(same(HEARTBEAT), any(SearchResultHandler.class));
 
         // Send fake bind response, releasing the heartbeat.
-        ((ResultHandler) result).handleResult(newResult(ResultCode.SUCCESS));
+        ((PromiseImpl) promise.getWrappedPromise()).handleResult(newResult(SUCCESS));
 
         // Attempt to send a heartbeat again.
         when(hbcf.timeSource.currentTimeMillis()).thenReturn(16000L);
@@ -404,42 +401,43 @@
 
         // Create heart beat connection factory.
         scheduler = new MockScheduler();
-        hbcf =
-                new HeartBeatConnectionFactory(factory, 10000, 100, TimeUnit.MILLISECONDS,
-                        HEARTBEAT, scheduler);
+        hbcf = new HeartBeatConnectionFactory(factory, 10000, 100, TimeUnit.MILLISECONDS, HEARTBEAT, scheduler);
 
         // Set initial time stamp.
         hbcf.timeSource = mockTimeSource(0);
     }
 
-    private void mockBindAsyncResponse() {
-        doAnswer(new Answer<FutureResult<Result>>() {
+    private BindResultLdapPromiseImpl mockBindAsyncResponse() {
+        final BindResultLdapPromiseImpl bindPromise = newBindLdapPromise(-1, null, null, null, connection);
+        doAnswer(new Answer<LdapPromise<BindResult>>() {
             @Override
-            public FutureResult<Result> answer(final InvocationOnMock invocation) throws Throwable {
-                return new FutureResultImpl<Result>();
+            public LdapPromise<BindResult> answer(final InvocationOnMock invocation) throws Throwable {
+                return bindPromise;
             }
         }).when(connection).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class));
+
+        return bindPromise;
     }
 
     private Connection mockHeartBeatResponse(final Connection mockConnection,
-        final List<ConnectionEventListener> listeners, final ResultCode resultCode) {
-        Answer<FutureResult<Result>> answer = new Answer<FutureResult<Result>>() {
+            final List<ConnectionEventListener> listeners, final ResultCode resultCode) {
+        Answer<LdapPromise<Result>> answer = new Answer<LdapPromise<Result>>() {
             @Override
-            public FutureResult<Result> answer(final InvocationOnMock invocation) throws Throwable {
+            public LdapPromise<Result> answer(final InvocationOnMock invocation) throws Throwable {
                 if (resultCode == null) {
-                    return null;
+                    return newLdapPromiseImpl();
                 }
 
                 if (resultCode.isExceptional()) {
-                    final LdapException error = newErrorResult(resultCode);
+                    final LdapException error = newLdapException(resultCode);
                     if (error instanceof ConnectionException) {
                         for (final ConnectionEventListener listener : listeners) {
                             listener.handleConnectionError(false, error);
                         }
                     }
-                    return newFailedFutureResult(error);
+                    return newFailedLdapPromise(error);
                 } else {
-                    return newSuccessfulFutureResult(newResult(resultCode));
+                    return newSuccessfulLdapPromise(newResult(resultCode));
                 }
             }
         };
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 0610d75..334bad3 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
@@ -76,6 +76,7 @@
 import com.forgerock.opendj.ldap.controls.AccountUsabilityRequestControl;
 import com.forgerock.opendj.ldap.controls.AccountUsabilityResponseControl;
 
+import static org.forgerock.opendj.ldap.LdapException.*;
 import static org.forgerock.opendj.ldap.TestCaseUtils.*;
 
 /**
@@ -177,7 +178,7 @@
             if (entryMap.containsKey(dn)) {
                 // duplicate entry.
                 result = Responses.newResult(ResultCode.ENTRY_ALREADY_EXISTS);
-                handler.handleError(LdapException.newErrorResult(result));
+                handler.handleError(newLdapException(result));
                 // doesn't matter if it was canceled.
                 requestsInProgress.remove(context);
                 return;
@@ -195,7 +196,7 @@
 
             if (abReq.isCanceled()) {
                 result = Responses.newResult(ResultCode.CANCELLED);
-                handler.handleError(LdapException.newErrorResult(result));
+                handler.handleError(newLdapException(result));
                 requestsInProgress.remove(context);
                 return;
             }
@@ -284,7 +285,7 @@
                                     try {
                                         return saslServer.unwrap(incoming, offset, len);
                                     } catch (SaslException e) {
-                                        throw LdapException.newErrorResult(
+                                        throw newLdapException(
                                                 Responses.newResult(ResultCode.OPERATIONS_ERROR).setCause(e));
                                     }
                                 }
@@ -294,7 +295,7 @@
                                     try {
                                         return saslServer.wrap(outgoing, offset, len);
                                     } catch (SaslException e) {
-                                        throw LdapException.newErrorResult(
+                                        throw newLdapException(
                                                 Responses.newResult(ResultCode.OPERATIONS_ERROR).setCause(e));
                                     }
                                 }
@@ -309,7 +310,7 @@
                                 ByteString.wrap(challenge)));
                     }
                 } catch (Exception e) {
-                    resultHandler.handleError(LdapException.newErrorResult(Responses
+                    resultHandler.handleError(newLdapException(Responses
                             .newBindResult(ResultCode.OPERATIONS_ERROR).setCause(e)
                             .setDiagnosticMessage(e.toString())));
                 }
@@ -357,7 +358,7 @@
             if (!entryMap.containsKey(dn)) {
                 // entry not found.
                 result = Responses.newCompareResult(ResultCode.NO_SUCH_ATTRIBUTE);
-                resultHandler.handleError(LdapException.newErrorResult(result));
+                resultHandler.handleError(newLdapException(result));
                 // doesn't matter if it was canceled.
                 requestsInProgress.remove(context);
                 return;
@@ -372,7 +373,7 @@
                     final ByteString s = it.next();
                     if (abReq.isCanceled()) {
                         final Result r = Responses.newResult(ResultCode.CANCELLED);
-                        resultHandler.handleError(LdapException.newErrorResult(r));
+                        resultHandler.handleError(newLdapException(r));
                         requestsInProgress.remove(context);
                         return;
                     }
@@ -401,7 +402,7 @@
             if (!entryMap.containsKey(dn)) {
                 // entry is not found.
                 result = Responses.newResult(ResultCode.NO_SUCH_OBJECT);
-                handler.handleError(LdapException.newErrorResult(result));
+                handler.handleError(newLdapException(result));
                 // doesn't matter if it was canceled.
                 requestsInProgress.remove(context);
                 return;
@@ -409,7 +410,7 @@
 
             if (abReq.isCanceled()) {
                 result = Responses.newResult(ResultCode.CANCELLED);
-                handler.handleError(LdapException.newErrorResult(result));
+                handler.handleError(newLdapException(result));
                 requestsInProgress.remove(context);
                 return;
             }
@@ -461,7 +462,7 @@
             if (!entryMap.containsKey(dn)) {
                 // Entry not found.
                 result = Responses.newResult(ResultCode.NO_SUCH_OBJECT);
-                resultHandler.handleError(LdapException.newErrorResult(result));
+                resultHandler.handleError(newLdapException(result));
                 // Should searchResultHandler handle anything?
 
                 // doesn't matter if it was canceled.
@@ -471,7 +472,7 @@
 
             if (abReq.isCanceled()) {
                 result = Responses.newResult(ResultCode.CANCELLED);
-                resultHandler.handleError(LdapException.newErrorResult(result));
+                resultHandler.handleError(newLdapException(result));
                 requestsInProgress.remove(context);
                 return;
             }
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 1d54f96..21915e2 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
@@ -44,7 +44,7 @@
 import com.forgerock.opendj.util.TimeSource;
 
 import static org.fest.assertions.Fail.*;
-import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
+import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
 import static org.mockito.Matchers.*;
 import static org.mockito.Mockito.*;
 
@@ -174,7 +174,7 @@
             @Override
             public Promise<Connection, LdapException> answer(final InvocationOnMock invocation)
                     throws Throwable {
-                return newSuccessfulFutureResult(factory.getConnection());
+                return newSuccessfulLdapPromise(factory.getConnection());
             }
         });
         return factory;
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/EntrySchemaCheckingTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/EntrySchemaCheckingTestCase.java
index 15ff1db..ff7ab50 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/EntrySchemaCheckingTestCase.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/EntrySchemaCheckingTestCase.java
@@ -26,6 +26,7 @@
 package org.forgerock.opendj.ldap.schema;
 
 import static org.fest.assertions.Assertions.assertThat;
+import static org.forgerock.opendj.ldap.LdapException.*;
 import static org.forgerock.opendj.ldap.schema.SchemaValidationPolicy.defaultPolicy;
 import static org.forgerock.opendj.ldap.schema.SchemaValidationPolicy.ignoreAll;
 
@@ -1111,7 +1112,7 @@
 
             public Entry getEntry(final DN dn) throws LdapException {
                 if (e == null) {
-                    throw LdapException.newErrorResult(ResultCode.NO_SUCH_OBJECT,
+                    throw newLdapException(ResultCode.NO_SUCH_OBJECT,
                             "no such entry " + dn.toString());
                 }
 
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 2bffd68..5db59b1 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
@@ -36,20 +36,17 @@
 import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.Entry;
 import org.forgerock.opendj.ldap.EntryNotFoundException;
-import org.forgerock.opendj.ldap.LdapException;
-import org.forgerock.opendj.ldap.FutureResult;
-import org.forgerock.opendj.ldap.FutureResultWrapper;
+import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.LinkedHashMapEntry;
 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.forgerock.opendj.ldap.spi.LdapPromises.*;
 import static org.mockito.Matchers.*;
 import static org.mockito.Mockito.*;
 
@@ -1992,9 +1989,8 @@
         };
 
         // Send a search entry result promise :
-        Promise<SearchResultEntry, LdapException> promise =
-                newSuccessfulPromise(Responses.newSearchResultEntry(entry));
-        FutureResult<SearchResultEntry> result = FutureResultWrapper.asFutureResult(promise);
+        LdapPromise<SearchResultEntry> result =
+                newSuccessfulLdapPromise(Responses.newSearchResultEntry(entry));
         when(connection.searchSingleEntryAsync((SearchRequest) any())).thenReturn(result);
         DN testDN = DN.valueOf("uid=bjensen,ou=People,dc=example,dc=com");
         // @formatter:on
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 c3394f2..d7b649a 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
@@ -25,22 +25,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.LdapException;
-import org.forgerock.opendj.ldap.FutureResult;
-import org.forgerock.opendj.ldap.FutureResultWrapper;
+import org.forgerock.opendj.ldap.LdapPromise;
 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.forgerock.opendj.ldap.spi.LdapPromises.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
 /**
  * Tests the Schema class.
  */
@@ -83,9 +80,7 @@
         };
 
         // Send a search entry result promise :
-        Promise<SearchResultEntry, LdapException> promise =
-                newSuccessfulPromise(Responses.newSearchResultEntry(entry));
-        FutureResult<SearchResultEntry> result = FutureResultWrapper.asFutureResult(promise);
+        LdapPromise<SearchResultEntry> result = newSuccessfulLdapPromise(Responses.newSearchResultEntry(entry));
         when(connection.searchSingleEntryAsync((SearchRequest) any())).thenReturn(result);
         DN testDN = DN.valueOf("uid=bjensen,ou=People,dc=example,dc=com");
         // @formatter:on
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 8b2ed52..e091808 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
@@ -73,7 +73,7 @@
         try {
             return getConnectionAsync().getOrThrow();
         } catch (final InterruptedException e) {
-            throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, e);
+            throw newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, e);
         }
     }
 
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/spi/ConnectionStateTest.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/spi/ConnectionStateTest.java
index 3018611..52e76a2 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/spi/ConnectionStateTest.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/spi/ConnectionStateTest.java
@@ -26,7 +26,7 @@
 package org.forgerock.opendj.ldap.spi;
 
 import static org.fest.assertions.Assertions.assertThat;
-import static org.forgerock.opendj.ldap.LdapException.newErrorResult;
+import static org.forgerock.opendj.ldap.LdapException.newLdapException;
 import static org.forgerock.opendj.ldap.responses.Responses.newGenericExtendedResult;
 import static org.mockito.Matchers.same;
 import static org.mockito.Mockito.doAnswer;
@@ -48,8 +48,8 @@
  */
 @SuppressWarnings("javadoc")
 public class ConnectionStateTest extends LDAPTestCase {
-    private static final LdapException ERROR = newErrorResult(ResultCode.OTHER);
-    private static final LdapException LATE_ERROR = newErrorResult(ResultCode.BUSY);
+    private static final LdapException ERROR = newLdapException(ResultCode.OTHER);
+    private static final LdapException LATE_ERROR = newLdapException(ResultCode.BUSY);
     private static final ExtendedResult UNSOLICITED =
             newGenericExtendedResult(ResultCode.OPERATIONS_ERROR);
 
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldif/ConnectionChangeRecordWriterTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldif/ConnectionChangeRecordWriterTestCase.java
index 3c900ea..62de09f 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldif/ConnectionChangeRecordWriterTestCase.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldif/ConnectionChangeRecordWriterTestCase.java
@@ -255,7 +255,7 @@
                         if (handler != null) {
                             // Data here if needed.
                         }
-                        return newErrorResult(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
+                        return newLdapException(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
                     }
                 });
 
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 69c354d..60a7bb3 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
@@ -26,23 +26,11 @@
 
 package org.forgerock.opendj.ldif;
 
-import static org.fest.assertions.Assertions.assertThat;
-import static org.fest.assertions.Fail.fail;
-import static org.forgerock.opendj.ldap.LdapException.newErrorResult;
-import static org.forgerock.opendj.ldap.responses.Responses.newResult;
-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.same;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 import java.util.NoSuchElementException;
 
 import org.forgerock.opendj.ldap.Connection;
-import org.forgerock.opendj.ldap.FutureResult;
-import org.forgerock.opendj.ldap.FutureResultWrapper;
 import org.forgerock.opendj.ldap.LdapException;
+import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchResultHandler;
 import org.forgerock.opendj.ldap.SearchResultReferenceIOException;
@@ -56,6 +44,14 @@
 import org.mockito.stubbing.Answer;
 import org.testng.annotations.Test;
 
+import static org.fest.assertions.Assertions.*;
+import static org.fest.assertions.Fail.*;
+import static org.forgerock.opendj.ldap.LdapException.*;
+import static org.forgerock.opendj.ldap.responses.Responses.*;
+import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
 /**
  * This class tests the ConnectionEntryReader functionality.
  */
@@ -251,9 +247,9 @@
         final Connection connection = mock(Connection.class);
         // @formatter:off
         when(connection.searchAsync(same(SEARCH), any(SearchResultHandler.class))).thenAnswer(
-            new Answer<FutureResult<Result>>() {
+            new Answer<LdapPromise<Result>>() {
                 @Override
-                public FutureResult<Result> answer(final InvocationOnMock invocation) throws Throwable {
+                public LdapPromise<Result> answer(final InvocationOnMock invocation) throws Throwable {
                     // Execute handler and return future.
                     final SearchResultHandler handler = (SearchResultHandler) invocation.getArguments()[1];
                     if (handler != null) {
@@ -268,9 +264,9 @@
                     }
                     final Result result = (Result) responses[responses.length - 1];
                     if (result.isSuccess()) {
-                        return FutureResultWrapper.newSuccessfulFutureResult(result);
+                        return newSuccessfulLdapPromise(result);
                     } else {
-                        return FutureResultWrapper.newFailedFutureResult(newErrorResult(result));
+                        return newFailedLdapPromise(newLdapException(result));
                     }
                 }
             });
diff --git a/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyTransportProvider.java b/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyTransportProvider.java
index 43a6718..8e75c6a 100644
--- a/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyTransportProvider.java
+++ b/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyTransportProvider.java
@@ -44,8 +44,7 @@
 public class GrizzlyTransportProvider implements TransportProvider {
 
     @Override
-    public LDAPConnectionFactoryImpl getLDAPConnectionFactory(String host, int port,
-                                                              LDAPOptions options) {
+    public LDAPConnectionFactoryImpl getLDAPConnectionFactory(String host, int port, LDAPOptions options) {
         return new GrizzlyLDAPConnectionFactory(host, port, options);
     }
 
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 2fbd27b..e332639 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
@@ -44,10 +44,10 @@
 import org.forgerock.opendj.ldap.AbstractAsynchronousConnection;
 import org.forgerock.opendj.ldap.ConnectionEventListener;
 import org.forgerock.opendj.ldap.Connections;
-import org.forgerock.opendj.ldap.LdapException;
-import org.forgerock.opendj.ldap.FutureResult;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.LDAPOptions;
+import org.forgerock.opendj.ldap.LdapException;
+import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SSLContextBuilder;
 import org.forgerock.opendj.ldap.SearchResultHandler;
@@ -71,12 +71,10 @@
 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.spi.AbstractLDAPFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.LDAPBindFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.LDAPCompareFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.LDAPExtendedFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.LDAPFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.LDAPSearchFutureResultImpl;
+import org.forgerock.opendj.ldap.spi.BindResultLdapPromiseImpl;
+import org.forgerock.opendj.ldap.spi.ExtendedResultLdapPromiseImpl;
+import org.forgerock.opendj.ldap.spi.ResultLdapPromiseImpl;
+import org.forgerock.opendj.ldap.spi.SearchResultLdapPromiseImpl;
 import org.forgerock.util.Reject;
 import org.glassfish.grizzly.CompletionHandler;
 import org.glassfish.grizzly.filterchain.Filter;
@@ -84,8 +82,8 @@
 import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
 import org.glassfish.grizzly.ssl.SSLFilter;
 
-import static org.forgerock.opendj.ldap.LdapException.newErrorResult;
-import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
+import static org.forgerock.opendj.ldap.LdapException.*;
+import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
 
 import static com.forgerock.opendj.grizzly.GrizzlyMessages.*;
 
@@ -115,8 +113,8 @@
     private final org.glassfish.grizzly.Connection<?> connection;
     private final AtomicInteger nextMsgID = new AtomicInteger(1);
     private final GrizzlyLDAPConnectionFactory factory;
-    private final ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>> pendingRequests =
-            new ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>>();
+    private final ConcurrentHashMap<Integer, ResultLdapPromiseImpl<?, ?>> pendingRequests =
+            new ConcurrentHashMap<Integer, ResultLdapPromiseImpl<?, ?>>();
     private final Object stateLock = new Object();
     /** Guarded by stateLock. */
     private Result connectionInvalidReason;
@@ -141,12 +139,12 @@
     }
 
     @Override
-    public FutureResult<Void> abandonAsync(final AbandonRequest request) {
+    public LdapPromise<Void> abandonAsync(final AbandonRequest request) {
         /*
-         * Need to be careful here since both abandonAsync and Future.cancel can
+         * Need to be careful here since both abandonAsync and Promise.cancel can
          * be called separately by the client application. Therefore
-         * future.cancel() should abandon the request, and abandonAsync should
-         * cancel the future. In addition, bind or StartTLS requests cannot be
+         * promise.cancel() should abandon the request, and abandonAsync should
+         * cancel the promise. In addition, bind or StartTLS requests cannot be
          * abandoned.
          */
         try {
@@ -160,22 +158,22 @@
                 checkBindOrStartTLSInProgress();
             }
         } catch (final LdapException e) {
-            return newFailedFutureResult(e);
+            return newFailedLdapPromise(e);
         }
 
-        // Remove the future associated with the request to be abandoned.
-        final AbstractLDAPFutureResultImpl<?> pendingRequest = pendingRequests.remove(request.getRequestID());
+        // Remove the promise associated with the request to be abandoned.
+        final ResultLdapPromiseImpl<?, ?> 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 newSuccessfulFutureResult((Void) null);
+            return newSuccessfulLdapPromise((Void) null);
         }
 
         /*
-         * This will cancel the future, but will also recursively invoke this
+         * This will cancel the promise, but will also recursively invoke this
          * method. Since the pending request has been removed, there is no risk
          * of an infinite loop.
          */
@@ -188,31 +186,31 @@
         return sendAbandonRequest(request);
     }
 
-    private FutureResult<Void> sendAbandonRequest(final AbandonRequest request) {
+    private LdapPromise<Void> sendAbandonRequest(final AbandonRequest request) {
         final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
         try {
             final int messageID = nextMsgID.getAndIncrement();
             writer.writeAbandonRequest(messageID, request);
             connection.write(writer.getASN1Writer().getBuffer(), null);
-            return newSuccessfulFutureResult((Void) null, messageID);
+            return newSuccessfulLdapPromise((Void) null, messageID);
         } catch (final IOException e) {
-            return newFailedFutureResult(adaptRequestIOException(e));
+            return newFailedLdapPromise(adaptRequestIOException(e));
         } finally {
             GrizzlyUtils.recycleWriter(writer);
         }
     }
 
     @Override
-    public FutureResult<Result> addAsync(final AddRequest request,
+    public LdapPromise<Result> addAsync(final AddRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         final int messageID = nextMsgID.getAndIncrement();
-        final LDAPFutureResultImpl future =
-                new LDAPFutureResultImpl(messageID, request, intermediateResponseHandler, this);
+        final ResultLdapPromiseImpl<AddRequest, Result> promise =
+                newResultLdapPromise(messageID, request, intermediateResponseHandler, this);
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
                 checkBindOrStartTLSInProgress();
-                pendingRequests.put(messageID, future);
+                pendingRequests.put(messageID, promise);
             }
             try {
                 final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
@@ -227,9 +225,9 @@
                 throw adaptRequestIOException(e);
             }
         } catch (final LdapException e) {
-            future.adaptErrorResult(e.getResult());
+            promise.adaptErrorResult(e.getResult());
         }
-        return future;
+        return promise;
     }
 
     @Override
@@ -250,7 +248,7 @@
         if (notifyErrorOccurred) {
             // Use the reason provided in the disconnect notification.
             listener.handleConnectionError(failedDueToDisconnect,
-                    newErrorResult(connectionInvalidReason));
+                    newLdapException(connectionInvalidReason));
         }
         if (notifyClose) {
             listener.handleConnectionClosed();
@@ -258,39 +256,33 @@
     }
 
     @Override
-    public FutureResult<BindResult> bindAsync(final BindRequest request,
+    public LdapPromise<BindResult> bindAsync(final BindRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         final int messageID = nextMsgID.getAndIncrement();
         final BindClient context;
         try {
             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 LdapException error = LdapException.newErrorResult(errorResult);
-
-            return newFailedFutureResult(error, messageID);
+        } catch (final LdapException e) {
+            return newFailedLdapPromise(e, messageID);
         }
 
-        final LDAPBindFutureResultImpl future =
-                new LDAPBindFutureResultImpl(messageID, context, intermediateResponseHandler, this);
+        final BindResultLdapPromiseImpl promise =
+                newBindLdapPromise(messageID, request, context, intermediateResponseHandler, this);
 
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
                 if (!pendingRequests.isEmpty()) {
-                    future.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
+                    promise.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
                             "There are other operations pending on this connection"));
-                    return future;
+                    return promise;
                 }
                 if (!bindOrStartTLSInProgress.compareAndSet(false, true)) {
-                    future.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
+                    promise.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
                             "Bind or Start TLS operation in progress"));
-                    return future;
+                    return promise;
                 }
-                pendingRequests.put(messageID, future);
+                pendingRequests.put(messageID, promise);
             }
 
             try {
@@ -310,10 +302,10 @@
                 throw adaptRequestIOException(e);
             }
         } catch (final LdapException e) {
-            future.adaptErrorResult(e.getResult());
+            promise.adaptErrorResult(e.getResult());
         }
 
-        return future;
+        return promise;
     }
 
     @Override
@@ -325,16 +317,16 @@
     }
 
     @Override
-    public FutureResult<CompareResult> compareAsync(final CompareRequest request,
+    public LdapPromise<CompareResult> compareAsync(final CompareRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         final int messageID = nextMsgID.getAndIncrement();
-        final LDAPCompareFutureResultImpl future =
-                new LDAPCompareFutureResultImpl(messageID, request, intermediateResponseHandler, this);
+        final ResultLdapPromiseImpl<CompareRequest, CompareResult> promise =
+                newCompareLdapPromise(messageID, request, intermediateResponseHandler, this);
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
                 checkBindOrStartTLSInProgress();
-                pendingRequests.put(messageID, future);
+                pendingRequests.put(messageID, promise);
             }
             try {
                 final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
@@ -349,22 +341,22 @@
                 throw adaptRequestIOException(e);
             }
         } catch (final LdapException e) {
-            future.adaptErrorResult(e.getResult());
+            promise.adaptErrorResult(e.getResult());
         }
-        return future;
+        return promise;
     }
 
     @Override
-    public FutureResult<Result> deleteAsync(final DeleteRequest request,
+    public LdapPromise<Result> deleteAsync(final DeleteRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         final int messageID = nextMsgID.getAndIncrement();
-        final LDAPFutureResultImpl future =
-                new LDAPFutureResultImpl(messageID, request, intermediateResponseHandler, this);
+        final ResultLdapPromiseImpl<DeleteRequest, Result> promise =
+                newResultLdapPromise(messageID, request, intermediateResponseHandler, this);
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
                 checkBindOrStartTLSInProgress();
-                pendingRequests.put(messageID, future);
+                pendingRequests.put(messageID, promise);
             }
             try {
                 final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
@@ -379,38 +371,38 @@
                 throw adaptRequestIOException(e);
             }
         } catch (final LdapException e) {
-            future.adaptErrorResult(e.getResult());
+            promise.adaptErrorResult(e.getResult());
         }
-        return future;
+        return promise;
     }
 
     @Override
-    public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request,
+    public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         final int messageID = nextMsgID.getAndIncrement();
-        final LDAPExtendedFutureResultImpl<R> future =
-                new LDAPExtendedFutureResultImpl<R>(messageID, request, intermediateResponseHandler, this);
+        final ExtendedResultLdapPromiseImpl<R> promise =
+                newExtendedLdapPromise(messageID, request, intermediateResponseHandler, this);
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
                 if (StartTLSExtendedRequest.OID.equals(request.getOID())) {
                     if (!pendingRequests.isEmpty()) {
-                        future.setResultOrError(request.getResultDecoder().newExtendedErrorResult(
+                        promise.setResultOrError(request.getResultDecoder().newExtendedErrorResult(
                                 ResultCode.OPERATIONS_ERROR, "", "There are pending operations on this connection"));
-                        return future;
+                        return promise;
                     } else if (isTLSEnabled()) {
-                        future.setResultOrError(request.getResultDecoder().newExtendedErrorResult(
+                        promise.setResultOrError(request.getResultDecoder().newExtendedErrorResult(
                                 ResultCode.OPERATIONS_ERROR, "", "This connection is already TLS enabled"));
-                        return future;
+                        return promise;
                     } else if (!bindOrStartTLSInProgress.compareAndSet(false, true)) {
-                        future.setResultOrError(request.getResultDecoder().newExtendedErrorResult(
+                        promise.setResultOrError(request.getResultDecoder().newExtendedErrorResult(
                                 ResultCode.OPERATIONS_ERROR, "", "Bind or Start TLS operation in progress"));
-                        return future;
+                        return promise;
                     }
                 } else {
                     checkBindOrStartTLSInProgress();
                 }
-                pendingRequests.put(messageID, future);
+                pendingRequests.put(messageID, promise);
             }
             try {
                 final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
@@ -426,9 +418,9 @@
                 throw adaptRequestIOException(e);
             }
         } catch (final LdapException e) {
-            future.adaptErrorResult(e.getResult());
+            promise.adaptErrorResult(e.getResult());
         }
-        return future;
+        return promise;
     }
 
     @Override
@@ -446,16 +438,16 @@
     }
 
     @Override
-    public FutureResult<Result> modifyAsync(final ModifyRequest request,
+    public LdapPromise<Result> modifyAsync(final ModifyRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         final int messageID = nextMsgID.getAndIncrement();
-        final LDAPFutureResultImpl future =
-                new LDAPFutureResultImpl(messageID, request, intermediateResponseHandler, this);
+        final ResultLdapPromiseImpl<ModifyRequest, Result> promise =
+                newResultLdapPromise(messageID, request, intermediateResponseHandler, this);
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
                 checkBindOrStartTLSInProgress();
-                pendingRequests.put(messageID, future);
+                pendingRequests.put(messageID, promise);
             }
             try {
                 final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
@@ -470,22 +462,22 @@
                 throw adaptRequestIOException(e);
             }
         } catch (final LdapException e) {
-            future.adaptErrorResult(e.getResult());
+            promise.adaptErrorResult(e.getResult());
         }
-        return future;
+        return promise;
     }
 
     @Override
-    public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
+    public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request,
             final IntermediateResponseHandler intermediateResponseHandler) {
         final int messageID = nextMsgID.getAndIncrement();
-        final LDAPFutureResultImpl future =
-                new LDAPFutureResultImpl(messageID, request, intermediateResponseHandler, this);
+        final ResultLdapPromiseImpl<ModifyDNRequest, Result> promise =
+                newResultLdapPromise(messageID, request, intermediateResponseHandler, this);
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
                 checkBindOrStartTLSInProgress();
-                pendingRequests.put(messageID, future);
+                pendingRequests.put(messageID, promise);
             }
             try {
                 final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
@@ -500,9 +492,9 @@
                 throw adaptRequestIOException(e);
             }
         } catch (final LdapException e) {
-            future.adaptErrorResult(e.getResult());
+            promise.adaptErrorResult(e.getResult());
         }
-        return future;
+        return promise;
     }
 
     @Override
@@ -517,16 +509,16 @@
 
     /** {@inheritDoc} */
     @Override
-    public FutureResult<Result> searchAsync(final SearchRequest request,
+    public LdapPromise<Result> searchAsync(final SearchRequest request,
         final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) {
         final int messageID = nextMsgID.getAndIncrement();
-        final LDAPSearchFutureResultImpl future =
-            new LDAPSearchFutureResultImpl(messageID, request, entryHandler, intermediateResponseHandler, this);
+        final SearchResultLdapPromiseImpl promise =
+                newSearchLdapPromise(messageID, request, entryHandler, intermediateResponseHandler, this);
         try {
             synchronized (stateLock) {
                 checkConnectionIsValid();
                 checkBindOrStartTLSInProgress();
-                pendingRequests.put(messageID, future);
+                pendingRequests.put(messageID, promise);
             }
             try {
                 final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
@@ -541,9 +533,9 @@
                 throw adaptRequestIOException(e);
             }
         } catch (final LdapException e) {
-            future.adaptErrorResult(e.getResult());
+            promise.adaptErrorResult(e.getResult());
         }
-        return future;
+        return promise;
     }
 
     @Override
@@ -565,18 +557,18 @@
         }
 
         long delay = timeout;
-        for (final AbstractLDAPFutureResultImpl<?> future : pendingRequests.values()) {
-            if (future == null || !future.checkForTimeout()) {
+        for (final ResultLdapPromiseImpl<?, ?> promise : pendingRequests.values()) {
+            if (promise == null || !promise.checkForTimeout()) {
                 continue;
             }
-            final long diff = (future.getTimestamp() + timeout) - currentTime;
+            final long diff = (promise.getTimestamp() + timeout) - currentTime;
             if (diff > 0) {
                 // Will expire in diff milliseconds.
                 delay = Math.min(delay, diff);
-            } else if (pendingRequests.remove(future.getRequestID()) == null) {
+            } else if (pendingRequests.remove(promise.getRequestID()) == null) {
                 // Result arrived at the same time.
                 continue;
-            } else if (future.isBindOrStartTLS()) {
+            } else if (promise.isBindOrStartTLS()) {
                 /*
                  * No other operations can be performed while a bind or StartTLS
                  * request is active, so we cannot time out the request. We
@@ -586,20 +578,20 @@
                  * ignoring timeouts could cause the application to hang.
                  */
                 logger.debug(LocalizableMessage.raw("Failing bind or StartTLS request due to timeout %s"
-                        + "(connection will be invalidated): ", future));
+                        + "(connection will be invalidated): ", promise));
                 final Result result = Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT).setDiagnosticMessage(
                         LDAP_CONNECTION_BIND_OR_START_TLS_REQUEST_TIMEOUT.get(timeout).toString());
-                future.adaptErrorResult(result);
+                promise.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());
                 connectionErrorOccurred(errorResult);
             } else {
-                logger.debug(LocalizableMessage.raw("Failing request due to timeout: %s", future));
+                logger.debug(LocalizableMessage.raw("Failing request due to timeout: %s", promise));
                 final Result result = Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT).setDiagnosticMessage(
                         LDAP_CONNECTION_REQUEST_TIMEOUT.get(timeout).toString());
-                future.adaptErrorResult(result);
+                promise.adaptErrorResult(result);
 
                 /*
                  * FIXME: there's a potential race condition here if a bind or
@@ -609,7 +601,7 @@
                  * could hang the application.
                  */
                 // if (!bindOrStartTLSInProgress.get()) {
-                // sendAbandonRequest(newAbandonRequest(future.getRequestID()));
+                // sendAbandonRequest(newAbandonRequest(promise.getRequestID()));
                 // }
             }
         }
@@ -668,9 +660,9 @@
 
         // First abort all outstanding requests.
         for (final int requestID : pendingRequests.keySet()) {
-            final AbstractLDAPFutureResultImpl<?> future = pendingRequests.remove(requestID);
-            if (future != null) {
-                future.adaptErrorResult(connectionInvalidReason);
+            final ResultLdapPromiseImpl<?, ?> promise = pendingRequests.remove(requestID);
+            if (promise != null) {
+                promise.adaptErrorResult(connectionInvalidReason);
             }
         }
 
@@ -701,7 +693,7 @@
             if (notifyErrorOccurred) {
                 for (final ConnectionEventListener listener : tmpListeners) {
                     // Use the reason provided in the disconnect notification.
-                    listener.handleConnectionError(isDisconnectNotification, newErrorResult(reason));
+                    listener.handleConnectionError(isDisconnectNotification, newLdapException(reason));
                 }
             }
             if (notifyClose) {
@@ -712,11 +704,11 @@
         }
     }
 
-    int continuePendingBindRequest(final LDAPBindFutureResultImpl future) throws LdapException {
+    int continuePendingBindRequest(final BindResultLdapPromiseImpl promise) throws LdapException {
         final int newMsgID = nextMsgID.getAndIncrement();
         synchronized (stateLock) {
             checkConnectionIsValid();
-            pendingRequests.put(newMsgID, future);
+            pendingRequests.put(newMsgID, promise);
         }
         return newMsgID;
     }
@@ -725,7 +717,7 @@
         return factory.getLDAPOptions();
     }
 
-    AbstractLDAPFutureResultImpl<?> getPendingRequest(final Integer messageID) {
+    ResultLdapPromiseImpl<?, ?> getPendingRequest(final Integer messageID) {
         return pendingRequests.get(messageID);
     }
 
@@ -772,7 +764,7 @@
         }
     }
 
-    AbstractLDAPFutureResultImpl<?> removePendingRequest(final Integer messageID) {
+    ResultLdapPromiseImpl<?, ?> removePendingRequest(final Integer messageID) {
         return pendingRequests.remove(messageID);
     }
 
@@ -804,12 +796,12 @@
         // FIXME: Is this the best result code?
         final Result errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
         connectionErrorOccurred(errorResult);
-        return newErrorResult(errorResult);
+        return newLdapException(errorResult);
     }
 
     private void checkBindOrStartTLSInProgress() throws LdapException {
         if (bindOrStartTLSInProgress.get()) {
-            throw newErrorResult(ResultCode.OPERATIONS_ERROR, "Bind or Start TLS operation in progress");
+            throw newLdapException(ResultCode.OPERATIONS_ERROR, "Bind or Start TLS operation in progress");
         }
     }
 
@@ -824,9 +816,9 @@
                  * 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 newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN, "Connection closed by server");
             } else {
-                throw newErrorResult(connectionInvalidReason);
+                throw newLdapException(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 b18fe61..8df6801 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
@@ -38,9 +38,8 @@
 
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.ldap.Connection;
-import org.forgerock.opendj.ldap.LdapException;
-import org.forgerock.opendj.ldap.FutureResultImpl;
 import org.forgerock.opendj.ldap.LDAPOptions;
+import org.forgerock.opendj.ldap.LdapException;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.TimeoutChecker;
 import org.forgerock.opendj.ldap.TimeoutEventListener;
@@ -50,6 +49,7 @@
 import org.forgerock.opendj.ldap.spi.LDAPConnectionFactoryImpl;
 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.glassfish.grizzly.CompletionHandler;
 import org.glassfish.grizzly.EmptyCompletionHandler;
@@ -74,17 +74,16 @@
     private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
 
     /**
-     * Adapts a Grizzly connection completion handler to an LDAP connection
-     * asynchronous future result.
+     * Adapts a Grizzly connection completion handler to an LDAP connection promise.
      */
     @SuppressWarnings("rawtypes")
     private final class CompletionHandlerAdapter implements
             CompletionHandler<org.glassfish.grizzly.Connection>, TimeoutEventListener {
-        private final FutureResultImpl<Connection> future;
+        private final PromiseImpl<Connection, LdapException> promise;
         private final long timeoutEndTime;
 
-        private CompletionHandlerAdapter(final FutureResultImpl<Connection> future) {
-            this.future = future;
+        private CompletionHandlerAdapter(final PromiseImpl<Connection, LdapException> promise) {
+            this.promise = promise;
             final long timeoutMS = getTimeout();
             this.timeoutEndTime = timeoutMS > 0 ? System.currentTimeMillis() + timeoutMS : 0;
             timeoutChecker.get().addListener(this);
@@ -108,8 +107,8 @@
 
             // Start TLS or install SSL layer asynchronously.
 
-            // Give up immediately if the future has been cancelled or timed out.
-            if (future.isDone()) {
+            // Give up immediately if the promise has been cancelled or timed out.
+            if (promise.isDone()) {
                 timeoutChecker.get().removeListener(this);
                 connection.close();
                 return;
@@ -160,7 +159,7 @@
         public void failed(final Throwable throwable) {
             // Adapt and forward.
             timeoutChecker.get().removeListener(this);
-            future.handleError(adaptConnectionException(throwable));
+            promise.handleError(adaptConnectionException(throwable));
             releaseTransportAndTimeoutChecker();
         }
 
@@ -189,20 +188,20 @@
             if (t instanceof LdapException) {
                 return (LdapException) t;
             } else {
-                return newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR, t.getMessage(), t);
+                return newLdapException(ResultCode.CLIENT_SIDE_CONNECT_ERROR, t.getMessage(), t);
             }
         }
 
         private void onFailure(final GrizzlyLDAPConnection connection, final Throwable t) {
             // Abort connection attempt due to error.
             timeoutChecker.get().removeListener(this);
-            future.handleError(adaptConnectionException(t));
+            promise.handleError(adaptConnectionException(t));
             connection.close();
         }
 
         private void onSuccess(final GrizzlyLDAPConnection connection) {
             timeoutChecker.get().removeListener(this);
-            if (!future.tryHandleResult(connection)) {
+            if (!promise.tryHandleResult(connection)) {
                 // The connection has been either cancelled or it has timed out.
                 connection.close();
             }
@@ -215,7 +214,7 @@
             } else if (timeoutEndTime > currentTime) {
                 return timeoutEndTime - currentTime;
             } else {
-                future.handleError(newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
+                promise.handleError(newLdapException(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
                         LDAP_CONNECTION_CONNECT_TIMEOUT.get(getSocketAddress(), getTimeout()).toString()));
                 return 0;
             }
@@ -304,7 +303,7 @@
         try {
             return getConnectionAsync().getOrThrow();
         } catch (final InterruptedException e) {
-            throw newErrorResult(ResultCode.CLIENT_SIDE_USER_CANCELLED, e);
+            throw newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, e);
         }
     }
 
@@ -314,9 +313,9 @@
         final SocketConnectorHandler connectorHandler =
                 TCPNIOConnectorHandler.builder(transport.get()).processor(defaultFilterChain)
                         .build();
-        final FutureResultImpl<Connection> future = new FutureResultImpl<Connection>();
-        connectorHandler.connect(getSocketAddress(), new CompletionHandlerAdapter(future));
-        return future;
+        final PromiseImpl<Connection, LdapException> promise = PromiseImpl.create();
+        connectorHandler.connect(getSocketAddress(), new CompletionHandlerAdapter(promise));
+        return promise;
     }
 
     @Override
diff --git a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPClientFilter.java b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPClientFilter.java
index 736101f..e90e5ef 100644
--- a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPClientFilter.java
+++ b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPClientFilter.java
@@ -43,10 +43,13 @@
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.requests.AddRequest;
 import org.forgerock.opendj.ldap.requests.BindClient;
+import org.forgerock.opendj.ldap.requests.CompareRequest;
 import org.forgerock.opendj.ldap.requests.DeleteRequest;
+import org.forgerock.opendj.ldap.requests.ExtendedRequest;
 import org.forgerock.opendj.ldap.requests.GenericBindRequest;
 import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
 import org.forgerock.opendj.ldap.requests.ModifyRequest;
+import org.forgerock.opendj.ldap.requests.SearchRequest;
 import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
 import org.forgerock.opendj.ldap.responses.BindResult;
 import org.forgerock.opendj.ldap.responses.CompareResult;
@@ -56,12 +59,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.opendj.ldap.spi.AbstractLDAPFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.LDAPBindFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.LDAPCompareFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.LDAPExtendedFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.LDAPFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.LDAPSearchFutureResultImpl;
+import org.forgerock.opendj.ldap.spi.BindResultLdapPromiseImpl;
+import org.forgerock.opendj.ldap.spi.ExtendedResultLdapPromiseImpl;
+import org.forgerock.opendj.ldap.spi.ResultLdapPromiseImpl;
+import org.forgerock.opendj.ldap.spi.SearchResultLdapPromiseImpl;
 import org.glassfish.grizzly.Connection;
 import org.glassfish.grizzly.EmptyCompletionHandler;
 import org.glassfish.grizzly.Grizzly;
@@ -69,6 +70,9 @@
 import org.glassfish.grizzly.filterchain.FilterChainContext;
 import org.glassfish.grizzly.filterchain.NextAction;
 
+import static org.forgerock.opendj.ldap.ResultCode.*;
+import static org.forgerock.opendj.ldap.responses.Responses.*;
+
 /**
  * Grizzly filter implementation for decoding LDAP responses and handling client
  * side logic for SSL and SASL operations over LDAP.
@@ -109,22 +113,17 @@
             return this.reader;
         }
 
+        @SuppressWarnings({ "rawtypes", "unchecked" })
         @Override
         public void addResult(final int messageID, final Result result) throws DecodeException,
                 IOException {
-            final GrizzlyLDAPConnection ldapConnection =
-                    LDAP_CONNECTION_ATTR.get(context.getConnection());
+            final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
-                final AbstractLDAPFutureResultImpl<?> pendingRequest =
-                        ldapConnection.removePendingRequest(messageID);
-
+                final ResultLdapPromiseImpl pendingRequest = ldapConnection.removePendingRequest(messageID);
                 if (pendingRequest != null) {
-                    if (pendingRequest instanceof LDAPFutureResultImpl) {
-                        final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
-                        if (future.getRequest() instanceof AddRequest) {
-                            future.setResultOrError(result);
-                            return;
-                        }
+                    if (pendingRequest.getRequest() instanceof AddRequest) {
+                        pendingRequest.setResultOrError(result);
+                        return;
                     }
                     throw newUnexpectedResponseException(messageID, result);
                 }
@@ -134,23 +133,19 @@
         @Override
         public void bindResult(final int messageID, final BindResult result)
                 throws DecodeException, IOException {
-            final GrizzlyLDAPConnection ldapConnection =
-                    LDAP_CONNECTION_ATTR.get(context.getConnection());
+            final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
-                final AbstractLDAPFutureResultImpl<?> pendingRequest =
-                        ldapConnection.removePendingRequest(messageID);
-
+                final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID);
                 if (pendingRequest != null) {
-                    if (pendingRequest instanceof LDAPBindFutureResultImpl) {
-                        final LDAPBindFutureResultImpl future =
-                                ((LDAPBindFutureResultImpl) pendingRequest);
-                        final BindClient bindClient = future.getBindClient();
+                    if (pendingRequest instanceof BindResultLdapPromiseImpl) {
+                        final BindResultLdapPromiseImpl promise = (BindResultLdapPromiseImpl) pendingRequest;
+                        final BindClient bindClient = promise.getBindClient();
 
                         try {
                             if (!bindClient.evaluateResult(result)) {
                                 // The server is expecting a multi stage
                                 // bind response.
-                                final int msgID = ldapConnection.continuePendingBindRequest(future);
+                                final int msgID = ldapConnection.continuePendingBindRequest(promise);
 
                                 LDAPWriter<ASN1BufferWriter> ldapWriter = GrizzlyUtils.getWriter();
                                 try {
@@ -165,11 +160,10 @@
                             }
                         } catch (final LdapException e) {
                             ldapConnection.setBindOrStartTLSInProgress(false);
-                            future.adaptErrorResult(e.getResult());
+                            promise.adaptErrorResult(e.getResult());
                             return;
                         } catch (final IOException e) {
-                            // FIXME: I18N need to have a better error
-                            // message.
+                            // FIXME: I18N need to have a better error message.
                             // FIXME: Is this the best result code?
                             ldapConnection.setBindOrStartTLSInProgress(false);
                             final Result errorResult =
@@ -178,7 +172,7 @@
                                             .setDiagnosticMessage(
                                                     "An error occurred during multi-stage authentication")
                                             .setCause(e);
-                            future.adaptErrorResult(errorResult);
+                            promise.adaptErrorResult(errorResult);
                             return;
                         }
 
@@ -194,7 +188,7 @@
                         }
 
                         ldapConnection.setBindOrStartTLSInProgress(false);
-                        future.setResultOrError(result);
+                        promise.setResultOrError(result);
                         return;
                     }
                     throw newUnexpectedResponseException(messageID, result);
@@ -202,20 +196,17 @@
             }
         }
 
+        @SuppressWarnings("unchecked")
         @Override
         public void compareResult(final int messageID, final CompareResult result)
                 throws DecodeException, IOException {
-            final GrizzlyLDAPConnection ldapConnection =
-                    LDAP_CONNECTION_ATTR.get(context.getConnection());
+            final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
-                final AbstractLDAPFutureResultImpl<?> pendingRequest =
-                        ldapConnection.removePendingRequest(messageID);
-
+                final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID);
                 if (pendingRequest != null) {
-                    if (pendingRequest instanceof LDAPCompareFutureResultImpl) {
-                        final LDAPCompareFutureResultImpl future =
-                                (LDAPCompareFutureResultImpl) pendingRequest;
-                        future.setResultOrError(result);
+                    if (pendingRequest.getRequest() instanceof CompareRequest) {
+                        ((ResultLdapPromiseImpl<CompareRequest, CompareResult>) pendingRequest)
+                                .setResultOrError(result);
                         return;
                     }
                     throw newUnexpectedResponseException(messageID, result);
@@ -223,22 +214,17 @@
             }
         }
 
+        @SuppressWarnings("unchecked")
         @Override
         public void deleteResult(final int messageID, final Result result) throws DecodeException,
                 IOException {
-            final GrizzlyLDAPConnection ldapConnection =
-                    LDAP_CONNECTION_ATTR.get(context.getConnection());
+            final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
-                final AbstractLDAPFutureResultImpl<?> pendingRequest =
-                        ldapConnection.removePendingRequest(messageID);
-
+                final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID);
                 if (pendingRequest != null) {
-                    if (pendingRequest instanceof LDAPFutureResultImpl) {
-                        final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
-                        if (future.getRequest() instanceof DeleteRequest) {
-                            future.setResultOrError(result);
-                            return;
-                        }
+                    if (pendingRequest.getRequest() instanceof DeleteRequest) {
+                        ((ResultLdapPromiseImpl<DeleteRequest, Result>) pendingRequest).setResultOrError(result);
+                        return;
                     }
                     throw newUnexpectedResponseException(messageID, result);
                 }
@@ -248,39 +234,32 @@
         @Override
         public void extendedResult(final int messageID, final ExtendedResult result)
                 throws DecodeException, IOException {
-            final GrizzlyLDAPConnection ldapConnection =
-                    LDAP_CONNECTION_ATTR.get(context.getConnection());
+            final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
                 if (messageID == 0) {
                     // Unsolicited notification received.
-                    if ((result.getOID() != null)
-                            && result.getOID().equals(LDAP.OID_NOTICE_OF_DISCONNECTION)) {
+                    if (result.getOID() != null && result.getOID().equals(LDAP.OID_NOTICE_OF_DISCONNECTION)) {
                         // Treat this as a connection error.
-                        final Result errorResult =
-                                Responses.newResult(result.getResultCode()).setDiagnosticMessage(
+                        final Result errorResult = newResult(result.getResultCode()).setDiagnosticMessage(
                                         result.getDiagnosticMessage());
                         ldapConnection.close(null, true, errorResult);
                     } else {
                         ldapConnection.handleUnsolicitedNotification(result);
                     }
                 } else {
-                    final AbstractLDAPFutureResultImpl<?> pendingRequest =
-                            ldapConnection.removePendingRequest(messageID);
-
+                    final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID);
                     if (pendingRequest != null) {
-                        if (pendingRequest instanceof LDAPExtendedFutureResultImpl<?>) {
-                            final LDAPExtendedFutureResultImpl<?> extendedFuture =
-                                    ((LDAPExtendedFutureResultImpl<?>) pendingRequest);
+                        if (pendingRequest.getRequest() instanceof ExtendedRequest) {
+                            final ExtendedResultLdapPromiseImpl<?> extendedPromise =
+                                    (ExtendedResultLdapPromiseImpl<?>) pendingRequest;
                             try {
-                                handleExtendedResult0(ldapConnection, extendedFuture, result);
+                                handleExtendedResult0(ldapConnection, extendedPromise, result);
                             } catch (final DecodeException de) {
-                                // FIXME: should the connection be closed as
-                                // well?
-                                final Result errorResult =
-                                        Responses.newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR)
+                                // FIXME: should the connection be closed as well?
+                                final Result errorResult = newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR)
                                                 .setDiagnosticMessage(de.getLocalizedMessage())
                                                 .setCause(de);
-                                extendedFuture.adaptErrorResult(errorResult);
+                                extendedPromise.adaptErrorResult(errorResult);
                             }
                         } else {
                             throw newUnexpectedResponseException(messageID, result);
@@ -293,74 +272,58 @@
         @Override
         public void intermediateResponse(final int messageID, final IntermediateResponse response)
                 throws DecodeException, IOException {
-            final GrizzlyLDAPConnection ldapConnection =
-                    LDAP_CONNECTION_ATTR.get(context.getConnection());
+            final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
-                final AbstractLDAPFutureResultImpl<?> pendingRequest =
-                        ldapConnection.getPendingRequest(messageID);
-
+                final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.getPendingRequest(messageID);
                 if (pendingRequest != null) {
                     pendingRequest.handleIntermediateResponse(response);
                 }
             }
         }
 
+        @SuppressWarnings("unchecked")
         @Override
         public void modifyDNResult(final int messageID, final Result result)
                 throws DecodeException, IOException {
-            final GrizzlyLDAPConnection ldapConnection =
-                    LDAP_CONNECTION_ATTR.get(context.getConnection());
+            final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
-                final AbstractLDAPFutureResultImpl<?> pendingRequest =
-                        ldapConnection.removePendingRequest(messageID);
-
+                final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID);
                 if (pendingRequest != null) {
-                    if (pendingRequest instanceof LDAPFutureResultImpl) {
-                        final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
-                        if (future.getRequest() instanceof ModifyDNRequest) {
-                            future.setResultOrError(result);
-                            return;
-                        }
+                    if (pendingRequest.getRequest() instanceof ModifyDNRequest) {
+                        ((ResultLdapPromiseImpl<ModifyDNRequest, Result>) pendingRequest).setResultOrError(result);
+                        return;
                     }
                     throw newUnexpectedResponseException(messageID, result);
                 }
             }
         }
 
+        @SuppressWarnings("unchecked")
         @Override
-        public void modifyResult(final int messageID, final Result result) throws DecodeException,
-                IOException {
-            final GrizzlyLDAPConnection ldapConnection =
-                    LDAP_CONNECTION_ATTR.get(context.getConnection());
+        public void modifyResult(final int messageID, final Result result) throws DecodeException, IOException {
+            final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
-                final AbstractLDAPFutureResultImpl<?> pendingRequest =
-                        ldapConnection.removePendingRequest(messageID);
-
+                final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID);
                 if (pendingRequest != null) {
-                    if (pendingRequest instanceof LDAPFutureResultImpl) {
-                        final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
-                        if (future.getRequest() instanceof ModifyRequest) {
-                            future.setResultOrError(result);
-                            return;
-                        }
+                    if (pendingRequest.getRequest() instanceof ModifyRequest) {
+                        ((ResultLdapPromiseImpl<ModifyRequest, Result>) pendingRequest).setResultOrError(result);
+                        return;
                     }
                     throw newUnexpectedResponseException(messageID, result);
                 }
             }
         }
 
+        @SuppressWarnings("unchecked")
         @Override
         public void searchResult(final int messageID, final Result result) throws DecodeException,
                 IOException {
-            final GrizzlyLDAPConnection ldapConnection =
-                    LDAP_CONNECTION_ATTR.get(context.getConnection());
+            final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
-                final AbstractLDAPFutureResultImpl<?> pendingRequest =
-                        ldapConnection.removePendingRequest(messageID);
-
+                final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID);
                 if (pendingRequest != null) {
-                    if (pendingRequest instanceof LDAPSearchFutureResultImpl) {
-                        ((LDAPSearchFutureResultImpl) pendingRequest).setResultOrError(result);
+                    if (pendingRequest.getRequest() instanceof SearchRequest) {
+                        ((ResultLdapPromiseImpl<SearchRequest, Result>) pendingRequest).setResultOrError(result);
                     } else {
                         throw newUnexpectedResponseException(messageID, result);
                     }
@@ -371,15 +334,12 @@
         @Override
         public void searchResultEntry(final int messageID, final SearchResultEntry entry)
                 throws DecodeException, IOException {
-            final GrizzlyLDAPConnection ldapConnection =
-                    LDAP_CONNECTION_ATTR.get(context.getConnection());
+            final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
-                final AbstractLDAPFutureResultImpl<?> pendingRequest =
-                        ldapConnection.getPendingRequest(messageID);
-
+                final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.getPendingRequest(messageID);
                 if (pendingRequest != null) {
-                    if (pendingRequest instanceof LDAPSearchFutureResultImpl) {
-                        ((LDAPSearchFutureResultImpl) pendingRequest).handleEntry(entry);
+                    if (pendingRequest instanceof SearchResultLdapPromiseImpl) {
+                        ((SearchResultLdapPromiseImpl) pendingRequest).handleEntry(entry);
                     } else {
                         throw newUnexpectedResponseException(messageID, entry);
                     }
@@ -390,15 +350,12 @@
         @Override
         public void searchResultReference(final int messageID, final SearchResultReference reference)
                 throws DecodeException, IOException {
-            final GrizzlyLDAPConnection ldapConnection =
-                    LDAP_CONNECTION_ATTR.get(context.getConnection());
+            final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
-                final AbstractLDAPFutureResultImpl<?> pendingRequest =
-                        ldapConnection.getPendingRequest(messageID);
-
+                final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.getPendingRequest(messageID);
                 if (pendingRequest != null) {
-                    if (pendingRequest instanceof LDAPSearchFutureResultImpl) {
-                        ((LDAPSearchFutureResultImpl) pendingRequest).handleReference(reference);
+                    if (pendingRequest instanceof SearchResultLdapPromiseImpl) {
+                        ((SearchResultLdapPromiseImpl) pendingRequest).handleReference(reference);
                     } else {
                         throw newUnexpectedResponseException(messageID, reference);
                     }
@@ -408,50 +365,44 @@
 
         // Needed in order to expose type information.
         private <R extends ExtendedResult> void handleExtendedResult0(
-                final GrizzlyLDAPConnection conn, final LDAPExtendedFutureResultImpl<R> future,
+                final GrizzlyLDAPConnection conn, final ExtendedResultLdapPromiseImpl<R> promise,
                 final ExtendedResult result) throws DecodeException {
-            final R decodedResponse =
-                    future.decodeResult(result, conn.getLDAPOptions().getDecodeOptions());
+            final R decodedResponse = promise.decodeResult(result, conn.getLDAPOptions().getDecodeOptions());
 
-            if (future.getRequest() instanceof StartTLSExtendedRequest) {
+            if (promise.getRequest() instanceof StartTLSExtendedRequest) {
                 if (result.getResultCode() == ResultCode.SUCCESS) {
                     try {
-                        final StartTLSExtendedRequest request =
-                                (StartTLSExtendedRequest) future.getRequest();
+                        final StartTLSExtendedRequest request = (StartTLSExtendedRequest) promise.getRequest();
                         conn.startTLS(request.getSSLContext(), request.getEnabledProtocols(),
                                 request.getEnabledCipherSuites(),
                                 new EmptyCompletionHandler<SSLEngine>() {
                                     @Override
                                     public void completed(final SSLEngine result) {
                                         conn.setBindOrStartTLSInProgress(false);
-                                        future.setResultOrError(decodedResponse);
+                                        promise.setResultOrError(decodedResponse);
                                     }
 
                                     @Override
                                     public void failed(final Throwable throwable) {
-                                        final Result errorResult =
-                                                Responses.newResult(
-                                                        ResultCode.CLIENT_SIDE_LOCAL_ERROR)
-                                                        .setCause(throwable).setDiagnosticMessage(
-                                                                "SSL handshake failed");
+                                        final Result errorResult = newResult(CLIENT_SIDE_LOCAL_ERROR)
+                                                .setCause(throwable).setDiagnosticMessage("SSL handshake failed");
                                         conn.setBindOrStartTLSInProgress(false);
                                         conn.close(null, false, errorResult);
-                                        future.adaptErrorResult(errorResult);
+                                        promise.adaptErrorResult(errorResult);
                                     }
                                 });
                         return;
                     } catch (final IOException e) {
-                        final Result errorResult =
-                                Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR).setCause(e)
-                                        .setDiagnosticMessage(e.getMessage());
-                        future.adaptErrorResult(errorResult);
+                        final Result errorResult = newResult(CLIENT_SIDE_LOCAL_ERROR).setCause(e)
+                                .setDiagnosticMessage(e.getMessage());
+                        promise.adaptErrorResult(errorResult);
                         conn.close(null, false, errorResult);
                         return;
                     }
                 }
             }
 
-            future.setResultOrError(decodedResponse);
+            promise.setResultOrError(decodedResponse);
         }
     }
 
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 58fcbbc..b9a8824 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
@@ -47,7 +47,7 @@
 import org.forgerock.opendj.ldap.DN;
 import org.forgerock.opendj.ldap.LdapException;
 import org.forgerock.opendj.ldap.FailoverLoadBalancingAlgorithm;
-import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.LDAPClientContext;
 import org.forgerock.opendj.ldap.LDAPConnectionFactory;
@@ -88,8 +88,8 @@
 import static org.fest.assertions.Assertions.*;
 import static org.forgerock.opendj.ldap.Connections.*;
 import static org.forgerock.opendj.ldap.LdapException.*;
-import static org.forgerock.opendj.ldap.FutureResultWrapper.*;
 import static org.forgerock.opendj.ldap.TestCaseUtils.*;
+import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
 import static org.mockito.Matchers.*;
 import static org.mockito.Mockito.*;
 import static org.testng.Assert.*;
@@ -255,13 +255,12 @@
     }
 
     /**
-     * Tests the async connection in the blocking mode. This is not fully async
-     * as it blocks on the future.
+     * Tests the async connection in the blocking mode. This is not fully async as it blocks on the promise.
      *
      * @throws Exception
      */
     @Test(dataProvider = "connectionFactories", timeOut = TEST_TIMEOUT_MS)
-    public void testBlockingFutureNoHandler(ConnectionFactory factory) throws Exception {
+    public void testBlockingPromiseNoHandler(ConnectionFactory factory) throws Exception {
         final Promise<? extends Connection, LdapException> promise = factory.getConnectionAsync();
         final Connection con = promise.get();
         // quickly check if it is a valid connection.
@@ -276,8 +275,8 @@
      * @throws Exception
      */
     @Test(dataProvider = "connectionFactories", timeOut = TEST_TIMEOUT_MS)
-    public void testNonBlockingFutureWithHandler(ConnectionFactory factory) throws Exception {
-        // Use the handler to get the result asynchronously.
+    public void testNonBlockingPromiseWithHandler(ConnectionFactory factory) throws Exception {
+        // Use the promise to get the result asynchronously.
         final PromiseImpl<Connection, LdapException> promise = PromiseImpl.create();
 
         factory.getConnectionAsync().onSuccess(new SuccessHandler<Connection>() {
@@ -287,7 +286,6 @@
                 promise.handleResult(con);
             }
         }).onFailure(new FailureHandler<LdapException>() {
-
             @Override
             public void handleError(LdapException error) {
                 promise.handleError(error);
@@ -297,7 +295,8 @@
 
         // Since we don't have anything to do, we would rather
         // be notified by the promise when the other thread calls our handler.
-        promise.getOrThrow(); // should do a timed wait rather?
+        // should do a timed wait rather?
+        promise.getOrThrow();
     }
 
     /**
@@ -379,10 +378,10 @@
 
         // Mock underlying connection factory which always succeeds.
         final ConnectionFactory mockFactory = mock(ConnectionFactory.class);
-        when(mockFactory.getConnectionAsync()).thenAnswer(new Answer<FutureResult<Connection>>() {
+        when(mockFactory.getConnectionAsync()).thenAnswer(new Answer<LdapPromise<Connection>>() {
 
             @Override
-            public FutureResult<Connection> answer(InvocationOnMock invocation) throws Throwable {
+            public LdapPromise<Connection> answer(InvocationOnMock invocation) throws Throwable {
                 // Update state.
                 final int connectionID = realConnectionCount.getAndIncrement();
                 realConnectionIsClosed[connectionID] = false;
@@ -400,7 +399,7 @@
                 when(mockConnection.isValid()).thenReturn(true);
                 when(mockConnection.toString()).thenReturn("Mock connection " + connectionID);
 
-                return newSuccessfulFutureResult(mockConnection);
+                return newSuccessfulLdapPromise(mockConnection);
             }
         });
 
@@ -537,7 +536,7 @@
                         contextHolder.set((LDAPClientContext) invocation.getArguments()[0]);
                         connectLatch.countDown(); /* is this needed? */
                         if (config.closeOnAccept) {
-                            throw newErrorResult(ResultCode.UNAVAILABLE);
+                            throw newLdapException(ResultCode.UNAVAILABLE);
                         } else {
                             // Return a mock connection which always succeeds for binds.
                             ServerConnection<Integer> mockConnection = mock(ServerConnection.class);
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 24f1d7e..bb5d9ae 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
@@ -38,7 +38,7 @@
 import org.forgerock.opendj.ldap.Connections;
 import org.forgerock.opendj.ldap.DN;
 import org.forgerock.opendj.ldap.LdapException;
-import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.LDAPClientContext;
 import org.forgerock.opendj.ldap.LDAPConnectionFactory;
@@ -127,14 +127,14 @@
         try {
             for (int i = 0; i < ITERATIONS; i++) {
                 final PromiseImpl<LdapException, NeverThrowsException> promise = PromiseImpl.create();
-                final Promise<? extends Connection, LdapException> future = factory.getConnectionAsync();
-                future.onFailure(getFailureHandler(promise));
+                final Promise<? extends Connection, LdapException> connectionPromise = factory.getConnectionAsync();
+                connectionPromise.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.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
+                    connectionPromise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
                     fail("The connect request succeeded unexpectedly");
                 } catch (ConnectionException ce) {
                     assertThat(ce.getResult().getResultCode()).isEqualTo(ResultCode.CLIENT_SIDE_CONNECT_ERROR);
@@ -164,8 +164,8 @@
             final MockConnectionEventListener listener = new MockConnectionEventListener();
             connection.addConnectionEventListener(listener);
             final PromiseImpl<LdapException, NeverThrowsException> promise = PromiseImpl.create();
-            final FutureResult<BindResult> future = connection.bindAsync(newSimpleBindRequest());
-            future.onFailure(getFailureHandler(promise));
+            final LdapPromise<BindResult> bindPromise = connection.bindAsync(newSimpleBindRequest());
+            bindPromise.onFailure(getFailureHandler(promise));
             waitForBind();
 
             TimeoutResultException e = (TimeoutResultException) promise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
@@ -173,7 +173,7 @@
 
             // Wait for the request to timeout.
             try {
-                future.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
+                bindPromise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
                 fail("The bind request succeeded unexpectedly");
             } catch (TimeoutResultException te) {
                 verifyResultCodeIsClientSideTimeout(te);
@@ -214,17 +214,17 @@
 
                 // Now bind with timeout.
                 final PromiseImpl<LdapException, NeverThrowsException> promise = PromiseImpl.create();
-                final FutureResult<BindResult> future = connection.bindAsync(newSimpleBindRequest());
-                future.onFailure(getFailureHandler(promise));
+                final LdapPromise<BindResult> bindPromise = connection.bindAsync(newSimpleBindRequest());
+                bindPromise.onFailure(getFailureHandler(promise));
                 waitForBind();
 
                 // Wait for the request to timeout and check the handler was invoked.
                 TimeoutResultException e = (TimeoutResultException) promise.getOrThrow(5, TimeUnit.SECONDS);
                 verifyResultCodeIsClientSideTimeout(e);
 
-                // Now check the future was completed as expected.
+                // Now check the promise was completed as expected.
                 try {
-                    future.getOrThrow(5, TimeUnit.SECONDS);
+                    bindPromise.getOrThrow(5, TimeUnit.SECONDS);
                     fail("The bind request succeeded unexpectedly");
                 } catch (TimeoutResultException te) {
                     verifyResultCodeIsClientSideTimeout(te);
@@ -267,15 +267,16 @@
                 final ConnectionEventListener listener = mock(ConnectionEventListener.class);
                 connection.addConnectionEventListener(listener);
                 final PromiseImpl<LdapException, NeverThrowsException> promise = PromiseImpl.create();
-                final FutureResult<SearchResultEntry> future = connection.readEntryAsync(DN.valueOf("cn=test"), null);
-                future.onFailure(getFailureHandler(promise));
+                final LdapPromise<SearchResultEntry> connectionPromise =
+                        connection.readEntryAsync(DN.valueOf("cn=test"), null);
+                connectionPromise.onFailure(getFailureHandler(promise));
                 waitForSearch();
 
                 LdapException e = promise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
                 verifyResultCodeIsClientSideTimeout(e);
                 // Wait for the request to timeout.
                 try {
-                    future.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
+                    connectionPromise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
                     fail("The search request succeeded unexpectedly");
                 } catch (TimeoutResultException te) {
                     verifyResultCodeIsClientSideTimeout(te);
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 4f89f64..5a9b2a9 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
@@ -37,13 +37,12 @@
 import org.forgerock.opendj.ldap.ConnectionFactory;
 import org.forgerock.opendj.ldap.Connections;
 import org.forgerock.opendj.ldap.DecodeException;
-import org.forgerock.opendj.ldap.LdapException;
-import org.forgerock.opendj.ldap.FutureResultImpl;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.LDAPClientContext;
 import org.forgerock.opendj.ldap.LDAPConnectionFactory;
 import org.forgerock.opendj.ldap.LDAPListener;
 import org.forgerock.opendj.ldap.LDAPListenerOptions;
+import org.forgerock.opendj.ldap.LdapException;
 import org.forgerock.opendj.ldap.ProviderNotFoundException;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.ResultHandler;
@@ -68,14 +67,16 @@
 import org.forgerock.opendj.ldap.responses.ExtendedResult;
 import org.forgerock.opendj.ldap.responses.Responses;
 import org.forgerock.opendj.ldap.responses.Result;
+import org.forgerock.util.promise.PromiseImpl;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-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 static org.fest.assertions.Assertions.*;
+import static org.fest.assertions.Fail.*;
+import static org.forgerock.opendj.ldap.LdapException.*;
+import static org.forgerock.opendj.ldap.TestCaseUtils.*;
+import static org.mockito.Mockito.*;
 
 /**
  * Tests the LDAPListener class.
@@ -84,8 +85,8 @@
 public class GrizzlyLDAPListenerTestCase extends SdkTestCase {
 
     private static class MockServerConnection implements ServerConnection<Integer> {
-        final FutureResultImpl<Throwable> connectionError = new FutureResultImpl<Throwable>();
-        final FutureResultImpl<LDAPClientContext> context = new FutureResultImpl<LDAPClientContext>();
+        final PromiseImpl<Throwable, LdapException> connectionError = PromiseImpl.create();
+        final PromiseImpl<LDAPClientContext, LdapException> context = PromiseImpl.create();
         final CountDownLatch isClosed = new CountDownLatch(1);
 
         MockServerConnection() {
@@ -175,7 +176,7 @@
                 final ExtendedRequest<R> request,
                 final IntermediateResponseHandler intermediateResponseHandler,
                 final ResultHandler<R> resultHandler) throws UnsupportedOperationException {
-            resultHandler.handleError(LdapException.newErrorResult(request
+            resultHandler.handleError(newLdapException(request
                     .getResultDecoder().newExtendedErrorResult(ResultCode.PROTOCOL_ERROR, "",
                             "Extended operation " + request.getOID() + " not supported")));
         }
@@ -453,8 +454,7 @@
                         resultHandler.handleResult(Responses.newBindResult(ResultCode.SUCCESS));
                     } catch (final Exception e) {
                         // Unexpected.
-                        resultHandler.handleError(LdapException.newErrorResult(
-                                ResultCode.OTHER,
+                        resultHandler.handleError(newLdapException(ResultCode.OTHER,
                                 "Unexpected exception when connecting to load balancer", e));
                     }
                 }
@@ -522,7 +522,7 @@
                             try {
                                 // This is expected to fail.
                                 lcf.getConnection().close();
-                                throw LdapException.newErrorResult(ResultCode.OTHER,
+                                throw newLdapException(ResultCode.OTHER,
                                         "Connection to offline server succeeded unexpectedly");
                             } catch (final ConnectionException ce) {
                                 // This is expected - so go to online server.
@@ -534,14 +534,14 @@
                                     lcf.getConnection().close();
                                 } catch (final Exception e) {
                                     // Unexpected.
-                                    throw LdapException.newErrorResult(
+                                    throw newLdapException(
                                                     ResultCode.OTHER,
                                                     "Unexpected exception when connecting to online server",
                                                     e);
                                 }
                             } catch (final Exception e) {
                                 // Unexpected.
-                                throw LdapException.newErrorResult(
+                                throw newLdapException(
                                                 ResultCode.OTHER,
                                                 "Unexpected exception when connecting to offline server",
                                                 e);
@@ -608,7 +608,7 @@
                     try {
                         // This is expected to fail.
                         lcf.getConnection().close();
-                        resultHandler.handleError(LdapException.newErrorResult(
+                        resultHandler.handleError(newLdapException(
                                 ResultCode.OTHER,
                                 "Connection to offline server succeeded unexpectedly"));
                     } catch (final ConnectionException ce) {
@@ -620,13 +620,13 @@
                             resultHandler.handleResult(Responses.newBindResult(ResultCode.SUCCESS));
                         } catch (final Exception e) {
                             // Unexpected.
-                            resultHandler.handleError(LdapException.newErrorResult(
+                            resultHandler.handleError(newLdapException(
                                     ResultCode.OTHER,
                                     "Unexpected exception when connecting to online server", e));
                         }
                     } catch (final Exception e) {
                         // Unexpected.
-                        resultHandler.handleError(LdapException.newErrorResult(
+                        resultHandler.handleError(newLdapException(
                                 ResultCode.OTHER,
                                 "Unexpected exception when connecting to offline server", e));
                     }
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 f242122..7095db6 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
@@ -123,7 +123,7 @@
 
         if (request.getAuthenticationType() != BindRequest.AUTHENTICATION_TYPE_SIMPLE) {
             // TODO: SASL authentication not implemented.
-            resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR,
+            resultHandler.handleError(newLdapException(ResultCode.PROTOCOL_ERROR,
                     "non-SIMPLE authentication not supported: " + request.getAuthenticationType()));
         } else {
             // Authenticate using a separate bind connection pool, because
@@ -188,11 +188,11 @@
         final ResultHandler<R> resultHandler) {
         if (CancelExtendedRequest.OID.equals(request.getOID())) {
             // TODO: not implemented.
-            resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR,
+            resultHandler.handleError(newLdapException(ResultCode.PROTOCOL_ERROR,
                 "Cancel extended request operation not supported"));
         } else if (StartTLSExtendedRequest.OID.equals(request.getOID())) {
             // TODO: not implemented.
-            resultHandler.handleError(newErrorResult(ResultCode.PROTOCOL_ERROR,
+            resultHandler.handleError(newLdapException(ResultCode.PROTOCOL_ERROR,
                 "StartTLS extended request operation not supported"));
         } else {
             // Forward all other extended operations.
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 7d91c74..414f6e6 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
@@ -32,10 +32,9 @@
 import java.util.concurrent.CountDownLatch;
 
 import org.forgerock.opendj.ldap.Connection;
-import org.forgerock.opendj.ldap.LdapException;
-import org.forgerock.opendj.ldap.FutureResult;
-import org.forgerock.opendj.ldap.FutureResultWrapper;
 import org.forgerock.opendj.ldap.LDAPConnectionFactory;
+import org.forgerock.opendj.ldap.LdapException;
+import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchResultHandler;
 import org.forgerock.opendj.ldap.SearchScope;
@@ -52,6 +51,8 @@
 import org.forgerock.util.promise.Promise;
 import org.forgerock.util.promise.SuccessHandler;
 
+import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
+
 /**
  * An example client application which searches a Directory Server using the
  * asynchronous APIs. This example takes the following command line parameters:
@@ -84,7 +85,7 @@
                         @Override
                         public void handleError(LdapException error) {
                             System.err.println("Cancel request failed with result code: "
-                                + error.getResult().getResultCode().intValue());
+                                    + error.getResult().getResultCode().intValue());
                             CANCEL_LATCH.countDown();
                         }
                     });
@@ -114,8 +115,8 @@
         }
 
     }
-    // --- JCite search result handler ---
 
+    // --- JCite search result handler ---
 
     // --- JCite decl1 ---
     private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1);
@@ -134,6 +135,7 @@
     // --- JCite decl2 ---
     static int requestID;
     static int entryCount = 0;
+
     // --- JCite decl2 ---
 
     /**
@@ -178,7 +180,6 @@
             return;
         }
 
-
         // Initiate the asynchronous connect, bind, and search.
         final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName, port);
 
@@ -191,10 +192,10 @@
         }).thenAsync(new AsyncFunction<BindResult, Result, LdapException>() {
             @Override
             public Promise<Result, LdapException> apply(BindResult result) throws LdapException {
-                FutureResult<Result> future = FutureResultWrapper.asFutureResult(connection.searchAsync(
-                        Requests.newSearchRequest(baseDN, scope, filter, attributes), new SearchResultHandlerImpl()));
-                requestID = future.getRequestID();
-                return future;
+                LdapPromise<Result> promise = connection.searchAsync(
+                        Requests.newSearchRequest(baseDN, scope, filter, attributes), new SearchResultHandlerImpl());
+                requestID = promise.getRequestID();
+                return promise;
             }
         }).onSuccess(new SuccessHandler<Result>() {
             @Override
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 2a7c3b4..bd1c1b5 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
@@ -57,6 +57,8 @@
 import com.forgerock.opendj.cli.MultiChoiceArgument;
 import com.forgerock.opendj.cli.StringArgument;
 
+import static org.forgerock.opendj.ldap.LdapException.*;
+
 import static com.forgerock.opendj.cli.ArgumentConstants.*;
 import static com.forgerock.opendj.cli.Utils.*;
 import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
@@ -172,7 +174,7 @@
                 } catch (IOException e) {
                     // faking an error result by notifying the Handler
                     UpdateStatsResultHandler<Result> resHandler = new UpdateStatsResultHandler<Result>(currentTime);
-                    resHandler.handleError(LdapException.newErrorResult(ResultCode.OTHER, e));
+                    resHandler.handleError(newLdapException(ResultCode.OTHER, e));
                     return null;
                 }
             }
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 5a5bc91..a212d74 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
@@ -252,10 +252,10 @@
                                      */
                                     if (error instanceof EntryNotFoundException
                                             || error instanceof MultipleEntriesFoundException) {
-                                        normalizedError = newErrorResult(ResultCode.INVALID_CREDENTIALS, error);
+                                        normalizedError = newLdapException(ResultCode.INVALID_CREDENTIALS, error);
                                     } else if (error instanceof AuthenticationException
                                             || error instanceof AuthorizationException) {
-                                        normalizedError = newErrorResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR, error);
+                                        normalizedError = newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR, error);
                                     } else {
                                         normalizedError = error;
                                     }
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 66f31fd..aefadc6 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
@@ -30,7 +30,7 @@
 import org.forgerock.opendj.ldap.ConnectionEventListener;
 import org.forgerock.opendj.ldap.DN;
 import org.forgerock.opendj.ldap.LdapException;
-import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
 import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.SearchResultHandler;
@@ -72,8 +72,9 @@
     private static final class CachedRead implements SearchResultHandler, ResultHandler<Result> {
         private SearchResultEntry cachedEntry;
         private final String cachedFilterString;
-        private FutureResult<Result> cachedFuture; // Guarded by latch.
-        private final CountDownLatch cachedFutureLatch = new CountDownLatch(1);
+        /**  Guarded by cachedPromiseLatch.*/
+        private LdapPromise<Result> cachedPromise;
+        private final CountDownLatch cachedPromiseLatch = new CountDownLatch(1);
         private final SearchRequest cachedRequest;
         private volatile Result cachedResult;
         private final ConcurrentLinkedQueue<SearchResultHandler> waitingResultHandlers =
@@ -121,7 +122,7 @@
             }
         }
 
-        FutureResult<Result> getFutureResult() {
+        LdapPromise<Result> getPromise() {
             /*
              * Perform uninterrupted wait since this method is unlikely to block
              * for a long time.
@@ -129,11 +130,11 @@
             boolean wasInterrupted = false;
             while (true) {
                 try {
-                    cachedFutureLatch.await();
+                    cachedPromiseLatch.await();
                     if (wasInterrupted) {
                         Thread.currentThread().interrupt();
                     }
-                    return cachedFuture;
+                    return cachedPromise;
                 } catch (final InterruptedException e) {
                     wasInterrupted = true;
                 }
@@ -160,9 +161,9 @@
             return true;
         }
 
-        void setFuture(final FutureResult<Result> future) {
-            cachedFuture = future;
-            cachedFutureLatch.countDown();
+        void setPromise(final LdapPromise<Result> promise) {
+            cachedPromise = promise;
+            cachedPromiseLatch.countDown();
         }
 
         private void drainQueue() {
@@ -315,12 +316,12 @@
          */
         return new AbstractAsynchronousConnection() {
             @Override
-            public FutureResult<Void> abandonAsync(final AbandonRequest request) {
+            public LdapPromise<Void> abandonAsync(final AbandonRequest request) {
                 return connection.abandonAsync(request);
             }
 
             @Override
-            public FutureResult<Result> addAsync(final AddRequest request,
+            public LdapPromise<Result> addAsync(final AddRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
                 return connection.addAsync(withControls(request), intermediateResponseHandler);
             }
@@ -331,7 +332,7 @@
             }
 
             @Override
-            public FutureResult<BindResult> bindAsync(final BindRequest request,
+            public LdapPromise<BindResult> bindAsync(final BindRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
                 /*
                  * Simple brute force implementation in case the bind operation
@@ -352,20 +353,20 @@
             }
 
             @Override
-            public FutureResult<CompareResult> compareAsync(final CompareRequest request,
+            public LdapPromise<CompareResult> compareAsync(final CompareRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
                 return connection.compareAsync(withControls(request), intermediateResponseHandler);
             }
 
             @Override
-            public FutureResult<Result> deleteAsync(final DeleteRequest request,
+            public LdapPromise<Result> deleteAsync(final DeleteRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
                 evict(request.getName());
                 return connection.deleteAsync(withControls(request), intermediateResponseHandler);
             }
 
             @Override
-            public <R extends ExtendedResult> FutureResult<R> extendedRequestAsync(final ExtendedRequest<R> request,
+            public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
                 /*
                  * Simple brute force implementation in case the extended
@@ -386,14 +387,14 @@
             }
 
             @Override
-            public FutureResult<Result> modifyAsync(final ModifyRequest request,
+            public LdapPromise<Result> modifyAsync(final ModifyRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
                 evict(request.getName());
                 return connection.modifyAsync(withControls(request), intermediateResponseHandler);
             }
 
             @Override
-            public FutureResult<Result> modifyDNAsync(final ModifyDNRequest request,
+            public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler) {
                 // Simple brute force implementation: clear the cachedReads.
                 evictAll();
@@ -409,7 +410,7 @@
              * Try and re-use a cached result if possible.
              */
             @Override
-            public FutureResult<Result> searchAsync(final SearchRequest request,
+            public LdapPromise<Result> searchAsync(final SearchRequest request,
                 final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) {
                 /*
                  * Don't attempt caching if this search is not a read (base
@@ -428,18 +429,18 @@
                 if (cachedRead != null && cachedRead.isMatchingRead(request)) {
                     // The cached read matches this read request.
                     cachedRead.addResultHandler(entryHandler);
-                    return cachedRead.getFutureResult();
+                    return cachedRead.getPromise();
                 } else {
                     // Cache the read, possibly evicting a non-matching cached read.
                     final CachedRead pendingCachedRead = new CachedRead(request, entryHandler);
                     synchronized (cachedReads) {
                         cachedReads.put(request.getName(), pendingCachedRead);
                     }
-                    final FutureResult<Result> future = (FutureResult<Result>) connection
+                    final LdapPromise<Result> promise = (LdapPromise<Result>) connection
                             .searchAsync(withControls(request), intermediateResponseHandler, pendingCachedRead)
                             .onSuccess(pendingCachedRead).onFailure(pendingCachedRead);
-                    pendingCachedRead.setFuture(future);
-                    return future;
+                    pendingCachedRead.setPromise(promise);
+                    return promise;
                 }
             }
 
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 9f8d349..0a0521a 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
@@ -417,7 +417,7 @@
                                      * 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,
+                                     * possible fix will be to use a queue of pending resources (promises?). 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
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 6048251..9169b5f 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
@@ -168,7 +168,7 @@
                     @Override
                     public void handleResult(Result result) {
                         if (subFilters.size() >= SEARCH_MAX_CANDIDATES) {
-                            failureHandler.handleError(newErrorResult(ResultCode.ADMIN_LIMIT_EXCEEDED));
+                            failureHandler.handleError(newLdapException(ResultCode.ADMIN_LIMIT_EXCEEDED));
                         } else if (subFilters.size() == 1) {
                             h.handleResult(subFilters.get(0));
                         } else {
diff --git a/opendj-server/src/main/java/org/forgerock/opendj/server/core/ArchivableDataProvider.java b/opendj-server/src/main/java/org/forgerock/opendj/server/core/ArchivableDataProvider.java
index 2c8869f..5ebdeac 100644
--- a/opendj-server/src/main/java/org/forgerock/opendj/server/core/ArchivableDataProvider.java
+++ b/opendj-server/src/main/java/org/forgerock/opendj/server/core/ArchivableDataProvider.java
@@ -26,7 +26,7 @@
  */
 package org.forgerock.opendj.server.core;
 
-import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.ResultHandler;
 
 /**
@@ -56,9 +56,9 @@
      *            The configuration to use when performing the backup.
      * @param handler
      *            A handler which will be notified when the backup completes.
-     * @return A future representing the completion of the backup.
+     * @return A promise representing the completion of the backup.
      */
-    FutureResult<Void> createBackup(BackupConfig backupConfig, ResultHandler<Void> handler);
+    LdapPromise<Void> createBackup(BackupConfig backupConfig, ResultHandler<Void> handler);
 
     /**
      * Returns the ID of this data provider.
@@ -77,9 +77,9 @@
      *            The configuration to use when performing the restore.
      * @param handler
      *            A handler which will be notified when the restore completes.
-     * @return A future representing the completion of the restore.
+     * @return A promise representing the completion of the restore.
      */
-    FutureResult<Void> restoreBackup(RestoreConfig restoreConfig, ResultHandler<Void> handler);
+    LdapPromise<Void> restoreBackup(RestoreConfig restoreConfig, ResultHandler<Void> handler);
 
     /**
      * Indicates whether this data provider provides a mechanism to perform a
diff --git a/opendj-server/src/main/java/org/forgerock/opendj/server/core/ExportableDataProvider.java b/opendj-server/src/main/java/org/forgerock/opendj/server/core/ExportableDataProvider.java
index ac33949..0dd3c44 100644
--- a/opendj-server/src/main/java/org/forgerock/opendj/server/core/ExportableDataProvider.java
+++ b/opendj-server/src/main/java/org/forgerock/opendj/server/core/ExportableDataProvider.java
@@ -26,7 +26,7 @@
  */
 package org.forgerock.opendj.server.core;
 
-import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldif.EntryWriter;
 
@@ -48,9 +48,9 @@
      *            The entry writer.
      * @param handler
      *            A handler which will be notified when the export completes.
-     * @return A future representing the completion of the export.
+     * @return A promise representing the completion of the export.
      */
-    FutureResult<Void> exportEntries(EntryWriter writer, ResultHandler<Void> handler);
+    LdapPromise<Void> exportEntries(EntryWriter writer, ResultHandler<Void> handler);
 
     /**
      * Returns the ID of this data provider.
diff --git a/opendj-server/src/main/java/org/forgerock/opendj/server/core/ImportableDataProvider.java b/opendj-server/src/main/java/org/forgerock/opendj/server/core/ImportableDataProvider.java
index f532365..34a3559 100644
--- a/opendj-server/src/main/java/org/forgerock/opendj/server/core/ImportableDataProvider.java
+++ b/opendj-server/src/main/java/org/forgerock/opendj/server/core/ImportableDataProvider.java
@@ -26,7 +26,7 @@
  */
 package org.forgerock.opendj.server.core;
 
-import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldif.EntryReader;
 
@@ -63,7 +63,7 @@
      *            The entry reader.
      * @param handler
      *            A handler which will be notified when the import completes.
-     * @return A future representing the completion of the import.
+     * @return A promise representing the completion of the import.
      */
-    FutureResult<Void> importEntries(EntryReader reader, ResultHandler<Void> handler);
+    LdapPromise<Void> importEntries(EntryReader reader, ResultHandler<Void> handler);
 }
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 0139638..aed623a 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
@@ -79,6 +79,7 @@
 
 import static org.forgerock.opendj.adapter.server2x.Converters.*;
 import static org.forgerock.opendj.ldap.ByteString.*;
+import static org.forgerock.opendj.ldap.LdapException.*;
 import static org.forgerock.util.promise.Promises.*;
 
 /**
@@ -190,7 +191,7 @@
         try {
             return newConnection(new InternalClientConnection(to(dn)));
         } catch (DirectoryException e) {
-            throw LdapException.newErrorResult(Responses.newResult(ResultCode.NO_SUCH_OBJECT));
+            throw newLdapException(Responses.newResult(ResultCode.NO_SUCH_OBJECT));
         }
     }
 
@@ -346,8 +347,7 @@
                     bindClient.dispose();
 
                 } else { // not supported
-                    throw LdapException.newErrorResult(Responses
-                            .newResult(ResultCode.AUTH_METHOD_NOT_SUPPORTED));
+                    throw newLdapException(Responses.newResult(ResultCode.AUTH_METHOD_NOT_SUPPORTED));
                 }
                 BindResult result =
                         Responses.newBindResult(getResultCode(bindOperation));
@@ -356,7 +356,7 @@
                 if (result.isSuccess()) {
                     return result;
                 } else {
-                    throw LdapException.newErrorResult(result);
+                    throw newLdapException(result);
                 }
             }
 
diff --git a/opendj-server2x-adapter/src/main/java/org/forgerock/opendj/adapter/server2x/Converters.java b/opendj-server2x-adapter/src/main/java/org/forgerock/opendj/adapter/server2x/Converters.java
index 2f6455b..00b8882 100644
--- a/opendj-server2x-adapter/src/main/java/org/forgerock/opendj/adapter/server2x/Converters.java
+++ b/opendj-server2x-adapter/src/main/java/org/forgerock/opendj/adapter/server2x/Converters.java
@@ -61,6 +61,8 @@
 import org.opends.server.types.LDAPException;
 import org.opends.server.types.Operation;
 
+import static org.forgerock.opendj.ldap.LdapException.*;
+
 /**
  * Common utility methods.
  */
@@ -635,7 +637,7 @@
         if (result.isSuccess()) {
             return result;
         } else {
-            throw LdapException.newErrorResult(result);
+            throw newLdapException(result);
         }
     }
 
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 049b09e..e4ce2d8 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
@@ -79,6 +79,7 @@
 
 import static org.forgerock.opendj.adapter.server3x.Converters.*;
 import static org.forgerock.opendj.ldap.ByteString.*;
+import static org.forgerock.opendj.ldap.LdapException.*;
 import static org.forgerock.util.promise.Promises.*;
 
 /**
@@ -190,7 +191,7 @@
         try {
             return newConnection(new InternalClientConnection(to(dn)));
         } catch (DirectoryException e) {
-            throw LdapException.newErrorResult(Responses.newResult(ResultCode.NO_SUCH_OBJECT));
+            throw newLdapException(Responses.newResult(ResultCode.NO_SUCH_OBJECT));
         }
     }
 
@@ -345,8 +346,7 @@
                     bindClient.dispose();
 
                 } else { // not supported
-                    throw LdapException.newErrorResult(Responses
-                            .newResult(ResultCode.AUTH_METHOD_NOT_SUPPORTED));
+                    throw newLdapException(Responses.newResult(ResultCode.AUTH_METHOD_NOT_SUPPORTED));
                 }
                 BindResult result = Responses.newBindResult(bindOperation.getResultCode());
                 result.setServerSASLCredentials(bindOperation.getSASLCredentials());
@@ -354,7 +354,7 @@
                 if (result.isSuccess()) {
                     return result;
                 } else {
-                    throw LdapException.newErrorResult(result);
+                    throw newLdapException(result);
                 }
             }
 
diff --git a/opendj-server3x-adapter/src/main/java/org/forgerock/opendj/adapter/server3x/Converters.java b/opendj-server3x-adapter/src/main/java/org/forgerock/opendj/adapter/server3x/Converters.java
index 8cf65f5..ec8802f 100644
--- a/opendj-server3x-adapter/src/main/java/org/forgerock/opendj/adapter/server3x/Converters.java
+++ b/opendj-server3x-adapter/src/main/java/org/forgerock/opendj/adapter/server3x/Converters.java
@@ -63,6 +63,8 @@
 import org.opends.server.types.LDAPException;
 import org.opends.server.types.Operation;
 
+import static org.forgerock.opendj.ldap.LdapException.*;
+
 /**
  * Common utility methods.
  */
@@ -658,7 +660,7 @@
         if (result.isSuccess()) {
             return result;
         } else {
-            throw LdapException.newErrorResult(result);
+            throw newLdapException(result);
         }
     }
 

--
Gitblit v1.10.0