From 2607f5c4a6e156f50caad96beb8328f2540b8eae Mon Sep 17 00:00:00 2001
From: gbellato <gbellato@localhost>
Date: Thu, 15 Jan 2009 07:38:52 +0000
Subject: [PATCH] Fix for issue 3683

---
 opendj-sdk/opends/src/server/org/opends/server/replication/plugin/Historical.java                                     |   22 +++++++
 opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java                          |   39 +++++++++++-
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java           |   36 +++++++++++
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java |   28 +++++++++
 4 files changed, 117 insertions(+), 8 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/Historical.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/Historical.java
index cd45bce..89443da 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/Historical.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/Historical.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Copyright 2006-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.replication.plugin;
 
@@ -433,6 +433,26 @@
     return builder.toAttribute();
   }
 
+  /**
+   * Indicates if the Entry was renamed or added after the ChangeNumber
+   * that is given as a parameter.
+   *
+   * @param cn The ChangeNumber with which the ADD or Rename date must be
+   *           compared.
+   *
+   * @return A boolean indicating if the Entry was renamed or added after
+   *                   the ChangeNumber that is given as a parameter.
+   */
+  public boolean AddedOrRenamedAfter(ChangeNumber cn)
+  {
+    if (cn.older(ADDDate) || cn.older(MODDNDate))
+    {
+      return true;
+    }
+    else
+      return false;
+  }
+
 
   /**
    * read the historical information from the entry attribute and
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
index a0e0d85..fa6cf27 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
@@ -64,6 +64,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.zip.CheckedOutputStream;
 import java.util.zip.DataFormatException;
+
 import org.opends.messages.Message;
 import org.opends.messages.MessageBuilder;
 import org.opends.server.admin.server.ConfigurationChangeListener;
@@ -75,6 +76,7 @@
 import org.opends.server.api.SynchronizationProvider;
 import org.opends.server.backends.jeb.BackendImpl;
 import org.opends.server.config.ConfigException;
+import org.opends.server.controls.SubtreeDeleteControl;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.DeleteOperation;
 import org.opends.server.core.DirectoryServer;
@@ -1069,6 +1071,8 @@
                * different operation.
                */
               op = msg.createOperation(conn);
+              if (op instanceof DeleteOperation)
+                op.addRequestControl(new SubtreeDeleteControl());
             }
           }
           else
@@ -1407,9 +1411,11 @@
       * The action taken here must be consistent with the actions
       * done in the solveNamingConflict(AddOperation) method
       * when we are adding an entry whose parent entry has already been deleted.
+      *
       */
-     findAndRenameChild(entryUid, op.getEntryDN(), op);
-     numUnresolvedNamingConflicts.incrementAndGet();
+     if (findAndRenameChild(entryUid, op.getEntryDN(), op))
+       numUnresolvedNamingConflicts.incrementAndGet();
+
      return false;
    }
    else
@@ -1652,17 +1658,25 @@
    * @param entryDN    The DN of the entry whose child must be renamed.
    * @param conflictOp The Operation that generated the conflict.
    */
-  private void findAndRenameChild(
+  private boolean findAndRenameChild(
       String entryUid, DN entryDN, Operation conflictOp)
   {
+    boolean conflict = false;
+
     // Find an rename child entries.
     InternalClientConnection conn =
       InternalClientConnection.getRootConnection();
+    DeleteContext ctx =
+      (DeleteContext) conflictOp.getAttachment(SYNCHROCONTEXT);
+    ChangeNumber cn = null;
+    if (ctx != null)
+      cn = ctx.getChangeNumber();
 
     try
     {
       LinkedHashSet<String> attrs = new LinkedHashSet<String>(1);
       attrs.add(ENTRYUIDNAME);
+      attrs.add(Historical.HISTORICALATTRIBUTENAME);
 
       SearchFilter ALLMATCH;
       ALLMATCH = SearchFilter.createFilterFromString("(objectClass=*)");
@@ -1678,9 +1692,22 @@
         {
           for (SearchResultEntry entry : entries)
           {
-            markConflictEntry(conflictOp, entry.getDN(), entryDN);
-            renameConflictEntry(conflictOp, entry.getDN(),
+            /*
+             * Check the ADD and ModRDN date of the child entry. If it is after
+             * the delete date then keep the entry as a conflicting entry,
+             * otherwise delete the entry with the operation.
+             */
+            if (cn != null)
+            {
+              Historical hist = Historical.load(entry);
+              if (hist.AddedOrRenamedAfter(cn))
+              {
+                conflict = true;
+                markConflictEntry(conflictOp, entry.getDN(), entryDN);
+                renameConflictEntry(conflictOp, entry.getDN(),
                                 Historical.getEntryUuid(entry));
+              }
+            }
           }
         }
       }
@@ -1708,6 +1735,8 @@
       mb.append(e.getLocalizedMessage());
       logError(mb.toMessage());
     }
+
+    return conflict;
   }
 
 
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
index 175339a..a7db448 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Copyright 2006-2009 Sun Microsystems, Inc.
  */
 
 package org.opends.server.replication;
@@ -1120,6 +1120,8 @@
 
     // add domain1 entry with 2 children : domain2 and domain3
     addEntry(domain1);
+    ChangeNumber olderCn = gen.newChangeNumber();
+    Thread.sleep(1000);
     domain1uid = getEntryUUID(DN.decode(domain1dn));
     addEntry(domain2);
     domain2uid = getEntryUUID(DN.decode(domain2dn));
@@ -1134,7 +1136,7 @@
     AlertCount = DummyAlertHandler.getAlertCount();
 
     // delete domain1
-    delMsg = new DeleteMsg(domain1dn, gen.newChangeNumber(), domain1uid);
+    delMsg = new DeleteMsg(domain1dn, olderCn, domain1uid);
     broker.publish(delMsg);
 
     // check that the domain1 has correctly been deleted
@@ -1167,6 +1169,36 @@
     delEntry(conflictDomain3dn);
 
     //
+    // Check that when a delete is replayed over an entry which has child
+    // those child are also deleted
+    //
+    // add domain1 entry with 2 children : domain2 and domain3
+    addEntry(domain1);
+    domain1uid = getEntryUUID(DN.decode(domain1dn));
+    addEntry(domain2);
+    domain2uid = getEntryUUID(DN.decode(domain2dn));
+    addEntry(domain3);
+    domain3uid = getEntryUUID(DN.decode(domain3dn));
+
+    updateMonitorCount(baseDn, unresolvedMonitorAttr);
+    AlertCount = DummyAlertHandler.getAlertCount();
+
+    // delete domain1
+    delMsg = new DeleteMsg(domain1dn, gen.newChangeNumber(), domain1uid);
+    broker.publish(delMsg);
+
+    // check that the domain1 has correctly been deleted
+    assertNull(getEntry(DN.decode(domain1dn), 10000, false),
+        "The DELETE replication message was not replayed");
+
+    // check that domain2 and domain3 have not been renamed as conflicting
+    assertNull(getEntry(conflictDomain2dn, 1000, true),
+        "The conflicting entries were not created");
+    assertNull(getEntry(conflictDomain3dn, 1000, true),
+        "The conflicting entries were not created");
+
+
+    //
     // Check that when an entry is added on one master below an entry
     // that is currently deleted on another master, the replay of the
     // add on the second master cause the added entry to be renamed
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java
index a3cdf79..d3cff52 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java
@@ -41,6 +41,7 @@
 import org.opends.server.replication.ReplicationTestCase;
 import org.opends.server.replication.common.DSInfo;
 import org.opends.server.replication.common.RSInfo;
+import org.opends.server.replication.common.ServerStatus;
 import org.opends.server.replication.protocol.UpdateMsg;
 import org.opends.server.replication.server.ReplServerFakeConfiguration;
 import org.opends.server.replication.server.ReplicationServer;
@@ -128,6 +129,11 @@
         assertTrue(replServerInfo.getGenerationId() == 1);
       }
 
+      for (DSInfo serverInfo : domain1.getDsList())
+      {
+        assertTrue(serverInfo.getStatus() == ServerStatus.NORMAL_STATUS);
+      }
+
       domain1.setGenerationID(2);
       domain1.resetReplicationLog();
 
@@ -138,6 +144,28 @@
         // The generation Id of the remote should now be 2
         assertTrue(replServerInfo.getGenerationId() == 2);
       }
+
+      for (DSInfo serverInfo : domain1.getDsList())
+      {
+        if (serverInfo.getDsId() == 2)
+          assertTrue(serverInfo.getStatus() == ServerStatus.BAD_GEN_ID_STATUS);
+        else
+        {
+          assertTrue(serverInfo.getDsId() == 1);
+          assertTrue(serverInfo.getStatus() == ServerStatus.NORMAL_STATUS);
+        }
+      }
+
+      for (DSInfo serverInfo : domain2.getDsList())
+      {
+        if (serverInfo.getDsId() == 2)
+          assertTrue(serverInfo.getStatus() == ServerStatus.BAD_GEN_ID_STATUS);
+        else
+        {
+          assertTrue(serverInfo.getDsId() == 1);
+          assertTrue(serverInfo.getStatus() == ServerStatus.NORMAL_STATUS);
+        }
+      }
     }
     finally
     {

--
Gitblit v1.10.0