mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

gbellato
19.05.2007 8082c686a8bb865354514b24ec567ba0466ce4bc
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

This change add some tests for this case and also fix the Historical.load() method.
3 files modified
104 ■■■■■ changed files
opends/src/server/org/opends/server/synchronization/plugin/AttrInfo.java 21 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/synchronization/plugin/SynchronizationDomain.java 3 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/plugin/ModifyConflictTest.java 80 ●●●●● patch | view | raw | blame | history
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;
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) &&
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;
@@ -139,6 +140,73 @@
  }
  /**
   * 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);
  }
  /*
   * helper function.
   */
@@ -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.