From 23e98d540a23d7f7a21e3ffae94938ea066306ac Mon Sep 17 00:00:00 2001
From: ludovicp <ludovicp@localhost>
Date: Mon, 31 May 2010 09:49:21 +0000
Subject: [PATCH] Fix issue #3891 - Handle replication conflict when adding child entry and parent is deleted

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java             |   18 +
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/NamingConflictTest.java       |  281 ++++++++++++++++++++++++++++++++++++++++
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java |   21 ++
 opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java                                      |   63 ++++++++
 opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java                            |   27 +--
 5 files changed, 374 insertions(+), 36 deletions(-)

diff --git a/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java b/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
index e472a43..f887c0a 100644
--- a/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
+++ b/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
@@ -77,7 +77,6 @@
 import org.opends.server.backends.jeb.BackendImpl;
 import org.opends.server.backends.task.Task;
 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;
@@ -2564,11 +2563,10 @@
               /*
                * Create a new operation as the ConflictResolution
                * different operation.
+               *  Note: When msg is a DeleteMsg, the DeleteOperation is properly
+               *  created with subtreeDelete request control when needed.
                */
               op = msg.createOperation(conn);
-              if (op instanceof DeleteOperation) {
-                op.addRequestControl(new SubtreeDeleteControl(false));
-              }
             }
           }
           else
@@ -3189,21 +3187,14 @@
           for (SearchResultEntry entry : entries)
           {
             /*
-             * 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.
+             * Check the ADD and ModRDN date of the child entry (All of them,
+             * not only the one that are newer than the DEL op)
+             * and keep the entry as a conflicting entry,
              */
-            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));
-              }
-            }
+            conflict = true;
+            markConflictEntry(conflictOp, entry.getDN(), entryDN);
+            renameConflictEntry(conflictOp, entry.getDN(),
+                Historical.getEntryUuid(entry));
           }
         }
       }
diff --git a/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java b/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java
index 4068db1..7e8c1cf 100644
--- a/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2009 Sun Microsystems, Inc.
+ *      Copyright 2006-2010 Sun Microsystems, Inc.
  */
 package org.opends.server.replication.protocol;
 
@@ -31,6 +31,7 @@
 import java.io.UnsupportedEncodingException;
 import java.util.zip.DataFormatException;
 
+import org.opends.server.controls.SubtreeDeleteControl;
 import org.opends.server.core.DeleteOperationBasis;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.replication.common.ChangeNumber;
@@ -43,7 +44,10 @@
  */
 public class DeleteMsg extends LDAPUpdateMsg
 {
-  String initiatorsName;
+  private String initiatorsName;
+
+  // whether the DEL operation is a subtree DEL
+  private boolean isSubtreeDelete = false;
 
   /**
    * Creates a new delete message.
@@ -54,15 +58,23 @@
   {
     super((OperationContext) operation.getAttachment(SYNCHROCONTEXT),
            operation.getRawEntryDN().toString());
+    try
+    {
+      if (operation.getRequestControl(SubtreeDeleteControl.DECODER) != null)
+        isSubtreeDelete = true;
+    }
+    catch(Exception e)
+    {}
+
   }
 
   /**
    * Creates a new delete message.
    *
-   * @param dn The dn with which the message must be created.
+   * @param dn           The dn with which the message must be created.
    * @param changeNumber The change number with which the message must be
    *                     created.
-   * @param uid The unique id with which the message must be created.
+   * @param uid          The unique id with which the message must be created.
    */
   public DeleteMsg(String dn, ChangeNumber changeNumber, String uid)
   {
@@ -87,6 +99,12 @@
     // protocol version has been read as part of the header
     if (protocolVersion >= 4)
       decodeBody_V4(in, pos);
+    else
+    {
+      // Keep the previous protocol version behavior - when we don't know the
+      // truth, we assume 'subtree'
+      isSubtreeDelete = true;
+    }
   }
 
 
@@ -101,6 +119,10 @@
         InternalClientConnection.nextOperationID(),
         InternalClientConnection.nextMessageID(), null,
         ByteString.valueOf(newDn));
+
+    if (isSubtreeDelete)
+      del.addRequestControl(new SubtreeDeleteControl(false));
+
     DeleteContext ctx = new DeleteContext(getChangeNumber(), getUniqueId());
     del.setAttachment(SYNCHROCONTEXT, ctx);
     return del;
@@ -152,6 +174,8 @@
     {
       bodyLength++;
     }
+    // subtree flag
+    bodyLength++;
 
     /* encode the header in a byte[] large enough to also contain the mods */
     byte [] encodedMsg = encodeHeader(MSG_TYPE_DELETE, bodyLength,
@@ -163,6 +187,9 @@
       encodedMsg[pos++] = 0;
     pos = addByteArray(byteEntryAttrLen, encodedMsg, pos);
     pos = addByteArray(encodedEclIncludes, encodedMsg, pos);
+
+    encodedMsg[pos++] = (isSubtreeDelete ? (byte) 1 : (byte) 0);
+
     return encodedMsg;
   }
 
@@ -173,7 +200,6 @@
   private void decodeBody_V4(byte[] in, int pos)
   throws DataFormatException, UnsupportedEncodingException
   {
-    // Read ecl attr len
     int length = getNextLength(in, pos);
     if (length != 0)
     {
@@ -185,15 +211,21 @@
       initiatorsName = null;
       pos += 1;
     }
+
+    // Read ecl attr len
     length = getNextLength(in, pos);
     int eclAttrLen = Integer.valueOf(new String(in, pos, length,"UTF-8"));
+    // Skip the length
     pos += length + 1;
 
     // Read/Don't decode entry attributes
     encodedEclIncludes = new byte[eclAttrLen];
     try
     {
+      // Copy ecl attr
       System.arraycopy(in, pos, encodedEclIncludes, 0, eclAttrLen);
+      // Skip the attrs
+      pos += eclAttrLen +1;
     } catch (IndexOutOfBoundsException e)
     {
       throw new DataFormatException(e.getMessage());
@@ -204,6 +236,10 @@
     {
       throw new DataFormatException(e.getMessage());
     }
+
+    // subtree flag
+    isSubtreeDelete = (in[pos] == 1);
+
   }
 
   /**
@@ -263,4 +299,21 @@
     return initiatorsName;
   }
 
+  /**
+   * Set the subtree flag.
+   * @param subtreeDelete the subtree flag.
+   */
+  public void setSubtreeDelete(boolean subtreeDelete)
+  {
+    this.isSubtreeDelete = subtreeDelete;
+  }
+
+  /**
+   * Get the subtree flag.
+   * @return the subtree flag.
+   */
+  public boolean isSubtreeDelete()
+  {
+    return this.isSubtreeDelete;
+  }
 }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
index e98164b..b26d5ca 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2009 Sun Microsystems, Inc.
+ *      Copyright 2006-2010 Sun Microsystems, Inc.
  */
 
 package org.opends.server.replication;
@@ -1194,12 +1194,18 @@
     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, 10000, true),
-        "The conflicting entries were created");
-    assertNull(getEntry(conflictDomain3dn, 10000, true),
-        "The conflicting entries were created");
+    // check that domain2 and domain3 have been renamed as conflicting
+    String confDomain2dn = "entryuuid="+domain2uid+"+dc=domain2,ou=people,"+TEST_ROOT_DN_STRING;
+    String confDomain3dn = "entryuuid="+domain3uid+"+dc=domain3,ou=people,"+TEST_ROOT_DN_STRING;
+    assertTrue(DirectoryServer.entryExists(DN.decode(confDomain2dn)),
+    "The conflicting entry exist for domain2" + confDomain2dn);
+    assertTrue(DirectoryServer.entryExists(DN.decode(confDomain3dn)),
+    "The conflicting entry exist for domain3" + confDomain3dn);
+    // check that unresolved conflict count has been incremented
+    assertEquals(getMonitorDelta(), 1);
 
+    delEntry(DN.decode(confDomain2dn));
+    delEntry(DN.decode(confDomain3dn));
 
     //
     // Check that when an entry is added on one master below an entry
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/NamingConflictTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/NamingConflictTest.java
index be76ec6..1977ed7 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/NamingConflictTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/NamingConflictTest.java
@@ -22,12 +22,13 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2009-2010 Sun Microsystems, Inc.
  */
 package org.opends.server.replication.plugin;
 
 import static org.opends.server.TestCaseUtils.TEST_ROOT_DN_STRING;
 
+import java.util.ArrayList;
 import java.util.TreeSet;
 
 import org.opends.server.TestCaseUtils;
@@ -36,7 +37,10 @@
 import org.opends.server.replication.ReplicationTestCase;
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.replication.common.ChangeNumberGenerator;
+import org.opends.server.replication.protocol.AddMsg;
 import org.opends.server.replication.protocol.ModifyDNMsg;
+import org.opends.server.replication.protocol.DeleteMsg;
+import org.opends.server.types.Attribute;
 import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
 import org.testng.annotations.Test;
@@ -113,7 +117,10 @@
           TEST_ROOT_DN_STRING,
       "uid=simultaneous2");
 
+      // Put the message in the replay queue
       domain.processUpdate(modDnMsg);
+
+      // Make the domain replay the change from the replay queue
       domain.replay(queue.take().getUpdateMessage());
 
       // This MODIFY DN uses an older DN and should therefore be cancelled
@@ -124,9 +131,14 @@
           TEST_ROOT_DN_STRING,
       "uid=simulatneouswrong");
 
+      // Put the message in the replay queue
       domain.processUpdate(modDnMsg);
+
+      // Make the domain replay the change from the replay queue
+      // and resolve conflict
       domain.replay(queue.take().getUpdateMessage());
 
+      // Expect the conflict resolution 
       assertFalse(DirectoryServer.entryExists(entry.getDN()),
       "The modDN conflict was not resolved as expected.");
     }
@@ -135,4 +147,271 @@
       MultimasterReplication.deleteDomain(baseDn);
     }
   }
+
+  /**
+   * Tests for issue 3891
+   *       S1                                       S2
+   *       ADD uid=xx,ou=parent,...          [SUBTREE] DEL ou=parent, ...
+   *
+   * 1/ removeParentConflict1 (on S1)
+   *    - t1(cn1) ADD uid=xx,ou=parent,...
+   *         - t2(cn2) replay SUBTREE DEL ou=parent, ....
+   *    => No conflict : expect the parent entry & subtree to be deleted
+   *
+   * 2/ removeParentConflict2 (on S1)
+   *    - t1(cn1) ADD uid=xx,ou=parent,...
+   *             - replay t2(cn2) DEL ou=parent, ....
+   *    => Conflict and no automatic resolution: expect 
+   *         - the child entry to be renamed under root entry
+   *         - the parent entry to be deleted
+   *
+   * 3/ removeParentConflict3 (on S2)
+   *                         - t2(cn2) DEL or SUBTREE DEL ou=parent, ....
+   *                         - t1(cn1) replay ADD uid=xx,ou=parent,...
+   *                        => Conflict and no automatic resolution: expect 
+   *                           - the child entry to be renamed under root entry
+   *
+   */
+  @Test(enabled=true)
+  public void removeParentConflict1() throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    final DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
+
+    TestSynchronousReplayQueue queue = new TestSynchronousReplayQueue();
+    DomainFakeCfg conf = new DomainFakeCfg(baseDn, 1, new TreeSet<String>());
+    conf.setIsolationPolicy(IsolationPolicy.ACCEPT_ALL_UPDATES);
+
+    LDAPReplicationDomain domain =
+      MultimasterReplication.createNewDomain(conf, queue);
+    domain.start();
+
+    try
+    {
+      /*
+       * Create a Change number generator to generate new ChangeNumbers
+       * when we need to send operations messages to the replicationServer.
+       */
+      ChangeNumberGenerator gen = new ChangeNumberGenerator(201, 0);
+
+      Entry parentEntry = TestCaseUtils.entryFromLdifString(
+          "dn: ou=rpConflict, "+ TEST_ROOT_DN_STRING + "\n"
+          + "objectClass: top\n"
+          + "objectClass: organizationalUnit\n");          
+
+      Entry childEntry = TestCaseUtils.entryFromLdifString(
+          "dn: cn=child, ou=rpConflict,"+ TEST_ROOT_DN_STRING + "\n"
+          + "objectClass: top\n" 
+          + "objectClass: person\n"
+          + "objectClass: organizationalPerson\n"
+          + "objectClass: inetOrgPerson\n" + "uid: user.1\n"
+          + "description: This is the description for Aaccf Amar.\n" + "st: NC\n"
+          + "postalAddress: Aaccf Amar$17984 Thirteenth Street"
+          + "$Rockford, NC  85762\n" + "mail: user.1@example.com\n"
+          + "cn: Aaccf Amar\n" + "l: Rockford\n"
+          + "street: 17984 Thirteenth Street\n"
+          + "employeeNumber: 1\n"
+          + "sn: Amar\n" + "givenName: Aaccf\n" + "postalCode: 85762\n"
+          + "userPassword: password\n" + "initials: AA\n");
+
+      TestCaseUtils.addEntry(parentEntry);
+      TestCaseUtils.addEntry(childEntry);
+
+      String parentUUID = getEntryUUID(parentEntry.getDN());
+      String childUUID = getEntryUUID(childEntry.getDN());
+
+      ChangeNumber cn2 = gen.newChangeNumber();
+
+      DeleteMsg  delMsg = new DeleteMsg(
+          parentEntry.getDN().toNormalizedString(),
+          cn2,
+          parentUUID);
+      delMsg.setSubtreeDelete(true);
+
+      // Put the message in the replay queue
+      domain.processUpdate(delMsg);
+      // Make the domain replay the change from the replay queue
+      domain.replay(queue.take().getUpdateMessage());
+
+      // Expect the subtree to be deleted and no conflict entry created
+      assertFalse(DirectoryServer.entryExists(parentEntry.getDN()),
+      "DEL subtree on parent was not processed as expected.");
+      assertFalse(DirectoryServer.entryExists(parentEntry.getDN()),
+      "DEL subtree on parent was not processed as expected.");
+    }
+    finally
+    {
+      MultimasterReplication.deleteDomain(baseDn);
+    }
+  }
+
+  @Test(enabled=true)
+  public void removeParentConflict2() throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    final DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
+
+    TestSynchronousReplayQueue queue = new TestSynchronousReplayQueue();
+    DomainFakeCfg conf = new DomainFakeCfg(baseDn, 1, new TreeSet<String>());
+    conf.setIsolationPolicy(IsolationPolicy.ACCEPT_ALL_UPDATES);
+
+    LDAPReplicationDomain domain =
+      MultimasterReplication.createNewDomain(conf, queue);
+    domain.start();
+
+    try
+    {
+      /*
+       * Create a Change number generator to generate new ChangeNumbers
+       * when we need to send operations messages to the replicationServer.
+       */
+      ChangeNumberGenerator gen = new ChangeNumberGenerator(201, 0);
+
+      Entry parentEntry = TestCaseUtils.entryFromLdifString(
+          "dn: ou=rpConflict, "+ TEST_ROOT_DN_STRING + "\n"
+          + "objectClass: top\n"
+          + "objectClass: organizationalUnit\n");          
+
+      Entry childEntry = TestCaseUtils.entryFromLdifString(
+          "dn: cn=child, ou=rpConflict,"+ TEST_ROOT_DN_STRING + "\n"
+          + "objectClass: top\n" 
+          + "objectClass: person\n"
+          + "objectClass: organizationalPerson\n"
+          + "objectClass: inetOrgPerson\n" + "uid: user.1\n"
+          + "description: This is the description for Aaccf Amar.\n" + "st: NC\n"
+          + "postalAddress: Aaccf Amar$17984 Thirteenth Street"
+          + "$Rockford, NC  85762\n" + "mail: user.1@example.com\n"
+          + "cn: Aaccf Amar\n" + "l: Rockford\n"
+          + "street: 17984 Thirteenth Street\n"
+          + "employeeNumber: 1\n"
+          + "sn: Amar\n" + "givenName: Aaccf\n" + "postalCode: 85762\n"
+          + "userPassword: password\n" + "initials: AA\n");
+
+      TestCaseUtils.addEntry(parentEntry);
+      TestCaseUtils.addEntry(childEntry);
+
+      assertTrue(DirectoryServer.entryExists(parentEntry.getDN()),
+      "Parent entry expected to exist.");
+      assertTrue(DirectoryServer.entryExists(childEntry.getDN()),
+      "Child  entry expected to be exist.");
+
+      String parentUUID = getEntryUUID(parentEntry.getDN());
+      String childUUID = getEntryUUID(childEntry.getDN());
+
+      ChangeNumber cn2 = gen.newChangeNumber();
+
+      DeleteMsg  delMsg = new DeleteMsg(
+          parentEntry.getDN().toNormalizedString(),
+          cn2,
+          parentUUID);
+      // NOT SUBTREE
+
+      // Put the message in the replay queue
+      domain.processUpdate(delMsg);
+      // Make the domain replay the change from the replay queue
+      domain.replay(queue.take().getUpdateMessage());
+
+      // Expect the parent entry to be deleted
+      assertTrue(!DirectoryServer.entryExists(parentEntry.getDN()),
+          "Parent entry expected to be deleted : " + parentEntry.getDN());
+
+      // Expect the child entry to be moved as conflict entry under the root
+      // entry of the suffix
+      DN childDN = DN.decode("entryuuid="+childUUID+
+          "+cn=child,o=test");
+      assertTrue(DirectoryServer.entryExists(childDN),
+          "Child entry conflict exist with DN="+childDN);
+
+    }
+    finally
+    {
+      MultimasterReplication.deleteDomain(baseDn);
+    }
+  }
+
+  @Test(enabled=true)
+  public void removeParentConflict3() throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    final DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
+
+    TestSynchronousReplayQueue queue = new TestSynchronousReplayQueue();
+    DomainFakeCfg conf = new DomainFakeCfg(baseDn, 1, new TreeSet<String>());
+    conf.setIsolationPolicy(IsolationPolicy.ACCEPT_ALL_UPDATES);
+
+    LDAPReplicationDomain domain =
+      MultimasterReplication.createNewDomain(conf, queue);
+    domain.start();
+
+    try
+    {
+      /*
+       * Create a Change number generator to generate new ChangeNumbers
+       * when we need to send operations messages to the replicationServer.
+       */
+      ChangeNumberGenerator gen = new ChangeNumberGenerator(201, 0);
+
+      Entry parentEntry = TestCaseUtils.entryFromLdifString(
+          "dn: ou=rpConflict, "+ TEST_ROOT_DN_STRING + "\n"
+          + "objectClass: top\n"
+          + "objectClass: organizationalUnit\n");          
+
+      Entry childEntry = TestCaseUtils.entryFromLdifString(
+          "dn: cn=child, ou=rpConflict,"+ TEST_ROOT_DN_STRING + "\n"
+          + "objectClass: top\n" 
+          + "objectClass: person\n"
+          + "objectClass: organizationalPerson\n"
+          + "objectClass: inetOrgPerson\n" + "uid: user.1\n"
+          + "description: This is the description for Aaccf Amar.\n" + "st: NC\n"
+          + "postalAddress: Aaccf Amar$17984 Thirteenth Street"
+          + "$Rockford, NC  85762\n" + "mail: user.1@example.com\n"
+          + "cn: Aaccf Amar\n" + "l: Rockford\n"
+          + "street: 17984 Thirteenth Street\n"
+          + "employeeNumber: 1\n"
+          + "sn: Amar\n" + "givenName: Aaccf\n" + "postalCode: 85762\n"
+          + "userPassword: password\n" + "initials: AA\n");
+
+
+      TestCaseUtils.addEntry(parentEntry);
+      String parentUUID = getEntryUUID(parentEntry.getDN());
+      TestCaseUtils.deleteEntry(parentEntry);
+
+      ChangeNumber cn1 = gen.newChangeNumber();
+
+      // Create and publish an update message to add the child entry.
+      String childUUID = "44444444-4444-4444-4444-444444444444";
+      AddMsg addMsg = new AddMsg(
+          cn1,
+          childEntry.getDN().toString(),
+          childUUID,
+          parentUUID,
+          childEntry.getObjectClassAttribute(),
+          childEntry.getAttributes(),
+          new ArrayList<Attribute>());
+      
+      // Put the message in the replay queue
+      domain.processUpdate(addMsg);
+      // Make the domain replay the change from the replay queue
+      domain.replay(queue.take().getUpdateMessage());
+
+      // Expect the parent entry to be deleted
+      assertFalse(DirectoryServer.entryExists(parentEntry.getDN()),
+          "Parent entry exists ");
+
+      // Expect the child entry to be moved as conflict entry under the root
+      // entry of the suffix
+      DN childDN = DN.decode("entryuuid="+childUUID+
+          "+cn=child,o=test");
+      assertTrue(DirectoryServer.entryExists(childDN),
+          "Child entry conflict exist with DN="+childDN);
+
+    }
+    finally
+    {
+      MultimasterReplication.deleteDomain(baseDn);
+    }
+  }
 }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
index 669f017..ef8fa71 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
@@ -79,7 +79,7 @@
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
-
+import org.opends.server.controls.SubtreeDeleteControl;
 /**
  * Test the constructors, encoders and decoders of the replication protocol
  * PDUs classes (message classes)
@@ -321,8 +321,8 @@
     entryAttrList.add(eattr2);
 
     return new Object[][] {
-        {"dc=com", entryAttrList},
-        {"dc=delete,dc=an,dc=entry,dc=with,dc=a,dc=long dn", null},
+        {"dc=com", entryAttrList, false},
+        {"dc=delete,dc=an,dc=entry,dc=with,dc=a,dc=long dn", null, true},
         };
   }
 
@@ -333,18 +333,23 @@
    * Finally test that both Msg matches.
    */
   @Test(enabled=true,dataProvider = "createDeleteData")
-  public void deleteMsgTest(String rawDN, List<Attribute> entryAttrList)
+  public void deleteMsgTest(String rawDN, List<Attribute> entryAttrList,
+      boolean subtree)
   throws Exception
   {
     InternalClientConnection connection =
         InternalClientConnection.getRootConnection();
     DeleteOperationBasis opBasis =
       new DeleteOperationBasis(connection, 1, 1,null, DN.decode(rawDN));
+    if (subtree)
+    {
+      opBasis.addRequestControl(new SubtreeDeleteControl(false));
+    }
     LocalBackendDeleteOperation op = new LocalBackendDeleteOperation(opBasis);
     ChangeNumber cn = new ChangeNumber(TimeThread.getTime(),123,  45);
     op.setAttachment(SYNCHROCONTEXT, new DeleteContext(cn, "uniqueid"));
     DeleteMsg msg = new DeleteMsg(op);
-
+    assertTrue((msg.isSubtreeDelete()==subtree));
     // Set ECL entry attributes
     if (entryAttrList != null)
     {
@@ -356,8 +361,8 @@
 
     assertEquals(msg.toString(), generatedMsg.toString());
     assertEquals(msg.getInitiatorsName(), generatedMsg.getInitiatorsName());
-
     assertEquals(msg.getChangeNumber(), generatedMsg.getChangeNumber());
+    assertEquals(generatedMsg.isSubtreeDelete(), subtree);
 
     // Get ECL entry attributes
     ArrayList<RawAttribute> genAttrList = generatedMsg.getEclIncludes();
@@ -379,6 +384,9 @@
     Operation generatedOperation = generatedMsg.createOperation(connection);
 
     assertEquals(generatedOperation.getClass(), DeleteOperationBasis.class);
+    assertTrue(
+        (subtree?(generatedOperation.getRequestControl(SubtreeDeleteControl.DECODER)!=null):
+          (generatedOperation.getRequestControl(SubtreeDeleteControl.DECODER)==null)));
 
     DeleteOperationBasis mod2 = (DeleteOperationBasis) generatedOperation;
 
@@ -387,6 +395,7 @@
     // Create an update message from this op
     DeleteMsg updateMsg = (DeleteMsg) LDAPUpdateMsg.generateMsg(op);
     assertEquals(msg.getChangeNumber(), updateMsg.getChangeNumber());
+    assertEquals(msg.isSubtreeDelete(), updateMsg.isSubtreeDelete());
   }
 
   @DataProvider(name = "createModifyDnData")

--
Gitblit v1.10.0