From 7c6dff1723be6ac0e0faeff0b9bc9747a18b98b5 Mon Sep 17 00:00:00 2001
From: pgamba <pgamba@localhost>
Date: Wed, 17 Jan 2007 11:01:32 +0000
Subject: [PATCH] Fix Issue 783 synchronization does not detect that the parent is renamed during add operations

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java |  100 +++++++++++++++++++++++++++++++++
 opends/src/server/org/opends/server/synchronization/plugin/SynchronizationDomain.java                |   26 ++++++++
 2 files changed, 124 insertions(+), 2 deletions(-)

diff --git a/opends/src/server/org/opends/server/synchronization/plugin/SynchronizationDomain.java b/opends/src/server/org/opends/server/synchronization/plugin/SynchronizationDomain.java
index 825ab82..cfcde54 100644
--- a/opends/src/server/org/opends/server/synchronization/plugin/SynchronizationDomain.java
+++ b/opends/src/server/org/opends/server/synchronization/plugin/SynchronizationDomain.java
@@ -532,6 +532,30 @@
         addOperation.setResultCode(ResultCode.SUCCESS);
         return new SynchronizationProviderResult(false);
       }
+
+      /* The parent entry may have been renamed here since the change was done
+       * on the first server, and another entry have taken the former dn
+       * of the parent entry
+       */
+
+      // There is a potential of perfs improvement here
+      // if we could avoid the following parent entry retrieval
+      DN parentDnFromCtx = findEntryDN(ctx.getParentUid());
+
+      if (parentDnFromCtx != null)
+      {
+        DN entryDN = addOperation.getEntryDN();
+        DN parentDnFromEntryDn = entryDN.getParentDNInSuffix();
+        if ((parentDnFromEntryDn != null)
+            && (!parentDnFromCtx.equals(parentDnFromEntryDn)))
+        {
+          // parentEntry has been renamed
+          // Synchronization name conflict resolution is expected to fix that
+          // later in the flow
+          addOperation.setResultCode(ResultCode.NO_SUCH_OBJECT);
+          return new SynchronizationProviderResult(false);
+        }
+      }
     }
     return new SynchronizationProviderResult(true);
   }
@@ -1397,7 +1421,7 @@
    * Solve a conflict detected when replaying a ADD operation.
    *
    * @param op The operation that triggered the conflict detection.
-   * @param msg The operation that triggered the conflict detection.
+   * @param msg The message that triggered the conflict detection.
    * @return true if the process is completed, false if it must continue.
    * @throws Exception When the operation is not valid.
    */
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java
index dffa201..f76d76c 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java
@@ -31,8 +31,11 @@
 import static org.testng.Assert.*;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.UUID;
 import java.util.concurrent.locks.Lock;
 
 import org.opends.server.TestCaseUtils;
@@ -41,6 +44,7 @@
 import org.opends.server.schema.IntegerSyntax;
 import org.opends.server.synchronization.common.ChangeNumberGenerator;
 import org.opends.server.synchronization.plugin.ChangelogBroker;
+import org.opends.server.synchronization.plugin.Historical;
 import org.opends.server.synchronization.protocol.AddMsg;
 import org.opends.server.synchronization.protocol.DeleteMsg;
 import org.opends.server.synchronization.protocol.ModifyDNMsg;
@@ -52,6 +56,7 @@
 import org.opends.server.core.ModifyDNOperation;
 import org.opends.server.core.ModifyOperation;
 import org.opends.server.core.Operation;
+import org.opends.server.protocols.asn1.ASN1OctetString;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.protocols.internal.InternalSearchOperation;
 import org.opends.server.protocols.ldap.LDAPFilter;
@@ -539,6 +544,99 @@
     // check that the delete operation has been applied
     assertNull(resultEntry,
         "The DELETE synchronization message was not replayed");
+
+    /*
+     * When replaying add operations it is possible that the parent entry has
+     * been renamed before and that another entry have taken the former dn of
+     * the parent entry. In such case the synchronization replay code should 
+     * detect that the parent has been renamed and should add the entry below
+     * the new dn of the parent (thus changing the original dn with which the 
+     * entry had been created)
+     *
+     * Steps
+     * - create parent entry 1 with baseDn1
+     * - create Add Msg for user1 with parent entry 1 UUID
+     * - MODDN parent entry 1 to baseDn2 in the LDAP server
+     * - add new parent entry 2 with baseDn1
+     * - publish msg
+     * - check that the Dn has been changed to baseDn2 in the msg received 
+     */
+
+    // - create parent entry 1 with baseDn1
+    String[] topEntries = new String[1];
+    topEntries[0] = "dn: ou=baseDn1,"+baseDn+"\n" + "objectClass: top\n"
+    + "objectClass: organizationalUnit\n"
+    + "entryUUID: 55555555-5555-5555-5555-555555555555\n";
+    Entry entry;
+    for (int i = 0; i < topEntries.length; i++)
+    {
+      entry = TestCaseUtils.entryFromLdifString(topEntries[i]);
+      AddOperation addOp = new AddOperation(connection,
+          InternalClientConnection.nextOperationID(), InternalClientConnection
+          .nextMessageID(), null, entry.getDN(), entry.getObjectClasses(),
+          entry.getUserAttributes(), entry.getOperationalAttributes());
+      addOp.setInternalOperation(true);
+      addOp.run();
+      entryList.add(entry.getDN());
+    }
+    resultEntry = getEntry(
+        DN.decode("ou=baseDn1,"+baseDn), 10000, true);
+    assertNotNull(resultEntry,
+        "Entry not added: ou=baseDn1,"+baseDn);
+
+    // - create Add Msg for user1 with parent entry 1 UUID
+    addMsg = new AddMsg(gen.NewChangeNumber(),
+        "uid=new person,ou=baseDn1,"+baseDn,
+        user1entryUUID,
+        getEntryUUID(DN.decode("ou=baseDn1,"+baseDn)),
+        personWithUUIDEntry.getObjectClassAttribute(),
+        personWithUUIDEntry.getAttributes(), new ArrayList<Attribute>());
+
+    // - MODDN parent entry 1 to baseDn2 in the LDAP server
+    ModifyDNOperation modDNOp = new ModifyDNOperation(connection,
+        InternalClientConnection.nextOperationID(), InternalClientConnection
+        .nextMessageID(), null, 
+        DN.decode("ou=baseDn1,"+baseDn),
+        RDN.decode("ou=baseDn2"), true, 
+        baseDn);
+    modDNOp.run();
+    entryList.add(DN.decode("ou=baseDn2,"+baseDn));
+
+    resultEntry = getEntry(
+        DN.decode("ou=baseDn2,"+baseDn), 10000, true);
+    assertNotNull(resultEntry,
+        "Entry not moved from ou=baseDn1,"+baseDn+" to ou=baseDn2,"+baseDn);
+
+    // - add new parent entry 2 with baseDn1
+    String p2 = new String("dn: ou=baseDn1,"+baseDn+"\n" + "objectClass: top\n"
+        + "objectClass: organizationalUnit\n"
+        + "entryUUID: 66666666-6666-6666-6666-666666666666\n");
+    entry = TestCaseUtils.entryFromLdifString(p2);
+    AddOperation addOp = new AddOperation(connection,
+        InternalClientConnection.nextOperationID(), InternalClientConnection
+        .nextMessageID(), null, entry.getDN(), entry.getObjectClasses(),
+        entry.getUserAttributes(), entry.getOperationalAttributes());
+    addOp.setInternalOperation(true);
+    addOp.run();
+    entryList.add(entry.getDN());
+
+
+    // - publish msg
+    broker.publish(addMsg);
+
+    // - check that the Dn has been changed to baseDn2
+    resultEntry = getEntry(
+        DN.decode("uid=new person,ou=baseDn1,"+baseDn), 10000, true);
+    assertNull(resultEntry,
+        "The ADD synchronization message was applied under ou=baseDn1,"+baseDn);
+
+    resultEntry = getEntry(
+        DN.decode("uid=new person,ou=baseDn2,"+baseDn), 10000, true);
+    assertNotNull(resultEntry,
+        "The ADD synchronization message was NOT applied under ou=baseDn2,"+baseDn);
+    entryList.add(resultEntry.getDN());
+
+
     broker.stop();
   }
 
@@ -690,7 +788,7 @@
        */
       Entry resultEntry = getEntry(personWithUUIDEntry.getDN(), 10000, true);
       assertNotNull(resultEntry,
-      "The send ADD synchronization message was not applied");
+      "The send ADD synchronization message was not applied for "+personWithUUIDEntry.getDN().toString());
       entryList.add(resultEntry.getDN());
 
       /*

--
Gitblit v1.10.0