From 5100085273f41f4cdb2d839e2d95b43503ba4f1d Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Fri, 05 Jul 2013 21:44:21 +0000
Subject: [PATCH] Backport fix for OPENDJ-1044: Doing a PUT with JSON data identical to what is on the server results in 400 status code

---
 opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java |  199 +++++++++++++++++++++++++++++++++----------------
 1 files changed, 133 insertions(+), 66 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 ad470ef..80f0ef2 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
@@ -15,6 +15,7 @@
  */
 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;
@@ -211,69 +212,103 @@
         final Context c = wrap(context);
         final ResultHandler<Resource> h = wrap(c, handler);
 
-        /*
-         * Get the connection, search if needed, then determine modifications,
-         * then perform modify.
-         */
-        c.run(h, doUpdate(c, resourceId, request.getRevision(), new ResultHandler<DN>() {
-            @Override
-            public void handleError(final ResourceException error) {
-                h.handleError(error);
-            }
-
-            @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);
-                                            if (config.readOnUpdatePolicy() == CONTROLS) {
-                                                final String[] attributes =
-                                                        getLDAPAttributes(c, request.getFields());
-                                                modifyRequest.addControl(PostReadRequestControl
-                                                        .newControl(false, attributes));
-                                            }
-                                            if (config.usePermissiveModify()) {
-                                                modifyRequest
-                                                        .addControl(PermissiveModifyRequestControl
-                                                                .newControl(true));
-                                            }
-                                            addAssertionControl(modifyRequest, request
-                                                    .getRevision());
-
-                                            // Add the modifications.
-                                            for (final List<Modification> modifications : result) {
-                                                if (modifications != null) {
-                                                    modifyRequest.getModifications().addAll(
-                                                            modifications);
-                                                }
-                                            }
-
-                                            // Perform the modify request.
-                                            c.getConnection().applyChangeAsync(modifyRequest, null,
-                                                    postUpdateHandler(c, h));
-                                        } catch (final Exception e) {
-                                            h.handleError(asResourceException(e));
-                                        }
-                                    }
-                                });
-
-                for (final PatchOperation operation : request.getPatchOperations()) {
-                    attributeMapper.patch(c, new JsonPointer(), operation, handler);
+        if (request.getPatchOperations().isEmpty()) {
+            /*
+             * 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));
                 }
-            }
-        }));
+            });
+        } else {
+            /*
+             * Get the connection, search if needed, then determine
+             * modifications, then perform modify.
+             */
+            c.run(h, doUpdate(c, resourceId, request.getRevision(), new ResultHandler<DN>() {
+                @Override
+                public void handleError(final ResourceException error) {
+                    h.handleError(error);
+                }
+
+                @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));
+                                            }
+                                        }
+                                    });
+
+                    for (final PatchOperation operation : request.getPatchOperations()) {
+                        attributeMapper.patch(c, new JsonPointer(), operation, handler);
+                    }
+                }
+            }));
+        }
     }
 
     @Override
@@ -545,10 +580,21 @@
                                                 public void handleResult(
                                                         final List<Modification> result) {
                                                     // Perform the modify operation.
-                                                    modifyRequest.getModifications().addAll(result);
-                                                    c.getConnection().applyChangeAsync(
-                                                            modifyRequest, null,
-                                                            postUpdateHandler(c, h));
+                                                    if (result.isEmpty()) {
+                                                        /*
+                                                         * 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));
+                                                    }
                                                 }
                                             });
                                 } catch (final Exception e) {
@@ -871,6 +917,27 @@
         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));
+            }
+
+            @Override
+            public void handleResult(final SearchResultEntry entry) {
+                try {
+                    // Fail if there is a version mismatch.
+                    ensureMVCCVersionMatches(entry, request.getRevision());
+                    adaptEntry(c, entry, h);
+                } catch (final Exception e) {
+                    h.handleError(asResourceException(e));
+                }
+            }
+        };
+    }
+
     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.

--
Gitblit v1.10.0