From b86b7b939d4fb1f3aa3a390d20352e7a16d86ef7 Mon Sep 17 00:00:00 2001
From: mrossign <mrossign@localhost>
Date: Fri, 31 Oct 2008 16:15:12 +0000
Subject: [PATCH] Partial fix for #3525: Attribute modifications are not replicated for modDN operations Warning: ModifyDNMsg PDU is updated and modifications are exchanged through the replication network. However at replay time, the moddn operation is re-created with the modifications, but they cannot be replayed at this time: The comment of the addModification() method for ModifyDNOperation states that "This should only be called by pre-operation plugins"

---
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyMsg.java                                         |   55 -------
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java    |   64 +++++++-
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java                                |    2 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java |   55 ++++++-
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java                                   |  137 +++++++++++++++++++
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java                                       |   90 ++++++++++++
 6 files changed, 321 insertions(+), 82 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java
new file mode 100644
index 0000000..5d1839a
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java
@@ -0,0 +1,137 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.protocol;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.opends.server.protocols.asn1.ASN1Element;
+import org.opends.server.protocols.ldap.LDAPAttribute;
+import org.opends.server.protocols.ldap.LDAPModification;
+import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.replication.plugin.Historical;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeUsage;
+import org.opends.server.types.Modification;
+
+/**
+ * This class holds every common code for the modify messages (mod, moddn).
+ */
+public abstract class ModifyCommonMsg extends UpdateMsg {
+
+  /**
+   * The modifications kept encoded in the message.
+   */
+  protected byte[] encodedMods = new byte[0];
+
+  /**
+   * Creates a new ModifyCommonMsg.
+   */
+  public ModifyCommonMsg()
+  {
+    super();
+  }
+
+  /**
+   * Creates a new ModifyCommonMsg with the given informations.
+   *
+   * @param ctx The replication Context of the operation for which the
+   *            update message must be created,.
+   * @param dn The DN of the entry on which the change
+   *           that caused the creation of this object happened
+   */
+  public ModifyCommonMsg(OperationContext ctx, String dn)
+  {
+   super(ctx, dn);
+  }
+
+  /**
+   * Creates a new ModifyCommonMsg with the given informations.
+   *
+   * @param cn        The ChangeNumber of the operation for which the
+   *                  UpdateMessage is created.
+   * @param entryUUID The Unique identifier of the entry that is updated
+   *                  by the operation for which the UpdateMessage is created.
+   * @param dn        The DN of the entry on which the change
+   *                  that caused the creation of this object happened
+   */
+  public ModifyCommonMsg(ChangeNumber cn, String entryUUID, String dn)
+  {
+    super(cn, entryUUID, dn);
+  }
+
+  /**
+   * Set the Modification associated to the UpdateMsg to the provided value.
+   *
+   * @param mods The new Modification associated to this ModifyMsg.
+   */
+  public void setMods(List<Modification> mods)
+  {
+    encodedMods = modsToByte(mods);
+  }
+
+  /**
+   * Encode an ArrayList of Modification into a byte[] suitable
+   * for storage in a database or send on the network.
+   *
+   * @param mods the ArrayList of Modification to be encoded.
+   * @return The encoded modifications.
+   */
+  protected byte[] modsToByte(List<Modification> mods)
+  {
+    if ((mods == null) || (mods.size() == 0))
+      return new byte[0];
+
+    ArrayList<ASN1Element> modsASN1;
+
+    modsASN1 = new ArrayList<ASN1Element>(mods.size());
+    for (Modification mod : mods)
+    {
+      Attribute attr = mod.getAttribute();
+      AttributeType type = attr.getAttributeType();
+      if (type != null )
+      {
+        if (AttributeUsage.DSA_OPERATION.equals(type.getUsage()))
+        {
+          // Attributes with a dsaOperation usage should not be synchronized.
+          // skip them.
+          continue;
+        }
+      }
+
+      if (!Historical.isHistoricalAttribute(attr))
+      {
+        LDAPModification ldapmod = new LDAPModification(
+          mod.getModificationType(), new LDAPAttribute(mod.getAttribute()));
+        modsASN1.add(ldapmod.encode());
+      }
+    }
+
+    return ASN1Element.encodeValue(modsASN1);
+  }
+
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java
index 1a3cd39..c146b2a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java
@@ -29,22 +29,29 @@
 import static org.opends.server.replication.protocol.OperationContext.*;
 
 import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.zip.DataFormatException;
 
 import org.opends.server.core.ModifyDNOperationBasis;
+import org.opends.server.protocols.asn1.ASN1Element;
+import org.opends.server.protocols.asn1.ASN1Exception;
 import org.opends.server.protocols.asn1.ASN1OctetString;
 import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.ldap.LDAPModification;
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.types.AbstractOperation;
 import org.opends.server.types.DN;
 import org.opends.server.types.DirectoryException;
+import org.opends.server.types.LDAPException;
+import org.opends.server.types.Modification;
 import org.opends.server.types.RDN;
 import org.opends.server.types.operation.PostOperationModifyDNOperation;
 
 /**
  * Message used to send Modify DN information.
  */
-public class ModifyDNMsg extends UpdateMsg
+public class ModifyDNMsg extends ModifyCommonMsg
 {
   private String newRDN;
   private String newSuperior;
@@ -61,6 +68,8 @@
     super((OperationContext) operation.getAttachment(SYNCHROCONTEXT),
         operation.getRawEntryDN().stringValue());
 
+    encodedMods = modsToByte(operation.getModifications());
+
     ModifyDnContext ctx =
       (ModifyDnContext) operation.getAttachment(SYNCHROCONTEXT);
     newSuperiorId = ctx.getNewParentId();
@@ -74,17 +83,19 @@
   }
 
   /**
-   * construct a new Modify DN message.
+   * Construct a new Modify DN message (no mods).
+   * Note: Keep this constructor version to support already written tests, not
+   * using mods.
    *
    * @param dn The dn to use for building the message.
    * @param changeNumber The changeNumberto use for building the message.
-   * @param uid The unique id to use for building the message.
+   * @param uid          The unique id to use for building the message.
    * @param newParentUid The new parent unique id to use for building
    *                     the message.
    * @param deleteOldRdn boolean indicating if old rdn must be deleted to use
    *                     for building the message.
-   * @param newSuperior The new Superior entry to use for building the message.
-   * @param newRDN The new Rdn to use for building the message.
+   * @param newSuperior  The new Superior entry to use for building the message.
+   * @param newRDN       The new Rdn to use for building the message.
    */
   public ModifyDNMsg(String dn, ChangeNumber changeNumber, String uid,
                      String newParentUid, boolean deleteOldRdn,
@@ -100,6 +111,29 @@
   }
 
   /**
+   * Construct a new Modify DN message (with mods).
+   *
+   * @param dn The dn to use for building the message.
+   * @param changeNumber The changeNumberto use for building the message.
+   * @param uid The unique id to use for building the message.
+   * @param newParentUid The new parent unique id to use for building
+   *                     the message.
+   * @param deleteOldRdn boolean indicating if old rdn must be deleted to use
+   *                     for building the message.
+   * @param newSuperior  The new Superior entry to use for building the message.
+   * @param newRDN       The new Rdn to use for building the message.
+   * @param mods         The mod of the operation.
+   */
+  public ModifyDNMsg(String dn, ChangeNumber changeNumber, String uid,
+                     String newParentUid, boolean deleteOldRdn,
+                     String newSuperior, String newRDN, List<Modification> mods)
+  {
+    this(dn, changeNumber, uid, newParentUid, deleteOldRdn, newSuperior,
+      newRDN);
+    this.encodedMods = modsToByte(mods);
+  }
+
+  /**
    * Creates a new ModifyDN message from a byte[].
    *
    * @param in The byte[] from which the operation must be read.
@@ -145,6 +179,33 @@
       deleteOldRdn = false;
     else
       deleteOldRdn = true;
+    pos++;
+
+    // For easiness (no additional method), simply compare PDU type to
+    // know if we have to read the mods of V2
+    if (in[0] == MSG_TYPE_MODIFYDN)
+    {
+      /* Read the mods : all the remaining bytes but the terminating 0 */
+      length = in.length - pos - 1;
+      if (length > 0) // Otherwise, there is only the trailing 0 byte which we
+        // do not need to read
+      {
+        encodedMods = new byte[length];
+        try
+        {
+          System.arraycopy(in, pos, encodedMods, 0, length);
+        } catch (IndexOutOfBoundsException e)
+        {
+          throw new DataFormatException(e.getMessage());
+        } catch (ArrayStoreException e)
+        {
+          throw new DataFormatException(e.getMessage());
+        } catch (NullPointerException e)
+        {
+          throw new DataFormatException(e.getMessage());
+        }
+      }
+    }
   }
 
   /**
@@ -153,6 +214,7 @@
   @Override
   public AbstractOperation createOperation(
          InternalClientConnection connection, String newDn)
+         throws LDAPException, ASN1Exception
   {
     ModifyDNOperationBasis moddn =  new ModifyDNOperationBasis(connection,
                InternalClientConnection.nextOperationID(),
@@ -160,6 +222,11 @@
                new ASN1OctetString(newDn), new ASN1OctetString(newRDN),
                deleteOldRdn,
                (newSuperior == null ? null : new ASN1OctetString(newSuperior)));
+
+    ArrayList<ASN1Element> mods = ASN1Element.decodeElements(encodedMods);
+    for (ASN1Element elem : mods)
+      moddn.addModification(LDAPModification.decode(elem).toModification());
+
     ModifyDnContext ctx = new ModifyDnContext(getChangeNumber(), getUniqueId(),
                                               newSuperiorId);
     moddn.setAttachment(SYNCHROCONTEXT, ctx);
@@ -194,6 +261,8 @@
     else
       length += 1;
 
+    length += encodedMods.length + 1;
+
     byte[] resultByteArray = encodeHeader(MSG_TYPE_MODIFYDN, length);
     int pos = resultByteArray.length - length;
 
@@ -222,6 +291,15 @@
     else
       resultByteArray[pos++] = 0;
 
+    /* add the mods */
+    if (encodedMods.length > 0)
+    {
+      pos = resultByteArray.length - (encodedMods.length + 1);
+      addByteArray(encodedMods, resultByteArray, pos);
+    }
+    else
+      resultByteArray[pos++] = 0;
+
     return resultByteArray;
   }
 
@@ -438,7 +516,7 @@
   {
     // The MODDN message size are mainly dependent on the
     // size of the DN. let's assume that they average on 100 bytes
-    return 100;
+    return encodedMods.length + 100;
   }
 
   /**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyMsg.java
index 5416659..9f6676f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyMsg.java
@@ -31,16 +31,11 @@
 import org.opends.server.core.ModifyOperationBasis;
 import org.opends.server.protocols.asn1.ASN1Exception;
 import org.opends.server.protocols.asn1.ASN1OctetString;
-import org.opends.server.protocols.ldap.LDAPAttribute;
 import org.opends.server.protocols.ldap.LDAPModification;
 import org.opends.server.protocols.asn1.ASN1Element;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.replication.common.ChangeNumber;
-import org.opends.server.replication.plugin.Historical;
 import org.opends.server.types.AbstractOperation;
-import org.opends.server.types.Attribute;
-import org.opends.server.types.AttributeType;
-import org.opends.server.types.AttributeUsage;
 import org.opends.server.types.DN;
 import org.opends.server.types.LDAPException;
 import org.opends.server.types.Modification;
@@ -56,10 +51,8 @@
 /**
  * Message used to send Modify information.
  */
-public class ModifyMsg extends UpdateMsg
+public class ModifyMsg extends ModifyCommonMsg
 {
-  private byte[] encodedMods = null;
-
   /**
    * Creates a new Modify message from a ModifyOperation.
    *
@@ -170,42 +163,6 @@
   }
 
   /**
-   * Encode an ArrayList of Modification into a byte[] suitable
-   * for storage in a database or send on the network.
-   *
-   * @param mods the ArrayList of Modification to be encoded.
-   */
-  private byte[] modsToByte(List<Modification> mods)
-  {
-    ArrayList<ASN1Element> modsASN1;
-
-    modsASN1 = new ArrayList<ASN1Element>(mods.size());
-    for (Modification mod : mods)
-    {
-      Attribute attr = mod.getAttribute();
-      AttributeType type = attr.getAttributeType();
-      if (type != null )
-      {
-        if (AttributeUsage.DSA_OPERATION.equals(type.getUsage()))
-        {
-          // Attributes with a dsaOperation usage should not be synchronized.
-          // skip them.
-          continue;
-        }
-      }
-
-      if (!Historical.isHistoricalAttribute(attr))
-      {
-        LDAPModification ldapmod = new LDAPModification(
-          mod.getModificationType(), new LDAPAttribute(mod.getAttribute()));
-        modsASN1.add(ldapmod.encode());
-      }
-    }
-
-    return ASN1Element.encodeValue(modsASN1);
-  }
-
-  /**
    * {@inheritDoc}
    */
   @Override
@@ -235,16 +192,6 @@
   }
 
   /**
-   * Set the Modification associated to the UpdateMsg to the provided value.
-   *
-   * @param mods The new Modification associated to this ModifyMsg.
-   */
-  public void setMods(List<Modification> mods)
-  {
-    encodedMods = modsToByte(mods);
-  }
-
-  /**
    * {@inheritDoc}
    */
   @Override
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java
index 47a33e5..5bed97d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java
@@ -156,7 +156,7 @@
       sslEncryption = Boolean.valueOf(new String(in, pos, length, "UTF-8"));
       pos += length +1;
 
-      // For easiness (no additional method), simpy compare PDU type to
+      // For easiness (no additional method), simply compare PDU type to
       // know if we have to read new parameters of V2
       if (in[0] == MSG_TYPE_REPL_SERVER_START)
       {
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java
index 45d87cf..519824d 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java
@@ -55,6 +55,7 @@
 import static org.opends.server.replication.protocol.OperationContext.SYNCHROCONTEXT;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
 
 /**
  * Test the conversions between the various protocol versions.
@@ -505,12 +506,44 @@
   
   @DataProvider(name = "createModifyDnData")
   public Object[][] createModifyDnData() {
+    
+    AttributeType type = DirectoryServer.getAttributeType("description");
+
+    Attribute attr1 = Attributes.create("description", "new value");
+    Modification mod1 = new Modification(ModificationType.REPLACE, attr1);
+    List<Modification> mods1 = new ArrayList<Modification>();
+    mods1.add(mod1);
+
+    Attribute attr2 = Attributes.empty("description");
+    Modification mod2 = new Modification(ModificationType.DELETE, attr2);
+    List<Modification> mods2 = new ArrayList<Modification>();
+    mods2.add(mod1);
+    mods2.add(mod2);
+
+    AttributeBuilder builder = new AttributeBuilder(type);
+    List<Modification> mods3 = new ArrayList<Modification>();
+    builder.add("string");
+    builder.add("value");
+    builder.add("again");
+    Attribute attr3 = builder.toAttribute();
+    Modification mod3 = new Modification(ModificationType.ADD, attr3);
+    mods3.add(mod3);
+
+    List<Modification> mods4 = new ArrayList<Modification>();
+    for (int i = 0; i < 10; i++)
+    {
+      Attribute attr = Attributes.create("description", "string"
+          + String.valueOf(i));
+      Modification mod = new Modification(ModificationType.ADD, attr);
+      mods4.add(mod);
+    }
+    
     return new Object[][] {
-        {"dc=test,dc=com", "dc=new", "11111111-1111-1111-1111-111111111111", "22222222-2222-2222-2222-222222222222", false, "dc=change", false, AssuredMode.SAFE_DATA_MODE, (byte)0},
-        {"dc=test,dc=com", "dc=new", "33333333-3333-3333-3333-333333333333", "44444444-4444-4444-4444-444444444444", true, "dc=change", true, AssuredMode.SAFE_READ_MODE, (byte)1},
-        {"dc=test,dc=com", "dc=new", "55555555-5555-5555-5555-555555555555", "66666666-6666-6666-6666-666666666666", false, null, true, AssuredMode.SAFE_READ_MODE, (byte)3},
+        {"dc=test,dc=com", "dc=new", "11111111-1111-1111-1111-111111111111", "22222222-2222-2222-2222-222222222222", false, "dc=change", mods1, false, AssuredMode.SAFE_DATA_MODE, (byte)0},
+        {"dc=test,dc=com", "dc=new", "33333333-3333-3333-3333-333333333333", "44444444-4444-4444-4444-444444444444", true, "dc=change", mods2, true, AssuredMode.SAFE_READ_MODE, (byte)1},
+        {"dc=test,dc=com", "dc=new", "55555555-5555-5555-5555-555555555555", "66666666-6666-6666-6666-666666666666", false, null, mods3, true, AssuredMode.SAFE_READ_MODE, (byte)3},
         {"dc=delete,dc=an,dc=entry,dc=with,dc=a,dc=long dn",
-                   "dc=new", "77777777-7777-7777-7777-777777777777", "88888888-8888-8888-8888-888888888888",true, null, true, AssuredMode.SAFE_DATA_MODE, (byte)99},
+                   "dc=new", "77777777-7777-7777-7777-777777777777", "88888888-8888-8888-8888-888888888888",true, null, mods4, true, AssuredMode.SAFE_DATA_MODE, (byte)99},
         };
   }
 
@@ -519,10 +552,10 @@
    * using protocol V1 and V2 are working.
    */
   @Test(dataProvider = "createModifyDnData")
-  public void modifyDnTest(String rawDN, String newRdn, String uid, String newParentUid,
+  public void modifyDnMsgTest(String rawDN, String newRdn, String uid, String newParentUid,
                                    boolean deleteOldRdn, String newSuperior,
-                                   boolean isAssured, AssuredMode assuredMode,
-                                   byte safeDataLevel)
+                                   List<Modification> mods, boolean isAssured,
+                                   AssuredMode assuredMode, byte safeDataLevel)
          throws Exception
   {
     // Create V2 message
@@ -530,7 +563,7 @@
                                       (short) 596, (short) 13);
     ModifyDNMsg msg = new ModifyDNMsg(rawDN, cn, uid,
                      newParentUid, deleteOldRdn,
-                     newSuperior, newRdn);
+                     newSuperior, newRdn, mods);
 
     msg.setAssured(isAssured);
     msg.setAssuredMode(assuredMode);
@@ -558,7 +591,7 @@
     assertEquals(newMsg.getNewSuperiorId(), msg.getNewSuperiorId());
     assertEquals(newMsg.deleteOldRdn(), msg.deleteOldRdn());
 
-    //        Create a modDn operation from each message to compare mods (kept encoded in messages)
+    //        Create a modDn operation from each message to compare fields)
     Operation op = msg.createOperation(connection);
     Operation generatedOperation = newMsg.createOperation(connection);
 
@@ -570,15 +603,17 @@
     assertEquals(modDnOpBasis.getRawEntryDN(), genModDnOpBasis.getRawEntryDN());
     assertEquals( modDnOpBasis.getAttachment(SYNCHROCONTEXT),
                   genModDnOpBasis.getAttachment(SYNCHROCONTEXT));
-    assertEquals(modDnOpBasis.getModifications(), genModDnOpBasis.getModifications());
 
     // Check default value for only V2 fields
     assertEquals(newMsg.getAssuredMode(), AssuredMode.SAFE_DATA_MODE);
     assertEquals(newMsg.getSafeDataLevel(), (byte)-1);
+    assertEquals(modDnOpBasis.getModifications(), mods);
+    assertTrue(genModDnOpBasis.getModifications() == null);
 
     // Set again only V2 fields
     newMsg.setAssuredMode(assuredMode);
     newMsg.setSafeDataLevel(safeDataLevel);
+    newMsg.setMods(mods);
 
     // Serialize in V2 msg
     ModifyDNMsg v2Msg = (ModifyDNMsg)ReplicationMsg.generateMsg(newMsg.getBytes());
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
index 1946193..04d1417 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
@@ -318,21 +318,54 @@
 
   @DataProvider(name = "createModifyDnData")
   public Object[][] createModifyDnData() {
+
+    AttributeType type = DirectoryServer.getAttributeType("description");
+
+    Attribute attr1 = Attributes.create("description", "new value");
+    Modification mod1 = new Modification(ModificationType.REPLACE, attr1);
+    List<Modification> mods1 = new ArrayList<Modification>();
+    mods1.add(mod1);
+
+    Attribute attr2 = Attributes.empty("description");
+    Modification mod2 = new Modification(ModificationType.DELETE, attr2);
+    List<Modification> mods2 = new ArrayList<Modification>();
+    mods2.add(mod1);
+    mods2.add(mod2);
+
+    AttributeBuilder builder = new AttributeBuilder(type);
+    List<Modification> mods3 = new ArrayList<Modification>();
+    builder.add("string");
+    builder.add("value");
+    builder.add("again");
+    Attribute attr3 = builder.toAttribute();
+    Modification mod3 = new Modification(ModificationType.ADD, attr3);
+    mods3.add(mod3);
+
+    List<Modification> mods4 = new ArrayList<Modification>();
+    for (int i = 0; i < 10; i++)
+    {
+      Attribute attr = Attributes.create("description", "string"
+          + String.valueOf(i));
+      Modification mod = new Modification(ModificationType.ADD, attr);
+      mods4.add(mod);
+    }
+
     return new Object[][] {
-        {"dc=test,dc=com", "dc=new", false, "dc=change", false, AssuredMode.SAFE_DATA_MODE, (byte)0},
-        {"dc=test,dc=com", "dc=new", true, "dc=change", true, AssuredMode.SAFE_READ_MODE, (byte)1},
+        {"dc=test,dc=com", "dc=new", false, "dc=change", mods1, false, AssuredMode.SAFE_DATA_MODE, (byte)0},
+        {"dc=test,dc=com", "dc=new", true, "dc=change", mods2, true, AssuredMode.SAFE_READ_MODE, (byte)1},
         // testNG does not like null argument so use "" for the newSuperior
         // instead of null
-        {"dc=test,dc=com", "dc=new", false, "", true, AssuredMode.SAFE_READ_MODE, (byte)3},
+        {"dc=test,dc=com", "dc=new", false, "", mods3, true, AssuredMode.SAFE_READ_MODE, (byte)3},
         {"dc=delete,dc=an,dc=entry,dc=with,dc=a,dc=long dn",
-                   "dc=new",true, "", true, AssuredMode.SAFE_DATA_MODE, (byte)99},
+                   "dc=new", true, "", mods4, true, AssuredMode.SAFE_DATA_MODE, (byte)99},
         };
   }
 
   @Test(dataProvider = "createModifyDnData")
-  public void modifyDnTest(String rawDN, String newRdn,
+  public void modifyDnMsgTest(String rawDN, String newRdn,
                                    boolean deleteOldRdn, String newSuperior,
-                                   boolean isAssured, AssuredMode assuredMode,
+                                   List<Modification> mods, boolean isAssured,
+                                   AssuredMode assuredMode,
                                byte safeDataLevel)
          throws Exception
   {
@@ -349,6 +382,8 @@
         new ModifyDnContext(cn, "uniqueid", "newparentId"));
     LocalBackendModifyDNOperation localOp =
       new LocalBackendModifyDNOperation(op);
+    for (Modification mod : mods)
+      localOp.addModification(mod);
     ModifyDNMsg msg = new ModifyDNMsg(localOp);
 
     msg.setAssured(isAssured);
@@ -363,14 +398,21 @@
     assertEquals(generatedMsg.getAssuredMode(), assuredMode);
     assertEquals(generatedMsg.getSafeDataLevel(), safeDataLevel);
 
+    Operation oriOp = msg.createOperation(connection);
     Operation generatedOperation = generatedMsg.createOperation(connection);
-    ModifyDNOperationBasis mod2 = (ModifyDNOperationBasis) generatedOperation;
+
+    assertEquals(oriOp.getClass(), ModifyDNOperationBasis.class);
+    assertEquals(generatedOperation.getClass(), ModifyDNOperationBasis.class);
+
+    ModifyDNOperationBasis moddn1 = (ModifyDNOperationBasis) oriOp;
+    ModifyDNOperationBasis moddn2 = (ModifyDNOperationBasis) generatedOperation;
 
     assertEquals(msg.getChangeNumber(), generatedMsg.getChangeNumber());
-    assertEquals(op.getRawEntryDN(), mod2.getRawEntryDN());
-    assertEquals(op.getRawNewRDN(), mod2.getRawNewRDN());
-    assertEquals(op.deleteOldRDN(), mod2.deleteOldRDN());
-    assertEquals(op.getRawNewSuperior(), mod2.getRawNewSuperior());
+    assertEquals(moddn1.getRawEntryDN(), moddn2.getRawEntryDN());
+    assertEquals(moddn1.getRawNewRDN(), moddn2.getRawNewRDN());
+    assertEquals(moddn1.deleteOldRDN(), moddn2.deleteOldRDN());
+    assertEquals(moddn1.getRawNewSuperior(), moddn2.getRawNewSuperior());
+    assertEquals(moddn1.getModifications(), moddn2.getModifications());
 
     // Create an update message from this op
     ModifyDNMsg updateMsg = (ModifyDNMsg) UpdateMsg.generateMsg(localOp);

--
Gitblit v1.10.0