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