mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Gaetan Boismal
29.04.2014 600f6cb4356f2355a004604b9353505f0c2f7f49
OPENDJ-1536 OPENDJ-1285 Rename FutureResult classes hierarchy in the SDK to enhance code consistency

* org.forgerock.opendj.ldap package:
** Rename FutureResult to LdapPromise
** Rename FutureResultImpl to LdapPromiseImpl and move it to org.forgerock.opendj.ldap.spi
** Rename the util class FutureResultWrapper to LdapPromises and move it to org.forgerock.opendj.ldap.spi package
** Solve some bugs in HeartBeatConnectionFactory class

org.forgerock.opendj.ldap.spi
** Merge old LDAPFutureResultImpl and AbstractLDAPFutureResultImpl and rename it as ResultLdapPromiseImpl
** Remove AbstractLDAPFutureResultImpl, LDAPFutureResultImpl and LDAPCompareFutureResultImpl
** Rename LDAP[Bind | Extended | Search]FutureResultImpl to [Bind | Extended | Search]ResultLdapPromiseImpl
** Add static methods in LdapPromises to return *LdapPromiseImpl instances
7 files deleted
6 files added
2 files renamed
64 files modified
4231 ■■■■ changed files
opendj-cli/src/main/java/com/forgerock/opendj/cli/AuthenticatedConnectionFactory.java 10 ●●●● patch | view | raw | blame | history
opendj-config/src/test/java/org/forgerock/opendj/config/client/ldap/LDAPClientTest.java 5 ●●●●● patch | view | raw | blame | history
opendj-core/clirr-ignored-api-changes.xml 32 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnection.java 6 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractConnection.java 47 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractConnectionWrapper.java 42 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithm.java 4 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/AbstractSynchronousConnection.java 28 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/AuthenticatedConnectionFactory.java 2 ●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/CachedConnectionPool.java 117 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/Connection.java 112 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/Entries.java 16 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResult.java 46 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResultImpl.java 64 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResultWrapper.java 278 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactory.java 587 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/InternalConnection.java 128 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/InternalConnectionFactory.java 4 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/LdapException.java 40 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/LdapPromise.java 75 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java 41 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactoryAdapter.java 20 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/ResultHandler.java 2 ●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/RootDSE.java 15 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java 10 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java 10 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java 6 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java 20 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java 4 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/responses/AbstractExtendedResultDecoder.java 6 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java 25 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java 34 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPFutureResultImpl.java 200 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/BindResultLdapPromiseImpl.java 51 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/ExtendedResultLdapPromiseImpl.java 99 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPCompareFutureResultImpl.java 85 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPExtendedFutureResultImpl.java 115 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPFutureResultImpl.java 92 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromiseImpl.java 92 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromiseWrapper.java 184 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromises.java 273 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/ResultLdapPromiseImpl.java 191 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/SearchResultLdapPromiseImpl.java 59 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/TransportProvider.java 3 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldif/ConnectionEntryReader.java 13 ●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractAsynchronousConnectionTestCase.java 53 ●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/AbstractLoadBalancingAlgorithmTestCase.java 8 ●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/ConnectionPoolTestCase.java 33 ●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/HeartBeatConnectionFactoryTestCase.java 54 ●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java 23 ●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/TestCaseUtils.java 4 ●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/EntrySchemaCheckingTestCase.java 3 ●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java 12 ●●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaTestCase.java 19 ●●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/spi/BasicLDAPConnectionFactory.java 2 ●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/spi/ConnectionStateTest.java 6 ●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldif/ConnectionChangeRecordWriterTestCase.java 2 ●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldif/ConnectionEntryReaderTestCase.java 30 ●●●●● patch | view | raw | blame | history
opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyTransportProvider.java 3 ●●●● patch | view | raw | blame | history
opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java 218 ●●●● patch | view | raw | blame | history
opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java 35 ●●●● patch | view | raw | blame | history
opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPClientFilter.java 217 ●●●●● patch | view | raw | blame | history
opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/ConnectionFactoryTestCase.java 25 ●●●● patch | view | raw | blame | history
opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactoryTestCase.java 29 ●●●● patch | view | raw | blame | history
opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java 34 ●●●● patch | view | raw | blame | history
opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ProxyBackend.java 6 ●●●● patch | view | raw | blame | history
opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java 19 ●●●● patch | view | raw | blame | history
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java 4 ●●● patch | view | raw | blame | history
opendj-rest2ldap-servlet/src/main/java/org/forgerock/opendj/rest2ldap/servlet/Rest2LDAPAuthnFilter.java 4 ●●●● patch | view | raw | blame | history
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java 45 ●●●● patch | view | raw | blame | history
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java 2 ●●● patch | view | raw | blame | history
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java 2 ●●● patch | view | raw | blame | history
opendj-server/src/main/java/org/forgerock/opendj/server/core/ArchivableDataProvider.java 10 ●●●● patch | view | raw | blame | history
opendj-server/src/main/java/org/forgerock/opendj/server/core/ExportableDataProvider.java 6 ●●●● patch | view | raw | blame | history
opendj-server/src/main/java/org/forgerock/opendj/server/core/ImportableDataProvider.java 6 ●●●● patch | view | raw | blame | history
opendj-server2x-adapter/src/main/java/org/forgerock/opendj/adapter/server2x/Adapters.java 8 ●●●● patch | view | raw | blame | history
opendj-server2x-adapter/src/main/java/org/forgerock/opendj/adapter/server2x/Converters.java 4 ●●● patch | view | raw | blame | history
opendj-server3x-adapter/src/main/java/org/forgerock/opendj/adapter/server3x/Adapters.java 8 ●●●● patch | view | raw | blame | history
opendj-server3x-adapter/src/main/java/org/forgerock/opendj/adapter/server3x/Converters.java 4 ●●● patch | view | raw | blame | history
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) {
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 =
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>
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);
    }
}
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,9 +411,9 @@
    }
    @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(
        return asPromise(searchAsync(enforceSingleEntrySearchRequest(request), handler).then(
            new Function<Result, SearchResultEntry, LdapException>() {
                @Override
                public SearchResultEntry apply(final Result value) throws LdapException {
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);
    }
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);
    }
}
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);
    }
}
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();
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()));
        }
    }
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);
}
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;
opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResult.java
File was deleted
opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResultImpl.java
File was deleted
opendj-core/src/main/java/org/forgerock/opendj/ldap/FutureResultWrapper.java
File was deleted
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 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.
         */
        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);
                    }
                }
            }
            protected abstract FutureResult<R> dispatch();
            @Override
            protected final LdapException tryCancel(final boolean mayInterruptIfRunning) {
                if (innerFuture != null) {
                    innerFuture.cancel(mayInterruptIfRunning);
                }
                        wrappedPromise.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,9 +482,19 @@
             */
            if (sync.tryLockExclusively()) {
                try {
                    FutureResult<Result> future = connection.searchAsync(heartBeatRequest, heartBeatEntryHandler);
                    if (future != null) {
                        future.onSuccess(new SuccessHandler<Result>() {
                    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);
@@ -707,11 +504,10 @@
                            @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.
                             * 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)) {
                                    /*
@@ -724,10 +520,8 @@
                                    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);
            connection = factory.getConnection();
            connection.searchAsync(heartBeatRequest, null).getOrThrow(timeoutMS, TimeUnit.MILLISECONDS);
            return registerConnection(new ConnectionImpl(connection));
            } catch (final Exception e) {
                throw adaptHeartBeatError(e);
            } finally {
                if (!succeeded) {
                    connection.close();
                }
            }
        } finally {
            if (!succeeded) {
            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();
            }
        }).then(compositeFuture.futureSearchSuccess, compositeFuture.futureSearchError);
                    }
                }, timeoutMS, TimeUnit.MILLISECONDS);
                return connection.searchAsync(heartBeatRequest, null);
            }
        }).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));
    }
}
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;
    }
    /**
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));
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.
opendj-core/src/main/java/org/forgerock/opendj/ldap/LdapPromise.java
New file
@@ -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);
}
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");
    }
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 {
                /*
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
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);
                }
            }));
            });
    }
    /**
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));
            }
        }
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);
            }
        }
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));
            }
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);
            }
        }
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);
            }
        }
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));
                }
            }
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();
                }
            }));
            });
    }
    /**
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,
    public LdapPromise<SchemaBuilder> addSchemaForEntryAsync(final Connection connection, final DN name,
        final boolean overwrite) {
        final SearchRequest request = getReadSchemaForEntrySearchRequest(name);
        return asFutureResult(connection.searchSingleEntryAsync(request).thenAsync(
        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);
                    }
                }));
                });
    }
    /**
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPFutureResultImpl.java
File was deleted
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/BindResultLdapPromiseImpl.java
File was renamed from opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPBindFutureResultImpl.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);
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/ExtendedResultLdapPromiseImpl.java
New file
@@ -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);
    }
}
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPCompareFutureResultImpl.java
File was deleted
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPExtendedFutureResultImpl.java
File was deleted
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPFutureResultImpl.java
File was deleted
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromiseImpl.java
New file
@@ -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);
    }
}
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromiseWrapper.java
New file
@@ -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;
    }
}
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LdapPromises.java
New file
@@ -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() {
    }
}
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/ResultLdapPromiseImpl.java
New file
@@ -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;
    }
}
opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/SearchResultLdapPromiseImpl.java
File was renamed from opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPSearchFutureResultImpl.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;
    }
}
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}.
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) {
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));
    }
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);
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);
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>>() {
        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));
                }
            }
        };
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;
            }
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;
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());
                }
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
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
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);
        }
    }
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);
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);
                    }
                });
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));
                    }
                }
            });
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);
    }
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);
            }
        }
    }
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
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,23 +113,18 @@
            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);
                    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,23 +214,18 @@
            }
        }
        @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);
                    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);
                    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);
                    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)
                        final Result errorResult = newResult(CLIENT_SIDE_LOCAL_ERROR).setCause(e)
                                        .setDiagnosticMessage(e.getMessage());
                        future.adaptErrorResult(errorResult);
                        promise.adaptErrorResult(errorResult);
                        conn.close(null, false, errorResult);
                        return;
                    }
                }
            }
            future.setResultOrError(decodedResponse);
            promise.setResultOrError(decodedResponse);
        }
    }
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);
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);
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));
                    }
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.
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:
@@ -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
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;
                }
            }
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;
                                    }
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;
                }
            }
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
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 {
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
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.
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);
}
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);
                }
            }
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);
        }
    }
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);
                }
            }
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);
        }
    }