mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

pgamba
06.34.2009 76ebd1ad82e2a1fc421519f09c62b948e9376e8a
Entry attributes for ECL - Protocol V4
32 files modified
4196 ■■■■ changed files
opends/resource/schema/02-config.ldif 7 ●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/ReplicationDomainConfiguration.xml 43 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/common/DSInfo.java 25 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java 111 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/protocol/AddMsg.java 486 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java 89 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java 399 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java 69 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java 493 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/protocol/ModifyMsg.java 217 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/protocol/ProtocolVersion.java 11 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/protocol/StartSessionMsg.java 374 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/protocol/TopologyMsg.java 284 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/server/DataServerHandler.java 7 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java 13 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java 5 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/server/ReplicationServerHandler.java 3 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/server/ServerReader.java 29 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/service/ReplicationBroker.java 20 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/service/ReplicationDomain.java 32 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/RawAttribute.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java 135 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java 337 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java 47 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java 2 ●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/DomainFakeCfg.java 15 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java 24 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java 320 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java 538 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeReplicationDomain.java 4 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java 51 ●●●●● patch | view | raw | blame | history
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'
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>
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();
  }
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.
   */
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;
  }
}
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);
  }
}
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;
    }
  }
}
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;
  }
}
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;
    }
  }
}
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());
    }
  }
}
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;
  }
  /**
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);
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;
  }
}
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;
    }
  }
  /**
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
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;
  }
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
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);
      }
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
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.
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;
  }
}
opends/src/server/org/opends/server/types/RawAttribute.java
@@ -218,8 +218,8 @@
        stream.writeOctetString(value);
      }
    }
    stream.writeEndSequence();
    stream.writeEndSet();
    stream.writeEndSequence();
  }
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(
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");
  }
}
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);
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);
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;
  }
}
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());
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);
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");
  }
}
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);
  }
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");