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

---
 opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java |  762 +++++++++++++++++++++++++++++-----------------------------
 1 files changed, 381 insertions(+), 381 deletions(-)

diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
index 69742ff..2d2d3e8 100644
--- a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
+++ b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
@@ -11,24 +11,11 @@
  * Header, with the fields enclosed by brackets [] replaced by your own identifying
  * information: "Portions Copyright [year] [name of copyright owner]".
  *
- * Copyright 2012-2013 ForgeRock AS.
+ * Copyright 2012-2014 ForgeRock AS.
  */
 package org.forgerock.opendj.rest2ldap;
 
-import static java.util.Arrays.asList;
-import static org.forgerock.opendj.ldap.Filter.alwaysFalse;
-import static org.forgerock.opendj.ldap.Filter.alwaysTrue;
-import static org.forgerock.opendj.ldap.requests.Requests.newAddRequest;
-import static org.forgerock.opendj.ldap.requests.Requests.newDeleteRequest;
-import static org.forgerock.opendj.ldap.requests.Requests.newModifyRequest;
-import static org.forgerock.opendj.ldap.requests.Requests.newSearchRequest;
-import static org.forgerock.opendj.rest2ldap.ReadOnUpdatePolicy.CONTROLS;
-import static org.forgerock.opendj.rest2ldap.Rest2LDAP.asResourceException;
-import static org.forgerock.opendj.rest2ldap.Utils.accumulate;
-import static org.forgerock.opendj.rest2ldap.Utils.i18n;
-import static org.forgerock.opendj.rest2ldap.Utils.toFilter;
-import static org.forgerock.opendj.rest2ldap.Utils.transform;
-
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
@@ -86,16 +73,48 @@
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
 import org.forgerock.opendj.ldif.ChangeRecord;
+import org.forgerock.util.promise.FailureHandler;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.PromiseImpl;
+import org.forgerock.util.promise.Promises;
+import org.forgerock.util.promise.SuccessHandler;
+
+import static java.util.Arrays.*;
+
+import static org.forgerock.opendj.ldap.Filter.*;
+import static org.forgerock.opendj.ldap.requests.Requests.*;
+import static org.forgerock.opendj.rest2ldap.ReadOnUpdatePolicy.*;
+import static org.forgerock.opendj.rest2ldap.Rest2LDAP.*;
+import static org.forgerock.opendj.rest2ldap.Utils.*;
 
 /**
  * A {@code CollectionResourceProvider} implementation which maps a JSON
  * resource collection to LDAP entries beneath a base DN.
  */
 final class LDAPCollectionResourceProvider implements CollectionResourceProvider {
-    // Dummy exception used for signalling search success.
+    private static class ResultHandlerFromPromise<T> implements ResultHandler<T> {
+        private final PromiseImpl<T, ResourceException> promise;
+
+        ResultHandlerFromPromise() {
+            promise = PromiseImpl.create();
+        }
+
+        @Override
+        public void handleError(ResourceException error) {
+            promise.handleError(error);
+
+        }
+
+        @Override
+        public void handleResult(T result) {
+            promise.handleResult(result);
+        }
+    }
+
+    /** Dummy exception used for signalling search success. */
     private static final ResourceException SUCCESS = new UncategorizedException(0, null, null);
 
-    // Empty decode options required for decoding response controls.
+    /** Empty decode options required for decoding response controls. */
     private static final DecodeOptions DECODE_OPTIONS = new DecodeOptions();
 
     private final List<Attribute> additionalLDAPAttributes;
@@ -140,39 +159,37 @@
             public void run() {
                 // Calculate entry content.
                 attributeMapper.create(c, new JsonPointer(), request.getContent(),
-                        new ResultHandler<List<Attribute>>() {
-                            @Override
-                            public void handleError(final ResourceException error) {
-                                h.handleError(error);
-                            }
+                    new ResultHandler<List<Attribute>>() {
+                        @Override
+                        public void handleError(final ResourceException error) {
+                            h.handleError(error);
+                        }
 
-                            @Override
-                            public void handleResult(final List<Attribute> result) {
-                                // Perform add operation.
-                                final AddRequest addRequest = newAddRequest(DN.rootDN());
-                                for (final Attribute attribute : additionalLDAPAttributes) {
-                                    addRequest.addAttribute(attribute);
-                                }
-                                for (final Attribute attribute : result) {
-                                    addRequest.addAttribute(attribute);
-                                }
-                                try {
-                                    nameStrategy.setResourceId(c, getBaseDN(c), request
-                                            .getNewResourceId(), addRequest);
-                                } catch (final ResourceException e) {
-                                    h.handleError(e);
-                                    return;
-                                }
-                                if (config.readOnUpdatePolicy() == CONTROLS) {
-                                    final String[] attributes =
-                                            getLDAPAttributes(c, request.getFields());
-                                    addRequest.addControl(PostReadRequestControl.newControl(false,
-                                            attributes));
-                                }
-                                c.getConnection().applyChangeAsync(addRequest, null,
-                                        postUpdateHandler(c, h));
+                        @Override
+                        public void handleResult(final List<Attribute> result) {
+                            // Perform add operation.
+                            final AddRequest addRequest = newAddRequest(DN.rootDN());
+                            for (final Attribute attribute : additionalLDAPAttributes) {
+                                addRequest.addAttribute(attribute);
                             }
-                        });
+                            for (final Attribute attribute : result) {
+                                addRequest.addAttribute(attribute);
+                            }
+                            try {
+                                nameStrategy.setResourceId(c, getBaseDN(c), request.getNewResourceId(), addRequest);
+                            } catch (final ResourceException e) {
+                                h.handleError(e);
+                                return;
+                            }
+                            if (config.readOnUpdatePolicy() == CONTROLS) {
+                                final String[] attributes = getLDAPAttributes(c, request.getFields());
+                                addRequest.addControl(PostReadRequestControl.newControl(false, attributes));
+                            }
+                            c.getConnection().applyChangeAsync(addRequest)
+                                        .onSuccess(postUpdateSuccessHandler(c, h))
+                                        .onFailure(postUpdateFailureHandler(h));
+                        }
+                    });
             }
         });
     }
@@ -196,15 +213,14 @@
                     final ChangeRecord deleteRequest = newDeleteRequest(dn);
                     if (config.readOnUpdatePolicy() == CONTROLS) {
                         final String[] attributes = getLDAPAttributes(c, request.getFields());
-                        deleteRequest.addControl(PreReadRequestControl
-                                .newControl(false, attributes));
+                        deleteRequest.addControl(PreReadRequestControl.newControl(false, attributes));
                     }
                     if (config.useSubtreeDelete()) {
                         deleteRequest.addControl(SubtreeDeleteRequestControl.newControl(true));
                     }
                     addAssertionControl(deleteRequest, request.getRevision());
-                    c.getConnection()
-                            .applyChangeAsync(deleteRequest, null, postUpdateHandler(c, h));
+                    c.getConnection().applyChangeAsync(deleteRequest).onSuccess(postUpdateSuccessHandler(c, h))
+                            .onFailure(postUpdateFailureHandler(h));
                 } catch (final Exception e) {
                     h.handleError(asResourceException(e));
                 }
@@ -213,31 +229,29 @@
     }
 
     @Override
-    public void patchInstance(final ServerContext context, final String resourceId,
-            final PatchRequest request, final ResultHandler<Resource> handler) {
+    public void patchInstance(final ServerContext context, final String resourceId, final PatchRequest request,
+            final ResultHandler<Resource> handler) {
         final Context c = wrap(context);
         final ResultHandler<Resource> h = wrap(c, handler);
 
         if (request.getPatchOperations().isEmpty()) {
             /*
-             * This patch is a no-op so just read the entry and check its
-             * version.
+             * This patch is a no-op so just read the entry and check its version.
              */
             c.run(h, new Runnable() {
                 @Override
                 public void run() {
                     final String[] attributes = getLDAPAttributes(c, request.getFields());
-                    final SearchRequest searchRequest =
-                            nameStrategy.createSearchRequest(c, getBaseDN(c), resourceId)
-                                    .addAttribute(attributes);
-                    c.getConnection().searchSingleEntryAsync(searchRequest,
-                            postEmptyPatchHandler(c, request, h));
+                    final SearchRequest searchRequest = nameStrategy.createSearchRequest(c, getBaseDN(c), resourceId)
+                            .addAttribute(attributes);
+                    c.getConnection().searchSingleEntryAsync(searchRequest)
+                            .onSuccess(postEmptyPatchSuccessHandler(c, request, h))
+                            .onFailure(postEmptyPatchFailureHandler(h));
                 }
             });
         } else {
             /*
-             * Get the connection, search if needed, then determine
-             * modifications, then perform modify.
+             * Get the connection, search if needed, then determine modifications, then perform modify.
              */
             c.run(h, doUpdate(c, resourceId, request.getRevision(), new ResultHandler<DN>() {
                 @Override
@@ -247,71 +261,62 @@
 
                 @Override
                 public void handleResult(final DN dn) {
-                    //  Convert the patch operations to LDAP modifications.
-                    final ResultHandler<List<Modification>> handler =
-                            accumulate(request.getPatchOperations().size(),
-                                    new ResultHandler<List<List<Modification>>>() {
-                                        @Override
-                                        public void handleError(final ResourceException error) {
-                                            h.handleError(error);
-                                        }
-
-                                        @Override
-                                        public void handleResult(
-                                                final List<List<Modification>> result) {
-                                            //  The patch operations have been converted successfully.
-                                            try {
-                                                final ModifyRequest modifyRequest =
-                                                        newModifyRequest(dn);
-
-                                                // Add the modifications.
-                                                for (final List<Modification> modifications : result) {
-                                                    if (modifications != null) {
-                                                        modifyRequest.getModifications().addAll(
-                                                                modifications);
-                                                    }
-                                                }
-
-                                                final List<String> attributes =
-                                                        asList(getLDAPAttributes(c, request
-                                                                .getFields()));
-                                                if (modifyRequest.getModifications().isEmpty()) {
-                                                    /*
-                                                     * This patch is a no-op so
-                                                     * just read the entry and
-                                                     * check its version.
-                                                     */
-                                                    c.getConnection().readEntryAsync(dn,
-                                                            attributes,
-                                                            postEmptyPatchHandler(c, request, h));
-                                                } else {
-                                                    // Add controls and perform the modify request.
-                                                    if (config.readOnUpdatePolicy() == CONTROLS) {
-                                                        modifyRequest
-                                                                .addControl(PostReadRequestControl
-                                                                        .newControl(false,
-                                                                                attributes));
-                                                    }
-                                                    if (config.usePermissiveModify()) {
-                                                        modifyRequest
-                                                                .addControl(PermissiveModifyRequestControl
-                                                                        .newControl(true));
-                                                    }
-                                                    addAssertionControl(modifyRequest, request
-                                                            .getRevision());
-                                                    c.getConnection().applyChangeAsync(
-                                                            modifyRequest, null,
-                                                            postUpdateHandler(c, h));
-                                                }
-                                            } catch (final Exception e) {
-                                                h.handleError(asResourceException(e));
-                                            }
-                                        }
-                                    });
-
+                    // Convert the patch operations to LDAP modifications.
+                    List<Promise<List<Modification>, ResourceException>> promises =
+                            new ArrayList<Promise<List<Modification>, ResourceException>>(
+                                    request.getPatchOperations().size());
                     for (final PatchOperation operation : request.getPatchOperations()) {
+                        final ResultHandlerFromPromise<List<Modification>> handler =
+                                new ResultHandlerFromPromise<List<Modification>>();
                         attributeMapper.patch(c, new JsonPointer(), operation, handler);
+                        promises.add(handler.promise);
                     }
+
+                    Promises.when(promises).onSuccess(new SuccessHandler<List<List<Modification>>>() {
+                        @Override
+                        public void handleResult(final List<List<Modification>> result) {
+                            // The patch operations have been converted successfully.
+                            try {
+                                final ModifyRequest modifyRequest = newModifyRequest(dn);
+
+                                // Add the modifications.
+                                for (final List<Modification> modifications : result) {
+                                    if (modifications != null) {
+                                        modifyRequest.getModifications().addAll(modifications);
+                                    }
+                                }
+
+                                final List<String> attributes = asList(getLDAPAttributes(c, request.getFields()));
+                                if (modifyRequest.getModifications().isEmpty()) {
+                                    /*
+                                     * This patch is a no-op so just read the entry and check its version.
+                                     */
+                                    c.getConnection().readEntryAsync(dn, attributes)
+                                            .onSuccess(postEmptyPatchSuccessHandler(c, request, h))
+                                            .onFailure(postEmptyPatchFailureHandler(h));
+                                } else {
+                                    // Add controls and perform the modify request.
+                                    if (config.readOnUpdatePolicy() == CONTROLS) {
+                                        modifyRequest.addControl(PostReadRequestControl.newControl(false, attributes));
+                                    }
+                                    if (config.usePermissiveModify()) {
+                                        modifyRequest.addControl(PermissiveModifyRequestControl.newControl(true));
+                                    }
+                                    addAssertionControl(modifyRequest, request.getRevision());
+                                    c.getConnection().applyChangeAsync(modifyRequest)
+                                            .onSuccess(postUpdateSuccessHandler(c, h))
+                                            .onFailure(postUpdateFailureHandler(h));
+                                }
+                            } catch (final Exception e) {
+                                h.handleError(asResourceException(e));
+                            }
+                        }
+                    }).onFailure(new FailureHandler<ResourceException>() {
+                        @Override
+                        public void handleError(ResourceException error) {
+                            h.handleError(asResourceException(error));
+                        }
+                    });
                 }
             }));
         }
@@ -324,14 +329,25 @@
         final QueryResultHandler h = wrap(c, handler);
 
         /*
-         * Get the connection, then calculate the search filter, then perform
-         * the search.
+         * Get the connection, then calculate the search filter, then perform the search.
          */
         c.run(h, new Runnable() {
             @Override
             public void run() {
                 // Calculate the filter (this may require the connection).
                 getLDAPFilter(c, request.getQueryFilter(), new ResultHandler<Filter>() {
+                    /**
+                     * The following fields are guarded by sequenceLock. In
+                     * addition, the sequenceLock ensures that we send one JSON
+                     * resource at a time back to the client.
+                     */
+                    private final Object sequenceLock = new Object();
+                    private String cookie;
+                    private ResourceException pendingResult;
+                    private int pendingResourceCount;
+                    private boolean resultSent;
+                    private int totalResourceCount;
+
                     @Override
                     public void handleError(final ResourceException error) {
                         h.handleError(error);
@@ -340,23 +356,21 @@
                     @Override
                     public void handleResult(final Filter ldapFilter) {
                         /*
-                         * Avoid performing a search if the filter could not be
-                         * mapped or if it will never match.
+                         * Avoid performing a search if the filter could not be mapped or if it will never match.
                          */
                         if (ldapFilter == null || ldapFilter == alwaysFalse()) {
                             h.handleResult(new QueryResult());
                         } else {
                             // Perform the search.
                             final String[] attributes = getLDAPAttributes(c, request.getFields());
+                            final Filter searchFilter =
+                                    ldapFilter == Filter.alwaysTrue() ? Filter.objectClassPresent() : ldapFilter;
                             final SearchRequest searchRequest =
-                                    newSearchRequest(getBaseDN(c), SearchScope.SINGLE_LEVEL,
-                                            ldapFilter == Filter.alwaysTrue() ? Filter
-                                                    .objectClassPresent() : ldapFilter, attributes);
+                                    newSearchRequest(getBaseDN(c), SearchScope.SINGLE_LEVEL, searchFilter, attributes);
 
                             /*
-                             * Add the page results control. We can support the
-                             * page offset by reading the next offset pages, or
-                             * offset x page size resources.
+                             * Add the page results control. We can support the page offset by
+                             * reading the next offset pages, or offset x page size resources.
                              */
                             final int pageResultStartIndex;
                             final int pageSize = request.getPageSize();
@@ -374,35 +388,18 @@
                                                 .valueOfBase64(request.getPagedResultsCookie())
                                                 : ByteString.empty();
                                 final SimplePagedResultsControl control =
-                                        SimplePagedResultsControl.newControl(true,
-                                                pageResultEndIndex, cookie);
+                                        SimplePagedResultsControl.newControl(true, pageResultEndIndex, cookie);
                                 searchRequest.addControl(control);
                             } else {
                                 pageResultStartIndex = 0;
                             }
 
-                            c.getConnection().searchAsync(searchRequest, null, new SearchResultHandler() {
-                                /*
-                                 * The following fields are guarded by
-                                 * sequenceLock. In addition, the sequenceLock
-                                 * ensures that we send one JSON resource at a
-                                 * time back to the client.
-                                 */
-                                private final Object sequenceLock = new Object();
-                                private int pendingResourceCount = 0;
-                                private ResourceException pendingResult = null;
-                                private boolean resultSent = false;
-                                private int totalResourceCount = 0;
-                                private String cookie = null;
-
+                            c.getConnection().searchAsync(searchRequest, new SearchResultHandler() {
                                 @Override
                                 public boolean handleEntry(final SearchResultEntry entry) {
                                     /*
-                                     * Search result entries will be returned
-                                     * before the search result/error so the
-                                     * only reason pendingResult will be
-                                     * non-null is if a mapping error has
-                                     * occurred.
+                                     * Search result entries will be returned before the search result/error so the
+                                     * only reason pendingResult will be non-null is if a mapping error has occurred.
                                      */
                                     synchronized (sequenceLock) {
                                         if (pendingResult != null) {
@@ -416,75 +413,59 @@
                                     }
 
                                     /*
-                                     * FIXME: secondary asynchronous searches
-                                     * will complete in a non-deterministic
-                                     * order and may cause the JSON resources to
-                                     * be returned in a different order to the
-                                     * order in which the primary LDAP search
-                                     * results were received. This is benign at
-                                     * the moment, but will need resolving when
-                                     * we implement server side sorting. A
-                                     * possible fix will be to use a queue of
-                                     * pending resources (futures?). However,
-                                     * the queue cannot be unbounded in case it
-                                     * grows very large, but it cannot be
-                                     * bounded either since that could cause a
-                                     * deadlock between rest2ldap and the LDAP
-                                     * server (imagine the case where the server
-                                     * has a single worker thread which is
+                                     * FIXME: secondary asynchronous searches will complete in a non-deterministic
+                                     * order and may cause the JSON resources to be returned in a different order to the
+                                     * order in which the primary LDAP search results were received. This is benign at
+                                     * the moment, but will need resolving when we implement server side sorting. A
+                                     * possible fix will be to use a queue of pending resources (futures?). However,
+                                     * the queue cannot be unbounded in case it grows very large, but it cannot be
+                                     * bounded either since that could cause a deadlock between rest2ldap and the LDAP
+                                     * server (imagine the case where the server has a single worker thread which is
                                      * occupied processing the primary search).
-                                     * The best solution is probably to process
-                                     * the primary search results in batches
+                                     * The best solution is probably to process the primary search results in batches
                                      * using the paged results control.
                                      */
                                     final String id = nameStrategy.getResourceId(c, entry);
                                     final String revision = getRevisionFromEntry(entry);
-                                    attributeMapper.read(c, new JsonPointer(), entry,
-                                            new ResultHandler<JsonValue>() {
-                                                @Override
-                                                public void handleError(final ResourceException e) {
-                                                    synchronized (sequenceLock) {
-                                                        pendingResourceCount--;
-                                                        completeIfNecessary(e);
-                                                    }
-                                                }
+                                    attributeMapper.read(c, new JsonPointer(), entry, new ResultHandler<JsonValue>() {
+                                        @Override
+                                        public void handleError(final ResourceException e) {
+                                            synchronized (sequenceLock) {
+                                                pendingResourceCount--;
+                                                completeIfNecessary(e);
+                                            }
+                                        }
 
-                                                @Override
-                                                public void handleResult(final JsonValue result) {
-                                                    synchronized (sequenceLock) {
-                                                        pendingResourceCount--;
-                                                        if (!resultSent) {
-                                                            h.handleResource(new Resource(id,
-                                                                    revision, result));
-                                                        }
-                                                        completeIfNecessary();
-                                                    }
+                                        @Override
+                                        public void handleResult(final JsonValue result) {
+                                            synchronized (sequenceLock) {
+                                                pendingResourceCount--;
+                                                if (!resultSent) {
+                                                    h.handleResource(new Resource(id, revision, result));
                                                 }
-                                            });
+                                                completeIfNecessary();
+                                            }
+                                        }
+                                    });
                                     return true;
                                 }
 
                                 @Override
-                                public void handleErrorResult(final ErrorResultException error) {
-                                    synchronized (sequenceLock) {
-                                        completeIfNecessary(asResourceException(error));
-                                    }
-                                }
-
-                                @Override
                                 public boolean handleReference(final SearchResultReference reference) {
                                     // TODO: should this be classed as an error since rest2ldap
                                     // assumes entries are all colocated?
                                     return true;
                                 }
 
+                            }).onSuccess(new SuccessHandler<Result>() {
                                 @Override
-                                public void handleResult(final Result result) {
+                                public void handleResult(Result result) {
                                     synchronized (sequenceLock) {
                                         if (request.getPageSize() > 0) {
                                             try {
-                                                final SimplePagedResultsControl control = result.getControl(
-                                                    SimplePagedResultsControl.DECODER, DECODE_OPTIONS);
+                                                final SimplePagedResultsControl control =
+                                                    result.getControl(SimplePagedResultsControl.DECODER,
+                                                        DECODE_OPTIONS);
                                                 if (control != null && !control.getCookie().isEmpty()) {
                                                     cookie = control.getCookie().toBase64String();
                                                 }
@@ -495,46 +476,50 @@
                                         completeIfNecessary(SUCCESS);
                                     }
                                 }
-
-                                /*
-                                 * This method must be invoked with the
-                                 * sequenceLock held.
-                                 */
-                                private void completeIfNecessary(final ResourceException e) {
-                                    if (pendingResult == null) {
-                                        pendingResult = e;
-                                    }
-                                    completeIfNecessary();
-                                }
-
-                                /*
-                                 * Close out the query result set if there are
-                                 * no more pending resources and the LDAP result
-                                 * has been received. This method must be
-                                 * invoked with the sequenceLock held.
-                                 */
-                                private void completeIfNecessary() {
-                                    if (pendingResourceCount == 0 && pendingResult != null
-                                            && !resultSent) {
-                                        if (pendingResult == SUCCESS) {
-                                            h.handleResult(new QueryResult(cookie, -1));
-                                        } else {
-                                            h.handleError(pendingResult);
-                                        }
-                                        resultSent = true;
+                            }).onFailure(new FailureHandler<ErrorResultException>() {
+                                @Override
+                                public void handleError(ErrorResultException error) {
+                                    synchronized (sequenceLock) {
+                                        completeIfNecessary(asResourceException(error));
                                     }
                                 }
                             });
                         }
                     }
+
+                    /**
+                     * This method must be invoked with the sequenceLock held.
+                     */
+                    private void completeIfNecessary(final ResourceException e) {
+                        if (pendingResult == null) {
+                            pendingResult = e;
+                        }
+                        completeIfNecessary();
+                    }
+
+                    /**
+                     * Close out the query result set if there are no more
+                     * pending resources and the LDAP result has been received.
+                     * This method must be invoked with the sequenceLock held.
+                     */
+                    private void completeIfNecessary() {
+                        if (pendingResourceCount == 0 && pendingResult != null && !resultSent) {
+                            if (pendingResult == SUCCESS) {
+                                h.handleResult(new QueryResult(cookie, -1));
+                            } else {
+                                h.handleError(pendingResult);
+                            }
+                            resultSent = true;
+                        }
+                    }
                 });
             }
         });
     }
 
     @Override
-    public void readInstance(final ServerContext context, final String resourceId,
-            final ReadRequest request, final ResultHandler<Resource> handler) {
+    public void readInstance(final ServerContext context, final String resourceId, final ReadRequest request,
+        final ResultHandler<Resource> handler) {
         final Context c = wrap(context);
         final ResultHandler<Resource> h = wrap(c, handler);
 
@@ -545,27 +530,27 @@
                 // Do the search.
                 final String[] attributes = getLDAPAttributes(c, request.getFields());
                 final SearchRequest request =
-                        nameStrategy.createSearchRequest(c, getBaseDN(c), resourceId).addAttribute(
-                                attributes);
-                c.getConnection().searchSingleEntryAsync(request,
-                        new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
-                            @Override
-                            public void handleErrorResult(final ErrorResultException error) {
-                                h.handleError(asResourceException(error));
-                            }
+                    nameStrategy.createSearchRequest(c, getBaseDN(c), resourceId).addAttribute(attributes);
 
-                            @Override
-                            public void handleResult(final SearchResultEntry entry) {
-                                adaptEntry(c, entry, h);
-                            }
-                        });
-            }
+                c.getConnection().searchSingleEntryAsync(request).onSuccess(new SuccessHandler<SearchResultEntry>() {
+                    @Override
+                    public void handleResult(final SearchResultEntry entry) {
+                        adaptEntry(c, entry, h);
+                    }
+                }).onFailure(new FailureHandler<ErrorResultException>() {
+                    @Override
+                    public void handleError(final ErrorResultException error) {
+                        h.handleError(asResourceException(error));
+                    }
+                });
+
+            };
         });
     }
 
     @Override
-    public void updateInstance(final ServerContext context, final String resourceId,
-            final UpdateRequest request, final ResultHandler<Resource> handler) {
+    public void updateInstance(final ServerContext context, final String resourceId, final UpdateRequest request,
+            final ResultHandler<Resource> handler) {
         /*
          * Update operations are a bit awkward because there is no direct
          * mapping to LDAP. We need to convert the update request into an LDAP
@@ -582,70 +567,53 @@
         c.run(h, new Runnable() {
             @Override
             public void run() {
-                final String[] attributes =
-                        getLDAPAttributes(c, Collections.<JsonPointer> emptyList());
-                final SearchRequest searchRequest =
-                        nameStrategy.createSearchRequest(c, getBaseDN(c), resourceId).addAttribute(
-                                attributes);
-                c.getConnection().searchSingleEntryAsync(searchRequest,
-                        new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
-                            @Override
-                            public void handleErrorResult(final ErrorResultException error) {
-                                h.handleError(asResourceException(error));
-                            }
+                final String[] attributes = getLDAPAttributes(c, Collections.<JsonPointer> emptyList());
+                final SearchRequest searchRequest = nameStrategy.createSearchRequest(c, getBaseDN(c), resourceId)
+                        .addAttribute(attributes);
 
+                c.getConnection().searchSingleEntryAsync(searchRequest)
+                        .onSuccess(new SuccessHandler<SearchResultEntry>() {
                             @Override
                             public void handleResult(final SearchResultEntry entry) {
                                 try {
                                     // Fail-fast if there is a version mismatch.
                                     ensureMVCCVersionMatches(entry, request.getRevision());
 
-                                    //  Create the modify request.
-                                    final ModifyRequest modifyRequest =
-                                            newModifyRequest(entry.getName());
+                                    // Create the modify request.
+                                    final ModifyRequest modifyRequest = newModifyRequest(entry.getName());
                                     if (config.readOnUpdatePolicy() == CONTROLS) {
-                                        final String[] attributes =
-                                                getLDAPAttributes(c, request.getFields());
-                                        modifyRequest.addControl(PostReadRequestControl.newControl(
-                                                false, attributes));
+                                        final String[] attributes = getLDAPAttributes(c, request.getFields());
+                                        modifyRequest.addControl(PostReadRequestControl.newControl(false, attributes));
                                     }
                                     if (config.usePermissiveModify()) {
-                                        modifyRequest.addControl(PermissiveModifyRequestControl
-                                                .newControl(true));
+                                        modifyRequest.addControl(PermissiveModifyRequestControl.newControl(true));
                                     }
                                     addAssertionControl(modifyRequest, request.getRevision());
 
                                     /*
-                                     * Determine the set of changes that need to
-                                     * be performed.
+                                     * Determine the set of changes that need to be performed.
                                      */
-                                    attributeMapper.update(c, new JsonPointer(), entry, request
-                                            .getNewContent(),
+                                    attributeMapper.update(c, new JsonPointer(), entry, request.getNewContent(),
                                             new ResultHandler<List<Modification>>() {
                                                 @Override
-                                                public void handleError(
-                                                        final ResourceException error) {
+                                                public void handleError(final ResourceException error) {
                                                     h.handleError(error);
                                                 }
 
                                                 @Override
-                                                public void handleResult(
-                                                        final List<Modification> result) {
+                                                public void handleResult(final List<Modification> result) {
                                                     // Perform the modify operation.
                                                     if (result.isEmpty()) {
                                                         /*
-                                                         * No changes to be
-                                                         * performed, so just
-                                                         * return the entry that
-                                                         * we read.
+                                                         * No changes to be performed, so just return
+                                                         * the entry that we read.
                                                          */
                                                         adaptEntry(c, entry, h);
                                                     } else {
-                                                        modifyRequest.getModifications().addAll(
-                                                                result);
-                                                        c.getConnection().applyChangeAsync(
-                                                                modifyRequest, null,
-                                                                postUpdateHandler(c, h));
+                                                        modifyRequest.getModifications().addAll(result);
+                                                        c.getConnection().applyChangeAsync(modifyRequest)
+                                                                .onSuccess(postUpdateSuccessHandler(c, h))
+                                                                .onFailure(postUpdateFailureHandler(h));
                                                     }
                                                 }
                                             });
@@ -653,13 +621,17 @@
                                     h.handleError(asResourceException(e));
                                 }
                             }
+                        }).onFailure(new FailureHandler<ErrorResultException>() {
+                            @Override
+                            public void handleError(final ErrorResultException error) {
+                                h.handleError(asResourceException(error));
+                            }
                         });
             }
         });
     }
 
-    private void adaptEntry(final Context c, final Entry entry,
-            final ResultHandler<Resource> handler) {
+    private void adaptEntry(final Context c, final Entry entry, final ResultHandler<Resource> handler) {
         final String actualResourceId = nameStrategy.getResourceId(c, entry);
         final String revision = getRevisionFromEntry(entry);
         attributeMapper.read(c, new JsonPointer(), entry, transform(
@@ -695,13 +667,8 @@
                     // There's no point in doing a search because we already know the DN.
                     updateHandler.handleResult(searchRequest.getName());
                 } else {
-                    c.getConnection().searchSingleEntryAsync(searchRequest,
-                            new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
-                                @Override
-                                public void handleErrorResult(final ErrorResultException error) {
-                                    updateHandler.handleError(asResourceException(error));
-                                }
-
+                    c.getConnection().searchSingleEntryAsync(searchRequest)
+                            .onSuccess(new SuccessHandler<SearchResultEntry>() {
                                 @Override
                                 public void handleResult(final SearchResultEntry entry) {
                                     try {
@@ -714,6 +681,11 @@
                                         updateHandler.handleError(asResourceException(e));
                                     }
                                 }
+                            }).onFailure(new FailureHandler<ErrorResultException>() {
+                                @Override
+                                public void handleError(final ErrorResultException error) {
+                                    updateHandler.handleError(asResourceException(error));
+                                }
                             });
                 }
             }
@@ -727,21 +699,18 @@
         }
     }
 
-    private void ensureMVCCVersionMatches(final Entry entry, final String expectedRevision)
-            throws ResourceException {
+    private void ensureMVCCVersionMatches(final Entry entry, final String expectedRevision) throws ResourceException {
         if (expectedRevision != null) {
             ensureMVCCSupported();
             final String actualRevision = entry.parseAttribute(etagAttribute).asString();
             if (actualRevision == null) {
                 throw new PreconditionFailedException(i18n(
                         "The resource could not be accessed because it did not contain any "
-                                + "version information, when the version '%s' was expected",
-                        expectedRevision));
+                                + "version information, when the version '%s' was expected", expectedRevision));
             } else if (!expectedRevision.equals(actualRevision)) {
                 throw new PreconditionFailedException(i18n(
                         "The resource could not be accessed because the expected version '%s' "
-                                + "does not match the current version '%s'", expectedRevision,
-                        actualRevision));
+                                + "does not match the current version '%s'", expectedRevision, actualRevision));
             }
         }
     }
@@ -785,42 +754,55 @@
         return requestedLDAPAttributes.toArray(new String[requestedLDAPAttributes.size()]);
     }
 
-    private void getLDAPFilter(final Context c, final QueryFilter queryFilter,
-            final ResultHandler<Filter> h) {
+    private void getLDAPFilter(final Context c, final QueryFilter queryFilter, final ResultHandler<Filter> h) {
         final QueryFilterVisitor<Void, ResultHandler<Filter>> visitor =
                 new QueryFilterVisitor<Void, ResultHandler<Filter>>() {
                     @Override
-                    public Void visitAndFilter(final ResultHandler<Filter> p,
-                            final List<QueryFilter> subFilters) {
-                        final ResultHandler<Filter> handler =
-                                accumulate(subFilters.size(), transform(
-                                        new Function<List<Filter>, Filter, Void>() {
-                                            @Override
-                                            public Filter apply(final List<Filter> value,
-                                                    final Void p) {
-                                                // Check for unmapped filter components and optimize.
-                                                final Iterator<Filter> i = value.iterator();
-                                                while (i.hasNext()) {
-                                                    final Filter f = i.next();
-                                                    if (f == alwaysFalse()) {
-                                                        return alwaysFalse();
-                                                    } else if (f == alwaysTrue()) {
-                                                        i.remove();
-                                                    }
-                                                }
-                                                switch (value.size()) {
-                                                case 0:
-                                                    return alwaysTrue();
-                                                case 1:
-                                                    return value.get(0);
-                                                default:
-                                                    return Filter.and(value);
-                                                }
-                                            }
-                                        }, p));
+                    public Void visitAndFilter(final ResultHandler<Filter> p, final List<QueryFilter> subFilters) {
+                        List<Promise<Filter, ResourceException>> promises =
+                                new ArrayList<Promise<Filter, ResourceException>>(subFilters.size());
                         for (final QueryFilter subFilter : subFilters) {
+                            final ResultHandlerFromPromise<Filter> handler = new ResultHandlerFromPromise<Filter>();
                             subFilter.accept(this, handler);
+                            promises.add(handler.promise);
                         }
+
+                        Promises.when(promises)
+                                .then(new org.forgerock.util.promise.Function<List<Filter>, Filter,
+                                        ResourceException>() {
+                                    @Override
+                                    public Filter apply(final List<Filter> value) {
+                                        // Check for unmapped filter components and optimize.
+                                        final Iterator<Filter> i = value.iterator();
+                                        while (i.hasNext()) {
+                                            final Filter f = i.next();
+                                            if (f == alwaysFalse()) {
+                                                return alwaysFalse();
+                                            } else if (f == alwaysTrue()) {
+                                                i.remove();
+                                            }
+                                        }
+                                        switch (value.size()) {
+                                        case 0:
+                                            return alwaysTrue();
+                                        case 1:
+                                            return value.get(0);
+                                        default:
+                                            return Filter.and(value);
+                                        }
+                                    }
+                                }).onSuccess(new SuccessHandler<Filter>() {
+                                    @Override
+                                    public void handleResult(Filter result) {
+                                        p.handleResult(result);
+                                    }
+                                }).onFailure(new FailureHandler<ResourceException>() {
+                                    @Override
+                                    public void handleError(ResourceException error) {
+                                        p.handleError(error);
+                                    }
+                                });
+
                         return null;
                     }
 
@@ -907,37 +889,51 @@
                     }
 
                     @Override
-                    public Void visitOrFilter(final ResultHandler<Filter> p,
-                            final List<QueryFilter> subFilters) {
-                        final ResultHandler<Filter> handler =
-                                accumulate(subFilters.size(), transform(
-                                        new Function<List<Filter>, Filter, Void>() {
-                                            @Override
-                                            public Filter apply(final List<Filter> value,
-                                                    final Void p) {
-                                                // Check for unmapped filter components and optimize.
-                                                final Iterator<Filter> i = value.iterator();
-                                                while (i.hasNext()) {
-                                                    final Filter f = i.next();
-                                                    if (f == alwaysFalse()) {
-                                                        i.remove();
-                                                    } else if (f == alwaysTrue()) {
-                                                        return alwaysTrue();
-                                                    }
-                                                }
-                                                switch (value.size()) {
-                                                case 0:
-                                                    return alwaysFalse();
-                                                case 1:
-                                                    return value.get(0);
-                                                default:
-                                                    return Filter.or(value);
-                                                }
-                                            }
-                                        }, p));
+                    public Void visitOrFilter(final ResultHandler<Filter> p, final List<QueryFilter> subFilters) {
+                        List<Promise<Filter, ResourceException>> promises =
+                                new ArrayList<Promise<Filter, ResourceException>>(subFilters.size());
                         for (final QueryFilter subFilter : subFilters) {
+                            final ResultHandlerFromPromise<Filter> handler = new ResultHandlerFromPromise<Filter>();
                             subFilter.accept(this, handler);
+                            promises.add(handler.promise);
                         }
+
+                        Promises.when(promises)
+                                .then(new org.forgerock.util.promise.Function<List<Filter>, Filter,
+                                        ResourceException>() {
+                                    @Override
+                                    public Filter apply(final List<Filter> value) {
+                                        // Check for unmapped filter components and optimize.
+                                        final Iterator<Filter> i = value.iterator();
+                                        while (i.hasNext()) {
+                                            final Filter f = i.next();
+                                            if (f == alwaysFalse()) {
+                                                i.remove();
+                                            } else if (f == alwaysTrue()) {
+                                                return alwaysTrue();
+                                            }
+                                        }
+                                        switch (value.size()) {
+                                        case 0:
+                                            return alwaysFalse();
+                                        case 1:
+                                            return value.get(0);
+                                        default:
+                                            return Filter.or(value);
+                                        }
+                                    }
+                                }).onSuccess(new SuccessHandler<Filter>() {
+                                    @Override
+                                    public void handleResult(Filter result) {
+                                        p.handleResult(result);
+                                    }
+                                }).onFailure(new FailureHandler<ResourceException>() {
+                                    @Override
+                                    public void handleError(ResourceException error) {
+                                        p.handleError(error);
+                                    }
+                                });
+
                         return null;
                     }
 
@@ -959,8 +955,7 @@
 
                 };
         /*
-         * Note that the returned LDAP filter may be null if it could not be
-         * mapped by any attribute mappers.
+         * Note that the returned LDAP filter may be null if it could not be mapped by any attribute mappers.
          */
         queryFilter.accept(visitor, h);
     }
@@ -969,14 +964,9 @@
         return etagAttribute != null ? entry.parseAttribute(etagAttribute).asString() : null;
     }
 
-    private org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry> postEmptyPatchHandler(
-            final Context c, final PatchRequest request, final ResultHandler<Resource> h) {
-        return new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
-            @Override
-            public void handleErrorResult(final ErrorResultException error) {
-                h.handleError(asResourceException(error));
-            }
-
+    private SuccessHandler<SearchResultEntry> postEmptyPatchSuccessHandler(final Context c,
+            final PatchRequest request, final ResultHandler<Resource> h) {
+        return new SuccessHandler<SearchResultEntry>() {
             @Override
             public void handleResult(final SearchResultEntry entry) {
                 try {
@@ -990,29 +980,30 @@
         };
     }
 
-    private org.forgerock.opendj.ldap.ResultHandler<Result> postUpdateHandler(final Context c,
-            final ResultHandler<Resource> handler) {
-        // The handler which will be invoked for the LDAP add result.
-        return new org.forgerock.opendj.ldap.ResultHandler<Result>() {
+    private FailureHandler<ErrorResultException> postEmptyPatchFailureHandler(final ResultHandler<Resource> h) {
+        return new FailureHandler<ErrorResultException>() {
             @Override
-            public void handleErrorResult(final ErrorResultException error) {
-                handler.handleError(asResourceException(error));
+            public void handleError(final ErrorResultException error) {
+                h.handleError(asResourceException(error));
             }
+        };
+    }
 
+    private SuccessHandler<Result> postUpdateSuccessHandler(final Context c, final ResultHandler<Resource> handler) {
+        // The handler which will be invoked for the LDAP add result.
+        return new SuccessHandler<Result>() {
             @Override
             public void handleResult(final Result result) {
                 // FIXME: handle USE_SEARCH policy.
                 Entry entry;
                 try {
                     final PostReadResponseControl postReadControl =
-                            result.getControl(PostReadResponseControl.DECODER, config
-                                    .decodeOptions());
+                        result.getControl(PostReadResponseControl.DECODER, config.decodeOptions());
                     if (postReadControl != null) {
                         entry = postReadControl.getEntry();
                     } else {
                         final PreReadResponseControl preReadControl =
-                                result.getControl(PreReadResponseControl.DECODER, config
-                                        .decodeOptions());
+                            result.getControl(PreReadResponseControl.DECODER, config.decodeOptions());
                         if (preReadControl != null) {
                             entry = preReadControl.getEntry();
                         } else {
@@ -1026,8 +1017,7 @@
                 if (entry != null) {
                     adaptEntry(c, entry, handler);
                 } else {
-                    final Resource resource =
-                            new Resource(null, null, new JsonValue(Collections.emptyMap()));
+                    final Resource resource = new Resource(null, null, new JsonValue(Collections.emptyMap()));
                     handler.handleResult(resource);
                 }
             }
@@ -1035,6 +1025,16 @@
         };
     }
 
+    private FailureHandler<ErrorResultException> postUpdateFailureHandler(final ResultHandler<Resource> handler) {
+        // The handler which will be invoked for the LDAP add result.
+        return new FailureHandler<ErrorResultException>() {
+            @Override
+            public void handleError(final ErrorResultException error) {
+                handler.handleError(asResourceException(error));
+            }
+        };
+    }
+
     private QueryResultHandler wrap(final Context c, final QueryResultHandler handler) {
         return new QueryResultHandler() {
             @Override

--
Gitblit v1.10.0