From 4b7bf9f0b51c9f487a92c3686825ec57d0ebf156 Mon Sep 17 00:00:00 2001
From: gbellato <gbellato@localhost>
Date: Mon, 19 Mar 2007 08:05:16 +0000
Subject: [PATCH] Fix for issue 1375 : Conflict between add and replace can be incorrectly resolved The problem happens because the historical information saved in the entry is badly read in the Historical.load() method

---
 opendj-sdk/opends/src/server/org/opends/server/synchronization/plugin/AttrInfo.java                                   |   21 ++++++++--
 opendj-sdk/opends/src/server/org/opends/server/synchronization/plugin/SynchronizationDomain.java                      |    3 +
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/plugin/ModifyConflictTest.java |   88 +++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 103 insertions(+), 9 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/synchronization/plugin/AttrInfo.java b/opendj-sdk/opends/src/server/org/opends/server/synchronization/plugin/AttrInfo.java
index a967b8d..33f6b32 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/synchronization/plugin/AttrInfo.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/synchronization/plugin/AttrInfo.java
@@ -27,6 +27,7 @@
 package org.opends.server.synchronization.plugin;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.LinkedHashSet;
 
 import org.opends.server.synchronization.common.ChangeNumber;
@@ -121,19 +122,31 @@
                                                             this.valuesInfo);
      return dup;
    }
+
    /**
-    * Delete all historical information for this attribute type.
-    * Replace it with delete attribute state information
+    * Delete all historical information that is older than
+    * the provided ChangeNumber for this attribute type.
+    * Add the delete attribute state information
     * @param CN time when the delete was done
     */
    void delete(ChangeNumber CN)
    {
-     if (this.valuesInfo != null)
-      this.valuesInfo.clear();
+     // iterate through the values in the valuesInfo
+     // and suppress all the values that have not been added
+     // after the date of this delete.
+     Iterator<ValueInfo> it = this.valuesInfo.iterator();
+     while (it.hasNext())
+     {
+       ValueInfo info = it.next();
+       if (CN.newerOrEquals(info.getValueUpdateTime()))
+         it.remove();
+     }
+
      if (CN.newer(deleteTime))
      {
        deleteTime = CN;
      }
+
      if (CN.newer(lastUpdateTime))
      {
        lastUpdateTime = CN;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/synchronization/plugin/SynchronizationDomain.java b/opendj-sdk/opends/src/server/org/opends/server/synchronization/plugin/SynchronizationDomain.java
index 74ddde4..7b2e078 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/synchronization/plugin/SynchronizationDomain.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/synchronization/plugin/SynchronizationDomain.java
@@ -740,6 +740,9 @@
     }
     else
     {
+      // This is a replayed operation, it is necessary to
+      // - check if the entry has been renamed
+      // - check for conflicts
       String modifiedEntryUUID = ctx.getEntryUid();
       String currentEntryUUID = Historical.getEntryUuid(modifiedEntry);
       if ((currentEntryUUID != null) &&
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/plugin/ModifyConflictTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/plugin/ModifyConflictTest.java
index 8e71dc3..9b8f009 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/plugin/ModifyConflictTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/plugin/ModifyConflictTest.java
@@ -38,6 +38,7 @@
 
 import static org.opends.server.synchronization.protocol.OperationContext.*;
 
+import org.opends.server.TestAccessLogger;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ModifyOperation;
@@ -113,14 +114,14 @@
      * simulate a modify-replace done at time t10
      */
     testModify(entry, hist, "description", ModificationType.REPLACE,
-        "init value", 10, true);
+               "init value", 10, true);
 
     /*
      * Now simulate an add at an earlier date that the previous replace
      * conflict resolution should remove it.
      */
     testModify(entry, hist, "description", ModificationType.ADD,
-        "older value", 1, false);
+               "older value", 1, false);
 
     /*
      * Now simulate an add at an earlier date that the previous replace
@@ -128,14 +129,81 @@
      * sure...)
      */
     testModify(entry, hist, "description", ModificationType.ADD,
-        "older value", 2, false);
+               "older value", 2, false);
 
     /*
      * Now simulate an add at a later date that the previous replace.
      * conflict resolution should keep it
      */
     testModify(entry, hist, "description", ModificationType.ADD, "new value",
-        11, true);
+               11, true);
+
+  }
+  
+  /**
+   * Test that conflict between modify-add and modify-replace for
+   * multi-valued attributes are handled correctly.
+   */
+  @Test()
+  public void addAndReplace() throws Exception
+  {
+    DN dn = DN.decode("dc=com");
+    Map<ObjectClass, String> objectClasses = new HashMap<ObjectClass, String>();
+    ObjectClass org = DirectoryServer.getObjectClass("organization");
+    objectClasses.put(org, "organization");
+
+    Entry entry = new Entry(dn, objectClasses, null, null);
+
+    // Construct a new random UUID. and add it into the entry
+    UUID uuid = UUID.randomUUID();
+
+    // Create the att values list of uuid
+    LinkedHashSet<AttributeValue> valuesUuid =
+      new LinkedHashSet<AttributeValue>(1);
+    valuesUuid.add(new AttributeValue(Historical.entryuuidAttrType,
+        new ASN1OctetString(uuid.toString())));
+    ArrayList<Attribute> uuidList = new ArrayList<Attribute>(1);
+    Attribute uuidAttr = new Attribute(Historical.entryuuidAttrType,
+        "entryUUID", valuesUuid);
+    uuidList.add(uuidAttr);
+
+    /*
+     * Add the uuid in the entry
+     */
+    Map<AttributeType, List<Attribute>> operationalAttributes = entry
+        .getOperationalAttributes();
+    operationalAttributes.put(Historical.entryuuidAttrType, uuidList);
+
+    // load historical from the entry
+    Historical hist = Historical.load(entry);
+
+    /*
+     * simulate a modify-add done at time t10
+     */
+    testModify(entry, hist, "description", ModificationType.ADD,
+        "init value", 10, true);
+
+    /*
+     * Now simulate a replace at an earlier date that the previous replace
+     * conflict resolution should keep it.
+     */
+    testModify(entry, hist, "description", ModificationType.REPLACE,
+        "older value", 1, true);
+
+    /*
+     * Now simulate a replace at an earlier date that the previous replace
+     * conflict resolution should remove it. (a second time to make
+     * sure...)
+     */
+    testModify(entry, hist, "description", ModificationType.REPLACE,
+        "older value", 2, true);
+
+    /*
+     * Now simulate a replace at a later date that the previous replace.
+     * conflict resolution should keep it
+     */
+    testModify(entry, hist, "description", ModificationType.REPLACE,
+        "new value", 11, true);
 
   }
 
@@ -294,7 +362,6 @@
      */
     testModify(entry, hist, "description", ModificationType.ADD, "new value",
         11, true);
-
   }
 
   /**
@@ -412,6 +479,17 @@
     {
       testHistoricalAndFake(hist, entry);
     }
+    
+    /*
+     * Check that the encoding decoding of historical information
+     * works  by encoding decoding and checking that the result is the same
+     * as the initial value.
+     */
+    entry.removeAttribute(Historical.historicalAttrType);
+    entry.addAttribute(hist.encode(), null);
+    Historical hist2 = Historical.load(entry);
+    assertEquals(hist2.encode().toString(), hist.encode().toString());
+    
     /*
      * The last older change should have been detected as conflicting and
      * should be removed by the conflict resolution code.

--
Gitblit v1.10.0