From b360a0579e5fcdb487ed7b5dc8fff6a9b1dceb0f Mon Sep 17 00:00:00 2001
From: Ludovic Poitou <ludovic.poitou@forgerock.com>
Date: Fri, 09 Sep 2011 13:24:02 +0000
Subject: [PATCH] Fix for OPENDJ-274. Added detection of modifications with identical ChangeNumber to a Single-Value Attribute. Unit tests added for all cases that were identified as not resulting in the same value in the attribute after replication.

---
 opendj-sdk/opends/src/server/org/opends/server/replication/plugin/AttrHistoricalSingle.java |   86 +++++++++++++++++++++++++++++++++++-------
 1 files changed, 71 insertions(+), 15 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/AttrHistoricalSingle.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/AttrHistoricalSingle.java
index c61c6e9..826bacc 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/AttrHistoricalSingle.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/AttrHistoricalSingle.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2008-2010 Sun Microsystems, Inc.
+ *      Portions Copyright 2011 ForgeRock AS
  */
 package org.opends.server.replication.plugin;
 
@@ -52,6 +53,10 @@
   private ChangeNumber addTime = null;    // last time when a value was added
   private AttributeValue value = null;    // last added value
 
+  // last operation applied. This is only used for multiple mods on the same
+  // single valued attribute in the same modification.
+  private HistAttrModificationKey lastMod = null;
+
   /**
    * {@inheritDoc}
    */
@@ -99,11 +104,13 @@
     case DELETE:
       this.deleteTime = changeNumber;
       this.value = newValue;
+      lastMod = HistAttrModificationKey.DEL;
       break;
 
     case ADD:
       this.addTime = changeNumber;
       this.value = newValue;
+      lastMod = HistAttrModificationKey.ADD;
       break;
 
     case REPLACE:
@@ -111,10 +118,12 @@
       {
         // REPLACE with null value is actually a DELETE
         this.deleteTime = changeNumber;
+        lastMod = HistAttrModificationKey.DEL;
       }
       else
       {
         this.deleteTime = addTime = changeNumber;
+        lastMod = HistAttrModificationKey.REPL;
       }
       this.value = newValue;
       break;
@@ -144,21 +153,51 @@
     switch (mod.getModificationType())
     {
     case DELETE:
-      if ((changeNumber.newer(addTime)) &&
-          ((newValue == null) ||
-              ((newValue != null) && (newValue.equals(value))) ||
-              (value == null)))
+      if (changeNumber.newer(addTime))
       {
-        if (changeNumber.newer(deleteTime))
-          deleteTime = changeNumber;
-        AttributeType type = modAttr.getAttributeType();
-        if (!modifiedEntry.hasAttribute(type))
+        if ((newValue == null) ||
+              ((newValue != null) && (newValue.equals(value))) ||
+              (value == null))
+        {
+          if (changeNumber.newer(deleteTime))
+          {
+            deleteTime = changeNumber;
+          }
+          AttributeType type = modAttr.getAttributeType();
+          if (!modifiedEntry.hasAttribute(type))
+          {
+            conflict = true;
+            modsIterator.remove();
+          }
+          else if ((newValue != null) &&
+              (!modifiedEntry.hasValue(type, modAttr.getOptions(), newValue)))
+          {
+            conflict = true;
+            modsIterator.remove();
+          }
+          else
+          {
+            lastMod = HistAttrModificationKey.DEL;
+          }
+        }
+        else
         {
           conflict = true;
           modsIterator.remove();
         }
-        else if ((newValue != null) &&
-            (!modifiedEntry.hasValue(type, modAttr.getOptions(), newValue)))
+      }
+      else if (changeNumber.equals(addTime))
+      {
+        if ((lastMod == HistAttrModificationKey.ADD)
+            || (lastMod == HistAttrModificationKey.REPL))
+        {
+          if (changeNumber.newer(deleteTime))
+          {
+            deleteTime = changeNumber;
+          }
+          lastMod = HistAttrModificationKey.DEL;
+        }
+        else
         {
           conflict = true;
           modsIterator.remove();
@@ -166,8 +205,8 @@
       }
       else
       {
-        conflict = true;
-        modsIterator.remove();
+          conflict = true;
+          modsIterator.remove();
       }
       break;
 
@@ -178,6 +217,7 @@
         mod.setModificationType(ModificationType.REPLACE);
         addTime = changeNumber;
         value = newValue;
+        lastMod = HistAttrModificationKey.REPL;
       }
       else
       {
@@ -187,18 +227,32 @@
           // no conflict : don't do anything beside setting the addTime
           addTime = changeNumber;
           value = newValue;
+          lastMod = HistAttrModificationKey.ADD;
         }
         else
         {
-          conflict = true;
-          modsIterator.remove();
+          // Case where changeNumber = addTime = deleteTime
+          if (changeNumber.equals(deleteTime)
+              && changeNumber.equals(addTime)
+              && (lastMod == HistAttrModificationKey.DEL))
+          {
+            // No conflict, record the new value.
+            value = newValue;
+            lastMod = HistAttrModificationKey.ADD;
+          }
+          else
+          {
+            conflict = true;
+            modsIterator.remove();
+          }
         }
       }
 
       break;
 
     case REPLACE:
-      if ((changeNumber.older(deleteTime)) && (changeNumber.older(deleteTime)))
+      if ((changeNumber.older(deleteTime))
+          && ((addTime == null) || (changeNumber.older(addTime))))
       {
         conflict = true;
         modsIterator.remove();
@@ -209,12 +263,14 @@
         {
           value = newValue;
           deleteTime = changeNumber;
+          lastMod = HistAttrModificationKey.DEL;
         }
         else
         {
           addTime = changeNumber;
           value = newValue;
           deleteTime = changeNumber;
+          lastMod = HistAttrModificationKey.REPL;
         }
       }
       break;

--
Gitblit v1.10.0