From 691d99dba67f7f5a89135e3c129b006b3c956b1e Mon Sep 17 00:00:00 2001
From: pgamba <pgamba@localhost>
Date: Tue, 06 Oct 2009 12:34:32 +0000
Subject: [PATCH] Entry attributes for ECL - Protocol V4

---
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyMsg.java                                         |  217 ++
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java                |   47 
 opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java                               |  111 +
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java    |  538 ++++++
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java                |    2 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java |  320 +++-
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java                                         |   89 +
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java                                    |    4 
 opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java                             |    5 
 opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerReader.java                                        |   29 
 opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerHandler.java                            |    3 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/DomainFakeCfg.java               |   15 
 opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java                            |   13 
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java                                     |  399 ++++-
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java              |  337 ++++
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java            |   24 
 opendj-sdk/opends/src/server/org/opends/server/replication/common/DSInfo.java                                              |   25 
 opendj-sdk/opends/src/server/org/opends/server/types/RawAttribute.java                                                     |    2 
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java                                   |   69 
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ProtocolVersion.java                                   |   11 
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java                                       |  493 ++++--
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/StartSessionMsg.java                                   |  374 ++++-
 opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java                                  |   20 
 opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/ReplicationDomainConfiguration.xml                            |   43 
 opendj-sdk/opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java                   |  135 -
 opendj-sdk/opends/src/server/org/opends/server/replication/server/DataServerHandler.java                                   |    7 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeReplicationDomain.java      |    4 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java      |   51 
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/TopologyMsg.java                                       |  284 ++-
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AddMsg.java                                            |  486 ++++--
 opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java                                  |   32 
 opendj-sdk/opends/resource/schema/02-config.ldif                                                                           |    7 
 32 files changed, 3,097 insertions(+), 1,099 deletions(-)

diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index d1010ec..8f0c851 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -2449,6 +2449,10 @@
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
   SINGLE-VALUE
   X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.601
+  NAME 'ds-cfg-ecl-include'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+  X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
   NAME 'ds-cfg-access-control-handler'
   SUP top
@@ -3011,7 +3015,8 @@
         ds-cfg-fractional-exclude $
         ds-cfg-fractional-include $
         ds-cfg-solve-conflicts $
-        ds-cfg-changetime-heartbeat-interval)
+        ds-cfg-changetime-heartbeat-interval $
+        ds-cfg-ecl-include )
   X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.58
   NAME 'ds-cfg-length-based-password-validator'
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/ReplicationDomainConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/ReplicationDomainConfiguration.xml
index a8439b5..154fd70 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/ReplicationDomainConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/ReplicationDomainConfiguration.xml
@@ -487,4 +487,47 @@
       </ldap:attribute>
     </adm:profile>
   </adm:property>
+  <adm:property name="ecl-include" multi-valued="true" mandatory="false">
+    <adm:synopsis>
+      Allows to include some target entry attributes in the ECL.
+    </adm:synopsis>
+    <adm:description>
+      Specifies an attribute that will be included in every External Change Log
+      entry related to this replication domain.
+    </adm:description>
+    <adm:default-behavior>
+      <adm:undefined/>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:string>
+        <adm:pattern>
+          <!-- This java regex is mostly derived from keystring BNF definition
+          that can be found in RFC 2252, section "4.1. Common Encoding Aspects".
+          This can be read as: (oid|\*):oid(,oid)*+
+          -->
+          <adm:regex>^((([a-zA-Z]([a-zA-Z]|[0-9]|-|;)*+)|(0|([1-9]([0-9])*+))(\\.(0|([1-9]([0-9])*+)))*+)|\\*):(([a-zA-Z]([a-zA-Z]|[0-9]|-|;)*+)|(0|([1-9]([0-9])*+))(\\.(0|([1-9]([0-9])*+)))*+)(,(([a-zA-Z]([a-zA-Z]|[0-9]|-|;)*+)|(0|([1-9]([0-9])*+))(\\.(0|([1-9]([0-9])*+)))*+))*+$</adm:regex>
+          <adm:usage>Syntax:
+          className:attributeName[,attributeName]
+          or
+          *:attributeName[,attributeName].
+          Note that any class (className) or attribute (attributeName) definition can be replaced with its OID definition.
+          Examples:
+          inetOrgPerson:photo,jpegPhoto : 'photo' and 'jpegPhoto' attributes of any entry of type 'inetOrgPerson' class.
+          This can also be 2.16.840.1.113730.3.2.2:0.9.2342.19200300.100.1.7,0.9.2342.19200300.100.1.60 or a mix.
+          *:jpegPhoto : the 'jpegPhoto' attribute of any entry that has this attribute.
+          This can also be *:0.9.2342.19200300.100.1.60
+          </adm:usage>
+          <adm:synopsis>
+            Defines attribute(s) of one particular class or of all possible
+            classes, to exclude from the replication.
+          </adm:synopsis>
+        </adm:pattern>
+      </adm:string>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-ecl-include</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
 </adm:managed-object>
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/common/DSInfo.java b/opendj-sdk/opends/src/server/org/opends/server/replication/common/DSInfo.java
index ffda986..32e00f7 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/common/DSInfo.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/common/DSInfo.java
@@ -22,12 +22,14 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.replication.common;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * This class holds information about a DS connected to the topology. This
@@ -57,6 +59,8 @@
   // Group id
   private byte groupId = (byte) -1;
 
+  private Set<String> eclIncludes = new HashSet<String>();
+
   /**
    * Creates a new instance of DSInfo with every given info.
    *
@@ -69,10 +73,11 @@
    * @param safeDataLevel DS safe data level
    * @param groupId DS group id
    * @param refUrls DS exported referrals URLs
+   * @param eclIncludes The list of entry attributes to include in the ECL.
    */
   public DSInfo(short dsId, short rsId, long generationId, ServerStatus status,
     boolean assuredFlag, AssuredMode assuredMode, byte safeDataLevel,
-    byte groupId, List<String> refUrls)
+    byte groupId, List<String> refUrls, Set<String> eclIncludes)
   {
 
     this.dsId = dsId;
@@ -84,6 +89,7 @@
     this.safeDataLevel = safeDataLevel;
     this.groupId = groupId;
     this.refUrls = refUrls;
+    this.eclIncludes = eclIncludes;
   }
 
   /**
@@ -168,6 +174,15 @@
   }
 
   /**
+   * Get the entry attributes to be included in the ECL.
+   * @return a.
+   */
+  public Set<String> getEclIncludes()
+  {
+    return eclIncludes;
+  }
+
+  /**
    * Test if the passed object is equal to this one.
    * @param obj The object to test
    * @return True if both objects are equal
@@ -190,7 +205,8 @@
         (assuredMode == dsInfo.getAssuredMode()) &&
         (safeDataLevel == dsInfo.getSafeDataLevel()) &&
         (groupId == dsInfo.getGroupId()) &&
-        (refUrls.equals(dsInfo.getRefUrls())));
+        (refUrls.equals(dsInfo.getRefUrls())) &&
+        (eclIncludes.equals(dsInfo.getEclIncludes())));
     } else
     {
       return false;
@@ -214,6 +230,7 @@
       73 * hash + (this.assuredMode != null ? this.assuredMode.hashCode() : 0);
     hash = 73 * hash + this.safeDataLevel;
     hash = 73 * hash + (this.refUrls != null ? this.refUrls.hashCode() : 0);
+    hash = 73 * hash + (this.eclIncludes != null ? eclIncludes.hashCode() : 0);
     hash = 73 * hash + this.groupId;
     return hash;
   }
@@ -244,6 +261,8 @@
     sb.append(groupId);
     sb.append("\nReferral URLs: ");
     sb.append(refUrls);
+    sb.append("\nECL Include: ");
+    sb.append(eclIncludes);
     return sb.toString();
   }
 
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 7a4a8d4..f1270e2 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
@@ -38,19 +38,12 @@
 import static org.opends.server.util.StaticUtils.getFileForPath;
 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
 
-import org.opends.server.replication.protocol.LDAPUpdateMsg;
-
-import org.opends.server.replication.service.ReplicationMonitor;
-
-import java.util.Collection;
-
-import org.opends.server.types.Attributes;
-
 import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -99,7 +92,6 @@
 import org.opends.server.protocols.ldap.LDAPAttribute;
 import org.opends.server.protocols.ldap.LDAPFilter;
 import org.opends.server.protocols.ldap.LDAPModification;
-import org.opends.server.replication.service.ReplicationDomain;
 import org.opends.server.replication.common.AssuredMode;
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.replication.common.ChangeNumberGenerator;
@@ -109,6 +101,8 @@
 import org.opends.server.replication.protocol.AddContext;
 import org.opends.server.replication.protocol.AddMsg;
 import org.opends.server.replication.protocol.DeleteContext;
+import org.opends.server.replication.protocol.DeleteMsg;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
 import org.opends.server.replication.protocol.ModifyContext;
 import org.opends.server.replication.protocol.ModifyDNMsg;
 import org.opends.server.replication.protocol.ModifyDnContext;
@@ -117,6 +111,8 @@
 import org.opends.server.replication.protocol.ProtocolSession;
 import org.opends.server.replication.protocol.RoutableMsg;
 import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.replication.service.ReplicationDomain;
+import org.opends.server.replication.service.ReplicationMonitor;
 import org.opends.server.tasks.TaskUtils;
 import org.opends.server.types.AbstractOperation;
 import org.opends.server.types.Attribute;
@@ -124,10 +120,12 @@
 import org.opends.server.types.AttributeType;
 import org.opends.server.types.AttributeValue;
 import org.opends.server.types.AttributeValues;
+import org.opends.server.types.Attributes;
 import org.opends.server.types.ByteString;
 import org.opends.server.types.ConfigChangeResult;
 import org.opends.server.types.Control;
 import org.opends.server.types.DN;
+import org.opends.server.types.DebugLogLevel;
 import org.opends.server.types.DereferencePolicy;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
@@ -149,6 +147,10 @@
 import org.opends.server.types.SearchScope;
 import org.opends.server.types.SynchronizationProviderResult;
 import org.opends.server.types.operation.PluginOperation;
+import org.opends.server.types.operation.PostOperationAddOperation;
+import org.opends.server.types.operation.PostOperationDeleteOperation;
+import org.opends.server.types.operation.PostOperationModifyDNOperation;
+import org.opends.server.types.operation.PostOperationModifyOperation;
 import org.opends.server.types.operation.PostOperationOperation;
 import org.opends.server.types.operation.PreOperationAddOperation;
 import org.opends.server.types.operation.PreOperationDeleteOperation;
@@ -346,6 +348,7 @@
   // The operation should become a no-op
   private static final int FRACTIONAL_BECOME_NO_OP = 3;
 
+
   /**
    * The thread that periodically saves the ServerState of this
    * LDAPReplicationDomain in the database.
@@ -428,6 +431,8 @@
     setGroupId((byte)configuration.getGroupId());
     setURLs(configuration.getReferralsUrl());
 
+    setCfgEclInclude(configuration.getEclInclude());
+
     /*
      * Modify conflicts are solved for all suffixes but the schema suffix
      * because we don't want to store extra information in the schema
@@ -2200,6 +2205,14 @@
         }
         else
         {
+          try
+          {
+            addEntryAttributesForCL(msg,op);
+          }
+          catch(Exception e)
+          {
+            TRACER.debugCaught(DebugLogLevel.ERROR, e);
+          }
           // If assured replication is configured, this will prepare blocking
           // mechanism. If assured replication is disabled, this returns
           // immediately
@@ -4498,6 +4511,86 @@
   }
 
   /**
+   * Called by synchronize post op plugin in order to add the entry historized
+   * attributes to the UpdateMsg.
+   * @param msg
+   * @param op
+   * @throws DirectoryException
+   */
+  private void addEntryAttributesForCL(UpdateMsg msg,PostOperationOperation op)
+  throws DirectoryException
+  {
+    String[] entryAttributeNames =
+      getEclInclude().toArray(new String[0]);
+    ArrayList<Attribute> newattrs = new ArrayList<Attribute>();
+
+    if (op instanceof PostOperationDeleteOperation)
+    {
+      Entry entry = null;
+      PostOperationDeleteOperation delOp = (PostOperationDeleteOperation)op;
+      entry = delOp.getEntryToDelete();
+      for (String name : entryAttributeNames)
+      {
+        AttributeType atype = DirectoryServer.getAttributeType(name);
+        List<Attribute> attrs = entry.getAttribute(atype);
+        for (Attribute a : attrs)
+          newattrs.add(a);
+      }
+      ((DeleteMsg)msg).setEclIncludes(newattrs);
+    }
+    else if (op instanceof PostOperationModifyOperation)
+    {
+      Entry entry = null;
+      PostOperationModifyOperation modOp = (PostOperationModifyOperation)op;
+      entry = modOp.getCurrentEntry();
+      for (String name : entryAttributeNames)
+      {
+        AttributeType atype = DirectoryServer.getAttributeType(name);
+        List<Attribute> attrs = entry.getAttribute(atype);
+        for (Attribute a : attrs)
+          newattrs.add(a);
+      }
+      ((ModifyMsg)msg).setEclIncludes(newattrs);
+    }
+    else if (op instanceof PostOperationModifyDNOperation)
+    {
+      Entry entry = null;
+      PostOperationModifyDNOperation modDNOp =
+        (PostOperationModifyDNOperation)op;
+      entry = modDNOp.getOriginalEntry();
+      for (String name : entryAttributeNames)
+      {
+        AttributeType atype = DirectoryServer.getAttributeType(name);
+        List<Attribute> attrs = entry.getAttribute(atype);
+        for (Attribute a : attrs)
+          newattrs.add(a);
+      }
+      ((ModifyDNMsg)msg).setEclIncludes(newattrs);
+    }
+    else if (op instanceof PostOperationAddOperation)
+    {
+      Entry entry = null;
+      PostOperationAddOperation addOp = (PostOperationAddOperation)op;
+      entry = addOp.getEntryToAdd();
+      for (String name : entryAttributeNames)
+      {
+        AttributeType atype = DirectoryServer.getAttributeType(name);
+        List<Attribute> attrs = entry.getAttribute(atype);
+        if (attrs != null)
+        {
+          for (Attribute a : attrs)
+            newattrs.add(a);
+        }
+        else
+        {
+          // FIXME:ECL
+        }
+      }
+      ((AddMsg)msg).setEclIncludes(newattrs);
+    }
+  }
+
+  /**
    * Gets the fractional configuration of this domain.
    * @return The fractional configuration of this domain.
    */
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AddMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AddMsg.java
index eb9384e..83defc1 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AddMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AddMsg.java
@@ -41,7 +41,6 @@
 import org.opends.server.protocols.asn1.ASN1Writer;
 import org.opends.server.protocols.asn1.ASN1;
 import org.opends.server.protocols.asn1.ASN1Exception;
-import org.opends.server.protocols.asn1.ASN1Reader;
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.types.*;
 import org.opends.server.types.operation.PostOperationAddOperation;
@@ -55,7 +54,10 @@
  */
 public class AddMsg extends LDAPUpdateMsg
 {
+  // Attributes are managed encoded
   private byte[] encodedAttributes;
+
+  // Parent is managed decoded
   private String parentUniqueId;
 
   /**
@@ -68,12 +70,248 @@
           op.getRawEntryDN().toString());
 
     AddContext ctx = (AddContext) op.getAttachment(SYNCHROCONTEXT);
+
+    // Stores parentUniqueID not encoded
     this.parentUniqueId = ctx.getParentUid();
 
-    this.encodedAttributes =
-      encodeAttributes(op.getObjectClasses(),
-          op.getUserAttributes(),
-          op.getOperationalAttributes());
+    // Stores attributes encoded
+    this.encodedAttributes = encodeAttributes(op.getObjectClasses(),
+        op.getUserAttributes(), op.getOperationalAttributes());
+  }
+
+  /**
+   * Creates a new AddMessage.
+   *
+   * @param cn                    ChangeNumber of the add.
+   * @param dn                    DN of the added entry.
+   * @param uniqueId              The Unique identifier of the added entry.
+   * @param parentId              The unique Id of the parent of the added
+   *                              entry.
+   * @param objectClasses           objectclass of the added entry.
+   * @param userAttributes        user attributes of the added entry.
+   * @param operationalAttributes operational attributes of the added entry.
+   */
+  public AddMsg(ChangeNumber cn,
+                String dn,
+                String uniqueId,
+                String parentId,
+                Map<ObjectClass, String> objectClasses,
+                Map<AttributeType,List<Attribute>> userAttributes,
+                Map<AttributeType,List<Attribute>> operationalAttributes)
+  {
+    super (cn, uniqueId, dn);
+
+    // Stores parentUniqueID not encoded
+    this.parentUniqueId = parentId;
+
+    // Stores attributes encoded
+    this.encodedAttributes = encodeAttributes(objectClasses, userAttributes,
+        operationalAttributes);
+  }
+
+
+  /**
+   * Creates a new AddMessage.
+   *
+   * @param cn ChangeNumber of the add.
+   * @param dn DN of the added entry.
+   * @param uniqueId The Unique identifier of the added entry.
+   * @param parentId The unique Id of the parent of the added entry.
+   * @param objectClass objectclass of the added entry.
+   * @param userAttributes user attributes of the added entry.
+   * @param operationalAttributes operational attributes of the added entry.
+   */
+  public AddMsg(ChangeNumber cn,
+                String dn,
+                String uniqueId,
+                String parentId,
+                Attribute objectClass,
+                Collection<Attribute> userAttributes,
+                Collection<Attribute> operationalAttributes)
+  {
+    super (cn, uniqueId, dn);
+
+    // Stores parentUniqueID not encoded
+    this.parentUniqueId = parentId;
+
+    // Stores attributes encoded
+    this.encodedAttributes = encodeAttributes(objectClass, userAttributes,
+        operationalAttributes);
+  }
+
+  /**
+   * Creates a new Add message from a byte[].
+   *
+   * @param in The byte[] from which the operation must be read.
+   * @throws DataFormatException The input byte[] is not a valid AddMsg
+   * @throws UnsupportedEncodingException If UTF8 is not supported by the jvm
+   */
+  public AddMsg(byte[] in) throws DataFormatException,
+                                  UnsupportedEncodingException
+  {
+    byte[] allowedPduTypes = new byte[2];
+    allowedPduTypes[0] = MSG_TYPE_ADD;
+    allowedPduTypes[1] = MSG_TYPE_ADD_V1;
+    int pos = decodeHeader(allowedPduTypes, in);
+
+    // protocol version has been read as part of the header
+    if (protocolVersion <= 3)
+      decodeBody_V123(in, pos);
+    else
+    {
+      decodeBody_V4(in, pos);
+    }
+    if (protocolVersion==ProtocolVersion.getCurrentVersion())
+    {
+      bytes = in;
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public AddOperationBasis createOperation(
+      InternalClientConnection connection, String newDn)
+  throws LDAPException, ASN1Exception
+  {
+    ArrayList<RawAttribute> attr = decodeRawAttributes(encodedAttributes);
+
+    AddOperationBasis add =  new AddOperationBasis(connection,
+        InternalClientConnection.nextOperationID(),
+        InternalClientConnection.nextMessageID(), null,
+        ByteString.valueOf(newDn), attr);
+    AddContext ctx = new AddContext(getChangeNumber(), getUniqueId(),
+        parentUniqueId);
+    add.setAttachment(SYNCHROCONTEXT, ctx);
+    return add;
+  }
+
+  // ============
+  // Msg encoding
+  // ============
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes_V1() throws UnsupportedEncodingException
+  {
+    int bodyLength = encodedAttributes.length;
+    byte[] byteParentId = null;
+    if (parentUniqueId != null)
+    {
+      byteParentId = parentUniqueId.getBytes("UTF-8");
+      bodyLength += byteParentId.length + 1;
+    }
+    else
+    {
+      bodyLength += 1;
+    }
+
+    /* encode the header in a byte[] large enough to also contain the mods */
+    byte [] resultByteArray = encodeHeader_V1(MSG_TYPE_ADD_V1, bodyLength);
+
+    int pos = resultByteArray.length - bodyLength;
+
+    if (byteParentId != null)
+      pos = addByteArray(byteParentId, resultByteArray, pos);
+    else
+      resultByteArray[pos++] = 0;
+
+    /* put the attributes */
+    for (int i=0; i<encodedAttributes.length; i++,pos++)
+    {
+      resultByteArray[pos] = encodedAttributes[i];
+    }
+    return resultByteArray;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes_V23() throws UnsupportedEncodingException
+  {
+    // Put together the different encoded pieces
+    int bodyLength = encodedAttributes.length;
+
+    // Compute the total length of the body
+    byte[] byteParentId = null;
+    if (parentUniqueId != null)
+    {
+      // Encode parentID now to get the length of the encoded bytes
+      byteParentId = parentUniqueId.getBytes("UTF-8");
+      bodyLength += byteParentId.length + 1;
+    }
+    else
+    {
+      bodyLength += 1;
+    }
+
+    /* encode the header in a byte[] large enough to also contain the mods */
+    byte [] resultByteArray = encodeHeader(MSG_TYPE_ADD, bodyLength);
+
+    int pos = resultByteArray.length - bodyLength;
+
+    if (byteParentId != null)
+      pos = addByteArray(byteParentId, resultByteArray, pos);
+    else
+      resultByteArray[pos++] = 0;
+
+    /* put the attributes */
+    for (int i=0; i<encodedAttributes.length; i++,pos++)
+    {
+      resultByteArray[pos] = encodedAttributes[i];
+    }
+    return resultByteArray;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes_V4() throws UnsupportedEncodingException
+  {
+    // Put together the different encoded pieces
+    int bodyLength = 0;
+
+    // Compute the total length of the body
+    byte[] byteParentId = null;
+    if (parentUniqueId != null)
+    {
+      // Encode parentID now to get the length of the encoded bytes
+      byteParentId = parentUniqueId.getBytes("UTF-8");
+      bodyLength += byteParentId.length + 1;
+    }
+    else
+    {
+      bodyLength += 1;
+    }
+
+    byte[] byteAttrLen =
+      String.valueOf(encodedAttributes.length).getBytes("UTF-8");
+    bodyLength += byteAttrLen.length + 1;
+    bodyLength += encodedAttributes.length + 1;
+
+    byte[] byteEntryAttrLen =
+      String.valueOf(encodedEclIncludes.length).getBytes("UTF-8");
+    bodyLength += byteEntryAttrLen.length + 1;
+    bodyLength += encodedEclIncludes.length + 1;
+
+    /* encode the header in a byte[] large enough to also contain the mods */
+    byte [] encodedMsg = encodeHeader(MSG_TYPE_ADD, bodyLength);
+
+    int pos = encodedMsg.length - bodyLength;
+    if (byteParentId != null)
+      pos = addByteArray(byteParentId, encodedMsg, pos);
+    else
+      encodedMsg[pos++] = 0;
+    pos = addByteArray(byteAttrLen, encodedMsg, pos);
+    pos = addByteArray(encodedAttributes, encodedMsg, pos);
+    pos = addByteArray(byteEntryAttrLen, encodedMsg, pos);
+    pos = addByteArray(encodedEclIncludes, encodedMsg, pos);
+    return encodedMsg;
   }
 
   private byte[] encodeAttributes(
@@ -128,59 +366,13 @@
     return byteBuilder.toByteArray();
   }
 
-  /**
-   * Creates a new AddMessage.
-   *
-   * @param cn                    ChangeNumber of the add.
-   * @param dn                    DN of the added entry.
-   * @param uniqueId              The Unique identifier of the added entry.
-   * @param parentId              The unique Id of the parent of the added
-   *                              entry.
-   * @param objectClasses           objectclass of the added entry.
-   * @param userAttributes        user attributes of the added entry.
-   * @param operationalAttributes operational attributes of the added entry.
-   */
-  public AddMsg(ChangeNumber cn,
-                String dn,
-                String uniqueId,
-                String parentId,
-                Map<ObjectClass, String> objectClasses,
-                Map<AttributeType,List<Attribute>> userAttributes,
-                Map<AttributeType,List<Attribute>> operationalAttributes)
+  private byte[] encodeAttributes(
+      Attribute objectClass,
+      Collection<Attribute> userAttributes,
+      Collection<Attribute> operationalAttributes)
   {
-    super (cn, uniqueId, dn);
-    this.parentUniqueId = parentId;
-
-    encodedAttributes = encodeAttributes(objectClasses,
-                                        userAttributes, operationalAttributes);
-  }
-
-
-  /**
-   * Creates a new AddMessage.
-   *
-   * @param cn ChangeNumber of the add.
-   * @param dn DN of the added entry.
-   * @param uniqueId The Unique identifier of the added entry.
-   * @param parentId The unique Id of the parent of the added entry.
-   * @param objectClass objectclass of the added entry.
-   * @param userAttributes user attributes of the added entry.
-   * @param operationalAttributes operational attributes of the added entry.
-   */
-  public AddMsg(ChangeNumber cn,
-                String dn,
-                String uniqueId,
-                String parentId,
-                Attribute objectClass,
-                Collection<Attribute> userAttributes,
-                Collection<Attribute> operationalAttributes)
-  {
-    super (cn, uniqueId, dn);
-    this.parentUniqueId = parentId;
-
     ByteStringBuilder byteBuilder = new ByteStringBuilder();
     ASN1Writer writer = ASN1.getWriter(byteBuilder);
-
     try
     {
       new LDAPAttribute(objectClass).write(writer);
@@ -196,25 +388,16 @@
     {
       // Do something
     }
-
-    encodedAttributes = byteBuilder.toByteArray();
+    return byteBuilder.toByteArray();
   }
 
-  /**
-   * Creates a new Add message from a byte[].
-   *
-   * @param in The byte[] from which the operation must be read.
-   * @throws DataFormatException The input byte[] is not a valid AddMsg
-   * @throws UnsupportedEncodingException If UTF8 is not supported by the jvm
-   */
-  public AddMsg(byte[] in) throws DataFormatException,
-                                  UnsupportedEncodingException
-  {
-    byte[] allowedPduTypes = new byte[2];
-    allowedPduTypes[0] = MSG_TYPE_ADD;
-    allowedPduTypes[1] = MSG_TYPE_ADD_V1;
-    int pos = decodeHeader(allowedPduTypes, in);
+  // ============
+  // Msg decoding
+  // ============
 
+  private void decodeBody_V123(byte[] in, int pos)
+  throws DataFormatException, UnsupportedEncodingException
+  {
     // read the parent unique Id
     int length = getNextLength(in, pos);
     if (length != 0)
@@ -228,7 +411,7 @@
       pos += 1;
     }
 
-    // Read the attributes : all the remaining bytes
+    // Read/Don't decode attributes : all the remaining bytes
     encodedAttributes = new byte[in.length-pos];
     int i =0;
     while (pos<in.length)
@@ -237,72 +420,63 @@
     }
   }
 
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public AddOperationBasis createOperation(
-         InternalClientConnection connection, String newDn)
-         throws LDAPException, ASN1Exception
+  private void decodeBody_V4(byte[] in, int pos)
+  throws DataFormatException, UnsupportedEncodingException
   {
-    ASN1Reader asn1Reader = ASN1.getReader(encodedAttributes);
-    ArrayList<RawAttribute> attr = new ArrayList<RawAttribute>();
-
-    while(asn1Reader.hasNextElement())
+    // read the parent unique Id
+    int length = getNextLength(in, pos);
+    if (length != 0)
     {
-      attr.add(LDAPAttribute.decode(asn1Reader));
-    }
-
-    AddOperationBasis add =  new AddOperationBasis(connection,
-                            InternalClientConnection.nextOperationID(),
-                            InternalClientConnection.nextMessageID(), null,
-        ByteString.valueOf(newDn), attr);
-    AddContext ctx = new AddContext(getChangeNumber(), getUniqueId(),
-                                    parentUniqueId);
-    add.setAttachment(SYNCHROCONTEXT, ctx);
-    return add;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public byte[] getBytes() throws UnsupportedEncodingException
-  {
-    if (bytes == null)
-    {
-      int length = encodedAttributes.length;
-      byte[] byteParentId = null;
-      if (parentUniqueId != null)
-      {
-        byteParentId = parentUniqueId.getBytes("UTF-8");
-        length += byteParentId.length + 1;
-      }
-      else
-      {
-        length += 1;
-      }
-
-      /* encode the header in a byte[] large enough to also contain the mods */
-      byte [] resultByteArray = encodeHeader(MSG_TYPE_ADD, length);
-
-      int pos = resultByteArray.length - length;
-
-      if (byteParentId != null)
-        pos = addByteArray(byteParentId, resultByteArray, pos);
-      else
-        resultByteArray[pos++] = 0;
-
-      /* put the attributes */
-      for (int i=0; i<encodedAttributes.length; i++,pos++)
-      {
-        resultByteArray[pos] = encodedAttributes[i];
-      }
-      return resultByteArray;
+      parentUniqueId = new String(in, pos, length, "UTF-8");
+      pos += length + 1;
     }
     else
     {
-      return bytes;
+      parentUniqueId = null;
+      pos += 1;
+    }
+
+    // Read attr len
+    length = getNextLength(in, pos);
+    int attrLen = Integer.valueOf(new String(in, pos, length,"UTF-8"));
+    pos += length + 1;
+
+    // Read/Don't decode attributes
+    this.encodedAttributes = new byte[attrLen];
+    try
+    {
+      System.arraycopy(in, pos, encodedAttributes, 0, attrLen);
+    } catch (IndexOutOfBoundsException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    } catch (ArrayStoreException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    } catch (NullPointerException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    }
+    pos += attrLen + 1;
+
+    // Read ecl attr len
+    length = getNextLength(in, pos);
+    int eclAttrLen = Integer.valueOf(new String(in, pos, length,"UTF-8"));
+    pos += length + 1;
+
+    // Read/Don't decode entry attributes
+    encodedEclIncludes = new byte[eclAttrLen];
+    try
+    {
+      System.arraycopy(in, pos, encodedEclIncludes, 0, eclAttrLen);
+    } catch (IndexOutOfBoundsException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    } catch (ArrayStoreException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    } catch (NullPointerException e)
+    {
+      throw new DataFormatException(e.getMessage());
     }
   }
 
@@ -371,13 +545,7 @@
    */
   public List<Attribute> getAttributes() throws LDAPException, ASN1Exception
   {
-    List<Attribute> attrs = new ArrayList<Attribute>();
-
-    ASN1Reader reader = ASN1.getReader(encodedAttributes);
-
-    while (reader.hasNextElement())
-      attrs.add(LDAPAttribute.decode(reader).toAttribute());
-
+    List<Attribute> attrs = decodeAttributes(encodedAttributes);
     return attrs;
   }
 
@@ -406,42 +574,8 @@
   @Override
   public int size()
   {
-    return encodedAttributes.length + 40;
+    return encodedAttributes.length + + encodedEclIncludes.length
+      + headerSize();
   }
 
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public byte[] getBytes_V1() throws UnsupportedEncodingException
-  {
-    int length = encodedAttributes.length;
-    byte[] byteParentId = null;
-    if (parentUniqueId != null)
-    {
-      byteParentId = parentUniqueId.getBytes("UTF-8");
-      length += byteParentId.length + 1;
-    }
-    else
-    {
-      length += 1;
-    }
-
-    /* encode the header in a byte[] large enough to also contain the mods */
-    byte [] resultByteArray = encodeHeader_V1(MSG_TYPE_ADD_V1, length);
-
-    int pos = resultByteArray.length - length;
-
-    if (byteParentId != null)
-      pos = addByteArray(byteParentId, resultByteArray, pos);
-    else
-      resultByteArray[pos++] = 0;
-
-    /* put the attributes */
-    for (int i=0; i<encodedAttributes.length; i++,pos++)
-    {
-      resultByteArray[pos] = encodedAttributes[i];
-    }
-    return resultByteArray;
-  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java
index 070c05b..fd1a111 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java
@@ -80,7 +80,11 @@
     byte[] allowedPduTypes = new byte[2];
     allowedPduTypes[0] = MSG_TYPE_DELETE;
     allowedPduTypes[1] = MSG_TYPE_DELETE_V1;
-    decodeHeader(allowedPduTypes, in);
+    int pos = decodeHeader(allowedPduTypes, in);
+
+    // protocol version has been read as part of the header
+    if (protocolVersion >= 4)
+      decodeBody_V4(in, pos);
   }
 
 
@@ -92,27 +96,83 @@
          InternalClientConnection connection, String newDn)
   {
     DeleteOperationBasis del =  new DeleteOperationBasis(connection,
-                               InternalClientConnection.nextOperationID(),
-                               InternalClientConnection.nextMessageID(), null,
+        InternalClientConnection.nextOperationID(),
+        InternalClientConnection.nextMessageID(), null,
         ByteString.valueOf(newDn));
     DeleteContext ctx = new DeleteContext(getChangeNumber(), getUniqueId());
     del.setAttachment(SYNCHROCONTEXT, ctx);
     return del;
   }
 
+  // ============
+  // Msg encoding
+  // ============
+
+  /**
+   * {@inheritDoc}
+   */
+  public byte[] getBytes_V1() throws UnsupportedEncodingException
+  {
+    return encodeHeader_V1(MSG_TYPE_DELETE_V1, 0);
+  }
+
   /**
    * {@inheritDoc}
    */
   @Override
-  public byte[] getBytes() throws UnsupportedEncodingException
+  public byte[] getBytes_V23() throws UnsupportedEncodingException
   {
-    if (bytes == null)
+    return encodeHeader(MSG_TYPE_DELETE, 0);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes_V4() throws UnsupportedEncodingException
+  {
+    // Put together the different encoded pieces
+    int bodyLength = 0;
+
+    byte[] byteEntryAttrLen =
+      String.valueOf(encodedEclIncludes.length).getBytes("UTF-8");
+    bodyLength += byteEntryAttrLen.length + 1;
+    bodyLength += encodedEclIncludes.length + 1;
+
+    /* encode the header in a byte[] large enough to also contain the mods */
+    byte [] encodedMsg = encodeHeader(MSG_TYPE_DELETE, bodyLength);
+    int pos = encodedMsg.length - bodyLength;
+    pos = addByteArray(byteEntryAttrLen, encodedMsg, pos);
+    pos = addByteArray(encodedEclIncludes, encodedMsg, pos);
+    return encodedMsg;
+  }
+
+  // ============
+  // Msg decoding
+  // ============
+
+  private void decodeBody_V4(byte[] in, int pos)
+  throws DataFormatException, UnsupportedEncodingException
+  {
+    // Read ecl attr len
+    int length = getNextLength(in, pos);
+    int eclAttrLen = Integer.valueOf(new String(in, pos, length,"UTF-8"));
+    pos += length + 1;
+
+    // Read/Don't decode entry attributes
+    encodedEclIncludes = new byte[eclAttrLen];
+    try
     {
-     return encodeHeader(MSG_TYPE_DELETE, 0);
-    }
-    else
+      System.arraycopy(in, pos, encodedEclIncludes, 0, eclAttrLen);
+    } catch (IndexOutOfBoundsException e)
     {
-      return bytes;
+      throw new DataFormatException(e.getMessage());
+    } catch (ArrayStoreException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    } catch (NullPointerException e)
+    {
+      throw new DataFormatException(e.getMessage());
     }
   }
 
@@ -151,16 +211,7 @@
   @Override
   public int size()
   {
-    // The DeleteMsg size is mostly dependent on the DN and should never
-    // grow very large. It is therefore safe to assume an average of 40 bytes.
-    return 40;
+    return encodedEclIncludes.length + headerSize();
   }
 
-  /**
-   * {@inheritDoc}
-   */
-  public byte[] getBytes_V1() throws UnsupportedEncodingException
-  {
-    return encodeHeader_V1(MSG_TYPE_DELETE_V1, 0);
-  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java
index 3927cfd..18e2914 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java
@@ -27,14 +27,25 @@
 package org.opends.server.replication.protocol;
 
 import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.zip.DataFormatException;
 
+import org.opends.server.protocols.asn1.ASN1;
 import org.opends.server.protocols.asn1.ASN1Exception;
+import org.opends.server.protocols.asn1.ASN1Reader;
+import org.opends.server.protocols.asn1.ASN1Writer;
 import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.ldap.LDAPAttribute;
 import org.opends.server.replication.common.AssuredMode;
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.types.AbstractOperation;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.ByteSequenceReader;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ByteStringBuilder;
 import org.opends.server.types.LDAPException;
+import org.opends.server.types.RawAttribute;
 import org.opends.server.types.operation.PostOperationAddOperation;
 import org.opends.server.types.operation.PostOperationDeleteOperation;
 import org.opends.server.types.operation.PostOperationModifyDNOperation;
@@ -63,6 +74,11 @@
   protected byte[] bytes = null;
 
   /**
+   * Encoded form of entry attributes.
+   */
+  protected byte[] encodedEclIncludes = new byte[0];
+
+  /**
    * Creates a new UpdateMsg.
    */
   public LDAPUpdateMsg()
@@ -164,21 +180,6 @@
   }
 
   /**
-   * Do all the work necessary for the encoding.
-   *
-   * This is useful in case when one wants to perform this outside
-   * of a synchronized portion of code.
-   *
-   * This method is not synchronized and therefore not MT safe.
-   *
-   * @throws UnsupportedEncodingException when encoding fails.
-   */
-  public void encode() throws UnsupportedEncodingException
-  {
-    bytes = getBytes();
-  }
-
-  /**
    * Create and Operation from the message.
    *
    * @param   conn connection to use when creating the message
@@ -208,6 +209,26 @@
          InternalClientConnection conn, String newDn)
          throws LDAPException, ASN1Exception, DataFormatException;
 
+
+  // ============
+  // Msg encoding
+  // ============
+
+  /**
+   * Do all the work necessary for the encoding.
+   *
+   * This is useful in case when one wants to perform this outside
+   * of a synchronized portion of code.
+   *
+   * This method is not synchronized and therefore not MT safe.
+   *
+   * @throws UnsupportedEncodingException when encoding fails.
+   */
+  public void encode() throws UnsupportedEncodingException
+  {
+    bytes = getBytes();
+  }
+
   /**
    * Encode the common header for all the UpdateMsg. This uses the current
    * protocol version.
@@ -270,30 +291,6 @@
   }
 
   /**
-   * {@inheritDoc}
-   */
-  @Override
-  public byte[] getBytes(short reqProtocolVersion)
-    throws UnsupportedEncodingException
-  {
-    if (reqProtocolVersion == ProtocolVersion.REPLICATION_PROTOCOL_V1)
-      return getBytes_V1();
-    else
-      return getBytes();
-  }
-
-  /**
-   * Get the byte array representation of this Message. This uses the version
-   * 1 of the replication protocol (used for compatibility purpose).
-   *
-   * @return The byte array representation of this Message.
-   *
-   * @throws UnsupportedEncodingException  When the encoding of the message
-   *         failed because the UTF-8 encoding is not supported.
-   */
-  public abstract byte[] getBytes_V1() throws UnsupportedEncodingException;
-
-  /**
    * Encode the common header for all the UpdateMessage. This uses the version
    * 1 of the replication protocol (used for compatibility purpose).
    *
@@ -345,6 +342,111 @@
   }
 
   /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes()
+  throws UnsupportedEncodingException
+  {
+    // Encode in the current protocol version
+    if (bytes == null)
+    {
+      // this is the current version of the protocol
+      bytes = getBytes_V4();
+    }
+    return bytes;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes(short reqProtocolVersion)
+    throws UnsupportedEncodingException
+  {
+    if (reqProtocolVersion == ProtocolVersion.REPLICATION_PROTOCOL_V1)
+    {
+      return getBytes_V1();
+    }
+    else if (reqProtocolVersion <= ProtocolVersion.REPLICATION_PROTOCOL_V3)
+    {
+      return getBytes_V23();
+    }
+    else
+    {
+      // Encode in the current protocol version
+      if (bytes == null)
+      {
+        // this is the current version of the protocol
+        bytes = getBytes_V4();
+      }
+      return bytes;
+    }
+  }
+
+  /**
+   * Get the byte array representation of this Message. This uses the version
+   * 1 of the replication protocol (used for compatibility purpose).
+   *
+   * @return The byte array representation of this Message.
+   *
+   * @throws UnsupportedEncodingException  When the encoding of the message
+   *         failed because the UTF-8 encoding is not supported.
+   */
+  public abstract byte[] getBytes_V1() throws UnsupportedEncodingException;
+
+  /**
+   * Get the byte array representation of this Message. This uses the version
+   * 2 of the replication protocol (used for compatibility purpose).
+   *
+   * @return The byte array representation of this Message.
+   *
+   * @throws UnsupportedEncodingException  When the encoding of the message
+   *         failed because the UTF-8 encoding is not supported.
+   */
+  public abstract byte[] getBytes_V23() throws UnsupportedEncodingException;
+
+
+  /**
+   * Get the byte array representation of this Message. This uses the version
+   * 4 of the replication protocol (used for compatibility purpose).
+   *
+   * @return The byte array representation of this Message.
+   *
+   * @throws UnsupportedEncodingException  When the encoding of the message
+   *         failed because the UTF-8 encoding is not supported.
+   */
+  public abstract byte[] getBytes_V4() throws UnsupportedEncodingException;
+
+
+  /**
+   * Encode a list of attributes.
+   */
+   static private byte[] encodeAttributes(List<Attribute> attributes)
+   {
+     if (attributes==null)
+       return new byte[0];
+     try
+     {
+       ByteStringBuilder byteBuilder = new ByteStringBuilder();
+       ASN1Writer writer = ASN1.getWriter(byteBuilder);
+       for (Attribute a : attributes)
+       {
+         new LDAPAttribute(a).write(writer);
+       }
+       return byteBuilder.toByteArray();
+     }
+     catch (Exception e)
+     {
+       return null;
+     }
+   }
+
+  // ============
+  // Msg decoding
+  // ============
+
+  /**
    * Decode the Header part of this Update Message, and check its type.
    *
    * @param types The allowed types of this Update Message.
@@ -353,82 +455,77 @@
    * @throws DataFormatException if the encodedMsg does not contain a valid
    *         common header.
    */
-  public int decodeHeader(byte[] types, byte[] encodedMsg)
+   public int decodeHeader(byte[] types, byte[] encodedMsg)
                           throws DataFormatException
-  {
-    /* The message header is stored in the form :
-     * <operation type><protocol version><changenumber><dn><entryuuid><assured>
-     * <assured mode> <safe data level>
-     */
+   {
+     /* first byte is the type */
+     boolean foundMatchingType = false;
+     for (int i = 0; i < types.length; i++)
+     {
+       if (types[i] == encodedMsg[0])
+       {
+         foundMatchingType = true;
+         break;
+       }
+     }
+     if (!foundMatchingType)
+       throw new DataFormatException("byte[] is not a valid update msg: "
+           + encodedMsg[0]);
 
-    /* first byte is the type */
-    boolean foundMatchingType = false;
-    for (int i = 0; i < types.length; i++)
-    {
-      if (types[i] == encodedMsg[0])
-      {
-        foundMatchingType = true;
-        break;
-      }
-    }
-    if (!foundMatchingType)
-      throw new DataFormatException("byte[] is not a valid update msg: "
-        + encodedMsg[0]);
+     /*
+      * For older protocol version PDUs, decode the matching version header
+      * instead.
+      */
+     if ((encodedMsg[0] == MSG_TYPE_ADD_V1) ||
+         (encodedMsg[0] == MSG_TYPE_DELETE_V1) ||
+         (encodedMsg[0] == MSG_TYPE_MODIFYDN_V1) ||
+         (encodedMsg[0] == MSG_TYPE_MODIFY_V1))
+     {
+       return decodeHeader_V1(encodedMsg);
+     }
 
-    /*
-     * For older protocol version PDUs, decode the matching version header
-     * instead.
-     */
-    if ((encodedMsg[0] == MSG_TYPE_ADD_V1) ||
-      (encodedMsg[0] == MSG_TYPE_DELETE_V1) ||
-      (encodedMsg[0] == MSG_TYPE_MODIFYDN_V1) ||
-      (encodedMsg[0] == MSG_TYPE_MODIFY_V1))
-    {
-      return decodeHeader_V1(encodedMsg);
-    }
+     /* read the protocol version */
+     protocolVersion = (short)encodedMsg[1];
 
-    /* read the protocol version */
-    protocolVersion = (short)encodedMsg[1];
+     try
+     {
+       /* Read the changeNumber */
+       int pos = 2;
+       int length = getNextLength(encodedMsg, pos);
+       String changenumberStr = new String(encodedMsg, pos, length, "UTF-8");
+       pos += length + 1;
+       changeNumber = new ChangeNumber(changenumberStr);
 
-    try
-    {
-      /* Read the changeNumber */
-      int pos = 2;
-      int length = getNextLength(encodedMsg, pos);
-      String changenumberStr = new String(encodedMsg, pos, length, "UTF-8");
-      pos += length + 1;
-      changeNumber = new ChangeNumber(changenumberStr);
+       /* Read the dn */
+       length = getNextLength(encodedMsg, pos);
+       dn = new String(encodedMsg, pos, length, "UTF-8");
+       pos += length + 1;
 
-      /* Read the dn */
-      length = getNextLength(encodedMsg, pos);
-      dn = new String(encodedMsg, pos, length, "UTF-8");
-      pos += length + 1;
+       /* Read the entryuuid */
+       length = getNextLength(encodedMsg, pos);
+       uniqueId = new String(encodedMsg, pos, length, "UTF-8");
+       pos += length + 1;
 
-      /* Read the entryuuid */
-      length = getNextLength(encodedMsg, pos);
-      uniqueId = new String(encodedMsg, pos, length, "UTF-8");
-      pos += length + 1;
+       /* Read the assured information */
+       if (encodedMsg[pos++] == 1)
+         assuredFlag = true;
+       else
+         assuredFlag = false;
 
-      /* Read the assured information */
-      if (encodedMsg[pos++] == 1)
-        assuredFlag = true;
-      else
-        assuredFlag = false;
+       /* Read the assured mode */
+       assuredMode = AssuredMode.valueOf(encodedMsg[pos++]);
 
-      /* Read the assured mode */
-      assuredMode = AssuredMode.valueOf(encodedMsg[pos++]);
+       /* Read the safe data level */
+       safeDataLevel = encodedMsg[pos++];
 
-      /* Read the safe data level */
-      safeDataLevel = encodedMsg[pos++];
-
-      return pos;
-    } catch (UnsupportedEncodingException e)
-    {
-      throw new DataFormatException("UTF-8 is not supported by this jvm.");
-    } catch (IllegalArgumentException e)
-    {
-      throw new DataFormatException(e.getMessage());
-    }
+       return pos;
+     } catch (UnsupportedEncodingException e)
+     {
+       throw new DataFormatException("UTF-8 is not supported by this jvm.");
+     } catch (IllegalArgumentException e)
+     {
+       throw new DataFormatException(e.getMessage());
+     }
   }
 
   /**
@@ -493,4 +590,96 @@
    * @return The number of bytes used by this message.
    */
   public abstract int size();
+
+  /**
+   * Return the number of bytes used by the header.
+   * @return The number of bytes used by the header.
+   */
+  protected int headerSize()
+  {
+    return 100;    // 100 let's assume header size is 100
+  }
+
+  /**
+   * Set a provided list of entry attributes.
+   * @param entryAttrs  The provided list of entry attributes.
+   */
+  public void setEclIncludes(List<Attribute> entryAttrs)
+  {
+    this.encodedEclIncludes = encodeAttributes(entryAttrs);
+  }
+
+  /**
+   * Returns the list of entry attributes.
+   * @return The list of entry attributes.
+   */
+  public ArrayList<RawAttribute> getEclIncludes()
+  {
+    try
+    {
+      return decodeRawAttributes(this.encodedEclIncludes);
+    }
+    catch(Exception e)
+    {
+      return null;
+    }
+  }
+
+  /**
+   * Decode a provided byte array as a list of RawAttribute.
+   * @param in The provided byte array.
+   * @return The list of Rawattribute objects.
+   * @throws LDAPException when it occurs.
+   * @throws ASN1Exception when it occurs.
+   */
+  public ArrayList<RawAttribute> decodeRawAttributes(byte[] in)
+  throws LDAPException, ASN1Exception
+  {
+    ArrayList<RawAttribute> rattr = new ArrayList<RawAttribute>();
+    try
+    {
+      ByteSequenceReader reader =
+        ByteString.wrap(in).asReader();
+      ASN1Reader asn1Reader = ASN1.getReader(reader);
+      // loop on attributes
+      while(asn1Reader.hasNextElement())
+      {
+        rattr.add(LDAPAttribute.decode(asn1Reader));
+      }
+      return rattr;
+    }
+    catch(Exception e)
+    {
+      return null;
+    }
+  }
+
+  /**
+   * Decode a provided byte array as a list of Attribute.
+   * @param in The provided byte array.
+   * @return The list of Attribute objects.
+   * @throws LDAPException when it occurs.
+   * @throws ASN1Exception when it occurs.
+   */
+  public ArrayList<Attribute> decodeAttributes(byte[] in)
+  throws LDAPException, ASN1Exception
+  {
+    ArrayList<Attribute> lattr = new ArrayList<Attribute>();
+    try
+    {
+      ByteSequenceReader reader =
+        ByteString.wrap(in).asReader();
+      ASN1Reader asn1Reader = ASN1.getReader(reader);
+      // loop on attributes
+      while(asn1Reader.hasNextElement())
+      {
+        lattr.add(LDAPAttribute.decode(asn1Reader).toAttribute());
+      }
+      return lattr;
+    }
+    catch(Exception e)
+    {
+      return null;
+    }
+  }
 }
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
index edf8877..b3d0e27 100644
--- 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
@@ -24,6 +24,7 @@
  *
  *      Copyright 2009 Sun Microsystems, Inc.
  */
+
 package org.opends.server.replication.protocol;
 
 import java.util.ArrayList;
@@ -43,6 +44,7 @@
 import org.opends.server.types.ByteStringBuilder;
 import org.opends.server.types.LDAPException;
 import org.opends.server.types.Modification;
+import org.opends.server.types.RawModification;
 
 /**
  * This class holds every common code for the modify messages (mod, moddn).
@@ -97,26 +99,12 @@
    */
   public void setMods(List<Modification> mods)
   {
-    encodedMods = modsToByte(mods);
+    encodedMods = encodeMods(mods);
   }
 
-  /**
-   * Get the Modifications associated to the UpdateMsg to the provided value.
-   * @throws LDAPException In case of LDAP decoding exception
-   * @throws ASN1Exception In case of ASN1 decoding exception
-   * @return the list of modifications
-   */
-  public List<Modification> getMods() throws ASN1Exception, LDAPException
-  {
-    List<Modification> mods = new ArrayList<Modification>();
-
-    ASN1Reader reader = ASN1.getReader(encodedMods);
-
-    while (reader.hasNextElement())
-      mods.add((LDAPModification.decode(reader)).toModification());
-
-    return mods;
-  }
+  // ============
+  // Msg encoding
+  // ============
 
   /**
    * Encode an ArrayList of Modification into a byte[] suitable
@@ -125,7 +113,7 @@
    * @param mods the ArrayList of Modification to be encoded.
    * @return The encoded modifications.
    */
-  protected byte[] modsToByte(List<Modification> mods)
+  protected byte[] encodeMods(List<Modification> mods)
   {
     if ((mods == null) || (mods.size() == 0))
       return new byte[0];
@@ -161,8 +149,49 @@
         }
       }
     }
-
     return byteBuilder.toByteArray();
   }
 
+  // ============
+  // Msg decoding
+  // ============
+
+  /**
+   * Decode mods from the provided byte array.
+   * @param in The provided byte array.
+   * @throws ASN1Exception when occurs.
+   * @throws LDAPException when occurs.
+   * @return The decoded mods.
+   */
+  protected List<Modification> decodeMods(byte[] in)
+  throws ASN1Exception, LDAPException
+  {
+    List<Modification> mods = new ArrayList<Modification>();
+    ASN1Reader reader = ASN1.getReader(in);
+    while (reader.hasNextElement())
+    {
+      mods.add((LDAPModification.decode(reader)).toModification());
+    }
+    return mods;
+  }
+
+  /**
+   * Decode raw mods from the provided byte array.
+   * @param in The provided byte array.
+   * @return The decoded mods.
+   * @throws ASN1Exception when occurs.
+   * @throws LDAPException when occurs.
+   */
+  protected ArrayList<RawModification> decodeRawMods(byte[] in)
+  throws LDAPException, ASN1Exception
+  {
+    ArrayList<RawModification> ldapmods = new ArrayList<RawModification>();
+    ASN1Reader asn1Reader = ASN1.getReader(in);
+    while(asn1Reader.hasNextElement())
+    {
+      ldapmods.add(LDAPModification.decode(asn1Reader));
+    }
+    return ldapmods;
+  }
+
 }
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 5beda7e..843c9e5 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
@@ -33,11 +33,8 @@
 import java.util.zip.DataFormatException;
 
 import org.opends.server.core.ModifyDNOperationBasis;
-import org.opends.server.protocols.asn1.ASN1;
 import org.opends.server.protocols.asn1.ASN1Exception;
-import org.opends.server.protocols.asn1.ASN1Reader;
 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.ByteString;
@@ -68,7 +65,7 @@
     super((OperationContext) operation.getAttachment(SYNCHROCONTEXT),
         operation.getRawEntryDN().toString());
 
-    encodedMods = modsToByte(operation.getModifications());
+    encodedMods = encodeMods(operation.getModifications());
 
     ModifyDnContext ctx =
       (ModifyDnContext) operation.getAttachment(SYNCHROCONTEXT);
@@ -130,7 +127,7 @@
   {
     this(dn, changeNumber, uid, newParentUid, deleteOldRdn, newSuperior,
       newRDN);
-    this.encodedMods = modsToByte(mods);
+    this.encodedMods = encodeMods(mods);
   }
 
   /**
@@ -143,11 +140,273 @@
   public ModifyDNMsg(byte[] in) throws DataFormatException,
                                        UnsupportedEncodingException
   {
+    // Decode header
     byte[] allowedPduTypes = new byte[2];
     allowedPduTypes[0] = MSG_TYPE_MODIFYDN;
     allowedPduTypes[1] = MSG_TYPE_MODIFYDN_V1;
     int pos = decodeHeader(allowedPduTypes, in);
 
+    // protocol version has been read as part of the header
+    if (protocolVersion <= 3)
+      decodeBody_V123(in, pos);
+    else
+    {
+      decodeBody_V4(in, pos);
+    }
+
+    if (protocolVersion==ProtocolVersion.getCurrentVersion())
+    {
+      bytes = in;
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public AbstractOperation createOperation(
+      InternalClientConnection connection, String newDn)
+  throws LDAPException, ASN1Exception
+  {
+    ModifyDNOperationBasis moddn =  new ModifyDNOperationBasis(connection,
+        InternalClientConnection.nextOperationID(),
+        InternalClientConnection.nextMessageID(), null,
+        ByteString.valueOf(newDn), ByteString.valueOf(newRDN),
+        deleteOldRdn,
+        (newSuperior == null ? null : ByteString.valueOf(newSuperior)));
+
+    for (Modification mod : decodeMods(encodedMods))
+    {
+      moddn.addModification(mod);
+    }
+
+    ModifyDnContext ctx = new ModifyDnContext(getChangeNumber(), getUniqueId(),
+        newSuperiorId);
+    moddn.setAttachment(SYNCHROCONTEXT, ctx);
+    return moddn;
+  }
+
+  // ============
+  // Msg Encoding
+  // ============
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes_V1() throws UnsupportedEncodingException
+  {
+    byte[] byteNewRdn = newRDN.getBytes("UTF-8");
+    byte[] byteNewSuperior = null;
+    byte[] byteNewSuperiorId = null;
+
+    // calculate the length necessary to encode the parameters
+    int bodyLength = byteNewRdn.length + 1 + 1;
+    if (newSuperior != null)
+    {
+      byteNewSuperior = newSuperior.getBytes("UTF-8");
+      bodyLength += byteNewSuperior.length + 1;
+    }
+    else
+      bodyLength += 1;
+
+    if (newSuperiorId != null)
+    {
+      byteNewSuperiorId = newSuperiorId.getBytes("UTF-8");
+      bodyLength += byteNewSuperiorId.length + 1;
+    }
+    else
+      bodyLength += 1;
+
+    byte[] encodedMsg = encodeHeader_V1(MSG_TYPE_MODIFYDN_V1, bodyLength);
+    int pos = encodedMsg.length - bodyLength;
+
+    /* put the new RDN and a terminating 0 */
+    pos = addByteArray(byteNewRdn, encodedMsg, pos);
+
+    /* put the newsuperior and a terminating 0 */
+    if (newSuperior != null)
+    {
+      pos = addByteArray(byteNewSuperior, encodedMsg, pos);
+    }
+    else
+      encodedMsg[pos++] = 0;
+
+    /* put the newsuperiorId and a terminating 0 */
+    if (newSuperiorId != null)
+    {
+      pos = addByteArray(byteNewSuperiorId, encodedMsg, pos);
+    }
+    else
+      encodedMsg[pos++] = 0;
+
+    /* put the deleteoldrdn flag */
+    if (deleteOldRdn)
+      encodedMsg[pos++] = 1;
+    else
+      encodedMsg[pos++] = 0;
+
+    return encodedMsg;
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes_V23() throws UnsupportedEncodingException
+  {
+    // Encoding V2 / V3
+
+    byte[] byteNewRdn = newRDN.getBytes("UTF-8");
+    byte[] byteNewSuperior = null;
+    byte[] byteNewSuperiorId = null;
+
+    // calculate the length necessary to encode the parameters
+    int length = byteNewRdn.length + 1 + 1;
+    if (newSuperior != null)
+    {
+      byteNewSuperior = newSuperior.getBytes("UTF-8");
+      length += byteNewSuperior.length + 1;
+    }
+    else
+      length += 1;
+
+    if (newSuperiorId != null)
+    {
+      byteNewSuperiorId = newSuperiorId.getBytes("UTF-8");
+      length += byteNewSuperiorId.length + 1;
+    }
+    else
+      length += 1;
+
+    length += encodedMods.length + 1;
+
+    /* encode the header in a byte[] large enough to also contain mods.. */
+    byte[] encodedMsg = encodeHeader(MSG_TYPE_MODIFYDN, length);
+    int pos = encodedMsg.length - length;
+
+    /* put the new RDN and a terminating 0 */
+    pos = addByteArray(byteNewRdn, encodedMsg, pos);
+
+    /* put the newsuperior and a terminating 0 */
+    if (newSuperior != null)
+    {
+      pos = addByteArray(byteNewSuperior, encodedMsg, pos);
+    }
+    else
+      encodedMsg[pos++] = 0;
+
+    /* put the newsuperiorId and a terminating 0 */
+    if (newSuperiorId != null)
+    {
+      pos = addByteArray(byteNewSuperiorId, encodedMsg, pos);
+    }
+    else
+      encodedMsg[pos++] = 0;
+
+    /* put the deleteoldrdn flag */
+    if (deleteOldRdn)
+      encodedMsg[pos++] = 1;
+    else
+      encodedMsg[pos++] = 0;
+
+    /* add the mods */
+    if (encodedMods.length > 0)
+    {
+      pos = encodedMsg.length - (encodedMods.length + 1);
+      addByteArray(encodedMods, encodedMsg, pos);
+    }
+    else
+      encodedMsg[pos++] = 0;
+
+    return encodedMsg;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public byte[] getBytes_V4() throws UnsupportedEncodingException
+  {
+    int bodyLength = 0;
+    byte[] byteNewSuperior = null;
+    byte[] byteNewSuperiorId = null;
+
+    // calculate the length necessary to encode the parameters
+
+    byte[] byteNewRdn = newRDN.getBytes("UTF-8");
+    bodyLength = byteNewRdn.length + 1 + 1;
+
+    if (newSuperior != null)
+    {
+      byteNewSuperior = newSuperior.getBytes("UTF-8");
+      bodyLength += byteNewSuperior.length + 1;
+    }
+    else
+      bodyLength += 1;
+
+    if (newSuperiorId != null)
+    {
+      byteNewSuperiorId = newSuperiorId.getBytes("UTF-8");
+      bodyLength += byteNewSuperiorId.length + 1;
+    }
+    else
+      bodyLength += 1;
+
+    byte[] byteModsLen =
+      String.valueOf(encodedMods.length).getBytes("UTF-8");
+    bodyLength += byteModsLen.length + 1;
+    bodyLength += encodedMods.length + 1;
+
+    byte[] byteEntryAttrLen =
+      String.valueOf(encodedEclIncludes.length).getBytes("UTF-8");
+    bodyLength += byteEntryAttrLen.length + 1;
+    bodyLength += encodedEclIncludes.length + 1;
+
+    /* encode the header in a byte[] large enough to also contain mods.. */
+    byte[] encodedMsg = encodeHeader(MSG_TYPE_MODIFYDN, bodyLength);
+
+    int pos = encodedMsg.length - bodyLength;
+
+    /* put the new RDN and a terminating 0 */
+    pos = addByteArray(byteNewRdn, encodedMsg, pos);
+    /* put the newsuperior and a terminating 0 */
+    if (newSuperior != null)
+    {
+      pos = addByteArray(byteNewSuperior, encodedMsg, pos);
+    }
+    else
+      encodedMsg[pos++] = 0;
+    /* put the newsuperiorId and a terminating 0 */
+    if (newSuperiorId != null)
+    {
+      pos = addByteArray(byteNewSuperiorId, encodedMsg, pos);
+    }
+    else
+      encodedMsg[pos++] = 0;
+
+    /* put the deleteoldrdn flag */
+    if (deleteOldRdn)
+      encodedMsg[pos++] = 1;
+    else
+      encodedMsg[pos++] = 0;
+
+    pos = addByteArray(byteModsLen, encodedMsg, pos);
+    pos = addByteArray(encodedMods, encodedMsg, pos);
+
+    pos = addByteArray(byteEntryAttrLen, encodedMsg, pos);
+    pos = addByteArray(encodedEclIncludes, encodedMsg, pos);
+
+    return encodedMsg;
+  }
+
+  // ============
+  // Msg decoding
+  // ============
+
+  private void decodeBody_V123(byte[] in, int pos)
+  throws DataFormatException, UnsupportedEncodingException
+  {
     /* read the newRDN
      * first calculate the length then construct the string
      */
@@ -208,102 +467,84 @@
     }
   }
 
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public AbstractOperation createOperation(
-         InternalClientConnection connection, String newDn)
-         throws LDAPException, ASN1Exception
+  private void decodeBody_V4(byte[] in, int pos)
+  throws DataFormatException, UnsupportedEncodingException
   {
-    ModifyDNOperationBasis moddn =  new ModifyDNOperationBasis(connection,
-               InternalClientConnection.nextOperationID(),
-               InternalClientConnection.nextMessageID(), null,
-               ByteString.valueOf(newDn), ByteString.valueOf(newRDN),
-               deleteOldRdn,
-               (newSuperior == null ? null : ByteString.valueOf(newSuperior)));
+    /* read the newRDN
+     * first calculate the length then construct the string
+     */
+    int length = getNextLength(in, pos);
+    newRDN = new String(in, pos, length, "UTF-8");
+    pos += length + 1;
 
-    ASN1Reader asn1Reader = ASN1.getReader(encodedMods);
-    while (asn1Reader.hasNextElement())
-    {
-      moddn.addModification(LDAPModification.decode(asn1Reader)
-          .toModification());
-    }
-
-    ModifyDnContext ctx = new ModifyDnContext(getChangeNumber(), getUniqueId(),
-        newSuperiorId);
-    moddn.setAttachment(SYNCHROCONTEXT, ctx);
-    return moddn;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public byte[] getBytes() throws UnsupportedEncodingException
-  {
-    byte[] byteNewRdn = newRDN.getBytes("UTF-8");
-    byte[] byteNewSuperior = null;
-    byte[] byteNewSuperiorId = null;
-
-    // calculate the length necessary to encode the parameters
-    int length = byteNewRdn.length + 1 + 1;
-    if (newSuperior != null)
-    {
-      byteNewSuperior = newSuperior.getBytes("UTF-8");
-      length += byteNewSuperior.length + 1;
-    }
+    /* read the newSuperior
+     * first calculate the length then construct the string
+     */
+    length = getNextLength(in, pos);
+    if (length != 0)
+      newSuperior = new String(in, pos, length, "UTF-8");
     else
-      length += 1;
+      newSuperior = null;
+    pos += length + 1;
 
-    if (newSuperiorId != null)
+    /* read the new parent Id
+     */
+    length = getNextLength(in, pos);
+    if (length != 0)
+      newSuperiorId = new String(in, pos, length, "UTF-8");
+    else
+      newSuperiorId = null;
+    pos += length + 1;
+
+    /* get the deleteoldrdn flag */
+    if (in[pos] == 0)
+      deleteOldRdn = false;
+    else
+      deleteOldRdn = true;
+    pos++;
+
+    // Read mods len
+    length = getNextLength(in, pos);
+    int modsLen = Integer.valueOf(new String(in, pos, length,"UTF-8"));
+    pos += length + 1;
+
+    // Read/Don't decode attributes
+    this.encodedMods = new byte[modsLen];
+    try
     {
-      byteNewSuperiorId = newSuperiorId.getBytes("UTF-8");
-      length += byteNewSuperiorId.length + 1;
-    }
-    else
-      length += 1;
-
-    length += encodedMods.length + 1;
-
-    byte[] resultByteArray = encodeHeader(MSG_TYPE_MODIFYDN, length);
-    int pos = resultByteArray.length - length;
-
-    /* put the new RDN and a terminating 0 */
-    pos = addByteArray(byteNewRdn, resultByteArray, pos);
-
-    /* put the newsuperior and a terminating 0 */
-    if (newSuperior != null)
+      System.arraycopy(in, pos, encodedMods, 0, modsLen);
+    } catch (IndexOutOfBoundsException e)
     {
-      pos = addByteArray(byteNewSuperior, resultByteArray, pos);
-    }
-    else
-      resultByteArray[pos++] = 0;
-
-    /* put the newsuperiorId and a terminating 0 */
-    if (newSuperiorId != null)
+      throw new DataFormatException(e.getMessage());
+    } catch (ArrayStoreException e)
     {
-      pos = addByteArray(byteNewSuperiorId, resultByteArray, pos);
-    }
-    else
-      resultByteArray[pos++] = 0;
-
-    /* put the deleteoldrdn flag */
-    if (deleteOldRdn)
-      resultByteArray[pos++] = 1;
-    else
-      resultByteArray[pos++] = 0;
-
-    /* add the mods */
-    if (encodedMods.length > 0)
+      throw new DataFormatException(e.getMessage());
+    } catch (NullPointerException e)
     {
-      pos = resultByteArray.length - (encodedMods.length + 1);
-      addByteArray(encodedMods, resultByteArray, pos);
+      throw new DataFormatException(e.getMessage());
     }
-    else
-      resultByteArray[pos++] = 0;
+    pos += modsLen + 1;
 
-    return resultByteArray;
+    // Read ecl attr len
+    length = getNextLength(in, pos);
+    int eclAttrLen = Integer.valueOf(new String(in, pos, length,"UTF-8"));
+    pos += length + 1;
+
+    // Read/Don't decode entry attributes
+    encodedEclIncludes = new byte[eclAttrLen];
+    try
+    {
+      System.arraycopy(in, pos, encodedEclIncludes, 0, eclAttrLen);
+    } catch (IndexOutOfBoundsException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    } catch (ArrayStoreException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    } catch (NullPointerException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    }
   }
 
   /**
@@ -526,74 +767,8 @@
   @Override
   public int size()
   {
-    // The MODDN message size are mainly dependent on the
-    // size of the DN. let's assume that they average on 100 bytes
-    return encodedMods.length + 100;
+    return encodedMods.length + newRDN.length() +
+      encodedEclIncludes.length + headerSize();
   }
 
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public byte[] getBytes_V1() throws UnsupportedEncodingException
-  {
-    if (bytes == null)
-    {
-      byte[] byteNewRdn = newRDN.getBytes("UTF-8");
-      byte[] byteNewSuperior = null;
-      byte[] byteNewSuperiorId = null;
-
-      // calculate the length necessary to encode the parameters
-      int length = byteNewRdn.length + 1 + 1;
-      if (newSuperior != null)
-      {
-        byteNewSuperior = newSuperior.getBytes("UTF-8");
-        length += byteNewSuperior.length + 1;
-      }
-      else
-        length += 1;
-
-      if (newSuperiorId != null)
-      {
-        byteNewSuperiorId = newSuperiorId.getBytes("UTF-8");
-        length += byteNewSuperiorId.length + 1;
-      }
-      else
-        length += 1;
-
-      byte[] resultByteArray = encodeHeader_V1(MSG_TYPE_MODIFYDN_V1, length);
-      int pos = resultByteArray.length - length;
-
-      /* put the new RDN and a terminating 0 */
-      pos = addByteArray(byteNewRdn, resultByteArray, pos);
-
-      /* put the newsuperior and a terminating 0 */
-      if (newSuperior != null)
-      {
-        pos = addByteArray(byteNewSuperior, resultByteArray, pos);
-      }
-      else
-        resultByteArray[pos++] = 0;
-
-      /* put the newsuperiorId and a terminating 0 */
-      if (newSuperiorId != null)
-      {
-        pos = addByteArray(byteNewSuperiorId, resultByteArray, pos);
-      }
-      else
-        resultByteArray[pos++] = 0;
-
-      /* put the deleteoldrdn flag */
-      if (deleteOldRdn)
-        resultByteArray[pos++] = 1;
-      else
-        resultByteArray[pos++] = 0;
-
-      return resultByteArray;
-    }
-    else
-    {
-      return bytes;
-    }
-  }
 }
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 2b50944..099e215 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
@@ -28,27 +28,23 @@
 
 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.ModifyOperationBasis;
-import org.opends.server.protocols.ldap.LDAPModification;
-import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.protocols.asn1.ASN1Exception;
-import org.opends.server.protocols.asn1.ASN1Reader;
-import org.opends.server.protocols.asn1.ASN1;
+import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.replication.common.ChangeNumber;
-import org.opends.server.types.*;
 import org.opends.server.types.AbstractOperation;
+import org.opends.server.types.ByteString;
 import org.opends.server.types.DN;
 import org.opends.server.types.LDAPException;
 import org.opends.server.types.Modification;
 import org.opends.server.types.RawModification;
 import org.opends.server.types.operation.PostOperationModifyOperation;
 
-
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.zip.DataFormatException;
-
 /**
  * Message used to send Modify information.
  */
@@ -63,7 +59,7 @@
   {
     super((OperationContext) op.getAttachment(OperationContext.SYNCHROCONTEXT),
           op.getRawEntryDN().toString());
-    encodedMods = modsToByte(op.getModifications());
+    encodedMods = encodeMods(op.getModifications());
   }
 
   /**
@@ -80,7 +76,7 @@
   {
     super(new ModifyContext(changeNumber, entryuuid),
           dn.toNormalizedString());
-    this.encodedMods = modsToByte(mods);
+    this.encodedMods = encodeMods(mods);
   }
 
   /**
@@ -93,30 +89,23 @@
   public ModifyMsg(byte[] in) throws DataFormatException,
                                      UnsupportedEncodingException
   {
-    bytes = in;
-
     // Decode header
     byte[] allowedPduTypes = new byte[2];
     allowedPduTypes[0] = MSG_TYPE_MODIFY;
     allowedPduTypes[1] = MSG_TYPE_MODIFY_V1;
     int pos = decodeHeader(allowedPduTypes, in);
 
-    /* Read the mods : all the remaining bytes but the terminating 0 */
-    int length = in.length - pos - 1;
-    encodedMods = new byte[length];
-    try
+    // protocol version has been read as part of the header
+    if (protocolVersion <= 3)
+      decodeBody_V123(in, pos);
+    else
+      decodeBody_V4(in, pos);
+
+    if (protocolVersion==ProtocolVersion.getCurrentVersion())
     {
-      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());
+      bytes = in;
     }
+
   }
 
   /**
@@ -132,6 +121,8 @@
                                      UnsupportedEncodingException
   {
     ModifyMsg msg = new ModifyMsg(in);
+
+    // bytes is only for current version (of the protocol) bytes !
     msg.bytes = null;
 
     return msg;
@@ -141,53 +132,26 @@
    * {@inheritDoc}
    */
   @Override
-  public byte[] getBytes() throws UnsupportedEncodingException
-  {
-    if (bytes == null)
-    {
-      /* encode the header in a byte[] large enough to also contain the mods */
-      byte[] mybytes = encodeHeader(MSG_TYPE_MODIFY, encodedMods.length + 1);
-
-      /* add the mods */
-      int pos = mybytes.length - (encodedMods.length + 1);
-      addByteArray(encodedMods, mybytes, pos);
-
-      return mybytes;
-    }
-    else
-    {
-      return bytes;
-    }
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
   public AbstractOperation createOperation(InternalClientConnection connection,
-                   String newDn)
-                   throws LDAPException, ASN1Exception, DataFormatException
+      String newDn)
+  throws LDAPException, ASN1Exception, DataFormatException
   {
     if (newDn == null)
       newDn = getDn();
 
-    ArrayList<RawModification> ldapmods = new ArrayList<RawModification>();
-
-    ASN1Reader asn1Reader = ASN1.getReader(encodedMods);
-    while(asn1Reader.hasNextElement())
-    {
-      ldapmods.add(LDAPModification.decode(asn1Reader));
-    }
+    ArrayList<RawModification> ldapmods = decodeRawMods(encodedMods);
 
     ModifyOperationBasis mod = new ModifyOperationBasis(connection,
-                               InternalClientConnection.nextOperationID(),
-                               InternalClientConnection.nextMessageID(), null,
+        InternalClientConnection.nextOperationID(),
+        InternalClientConnection.nextMessageID(), null,
         ByteString.valueOf(newDn), ldapmods);
     ModifyContext ctx = new ModifyContext(getChangeNumber(), getUniqueId());
     mod.setAttachment(SYNCHROCONTEXT, ctx);
     return mod;
+
   }
 
+
   /**
    * {@inheritDoc}
    */
@@ -224,11 +188,15 @@
   public int size()
   {
     // The ModifyMsg can be very large when added or deleted attribute
-    // values are very large. We therefore need to count the
-    // whole encoded msg.
-    return encodedMods.length + 100; // 100 let's assume header size is 100
+    // values are very large.
+    // We therefore need to count the whole encoded msg.
+    return encodedMods.length + encodedEclIncludes.length + headerSize();
   }
 
+  // ============
+  // Msg Encoding
+  // ============
+
   /**
    * {@inheritDoc}
    */
@@ -244,4 +212,121 @@
 
     return encodedMsg;
   }
+
+  /**
+   * {@inheritDoc}
+   */
+  public byte[] getBytes_V23() throws UnsupportedEncodingException
+  {
+    // Encoding V2 / V3
+
+    /* encode the header in a byte[] large enough to also contain mods */
+    byte[] encodedMsg = encodeHeader(MSG_TYPE_MODIFY, encodedMods.length + 1);
+
+    /* add the mods */
+    int pos = encodedMsg.length - (encodedMods.length + 1);
+    addByteArray(encodedMods, encodedMsg, pos);
+
+    return encodedMsg;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public byte[] getBytes_V4() throws UnsupportedEncodingException
+  {
+    int bodyLength = 0;
+    byte[] byteModsLen =
+      String.valueOf(encodedMods.length).getBytes("UTF-8");
+    bodyLength += byteModsLen.length + 1;
+    bodyLength += encodedMods.length + 1;
+
+    byte[] byteEntryAttrLen =
+      String.valueOf(encodedEclIncludes.length).getBytes("UTF-8");
+    bodyLength += byteEntryAttrLen.length + 1;
+    bodyLength += encodedEclIncludes.length + 1;
+
+    /* encode the header in a byte[] large enough to also contain the mods */
+    byte [] encodedMsg = encodeHeader(MSG_TYPE_MODIFY, bodyLength);
+
+    int pos = encodedMsg.length - bodyLength;
+    pos = addByteArray(byteModsLen, encodedMsg, pos);
+    pos = addByteArray(encodedMods, encodedMsg, pos);
+    pos = addByteArray(byteEntryAttrLen, encodedMsg, pos);
+    pos = addByteArray(encodedEclIncludes, encodedMsg, pos);
+    return encodedMsg;
+  }
+
+  // ============
+  // Msg decoding
+  // ============
+
+  private void decodeBody_V123(byte[] in, int pos)
+  throws DataFormatException
+  {
+    // Read and store the mods, in encoded form
+    // all the remaining bytes but the terminating 0 */
+    int length = in.length - pos - 1;
+    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());
+    }
+  }
+
+  private void decodeBody_V4(byte[] in, int pos)
+  throws DataFormatException, UnsupportedEncodingException
+  {
+    // Read mods len
+    int length = getNextLength(in, pos);
+    int modsLen = Integer.valueOf(new String(in, pos, length,"UTF-8"));
+    pos += length + 1;
+
+    // Read/Don't decode mods
+    this.encodedMods = new byte[modsLen];
+    try
+    {
+      System.arraycopy(in, pos, encodedMods, 0, modsLen);
+    } catch (IndexOutOfBoundsException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    } catch (ArrayStoreException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    } catch (NullPointerException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    }
+    pos += modsLen + 1;
+
+    // Read ecl attr len
+    length = getNextLength(in, pos);
+    int eclAttrLen = Integer.valueOf(new String(in, pos, length,"UTF-8"));
+    pos += length + 1;
+
+    // Read/Don't decode entry attributes
+    encodedEclIncludes = new byte[eclAttrLen];
+    try
+    {
+      System.arraycopy(in, pos, encodedEclIncludes, 0, eclAttrLen);
+    } catch (IndexOutOfBoundsException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    } catch (ArrayStoreException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    } catch (NullPointerException e)
+    {
+      throw new DataFormatException(e.getMessage());
+    }
+  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ProtocolVersion.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ProtocolVersion.java
index 042f5a2..997267d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ProtocolVersion.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ProtocolVersion.java
@@ -43,15 +43,24 @@
   public static final short REPLICATION_PROTOCOL_V1_REAL = 49;
   /**
    * The constant for the second version of the replication protocol.
+   * Add fields in the header for assured replication.
    */
   public static final short REPLICATION_PROTOCOL_V2 = 2;
 
   /**
    * The constant for the 3rd version of the replication protocol.
+   * Add messages for remote ECL : not used as of today.
    */
   public static final short REPLICATION_PROTOCOL_V3 = 3;
 
   /**
+   * 4th version of the replication protocol.
+   * Add to the body of the ADD/MOD/MODDN/DEL msgs, a list of attribute for
+   * ECL entry attributes.
+   */
+  public static final short REPLICATION_PROTOCOL_V4 = 4;
+
+  /**
    * The replication protocol version used by the instance of RS/DS in this VM.
    */
   private static short currentVersion = -1;
@@ -86,7 +95,7 @@
    */
   public static void resetCurrentVersion()
   {
-    currentVersion = REPLICATION_PROTOCOL_V3;
+    currentVersion = REPLICATION_PROTOCOL_V4;
   }
 
   /**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java
index 0feb441..466d884 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java
@@ -209,7 +209,7 @@
         msg = new WindowProbeMsg(buffer);
       break;
       case MSG_TYPE_TOPOLOGY:
-        msg = new TopologyMsg(buffer);
+        msg = new TopologyMsg(buffer, version);
       break;
       case MSG_TYPE_REPL_SERVER_MONITOR_REQUEST:
         msg = new MonitorRequestMsg(buffer);
@@ -218,7 +218,7 @@
         msg = new MonitorMsg(buffer, version);
       break;
       case MSG_TYPE_START_SESSION:
-        msg = new StartSessionMsg(buffer);
+        msg = new StartSessionMsg(buffer, version);
       break;
       case MSG_TYPE_CHANGE_STATUS:
         msg = new ChangeStatusMsg(buffer);
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/StartSessionMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/StartSessionMsg.java
index 5925ee5..3f22529 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/StartSessionMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/StartSessionMsg.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.replication.protocol;
 
@@ -30,10 +30,19 @@
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.zip.DataFormatException;
+
+import org.opends.server.protocols.asn1.ASN1;
+import org.opends.server.protocols.asn1.ASN1Reader;
+import org.opends.server.protocols.asn1.ASN1Writer;
 import org.opends.server.replication.common.AssuredMode;
 import org.opends.server.replication.common.ServerStatus;
+import org.opends.server.types.ByteSequenceReader;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ByteStringBuilder;
 
 /**
  * This message is used by DS to confirm a RS he wants to connect to him (open
@@ -62,14 +71,271 @@
   // DS safe data level (relevant if assured mode is safe data)
   private byte safeDataLevel = (byte) 1;
 
+  private Set<String> eclIncludes = new HashSet<String>();
+
+  /**
+   * The protocolVersion that should be used when serializing this message.
+   */
+  private final short protocolVersion;
+
   /**
    * Creates a new StartSessionMsg message from its encoded form.
    *
    * @param in The byte array containing the encoded form of the message.
+   * @param version The protocol version to use to decode the msg.
    * @throws java.util.zip.DataFormatException If the byte array does not
    * contain a valid encoded form of the message.
    */
-  public StartSessionMsg(byte[] in) throws DataFormatException
+  public StartSessionMsg(byte[] in, short version) throws DataFormatException
+  {
+    protocolVersion = ProtocolVersion.getCurrentVersion();
+    if (version <= ProtocolVersion.REPLICATION_PROTOCOL_V3)
+    {
+      decode_V23(in);
+    }
+    else
+    {
+      decode_V4(in);
+    }
+  }
+
+  /**
+   * Creates a new StartSessionMsg message from its encoded form.
+   *
+   * Creates a new  message with the given required parameters.
+   * @param status Status we are starting with
+   * @param referralsURLs Referrals URLs to be used by peer DSs
+   * @param assuredFlag If assured mode is enabled or not
+   * @param assuredMode Assured type
+   * @param safeDataLevel Assured mode safe data level
+   * @param replicationProtocol   The protocol version to use.
+   */
+  public StartSessionMsg(ServerStatus status, List<String> referralsURLs,
+      boolean assuredFlag, AssuredMode assuredMode, byte safeDataLevel,
+      short replicationProtocol)
+  {
+    this.referralsURLs = referralsURLs;
+    this.status = status;
+    this.assuredFlag = assuredFlag;
+    this.assuredMode = assuredMode;
+    this.safeDataLevel = safeDataLevel;
+    this.protocolVersion = replicationProtocol;
+  }
+
+  /**
+   * Creates a new  message with the given required parameters.
+   * @param status Status we are starting with
+   * @param referralsURLs Referrals URLs to be used by peer DSs
+   * @param assuredFlag If assured mode is enabled or not
+   * @param assuredMode Assured type
+   * @param safeDataLevel Assured mode safe data level
+   */
+  public StartSessionMsg(ServerStatus status, List<String> referralsURLs,
+    boolean assuredFlag, AssuredMode assuredMode, byte safeDataLevel)
+  {
+    this.referralsURLs = referralsURLs;
+    this.status = status;
+    this.assuredFlag = assuredFlag;
+    this.assuredMode = assuredMode;
+    this.safeDataLevel = safeDataLevel;
+    this.protocolVersion = ProtocolVersion.getCurrentVersion();
+  }
+
+  /**
+   * Creates a new message with the given required parameters.
+   * Assured mode is false.
+   * @param status Status we are starting with
+   * @param referralsURLs Referrals URLs to be used by peer DSs
+   */
+  public StartSessionMsg(ServerStatus status, List<String> referralsURLs)
+  {
+    this.referralsURLs = referralsURLs;
+    this.status = status;
+    this.assuredFlag = false;
+    this.protocolVersion = ProtocolVersion.getCurrentVersion();
+  }
+
+  /**
+   * Creates a new message with the given required parameters.
+   * Assured mode is false.
+   * @param status Status we are starting with
+   * @param referralsURLs Referrals URLs to be used by peer DSs
+   * @param replicationProtocol The requested protocol version.
+   */
+  public StartSessionMsg(ServerStatus status, List<String> referralsURLs,
+    short replicationProtocol)
+  {
+    this.referralsURLs = referralsURLs;
+    this.status = status;
+    this.assuredFlag = false;
+    this.protocolVersion = replicationProtocol;
+  }
+
+  // ============
+  // Msg encoding
+  // ============
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes()
+  throws UnsupportedEncodingException
+  {
+    if (protocolVersion <= ProtocolVersion.REPLICATION_PROTOCOL_V3)
+    {
+      return getBytes_V23();
+    }
+    else
+    {
+      return getBytes_V4();
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes(short reqProtocolVersion)
+    throws UnsupportedEncodingException
+  {
+    if (reqProtocolVersion <= ProtocolVersion.REPLICATION_PROTOCOL_V3)
+    {
+      return getBytes_V23();
+    }
+    else
+    {
+      return getBytes_V4();
+    }
+  }
+
+  private byte[] getBytes_V4()
+  {
+    try
+    {
+      ByteStringBuilder byteBuilder = new ByteStringBuilder();
+      ASN1Writer writer = ASN1.getWriter(byteBuilder);
+
+      byteBuilder.append(MSG_TYPE_START_SESSION);
+      byteBuilder.append(status.getValue());
+      byteBuilder.append(assuredFlag ? (byte) 1 : (byte) 0);
+      byteBuilder.append(assuredMode.getValue());
+      byteBuilder.append(safeDataLevel);
+
+      writer.writeStartSequence();
+      for (String url : referralsURLs)
+        writer.writeOctetString(url);
+      writer.writeEndSequence();
+
+      writer.writeStartSequence();
+      for (String attrDef : eclIncludes)
+        writer.writeOctetString(attrDef);
+      writer.writeEndSequence();
+
+      return byteBuilder.toByteArray();
+    }
+    catch (Exception e)
+    {
+      return null;
+    }
+  }
+
+  private byte[] getBytes_V23()
+  {
+    /*
+     * The message is stored in the form:
+     * <message type><status><assured flag><assured mode><safe data level>
+     * <list of referrals urls>
+     * (each referral url terminates with 0)
+     */
+
+    try
+    {
+      ByteArrayOutputStream oStream = new ByteArrayOutputStream();
+
+      /* Put the message type */
+      oStream.write(MSG_TYPE_START_SESSION);
+
+      // Put the status
+      oStream.write(status.getValue());
+
+      // Put the assured flag
+      oStream.write(assuredFlag ? (byte) 1 : (byte) 0);
+
+      // Put assured mode
+      oStream.write(assuredMode.getValue());
+
+      // Put safe data level
+      oStream.write(safeDataLevel);
+
+      // Put the referrals URLs
+      if (referralsURLs.size() >= 1)
+      {
+        for (String url : referralsURLs)
+        {
+          byte[] byteArrayURL = url.getBytes("UTF-8");
+          oStream.write(byteArrayURL);
+          oStream.write(0);
+        }
+      }
+      return oStream.toByteArray();
+    } catch (IOException e)
+    {
+      // never happens
+      return null;
+    }
+  }
+
+  // ============
+  // Msg decoding
+  // ============
+
+  private void decode_V4(byte[] in)
+  throws DataFormatException
+  {
+    ByteSequenceReader reader = ByteString.wrap(in).asReader();
+    try
+    {
+      if (reader.get() != MSG_TYPE_START_SESSION)
+        throw new DataFormatException("input is not a valid " +
+            this.getClass().getCanonicalName());
+
+      /*
+      status = ServerStatus.valueOf(asn1Reader.readOctetString().byteAt(0));
+      assuredFlag = (asn1Reader.readOctetString().byteAt(0) == 1);
+      assuredMode=AssuredMode.valueOf((asn1Reader.readOctetString().byteAt(0)));
+      safeDataLevel = asn1Reader.readOctetString().byteAt(0);
+      */
+      status = ServerStatus.valueOf(reader.get());
+      assuredFlag = (reader.get() == 1);
+      assuredMode = AssuredMode.valueOf(reader.get());
+      safeDataLevel = reader.get();
+
+      ASN1Reader asn1Reader = ASN1.getReader(reader);
+
+      asn1Reader.readStartSequence();
+      while(asn1Reader.hasNextElement())
+      {
+        String s = asn1Reader.readOctetStringAsString();
+        this.referralsURLs.add(s);
+      }
+      asn1Reader.readEndSequence();
+
+      asn1Reader.readStartSequence();
+      while(asn1Reader.hasNextElement())
+      {
+        String s = asn1Reader.readOctetStringAsString();
+        this.eclIncludes.add(s);
+      }
+      asn1Reader.readEndSequence();
+    }
+    catch (Exception e)
+    {
+    }
+  }
+
+  private void decode_V23(byte[] in)
+  throws DataFormatException
   {
     /*
      * The message is stored in the form:
@@ -128,88 +394,6 @@
   }
 
   /**
-   * Creates a new StartSessionMsg message with the given required parameters.
-   * @param status Status we are starting with
-   * @param referralsURLs Referrals URLs to be used by peer DSs
-   * @param assuredFlag If assured mode is enabled or not
-   * @param assuredMode Assured type
-   * @param safeDataLevel Assured mode safe data level
-   */
-  public StartSessionMsg(ServerStatus status, List<String> referralsURLs,
-    boolean assuredFlag, AssuredMode assuredMode, byte safeDataLevel)
-  {
-    this.referralsURLs = referralsURLs;
-    this.status = status;
-    this.assuredFlag = assuredFlag;
-    this.assuredMode = assuredMode;
-    this.safeDataLevel = safeDataLevel;
-  }
-
-  /**
-   * Creates a new StartSessionMsg message with the given required parameters.
-   * Assured mode is false.
-   * @param status Status we are starting with
-   * @param referralsURLs Referrals URLs to be used by peer DSs
-   */
-  public StartSessionMsg(ServerStatus status, List<String> referralsURLs)
-  {
-    this.referralsURLs = referralsURLs;
-    this.status = status;
-    this.assuredFlag = false;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public byte[] getBytes()
-  {
-    /*
-     * The message is stored in the form:
-     * <message type><status><assured flag><assured mode><safe data level>
-     * <list of referrals urls>
-     * (each referral url terminates with 0)
-     */
-
-    try
-    {
-      ByteArrayOutputStream oStream = new ByteArrayOutputStream();
-
-      /* Put the message type */
-      oStream.write(MSG_TYPE_START_SESSION);
-
-      // Put the status
-      oStream.write(status.getValue());
-
-      // Put the assured flag
-      oStream.write(assuredFlag ? (byte) 1 : (byte) 0);
-
-      // Put assured mode
-      oStream.write(assuredMode.getValue());
-
-      // Put safe data level
-      oStream.write(safeDataLevel);
-
-      // Put the referrals URLs
-      if (referralsURLs.size() >= 1)
-      {
-        for (String url : referralsURLs)
-        {
-          byte[] byteArrayURL = url.getBytes("UTF-8");
-          oStream.write(byteArrayURL);
-          oStream.write(0);
-        }
-      }
-
-      return oStream.toByteArray();
-    } catch (IOException e)
-    {
-      // never happens
-      return null;
-    }
-  }
-
-  /**
    * Get the list of referrals URLs.
    *
    * @return The list of referrals URLs.
@@ -243,7 +427,8 @@
       "\nassuredFlag: " + assuredFlag +
       "\nassuredMode: " + assuredMode +
       "\nsafeDataLevel: " + safeDataLevel +
-      "\nreferralsURLs: " + urls);
+      "\nreferralsURLs: " + urls +
+      "\nEclIncludes: " + eclIncludes);
   }
 
   /**
@@ -273,4 +458,23 @@
     return safeDataLevel;
   }
 
+  /**
+   * Set the list of entry attributes to include in the ECL.
+   * @param eclIncludes The list of attributes.
+   */
+  public void setEclIncludes(Set<String> eclIncludes)
+  {
+    if (eclIncludes != null)
+      this.eclIncludes = eclIncludes;
+  }
+
+  /**
+   * Get the list of entry attributes to include in the ECL..
+   * @return The list of entry attributes to include in the ECL.
+   */
+  public Set<String> getEclIncludes()
+  {
+    return eclIncludes;
+  }
+
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/TopologyMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/TopologyMsg.java
index b30f0dd..fd34473 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/TopologyMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/TopologyMsg.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2007-2008 Sun Microsystems, Inc.
+ *      Copyright 2007-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.replication.protocol;
 
@@ -30,8 +30,11 @@
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.zip.DataFormatException;
+
 import org.opends.server.replication.common.AssuredMode;
 import org.opends.server.replication.common.DSInfo;
 import org.opends.server.replication.common.RSInfo;
@@ -62,13 +65,165 @@
   private List<RSInfo> rsList = new ArrayList<RSInfo>();
 
   /**
+   * The protocolVersion that should be used when serializing this message.
+   */
+  private final short protocolVersion;
+
+  /**
    * Creates a new changelogInfo message from its encoded form.
    *
    * @param in The byte array containing the encoded form of the message.
+   * @param version The protocol version to use to decode the msg.
    * @throws java.util.zip.DataFormatException If the byte array does not
    * contain a valid encoded form of the message.
    */
-  public TopologyMsg(byte[] in) throws DataFormatException
+  public TopologyMsg(byte[] in, short version) throws DataFormatException
+  {
+    protocolVersion = ProtocolVersion.getCurrentVersion();
+    decode(in, version);
+  }
+
+  /**
+   * Creates a new  message from a list of the currently connected servers.
+   *
+   * @param dsList The list of currently connected DS servers ID.
+   * @param rsList The list of currently connected RS servers ID.
+   * @param version The protocol version to use to decode the msg.
+   */
+  public TopologyMsg(List<DSInfo> dsList, List<RSInfo> rsList, short version)
+  {
+    if (dsList != null) // null means no info, let empty list from init time
+      this.dsList = dsList;
+    if (rsList != null) // null means no info, let empty list from init time
+      this.rsList = rsList;
+    this.protocolVersion = version;
+  }
+
+  /**
+   * Creates a new  message from a list of the currently connected servers.
+   *
+   * @param dsList The list of currently connected DS servers ID.
+   * @param rsList The list of currently connected RS servers ID.
+   */
+  public TopologyMsg(List<DSInfo> dsList, List<RSInfo> rsList)
+  {
+    if (dsList != null) // null means no info, let empty list from init time
+      this.dsList = dsList;
+    if (rsList != null) // null means no info, let empty list from init time
+      this.rsList = rsList;
+    this.protocolVersion = ProtocolVersion.getCurrentVersion();
+  }
+
+  // ============
+  // Msg encoding
+  // ============
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes()
+  throws UnsupportedEncodingException
+  {
+    try
+    {
+      /**
+       * Message has the following form:
+       * <pdu type><number of following DSInfo entries>[<DSInfo>]*
+       * <number of following RSInfo entries>[<RSInfo>]*
+       */
+      ByteArrayOutputStream oStream = new ByteArrayOutputStream();
+
+      /* Put the message type */
+      oStream.write(MSG_TYPE_TOPOLOGY);
+
+      // Put number of following DS info entries
+      oStream.write((byte)dsList.size());
+
+      // Put DS info
+      for (DSInfo dsInfo : dsList)
+      {
+        // Put DS id
+        byte[] byteServerId =
+          String.valueOf(dsInfo.getDsId()).getBytes("UTF-8");
+        oStream.write(byteServerId);
+        oStream.write(0);
+        // Put RS id
+        byteServerId =
+          String.valueOf(dsInfo.getRsId()).getBytes("UTF-8");
+        oStream.write(byteServerId);
+        oStream.write(0);
+        // Put the generation id
+        oStream.write(String.valueOf(dsInfo.getGenerationId()).
+            getBytes("UTF-8"));
+        oStream.write(0);
+        // Put DS status
+        oStream.write(dsInfo.getStatus().getValue());
+        // Put DS assured flag
+        oStream.write(dsInfo.isAssured() ? (byte) 1 : (byte) 0);
+        // Put DS assured mode
+        oStream.write(dsInfo.getAssuredMode().getValue());
+        // Put DS safe data level
+        oStream.write(dsInfo.getSafeDataLevel());
+        // Put DS group id
+        oStream.write(dsInfo.getGroupId());
+
+        List<String> refUrls = dsInfo.getRefUrls();
+        // Put number of following URLs as a byte
+        oStream.write(refUrls.size());
+        for (String url : refUrls)
+        {
+          // Write the url and a 0 terminating byte
+          oStream.write(url.getBytes("UTF-8"));
+          oStream.write(0);
+        }
+
+        if (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V4)
+        {
+          Set<String> attrs = dsInfo.getEclIncludes();
+          oStream.write(attrs.size());
+          for (String attr : attrs)
+          {
+            oStream.write(attr.getBytes("UTF-8"));
+            oStream.write(0);
+          }
+        }
+      }
+
+      // Put number of following RS info entries
+      oStream.write((byte)rsList.size());
+
+      // Put RS info
+      for (RSInfo rsInfo : rsList)
+      {
+        // Put RS id
+        byte[] byteServerId =
+          String.valueOf(rsInfo.getId()).getBytes("UTF-8");
+        oStream.write(byteServerId);
+        oStream.write(0);
+        // Put the generation id
+        oStream.write(String.valueOf(rsInfo.getGenerationId()).
+          getBytes("UTF-8"));
+        oStream.write(0);
+        // Put DS group id
+        oStream.write(rsInfo.getGroupId());
+      }
+
+      return oStream.toByteArray();
+    } catch (IOException e)
+    {
+      // never happens
+      return null;
+    }
+
+  }
+
+  // ============
+  // Msg decoding
+  // ============
+
+  private void decode(byte[] in, short version)
+  throws DataFormatException
   {
     try
     {
@@ -151,10 +306,29 @@
           nRead++;
         }
 
+        Set<String> attrs = new HashSet<String>();
+        if (version>=ProtocolVersion.REPLICATION_PROTOCOL_V4)
+        {
+          byte nAttrs = in[pos++];
+          nRead = 0;
+          /* Read attrs until expected number read */
+          while ((nRead != nAttrs) &&
+            (pos < in.length) //security
+            )
+          {
+            length = getNextLength(in, pos);
+            String attr = new String(in, pos, length, "UTF-8");
+            attrs.add(attr);
+            pos +=
+              length + 1;
+            nRead++;
+          }
+        }
+
         /* Now create DSInfo and store it in list */
 
         DSInfo dsInfo = new DSInfo(dsId, rsId, generationId, status,
-          assuredFlag, assuredMode, safeDataLevel, groupId, refUrls);
+          assuredFlag, assuredMode, safeDataLevel, groupId, refUrls, attrs);
         dsList.add(dsInfo);
 
         nDsInfo--;
@@ -197,110 +371,6 @@
     {
       throw new DataFormatException("UTF-8 is not supported by this jvm.");
     }
-
-  }
-
-  /**
-   * Creates a new ReplServerInfo message from a list of the currently
-   * connected servers.
-   *
-   * @param dsList The list of currently connected DS servers ID.
-   * @param rsList The list of currently connected RS servers ID.
-   */
-  public TopologyMsg(List<DSInfo> dsList, List<RSInfo> rsList)
-  {
-    if (dsList != null) // null means no info, let empty list from init time
-      this.dsList = dsList;
-    if (rsList != null) // null means no info, let empty list from init time
-      this.rsList = rsList;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public byte[] getBytes()
-  {
-    try
-    {
-      /**
-       * Message has the following form:
-       * <pdu type><number of following DSInfo entries>[<DSInfo>]*
-       * <number of following RSInfo entries>[<RSInfo>]*
-       */
-      ByteArrayOutputStream oStream = new ByteArrayOutputStream();
-
-      /* Put the message type */
-      oStream.write(MSG_TYPE_TOPOLOGY);
-
-      // Put number of following DS info entries
-      oStream.write((byte)dsList.size());
-
-      // Put DS info
-      for (DSInfo dsInfo : dsList)
-      {
-        // Put DS id
-        byte[] byteServerId =
-          String.valueOf(dsInfo.getDsId()).getBytes("UTF-8");
-        oStream.write(byteServerId);
-        oStream.write(0);
-        // Put RS id
-        byteServerId =
-          String.valueOf(dsInfo.getRsId()).getBytes("UTF-8");
-        oStream.write(byteServerId);
-        oStream.write(0);
-        // Put the generation id
-        oStream.write(String.valueOf(dsInfo.getGenerationId()).
-          getBytes("UTF-8"));
-        oStream.write(0);
-        // Put DS status
-        oStream.write(dsInfo.getStatus().getValue());
-        // Put DS assured flag
-        oStream.write(dsInfo.isAssured() ? (byte) 1 : (byte) 0);
-        // Put DS assured mode
-        oStream.write(dsInfo.getAssuredMode().getValue());
-        // Put DS safe data level
-        oStream.write(dsInfo.getSafeDataLevel());
-        // Put DS group id
-        oStream.write(dsInfo.getGroupId());
-
-        List<String> refUrls = dsInfo.getRefUrls();
-        // Put number of following URLs as a byte
-        oStream.write(refUrls.size());
-        for (String url : refUrls)
-        {
-          // Write the url and a 0 terminating byte
-          oStream.write(url.getBytes("UTF-8"));
-          oStream.write(0);
-        }
-      }
-
-      // Put number of following RS info entries
-      oStream.write((byte)rsList.size());
-
-      // Put RS info
-      for (RSInfo rsInfo : rsList)
-      {
-        // Put RS id
-        byte[] byteServerId =
-          String.valueOf(rsInfo.getId()).getBytes("UTF-8");
-        oStream.write(byteServerId);
-        oStream.write(0);
-        // Put the generation id
-        oStream.write(String.valueOf(rsInfo.getGenerationId()).
-          getBytes("UTF-8"));
-        oStream.write(0);
-        // Put DS group id
-        oStream.write(rsInfo.getGroupId());
-      }
-
-      return oStream.toByteArray();
-    } catch (IOException e)
-    {
-      // never happens
-      return null;
-    }
-
   }
 
   /**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/DataServerHandler.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/DataServerHandler.java
index 2c12cc5..e7f00f0 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/DataServerHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/DataServerHandler.java
@@ -35,8 +35,10 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.zip.DataFormatException;
 
 import org.opends.messages.Message;
@@ -74,6 +76,7 @@
   private AssuredMode assuredMode = AssuredMode.SAFE_DATA_MODE;
   // DS safe data level (relevant if assured mode is safe data)
   private byte safeDataLevel = (byte) -1;
+  private Set<String> eclIncludes = new HashSet<String>();
 
   /**
    * Creates a new data server handler.
@@ -582,7 +585,8 @@
   public DSInfo toDSInfo()
   {
     DSInfo dsInfo = new DSInfo(serverId, replicationServerId, generationId,
-        status, assuredFlag, assuredMode, safeDataLevel, groupId, refUrls);
+      status, assuredFlag, assuredMode, safeDataLevel, groupId, refUrls,
+      eclIncludes);
 
     return dsInfo;
   }
@@ -645,6 +649,7 @@
     this.assuredFlag = startSessionMsg.isAssured();
     this.assuredMode = startSessionMsg.getAssuredMode();
     this.safeDataLevel = startSessionMsg.getSafeDataLevel();
+    this.eclIncludes = startSessionMsg.getEclIncludes();
 
     /*
      * If we have already a generationID set for the domain
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java
index eae7115..27d6e8d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java
@@ -32,8 +32,10 @@
 
 import java.util.ArrayList;
 import java.util.Date;
-
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+
 import org.opends.server.admin.std.server.MonitorProviderCfg;
 import org.opends.server.api.MonitorProvider;
 import org.opends.server.config.ConfigException;
@@ -86,6 +88,8 @@
   // DS safe data level (relevant if assured mode is safe data)
   private byte safeDataLevel = (byte) -1;
 
+  private Set<String> eclInclude = new HashSet<String>();
+
   /**
    * Creates a new LighweightServerHandler with the provided serverid, connected
    * to the remote Replication Server represented by replServerHandler.
@@ -102,11 +106,12 @@
    * @param assuredFlag The assured flag of the remote DS
    * @param assuredMode The assured mode of the remote DS
    * @param safeDataLevel The safe data level of the remote DS
+   * @param eclInclude The list of entry attributes to be added to the ECL.
    */
   public LightweightServerHandler(ReplicationServerHandler replServerHandler,
     short replicationServerId, short serverId, long generationId, byte groupId,
     ServerStatus status, List<String> refUrls, boolean assuredFlag,
-    AssuredMode assuredMode, byte safeDataLevel)
+    AssuredMode assuredMode, byte safeDataLevel, Set<String> eclInclude)
   {
     super("Server Handler");
     this.replServerHandler = replServerHandler;
@@ -120,6 +125,7 @@
     this.assuredFlag = assuredFlag;
     this.assuredMode = assuredMode;
     this.safeDataLevel = safeDataLevel;
+    this.eclInclude = eclInclude;
 
     if (debugEnabled())
       TRACER.debugInfo(
@@ -137,7 +143,8 @@
   public DSInfo toDSInfo()
   {
     DSInfo dsInfo = new DSInfo(serverId, replicationServerId, generationId,
-      status, assuredFlag, assuredMode, safeDataLevel, groupId, refUrls);
+      status, assuredFlag, assuredMode, safeDataLevel, groupId, refUrls,
+      eclInclude);
 
     return dsInfo;
   }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
index 06ebf06..2e4f34c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
@@ -63,8 +63,8 @@
 import org.opends.server.replication.common.ServerStatus;
 import org.opends.server.replication.common.StatusMachineEvent;
 import org.opends.server.replication.protocol.AckMsg;
-import org.opends.server.replication.protocol.ChangeTimeHeartbeatMsg;
 import org.opends.server.replication.protocol.ChangeStatusMsg;
+import org.opends.server.replication.protocol.ChangeTimeHeartbeatMsg;
 import org.opends.server.replication.protocol.DoneMsg;
 import org.opends.server.replication.protocol.EntryMsg;
 import org.opends.server.replication.protocol.ErrorMsg;
@@ -3045,6 +3045,7 @@
     return eligibleCN;
   }
 
+
   /**
    * Processes a ChangeTimeHeartbeatMsg received, by storing the CN (timestamp)
    * value received, and forwarding the message to the other RSes.
@@ -3070,7 +3071,7 @@
       if (senderHandler.isDataServer())
       {
         // If we are the first replication server warned,
-        // then forwards the reset message to the remote replication servers
+        // then forwards the message to the remote replication servers
         for (ReplicationServerHandler rsHandler : replicationServers.values())
         {
           try
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerHandler.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerHandler.java
index 2c61852..a288986 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerHandler.java
@@ -631,7 +631,8 @@
             serverId, dsInfo.getDsId(), dsInfo.getGenerationId(),
             dsInfo.getGroupId(), dsInfo.getStatus(), dsInfo.getRefUrls(),
             dsInfo.isAssured(), dsInfo.getAssuredMode(),
-            dsInfo.getSafeDataLevel());
+            dsInfo.getSafeDataLevel(),
+            dsInfo.getEclIncludes());
         lsh.startHandler();
         remoteDirectoryServers.put(lsh.getServerId(), lsh);
       }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerReader.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerReader.java
index 0bae9e6..6e35b64 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerReader.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerReader.java
@@ -26,38 +26,19 @@
  */
 package org.opends.server.replication.server;
 
-import org.opends.messages.Message;
-
-import static org.opends.server.loggers.ErrorLogger.logError;
 import static org.opends.messages.ReplicationMessages.*;
-import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.loggers.ErrorLogger.logError;
+import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
+import static org.opends.server.loggers.debug.DebugLogger.getTracer;
 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
 
-
 import java.io.IOException;
 
+import org.opends.messages.Message;
 import org.opends.server.api.DirectoryThread;
-import org.opends.server.replication.protocol.AckMsg;
-import org.opends.server.replication.protocol.DoneMsg;
-import org.opends.server.replication.protocol.EntryMsg;
-import org.opends.server.replication.protocol.ErrorMsg;
-import org.opends.server.replication.protocol.ResetGenerationIdMsg;
-import org.opends.server.replication.protocol.InitializeRequestMsg;
-import org.opends.server.replication.protocol.InitializeTargetMsg;
-import org.opends.server.replication.protocol.ProtocolSession;
-import org.opends.server.replication.protocol.ReplicationMsg;
-import org.opends.server.replication.protocol.UpdateMsg;
-import org.opends.server.replication.protocol.WindowMsg;
-import org.opends.server.replication.protocol.WindowProbeMsg;
-import org.opends.server.replication.protocol.TopologyMsg;
-import org.opends.server.replication.protocol.MonitorMsg;
-import org.opends.server.replication.protocol.MonitorRequestMsg;
-import org.opends.server.replication.protocol.ChangeTimeHeartbeatMsg;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.replication.common.ServerStatus;
-import org.opends.server.replication.protocol.ChangeStatusMsg;
-import org.opends.server.replication.protocol.
-  NotSupportedOldVersionPDUException;
+import org.opends.server.replication.protocol.*;
 
 /**
  * This class implement the part of the replicationServer that is reading
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java
index cfd8337..be3237c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java
@@ -1119,6 +1119,8 @@
               domain.isAssured(),
               domain.getAssuredMode(),
               domain.getAssuredSdLevel());
+        startSessionMsg.setEclIncludes(
+            domain.getEclInclude());
       } else
       {
         startSessionMsg =
@@ -1943,7 +1945,7 @@
 
   private boolean debugEnabled()
   {
-    return false;
+    return true;
   }
 
   private static final void debugInfo(String s)
@@ -2165,8 +2167,22 @@
         rsList = topoMsg.getRsList();
       }
     }
+    if (domain != null)
+    {
+      for (DSInfo info : dsList)
+      {
+        for (String attr : info.getEclIncludes())
+        {
+          domain.addEclInclude(attr);
+        }
+      }
+      if (debugEnabled())
+      {
+        TRACER.debugInfo("domain: " + domain.getServiceID() +
+            " EclIncludes" + domain.getEclInclude());
+      }
+    }
   }
-
   /**
    * Check if the broker could not find any Replication Server and therefore
    * connection attempt failed.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java
index c39d447..f70d3ae 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java
@@ -64,6 +64,7 @@
 import java.net.SocketTimeoutException;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.SortedMap;
@@ -318,6 +319,9 @@
   private Map<Short, ServerState> replicaStates =
     new HashMap<Short, ServerState>();
 
+  Set<String> cfgEclIncludes = new HashSet<String>();
+  Set<String>    eClIncludes = new HashSet<String>();
+
   /**
    * Returns the {@link ChangeNumberGenerator} that will be used to
    * generate {@link ChangeNumber} for this domain.
@@ -2923,4 +2927,32 @@
     else
       return 0;
   }
+
+  /**
+   * Add an attribute to the list of attributes to include in the ECL.
+   * @param attribute The attribute to add.
+   */
+  synchronized public void addEclInclude(String attribute)
+  {
+    eClIncludes.add(attribute);
+  }
+
+  /**
+   * Get the list of attributes to include in the ECL.
+   * @return The list of attributes.
+   */
+  public Set<String> getEclInclude()
+  {
+    return eClIncludes;
+  }
+
+  /**
+   * Set the list of attributes to include in the ECL.
+   * @param eclIncludes The list of attributes.
+   */
+  protected void setCfgEclInclude(Set<String> eclIncludes)
+  {
+    this.cfgEclIncludes = eclIncludes;
+    this.eClIncludes = eclIncludes;
+  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/RawAttribute.java b/opendj-sdk/opends/src/server/org/opends/server/types/RawAttribute.java
index 35435ef..1e9ad43 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/RawAttribute.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/RawAttribute.java
@@ -218,8 +218,8 @@
         stream.writeOctetString(value);
       }
     }
-    stream.writeEndSequence();
 
+    stream.writeEndSet();
     stream.writeEndSequence();
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java
index 1a40621..459b45a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java
@@ -82,6 +82,7 @@
 import org.opends.server.replication.protocol.UpdateMsg;
 import org.opends.server.replication.server.ReplicationServer;
 import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeBuilder;
 import org.opends.server.types.AttributeType;
 import org.opends.server.types.AttributeValue;
 import org.opends.server.types.Attributes;
@@ -720,6 +721,8 @@
       // Map the addMsg to an LDIF string for the 'changes' attribute
       String LDIFchanges = addMsgToLDIFString(addMsg);
 
+      ArrayList<RawAttribute> eclAttributes = addMsg.getEclIncludes();
+
       clEntry = createChangelogEntry(
           eclmsg.getServiceId(),
           eclmsg.getCookie().toString(),
@@ -728,8 +731,7 @@
           LDIFchanges, // entry as created (in LDIF format)
           addMsg.getUniqueId(),
           null, // real time current entry
-          null, // real time attrs names
-          null, // hist entry attributes
+          eclAttributes, // entry attributes
           eclmsg.getDraftChangeNumber(),
       "add");
 
@@ -747,8 +749,8 @@
             (ModifyOperation)modMsg.createOperation(conn);
           String LDIFchanges = modToLDIF(modifyOperation.getModifications());
 
-          // TODO:ECL Hist entry attributes
-          // ArrayList<RawAttribute> attributes = modMsg.getEntryAttributes();
+          ArrayList<RawAttribute> eclAttributes = modMsg.getEclIncludes();
+
           clEntry = createChangelogEntry(
               eclmsg.getServiceId(),
               eclmsg.getCookie().toString(),
@@ -757,10 +759,9 @@
               LDIFchanges,
               modMsg.getUniqueId(),
               null, // real time current entry
-              null, // real time attrs names
-              null, // hist entry attributes
+              eclAttributes, // entry attributes
               eclmsg.getDraftChangeNumber(),
-          "modify");
+              "modify");
 
         }
         catch(Exception e)
@@ -775,6 +776,8 @@
       {
         ModifyDNMsg modDNMsg = (ModifyDNMsg)msg;
 
+        ArrayList<RawAttribute> eclAttributes = modDNMsg.getEclIncludes();
+
         clEntry = createChangelogEntry(
             eclmsg.getServiceId(),
             eclmsg.getCookie().toString(),
@@ -783,10 +786,9 @@
             null,
             modDNMsg.getUniqueId(),
             null, // real time current entry
-            null, // real time attrs names
-            null, // hist entry attributes
+            eclAttributes, // entry attributes
             eclmsg.getDraftChangeNumber(),
-        "modrdn");
+           "modrdn");
 
         Attribute a = Attributes.create("newrdn", modDNMsg.getNewRDN());
         clEntry.addAttribute(a, null);
@@ -806,25 +808,20 @@
       else if (msg instanceof DeleteMsg)
       {
         DeleteMsg delMsg = (DeleteMsg)msg;
-        /* TODO:ECL Entry attributes for DEL op
-        ArrayList<RawAttribute> rattributes = new ArrayList<RawAttribute>();
-        ArrayList<RawAttribute> rattributes = delMsg.getEntryAttributes();
-        // Map the entry attributes of the DelMsg to an LDIF string
-        // for the 'deletedentryattributes' attribute of the CL entry
-        String delAttrs = delMsgToLDIFString(rattributes);
-        */
+
+        ArrayList<RawAttribute> eclAttributes = delMsg.getEclIncludes();
+
         clEntry = createChangelogEntry(
             eclmsg.getServiceId(),
             eclmsg.getCookie().toString(),
             DN.decode(delMsg.getDn()),
             delMsg.getChangeNumber(),
-            null,
+            null, // no changes
             delMsg.getUniqueId(),
             null,
-            null,
-            null, //rattributes,
+            eclAttributes, // entry attributes
             eclmsg.getDraftChangeNumber(),
-        "delete");
+           "delete");
       }
     return clEntry;
   }
@@ -861,8 +858,6 @@
    * @param clearLDIFchanges     The provided LDIF changes for ADD and MODIFY
    * @param targetUUID      The provided targetUUID.
    * @param entry           The provided related current entry.
-   * @param targetAttrNames The provided list of attributes names that should
-   *                        be read from the entry (real time values)
    * @param histEntryAttributes TODO:ECL Adress hist entry attributes
    * @param draftChangenumber The provided draft change number (integer)
    * @param changetype      The provided change type (add, ...)
@@ -878,7 +873,6 @@
       String clearLDIFchanges,
       String targetUUID,
       Entry entry,
-      List<String> targetAttrNames,
       List<RawAttribute> histEntryAttributes,
       int draftChangenumber,
       String changetype)
@@ -1013,28 +1007,13 @@
 
     if (clearLDIFchanges != null)
     {
-      if (changetype.equalsIgnoreCase("delete"))
-      {
-        a = Attributes.create("clearDeletedEntryAttrs", clearLDIFchanges);
-      }
-      attrList = new ArrayList<Attribute>(1);
-      attrList.add(a);
-      operationalAttrs.put(a.getAttributeType(), attrList);
+      if((attributeType =
+        DirectoryServer.getAttributeType("changes")) == null)
+        attributeType =
+            DirectoryServer.getDefaultAttributeType("changes");
 
-      if (changetype.equalsIgnoreCase("delete"))
-      {
-        a = Attributes.create("deletedentryattrs",
-            clearLDIFchanges + "\n"); // force base64
-      }
-      else
-      {
-        if((attributeType =
-          DirectoryServer.getAttributeType("changes")) == null)
-          attributeType =
-              DirectoryServer.getDefaultAttributeType("changes");
-        a = Attributes.create(attributeType, clearLDIFchanges + "\n");
-        // force base64
-      }
+      a = Attributes.create(attributeType, clearLDIFchanges + "\n");
+      // force base64
       attrList = new ArrayList<Attribute>(1);
       attrList.add(a);
       if(attributeType.isOperational())
@@ -1087,68 +1066,21 @@
     else
       uAttrs.put(attributeType, attrList);
 
-    // entryAttribute version
-    /*
-    if (targetAttrNames != null)
-    {
-      String sEntryAttrs = null;
-      for (String attrName : targetAttrNames)
-      {
-        List<Attribute> attrs = entry.getAttribute(attrName);
-        for (Attribute attr : attrs)
-        {
-          if (sEntryAttrs==null)
-            sEntryAttrs="";
-          else
-            sEntryAttrs+=";";
-          sEntryAttrs += attr.toString();
-        }
-      }
-      if (sEntryAttrs!=null)
-      {
-        a = Attributes.create("entryAttributes", sEntryAttrs);
-        attrList = new ArrayList<Attribute>(1);
-        attrList.add(a);
-        uAttrs.put(a.getAttributeType(), attrList);
-      }
-    }
-     */
-
-    /*
-    if (targetAttrNames != null)
-    {
-      for (String attrName : targetAttrNames)
-      {
-        String newName = "target"+attrName;
-        List<Attribute> attrs = entry.getAttribute(attrName);
-        for (Attribute aa : attrs)
-        {
-          AttributeBuilder builder = new AttributeBuilder(
-            DirectoryServer.getDefaultAttributeType(newName));
-          builder.setOptions(aa.getOptions());
-          builder.addAll(aa);
-          attrList = new ArrayList<Attribute>(1);
-          attrList.add(builder.toAttribute());
-          uAttrs.put(aa.getAttributeType(), attrList);
-        }
-      }
-    }
-     */
-    /* TODO: Implement entry attributes historical values
     if (histEntryAttributes != null)
     {
-      for (RawAttribute rea : histEntryAttributes)
+      for (RawAttribute ra : histEntryAttributes)
       {
-        // uAttrs.put(ea.getAttributeType(), null);
-        // FIXME: ERRONEOUS TYPING !!!!!!!!!!!!!
         try
         {
-          String newName = "target"+rea.getAttributeType();
-          AttributeType nat=DirectoryServer.getDefaultAttributeType(newName);
-          rea.setAttributeType(newName);
+          String eclName = "target" + ra.getAttributeType().toLowerCase();
+          AttributeBuilder builder = new AttributeBuilder(
+              DirectoryServer.getDefaultAttributeType(eclName));
+          AttributeType at = builder.getAttributeType();
+          builder.setOptions(ra.toAttribute().getOptions());
+          builder.addAll(ra.toAttribute());
           attrList = new ArrayList<Attribute>(1);
-          attrList.add(rea.toAttribute());
-          uAttrs.put(nat, attrList);
+          attrList.add(builder.toAttribute());
+          uAttrs.put(at, attrList);
         }
         catch(Exception e)
         {
@@ -1156,7 +1088,6 @@
         }
       }
     }
-     */
 
     // at the end build the CL entry to be returned
     Entry cle = new Entry(
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java
index 1c88152..3a644bb 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java
@@ -27,9 +27,8 @@
 package org.opends.server.replication;
 
 import static org.opends.server.TestCaseUtils.TEST_ROOT_DN_STRING;
-import static org.opends.server.loggers.ErrorLogger.logError;
-import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
 import static org.opends.server.loggers.debug.DebugLogger.getTracer;
+import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
 import static org.opends.server.replication.protocol.OperationContext.SYNCHROCONTEXT;
 import static org.opends.server.util.StaticUtils.createEntry;
 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
@@ -45,17 +44,16 @@
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
-import org.opends.messages.Category;
-import org.opends.messages.Message;
-import org.opends.messages.Severity;
 import org.opends.server.TestCaseUtils;
 import org.opends.server.api.Backend;
 import org.opends.server.api.ConnectionHandler;
@@ -66,8 +64,10 @@
 import org.opends.server.controls.PersistentSearchChangeType;
 import org.opends.server.controls.PersistentSearchControl;
 import org.opends.server.core.AddOperationBasis;
+import org.opends.server.core.DeleteOperationBasis;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ModifyDNOperationBasis;
+import org.opends.server.core.ModifyOperationBasis;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.plugins.InvocationCounterPlugin;
 import org.opends.server.protocols.asn1.ASN1Exception;
@@ -103,7 +103,6 @@
 import org.opends.server.replication.protocol.StartECLSessionMsg;
 import org.opends.server.replication.protocol.UpdateMsg;
 import org.opends.server.replication.server.DraftCNDbHandler;
-import org.opends.server.replication.server.ExternalChangeLogSessionImpl;
 import org.opends.server.replication.server.ReplServerFakeConfiguration;
 import org.opends.server.replication.server.ReplicationServer;
 import org.opends.server.replication.server.ReplicationServerDomain;
@@ -111,6 +110,7 @@
 import org.opends.server.tools.LDAPSearch;
 import org.opends.server.tools.LDAPWriter;
 import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeBuilder;
 import org.opends.server.types.AttributeValue;
 import org.opends.server.types.Attributes;
 import org.opends.server.types.ByteString;
@@ -157,6 +157,9 @@
   private static final String TEST_ROOT_DN_STRING2 = "o=test2";
   private static final String TEST_BACKEND_ID2 = "test2";
 
+  private static final String TEST_ROOT_DN_STRING3 = "o=test3";
+  private static final String TEST_BACKEND_ID3 = "test3";
+
   // The LDAPStatistics object associated with the LDAP connection handler.
   private LDAPStatistics ldapStatistics;
 
@@ -206,8 +209,9 @@
   @Test(enabled=true)
   public void ECLReplicationServerTest()
   {
-    // --
+    // ***********************************************
     // First set of test are in the cookie mode
+    // ***********************************************
     
     // Test that private backend is excluded from ECL
     ECLOnPrivateBackend();replicationServer.clearDb();
@@ -250,7 +254,6 @@
     // TODO:ECL Test SEARCH abandon and check everything shutdown and cleaned
     // TODO:ECL Test PSEARCH abandon and check everything shutdown and cleaned
     // TODO:ECL Test invalid DN in cookie returns UNWILLING + message
-    // TODO:ECL Test notif control returned contains the cookie
     // TODO:ECL Test the attributes list and values returned in ECL entries
     // TODO:ECL Test search -s base, -s one
     
@@ -262,8 +265,9 @@
     // optimize the request.
     ECLFilterTest();
 
-    // --
+    // ***********************************************
     // Second set of test are in the draft compat mode
+    // ***********************************************
     
     // Empty replication changelog
     ECLCompatEmpty();
@@ -309,6 +313,10 @@
     // Test simultaneous persistent searches in draft compat mode.
     ECLSimultaneousPsearches();replicationServer.clearDb();
     
+    // ***********************************************
+    // Entry attributes
+    // ***********************************************
+    ECLIncludeAttributes();replicationServer.clearDb();
   }
 
   //=======================================================
@@ -391,7 +399,6 @@
       server1.stop();
       server2.stop();
       server3.stop();
-      sleep(500);
       debugInfo(tn, "Ending test successfully\n\n");    
     }
     catch(Exception e)
@@ -484,8 +491,7 @@
       // clean
       serverECL.stop();
       server01.stop();
-      server02.stop();      
-      sleep(2000);
+      server02.stop();
       debugInfo(tn, "Ending test successfully");
     }
     catch(Exception e)
@@ -605,7 +611,8 @@
       // Initialize a second test backend o=test2, in addtion to o=test
       // Configure replication on this backend
       // Add the root entry in the backend
-      Backend backend2 = initializeTestBackend2(false);
+      Backend backend2 = initializeTestBackend(false, TEST_ROOT_DN_STRING2,
+          TEST_BACKEND_ID2);
       DN baseDn2 = DN.decode(TEST_ROOT_DN_STRING2);
       SortedSet<String> replServers = new TreeSet<String>();
       replServers.add("localhost:"+replicationServerPort);
@@ -647,16 +654,25 @@
       assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS,
           searchOp.getErrorMessage().toString() + searchOp.getAdditionalLogMessage());
       LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries();
+      assertEquals(entries.size(),2, "Entries number returned by search");
       assertTrue(entries != null);
       if (entries != null)
+      {
+        int i = 0;
         for (SearchResultEntry resultEntry : entries)
         {
+          i++;
           // Expect 
           debugInfo(tn, "Entry returned when test2 is public =" +
               resultEntry.toLDIFString());
+          
+          // Test entry attributes
+          //if (i==2)
+          //{
+          //  checkPossibleValues(resultEntry,"targetobjectclass","top","organization");            
+          //}
         }
-      assertEquals(entries.size(),2, "Entries number returned by search");
-
+      }
       //
       // Set the backend private and do again a search on ECL that should
       // now not return the entry
@@ -732,7 +748,8 @@
     try
     {
       // Initialize a second test backend
-      Backend backend2 = initializeTestBackend2(true);
+      Backend backend2 = initializeTestBackend(true,
+          TEST_ROOT_DN_STRING2, TEST_BACKEND_ID2);
 
       //
       LDIFWriter ldifWriter = getLDIFWriter();
@@ -1485,6 +1502,22 @@
     }
   }
 
+  private static String getAttributeValue(Entry entry, String attrName)
+  {
+    AttributeValue av = null;
+    try
+    {
+      List<Attribute> attrs = entry.getAttribute(attrName);
+      Attribute a = attrs.iterator().next();
+      av = a.iterator().next();
+      return av.toString();
+    }
+    catch(Exception e)
+    {
+    }
+    return null;
+  }
+
   private static void checkPossibleValues(Entry entry, String attrName, 
       String expectedValue1, String expectedValue2)
   {
@@ -1511,6 +1544,36 @@
     }
   }
 
+  private static void checkValues(Entry entry, String attrName, 
+      Set<String> expectedValues)
+  {
+    AttributeValue av = null;
+    int i=0;
+    try
+    {
+      List<Attribute> attrs = entry.getAttribute(attrName);
+      Attribute a;
+      Iterator<Attribute> iat = attrs.iterator();
+      while ((a=iat.next())!=null)
+      {
+        Iterator<AttributeValue> iatv = a.iterator();
+        while ((av = iatv.next())!=null)
+        {
+          String encodedValue = av.toString();
+          assertTrue(
+              expectedValues.contains(encodedValue),
+              "In entry " + entry + " attr <" + attrName + "> equals " +
+              av + " instead of one of the expected values " + expectedValues);
+          i++;
+        }
+      }
+    }
+    catch(NoSuchElementException e)
+    {
+      assertTrue(i==expectedValues.size());
+    }
+  }
+
   /**
    * Test persistent search
    */
@@ -2385,23 +2448,26 @@
    */
   private void debugInfo(String tn, String s)
   {
-    //if (debugEnabled())
+    if (debugEnabled())
     {
-      logError(Message.raw(Category.SYNC, Severity.NOTICE,
-          "** TEST " + tn + " ** " + s));
-      //TRACER.debugInfo("** TEST " + tn + " ** " + s);
+//      logError(Message.raw(Category.SYNC, Severity.NOTICE,
+//          "** TEST " + tn + " ** " + s));
+      TRACER.debugInfo("** TEST " + tn + " ** " + s);
     }
   }
 
   /**
    * Utility - create a second backend in order to test ECL with 2 suffixes.
    */
-  private static Backend initializeTestBackend2(boolean createBaseEntry)
+  private static Backend initializeTestBackend(
+      boolean createBaseEntry,
+      String rootDN,
+      String backendId)
   throws IOException, InitializationException, ConfigException,
   DirectoryException
   {
 
-    DN baseDN = DN.decode(TEST_ROOT_DN_STRING2);
+    DN baseDN = DN.decode(rootDN);
 
     //  Retrieve backend. Warning: it is important to perform this each time,
     //  because a test may have disabled then enabled the backend (i.e a test
@@ -2410,12 +2476,12 @@
     //  to memory backend must be invalidated. So to prevent this problem, we
     //  retrieve the memory backend reference each time before cleaning it.
     MemoryBackend memoryBackend =
-      (MemoryBackend)DirectoryServer.getBackend(TEST_BACKEND_ID2);
+      (MemoryBackend)DirectoryServer.getBackend(backendId);
 
     if (memoryBackend == null)
     {
       memoryBackend = new MemoryBackend();
-      memoryBackend.setBackendID(TEST_BACKEND_ID2);
+      memoryBackend.setBackendID(backendId);
       memoryBackend.setBaseDNs(new DN[] {baseDN});
       memoryBackend.initializeBackend();
       DirectoryServer.registerBackend(memoryBackend);
@@ -2447,7 +2513,8 @@
     try
     {
       // Initialize a second test backend
-      Backend backend2 = initializeTestBackend2(true);
+      Backend backend2 = initializeTestBackend(true, TEST_ROOT_DN_STRING2,
+          TEST_BACKEND_ID2);
 
       // --
       ReplicationBroker s1test = openReplicationSession(
@@ -2862,8 +2929,6 @@
         }
       }
       assertEquals(searchOp.getSearchEntries().size(), 4);
-
-
     }
     catch(Exception e)
     {
@@ -3410,4 +3475,220 @@
     debugInfo(tn, "Ending test with success");
   }
 
+  /**
+   * Test ECl entry attributes, and there configuration.
+   *
+   */
+  private void ECLIncludeAttributes()
+  {
+    String tn = "ECLIncludeAttributes";
+    debugInfo(tn, "Starting test\n\n");
+    try
+    {
+      // Initialize a second test backend o=test2, in addtion to o=test
+      // Configure replication on this backend
+      // Add the root entry in the backend
+      Backend backend2 = initializeTestBackend(false,
+          TEST_ROOT_DN_STRING2, TEST_BACKEND_ID2);
+      DN baseDn2 = DN.decode(TEST_ROOT_DN_STRING2);
+      SortedSet<String> replServers = new TreeSet<String>();
+      replServers.add("localhost:"+replicationServerPort);
+      DomainFakeCfg domainConf =
+        new DomainFakeCfg(baseDn2, (short) 1702, replServers);
+      SortedSet<String> includeAttributes = new TreeSet<String>();
+      includeAttributes.add("sn");
+      domainConf.setEclIncludes(includeAttributes);
+      LDAPReplicationDomain domain2 = MultimasterReplication.createNewDomain(domainConf);
+      SynchronizationProvider replicationPlugin2 = new MultimasterReplication();
+      replicationPlugin2.completeSynchronizationProvider();
+
+      Backend backend3 = initializeTestBackend(false,
+          TEST_ROOT_DN_STRING3, TEST_BACKEND_ID3);
+      DN baseDn3 = DN.decode(TEST_ROOT_DN_STRING3);
+      domainConf =
+        new DomainFakeCfg(baseDn3, (short) 1703, replServers);
+      includeAttributes = new TreeSet<String>();
+      includeAttributes.add("objectclass");
+      domainConf.setEclIncludes(includeAttributes);
+      LDAPReplicationDomain domain3 = MultimasterReplication.createNewDomain(domainConf);
+      SynchronizationProvider replicationPlugin3 = new MultimasterReplication();
+      replicationPlugin3.completeSynchronizationProvider();
+
+      domainConf =
+        new DomainFakeCfg(baseDn2, (short) 1704, replServers);
+      includeAttributes = new TreeSet<String>();
+      includeAttributes.add("cn");
+      domainConf.setEclIncludes(includeAttributes);
+      LDAPReplicationDomain domain21 = MultimasterReplication.createNewDomain(domainConf);
+
+      Set<String> attrList = new HashSet<String>();
+      attrList.add(new String("cn"));
+      ReplicationBroker server01 = openReplicationSession(
+          DN.decode(TEST_ROOT_DN_STRING2), (short) 1206, 
+          100, replicationServerPort,
+          1000, true, -1 , domain21);
+
+      sleep(1000);
+
+      Entry e2 = createEntry(baseDn2);
+      addEntry(e2);
+
+      Entry e3 = createEntry(baseDn3);
+      addEntry(e3);
+
+      String lentry = new String(
+          "dn: cn=Fiona Jensen," + TEST_ROOT_DN_STRING2 + "\n"
+          + "objectclass: top\n"
+          + "objectclass: person\n"
+          + "objectclass: organizationalPerson\n"
+          + "objectclass: inetOrgPerson\n"
+          + "cn: Fiona Jensen\n"
+          + "sn: Jensen\n"
+          + "uid: fiona\n"
+          + "telephonenumber: 12121212");
+          
+      Entry uentry1 = TestCaseUtils.entryFromLdifString(lentry);
+      addEntry(uentry1);
+
+      lentry = new String(
+          "dn: cn=Robert Hue," + TEST_ROOT_DN_STRING3 + "\n"
+          + "objectclass: top\n"
+          + "objectclass: person\n"
+          + "objectclass: organizationalPerson\n"
+          + "objectclass: inetOrgPerson\n"
+          + "cn: Robert Hue\n"
+          + "sn: Robby\n"
+          + "uid: robert\n"
+          + "telephonenumber: 131313");
+      Entry uentry2 = TestCaseUtils.entryFromLdifString(lentry);
+      addEntry(uentry2);
+
+      AttributeBuilder builder = new AttributeBuilder("sn");
+      builder.add("newsn");
+      Modification mod =
+        new Modification(ModificationType.REPLACE, builder.toAttribute());
+      List<Modification> mods = new ArrayList<Modification>();
+      mods.add(mod);
+
+      ModifyOperationBasis modOpBasis =
+        new ModifyOperationBasis(connection, 1, 1, null, uentry1.getDN(), mods);
+      modOpBasis.run();
+      
+      builder = new AttributeBuilder("telephonenumber");
+      builder.add("555555");
+      mod =
+        new Modification(ModificationType.REPLACE, builder.toAttribute());
+      mods = new ArrayList<Modification>();
+      mods.add(mod);
+
+      modOpBasis =
+        new ModifyOperationBasis(connection, 1, 1, null, uentry2.getDN(), mods);
+      modOpBasis.run();
+      
+      ModifyDNOperationBasis modDNOp = new ModifyDNOperationBasis(connection,
+          InternalClientConnection.nextOperationID(), 
+          InternalClientConnection.nextMessageID(),
+          null,
+          DN.decode("cn=Robert Hue," + TEST_ROOT_DN_STRING3),
+          RDN.decode("cn=Robert Hue2"), true,
+          DN.decode(TEST_ROOT_DN_STRING3));
+      modDNOp.run();
+      assertEquals(modDNOp.getResultCode(), ResultCode.SUCCESS,
+          modDNOp.getErrorMessage().toString() + modDNOp.getAdditionalLogMessage());
+
+      DeleteOperationBasis delOp = new DeleteOperationBasis(connection,
+          InternalClientConnection.nextOperationID(), 
+          InternalClientConnection.nextMessageID(), null, 
+          DN.decode("cn=Robert Hue2," + TEST_ROOT_DN_STRING3));
+      delOp.run();
+      assertEquals(delOp.getResultCode(), ResultCode.SUCCESS,
+          delOp.getErrorMessage().toString() + delOp.getAdditionalLogMessage());
+
+      sleep(400);
+
+      // Search on ECL from start on all suffixes
+      String cookie = "";
+      ExternalChangelogRequestControl control =
+        new ExternalChangelogRequestControl(true, 
+            new MultiDomainServerState(cookie));
+      ArrayList<Control> controls = new ArrayList<Control>(0);
+      controls.add(control);
+      LinkedHashSet<String> attributes = new LinkedHashSet<String>();
+      attributes.add("+");
+      attributes.add("*");
+
+
+      debugInfo(tn, "Search with cookie=" + cookie);
+      InternalSearchOperation searchOp = connection.processSearch(
+          ByteString.valueOf("cn=changelog"),
+          SearchScope.WHOLE_SUBTREE,
+          DereferencePolicy.NEVER_DEREF_ALIASES, 
+          0, // Size limit
+          0, // Time limit
+          false, // Types only
+          LDAPFilter.decode("(targetDN=*)"),
+          attributes,
+          controls,
+          null);
+
+      sleep(500);
+
+      // Expect SUCCESS and root entry returned
+      assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS,
+          searchOp.getErrorMessage().toString() + searchOp.getAdditionalLogMessage());
+      LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries();
+
+
+      assertEquals(entries.size(),8, "Entries number returned by search");
+      assertTrue(entries != null);
+
+      if (entries != null)
+      {
+        for (SearchResultEntry resultEntry : entries)
+        {
+          // Expect 
+          debugInfo(tn, "Entry returned =" +  resultEntry.toLDIFString());
+          
+          String targetdn = getAttributeValue(resultEntry, "targetdn");
+          if ((targetdn.endsWith("cn=robert hue,o=test3"))
+            ||(targetdn.endsWith("cn=robert hue2,o=test3")))
+          {
+            HashSet<String> eoc = new HashSet<String>();
+            eoc.add("person");eoc.add("inetOrgPerson");eoc.add("organizationalPerson");eoc.add("top");
+            checkValues(resultEntry,"targetobjectclass",eoc);
+          }
+          if (targetdn.endsWith("cn=fiona jensen,o=test2"))
+          {
+            checkValue(resultEntry,"targetsn","jensen");
+            checkValue(resultEntry,"targetcn","Fiona Jensen");
+          }
+        }
+      }
+      server01.stop();
+
+      // Cleaning
+      if (domain2 != null)
+        MultimasterReplication.deleteDomain(baseDn2);
+      if (replicationPlugin2 != null)
+        DirectoryServer.deregisterSynchronizationProvider(replicationPlugin2);
+      removeTestBackend2(backend2);
+      
+      if (domain3 != null)
+        MultimasterReplication.deleteDomain(baseDn3);
+      if (replicationPlugin3 != null)
+        DirectoryServer.deregisterSynchronizationProvider(replicationPlugin3);
+      removeTestBackend2(backend3);
+      
+    }
+    catch(Exception e)
+    {
+      fail("Ending "+tn+" test with exception:\n"
+          +  stackTraceToSingleLineString(e));
+    }
+    finally
+    {
+      
+    }
+    debugInfo(tn, "Ending test with success");
+  }  
 }
\ No newline at end of file
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
index d5bac70..45b7986 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
@@ -26,8 +26,9 @@
  */
 package org.opends.server.replication;
 
-import java.io.File;
-import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.config.ConfigConstants.ATTR_TASK_COMPLETION_TIME;
+import static org.opends.server.config.ConfigConstants.ATTR_TASK_LOG_MESSAGES;
+import static org.opends.server.config.ConfigConstants.ATTR_TASK_STATE;
 import static org.opends.server.loggers.ErrorLogger.logError;
 import static org.opends.server.loggers.debug.DebugLogger.getTracer;
 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
@@ -36,6 +37,7 @@
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
+import java.io.File;
 import java.net.SocketException;
 import java.util.ArrayList;
 import java.util.LinkedList;
@@ -48,6 +50,7 @@
 import org.opends.messages.MessageBuilder;
 import org.opends.messages.Severity;
 import org.opends.server.DirectoryServerTestCase;
+import org.opends.server.TestCaseUtils;
 import org.opends.server.backends.task.TaskState;
 import org.opends.server.config.ConfigException;
 import org.opends.server.core.AddOperation;
@@ -58,21 +61,35 @@
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.protocols.internal.InternalSearchOperation;
 import org.opends.server.protocols.ldap.LDAPFilter;
-import org.opends.server.replication.service.ReplicationBroker;
 import org.opends.server.replication.common.ServerState;
-import org.opends.server.replication.plugin.PersistentServerState;
 import org.opends.server.replication.plugin.LDAPReplicationDomain;
+import org.opends.server.replication.plugin.MultimasterReplication;
+import org.opends.server.replication.plugin.PersistentServerState;
 import org.opends.server.replication.protocol.ErrorMsg;
 import org.opends.server.replication.protocol.ReplSessionSecurity;
 import org.opends.server.replication.protocol.ReplicationMsg;
+import org.opends.server.replication.service.ReplicationBroker;
+import org.opends.server.replication.service.ReplicationDomain;
 import org.opends.server.schema.DirectoryStringSyntax;
 import org.opends.server.schema.IntegerSyntax;
-import org.opends.server.types.*;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.AttributeValues;
+import org.opends.server.types.Attributes;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.LockManager;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchScope;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
-import org.opends.server.TestCaseUtils;
-import org.opends.server.replication.plugin.MultimasterReplication;
 
 /**
  * An abstract class that all Replication unit test should extend.
@@ -214,12 +231,26 @@
         long generationId)
   throws Exception, SocketException
   {
+    return openReplicationSession(baseDn, serverId, window_size,
+        port, timeout, emptyOldChanges, generationId, null);
+  }
+
+  /**
+   * Open a replicationServer session to the local ReplicationServer
+   * providing the generationId.
+   */
+  protected ReplicationBroker openReplicationSession(
+        final DN baseDn, short serverId, int window_size,
+        int port, int timeout, boolean emptyOldChanges,
+        long generationId, ReplicationDomain replicationDomain)
+  throws Exception, SocketException
+  {
     ServerState state = new ServerState();
 
     if (emptyOldChanges)
        new PersistentServerState(baseDn, serverId, new ServerState());
 
-    ReplicationBroker broker = new ReplicationBroker(null,
+    ReplicationBroker broker = new ReplicationBroker(replicationDomain,
         state, baseDn.toNormalizedString(), serverId, window_size,
         generationId, 100000, getReplSessionSecurity(), (byte)1, 500);
     ArrayList<String> servers = new ArrayList<String>(1);
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 798cb28..f03f32d 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
@@ -1316,7 +1316,7 @@
     final DN baseDn = DN.decode("ou=People," + TEST_ROOT_DN_STRING);
 
     ReplicationBroker broker =
-      openReplicationSession(baseDn, (short) 27, 100, replServerPort, 1000, true);
+      openReplicationSession(baseDn, (short) 27, 100, replServerPort, 2000, true);
     try {
       ChangeNumberGenerator gen = new ChangeNumberGenerator((short) 27, 0);
 
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/DomainFakeCfg.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/DomainFakeCfg.java
index 7f65980..286ed40 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/DomainFakeCfg.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/DomainFakeCfg.java
@@ -26,10 +26,11 @@
  */
 package org.opends.server.replication.plugin;
 
+import java.util.HashSet;
 import java.util.List;
 import java.util.SortedSet;
-
 import java.util.TreeSet;
+
 import org.opends.server.admin.Configuration;
 import org.opends.server.admin.server.ConfigurationChangeListener;
 import org.opends.server.admin.server.ServerManagedObject;
@@ -67,6 +68,8 @@
   private SortedSet<String> fractionalExcludes = new TreeSet<String>();
   private SortedSet<String> fractionalIncludes = new TreeSet<String>();
 
+  private SortedSet<String> eclIncludes = new TreeSet<String>();
+
   /**
    * Creates a new Domain with the provided information
    * (assured mode disabled, default group id)
@@ -356,4 +359,14 @@
   {
     return true;
   }
+  
+  public void setEclIncludes(SortedSet<String> attrs)
+  {
+    this.eclIncludes = attrs;
+  }
+ 
+  public SortedSet<String> getEclInclude()
+  {
+    return this.eclIncludes;
+  }
 }
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java
index 3f4c7ad..b72b145 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java
@@ -26,18 +26,24 @@
  */
 package org.opends.server.replication.plugin;
 
+import static org.opends.server.TestCaseUtils.TEST_ROOT_DN_STRING;
+import static org.opends.server.loggers.ErrorLogger.logError;
+import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
+import static org.opends.server.loggers.debug.DebugLogger.getTracer;
+import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+
 import java.io.IOException;
 import java.net.ServerSocket;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
-import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
-import static org.opends.server.loggers.ErrorLogger.logError;
-import static org.opends.server.loggers.debug.DebugLogger.getTracer;
-import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
-
 import org.opends.messages.Category;
 import org.opends.messages.Message;
 import org.opends.messages.Severity;
@@ -54,8 +60,6 @@
 import org.opends.server.types.DN;
 import org.opends.server.types.DirectoryException;
 import org.testng.annotations.Test;
-import static org.testng.Assert.*;
-import static org.opends.server.TestCaseUtils.*;
 
 /**
  * Some tests to know if at any time the view DSs and RSs have of the current
@@ -850,6 +854,7 @@
     AssuredType assuredType = null;
     int assuredSdLevel = -100;
     SortedSet<String> refUrls = null;
+    SortedSet<String> attrs = null;
 
     switch (dsId)
       {
@@ -904,7 +909,7 @@
     }
 
     return new DSInfo(dsId, rsId, TEST_DN_WITH_ROOT_ENTRY_GENID, status, assuredFlag, assMode,
-       (byte)assuredSdLevel, groupId, urls);
+       (byte)assuredSdLevel, groupId, urls, attrs);
   }
 
   /**
@@ -1103,8 +1108,9 @@
      byte safeDataLevel = rd.getAssuredSdLevel();
      byte groupId = rd.getGroupId();
      List<String> refUrls = rd.getRefUrls();
+     Set<String> eclInclude = rd.getEclInclude();
      DSInfo dsInfo = new DSInfo(dsId, rsId, TEST_DN_WITH_ROOT_ENTRY_GENID, status, assuredFlag, assuredMode,
-       safeDataLevel, groupId, refUrls);
+       safeDataLevel, groupId, refUrls, eclInclude);
      dsList.add(dsInfo);
 
      TopoView dsTopoView = new TopoView(dsList, rd.getRsList());
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 1bae3dd..5f9f96e 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
@@ -47,6 +47,7 @@
 import org.opends.server.types.ModificationType;
 import org.opends.server.types.ObjectClass;
 import org.opends.server.types.Operation;
+import org.opends.server.types.RawAttribute;
 import org.opends.server.util.TimeThread;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -62,7 +63,7 @@
  */
 public class ProtocolCompatibilityTest extends ReplicationTestCase {
 
-  short REPLICATION_PROTOCOL_VLAST = ProtocolVersion.REPLICATION_PROTOCOL_V3;
+  short REPLICATION_PROTOCOL_VLAST = ProtocolVersion.REPLICATION_PROTOCOL_V4;
   /**
    * Set up the environment for performing the tests in this Class.
    *
@@ -177,16 +178,24 @@
   }
 
   @DataProvider(name = "createAddData")
-  public Object[][] createAddData() {
+  public Object[][] createAddData() 
+  {
+    // Entry attributes
+    Attribute eattr1 = Attributes.create("description", "eav description");
+    Attribute eattr2 = Attributes.create("namingcontexts", "eav naming contexts");
+    List<Attribute> entryAttrList = new ArrayList<Attribute>();
+    entryAttrList.add(eattr1);
+    entryAttrList.add(eattr2);
+
     return new Object[][] {
-      {"dc=example,dc=com", false, AssuredMode.SAFE_DATA_MODE, (byte)0},
-      {"o=test", true, AssuredMode.SAFE_READ_MODE, (byte)1},
-      {"o=group,dc=example,dc=com", true, AssuredMode.SAFE_READ_MODE, (byte)3}};
+      {"dc=example,dc=com", false, AssuredMode.SAFE_DATA_MODE, (byte)0, null},
+      {"o=test", true, AssuredMode.SAFE_READ_MODE, (byte)1, entryAttrList},
+      {"o=group,dc=example,dc=com", true, AssuredMode.SAFE_READ_MODE, (byte)3, entryAttrList}};
   }
 
   @Test(dataProvider = "createAddData")
   public void addMsgTestVLASTV2(String rawDN, boolean isAssured, AssuredMode assuredMode,
-    byte safeDataLevel)
+    byte safeDataLevel, List<Attribute> entryAttrList)
          throws Exception
   {
     // TODO: addMsgTest as soon as V3 will have any incompatibility with V2
@@ -194,8 +203,8 @@
 
   @Test(dataProvider = "createAddData")
   public void addMsgTestVLASTV1(String rawDN, boolean isAssured, AssuredMode assuredMode,
-    byte safeDataLevel)
-         throws Exception
+    byte safeDataLevel, List<Attribute> entryAttrList)
+  throws Exception
   {
     // Create VLAST message
     Attribute objectClass = Attributes.create(DirectoryServer
@@ -228,6 +237,11 @@
     msg.setAssured(isAssured);
     msg.setAssuredMode(assuredMode);
     msg.setSafeDataLevel(safeDataLevel);
+    // Set ECL entry attributes
+    if (entryAttrList != null)
+    {
+      msg.setEclIncludes(entryAttrList);
+    }
 
     // Check version of message
     assertEquals(msg.getVersion(), REPLICATION_PROTOCOL_VLAST);
@@ -249,7 +263,7 @@
     assertEquals(newMsg.isAssured(), msg.isAssured());
     assertEquals(newMsg.getParentUid(), msg.getParentUid());
 
-    //        Create an add operation from each message to compare attributes (kept encoded in messages)
+    // Create an add operation from each message to compare attributes (kept encoded in messages)
     Operation op = msg.createOperation(connection, rawDN);
     Operation generatedOperation = newMsg.createOperation(connection, rawDN);
 
@@ -268,10 +282,15 @@
     // Check default value for only VLAST fields
     assertEquals(newMsg.getAssuredMode(), AssuredMode.SAFE_DATA_MODE);
     assertEquals(newMsg.getSafeDataLevel(), (byte)1);
+    assertEquals(newMsg.getEclIncludes().size(), 0);
 
     // Set again only VLAST fields
     newMsg.setAssuredMode(assuredMode);
     newMsg.setSafeDataLevel(safeDataLevel);
+    if (entryAttrList != null)
+    {
+      newMsg.setEclIncludes(entryAttrList);
+    }
 
     // Serialize in VLAST msg
     AddMsg vlastMsg = (AddMsg)ReplicationMsg.generateMsg(
@@ -289,6 +308,23 @@
     assertEquals(msg.getAssuredMode(), vlastMsg.getAssuredMode());
     assertEquals(msg.getSafeDataLevel(), vlastMsg.getSafeDataLevel());
 
+    // Get ECL entry attributes
+    ArrayList<RawAttribute> genAttrList = vlastMsg.getEclIncludes();
+    if (entryAttrList==null)
+      assertTrue(genAttrList.size()==0);
+    else
+    {
+      assertTrue(genAttrList.size()==entryAttrList.size());
+      int i=0;
+      for (Attribute eattr : entryAttrList)
+      {
+        assertTrue(eattr.getName().equalsIgnoreCase(genAttrList.get(i).toAttribute().getName()));
+        assertTrue(eattr.toString().equalsIgnoreCase(genAttrList.get(i).toAttribute().toString()),
+            "Comparing: " + eattr.toString() + " and " + genAttrList.get(i).toAttribute().toString());
+        i++;
+      }
+    }
+
     //        Create an add operation from each message to compare attributes (kept encoded in messages)
     op = msg.createOperation(connection, rawDN);
     generatedOperation = vlastMsg.createOperation(connection, rawDN);
@@ -310,11 +346,19 @@
    * Build some data for the DeleteMsg test below.
    */
   @DataProvider(name = "createDeleteData")
-  public Object[][] createDeleteData() {
+  public Object[][] createDeleteData()
+  {
+    // Entry attributes
+    Attribute eattr1 = Attributes.create("description", "eav description");
+    Attribute eattr2 = Attributes.create("namingcontexts", "eav naming contexts");
+    List<Attribute> entryAttrList = new ArrayList<Attribute>();
+    entryAttrList.add(eattr1);
+    entryAttrList.add(eattr2);
+    
     return new Object[][] {
-      {"dc=example,dc=com", false, AssuredMode.SAFE_DATA_MODE, (byte)0},
-      {"dc=delete,dc=an,dc=entry,dc=with,dc=a,dc=long dn", true, AssuredMode.SAFE_READ_MODE, (byte)1},
-      {"o=group,dc=example,dc=com", true, AssuredMode.SAFE_READ_MODE, (byte)3}};
+      {"dc=example,dc=com", false, AssuredMode.SAFE_DATA_MODE, (byte)0, null},
+      {"dc=delete,dc=an,dc=entry,dc=with,dc=a,dc=long dn", true, AssuredMode.SAFE_READ_MODE, (byte)1, entryAttrList},
+      {"o=group,dc=example,dc=com", true, AssuredMode.SAFE_READ_MODE, (byte)3, entryAttrList}};
   }
 
   /**
@@ -323,7 +367,7 @@
    */
   @Test(dataProvider = "createDeleteData")
   public void deleteMsgTestVLASTV2(String rawDN, boolean isAssured, AssuredMode assuredMode,
-    byte safeDataLevel)
+    byte safeDataLevel, List<Attribute> entryAttrList)
          throws Exception
   {
     // TODO: deleteMsgTestVLASTV2 as soon as V3 will have any incompatibility with V2
@@ -335,8 +379,8 @@
    */
   @Test(dataProvider = "createDeleteData")
   public void deleteMsgTestVLASTV1(String rawDN, boolean isAssured, AssuredMode assuredMode,
-    byte safeDataLevel)
-         throws Exception
+    byte safeDataLevel, List<Attribute> entryAttrList)
+  throws Exception
   {
     ChangeNumber cn = new ChangeNumber(TimeThread.getTime(),
       (short) 123, (short) 45);
@@ -346,6 +390,12 @@
     msg.setAssuredMode(assuredMode);
     msg.setSafeDataLevel(safeDataLevel);
 
+    // Set ECL entry attributes
+    if (entryAttrList != null)
+    {
+      msg.setEclIncludes(entryAttrList);
+    }
+
     // Check version of message
     assertEquals(msg.getVersion(), REPLICATION_PROTOCOL_VLAST);
 
@@ -368,10 +418,16 @@
     // Check default value for only VLAST fields
     assertEquals(newMsg.getAssuredMode(), AssuredMode.SAFE_DATA_MODE);
     assertEquals(newMsg.getSafeDataLevel(), (byte)1);
+    assertEquals(newMsg.getEclIncludes().size(), 0);
 
     // Set again only VLAST fields
     newMsg.setAssuredMode(assuredMode);
     newMsg.setSafeDataLevel(safeDataLevel);
+    // Set ECL entry attributes
+    if (entryAttrList != null)
+    {
+      newMsg.setEclIncludes(entryAttrList);
+    }
 
     // Serialize in VLAST msg
     DeleteMsg vlastMsg = (DeleteMsg)ReplicationMsg.generateMsg(
@@ -380,13 +436,30 @@
     // Check original version of message
     assertEquals(vlastMsg.getVersion(), REPLICATION_PROTOCOL_VLAST);
 
-    // Check we retrieve original V2 message (V2 fields)
+    // Check we retrieve original VLAST message (VLAST fields)
     assertEquals(msg.getUniqueId(), vlastMsg.getUniqueId());
     assertEquals(msg.getDn(), vlastMsg.getDn());
     assertEquals(msg.getChangeNumber(), vlastMsg.getChangeNumber());
     assertEquals(msg.isAssured(), vlastMsg.isAssured());
     assertEquals(msg.getAssuredMode(), vlastMsg.getAssuredMode());
     assertEquals(msg.getSafeDataLevel(), vlastMsg.getSafeDataLevel());
+    
+    // Get ECL entry attributes
+    ArrayList<RawAttribute> genAttrList = vlastMsg.getEclIncludes();
+    if (entryAttrList==null)
+      assertTrue(genAttrList.size()==0);
+    else
+    {
+      assertTrue(genAttrList.size()==entryAttrList.size());
+      int i=0;
+      for (Attribute attr : entryAttrList)
+      {
+        assertTrue(attr.getName().equalsIgnoreCase(genAttrList.get(i).toAttribute().getName()));
+        assertTrue(attr.toString().equalsIgnoreCase(genAttrList.get(i).toAttribute().toString()),
+            "Comparing: " + attr.toString() + " and " + genAttrList.get(i).toAttribute().toString());
+        i++;
+      }
+    }
   }
 
   /**
@@ -434,17 +507,24 @@
     List<Modification> mods5 = new ArrayList<Modification>();
     mods5.add(mod5);
 
+    // Entry attributes
+    Attribute eattr1 = Attributes.create("description", "eav description");
+    Attribute eattr2 = Attributes.create("namingcontexts", "eav naming contexts");
+    List<Attribute> entryAttrList = new ArrayList<Attribute>();
+    entryAttrList.add(eattr1);
+    entryAttrList.add(eattr2);
+
     return new Object[][] {
-        { cn1, "dc=test", mods1, false, AssuredMode.SAFE_DATA_MODE, (byte)0},
-        { cn2, "dc=cn2", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)1},
+        { cn1, "dc=test", mods1, false, AssuredMode.SAFE_DATA_MODE, (byte)0, null},
+        { cn2, "dc=cn2", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)1, entryAttrList},
         { cn2, "dc=test with a much longer dn in case this would "
-               + "make a difference", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)3},
-        { cn2, "dc=test, cn=with a, o=more complex, ou=dn", mods1, false, AssuredMode.SAFE_READ_MODE, (byte)5},
-        { cn2, "cn=use\\, backslash", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)3},
-        { cn2, "dc=test with several mod", mods2, false, AssuredMode.SAFE_DATA_MODE, (byte)16},
-        { cn2, "dc=test with several values", mods3, false, AssuredMode.SAFE_READ_MODE, (byte)3},
-        { cn2, "dc=test with long mod", mods4, true, AssuredMode.SAFE_READ_MODE, (byte)120},
-        { cn2, "dc=testDsaOperation", mods5, true, AssuredMode.SAFE_DATA_MODE, (byte)99},
+               + "make a difference", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)3, null},
+        { cn2, "dc=test, cn=with a, o=more complex, ou=dn", mods1, false, AssuredMode.SAFE_READ_MODE, (byte)5, entryAttrList},
+        { cn2, "cn=use\\, backslash", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)3, null},
+        { cn2, "dc=test with several mod", mods2, false, AssuredMode.SAFE_DATA_MODE, (byte)16, entryAttrList},
+        { cn2, "dc=test with several values", mods3, false, AssuredMode.SAFE_READ_MODE, (byte)3, null},
+        { cn2, "dc=test with long mod", mods4, true, AssuredMode.SAFE_READ_MODE, (byte)120, entryAttrList},
+        { cn2, "dc=testDsaOperation", mods5, true, AssuredMode.SAFE_DATA_MODE, (byte)99, null},
         };
   }
 
@@ -456,7 +536,8 @@
   public void modifyMsgTestVLASTV2(ChangeNumber changeNumber,
                                String rawdn, List<Modification> mods,
                                boolean isAssured, AssuredMode assuredMode,
-                               byte safeDataLevel)
+                               byte safeDataLevel,
+                               List<Attribute> entryAttrList)
          throws Exception
   {
     // TODO: modifyMsgTestVLASTV2 as soon as V3 will have any incompatibility with V2
@@ -470,86 +551,120 @@
   public void modifyMsgTestVLASTV1(ChangeNumber changeNumber,
                                String rawdn, List<Modification> mods,
                                boolean isAssured, AssuredMode assuredMode,
-                               byte safeDataLevel)
+                               byte safeDataLevel,
+                               List<Attribute> entryAttrList)
          throws Exception
   {
     // Create VLAST message
     DN dn = DN.decode(rawdn);
-    ModifyMsg msg = new ModifyMsg(changeNumber, dn, mods, "fakeuniqueid");
+    ModifyMsg origVlastMsg = new ModifyMsg(changeNumber, dn, mods, "fakeuniqueid");
 
-    msg.setAssured(isAssured);
-    msg.setAssuredMode(assuredMode);
-    msg.setSafeDataLevel(safeDataLevel);
+    origVlastMsg.setAssured(isAssured);
+    origVlastMsg.setAssuredMode(assuredMode);
+    origVlastMsg.setSafeDataLevel(safeDataLevel);
+    // Set ECL entry attributes
+    if (entryAttrList != null)
+    {
+      origVlastMsg.setEclIncludes(entryAttrList);
+    }
 
     // Check version of message
-    assertEquals(msg.getVersion(), REPLICATION_PROTOCOL_VLAST);
+    assertEquals(origVlastMsg.getVersion(), REPLICATION_PROTOCOL_VLAST);
 
     // Serialize in V1
-    byte[] v1MsgBytes = msg.getBytes(ProtocolVersion.REPLICATION_PROTOCOL_V1);
+    byte[] v1MsgBytes = origVlastMsg.getBytes(ProtocolVersion.REPLICATION_PROTOCOL_V1);
 
     // Un-serialize V1 message
-    ModifyMsg newMsg = (ModifyMsg)ReplicationMsg.generateMsg(
+    ModifyMsg newv1Msg = (ModifyMsg)ReplicationMsg.generateMsg(
         v1MsgBytes, ProtocolVersion.REPLICATION_PROTOCOL_V1);
 
     // Check original version of message
-    assertEquals(newMsg.getVersion(), ProtocolVersion.REPLICATION_PROTOCOL_V1);
+    assertEquals(newv1Msg.getVersion(), ProtocolVersion.REPLICATION_PROTOCOL_V1);
 
     // Check fields common to both versions
-    assertEquals(newMsg.getUniqueId(), msg.getUniqueId());
-    assertEquals(newMsg.getDn(), msg.getDn());
-    assertEquals(newMsg.getChangeNumber(), msg.getChangeNumber());
-    assertEquals(newMsg.isAssured(), msg.isAssured());
+    assertEquals(newv1Msg.getUniqueId(), origVlastMsg.getUniqueId());
+    assertEquals(newv1Msg.getDn(), origVlastMsg.getDn());
+    assertEquals(newv1Msg.getChangeNumber(), origVlastMsg.getChangeNumber());
+    assertEquals(newv1Msg.isAssured(), origVlastMsg.isAssured());
 
-    //        Create a modify operation from each message to compare mods (kept encoded in messages)
-    Operation op = msg.createOperation(connection);
-    Operation generatedOperation = newMsg.createOperation(connection);
+    // Create a modify operation from each message to compare mods (kept encoded in messages)
+    Operation opFromOrigVlast = origVlastMsg.createOperation(connection);
+    Operation opFromV1 = newv1Msg.createOperation(connection);
 
-    assertEquals(op.getClass(), ModifyOperationBasis.class);
-    assertEquals(generatedOperation.getClass(), ModifyOperationBasis.class);
-    ModifyOperationBasis modOpBasis = (ModifyOperationBasis) op;
-    ModifyOperationBasis genModOpBasis = (ModifyOperationBasis) generatedOperation;
+    assertEquals(opFromOrigVlast.getClass(), ModifyOperationBasis.class);
+    assertEquals(opFromV1.getClass(), ModifyOperationBasis.class);
+    
+    ModifyOperationBasis modOpBasisFromOrigVlast = (ModifyOperationBasis) opFromOrigVlast;
+    ModifyOperationBasis genModOpBasisFromV1 = (ModifyOperationBasis) opFromV1;
 
-    assertEquals(modOpBasis.getRawEntryDN(), genModOpBasis.getRawEntryDN());
-    assertEquals( modOpBasis.getAttachment(SYNCHROCONTEXT),
-                  genModOpBasis.getAttachment(SYNCHROCONTEXT));
-    assertEquals(modOpBasis.getModifications(), genModOpBasis.getModifications());
+    assertEquals(modOpBasisFromOrigVlast.getRawEntryDN(), genModOpBasisFromV1.getRawEntryDN());
+    assertEquals( modOpBasisFromOrigVlast.getAttachment(SYNCHROCONTEXT),
+                  genModOpBasisFromV1.getAttachment(SYNCHROCONTEXT));
+    List<Modification> modsvlast = modOpBasisFromOrigVlast.getModifications();
+    List<Modification> modsv1 = genModOpBasisFromV1.getModifications();
+    
+    assertEquals(modsvlast, modsv1);
 
     // Check default value for only VLAST fields
-    assertEquals(newMsg.getAssuredMode(), AssuredMode.SAFE_DATA_MODE);
-    assertEquals(newMsg.getSafeDataLevel(), (byte)1);
+    assertEquals(newv1Msg.getAssuredMode(), AssuredMode.SAFE_DATA_MODE);
+    assertEquals(newv1Msg.getSafeDataLevel(), (byte)1);
 
     // Set again only VLAST fields
-    newMsg.setAssuredMode(assuredMode);
-    newMsg.setSafeDataLevel(safeDataLevel);
-
+    newv1Msg.setAssuredMode(assuredMode);
+    newv1Msg.setSafeDataLevel(safeDataLevel);
+    if (entryAttrList != null)
+    {
+      newv1Msg.setEclIncludes(entryAttrList);
+    }
+    
     // Serialize in VLAST msg
-    ModifyMsg vlastMsg = (ModifyMsg)ReplicationMsg.generateMsg(
-        newMsg.getBytes(), ProtocolVersion.REPLICATION_PROTOCOL_V1);
+    ModifyMsg generatedVlastMsg = (ModifyMsg)ReplicationMsg.generateMsg(
+        newv1Msg.getBytes(), ProtocolVersion.REPLICATION_PROTOCOL_V1);
 
     // Check original version of message
-    assertEquals(vlastMsg.getVersion(), REPLICATION_PROTOCOL_VLAST);
+    assertEquals(generatedVlastMsg.getVersion(), REPLICATION_PROTOCOL_VLAST);
 
     // Check we retrieve original VLAST message (VLAST fields)
-    assertEquals(msg.getUniqueId(), vlastMsg.getUniqueId());
-    assertEquals(msg.getDn(), vlastMsg.getDn());
-    assertEquals(msg.getChangeNumber(), vlastMsg.getChangeNumber());
-    assertEquals(msg.isAssured(), vlastMsg.isAssured());
-    assertEquals(msg.getAssuredMode(), vlastMsg.getAssuredMode());
-    assertEquals(msg.getSafeDataLevel(), vlastMsg.getSafeDataLevel());
+    assertEquals(origVlastMsg.getUniqueId(), generatedVlastMsg.getUniqueId());
+    assertEquals(origVlastMsg.getDn(), generatedVlastMsg.getDn());
+    assertEquals(origVlastMsg.getChangeNumber(), generatedVlastMsg.getChangeNumber());
+    assertEquals(origVlastMsg.isAssured(), generatedVlastMsg.isAssured());
+    assertEquals(origVlastMsg.getAssuredMode(), generatedVlastMsg.getAssuredMode());
+    assertEquals(origVlastMsg.getSafeDataLevel(), generatedVlastMsg.getSafeDataLevel());
+    assertEquals(origVlastMsg.getSafeDataLevel(), generatedVlastMsg.getSafeDataLevel());
+    // Get ECL entry attributes
+    ArrayList<RawAttribute> genAttrList = generatedVlastMsg.getEclIncludes();
+    if (entryAttrList==null)
+      assertTrue(genAttrList.size()==0);
+    else
+    {
+      assertTrue(genAttrList.size()==entryAttrList.size());
+      int i=0;
+      for (Attribute attr : entryAttrList)
+      {
+        assertTrue(attr.getName().equalsIgnoreCase(genAttrList.get(i).toAttribute().getName()));
+        assertTrue(attr.toString().equalsIgnoreCase(genAttrList.get(i).toAttribute().toString()),
+            "Comparing: " + attr.toString() + " and " + genAttrList.get(i).toAttribute().toString());
+        i++;
+      }
+    }
 
-    //        Create a modify operation from each message to compare mods (kept encoded in messages)
-    op = msg.createOperation(connection);
-    generatedOperation = vlastMsg.createOperation(connection);
+    // Create a modify operation from each message to compare mods (kept encoded in messages)
+    opFromOrigVlast = origVlastMsg.createOperation(connection);
+    Operation opFromGeneratedVlastMsg = generatedVlastMsg.createOperation(connection);
 
-    assertEquals(op.getClass(), ModifyOperationBasis.class);
-    assertEquals(generatedOperation.getClass(), ModifyOperationBasis.class);
-    modOpBasis = (ModifyOperationBasis) op;
-    genModOpBasis = (ModifyOperationBasis) generatedOperation;
+    assertEquals(opFromOrigVlast.getClass(), ModifyOperationBasis.class);
+    assertEquals(opFromGeneratedVlastMsg.getClass(), ModifyOperationBasis.class);
 
-    assertEquals(modOpBasis.getRawEntryDN(), genModOpBasis.getRawEntryDN());
-    assertEquals( modOpBasis.getAttachment(SYNCHROCONTEXT),
-                  genModOpBasis.getAttachment(SYNCHROCONTEXT));
-    assertEquals(modOpBasis.getModifications(), genModOpBasis.getModifications());
+    modOpBasisFromOrigVlast = (ModifyOperationBasis) opFromOrigVlast;
+    ModifyOperationBasis modOpBasisFromGeneratedVlast = (ModifyOperationBasis) opFromGeneratedVlastMsg;
+
+    assertEquals(modOpBasisFromOrigVlast.getRawEntryDN(), 
+        modOpBasisFromGeneratedVlast.getRawEntryDN());
+    assertEquals( modOpBasisFromOrigVlast.getAttachment(SYNCHROCONTEXT),
+        modOpBasisFromGeneratedVlast.getAttachment(SYNCHROCONTEXT));
+    assertEquals(modOpBasisFromOrigVlast.getModifications(), 
+        modOpBasisFromGeneratedVlast.getModifications());
   }
 
   @DataProvider(name = "createModifyDnData")
@@ -586,12 +701,19 @@
       mods4.add(mod);
     }
 
+    // Entry attributes
+    Attribute eattr1 = Attributes.create("description", "eav description");
+    Attribute eattr2 = Attributes.create("namingcontexts", "eav naming contexts");
+    List<Attribute> entryAttrList = new ArrayList<Attribute>();
+    entryAttrList.add(eattr1);
+    entryAttrList.add(eattr2);
+
     return new Object[][] {
-        {"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=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, null},
+        {"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, entryAttrList},
+        {"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, null},
         {"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, mods4, 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, entryAttrList},
         };
   }
 
@@ -603,7 +725,8 @@
   public void modifyDnMsgTestVLASTV2(String rawDN, String newRdn, String uid, String newParentUid,
                                    boolean deleteOldRdn, String newSuperior,
                                    List<Modification> mods, boolean isAssured,
-                                   AssuredMode assuredMode, byte safeDataLevel)
+                                   AssuredMode assuredMode, byte safeDataLevel,
+                                   List<Attribute> entryAttrList)
          throws Exception
   {
     // TODO: modifyMsgTestVLASTV2 as soon as V3 will have any incompatibility with V2
@@ -617,7 +740,8 @@
   public void modifyDnMsgTestVLASTV1(String rawDN, String newRdn, String uid, String newParentUid,
                                    boolean deleteOldRdn, String newSuperior,
                                    List<Modification> mods, boolean isAssured,
-                                   AssuredMode assuredMode, byte safeDataLevel)
+                                   AssuredMode assuredMode, byte safeDataLevel,
+                                   List<Attribute> entryAttrList)
          throws Exception
   {
     // Create VLAST message
@@ -631,6 +755,12 @@
     msg.setAssuredMode(assuredMode);
     msg.setSafeDataLevel(safeDataLevel);
 
+    // Set ECL entry attributes
+    if (entryAttrList != null)
+    {
+      msg.setEclIncludes(entryAttrList);
+    }
+
     // Check version of message
     assertEquals(msg.getVersion(), REPLICATION_PROTOCOL_VLAST);
 
@@ -654,7 +784,7 @@
     assertEquals(newMsg.getNewSuperiorId(), msg.getNewSuperiorId());
     assertEquals(newMsg.deleteOldRdn(), msg.deleteOldRdn());
 
-    //        Create a modDn operation from each message to compare fields)
+    // Create a modDn operation from each message to compare fields)
     Operation op = msg.createOperation(connection);
     Operation generatedOperation = newMsg.createOperation(connection);
 
@@ -677,6 +807,11 @@
     newMsg.setAssuredMode(assuredMode);
     newMsg.setSafeDataLevel(safeDataLevel);
     newMsg.setMods(mods);
+    // Set ECL entry attributes
+    if (entryAttrList != null)
+    {
+      newMsg.setEclIncludes(entryAttrList);
+    }
 
     // Serialize in VLAST msg
     ModifyDNMsg vlastMsg = (ModifyDNMsg)ReplicationMsg.generateMsg(
@@ -697,7 +832,24 @@
     assertEquals(msg.getNewSuperiorId(), vlastMsg.getNewSuperiorId());
     assertEquals(msg.deleteOldRdn(), vlastMsg.deleteOldRdn());
 
-    //        Create a modDn operation from each message to compare mods (kept encoded in messages)
+    // Get ECL entry attributes
+    ArrayList<RawAttribute> genAttrList = vlastMsg.getEclIncludes();
+    if (entryAttrList==null)
+      assertTrue(genAttrList.size()==0);
+    else
+    {
+      assertTrue(genAttrList.size()==entryAttrList.size());
+      int i=0;
+      for (Attribute attr : entryAttrList)
+      {
+        assertTrue(attr.getName().equalsIgnoreCase(genAttrList.get(i).toAttribute().getName()));
+        assertTrue(attr.toString().equalsIgnoreCase(genAttrList.get(i).toAttribute().toString()),
+            "Comparing: " + attr.toString() + " and " + genAttrList.get(i).toAttribute().toString());
+        i++;
+      }
+    }
+     
+    // Create a modDn operation from each message to compare mods (kept encoded in messages)
     op = msg.createOperation(connection);
     generatedOperation = vlastMsg.createOperation(connection);
 
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 c73ac80..a3a7b3b 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
@@ -36,8 +36,10 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 import java.util.UUID;
 import java.util.zip.DataFormatException;
 
@@ -47,6 +49,7 @@
 import org.opends.server.core.DeleteOperationBasis;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ModifyDNOperationBasis;
+import org.opends.server.core.ModifyOperation;
 import org.opends.server.core.ModifyOperationBasis;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.replication.ReplicationTestCase;
@@ -67,10 +70,12 @@
 import org.opends.server.types.ObjectClass;
 import org.opends.server.types.Operation;
 import org.opends.server.types.RDN;
+import org.opends.server.types.RawAttribute;
 import org.opends.server.util.TimeThread;
 import org.opends.server.workflowelement.localbackend.LocalBackendAddOperation;
 import org.opends.server.workflowelement.localbackend.LocalBackendDeleteOperation;
 import org.opends.server.workflowelement.localbackend.LocalBackendModifyDNOperation;
+import org.opends.server.workflowelement.localbackend.LocalBackendModifyOperation;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
@@ -141,17 +146,24 @@
     List<Modification> mods5 = new ArrayList<Modification>();
     mods5.add(mod5);
 
+    // Entry attributes
+    Attribute eattr1 = Attributes.create("description", "eav description");
+    Attribute eattr2 = Attributes.create("namingcontexts", "eav naming contexts");
+    List<Attribute> eclIncludes = new ArrayList<Attribute>();
+    eclIncludes.add(eattr1);
+    eclIncludes.add(eattr2);
+
     return new Object[][] {
-        { cn1, "dc=test", mods1, false, AssuredMode.SAFE_DATA_MODE, (byte)0},
-        { cn2, "dc=cn2", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)1},
+        { cn1, "dc=test", mods1, false, AssuredMode.SAFE_DATA_MODE, (byte)0, null},
+        { cn2, "dc=cn2", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)1, eclIncludes},
         { cn2, "dc=test with a much longer dn in case this would "
-               + "make a difference", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)3},
-        { cn2, "dc=test, cn=with a, o=more complex, ou=dn", mods1, false, AssuredMode.SAFE_READ_MODE, (byte)5},
-        { cn2, "cn=use\\, backslash", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)3},
-        { cn2, "dc=test with several mod", mods2, false, AssuredMode.SAFE_DATA_MODE, (byte)16},
-        { cn2, "dc=test with several values", mods3, false, AssuredMode.SAFE_READ_MODE, (byte)3},
-        { cn2, "dc=test with long mod", mods4, true, AssuredMode.SAFE_READ_MODE, (byte)120},
-        { cn2, "dc=testDsaOperation", mods5, true, AssuredMode.SAFE_DATA_MODE, (byte)99},
+               + "make a difference", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)3, null},
+        { cn2, "dc=test, cn=with a, o=more complex, ou=dn", mods1, false, AssuredMode.SAFE_READ_MODE, (byte)5, eclIncludes},
+        { cn2, "cn=use\\, backslash", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)3, null},
+        { cn2, "dc=test with several mod", mods2, false, AssuredMode.SAFE_DATA_MODE, (byte)16, eclIncludes},
+        { cn2, "dc=test with several values", mods3, false, AssuredMode.SAFE_READ_MODE, (byte)3, null},
+        { cn2, "dc=test with long mod", mods4, true, AssuredMode.SAFE_READ_MODE, (byte)120, eclIncludes},
+        { cn2, "dc=testDsaOperation", mods5, true, AssuredMode.SAFE_DATA_MODE, (byte)99, null},
         };
   }
 
@@ -161,11 +173,12 @@
    * create another ModifyMsg from the encoded byte array.
    * Finally test that both Msg matches.
    */
-  @Test(dataProvider = "createModifyData")
+  @Test(enabled=true,dataProvider = "createModifyData")
   public void modifyMsgTest(ChangeNumber changeNumber,
                                String rawdn, List<Modification> mods,
                                boolean isAssured, AssuredMode assuredMode,
-                               byte safeDataLevel)
+                               byte safeDataLevel,
+                               List<Attribute> entryAttrList)
          throws Exception
   {
     DN dn = DN.decode(rawdn);
@@ -177,6 +190,12 @@
     msg.setAssuredMode(assuredMode);
     msg.setSafeDataLevel(safeDataLevel);
 
+    // Set ECL entry inlcuded attributes
+    if (entryAttrList != null)
+    {
+      msg.setEclIncludes(entryAttrList);
+    }
+
     ModifyMsg generatedMsg = (ModifyMsg) ReplicationMsg.generateMsg(
         msg.getBytes(), ProtocolVersion.getCurrentVersion());
 
@@ -187,6 +206,23 @@
 
     assertEquals(msg.getChangeNumber(), generatedMsg.getChangeNumber());
 
+    // Get ECL entry attributes
+    ArrayList<RawAttribute> genAttrList = generatedMsg.getEclIncludes();
+    if (entryAttrList==null)
+      assertTrue(genAttrList.size()==0);
+    else
+    {
+      assertTrue(genAttrList.size()==entryAttrList.size());
+      int i=0;
+      for (Attribute attr : entryAttrList)
+      {
+        assertTrue(attr.getName().equalsIgnoreCase(genAttrList.get(i).toAttribute().getName()));
+        assertTrue(attr.toString().equalsIgnoreCase(genAttrList.get(i).toAttribute().toString()),
+            "Comparing: " + attr.toString() + " and " + genAttrList.get(i).toAttribute().toString());
+        i++;
+      }
+    }
+ 
     Operation op = msg.createOperation(connection);
     Operation generatedOperation = generatedMsg.createOperation(connection);
 
@@ -208,11 +244,12 @@
    * create another ModifyMsg from the encoded byte array.
    * Finally test that both Msgs match.
    */
-  @Test(dataProvider = "createModifyData")
+  @Test(enabled=true,dataProvider = "createModifyData")
   public void updateMsgTest(ChangeNumber changeNumber,
                                String rawdn, List<Modification> mods,
                                boolean isAssured, AssuredMode assuredMode,
-                               byte safeDataLevel)
+                               byte safeDataLevel ,
+                               List<Attribute> entryAttrList)
          throws Exception
   {
     DN dn = DN.decode(rawdn);
@@ -272,10 +309,19 @@
    * Build some data for the DeleteMsg test below.
    */
   @DataProvider(name = "createDeleteData")
-  public Object[][] createDeleteData() {
+  public Object[][] createDeleteData() 
+  {
+
+    // Entry attributes
+    Attribute eattr1 = Attributes.create("description", "eav description");
+    Attribute eattr2 = Attributes.create("namingcontexts", "eav naming contexts");
+    List<Attribute> entryAttrList = new ArrayList<Attribute>();
+    entryAttrList.add(eattr1);
+    entryAttrList.add(eattr2);
+
     return new Object[][] {
-        {"dc=com"},
-        {"dc=delete,dc=an,dc=entry,dc=with,dc=a,dc=long dn"},
+        {"dc=com", entryAttrList},
+        {"dc=delete,dc=an,dc=entry,dc=with,dc=a,dc=long dn", null},
         };
   }
 
@@ -285,9 +331,9 @@
    * create another DeleteMsg from the encoded byte array.
    * Finally test that both Msg matches.
    */
-  @Test(dataProvider = "createDeleteData")
-  public void deleteMsgTest(String rawDN)
-         throws Exception
+  @Test(enabled=true,dataProvider = "createDeleteData")
+  public void deleteMsgTest(String rawDN, List<Attribute> entryAttrList)
+  throws Exception
   {
     InternalClientConnection connection =
         InternalClientConnection.getRootConnection();
@@ -298,6 +344,13 @@
       (short) 123, (short) 45);
     op.setAttachment(SYNCHROCONTEXT, new DeleteContext(cn, "uniqueid"));
     DeleteMsg msg = new DeleteMsg(op);
+    
+    // Set ECL entry attributes
+    if (entryAttrList != null)
+    {
+      msg.setEclIncludes(entryAttrList);
+    }
+
     DeleteMsg generatedMsg = (DeleteMsg) ReplicationMsg.generateMsg(
         msg.getBytes(), ProtocolVersion.getCurrentVersion());
 
@@ -305,6 +358,23 @@
 
     assertEquals(msg.getChangeNumber(), generatedMsg.getChangeNumber());
 
+    // Get ECL entry attributes
+    ArrayList<RawAttribute> genAttrList = generatedMsg.getEclIncludes();
+    if (entryAttrList==null)
+      assertTrue(genAttrList.size()==0);
+    else
+    {
+      assertTrue(genAttrList.size()==entryAttrList.size());
+      int i=0;
+      for (Attribute attr : entryAttrList)
+      {
+        assertTrue(attr.getName().equalsIgnoreCase(genAttrList.get(i).toAttribute().getName()));
+        assertTrue(attr.toString().equalsIgnoreCase(genAttrList.get(i).toAttribute().toString()),
+            "Comparing: " + attr.toString() + " and " + genAttrList.get(i).toAttribute().toString());
+        i++;
+      }
+    }
+ 
     Operation generatedOperation = generatedMsg.createOperation(connection);
 
     assertEquals(generatedOperation.getClass(), DeleteOperationBasis.class);
@@ -352,23 +422,31 @@
       mods4.add(mod);
     }
 
+    // Entry attributes
+    Attribute eattr1 = Attributes.create("description", "eav description");
+    Attribute eattr2 = Attributes.create("namingcontexts", "eav naming contexts");
+    List<Attribute> entryAttrList = new ArrayList<Attribute>();
+    entryAttrList.add(eattr1);
+    entryAttrList.add(eattr2);
+
+
     return new Object[][] {
-        {"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},
+        {"dc=test,dc=com", "dc=new", false, "dc=change", mods1, false, AssuredMode.SAFE_DATA_MODE, (byte)0, entryAttrList},
+        {"dc=test,dc=com", "dc=new", true, "dc=change", mods2, true, AssuredMode.SAFE_READ_MODE, (byte)1, null},
         // testNG does not like null argument so use "" for the newSuperior
         // instead of null
-        {"dc=test,dc=com", "dc=new", false, "", mods3, true, AssuredMode.SAFE_READ_MODE, (byte)3},
+        {"dc=test,dc=com", "dc=new", false, "", mods3, true, AssuredMode.SAFE_READ_MODE, (byte)3, entryAttrList},
         {"dc=delete,dc=an,dc=entry,dc=with,dc=a,dc=long dn",
-                   "dc=new", true, "", mods4, true, AssuredMode.SAFE_DATA_MODE, (byte)99},
+                   "dc=new", true, "", mods4, true, AssuredMode.SAFE_DATA_MODE, (byte)99, null},
         };
   }
 
-  @Test(dataProvider = "createModifyDnData")
+  @Test(enabled=true,dataProvider = "createModifyDnData")
   public void modifyDnMsgTest(String rawDN, String newRdn,
                                    boolean deleteOldRdn, String newSuperior,
                                    List<Modification> mods, boolean isAssured,
                                    AssuredMode assuredMode,
-                               byte safeDataLevel)
+                               byte safeDataLevel, List<Attribute> entryAttrList)
          throws Exception
   {
     InternalClientConnection connection =
@@ -392,6 +470,12 @@
     msg.setAssuredMode(assuredMode);
     msg.setSafeDataLevel(safeDataLevel);
 
+    // Set ECL entry attributes
+    if (entryAttrList != null)
+    {
+      msg.setEclIncludes(entryAttrList);
+    }
+
     ModifyDNMsg generatedMsg = (ModifyDNMsg) ReplicationMsg
         .generateMsg(msg.getBytes(), ProtocolVersion.getCurrentVersion());
 
@@ -400,6 +484,23 @@
     assertEquals(generatedMsg.getAssuredMode(), assuredMode);
     assertEquals(generatedMsg.getSafeDataLevel(), safeDataLevel);
 
+    // Get ECL entry attributes
+    ArrayList<RawAttribute> genAttrList = generatedMsg.getEclIncludes();
+    if (entryAttrList==null)
+      assertTrue(genAttrList.size()==0);
+    else
+    {
+      assertTrue(genAttrList.size()==entryAttrList.size());
+      int i=0;
+      for (Attribute attr : entryAttrList)
+      {
+        assertTrue(attr.getName().equalsIgnoreCase(genAttrList.get(i).toAttribute().getName()));
+        assertTrue(attr.toString().equalsIgnoreCase(genAttrList.get(i).toAttribute().toString()),
+            "Comparing: " + attr.toString() + " and " + genAttrList.get(i).toAttribute().toString());
+        i++;
+      }
+    }
+ 
     Operation oriOp = msg.createOperation(connection);
     Operation generatedOperation = generatedMsg.createOperation(connection);
 
@@ -422,16 +523,24 @@
   }
 
   @DataProvider(name = "createAddData")
-  public Object[][] createAddData() {
+  public Object[][] createAddData() 
+  {
+
+    // Entry attributes
+    Attribute eattr1 = Attributes.create("description", "eav description");
+    Attribute eattr2 = Attributes.create("namingcontexts", "eav naming contexts");
+    List<Attribute> entryAttrList = new ArrayList<Attribute>();
+    entryAttrList.add(eattr1);
+    entryAttrList.add(eattr2);
     return new Object[][] {
-      {"dc=example,dc=com", false, AssuredMode.SAFE_DATA_MODE, (byte)0},
-      {"o=test", true, AssuredMode.SAFE_READ_MODE, (byte)1},
-      {"o=group,dc=example,dc=com", true, AssuredMode.SAFE_READ_MODE, (byte)3}};
+        {"dc=example,dc=com", false, AssuredMode.SAFE_DATA_MODE, (byte)0, entryAttrList},
+        {"o=test", true, AssuredMode.SAFE_READ_MODE, (byte)1, null},
+        {"o=group,dc=example,dc=com", true, AssuredMode.SAFE_READ_MODE, (byte)3, entryAttrList}};
   }
 
-  @Test(dataProvider = "createAddData")
+  @Test(enabled=true,dataProvider = "createAddData")
   public void addMsgTest(String rawDN, boolean isAssured, AssuredMode assuredMode,
-    byte safeDataLevel)
+    byte safeDataLevel, List<Attribute> entryAttrList)
          throws Exception
   {
     Attribute objectClass = Attributes.create(DirectoryServer
@@ -465,6 +574,12 @@
     msg.setAssuredMode(assuredMode);
     msg.setSafeDataLevel(safeDataLevel);
 
+    // Set ECL entry attributes
+    if (entryAttrList != null)
+    {
+      msg.setEclIncludes(entryAttrList);
+    }
+
     AddMsg generatedMsg = (AddMsg) ReplicationMsg.generateMsg(msg
         .getBytes(), ProtocolVersion.getCurrentVersion());
     assertEquals(msg.getBytes(), generatedMsg.getBytes());
@@ -476,6 +591,24 @@
     assertEquals(generatedMsg.getAssuredMode(), assuredMode);
     assertEquals(generatedMsg.getSafeDataLevel(), safeDataLevel);
 
+    // Get ECL entry attributes
+    ArrayList<RawAttribute> genAttrList = generatedMsg.getEclIncludes();
+    if (entryAttrList==null)
+      assertTrue(genAttrList.size()==0);
+    else
+    {
+      assertTrue(genAttrList.size()==entryAttrList.size());
+      int i=0;
+      for (Attribute eattr : entryAttrList)
+      {
+        assertTrue(eattr.getName().equalsIgnoreCase(genAttrList.get(i).toAttribute().getName()));
+        assertTrue(eattr.toString().equalsIgnoreCase(genAttrList.get(i).toAttribute().toString()),
+            "Comparing: " + eattr.toString() + " and " + genAttrList.get(i).toAttribute().toString());
+        i++;
+      }
+    }
+ 
+
     // Create an new Add Operation from the current addMsg
     InternalClientConnection connection =
         InternalClientConnection.getRootConnection();
@@ -514,6 +647,11 @@
     generatedMsg.setAssuredMode(assuredMode);
     generatedMsg.setSafeDataLevel(safeDataLevel);
 
+    if (entryAttrList != null)
+    {
+      generatedMsg.setEclIncludes(entryAttrList);
+    }
+
     assertEquals(msg.getBytes(), generatedMsg.getBytes());
     assertEquals(msg.toString(), generatedMsg.toString());
     // TODO : should test that generated attributes match original attributes.
@@ -562,7 +700,7 @@
         };
   }
 
-  @Test(dataProvider = "createAckData")
+  @Test(enabled=true,dataProvider = "createAckData")
   public void ackMsgTest(ChangeNumber cn, boolean hasTimeout, boolean hasWrongStatus,
     boolean hasReplayError, List<Short> failedServers)
          throws Exception
@@ -615,7 +753,7 @@
         msg1.getBytes(), ProtocolVersion.getCurrentVersion());
   }
 
-  @Test()
+  @Test(enabled=true)
   public void eclUpdateMsg()
          throws Exception
   {
@@ -681,7 +819,7 @@
    * Test that ServerStartMsg encoding and decoding works
    * by checking that : msg == new ServerStartMsg(msg.getBytes()).
    */
-  @Test(dataProvider="createServerStartData")
+  @Test(enabled=true,dataProvider="createServerStartData")
   public void serverStartMsgTest(short serverId, String baseDN, int window,
          ServerState state, long genId, boolean sslEncryption, byte groupId) throws Exception
   {
@@ -720,7 +858,7 @@
    * Test that ReplServerStartMsg encoding and decoding works
    * by checking that : msg == new ReplServerStartMsg(msg.getBytes()).
    */
-  @Test(dataProvider="createReplServerStartData")
+  @Test(enabled=true,dataProvider="createReplServerStartData")
   public void replServerStartMsgTest(short serverId, String baseDN, int window,
          String url, ServerState state, long genId, byte groupId, int degTh) throws Exception
   {
@@ -783,17 +921,25 @@
     urls4.add("ldaps://host:port/dc=foobar1??sub?(sn=Another Entry 1)");
     urls4.add("ldaps://host:port/dc=foobar2??sub?(sn=Another Entry 2)");
 
+    Set<String> a1 = new HashSet<String>();
+    Set<String> a2 = new HashSet<String>();
+    a2.add("dc");
+    Set<String> a3 = new HashSet<String>();
+    a3.add("dc");
+    a3.add("uid");
+    Set<String> a4 = new HashSet<String>();
+    
     DSInfo dsInfo1 = new DSInfo((short)13, (short)26, (long)154631, ServerStatus.FULL_UPDATE_STATUS,
-      false, AssuredMode.SAFE_DATA_MODE, (byte)12, (byte)132, urls1);
+      false, AssuredMode.SAFE_DATA_MODE, (byte)12, (byte)132, urls1, a1);
 
     DSInfo dsInfo2 = new DSInfo((short)-436, (short)493, (long)-227896, ServerStatus.DEGRADED_STATUS,
-      true, AssuredMode.SAFE_READ_MODE, (byte)-7, (byte)-265, urls2);
+      true, AssuredMode.SAFE_READ_MODE, (byte)-7, (byte)-265, urls2, a2);
 
     DSInfo dsInfo3 = new DSInfo((short)2436, (short)591, (long)0, ServerStatus.NORMAL_STATUS,
-      false, AssuredMode.SAFE_READ_MODE, (byte)17, (byte)0, urls3);
+      false, AssuredMode.SAFE_READ_MODE, (byte)17, (byte)0, urls3, a3);
 
     DSInfo dsInfo4 = new DSInfo((short)415, (short)146, (long)0, ServerStatus.BAD_GEN_ID_STATUS,
-      true, AssuredMode.SAFE_DATA_MODE, (byte)2, (byte)15, urls4);
+      true, AssuredMode.SAFE_DATA_MODE, (byte)2, (byte)15, urls4, a4);
 
     List<DSInfo> dsList1 = new ArrayList<DSInfo>();
     dsList1.add(dsInfo1);
@@ -824,25 +970,27 @@
     rsList2.add(rsInfo3);
 
     return new Object [][] {
-      {dsList1, rsList1},
-      {dsList2, rsList2},
-      {dsList3, rsList1},
-      {dsList3, null},
-      {null, rsList1},
-      {null, null},
-      {dsList4, rsList2}
+      {dsList1, rsList1, a1},
+      {dsList2, rsList2, a2},
+      {dsList3, rsList1, a3},
+      {dsList3, null, null},
+      {null, rsList1, a1},
+      {null, null, a2},
+      {dsList4, rsList2, a3}
     };
   }
 
   /**
    * Test TopologyMsg encoding and decoding.
    */
-  @Test(dataProvider = "createTopologyData")
-  public void topologyMsgTest(List<DSInfo> dsList, List<RSInfo> rsList)
+  @Test(enabled=true,dataProvider = "createTopologyData")
+  public void topologyMsgTest(List<DSInfo> dsList, List<RSInfo> rsList,
+      Set<String> attrs)
     throws Exception
   {
     TopologyMsg msg = new TopologyMsg(dsList, rsList);
-    TopologyMsg newMsg = new TopologyMsg(msg.getBytes());
+    TopologyMsg newMsg = new TopologyMsg(msg.getBytes(),
+        ProtocolVersion.getCurrentVersion());
     assertEquals(msg.getDsList(), newMsg.getDsList());
     assertEquals(msg.getRsList(), newMsg.getRsList());
   }
@@ -879,32 +1027,44 @@
     urls6.add("ldaps://host:port/dc=foo??sub?(sn=Fourth Entry)");
     urls6.add("ldaps://host:port/dc=foo??sub?(sn=Fifth Entry)");
 
+    Set<String> a1 = new HashSet<String>();
+    Set<String> a2 = new HashSet<String>();
+    a2.add("dc");
+    Set<String> a3 = new HashSet<String>();
+    a3.add("dc");
+    a3.add("uid");
+
     return new Object[][]{
-      {ServerStatus.NORMAL_STATUS, urls1, true, AssuredMode.SAFE_DATA_MODE, (byte)1},
-      {ServerStatus.DEGRADED_STATUS, urls2, false, AssuredMode.SAFE_READ_MODE, (byte)123},
-      {ServerStatus.FULL_UPDATE_STATUS, urls3, false, AssuredMode.SAFE_DATA_MODE, (byte)111},
-      {ServerStatus.NORMAL_STATUS, urls4, true, AssuredMode.SAFE_READ_MODE, (byte)-1},
-      {ServerStatus.DEGRADED_STATUS, urls5, true, AssuredMode.SAFE_DATA_MODE, (byte)97},
-      {ServerStatus.FULL_UPDATE_STATUS, urls6, false, AssuredMode.SAFE_READ_MODE, (byte)-13}
+      {ServerStatus.NORMAL_STATUS, urls1, true, AssuredMode.SAFE_DATA_MODE, (byte)1, a1},
+      {ServerStatus.DEGRADED_STATUS, urls2, false, AssuredMode.SAFE_READ_MODE, (byte)123, a2},
+      {ServerStatus.FULL_UPDATE_STATUS, urls3, false, AssuredMode.SAFE_DATA_MODE, (byte)111, a3},
+      {ServerStatus.NORMAL_STATUS, urls4, true, AssuredMode.SAFE_READ_MODE, (byte)-1, a1},
+      {ServerStatus.DEGRADED_STATUS, urls5, true, AssuredMode.SAFE_DATA_MODE, (byte)97, a2},
+      {ServerStatus.FULL_UPDATE_STATUS, urls6, false, AssuredMode.SAFE_READ_MODE, (byte)-13, a3}
     };
   }
 
   /**
    * Test StartSessionMsg encoding and decoding.
    */
-  @Test(dataProvider = "createStartSessionData")
+  @Test(enabled=true,dataProvider = "createStartSessionData")
   public void startSessionMsgTest(ServerStatus status, List<String> refUrls,
-    boolean assuredFlag, AssuredMode assuredMode, byte safedataLevel)
+    boolean assuredFlag, AssuredMode assuredMode, byte safedataLevel,
+    Set<String> attrs)
     throws Exception
   {
     StartSessionMsg msg = new StartSessionMsg(status, refUrls, assuredFlag,
       assuredMode, safedataLevel);
-    StartSessionMsg newMsg = new StartSessionMsg(msg.getBytes());
+    msg.setEclIncludes(attrs);
+    StartSessionMsg newMsg =
+      new StartSessionMsg(msg.getBytes(),ProtocolVersion.getCurrentVersion());
     assertEquals(msg.getStatus(), newMsg.getStatus());
     assertTrue(msg.isAssured() == newMsg.isAssured());
     assertEquals(msg.getAssuredMode(), newMsg.getAssuredMode());
     assertTrue(msg.getSafeDataLevel() == newMsg.getSafeDataLevel());
     assertEquals(msg.getReferralsURLs(), newMsg.getReferralsURLs());
+    Set<String> newAttrs = newMsg.getEclIncludes();
+    assertTrue(attrs.size() == newAttrs.size());
   }
 
   /**
@@ -923,7 +1083,7 @@
   /**
    * Test ChangeStatusMsg encoding and decoding.
    */
-  @Test(dataProvider = "createChangeStatusData")
+  @Test(enabled=true,dataProvider = "createChangeStatusData")
   public void changeStatusMsgTest(ServerStatus reqStatus, ServerStatus newStatus)
     throws Exception
   {
@@ -1174,7 +1334,7 @@
    * Test that ServerStartMsg encoding and decoding works
    * by checking that : msg == new ServerStartMsg(msg.getBytes()).
    */
-  @Test(dataProvider="createServerStartData")
+  @Test(enabled=true,dataProvider="createServerStartData")
   public void startECLMsgTest(short serverId, String baseDN, int window,
          ServerState state, long genId, boolean sslEncryption, byte groupId) throws Exception
   {
@@ -1250,4 +1410,262 @@
     assertTrue(dn1found);
     assertTrue(dn2found);
   }
+  
+  int perfRep = 100000;
+
+
+  @Test(enabled=false,dataProvider = "createAddData")
+  public void addMsgPerfs(String rawDN, boolean isAssured, AssuredMode assuredMode,
+      byte safeDataLevel, List<Attribute> entryAttrList)
+  throws Exception
+  {
+    long createop = 0;
+    long createmsgfromop = 0;
+    long encodemsg = 0;
+    long getbytes = 0;
+    long alld = 0;
+    long setentryattr = 0;
+    long buildnew = 0;
+    long t1,t2,t3,t31,t4,t5,t6 = 0;
+    
+    HashMap<ObjectClass, String> objectClassList = new HashMap<ObjectClass, String>();
+    objectClassList.put(DirectoryServer.getObjectClass("organization"),
+        "organization");
+
+    ArrayList<Attribute> userAttributes = new ArrayList<Attribute>(1);
+    Attribute attr = Attributes.create("o", "com");
+    userAttributes.add(attr);
+    HashMap<AttributeType, List<Attribute>> userAttList = new HashMap<AttributeType, List<Attribute>>();
+    userAttList.put(attr.getAttributeType(), userAttributes);
+
+
+    ArrayList<Attribute> operationalAttributes = new ArrayList<Attribute>(1);
+    attr = Attributes.create("creatorsname", "dc=creator");
+    operationalAttributes.add(attr);
+    HashMap<AttributeType,List<Attribute>> opList=
+      new HashMap<AttributeType,List<Attribute>>();
+    opList.put(attr.getAttributeType(), operationalAttributes);
+
+    ChangeNumber cn = new ChangeNumber(TimeThread.getTime(),
+        (short) 123, (short) 45);
+    DN dn = DN.decode(rawDN);
+
+    for (int i=1;i<perfRep;i++)
+    {
+      t1 = System.nanoTime();
+
+      // create op
+      AddOperation addOpB = new AddOperationBasis(connection,
+          (long) 1, 1, null, dn, objectClassList, userAttList, opList);
+      LocalBackendAddOperation addOp = new LocalBackendAddOperation(addOpB);
+      OperationContext opCtx = new AddContext(cn, "thisIsaUniqueID",
+          "parentUniqueId");
+      addOp.setAttachment(SYNCHROCONTEXT, opCtx);
+      t2 = System.nanoTime();
+      createop += (t2 - t1);
+
+      // create msg from op
+      AddMsg generatedMsg = new AddMsg(addOp);
+      t3 = System.nanoTime();
+      createmsgfromop += (t3 - t2);
+
+      // set entry attr
+      generatedMsg.setEclIncludes(entryAttrList);
+      t31 = System.nanoTime();
+      setentryattr += (t31 - t3);
+      
+      // encode msg
+      generatedMsg.encode();
+      t4 = System.nanoTime();
+      encodemsg += (t4 - t31);
+
+      // getBytes
+      byte[] bytes = generatedMsg.getBytes(ProtocolVersion.getCurrentVersion());
+      t5 = System.nanoTime();
+      getbytes += (t5 - t4);
+
+      // getBytes
+      new AddMsg(bytes);
+      t6 = System.nanoTime();
+      buildnew += (t6 - t5);
+
+      alld += (t6 - t1);
+    }
+
+    System.out.println(
+        "addMsgPerfs " 
+        + "createop\t" 
+        + "createmsgfromop\t" 
+        + "setentryattr\t"
+        + "encodemsg\t" 
+        + "getbytes\t"
+        + "buildnew\t");
+
+    System.out.println(
+        "addMsgPerfs " 
+        + createop/perfRep/1000.0 + " μs \t" 
+        + createmsgfromop/perfRep/1000.0 + " μs \t" 
+        + setentryattr/perfRep/1000.0 + " μs \t"
+        + encodemsg/perfRep/1000.0 + " μs \t" 
+        + getbytes/perfRep/1000.0 + " μs \t"
+        + buildnew/perfRep/1000.0 + " μs \t");
+  }  
+
+  @Test(enabled=false,dataProvider = "createModifyData")
+  public void modMsgPerfs(ChangeNumber changeNumber,
+      String rawdn, List<Modification> mods,
+      boolean isAssured, AssuredMode assuredMode,
+      byte safeDataLevel, List<Attribute> entryAttrList)
+  throws Exception
+  {
+    long createop = 0;
+    long createmsgfromop = 0;
+    long encodemsg = 0;
+    long getbytes = 0;
+    long alld = 0;
+    long setentryattr = 0;
+    long buildnew = 0;
+    long t1,t2,t3,t31,t4,t5,t6 = 0;
+    
+    ChangeNumber cn = new ChangeNumber(TimeThread.getTime(),
+        (short) 123, (short) 45);
+    DN dn = DN.decode(rawdn);
+    
+    for (int i=1;i<perfRep;i++)
+    {
+      t1 = System.nanoTime();
+
+      // create op
+      ModifyOperation modifyOpB = new ModifyOperationBasis(
+          connection, (long)1, 1, null, dn, mods);
+      LocalBackendModifyOperation modifyOp =
+        new LocalBackendModifyOperation(modifyOpB);
+      OperationContext opCtx = new ModifyContext(cn, "thisIsaUniqueID");
+      modifyOp.setAttachment(SYNCHROCONTEXT, opCtx);
+      t2 = System.nanoTime();
+      createop += (t2 - t1);
+
+      // create msg from op
+      ModifyMsg generatedMsg = new ModifyMsg(modifyOp);
+      t3 = System.nanoTime();
+      createmsgfromop += (t3 - t2);
+
+      // set entry attr
+      // generatedMsg.setEntryAttributes(entryAttrList);
+      t31 = System.nanoTime();
+      setentryattr += (t31 - t3);
+      
+      // encode msg
+      generatedMsg.encode();
+      t4 = System.nanoTime();
+      encodemsg += (t4 - t31);
+
+      // getBytes
+      byte[] bytes = generatedMsg.getBytes(ProtocolVersion.getCurrentVersion());
+      t5 = System.nanoTime();
+      getbytes += (t5 - t4);
+
+      // getBytes
+      new ModifyMsg(bytes);
+      t6 = System.nanoTime();
+      buildnew += (t6 - t5);
+
+      alld += (t6 - t1);
+    }
+
+    System.out.println(
+        "modMsgPerfs " 
+        + "createop\t" 
+        + "createmsgfromop\t" 
+        + "setentryattr\t"
+        + "encodemsg\t" 
+        + "getbytes\t"
+        + "buildnew\t");
+
+    System.out.println(
+        "modMsgPerfs " 
+        + createop/perfRep/1000.0 + " μs \t" 
+        + createmsgfromop/perfRep/1000.0 + " μs \t" 
+        + setentryattr/perfRep/1000.0 + " μs \t"
+        + encodemsg/perfRep/1000.0 + " μs \t" 
+        + getbytes/perfRep/1000.0 + " μs \t"
+        + buildnew/perfRep/1000.0 + " μs \t");
+  }  
+
+  @Test(enabled=false,dataProvider = "createDeleteData")
+  public void deleteMsgPerfs(String rawDN, List<Attribute> entryAttrList)
+  throws Exception
+  {
+    InternalClientConnection connection =
+        InternalClientConnection.getRootConnection();
+    
+    long createop = 0;
+    long createmsgfromop = 0;
+    long encodemsg = 0;
+    long getbytes = 0;
+    long alld = 0;
+    long setentryattr = 0;
+    long buildnew = 0;
+    long t1,t2,t3,t31,t4,t5,t6 = 0;
+    
+    for (int i=1;i<perfRep;i++)
+    {
+      t1 = System.nanoTime();
+
+      // create op
+      DeleteOperationBasis opBasis =
+        new DeleteOperationBasis(connection, 1, 1,null, DN.decode(rawDN));
+      LocalBackendDeleteOperation op = new LocalBackendDeleteOperation(opBasis);
+      ChangeNumber cn = new ChangeNumber(TimeThread.getTime(),
+        (short) 123, (short) 45);
+      op.setAttachment(SYNCHROCONTEXT, new DeleteContext(cn, "uniqueid"));
+      t2 = System.nanoTime();
+      createop += (t2 - t1);
+
+      // create msg from op
+      DeleteMsg generatedMsg = new DeleteMsg(op);
+      t3 = System.nanoTime();
+      createmsgfromop += (t3 - t2);
+
+      // set entry attr
+      //generatedMsg.setEntryAttributes(entryAttrList);
+      t31 = System.nanoTime();
+      setentryattr += (t31 - t3);
+      
+      // encode msg
+      generatedMsg.encode();
+      t4 = System.nanoTime();
+      encodemsg += (t4 - t31);
+
+      // getBytes
+      byte[] bytes = generatedMsg.getBytes(ProtocolVersion.getCurrentVersion());
+      t5 = System.nanoTime();
+      getbytes += (t5 - t4);
+
+      // getBytes
+      new DeleteMsg(bytes);
+      t6 = System.nanoTime();
+      buildnew += (t6 - t5);
+
+      alld += (t6 - t1);
+    }
+
+    System.out.println(
+        "deleteMsgPerfs " 
+        + "createop\t" 
+        + "createmsgfromop\t" 
+        + "setentryattr\t"
+        + "encodemsg\t" 
+        + "getbytes\t"
+        + "buildnew\t");
+
+    System.out.println(
+        "deleteMsgPerfs " 
+        + createop/perfRep/1000.0 + " μs \t" 
+        + createmsgfromop/perfRep/1000.0 + " μs \t" 
+        + setentryattr/perfRep/1000.0 + " μs \t"
+        + encodemsg/perfRep/1000.0 + " μs \t" 
+        + getbytes/perfRep/1000.0 + " μs \t"
+        + buildnew/perfRep/1000.0 + " μs \t");
+  }
 }
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeReplicationDomain.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeReplicationDomain.java
index fbb952e..06a801b 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeReplicationDomain.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeReplicationDomain.java
@@ -123,7 +123,6 @@
   protected void importBackend(InputStream input) throws DirectoryException
   {
     byte[] buffer = new byte[1000];
-
     int ret;
     do
     {
@@ -136,7 +135,8 @@
             ResultCode.OPERATIONS_ERROR,
             ERR_BACKEND_EXPORT_ENTRY.get("", ""));
       }
-      importString.append(new String(buffer, 0, ret));
+      if (ret>0)
+        importString.append(new String(buffer, 0, ret));
     }
     while (ret >= 0);
   }
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 773babb..d1d639b 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
@@ -152,28 +152,45 @@
         assertTrue(replServerInfo.getGenerationId() == 2);
       }
 
-      for (DSInfo serverInfo : domain1.getReplicasList())
+      int sleepTime = 50;
+      while (true)
       {
-        if (serverInfo.getDsId() == domain2ServerId)
-          assertEquals(serverInfo.getStatus(), ServerStatus.BAD_GEN_ID_STATUS);
-        else
+        try
         {
-          assertTrue(serverInfo.getDsId() == domain1ServerId);
-          assertTrue(serverInfo.getStatus() == ServerStatus.NORMAL_STATUS);
-        }
-      }
+          for (DSInfo serverInfo : domain1.getReplicasList())
+          {
+            if (serverInfo.getDsId() == domain2ServerId)
+              assertEquals(serverInfo.getStatus(), ServerStatus.BAD_GEN_ID_STATUS);
+            else
+            {
+              assertTrue(serverInfo.getDsId() == domain1ServerId);
+              assertTrue(serverInfo.getStatus() == ServerStatus.NORMAL_STATUS);
+            }
+          }
 
-      for (DSInfo serverInfo : domain2.getReplicasList())
-      {
-        if (serverInfo.getDsId() == domain2ServerId)
-          assertTrue(serverInfo.getStatus() == ServerStatus.BAD_GEN_ID_STATUS);
-        else
+          for (DSInfo serverInfo : domain2.getReplicasList())
+          {
+            if (serverInfo.getDsId() == domain2ServerId)
+              assertTrue(serverInfo.getStatus() == ServerStatus.BAD_GEN_ID_STATUS);
+            else
+            {
+              assertTrue(serverInfo.getDsId() == domain1ServerId);
+              assertTrue(serverInfo.getStatus() == ServerStatus.NORMAL_STATUS);
+            }
+          }
+          break;
+        }
+        catch (AssertionError e)
         {
-          assertTrue(serverInfo.getDsId() == domain1ServerId);
-          assertTrue(serverInfo.getStatus() == ServerStatus.NORMAL_STATUS);
-        }
+          if (sleepTime < 30000)
+          {
+            Thread.sleep(sleepTime);
+            sleepTime *=2;
+          }
+          else
+            throw e;
+        } 
       }
-
       Map<Short, ServerState> states1 = domain1.getReplicaStates();
       ServerState state2 = states1.get(domain2ServerId);
       assertNotNull(state2, "getReplicaStates is not showing DS2");

--
Gitblit v1.10.0