From eff0d28a00c8b5088c365b9b40303be14784eff2 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Fri, 10 Jun 2011 10:25:31 +0000
Subject: [PATCH] Partial fix for OPENDJ-194: Minor improvements to change log content and configuration

---
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/StartSessionMsg.java                                      |   75 ++++
 opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java                                     |   11 
 opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java                                  |  183 +++++++-----
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java       |   19 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java    |   17 
 opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerHandler.java                               |    1 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/DomainFakeCfg.java                  |   16 
 opendj-sdk/opends/src/admin/messages/ExternalChangelogDomainCfgDefn.properties                                                |    6 
 opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java                               |    8 
 opendj-sdk/opends/src/server/org/opends/server/replication/server/DataServerHandler.java                                      |    4 
 opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/ExternalChangelogDomainConfiguration.xml                         |   41 ++
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java                                        |    7 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ExternalChangelogDomainFakeCfg.java |   41 +-
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java                 |   59 ++-
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java               |   10 
 opendj-sdk/opends/src/server/org/opends/server/replication/common/DSInfo.java                                                 |   74 +++-
 opendj-sdk/opends/src/server/org/opends/server/types/Entry.java                                                               |   50 ---
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/TopologyMsg.java                                          |   32 +
 opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ExternalChangelogDomain.java                                |   32 -
 opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java                                     |  203 +++++++++----
 opendj-sdk/opends/resource/schema/02-config.ldif                                                                              |    6 
 21 files changed, 544 insertions(+), 351 deletions(-)

diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index 13f4a63..d934216 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -2446,6 +2446,10 @@
   NAME 'ds-cfg-ecl-include'
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
   X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.7
+  NAME 'ds-cfg-ecl-include-for-deletes'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+  X-ORIGIN 'OpenDJ Directory Server' )
 attributeTypes: ( 1.3.6.1.4.1.26027.1.1.603
   NAME 'ds-cfg-weight'
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
@@ -4262,7 +4266,7 @@
   STRUCTURAL
   MUST ( cn $
          ds-cfg-enabled )
-  MAY  ( ds-cfg-ecl-include )
+  MAY  ( ds-cfg-ecl-include $ ds-cfg-ecl-include-for-deletes )
   X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.235
   NAME 'ds-cfg-collective-attribute-subentries-virtual-attribute'
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/ExternalChangelogDomainConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/ExternalChangelogDomainConfiguration.xml
index deac4c2..3738977 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/ExternalChangelogDomainConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/ExternalChangelogDomainConfiguration.xml
@@ -24,6 +24,7 @@
   !
   !
   !      Copyright 2009 Sun Microsystems, Inc.
+  !      Portions copyright 2011 ForgeRock AS
   ! -->
 <adm:managed-object name="external-changelog-domain"
   plural-name="external-changelog-domains"
@@ -59,17 +60,25 @@
   </adm:property>
   <adm:property name="ecl-include" multi-valued="true" mandatory="false">
     <adm:synopsis>
-      Allows to include some target entry attributes in the external changelog.
+      Specifies a list of attributes which should be published with every
+      change log entry, regardless of whether or not the attribute itself
+      has changed.
     </adm:synopsis>
     <adm:description>
-      Specifies an attribute that will be included in every External Change Log
-      entry related to this replication domain.
+      The list of attributes may include wild cards such as "*" and "+" as
+      well as object class references prefixed with an ampersand, for
+      example "@person".
+      The included attributes will be published using the "includedAttributes"
+      operational attribute as a single LDIF value rather like the
+      "changes" attribute. For modify and modifyDN operations the included
+      attributes will be taken from the entry before any changes were applied.
     </adm:description>
     <adm:default-behavior>
       <adm:undefined/>
     </adm:default-behavior>
     <adm:syntax>
-      <adm:attribute-type />
+      <!--  FIXME: can we constrain this with a regex? -->
+      <adm:string />
     </adm:syntax>
     <adm:profile name="ldap">
       <ldap:attribute>
@@ -77,4 +86,28 @@
       </ldap:attribute>
     </adm:profile>
   </adm:property>
+  <adm:property name="ecl-include-for-deletes" multi-valued="true" mandatory="false">
+    <adm:synopsis>
+      Specifies a list of attributes which should be published with every
+      delete operation change log entry, in addition to those specified by the
+      "ecl-include" property.
+    </adm:synopsis>
+    <adm:description>
+      This property provides a means for applications to archive entries after
+      they have been deleted. See the description of the "ecl-include" property
+      for further information about how the included attributes are published.
+    </adm:description>
+    <adm:default-behavior>
+      <adm:undefined/>
+    </adm:default-behavior>
+    <adm:syntax>
+      <!--  FIXME: can we constrain this with a regex? -->
+      <adm:string />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-ecl-include-for-deletes</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
 </adm:managed-object>
diff --git a/opendj-sdk/opends/src/admin/messages/ExternalChangelogDomainCfgDefn.properties b/opendj-sdk/opends/src/admin/messages/ExternalChangelogDomainCfgDefn.properties
index 9352278..bcacf73 100644
--- a/opendj-sdk/opends/src/admin/messages/ExternalChangelogDomainCfgDefn.properties
+++ b/opendj-sdk/opends/src/admin/messages/ExternalChangelogDomainCfgDefn.properties
@@ -1,6 +1,8 @@
 user-friendly-name=External Changelog Domain
 user-friendly-plural-name=External Changelog Domains
 synopsis=The External Changelog Domain provides configuration of the external changelog for the replication domain.
-property.ecl-include.synopsis=Allows to include some target entry attributes in the external changelog.
-property.ecl-include.description=Specifies an attribute that will be included in every External Change Log entry related to this replication domain.
+property.ecl-include.synopsis=Specifies a list of attributes which should be published with every change log entry, regardless of whether or not the attribute itself has changed.
+property.ecl-include.description=The list of attributes may include wild cards such as "*" and "+" as well as object class references prefixed with an ampersand, for example "@person". The included attributes will be published using the "includedAttributes" operational attribute as a single LDIF value rather like the "changes" attribute. For modify and modifyDN operations the included attributes will be taken from the entry before any changes were applied.
+property.ecl-include-for-deletes.synopsis=Specifies a list of attributes which should be published with every delete operation change log entry, in addition to those specified by the "ecl-include" property.
+property.ecl-include-for-deletes.description=This property provides a means for applications to archive entries after they have been deleted. See the description of the "ecl-include" property for further information about how the included attributes are published.
 property.enabled.synopsis=Indicates whether the External Changelog Domain is enabled.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/common/DSInfo.java b/opendj-sdk/opends/src/server/org/opends/server/replication/common/DSInfo.java
index 636eb8b..ebbdf86 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/common/DSInfo.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/common/DSInfo.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2008-2010 Sun Microsystems, Inc.
+ *      Portions copyright 2011 ForgeRock AS
  */
 package org.opends.server.replication.common;
 
@@ -63,27 +64,44 @@
 
   private Set<String> eclIncludes = new HashSet<String>();
 
+  private Set<String> eclIncludesForDeletes = new HashSet<String>();
+
+
+
   /**
    * Creates a new instance of DSInfo with every given info.
    *
-   * @param dsId The DS id
-   * @param rsId The RS id the DS is connected to
-   * @param generationId The generation id the DS is using
-   * @param status The DS status
-   * @param assuredFlag DS assured replication enabled or not
-   * @param assuredMode DS assured mode
-   * @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.
-   * @param protocolVersion Protocol version supported by this server.
+   * @param dsId
+   *          The DS id
+   * @param rsId
+   *          The RS id the DS is connected to
+   * @param generationId
+   *          The generation id the DS is using
+   * @param status
+   *          The DS status
+   * @param assuredFlag
+   *          DS assured replication enabled or not
+   * @param assuredMode
+   *          DS assured mode
+   * @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.
+   * @param eclIncludesForDeletes
+   *          The list of entry attributes to include in the ECL for deletes.
+   * @param protocolVersion
+   *          Protocol version supported by this server.
    */
-  public DSInfo(int dsId, int rsId, long generationId, ServerStatus status,
-    boolean assuredFlag, AssuredMode assuredMode, byte safeDataLevel,
-    byte groupId, List<String> refUrls, Set<String> eclIncludes,
-    short protocolVersion)
+  public DSInfo(int dsId, int rsId, long generationId,
+      ServerStatus status, boolean assuredFlag,
+      AssuredMode assuredMode, byte safeDataLevel, byte groupId,
+      List<String> refUrls, Set<String> eclIncludes,
+      Set<String> eclIncludesForDeletes, short protocolVersion)
   {
-
     this.dsId = dsId;
     this.rsId = rsId;
     this.generationId = generationId;
@@ -94,6 +112,7 @@
     this.groupId = groupId;
     this.refUrls = refUrls;
     this.eclIncludes = eclIncludes;
+    this.eclIncludesForDeletes = eclIncludesForDeletes;
     this.protocolVersion = protocolVersion;
   }
 
@@ -188,6 +207,15 @@
   }
 
   /**
+   * Get the entry attributes to be included in the ECL for delete operations.
+   * @return The entry attributes to be included in the ECL.
+   */
+  public Set<String> getEclIncludesForDeletes()
+  {
+    return eclIncludesForDeletes;
+  }
+
+  /**
    * Get the protocol version supported by this server.
    * Returns -1 when the protocol version is not known (too old version).
    * @return The protocol version.
@@ -222,9 +250,13 @@
         (groupId == dsInfo.getGroupId()) &&
         (protocolVersion == dsInfo.getProtocolVersion()) &&
         (refUrls.equals(dsInfo.getRefUrls())) &&
-         (((eclIncludes == null) && (dsInfo.getEclIncludes() == null)) ||
-           ((eclIncludes != null) &&
-                         (eclIncludes.equals(dsInfo.getEclIncludes())))));
+        (((eclIncludes == null) && (dsInfo.getEclIncludes() == null)) ||
+          ((eclIncludes != null) &&
+            (eclIncludes.equals(dsInfo.getEclIncludes())))) &&
+        (((eclIncludesForDeletes == null)
+          && (dsInfo.getEclIncludesForDeletes() == null)) ||
+          ((eclIncludesForDeletes != null) &&
+           (eclIncludesForDeletes.equals(dsInfo.getEclIncludesForDeletes())))));
     } else
     {
       return false;
@@ -249,6 +281,8 @@
     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.eclIncludesForDeletes != null ?
+        eclIncludesForDeletes.hashCode() : 0);
     hash = 73 * hash + this.groupId;
     hash = 73 * hash + this.protocolVersion;
     return hash;
@@ -284,6 +318,8 @@
     sb.append(refUrls);
     sb.append(" ; ECL Include: ");
     sb.append(eclIncludes);
+    sb.append(" ; ECL Include for Deletes: ");
+    sb.append(eclIncludesForDeletes);
     return sb.toString();
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ExternalChangelogDomain.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ExternalChangelogDomain.java
index b8a0d94..46db8f4 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ExternalChangelogDomain.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ExternalChangelogDomain.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
+ *      Portions Copyright 2011 ForgeRock AS
  */
 package org.opends.server.replication.plugin;
 
@@ -33,11 +34,9 @@
 import org.opends.server.admin.server.ConfigurationChangeListener;
 import org.opends.server.admin.server.ConfigurationDeleteListener;
 import org.opends.server.admin.std.server.ExternalChangelogDomainCfg;
-import org.opends.server.types.AttributeType;
 import org.opends.server.types.ConfigChangeResult;
 import org.opends.server.types.DN;
 import org.opends.server.types.ResultCode;
-import java.util.HashSet;
 
 /**
  * This class specifies the external changelog feature for a replication
@@ -49,8 +48,8 @@
              ConfigurationChangeListener<ExternalChangelogDomainCfg>
 {
 
-  LDAPReplicationDomain domain;
-  boolean isEnabled;
+  private LDAPReplicationDomain domain;
+  private boolean isEnabled;
 
   /**
    * Constructor from a provided LDAPReplicationDomain.
@@ -60,16 +59,12 @@
   public ExternalChangelogDomain(LDAPReplicationDomain domain,
       ExternalChangelogDomainCfg configuration)
   {
-    this.domain =domain;
+    this.domain = domain;
     this.isEnabled = configuration.isEnabled();
     configuration.addChangeListener(this);
-    if (configuration.getECLInclude() != null)
-    {
-      HashSet<String> attrNames = new HashSet<String>(0);
-      for (AttributeType eclIncludeAttribute : configuration.getECLInclude())
-        attrNames.add(eclIncludeAttribute.getNormalizedPrimaryName());
-      domain.setEclInclude(domain.getServerId(), attrNames);
-    }
+    domain.setEclIncludes(domain.getServerId(),
+        configuration.getECLInclude(),
+        configuration.getECLIncludeForDeletes());
   }
 
 
@@ -95,10 +90,9 @@
     }
 
     this.isEnabled = configuration.isEnabled();
-    HashSet<String> attrNames = new HashSet<String>(0);
-    for (AttributeType eclInclude : configuration.getECLInclude())
-      attrNames.add(eclInclude.getNormalizedPrimaryName());
-    domain.setEclInclude(domain.getServerId(), attrNames);
+    domain.setEclIncludes(domain.getServerId(),
+        configuration.getECLInclude(),
+        configuration.getECLIncludeForDeletes());
     return new ConfigChangeResult(ResultCode.SUCCESS, false);
   }
 
@@ -126,10 +120,8 @@
       }
 
       this.isEnabled = configuration.isEnabled();
-      HashSet<String> attrNames = new HashSet<String>(0);
-      for (AttributeType eclInclude : configuration.getECLInclude())
-        attrNames.add(eclInclude.getNormalizedPrimaryName());
-      domain.changeConfig(attrNames);
+      domain.changeConfig(configuration.getECLInclude(),
+          configuration.getECLIncludeForDeletes());
       return new ConfigChangeResult(ResultCode.SUCCESS, false);
     }
     catch (Exception e)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
index f6b68bc..0b72925 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
@@ -39,27 +39,14 @@
 import static org.opends.server.util.StaticUtils.createEntry;
 import static org.opends.server.util.StaticUtils.getFileForPath;
 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
+import static org.opends.server.util.StaticUtils.toLowerCase;
 
 import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.StringReader;
 import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.StringTokenizer;
-import java.util.TreeMap;
+import java.util.*;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -5104,91 +5091,133 @@
    * @param op
    * @throws DirectoryException
    */
-  private void addEntryAttributesForCL(UpdateMsg msg,PostOperationOperation 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);
-        if (attrs != null)
-          for (Attribute a : attrs)
-            newattrs.add(a);
-      }
-      ((DeleteMsg)msg).setEclIncludes(newattrs);
+      Set<String> names = getEclIncludesForDeletes();
+      PostOperationDeleteOperation delOp = (PostOperationDeleteOperation) op;
+      Entry entry = delOp.getEntryToDelete();
+      ((DeleteMsg) msg).setEclIncludes(getIncludedAttributes(entry, names));
 
       // For delete only, add the Authorized DN since it's required in the
       // ECL entry but is not part of rest of the message.
       DN deleterDN = delOp.getAuthorizationDN();
       if (deleterDN != null)
       {
-        ((DeleteMsg)msg).setInitiatorsName(deleterDN.toString());
+        ((DeleteMsg) msg).setInitiatorsName(deleterDN.toString());
       }
-
     }
     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);
-        if (attrs != null)
-          for (Attribute a : attrs)
-            newattrs.add(a);
-      }
-      ((ModifyMsg)msg).setEclIncludes(newattrs);
+      Set<String> names = getEclIncludes();
+      PostOperationModifyOperation modOp = (PostOperationModifyOperation) op;
+      Entry entry = modOp.getCurrentEntry();
+      ((ModifyMsg) msg).setEclIncludes(getIncludedAttributes(entry, names));
     }
     else if (op instanceof PostOperationModifyDNOperation)
     {
-      Entry entry = null;
+      Set<String> names = getEclIncludes();
       PostOperationModifyDNOperation modDNOp =
-        (PostOperationModifyDNOperation)op;
-      entry = modDNOp.getOriginalEntry();
-      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);
-      }
-      ((ModifyDNMsg)msg).setEclIncludes(newattrs);
+        (PostOperationModifyDNOperation) op;
+      Entry entry = modDNOp.getOriginalEntry();
+      ((ModifyDNMsg) msg).setEclIncludes(getIncludedAttributes(entry, names));
     }
     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);
+      Set<String> names = getEclIncludes();
+      PostOperationAddOperation addOp = (PostOperationAddOperation) op;
+      Entry entry = addOp.getEntryToAdd();
+      ((AddMsg) msg).setEclIncludes(getIncludedAttributes(entry, names));
     }
   }
 
+
+
+  private Collection<Attribute> getIncludedAttributes(Entry entry,
+      Set<String> names)
+  {
+    if (names.isEmpty())
+    {
+      // Fast-path.
+      return Collections.emptySet();
+    }
+    else if (names.size() == 1 && names.contains("*"))
+    {
+      // Potential fast-path for delete operations.
+      LinkedList<Attribute> attributes = new LinkedList<Attribute>();
+      for (List<Attribute> alist : entry.getUserAttributes().values())
+      {
+        attributes.addAll(alist);
+      }
+      Attribute ocattr = entry.getObjectClassAttribute();
+      if (ocattr != null)
+      {
+        attributes.add(ocattr);
+      }
+      return attributes;
+    }
+    else
+    {
+      // Expand @objectclass references in attribute list if needed. We
+      // do this now in order to take into account dynamic schema changes.
+
+      // Only rebuild the attribute set if necessary.
+      boolean needsExpanding = false;
+      for (String name : names)
+      {
+        if (name.startsWith("@"))
+        {
+          needsExpanding = true;
+          break;
+        }
+      }
+
+      Set<String> expandedNames;
+      if (needsExpanding)
+      {
+        expandedNames = new HashSet<String>(names.size());
+        for (String name : names)
+        {
+          if (name.startsWith("@"))
+          {
+            String ocName = name.substring(1);
+            ObjectClass objectClass = DirectoryServer
+                .getObjectClass(toLowerCase(ocName));
+            if (objectClass != null)
+            {
+              for (AttributeType at : objectClass
+                  .getRequiredAttributeChain())
+              {
+                expandedNames.add(at.getNameOrOID());
+              }
+              for (AttributeType at : objectClass
+                  .getOptionalAttributeChain())
+              {
+                expandedNames.add(at.getNameOrOID());
+              }
+            }
+          }
+          else
+          {
+            expandedNames.add(name);
+          }
+        }
+      }
+      else
+      {
+        expandedNames = names;
+      }
+
+      Entry filteredEntry = entry.filterEntry(expandedNames, false,
+          false, false);
+      return filteredEntry.getAttributes();
+    }
+  }
+
+
+
   /**
    * Gets the fractional configuration of this domain.
    * @return The fractional configuration of this domain.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java
index 00feb7c..b6e75c3 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java
@@ -23,12 +23,13 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
+ *      Portions Copyright 2011 ForgeRock AS
  */
 package org.opends.server.replication.protocol;
 
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
 import java.util.zip.DataFormatException;
 
 import org.opends.server.protocols.asn1.ASN1;
@@ -424,7 +425,7 @@
   /**
    * Encode a list of attributes.
    */
-   static private byte[] encodeAttributes(List<Attribute> attributes)
+   static private byte[] encodeAttributes(Collection<Attribute> attributes)
    {
      if (attributes==null)
        return new byte[0];
@@ -606,7 +607,7 @@
    * Set a provided list of entry attributes.
    * @param entryAttrs  The provided list of entry attributes.
    */
-  public void setEclIncludes(List<Attribute> entryAttrs)
+  public void setEclIncludes(Collection<Attribute> entryAttrs)
   {
     this.encodedEclIncludes = encodeAttributes(entryAttrs);
   }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/StartSessionMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/StartSessionMsg.java
index 3f22529..39aa36e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/StartSessionMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/StartSessionMsg.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2008-2009 Sun Microsystems, Inc.
+ *      Portions copyright 2011 ForgeRock AS
  */
 package org.opends.server.replication.protocol;
 
@@ -73,6 +74,8 @@
 
   private Set<String> eclIncludes = new HashSet<String>();
 
+  private Set<String> eclIncludesForDeletes = new HashSet<String>();
+
   /**
    * The protocolVersion that should be used when serializing this message.
    */
@@ -95,7 +98,7 @@
     }
     else
     {
-      decode_V4(in);
+      decode_V4(in, version);
     }
   }
 
@@ -188,7 +191,7 @@
     }
     else
     {
-      return getBytes_V4();
+      return getBytes_V4(protocolVersion);
     }
   }
 
@@ -205,11 +208,11 @@
     }
     else
     {
-      return getBytes_V4();
+      return getBytes_V4(reqProtocolVersion);
     }
   }
 
-  private byte[] getBytes_V4()
+  private byte[] getBytes_V4(short version)
   {
     try
     {
@@ -229,14 +232,23 @@
 
       writer.writeStartSequence();
       for (String attrDef : eclIncludes)
+      {
         writer.writeOctetString(attrDef);
+      }
+      writer.writeEndSequence();
+
+      writer.writeStartSequence();
+      for (String attrDef : eclIncludesForDeletes)
+      {
+        writer.writeOctetString(attrDef);
+      }
       writer.writeEndSequence();
 
       return byteBuilder.toByteArray();
     }
     catch (Exception e)
     {
-      return null;
+      throw new RuntimeException(e);
     }
   }
 
@@ -290,7 +302,7 @@
   // Msg decoding
   // ============
 
-  private void decode_V4(byte[] in)
+  private void decode_V4(byte[] in, short version)
   throws DataFormatException
   {
     ByteSequenceReader reader = ByteString.wrap(in).asReader();
@@ -328,6 +340,14 @@
         this.eclIncludes.add(s);
       }
       asn1Reader.readEndSequence();
+
+      asn1Reader.readStartSequence();
+      while (asn1Reader.hasNextElement())
+      {
+        String s = asn1Reader.readOctetStringAsString();
+        this.eclIncludesForDeletes.add(s);
+      }
+      asn1Reader.readEndSequence();
     }
     catch (Exception e)
     {
@@ -428,7 +448,8 @@
       "\nassuredMode: " + assuredMode +
       "\nsafeDataLevel: " + safeDataLevel +
       "\nreferralsURLs: " + urls +
-      "\nEclIncludes: " + eclIncludes);
+      "\nEclIncludes " + eclIncludes +
+      "\nEclIncludeForDeletes: " + eclIncludesForDeletes);
   }
 
   /**
@@ -459,22 +480,48 @@
   }
 
   /**
-   * Set the list of entry attributes to include in the ECL.
-   * @param eclIncludes The list of attributes.
+   * Set the attributes configured on a server to be included in the ECL.
+   *
+   * @param includeAttributes
+   *          attributes to be included with all change records.
+   * @param includeAttributesForDeletes
+   *          additional attributes to be included with delete change records.
    */
-  public void setEclIncludes(Set<String> eclIncludes)
+  public void setEclIncludes(
+      Set<String> includeAttributes,
+      Set<String> includeAttributesForDeletes)
   {
-    if (eclIncludes != null)
-      this.eclIncludes = eclIncludes;
+    if (includeAttributes != null)
+    {
+      eclIncludes = includeAttributes;
+    }
+
+    if (includeAttributesForDeletes != null)
+    {
+      eclIncludesForDeletes = includeAttributesForDeletes;
+    }
   }
 
   /**
-   * Get the list of entry attributes to include in the ECL..
-   * @return The list of entry attributes to include in the ECL.
+   * Get the attributes to include in each change for the ECL.
+   *
+   * @return The attributes to include in each change for the ECL.
    */
   public Set<String> getEclIncludes()
   {
     return eclIncludes;
   }
 
+
+
+  /**
+   * Get the attributes to include in each delete change for the ECL.
+   *
+   * @return The attributes to include in each delete change for the ECL.
+   */
+  public Set<String> getEclIncludesForDeletes()
+  {
+    return eclIncludesForDeletes;
+  }
+
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/TopologyMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/TopologyMsg.java
index 2fdf43b..4288497 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/TopologyMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/TopologyMsg.java
@@ -151,15 +151,14 @@
         }
 
         Set<String> attrs = new HashSet<String>();
+        Set<String> delattrs = new HashSet<String>();
         short protocolVersion = -1;
-        if (version>=ProtocolVersion.REPLICATION_PROTOCOL_V4)
+        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
-            )
+          while ((nRead != nAttrs) && (pos < in.length))
           {
             length = getNextLength(in, pos);
             String attr = new String(in, pos, length, "UTF-8");
@@ -167,6 +166,19 @@
             pos += length + 1;
             nRead++;
           }
+
+          nAttrs = in[pos++];
+          nRead = 0;
+          /* Read attrs until expected number read */
+          while ((nRead != nAttrs) && (pos < in.length))
+          {
+            length = getNextLength(in, pos);
+            String attr = new String(in, pos, length, "UTF-8");
+            delattrs.add(attr);
+            pos += length + 1;
+            nRead++;
+          }
+
           /* Read Protocol version */
           protocolVersion = Short.valueOf(in[pos++]);
         }
@@ -175,7 +187,7 @@
 
         DSInfo dsInfo = new DSInfo(dsId, rsId, generationId, status,
           assuredFlag, assuredMode, safeDataLevel, groupId, refUrls, attrs,
-          protocolVersion);
+          delattrs, protocolVersion);
         dsList.add(dsInfo);
 
         nDsInfo--;
@@ -347,9 +359,17 @@
             oStream.write(attr.getBytes("UTF-8"));
             oStream.write(0);
           }
+
+          Set<String> delattrs = dsInfo.getEclIncludesForDeletes();
+          oStream.write(delattrs.size());
+          for (String attr : delattrs)
+          {
+            oStream.write(attr.getBytes("UTF-8"));
+            oStream.write(0);
+          }
+
           oStream.write(dsInfo.getProtocolVersion());
         }
-
       }
 
       // Put number of following RS info entries
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/DataServerHandler.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/DataServerHandler.java
index c0f67f9..4848f37 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/DataServerHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/DataServerHandler.java
@@ -77,6 +77,7 @@
   // DS safe data level (relevant if assured mode is safe data)
   private byte safeDataLevel = (byte) -1;
   private Set<String> eclIncludes = new HashSet<String>();
+  private Set<String> eclIncludesForDeletes = new HashSet<String>();
 
   /**
    * Creates a new data server handler.
@@ -651,7 +652,7 @@
   {
     DSInfo dsInfo = new DSInfo(serverId, replicationServerId, generationId,
       status, assuredFlag, assuredMode, safeDataLevel, groupId, refUrls,
-      eclIncludes, protocolVersion);
+      eclIncludes, eclIncludesForDeletes, protocolVersion);
 
     return dsInfo;
   }
@@ -727,6 +728,7 @@
     this.assuredMode = startSessionMsg.getAssuredMode();
     this.safeDataLevel = startSessionMsg.getSafeDataLevel();
     this.eclIncludes = startSessionMsg.getEclIncludes();
+    this.eclIncludesForDeletes = startSessionMsg.getEclIncludesForDeletes();
 
     /*
      * If we have already a generationID set for the domain
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java
index 9bb878d..26bcd5f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java
@@ -91,6 +91,7 @@
   private short protocolVersion = -1;
 
   private Set<String> eclInclude = new HashSet<String>();
+  private Set<String> eclIncludeForDeletes = new HashSet<String>();
 
   /**
    * Creates a new LighweightServerHandler with the provided serverid, connected
@@ -109,13 +110,15 @@
    * @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.
+   * @param eclIncludeForDeletes The list of entry attributes to be added to
+   *                             the ECL.
    * @param protocolVersion The protocol version supported by the remote DS.
    */
   public LightweightServerHandler(ReplicationServerHandler replServerHandler,
     int replicationServerId, int serverId, long generationId, byte groupId,
     ServerStatus status, List<String> refUrls, boolean assuredFlag,
     AssuredMode assuredMode, byte safeDataLevel, Set<String> eclInclude,
-    short protocolVersion)
+    Set<String> eclIncludeForDeletes, short protocolVersion)
   {
     this.replServerHandler = replServerHandler;
     this.rsDomain = replServerHandler.getDomain();
@@ -129,6 +132,7 @@
     this.assuredMode = assuredMode;
     this.safeDataLevel = safeDataLevel;
     this.eclInclude = eclInclude;
+    this.eclIncludeForDeletes = eclIncludeForDeletes;
     this.protocolVersion = protocolVersion;
 
     if (debugEnabled())
@@ -148,7 +152,7 @@
   {
     DSInfo dsInfo = new DSInfo(serverId, replicationServerId, generationId,
       status, assuredFlag, assuredMode, safeDataLevel, groupId, refUrls,
-      eclInclude, protocolVersion);
+      eclInclude, eclIncludeForDeletes, protocolVersion);
 
     return dsInfo;
   }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerHandler.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerHandler.java
index 320aacd..9d7a650b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerHandler.java
@@ -721,6 +721,7 @@
             dsInfo.isAssured(), dsInfo.getAssuredMode(),
             dsInfo.getSafeDataLevel(),
             dsInfo.getEclIncludes(),
+            dsInfo.getEclIncludesForDeletes(),
             dsInfo.getProtocolVersion());
         lsh.startHandler();
         remoteDirectoryServers.put(lsh.getServerId(), lsh);
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java
index 9559252..af7bbf0 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java
@@ -1406,8 +1406,10 @@
           domain.getAssuredMode(),
           domain.getAssuredSdLevel());
         startSessionMsg.setEclIncludes(
-          domain.getEclInclude(domain.getServerId()));
-      } else
+            domain.getEclIncludes(domain.getServerId()),
+            domain.getEclIncludesForDeletes(domain.getServerId()));
+      }
+      else
       {
         startSessionMsg =
           new StartSessionMsg(initStatus, new ArrayList<String>());
@@ -2908,7 +2910,10 @@
     if (domain != null)
     {
       for (DSInfo info : dsList)
-        domain.setEclInclude(info.getDsId(), info.getEclIncludes());
+      {
+        domain.setEclIncludes(info.getDsId(), info.getEclIncludes(),
+            info.getEclIncludesForDeletes());
+      }
     }
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java
index 6165758..e2be1e3 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java
@@ -38,17 +38,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.SocketTimeoutException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.*;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -305,9 +295,15 @@
    */
   private final ChangeNumberGenerator generator;
 
-  private final Map<Integer, Set<String>> eclIncludeByServer =
-    new ConcurrentHashMap<Integer, Set<String>>();
-  Set<String> crossServersECLIncludes = new HashSet<String>();
+  private final Object eclIncludesLock = new Object();
+  private final Map<Integer, Set<String>> eclIncludesByServer =
+    new HashMap<Integer, Set<String>>();
+  private Set<String> eclIncludesAllServers = Collections.emptySet();
+
+  private final Map<Integer, Set<String>> eclIncludesForDeletesByServer =
+    new HashMap<Integer, Set<String>>();
+  private Set<String> eclIncludesForDeletesAllServers = Collections
+      .emptySet();
 
   /**
    * An object used to protect the initialization of the underlying broker
@@ -3174,37 +3170,23 @@
     }
   }
 
+
+
   /**
-   * Change some ReplicationDomain parameters : the ECL include attribute.
+   * Applies a configuration change to the attributes which should be be
+   * included in the ECL.
    *
-   * @param newECLInclude The new ECL attribute.
+   * @param includeAttributes
+   *          attributes to be included with all change records.
+   * @param includeAttributesForDeletes
+   *          additional attributes to be included with delete change records.
    */
-  public void changeConfig(Set<String> newECLInclude)
+  public void changeConfig(Set<String> includeAttributes,
+      Set<String> includeAttributesForDeletes)
   {
-    boolean configECLIncludeChanged = false;
-    Set<String>  currentECLInclude = this.getEclInclude(serverID);
-
-    if (newECLInclude.size() != currentECLInclude.size())
+    if (setEclIncludes(serverID, includeAttributes,
+        includeAttributesForDeletes))
     {
-      configECLIncludeChanged = true;
-    }
-    else
-    {
-      // compare current config and new config
-      for (String attr : currentECLInclude)
-      {
-        if (!newECLInclude.contains(attr))
-        {
-          configECLIncludeChanged = true;
-          break;
-        }
-      }
-    }
-
-    if (configECLIncludeChanged)
-    {
-      // set new config
-      this.setEclInclude(this.serverID, newECLInclude);
       if (broker != null)
       {
         disableService();
@@ -3213,6 +3195,8 @@
     }
   }
 
+
+
   /**
    * This method should trigger an export of the replicated data.
    * to the provided outputStream.
@@ -3633,50 +3617,137 @@
       return 0;
   }
 
+
+
   /**
-   * Set the attributes configured on a server  to be included in the ECL.
-   * @param serverId    server where these attributes are configured.
-   * @param attributes  the configured attributes.
+   * Set the attributes configured on a server to be included in the ECL.
+   *
+   * @param serverId
+   *          Server where these attributes are configured.
+   * @param includeAttributes
+   *          Attributes to be included with all change records, may include
+   *          wild-cards.
+   * @param includeAttributesForDeletes
+   *          Additional attributes to be included with delete change records,
+   *          may include wild-cards.
+   * @return {@code true} if the set of attributes was modified.
    */
-  public void setEclInclude(int serverId, Set<String> attributes)
+  public boolean setEclIncludes(int serverId,
+      Set<String> includeAttributes,
+      Set<String> includeAttributesForDeletes)
   {
-    synchronized(eclIncludeByServer)
+    boolean configurationChanged = false;
+
+    synchronized (eclIncludesLock)
     {
-      eclIncludeByServer.put(serverId, attributes);
+      Set<String> s1 = new HashSet<String>(includeAttributes);
+
+      // Combine all+delete attributes.
+      Set<String> s2 = new HashSet<String>(s1);
+      s2.addAll(includeAttributesForDeletes);
+
+      Set<String> s = eclIncludesByServer.get(serverId);
+      if (!s1.equals(s))
+      {
+        configurationChanged = true;
+        eclIncludesByServer.put(serverId, Collections.unmodifiableSet(s1));
+      }
+
+      s = eclIncludesForDeletesByServer.get(serverId);
+      if (!s2.equals(s))
+      {
+        configurationChanged = true;
+        eclIncludesForDeletesByServer.put(serverId,
+            Collections.unmodifiableSet(s2));
+      }
 
       // and rebuild the global list to be ready for usage
-      crossServersECLIncludes.clear();
-      for (Set<String> attributesByServer : eclIncludeByServer.values())
-        for (String attribute : attributesByServer)
-          crossServersECLIncludes.add(attribute);
+      s = new HashSet<String>();
+      for (Set<String> attributes : eclIncludesByServer.values())
+      {
+        s.addAll(attributes);
+      }
+      eclIncludesAllServers = Collections.unmodifiableSet(s);
+
+      s = new HashSet<String>();
+      for (Set<String> attributes : eclIncludesForDeletesByServer.values())
+      {
+        s.addAll(attributes);
+      }
+      eclIncludesForDeletesAllServers = Collections.unmodifiableSet(s);
     }
+
+    return configurationChanged;
   }
 
+
+
   /**
    * Get the attributes to include in each change for the ECL.
-   * It's a set : an attribute appears once even if configured on more than one
-   * server.
+   *
    * @return The attributes to include in each change for the ECL.
    */
-  public Set<String> getEclInclude()
+  public Set<String> getEclIncludes()
   {
-    return crossServersECLIncludes;
-  }
-
-  /**
-   * Get the attributes to include in each change for the ECL
-   * for a given serverId.
-   * @param  serverId The serverId for which we want the include attributes.
-   * @return The attributes.
-   */
-  public Set<String> getEclInclude(int serverId)
-  {
-    synchronized(eclIncludeByServer)
+    synchronized (eclIncludesLock)
     {
-      return eclIncludeByServer.get(serverId);
+      return eclIncludesAllServers;
     }
   }
 
+
+
+  /**
+   * Get the attributes to include in each delete change for the ECL.
+   *
+   * @return The attributes to include in each delete change for the ECL.
+   */
+  public Set<String> getEclIncludesForDeletes()
+  {
+    synchronized (eclIncludesLock)
+    {
+      return eclIncludesForDeletesAllServers;
+    }
+  }
+
+
+
+  /**
+   * Get the attributes to include in each change for the ECL for a given
+   * serverId.
+   *
+   * @param serverId
+   *          The serverId for which we want the include attributes.
+   * @return The attributes.
+   */
+  public Set<String> getEclIncludes(int serverId)
+  {
+    synchronized (eclIncludesLock)
+    {
+      return eclIncludesByServer.get(serverId);
+    }
+  }
+
+
+
+  /**
+   * Get the attributes to include in each change for the ECL for a given
+   * serverId.
+   *
+   * @param serverId
+   *          The serverId for which we want the include attributes.
+   * @return The attributes.
+   */
+  public Set<String> getEclIncludesForDeletes(int serverId)
+  {
+    synchronized (eclIncludesLock)
+    {
+      return eclIncludesForDeletesByServer.get(serverId);
+    }
+  }
+
+
+
   /**
    * Returns the ChangeNUmber of the last Change that was fully processed
    * by this ReplicationDomain.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java b/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
index 7f24093..0782a5f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
@@ -5062,9 +5062,7 @@
   @Override
   public String toString()
   {
-    StringBuilder buffer = new StringBuilder();
-    toString(buffer);
-    return buffer.toString();
+    return toLDIFString();
   }
 
 
@@ -5078,51 +5076,7 @@
    */
   public void toString(StringBuilder buffer)
   {
-    buffer.append("Entry(dn=\"");
-    dn.toString(buffer);
-
-    buffer.append("\", objectClasses={");
-    if (! objectClasses.isEmpty())
-    {
-      Iterator<String> ocNames = objectClasses.values().iterator();
-      buffer.append(ocNames.next());
-
-      while (ocNames.hasNext())
-      {
-        buffer.append(",");
-        buffer.append(ocNames.next());
-      }
-    }
-
-    buffer.append("}, userAttrs={");
-    if (! userAttributes.isEmpty())
-    {
-      Iterator<AttributeType> attrs =
-           userAttributes.keySet().iterator();
-      buffer.append(attrs.next().getNameOrOID());
-
-      while (attrs.hasNext())
-      {
-        buffer.append(",");
-        buffer.append(attrs.next().getNameOrOID());
-      }
-    }
-
-    buffer.append("}, operationalAttrs={");
-    if (! operationalAttributes.isEmpty())
-    {
-      Iterator<AttributeType> attrs =
-           operationalAttributes.keySet().iterator();
-      buffer.append(attrs.next().getNameOrOID());
-
-      while (attrs.hasNext())
-      {
-        buffer.append(",");
-        buffer.append(attrs.next().getNameOrOID());
-      }
-    }
-
-    buffer.append("})");
+    buffer.append(toString());
   }
 
 
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java
index 0267b11..cc96e44 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java
@@ -48,16 +48,7 @@
 import java.io.StringReader;
 import java.net.ServerSocket;
 import java.net.Socket;
-import java.util.ArrayList;
-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 java.util.*;
 
 import org.opends.server.TestCaseUtils;
 import org.opends.server.api.Backend;
@@ -117,7 +108,6 @@
 import org.opends.server.types.AbstractOperation;
 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;
 import org.opends.server.types.ByteString;
@@ -693,7 +683,7 @@
       DomainFakeCfg domainConf =
         new DomainFakeCfg(baseDn2,  1602, replServers);
       ExternalChangelogDomainFakeCfg eclCfg =
-        new ExternalChangelogDomainFakeCfg(true, null);
+        new ExternalChangelogDomainFakeCfg(true, null, null);
       domainConf.setExternalChangelogDomain(eclCfg);
       LDAPReplicationDomain domain2 =
         MultimasterReplication.createNewDomain(domainConf);
@@ -753,7 +743,7 @@
       }
 
       eclCfg =
-        new ExternalChangelogDomainFakeCfg(false, null);
+        new ExternalChangelogDomainFakeCfg(false, null, null);
       domainConf.setExternalChangelogDomain(eclCfg);
       domain2.applyConfigurationChange(domainConf);
 
@@ -4008,11 +3998,11 @@
         new DomainFakeCfg(baseDn2, 1702, replServers);
 
       // on o=test2,sid=1702 include attrs set to : 'sn'
-      SortedSet<AttributeType> eclInclude = new TreeSet<AttributeType>();
-      eclInclude.add(DirectoryServer.getAttributeType("sn"));
-      eclInclude.add(DirectoryServer.getAttributeType("roomnumber"));
+      SortedSet<String> eclInclude = new TreeSet<String>();
+      eclInclude.add("sn");
+      eclInclude.add("roomnumber");
       ExternalChangelogDomainFakeCfg eclCfg =
-        new ExternalChangelogDomainFakeCfg(true, eclInclude);
+        new ExternalChangelogDomainFakeCfg(true, eclInclude, eclInclude);
       domainConf.setExternalChangelogDomain(eclCfg);
       // Set a Changetime heartbeat interval low enough (less than default
       // value that is 1000 ms) for the test to be sure to consider all changes
@@ -4028,10 +4018,13 @@
         new DomainFakeCfg(baseDn3, 1703, replServers);
 
       // on o=test3,sid=1703 include attrs set to : 'objectclass'
-      eclInclude = new TreeSet<AttributeType>();
-      eclInclude.add(DirectoryServer.getAttributeType("objectclass"));
-      eclCfg =
-        new ExternalChangelogDomainFakeCfg(true, eclInclude);
+      eclInclude = new TreeSet<String>();
+      eclInclude.add("objectclass");
+
+      TreeSet<String> eclIncludeForDeletes = new TreeSet<String>();
+      eclIncludeForDeletes.add("*");
+
+      eclCfg = new ExternalChangelogDomainFakeCfg(true, eclInclude, eclIncludeForDeletes);
       domainConf.setExternalChangelogDomain(eclCfg);
       // Set a Changetime heartbeat interval low enough (less than default
       // value that is 1000 ms) for the test to be sure to consider all changes
@@ -4043,10 +4036,11 @@
       // on o=test2,sid=1704 include attrs set to : 'cn'
       domainConf =
         new DomainFakeCfg(baseDn2, 1704, replServers);
-      eclInclude = new TreeSet<AttributeType>();
-      eclInclude.add(DirectoryServer.getAttributeType("cn"));
+      eclInclude = new TreeSet<String>();
+      eclInclude.add("cn");
+
       eclCfg =
-        new ExternalChangelogDomainFakeCfg(true, eclInclude);
+        new ExternalChangelogDomainFakeCfg(true, eclInclude, eclInclude);
       domainConf.setExternalChangelogDomain(eclCfg);
       // Set a Changetime heartbeat interval low enough (less than default
       // value that is 1000 ms) for the test to be sure to consider all changes
@@ -4185,8 +4179,23 @@
 
             HashSet<String> eoc = new HashSet<String>();
             eoc.add("person");eoc.add("inetOrgPerson");eoc.add("organizationalPerson");eoc.add("top");
-            assertEquals(targetEntry.getAttributes().size(), 0); // objectClass is handled separately
             checkValues(targetEntry,"objectclass",eoc);
+
+            String changeType = getAttributeValue(resultEntry,
+                "changetype");
+            if (changeType.equals("delete"))
+            {
+              // We are using "*" for deletes so should get back 4 attributes.
+              assertEquals(targetEntry.getAttributes().size(), 4);
+              checkValue(targetEntry, "uid", "robert");
+              checkValue(targetEntry, "cn", "Robert Hue2");
+              checkValue(targetEntry, "telephonenumber", "555555");
+              checkValue(targetEntry, "sn", "Robby");
+            }
+            else
+            {
+              assertEquals(targetEntry.getAttributes().size(), 0);
+            }
           }
           if (targetdn.endsWith("cn=fiona jensen,o=test2"))
           {
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/DomainFakeCfg.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/DomainFakeCfg.java
index e00d04c..22f8ab3 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/DomainFakeCfg.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/DomainFakeCfg.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2007-2010 Sun Microsystems, Inc.
+ *      Portions copyright 2011 ForgeRock AS
  */
 package org.opends.server.replication.plugin;
 
@@ -78,9 +79,8 @@
   private SortedSet<String> fractionalExcludes = new TreeSet<String>();
   private SortedSet<String> fractionalIncludes = new TreeSet<String>();
 
-  private SortedSet<String> eclIncludes = new TreeSet<String>();
   private ExternalChangelogDomainCfg eclCfg =
-    new ExternalChangelogDomainFakeCfg(true, new TreeSet<AttributeType>());
+    new ExternalChangelogDomainFakeCfg(true, null, null);
 
   /**
    * Creates a new Domain with the provided information
@@ -380,16 +380,6 @@
     return true;
   }
 
-  public void setEclIncludes(SortedSet<String> attrs)
-  {
-    this.eclIncludes = attrs;
-  }
-
-  public SortedSet<String> getECLInclude()
-  {
-    return this.eclIncludes;
-  }
-
   public long getInitializationHeartbeatInterval()
   {
     return 180;
@@ -492,7 +482,7 @@
   {
     return true;
   }
-  
+
   /**
    * Gets the "conflicts-historical-purge-delay" property.
    * <p>
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ExternalChangelogDomainFakeCfg.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ExternalChangelogDomainFakeCfg.java
index 7ad58c9..c976d46 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ExternalChangelogDomainFakeCfg.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ExternalChangelogDomainFakeCfg.java
@@ -23,41 +23,47 @@
  *
  *
  *      Copyright 2007-2009 Sun Microsystems, Inc.
+ *      Portions copyright 2011 ForgeRock AS
  */
 package org.opends.server.replication.plugin;
 
 import java.util.SortedSet;
+import java.util.TreeSet;
 
 import org.opends.server.admin.server.ConfigurationChangeListener;
 import org.opends.server.admin.std.server.ExternalChangelogDomainCfg;
-import org.opends.server.types.AttributeType;
 import org.opends.server.types.DN;
 
 /**
  * This class implement a configuration object for the ExternalChangelog domain
  * that can be used in unit tests to instantiate ExternalChangelogDomain.
  */
-public class ExternalChangelogDomainFakeCfg 
+public class ExternalChangelogDomainFakeCfg
   implements ExternalChangelogDomainCfg
 {
 
   // The value of the "ecl-include" property.
-  private SortedSet<AttributeType> pECLInclude;
+  private SortedSet<String> pECLInclude;
+
+  // The value of the "ecl-include-for-deletes" property.
+  private SortedSet<String> pECLIncludeForDeletes;
 
   // The value of the "enabled" property.
   private boolean pEnabled;
 
   private DN pDN;
-  
+
   /**
    * Creates a new Domain with the provided information
    * (assured mode disabled, default group id)
    */
   public ExternalChangelogDomainFakeCfg(boolean isEnabled,
-      SortedSet<AttributeType> eCLInclude)
+      SortedSet<String> eclInclude,
+      SortedSet<String> eclIncludeForDeletes)
   {
     this.pEnabled = isEnabled;
-    this.pECLInclude = eCLInclude;
+    this.pECLInclude = eclInclude != null ? eclInclude : new TreeSet<String>();
+    this.pECLIncludeForDeletes = eclIncludeForDeletes != null ? eclIncludeForDeletes : new TreeSet<String>();
   }
 
   /**
@@ -92,29 +98,14 @@
 
 
 
-  /**
-   * Gets the "ecl-include" property.
-   * <p>
-   * Allows to include some target entry attributes in the external
-   * changelog.
-   * <p>
-   * Specifies an attribute that will be included in every External
-   * Change Log entry related to this replication domain.
-   *
-   * @return Returns an unmodifiable set containing the values of the "ecl-include" property.
-   */
-  public SortedSet<AttributeType> getECLInclude()
+  public SortedSet<String> getECLInclude()
   {
     return this.pECLInclude;
   }
 
-  /**
-   * Set eclInclude.
-   * @param eclInclude the attribute to include.
-   */
-  public void setECLInclude(SortedSet<AttributeType> eclInclude)
+  public SortedSet<String> getECLIncludeForDeletes()
   {
-    this.pECLInclude = eclInclude;
+    return this.pECLIncludeForDeletes;
   }
 
 
@@ -140,7 +131,7 @@
   {
     return this.pEnabled;
   }
-  
+
   public DN dn()
   {
     return pDN;
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java
index 70d7e07..735f221 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java
@@ -23,12 +23,11 @@
  *
  *
  *      Copyright 2008-2010 Sun Microsystems, Inc.
+ *      Portions copyright 2011 ForgeRock AS
  */
 package org.opends.server.replication.plugin;
 
 import java.net.UnknownHostException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 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;
@@ -922,7 +921,7 @@
     }
 
     return new DSInfo(dsId, rsId, TEST_DN_WITH_ROOT_ENTRY_GENID, status, assuredFlag, assMode,
-       (byte)assuredSdLevel, groupId, urls, eclIncludes, protocolVersion);
+       (byte)assuredSdLevel, groupId, urls, eclIncludes, eclIncludes, protocolVersion);
   }
 
   /**
@@ -1122,10 +1121,11 @@
      byte safeDataLevel = rd.getAssuredSdLevel();
      byte groupId = rd.getGroupId();
      List<String> refUrls = rd.getRefUrls();
-     Set<String> eclInclude = rd.getEclInclude();
+     Set<String> eclInclude = rd.getEclIncludes();
+     Set<String> eclIncludeForDeletes = rd.getEclIncludesForDeletes();
      short protocolVersion = 4;
      DSInfo dsInfo = new DSInfo(dsId, rsId, TEST_DN_WITH_ROOT_ENTRY_GENID, status, assuredFlag, assuredMode,
-       safeDataLevel, groupId, refUrls, eclInclude, protocolVersion);
+       safeDataLevel, groupId, refUrls, eclInclude, eclIncludeForDeletes, protocolVersion);
      dsList.add(dsInfo);
 
      TopoView dsTopoView = new TopoView(dsList, rd.getRsList());
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java
index 09b1561..9e17bc9 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
+ *      Portions copyright 2011 ForgeRock AS
  */
 
 package org.opends.server.replication.protocol;
@@ -1069,16 +1070,16 @@
     urls4.add("ldaps://host:port/dc=foobar2??sub?(sn=Another Entry 2)");
 
     DSInfo dsInfo1 = new DSInfo(13, 26, (long)154631, ServerStatus.FULL_UPDATE_STATUS,
-      false, AssuredMode.SAFE_DATA_MODE, (byte)12, (byte)132, urls1, new HashSet<String>(), (short)-1);
+      false, AssuredMode.SAFE_DATA_MODE, (byte)12, (byte)132, urls1, new HashSet<String>(), new HashSet<String>(), (short)-1);
 
     DSInfo dsInfo2 = new DSInfo(-436, 493, (long)-227896, ServerStatus.DEGRADED_STATUS,
-      true, AssuredMode.SAFE_READ_MODE, (byte)-7, (byte)-265, urls2, new HashSet<String>(), (short)-1);
+      true, AssuredMode.SAFE_READ_MODE, (byte)-7, (byte)-265, urls2, new HashSet<String>(), new HashSet<String>(), (short)-1);
 
     DSInfo dsInfo3 = new DSInfo(2436, 591, (long)0, ServerStatus.NORMAL_STATUS,
-      false, AssuredMode.SAFE_READ_MODE, (byte)17, (byte)0, urls3, new HashSet<String>(), (short)-1);
+      false, AssuredMode.SAFE_READ_MODE, (byte)17, (byte)0, urls3, new HashSet<String>(), new HashSet<String>(), (short)-1);
 
     DSInfo dsInfo4 = new DSInfo(415, 146, (long)0, ServerStatus.BAD_GEN_ID_STATUS,
-      true, AssuredMode.SAFE_DATA_MODE, (byte)2, (byte)15, urls4, new HashSet<String>(), (short)-1);
+      true, AssuredMode.SAFE_DATA_MODE, (byte)2, (byte)15, urls4, new HashSet<String>(), new HashSet<String>(), (short)-1);
 
     List<DSInfo> dsList1 = new ArrayList<DSInfo>();
     dsList1.add(dsInfo1);
@@ -1261,7 +1262,7 @@
    * using protocol VLAST and V3 are working.
    */
   @Test(enabled=true, dataProvider="createInitializationRequestMsgData")
-  public void initializationRequestMsgTestVLASTV3(int sender, int dest, 
+  public void initializationRequestMsgTestVLASTV3(int sender, int dest,
       String baseDn, int initWindow)
   throws Exception
   {
@@ -1305,7 +1306,7 @@
     String baseDn = "dc=whatever";
     int entryCount = 56;
     int initWindow = 22;
-    Object[] set1 = new Object[] {sender, dest, initiator, baseDn, 
+    Object[] set1 = new Object[] {sender, dest, initiator, baseDn,
         entryCount, initWindow };
     return new Object [][] { set1};
   }
@@ -1315,7 +1316,7 @@
    * using protocol VLAST and V3 are working.
    */
   @Test(enabled=true, dataProvider="createInitializeTargetMsgData")
-  public void initializeTargetMsgTestVLASTV3(int sender, int dest, 
+  public void initializeTargetMsgTestVLASTV3(int sender, int dest,
       int initiator, String baseDn, int entryCount, int initWindow)
   throws Exception
   {
@@ -1354,7 +1355,7 @@
     assertEquals(msg.getInitiatorID(), vlastMsg.getInitiatorID());
     assertEquals(msg.getInitWindow(), vlastMsg.getInitWindow());
   }
-  
+
   @DataProvider(name = "createEntryMsgV3")
   public Object[][] createEntryMsgV3()
   {
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
index ef8fa71..53f1a71 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
+ *      Portions copyright 2011 ForgeRock AS
  */
 package org.opends.server.replication.protocol;
 
@@ -380,7 +381,7 @@
         i++;
       }
     }
-    
+
     Operation generatedOperation = generatedMsg.createOperation(connection);
 
     assertEquals(generatedOperation.getClass(), DeleteOperationBasis.class);
@@ -1006,19 +1007,19 @@
     Set<String> a4 = new HashSet<String>();
 
     DSInfo dsInfo1 = new DSInfo(13, 26, (long)154631, ServerStatus.FULL_UPDATE_STATUS,
-      false, AssuredMode.SAFE_DATA_MODE, (byte)12, (byte)132, urls1, a1, (short)1);
+      false, AssuredMode.SAFE_DATA_MODE, (byte)12, (byte)132, urls1, a1, a1, (short)1);
 
     DSInfo dsInfo2 = new DSInfo(-436, 493, (long)-227896, ServerStatus.DEGRADED_STATUS,
-      true, AssuredMode.SAFE_READ_MODE, (byte)-7, (byte)-265, urls2, a2, (short)2);
+      true, AssuredMode.SAFE_READ_MODE, (byte)-7, (byte)-265, urls2, a2, a2, (short)2);
 
     DSInfo dsInfo3 = new DSInfo(2436, 591, (long)0, ServerStatus.NORMAL_STATUS,
-      false, AssuredMode.SAFE_READ_MODE, (byte)17, (byte)0, urls3, a3, (short)3);
+      false, AssuredMode.SAFE_READ_MODE, (byte)17, (byte)0, urls3, a3, a3, (short)3);
 
     DSInfo dsInfo4 = new DSInfo(415, 146, (long)0, ServerStatus.BAD_GEN_ID_STATUS,
-      true, AssuredMode.SAFE_DATA_MODE, (byte)2, (byte)15, urls4, a4, (short)4);
+      true, AssuredMode.SAFE_DATA_MODE, (byte)2, (byte)15, urls4, a4, a4, (short)4);
 
     DSInfo dsInfo5 = new DSInfo(452436, 45591, (long)0, ServerStatus.NORMAL_STATUS,
-        false, AssuredMode.SAFE_READ_MODE, (byte)17, (byte)0, urls3, a1, (short)5);
+        false, AssuredMode.SAFE_READ_MODE, (byte)17, (byte)0, urls3, a1, a1, (short)5);
 
     List<DSInfo> dsList1 = new ArrayList<DSInfo>();
     dsList1.add(dsInfo1);
@@ -1138,7 +1139,7 @@
   {
     StartSessionMsg msg = new StartSessionMsg(status, refUrls, assuredFlag,
       assuredMode, safedataLevel);
-    msg.setEclIncludes(attrs);
+    msg.setEclIncludes(attrs, attrs);
     StartSessionMsg newMsg =
       new StartSessionMsg(msg.getBytes(),ProtocolVersion.getCurrentVersion());
     assertEquals(msg.getStatus(), newMsg.getStatus());
@@ -1146,8 +1147,8 @@
     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());
+    assertTrue(attrs.equals(newMsg.getEclIncludes()));
+    assertTrue(attrs.equals(newMsg.getEclIncludesForDeletes()));
   }
 
   /**

--
Gitblit v1.10.0