From 4c7d89a5b8f1503bf63988ae13d47d269e1615de 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"

---
 opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java |   90 ++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 84 insertions(+), 6 deletions(-)

diff --git a/opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java b/opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java
index 1a3cd39..c146b2a 100644
--- a/opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java
+++ b/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;
   }
 
   /**

--
Gitblit v1.10.0