From d04fb0f282e0fd9a4bc80d3f9d5ee15506a3b83b Mon Sep 17 00:00:00 2001
From: gbellato <gbellato@localhost>
Date: Mon, 08 Dec 2008 08:03:33 +0000
Subject: [PATCH] Merge the replication-service branch with the OpenDS trunk

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalTest.java                   |   12 
 opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java                                    |   72 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java      |   20 
 opends/src/server/org/opends/server/replication/protocol/AckMsg.java                                                 |   67 
 opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java                                          |  492 ++
 opends/src/server/org/opends/server/replication/plugin/RemotePendingChanges.java                                     |   40 
 opends/src/server/org/opends/server/replication/server/ExpectedAcksInfo.java                                         |  141 
 opends/src/server/org/opends/server/replication/server/ServerWriter.java                                             |    6 
 opends/src/server/org/opends/server/replication/plugin/PendingChanges.java                                           |   48 
 opends/src/server/org/opends/server/replication/server/SafeReadExpectedAcksInfo.java                                 |  240 +
 opends/src/server/org/opends/server/replication/service/ReplInputStream.java                                         |   10 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ChangeNumberControlPluginTestCase.java       |   44 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReSyncTest.java                              |   13 
 opends/src/messages/messages/replication.properties                                                                  |   19 
 opends/src/server/org/opends/server/replication/plugin/FakeAddOperation.java                                         |    2 
 opends/src/server/org/opends/server/replication/protocol/EntryMsg.java                                               |   29 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java                          |  141 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeStressReplicationDomain.java     |  159 
 opends/src/server/org/opends/server/replication/protocol/InitializeTargetMsg.java                                    |   19 
 opends/src/server/org/opends/server/replication/server/MonitorData.java                                              |   66 
 opends/src/server/org/opends/server/replication/protocol/ServerStartMsg.java                                         |   16 
 opends/src/server/org/opends/server/replication/server/SafeDataExpectedAcksInfo.java                                 |  110 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/IsolationTest.java                    |   11 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/MonitorTest.java                      |    9 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java           |  317 +
 opends/src/server/org/opends/server/replication/service/ListenerThread.java                                          |   38 
 opends/resource/config/config.ldif                                                                                   |    6 
 opends/src/server/org/opends/server/replication/protocol/HeartbeatMonitor.java                                       |    3 
 opends/src/server/org/opends/server/replication/server/ServerReader.java                                             |    8 
 opends/src/server/org/opends/server/replication/protocol/InitializeRequestMsg.java                                   |    4 
 opends/src/server/org/opends/server/replication/service/ReplicationDomain.java                                       | 2433 +++++++++++
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ModifyConflictTest.java               |    7 
 opends/src/server/org/opends/server/replication/service/InitializeTargetTask.java                                    |   45 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/StressTest.java                              |    4 
 opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java                                    |   24 
 opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java                                    | 1874 +------
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeReplicationDomain.java           |  149 
 opends/src/server/org/opends/server/replication/plugin/ReplayThread.java                                             |    7 
 opends/src/server/org/opends/server/replication/plugin/UpdateToReplay.java                                           |   14 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ReplicationServerFailoverTest.java    |   12 
 opends/src/server/org/opends/server/replication/plugin/FakeModdnOperation.java                                       |    2 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/AssuredReplicationPluginTest.java     | 1535 +++++++
 opends/src/server/org/opends/server/replication/service/SetGenerationIdTask.java                                     |   32 
 opends/src/server/org/opends/server/replication/server/StatusAnalyzer.java                                           |    2 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/StateMachineTest.java                 |  159 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/GenerationIdTest.java                        |  325 -
 opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java                                                 |    6 
 opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java                                         |    7 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/GroupIdHandshakeTest.java             |   24 
 opends/src/server/org/opends/server/replication/server/DbHandler.java                                                |    5 
 opends/src/server/org/opends/server/replication/protocol/UpdateMsg.java                                              |  596 -
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java                   |   10 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java         |   68 
 opends/src/server/org/opends/server/replication/plugin/ReplLDIFOutputStream.java                                     |    4 
 opends/src/server/org/opends/server/replication/server/ReplicationBackend.java                                       |  280 
 opends/src/server/org/opends/server/replication/server/ServerHandler.java                                            |  313 -
 opends/src/server/org/opends/server/replication/service/InitializeTask.java                                          |   37 
 opends/src/server/org/opends/server/replication/server/NotAssuredUpdateMsg.java                                      |  358 +
 opends/src/server/org/opends/server/replication/service/ReplicationBroker.java                                       |  450 -
 opends/src/server/org/opends/server/replication/protocol/WindowMsg.java                                              |    3 
 opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java                                  |  672 ++
 opends/src/server/org/opends/server/tools/dsreplication/ReplicationCliMain.java                                      |    4 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerDynamicConfTest.java |    4 
 opends/src/server/org/opends/server/replication/common/ServerState.java                                              |   31 
 opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java                                     |   20 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ProtocolWindowTest.java                      |    3 
 opends/src/server/org/opends/server/replication/common/ServerStatus.java                                             |   27 
 opends/src/server/org/opends/server/replication/service/package-info.java                                            |   26 
 opends/src/server/org/opends/server/replication/protocol/AddMsg.java                                                 |    2 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalCsnOrderingTest.java        |   11 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java                 |   56 
 opends/src/server/org/opends/server/replication/plugin/MultimasterReplication.java                                   |   59 
 opends/src/server/org/opends/server/replication/service/ReplOutputStream.java                                        |   82 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/DbHandlerTest.java                    |    5 
 opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java                                 |    2 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java                     |   80 
 /dev/null                                                                                                            |   84 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/DependencyTest.java                          |   18 
 opends/src/server/org/opends/server/replication/server/ReplicationServer.java                                        |   20 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java                     |   45 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/PersistentServerStateTest.java        |   12 
 opends/src/server/org/opends/server/replication/plugin/PendingChange.java                                            |   10 
 opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java                                        |    2 
 opends/src/server/org/opends/server/replication/service/ReplicationMonitor.java                                      |  331 +
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java            |   66 
 opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java                                              |    2 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ComputeBestServerTest.java            |   52 
 opends/src/server/org/opends/server/replication/server/ReplicationDB.java                                            |    5 
 opends/src/server/org/opends/server/replication/server/ReplicationDbEnv.java                                         |   53 
 89 files changed, 8,912 insertions(+), 3,819 deletions(-)

diff --git a/opends/resource/config/config.ldif b/opends/resource/config/config.ldif
index 2107319..f782b11 100644
--- a/opends/resource/config/config.ldif
+++ b/opends/resource/config/config.ldif
@@ -58,9 +58,9 @@
 ds-cfg-allowed-task: org.opends.server.tasks.EnterLockdownModeTask
 ds-cfg-allowed-task: org.opends.server.tasks.ExportTask
 ds-cfg-allowed-task: org.opends.server.tasks.ImportTask
-ds-cfg-allowed-task: org.opends.server.tasks.InitializeTargetTask
-ds-cfg-allowed-task: org.opends.server.tasks.InitializeTask
-ds-cfg-allowed-task: org.opends.server.tasks.SetGenerationIdTask
+ds-cfg-allowed-task: org.opends.server.replication.service.InitializeTargetTask
+ds-cfg-allowed-task: org.opends.server.replication.service.InitializeTask
+ds-cfg-allowed-task: org.opends.server.replication.service.SetGenerationIdTask
 ds-cfg-allowed-task: org.opends.server.tasks.LeaveLockdownModeTask
 ds-cfg-allowed-task: org.opends.server.tasks.RebuildTask
 ds-cfg-allowed-task: org.opends.server.tasks.RestoreTask
diff --git a/opends/src/messages/messages/replication.properties b/opends/src/messages/messages/replication.properties
index d405c94..b519ed2 100644
--- a/opends/src/messages/messages/replication.properties
+++ b/opends/src/messages/messages/replication.properties
@@ -109,8 +109,9 @@
  connection
 SEVERE_ERR_WRITER_UNEXPECTED_EXCEPTION_32=An unexpected error happened \
  handling connection with %s.  This connection is going to be closed
-SEVERE_ERR_CHANGELOG_ERROR_SENDING_ACK_33=An unexpected error occurred  while \
- sending an ack to %s.  This connection is going to be closed and reopened
+SEVERE_ERR_RS_ERROR_SENDING_ACK_33=In replication server %s: an unexpected error \
+ occurred while sending an ack to server id %s for change number %s in domain %s \
+ . This connection is going to be closed and reopened
 SEVERE_ERR_EXCEPTION_RECEIVING_REPLICATION_MESSAGE_34=An Exception was caught \
  while receiving replication message : %s
 MILD_ERR_LOOP_REPLAYING_OPERATION_35=A loop was detected while replaying \
@@ -269,7 +270,7 @@
 DEBUG_SENDING_CHANGE_112=Sending change number: %s
 DEBUG_CHANGES_SENT_113=All missing changes sent to replication server
 SEVERE_ERR_PUBLISHING_FAKE_OPS_114=Caught exception publishing fake operations \
-for domain %s to replication server %s : %s
+for domain %s : %s
 SEVERE_ERR_COMPUTING_FAKE_OPS_115=Caught exception computing fake operations \
 for domain %s for replication server %s : %s
 NOTICE_IGNORING_REMOTE_MONITOR_DATA_116=Some monitor data have been received \
@@ -346,3 +347,15 @@
 server %s will be aborted. Simultanate cross connection attempt ?
 NOTICE_BAD_GENERATION_ID_FROM_DS_146=On suffix %s, directory server %s presented \
  generation ID=%s when expected generation ID=%s
+SEVERE_ERR_DS_RECEIVED_ACK_ERROR_147=In replication service %s, the assured \
+ update message %s was acknowledged with the following errors: %s
+SEVERE_ERR_DS_ACK_TIMEOUT_148=In replication service %s, timeout after %s ms \
+ waiting for the acknowledgement of the assured update message: %s
+SEVERE_ERR_DS_UNKNOWN_ASSURED_MODE_149=In directory server %s, received unknown \
+ assured update mode: %s, for domain %s. Message: %s
+SEVERE_ERR_RS_UNKNOWN_ASSURED_MODE_150=In replication server %s, received unknown \
+ assured update mode: %s, for domain %s. Message: %s
+SEVERE_ERR_UNKNOWN_ASSURED_SAFE_DATA_LEVEL_151=In replication server %s, \
+ received a safe data assured update message with incoherent level: %s, this is \
+ for domain %s. Message: %s
+
diff --git a/opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java b/opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java
index 6b468d2..6dfe642 100644
--- a/opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java
+++ b/opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java
@@ -4204,7 +4204,9 @@
     oc.add("ds-task");
     oc.add("ds-task-initialize-from-remote-replica");
     attrs.put(oc);
-    attrs.put("ds-task-class-name", "org.opends.server.tasks.InitializeTask");
+    attrs.put(
+        "ds-task-class-name",
+        "org.opends.server.replication.service.InitializeTask");
     attrs.put("ds-task-initialize-domain-dn", suffixDn);
     attrs.put("ds-task-initialize-replica-server-id",
         String.valueOf(replicaId));
@@ -4521,7 +4523,7 @@
     oc.add("ds-task-reset-generation-id");
     attrs.put(oc);
     attrs.put("ds-task-class-name",
-        "org.opends.server.tasks.SetGenerationIdTask");
+        "org.opends.server.replication.service.SetGenerationIdTask");
     attrs.put("ds-task-reset-generation-id-domain-base-dn", suffixDn);
     while (!taskCreated)
     {
diff --git a/opends/src/server/org/opends/server/replication/common/ServerState.java b/opends/src/server/org/opends/server/replication/common/ServerState.java
index 81d2ac3..559ae24 100644
--- a/opends/src/server/org/opends/server/replication/common/ServerState.java
+++ b/opends/src/server/org/opends/server/replication/common/ServerState.java
@@ -49,6 +49,7 @@
 public class ServerState implements Iterable<Short>
 {
   private HashMap<Short, ChangeNumber> list;
+  private boolean saved = true;
 
   /**
    * Creates a new empty ServerState.
@@ -142,16 +143,18 @@
 
   /**
    * Update the Server State with a ChangeNumber.
-   * All operations with smaller CSN and the same serverID must be committed
-   * before calling this method.
-   * @param changeNumber the committed ChangeNumber.
-   * @return a boolean indicating if the update was meaningfull.
+   *
+   * @param changeNumber    The committed ChangeNumber.
+   *
+   * @return a boolean indicating if the update was meaningful.
    */
   public boolean update(ChangeNumber changeNumber)
   {
     if (changeNumber == null)
       return false;
 
+    saved = false;
+
     synchronized(this)
     {
       Short id =  changeNumber.getServerId();
@@ -395,4 +398,24 @@
 
      return diff;
   }
+
+  /**
+   * Set the saved status of this ServerState.
+   *
+   * @param b A booelan indicating if the State has been safely stored.
+   */
+  public void setSaved(boolean b)
+  {
+    saved = false;
+  }
+
+  /**
+   * Get the saved status of this ServerState.
+   *
+   * @return The saved status of this ServerState.
+   */
+  public boolean isSaved()
+  {
+    return saved;
+  }
 }
diff --git a/opends/src/server/org/opends/server/replication/common/ServerStatus.java b/opends/src/server/org/opends/server/replication/common/ServerStatus.java
index d46ab82..563c4b9 100644
--- a/opends/src/server/org/opends/server/replication/common/ServerStatus.java
+++ b/opends/src/server/org/opends/server/replication/common/ServerStatus.java
@@ -131,4 +131,31 @@
   {
     return value;
   }
+
+  /**
+   * Get a user readable string representing this status (User friendly string
+   * for monitoring purpose).
+   * @return A user readable string representing this status.
+   */
+  public String toString()
+  {
+    switch (value)
+    {
+      case -1:
+        return "Invalid";
+      case 0:
+        return "Not connected";
+      case 1:
+        return "Normal";
+      case 2:
+        return "Degraded";
+      case 3:
+        return "Full update";
+      case 4:
+        return "Bad generation id";
+      default:
+        throw new IllegalArgumentException("Wrong status numeric value: " +
+          value);
+    }
+  }
 }
diff --git a/opends/src/server/org/opends/server/replication/plugin/FakeAddOperation.java b/opends/src/server/org/opends/server/replication/plugin/FakeAddOperation.java
index 9a99c4a..65dd36b 100644
--- a/opends/src/server/org/opends/server/replication/plugin/FakeAddOperation.java
+++ b/opends/src/server/org/opends/server/replication/plugin/FakeAddOperation.java
@@ -62,7 +62,7 @@
   {
     return new AddMsg(getChangeNumber(), entry.getDN().toString(),
                Historical.getEntryUuid(entry),
-               ReplicationDomain.findEntryId(
+               LDAPReplicationDomain.findEntryId(
                    entry.getDN().getParentDNInSuffix()),
                entry.getObjectClasses(),
                entry.getUserAttributes(), entry.getOperationalAttributes());
diff --git a/opends/src/server/org/opends/server/replication/plugin/FakeModdnOperation.java b/opends/src/server/org/opends/server/replication/plugin/FakeModdnOperation.java
index 15fd6bc..b750cea 100644
--- a/opends/src/server/org/opends/server/replication/plugin/FakeModdnOperation.java
+++ b/opends/src/server/org/opends/server/replication/plugin/FakeModdnOperation.java
@@ -66,7 +66,7 @@
     DN dn = entry.getDN();
     return new ModifyDNMsg(dn.toString(), this.getChangeNumber(),
         Historical.getEntryUuid(entry),
-        ReplicationDomain.findEntryId(dn.getParent()),
+        LDAPReplicationDomain.findEntryId(dn.getParent()),
         false, dn.getParent().toString(), dn.getRDN().toString());
   }
 }
diff --git a/opends/src/server/org/opends/server/replication/plugin/ReplicationDomain.java b/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
similarity index 65%
rename from opends/src/server/org/opends/server/replication/plugin/ReplicationDomain.java
rename to opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
index ed583c6..87cb100 100644
--- a/opends/src/server/org/opends/server/replication/plugin/ReplicationDomain.java
+++ b/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
@@ -25,6 +25,7 @@
  *      Copyright 2006-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.replication.plugin;
+
 import static org.opends.messages.ReplicationMessages.*;
 import static org.opends.messages.ToolMessages.*;
 import static org.opends.server.loggers.ErrorLogger.logError;
@@ -36,28 +37,31 @@
 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.replication.common.StatusMachine.*;
+
+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.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
-import java.net.SocketTimeoutException;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.NoSuchElementException;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeMap;
+import java.util.Set;
+import java.util.TreeSet;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.zip.CheckedOutputStream;
 import java.util.zip.DataFormatException;
-
 import org.opends.messages.Message;
 import org.opends.messages.MessageBuilder;
 import org.opends.server.admin.server.ConfigurationChangeListener;
@@ -68,7 +72,6 @@
 import org.opends.server.api.DirectoryThread;
 import org.opends.server.api.SynchronizationProvider;
 import org.opends.server.backends.jeb.BackendImpl;
-import org.opends.server.backends.task.Task;
 import org.opends.server.config.ConfigException;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.DeleteOperation;
@@ -82,49 +85,31 @@
 import org.opends.server.protocols.asn1.ASN1Exception;
 import org.opends.server.protocols.asn1.ASN1OctetString;
 import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchListener;
 import org.opends.server.protocols.internal.InternalSearchOperation;
 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;
-import org.opends.server.replication.common.DSInfo;
-import org.opends.server.replication.common.RSInfo;
 import org.opends.server.replication.common.ServerState;
 import org.opends.server.replication.common.ServerStatus;
-import org.opends.server.replication.common.StatusMachine;
-import org.opends.server.replication.common.StatusMachineEvent;
-import org.opends.server.replication.protocol.AckMsg;
 import org.opends.server.replication.protocol.AddContext;
 import org.opends.server.replication.protocol.AddMsg;
-import org.opends.server.replication.protocol.ChangeStatusMsg;
 import org.opends.server.replication.protocol.DeleteContext;
-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.HeartbeatMsg;
-import org.opends.server.replication.protocol.InitializeRequestMsg;
-import org.opends.server.replication.protocol.InitializeTargetMsg;
 import org.opends.server.replication.protocol.ModifyContext;
 import org.opends.server.replication.protocol.ModifyDNMsg;
 import org.opends.server.replication.protocol.ModifyDnContext;
 import org.opends.server.replication.protocol.ModifyMsg;
 import org.opends.server.replication.protocol.OperationContext;
-import org.opends.server.replication.protocol.ReplSessionSecurity;
-import org.opends.server.replication.protocol.ReplicationMsg;
-import org.opends.server.replication.protocol.ResetGenerationIdMsg;
-import org.opends.server.replication.protocol.RoutableMsg;
-import org.opends.server.replication.protocol.TopologyMsg;
+import org.opends.server.replication.protocol.ProtocolSession;
 import org.opends.server.replication.protocol.UpdateMsg;
-import org.opends.server.tasks.InitializeTargetTask;
-import org.opends.server.tasks.InitializeTask;
 import org.opends.server.tasks.TaskUtils;
-import org.opends.server.types.AttributeBuilder;
-import org.opends.server.types.Attributes;
-import org.opends.server.types.ExistingFileBehavior;
 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.ConfigChangeResult;
@@ -133,6 +118,7 @@
 import org.opends.server.types.DereferencePolicy;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
+import org.opends.server.types.ExistingFileBehavior;
 import org.opends.server.types.LDAPException;
 import org.opends.server.types.LDIFExportConfig;
 import org.opends.server.types.LDIFImportConfig;
@@ -144,6 +130,7 @@
 import org.opends.server.types.ResultCode;
 import org.opends.server.types.SearchFilter;
 import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchResultReference;
 import org.opends.server.types.SearchScope;
 import org.opends.server.types.SynchronizationProviderResult;
 import org.opends.server.types.operation.PluginOperation;
@@ -163,9 +150,9 @@
  *  handle conflict resolution,
  *  handle protocol messages from the replicationServer.
  */
-public class ReplicationDomain extends DirectoryThread
+public class LDAPReplicationDomain extends ReplicationDomain
        implements ConfigurationChangeListener<ReplicationDomainCfg>,
-                  AlertGenerator
+                  AlertGenerator, InternalSearchListener
 {
   /**
    * The fully-qualified name of this class.
@@ -185,37 +172,20 @@
    */
   private static final DebugTracer TRACER = getTracer();
 
-  private ReplicationMonitor monitor;
-
-  private ReplicationBroker broker;
-  // Thread waiting for incoming update messages for this domain and pushing
-  // them to the global incoming update message queue for later processing by
-  // replay threads.
-  private ListenerThread listenerThread;
   // The update to replay message queue where the listener thread is going to
   // push incoming update messages.
   private LinkedBlockingQueue<UpdateToReplay> updateToReplayQueue;
-  private SortedMap<ChangeNumber, UpdateMsg> waitingAckMsgs =
-    new TreeMap<ChangeNumber, UpdateMsg>();
-  private AtomicInteger numRcvdUpdates = new AtomicInteger(0);
-  private AtomicInteger numSentUpdates = new AtomicInteger(0);
-  private AtomicInteger numProcessedUpdates = new AtomicInteger();
   private AtomicInteger numResolvedNamingConflicts = new AtomicInteger();
   private AtomicInteger numResolvedModifyConflicts = new AtomicInteger();
   private AtomicInteger numUnresolvedNamingConflicts = new AtomicInteger();
   private int debugCount = 0;
-  private PersistentServerState state;
+  private final PersistentServerState state;
   private int numReplayedPostOpCalled = 0;
 
-  private int maxReceiveQueue = 0;
-  private int maxSendQueue = 0;
-  private int maxReceiveDelay = 0;
-  private int maxSendDelay = 0;
-
   private long generationId = -1;
   private boolean generationIdSavedStatus = false;
 
-  ChangeNumberGenerator generator;
+  private ChangeNumberGenerator generator;
 
   /**
    * This object is used to store the list of update currently being
@@ -235,19 +205,8 @@
    */
   private RemotePendingChanges remotePendingChanges;
 
-  /**
-   * The time in milliseconds between heartbeats from the replication
-   * server.  Zero means heartbeats are off.
-   */
-  private long heartbeatInterval = 0;
   private short serverId;
 
-  // The context related to an import or export being processed
-  // Null when none is being processed.
-  private IEContext ieContext = null;
-
-  private Collection<String> replicationServers;
-
   private DN baseDn;
 
   private boolean shutdown = false;
@@ -260,37 +219,10 @@
   private boolean disabled = false;
   private boolean stateSavingDisabled = false;
 
-  private int window = 100;
-
-  /*
-   * Assured mode properties
-   */
-  // Is assured mode enabled or not for this domain ?
-  private boolean assured = false;
-  // Assured sub mode (used when assured is true)
-  private AssuredMode assuredMode = AssuredMode.SAFE_DATA_MODE;
-  // Safe Data level (used when assuredMode is SAFE_DATA)
-  private byte assuredSdLevel = (byte)1;
-  // Timeout (in milliseconds) when waiting for acknowledgments
-  private long assuredTimeout = 1000;
-
-  // Group id
-  private byte groupId = (byte)1;
-  // Referrals urls to be published to other servers of the topology
-  // TODO: fill that with all currently opened urls if no urls configured
-  private List<String> refUrls = new ArrayList<String>();
-
-  // Current status for this replicated domain
-  private ServerStatus status = ServerStatus.NOT_CONNECTED_STATUS;
-
-  /*
-   * Properties for the last topology info received from the network.
-   */
-  // Info for other DSs.
-  // Warning: does not contain info for us (for our server id)
-  private List<DSInfo> dsList = new ArrayList<DSInfo>();
-  // Info for other RSs.
-  private List<RSInfo> rsList = new ArrayList<RSInfo>();
+  // This list is used to temporary store operations that needs
+  // to be replayed at session establishment time.
+  private TreeSet<FakeOperation> replayOperations  =
+    new TreeSet<FakeOperation>(new FakeOperationComparator());;
 
   /**
    * The isolation policy that this domain is going to use.
@@ -312,131 +244,47 @@
    */
   private boolean done = true;
 
+  private ServerStateFlush flushThread;
+
   /**
-   * This class contain the context related to an import or export
-   * launched on the domain.
+   * The thread that periodically saves the ServerState of this
+   * LDAPReplicationDomain in the database.
    */
-  private class IEContext
+  private class  ServerStateFlush extends DirectoryThread
   {
-    // The task that initiated the operation.
-    Task initializeTask;
-    // The input stream for the import
-    ReplLDIFInputStream ldifImportInputStream = null;
-    // The target in the case of an export
-    short exportTarget = RoutableMsg.UNKNOWN_SERVER;
-    // The source in the case of an import
-    short importSource = RoutableMsg.UNKNOWN_SERVER;
-
-    // The total entry count expected to be processed
-    long entryCount = 0;
-    // The count for the entry not yet processed
-    long entryLeftCount = 0;
-
-    // The exception raised when any
-    DirectoryException exception = null;
-
-    /**
-     * Initializes the import/export counters with the provider value.
-     * @param total
-     * @param left
-     * @throws DirectoryException
-     */
-    public void setCounters(long total, long left)
-      throws DirectoryException
+    protected ServerStateFlush()
     {
-      entryCount = total;
-      entryLeftCount = left;
-
-      if (initializeTask != null)
-      {
-        if (initializeTask instanceof InitializeTask)
-        {
-          ((InitializeTask)initializeTask).setTotal(entryCount);
-          ((InitializeTask)initializeTask).setLeft(entryCount);
-        }
-        else if (initializeTask instanceof InitializeTargetTask)
-        {
-          ((InitializeTargetTask)initializeTask).setTotal(entryCount);
-          ((InitializeTargetTask)initializeTask).setLeft(entryCount);
-        }
-      }
-    }
-
-    /**
-     * Update the counters of the task for each entry processed during
-     * an import or export.
-     * @throws DirectoryException
-     */
-    public void updateCounters()
-      throws DirectoryException
-    {
-      entryLeftCount--;
-
-      if (initializeTask != null)
-      {
-        if (initializeTask instanceof InitializeTask)
-        {
-          ((InitializeTask)initializeTask).setLeft(entryLeftCount);
-        }
-        else if (initializeTask instanceof InitializeTargetTask)
-        {
-          ((InitializeTargetTask)initializeTask).setLeft(entryLeftCount);
-        }
-      }
+      super("Replication State Saver for server id " +
+            serverId + " and domain " + baseDn.toString());
     }
 
     /**
      * {@inheritDoc}
      */
-    public String toString()
-    {
-      return new String("[ Entry count=" + this.entryCount +
-                        ", Entry left count=" + this.entryLeftCount + "]");
-    }
-  }
-
-  /**
-   * This thread is launched when we want to export data to another server that
-   * has requested to be initialized with the data of our backend.
-   */
-  private class ExportThread extends DirectoryThread
-  {
-    // Id of server that will receive updates
-    private short target;
-
-    /**
-     * Constructor for the ExportThread.
-     *
-     * @param target Id of server that will receive updates
-     */
-    public ExportThread(short target)
-    {
-      super("Export thread " + serverId);
-      this.target = target;
-    }
-
-    /**
-     * Run method for this class.
-     */
+    @Override
     public void run()
     {
-      if (debugEnabled())
-      {
-        TRACER.debugInfo("Export thread starting.");
-      }
+      done = false;
 
-      try
+      while (shutdown  == false)
       {
-        initializeRemote(target, target, null);
-      } catch (DirectoryException de)
-      {
-      // An error message has been sent to the peer
-      // Nothing more to do locally
+        try
+        {
+          synchronized (this)
+          {
+            this.wait(1000);
+            if (!disabled && !stateSavingDisabled )
+            {
+              // save the ServerState
+              state.save();
+            }
+          }
+        } catch (InterruptedException e)
+        { }
       }
-      if (debugEnabled())
-      {
-        TRACER.debugInfo("Export thread stopping.");
-      }
+      state.save();
+
+      done = true;
     }
   }
 
@@ -447,18 +295,24 @@
    * @param updateToReplayQueue The queue for update messages to replay.
    * @throws ConfigException In case of invalid configuration.
    */
-  public ReplicationDomain(ReplicationDomainCfg configuration,
+  public LDAPReplicationDomain(ReplicationDomainCfg configuration,
     LinkedBlockingQueue<UpdateToReplay> updateToReplayQueue)
     throws ConfigException
   {
-    super("Replication State Saver for server id " + configuration.getServerId()
-      + " and domain " + configuration.getBaseDN());
+    super(configuration.getBaseDN().toNormalizedString(),
+        (short) configuration.getServerId());
+
+    /**
+     * The time in milliseconds between heartbeats from the replication
+     * server.  Zero means heartbeats are off.
+     */
+    long heartbeatInterval = 0;
 
     // Read the configuration parameters.
-    replicationServers = configuration.getReplicationServer();
+    Set<String> replicationServers = configuration.getReplicationServer();
     serverId = (short) configuration.getServerId();
     baseDn = configuration.getBaseDN();
-    window  = configuration.getWindowSize();
+    int window  = configuration.getWindowSize();
     heartbeatInterval = configuration.getHeartbeatInterval();
     isolationpolicy = configuration.getIsolationPolicy();
     configDn = configuration.dn();
@@ -471,28 +325,22 @@
     switch (assuredType)
     {
       case NOT_ASSURED:
-        assured = false;
+        setAssured(false);
         break;
       case SAFE_DATA:
-        assured = true;
-        this.assuredMode = AssuredMode.SAFE_DATA_MODE;
+        setAssured(true);
+        setAssuredMode(AssuredMode.SAFE_DATA_MODE);
         break;
       case SAFE_READ:
-        assured = true;
-        this.assuredMode = AssuredMode.SAFE_READ_MODE;
+        setAssured(true);
+        setAssuredMode(AssuredMode.SAFE_READ_MODE);
         break;
     }
-    this.assuredSdLevel = (byte)configuration.getAssuredSdLevel();
-    this.groupId = (byte)configuration.getGroupId();
-    this.assuredTimeout = configuration.getAssuredTimeout();
-    SortedSet<String> urls = configuration.getReferralsUrl();
-    if (urls != null)
-    {
-      for (String url : urls)
-      {
-        this.refUrls.add(url);
-      }
-    }
+    setAssuredSdLevel((byte)configuration.getAssuredSdLevel());
+    setAssuredTimeout(configuration.getAssuredTimeout());
+
+    setGroupId((byte)configuration.getGroupId());
+    setURLs(configuration.getReferralsUrl());
 
     /*
      * Modify conflicts are solved for all suffixes but the schema suffix
@@ -510,19 +358,6 @@
       solveConflictFlag = true;
     }
 
-    /*
-     * Create a new Persistent Server State that will be used to store
-     * the last ChangeNmber seen from all LDAP servers in the topology.
-     */
-    state = new PersistentServerState(baseDn, serverId);
-
-    /*
-     * Create a replication monitor object responsible for publishing
-     * monitoring information below cn=monitor.
-     */
-    monitor = new ReplicationMonitor(this);
-    DirectoryServer.registerMonitorProvider(monitor);
-
     Backend backend = retrievesBackend(baseDn);
     if (backend == null)
     {
@@ -541,14 +376,12 @@
     }
 
     /*
-     * create the broker object used to publish and receive changes
+     * Create a new Persistent Server State that will be used to store
+     * the last ChangeNmber seen from all LDAP servers in the topology.
      */
-    broker = new ReplicationBroker(this, state, baseDn, serverId,
-        maxReceiveQueue, maxReceiveDelay, maxSendQueue, maxSendDelay, window,
-        heartbeatInterval, generationId,
-        new ReplSessionSecurity(configuration),getGroupId());
+    state = new PersistentServerState(baseDn, serverId, getServerState());
 
-    broker.start(replicationServers);
+    startPublishService(replicationServers, window, heartbeatInterval);
 
     /*
      * ChangeNumberGenerator is used to create new unique ChangeNumbers
@@ -557,14 +390,12 @@
      * The generator time is adjusted to the time of the last CN received from
      * remote other servers.
      */
-    generator =
-      new ChangeNumberGenerator(serverId, state);
+    generator = getGenerator();
 
     pendingChanges =
-      new PendingChanges(generator,
-                         broker, state);
+      new PendingChanges(generator, this);
 
-    remotePendingChanges = new RemotePendingChanges(generator, state);
+    remotePendingChanges = new RemotePendingChanges(getServerState());
 
     // listen for changes on the configuration
     configuration.addChangeListener(this);
@@ -573,7 +404,6 @@
     DirectoryServer.registerAlertGenerator(this);
   }
 
-
   /**
    * Returns the base DN of this ReplicationDomain.
    *
@@ -741,7 +571,7 @@
     {
       // this isolation policy specifies that the updates are denied
       // when the broker is not connected.
-      return broker.isConnected();
+      return isConnected();
     }
     // we should never get there as the only possible policies are
     // ACCEPT_ALL_UPDATES and REJECT_ALL_UPDATES
@@ -931,322 +761,6 @@
   }
 
   /**
-   * Receives an update message from the replicationServer.
-   * also responsible for updating the list of pending changes
-   * @return the received message - null if none
-   */
-  public UpdateMsg receive()
-  {
-    UpdateMsg update = null;
-
-    while ( (update == null) && (!shutdown) )
-    {
-      InitializeRequestMsg initMsg = null;
-      ReplicationMsg msg;
-      try
-      {
-        msg = broker.receive();
-        if (msg == null)
-        {
-          // The server is in the shutdown process
-          return null;
-        }
-
-        if (debugEnabled())
-          if (!(msg instanceof HeartbeatMsg))
-            TRACER.debugVerbose("Message received <" + msg + ">");
-
-        if (msg instanceof AckMsg)
-        {
-          AckMsg ack = (AckMsg) msg;
-          receiveAck(ack);
-        }
-        else if (msg instanceof InitializeRequestMsg)
-        {
-          // Another server requests us to provide entries
-          // for a total update
-          initMsg = (InitializeRequestMsg)msg;
-        }
-        else if (msg instanceof InitializeTargetMsg)
-        {
-          // Another server is exporting its entries to us
-          InitializeTargetMsg importMsg = (InitializeTargetMsg) msg;
-
-          try
-          {
-            // This must be done while we are still holding the
-            // broker lock because we are now going to receive a
-            // bunch of entries from the remote server and we
-            // want the import thread to catch them and
-            // not the ListenerThread.
-            initialize(importMsg);
-          }
-          catch(DirectoryException de)
-          {
-            // Returns an error message to notify the sender
-            ErrorMsg errorMsg =
-              new ErrorMsg(importMsg.getsenderID(),
-                  de.getMessageObject());
-            MessageBuilder mb = new MessageBuilder();
-            mb.append(de.getMessageObject());
-            TRACER.debugInfo(Message.toString(mb.toMessage()));
-            broker.publish(errorMsg);
-          }
-        }
-        else if (msg instanceof ErrorMsg)
-        {
-          if (ieContext != null)
-          {
-            // This is an error termination for the 2 following cases :
-            // - either during an export
-            // - or before an import really started
-            //   For example, when we publish a request and the
-            //  replicationServer did not find any import source.
-            abandonImportExport((ErrorMsg)msg);
-          }
-          else
-          {
-            /*
-             * Log error message
-             */
-            ErrorMsg errorMsg = (ErrorMsg)msg;
-            logError(ERR_ERROR_MSG_RECEIVED.get(
-                errorMsg.getDetails()));
-          }
-        }
-        if (msg instanceof TopologyMsg)
-        {
-          TopologyMsg topoMsg = (TopologyMsg)msg;
-          receiveTopo(topoMsg);
-        }
-        if (msg instanceof ChangeStatusMsg)
-        {
-          ChangeStatusMsg csMsg = (ChangeStatusMsg)msg;
-          receiveChangeStatus(csMsg);
-        }
-        else if (msg instanceof UpdateMsg)
-        {
-          update = (UpdateMsg) msg;
-          receiveUpdate(update);
-        }
-      }
-      catch (SocketTimeoutException e)
-      {
-        // just retry
-      }
-      // Test if we have received and export request message and
-      // if that's the case handle it now.
-      // This must be done outside of the portion of code protected
-      // by the broker lock so that we keep receiveing update
-      // when we are doing and export and so that a possible
-      // closure of the socket happening when we are publishing the
-      // entries to the remote can be handled by the other
-      // replay thread when they call this method and therefore the
-      // broker.receive() method.
-      if (initMsg != null)
-      {
-        // Do this work in a thread to allow replay thread continue working
-        ExportThread exportThread = new ExportThread(initMsg.getsenderID());
-        exportThread.start();
-      }
-    }
-    return update;
-  }
-
-  /**
-   * Processes an incoming TopologyMsg.
-   * Updates the structures for the local view of the topology.
-   *
-   * @param topoMsg The topology information received from RS.
-   */
-  public void receiveTopo(TopologyMsg topoMsg)
-  {
-
-    if (debugEnabled())
-      TRACER.debugInfo("Replication domain " + baseDn
-        + " received topology info update:\n" + topoMsg);
-
-    // Store new lists
-    synchronized(getDsList())
-    {
-      synchronized(getRsList())
-      {
-        dsList = topoMsg.getDsList();
-        rsList = topoMsg.getRsList();
-      }
-    }
-  }
-
-  /**
-   * Set the initial status of the domain, once he is connected to the topology.
-   * @param initStatus The status to enter the state machine with
-   */
-  public void setInitialStatus(ServerStatus initStatus)
-  {
-    // Sanity check: is it a valid initial status?
-    if (!isValidInitialStatus(initStatus))
-    {
-      Message msg = ERR_DS_INVALID_INIT_STATUS.get(initStatus.toString(),
-        baseDn.toString(), Short.toString(serverId));
-      logError(msg);
-    } else
-    {
-      status = initStatus;
-    }
-  }
-
-  /**
-   * Processes an incoming ChangeStatusMsg. Compute new status according to
-   * given order. Then update domain for being compliant with new status
-   * definition.
-   * @param csMsg The received status message
-   */
-  private void receiveChangeStatus(ChangeStatusMsg csMsg)
-  {
-    if (debugEnabled())
-      TRACER.debugInfo("Replication domain " + baseDn +
-        " received change status message:\n" + csMsg);
-
-    ServerStatus reqStatus = csMsg.getRequestedStatus();
-
-    // Translate requested status to a state machine event
-    StatusMachineEvent event = StatusMachineEvent.statusToEvent(reqStatus);
-    if (event == StatusMachineEvent.INVALID_EVENT)
-    {
-      Message msg = ERR_DS_INVALID_REQUESTED_STATUS.get(reqStatus.toString(),
-        baseDn.toString(), Short.toString(serverId));
-      logError(msg);
-      return;
-    }
-
-    // Compute new status and do matching tasks
-    // Use synchronized as admin task (thread) could order to go in admin status
-    // for instance (concurrent with receive thread).
-    synchronized (status)
-    {
-      ServerStatus newStatus =
-        StatusMachine.computeNewStatus(status, event);
-
-      if (newStatus == ServerStatus.INVALID_STATUS)
-      {
-        Message msg = ERR_DS_CANNOT_CHANGE_STATUS.get(baseDn.toString(),
-          Short.toString(serverId), status.toString(), event.toString());
-        logError(msg);
-        return;
-      }
-
-      // Store new status
-      status = newStatus;
-
-      if (debugEnabled())
-      TRACER.debugInfo("Replication domain " + baseDn +
-        " new status is: " + status);
-
-      // Perform whatever actions are needed to apply properties for being
-      // compliant with new status
-      updateDomainForNewStatus();
-    }
-  }
-
-  /**
-   * Called when first connection or disconnection detected.
-   */
-  public void toNotConnectedStatus()
-  {
-    // Go into not connected status
-    // Use synchronized as somebody could ask another status change at the same
-    // time
-    synchronized (status)
-    {
-      StatusMachineEvent event =
-        StatusMachineEvent.TO_NOT_CONNECTED_STATUS_EVENT;
-      ServerStatus newStatus =
-        StatusMachine.computeNewStatus(status, event);
-
-      if (newStatus == ServerStatus.INVALID_STATUS)
-      {
-        Message msg = ERR_DS_CANNOT_CHANGE_STATUS.get(baseDn.toString(),
-          Short.toString(serverId), status.toString(), event.toString());
-        logError(msg);
-        return;
-      }
-
-      // Store new status
-      status = newStatus;
-
-      if (debugEnabled())
-        TRACER.debugInfo("Replication domain " + baseDn +
-          " new status is: " + status);
-
-      // Perform whatever actions are needed to apply properties for being
-      // compliant with new status
-      updateDomainForNewStatus();
-    }
-  }
-
-  /**
-   * Perform whatever actions are needed to apply properties for being
-   * compliant with new status. Must be called in synchronized section for
-   * status. The new status is already set in status variable.
-   */
-  private void updateDomainForNewStatus()
-  {
-    switch (status)
-    {
-      case NOT_CONNECTED_STATUS:
-        break;
-      case NORMAL_STATUS:
-        break;
-      case DEGRADED_STATUS:
-        break;
-      case FULL_UPDATE_STATUS:
-        // Signal RS we just entered the full update status
-        broker.signalStatusChange(status);
-        break;
-      case BAD_GEN_ID_STATUS:
-        break;
-      default:
-        if (debugEnabled())
-          TRACER.debugInfo("updateDomainForNewStatus: unexpected status: " +
-            status);
-    }
-  }
-
-  /**
-   * Do the necessary processing when an UpdateMsg was received.
-   *
-   * @param update The received UpdateMsg.
-   */
-  public void receiveUpdate(UpdateMsg update)
-  {
-    remotePendingChanges.putRemoteUpdate(update);
-    numRcvdUpdates.incrementAndGet();
-  }
-
-  /**
-   * Do the necessary processing when an AckMsg is received.
-   *
-   * @param ack The AckMsg that was received.
-   */
-  public void receiveAck(AckMsg ack)
-  {
-    UpdateMsg update;
-    ChangeNumber changeNumber = ack.getChangeNumber();
-
-    synchronized (waitingAckMsgs)
-    {
-      update = waitingAckMsgs.remove(changeNumber);
-    }
-    if (update != null)
-    {
-      synchronized (update)
-      {
-        update.notify();
-      }
-    }
-  }
-
-  /**
    * Check if an operation must be synchronized.
    * Also update the list of pending changes and the server RUV
    * @param op the operation
@@ -1258,19 +772,17 @@
     {
       numReplayedPostOpCalled++;
     }
-    UpdateMsg msg = null;
+    LDAPUpdateMsg msg = null;
 
     // Note that a failed non-replication operation might not have a change
     // number.
     ChangeNumber curChangeNumber = OperationContext.getChangeNumber(op);
 
-    boolean isAssured = isAssured(op);
-
     if ((result == ResultCode.SUCCESS) && (!op.isSynchronizationOperation()))
     {
       // Generate a replication message for a successful non-replication
       // operation.
-      msg = UpdateMsg.generateMsg(op);
+      msg = LDAPUpdateMsg.generateMsg(op);
 
       if (msg == null)
       {
@@ -1307,16 +819,6 @@
         return;
       }
 
-      if (msg != null && isAssured)
-      {
-        synchronized (waitingAckMsgs)
-        {
-          // Add the assured message to the list of update that are
-          // waiting acknowledgements
-          waitingAckMsgs.put(curChangeNumber, msg);
-        }
-      }
-
       if (generationIdSavedStatus != true)
       {
         this.saveGenerationId(generationId);
@@ -1334,53 +836,8 @@
 
     if (!op.isSynchronizationOperation())
     {
-      int pushedChanges = pendingChanges.pushCommittedChanges();
-      numSentUpdates.addAndGet(pushedChanges);
+      pendingChanges.pushCommittedChanges();
     }
-
-    // Wait for acknowledgement of an assured message.
-    if (msg != null && isAssured)
-    {
-      synchronized (msg)
-      {
-        while (waitingAckMsgs.containsKey(msg.getChangeNumber()))
-        {
-          // TODO : should have a configurable timeout to get
-          // out of this loop
-          try
-          {
-            msg.wait(1000);
-          } catch (InterruptedException e)
-          { }
-        }
-      }
-    }
-  }
-
-  /**
-   * get the number of updates received by the replication plugin.
-   *
-   * @return the number of updates received
-   */
-  public int getNumRcvdUpdates()
-  {
-    if (numRcvdUpdates != null)
-      return numRcvdUpdates.get();
-    else
-      return 0;
-  }
-
-  /**
-   * Get the number of updates sent by the replication plugin.
-   *
-   * @return the number of updates sent
-   */
-  public int getNumSentUpdates()
-  {
-    if (numSentUpdates != null)
-      return numSentUpdates.get();
-    else
-      return 0;
   }
 
   /**
@@ -1397,27 +854,6 @@
   }
 
   /**
-   * Increment the number of processed updates.
-   */
-  public void incProcessedUpdates()
-  {
-    numProcessedUpdates.incrementAndGet();
-  }
-
-  /**
-   * get the number of updates replayed by the replication.
-   *
-   * @return The number of updates replayed by the replication
-   */
-  public int getNumProcessedUpdates()
-  {
-    if (numProcessedUpdates != null)
-      return numProcessedUpdates.get();
-    else
-      return 0;
-  }
-
-  /**
    * get the number of updates replayed successfully by the replication.
    *
    * @return The number of updates replayed successfully
@@ -1428,16 +864,6 @@
   }
 
   /**
-   * get the ServerState.
-   *
-   * @return the ServerState
-   */
-  public ServerState getServerState()
-  {
-    return state;
-  }
-
-  /**
    * Get the debugCount.
    *
    * @return Returns the debugCount.
@@ -1448,49 +874,6 @@
   }
 
   /**
-   * Send an Ack message.
-   *
-   * @param changeNumber The ChangeNumber for which the ack must be sent.
-   */
-  public void ack(ChangeNumber changeNumber)
-  {
-    broker.publish(new AckMsg(changeNumber));
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public void run()
-  {
-    done = false;
-
-    // Create the listener thread
-    listenerThread = new ListenerThread(this, updateToReplayQueue);
-    listenerThread.start();
-
-    while (shutdown  == false)
-    {
-      try
-      {
-        synchronized (this)
-        {
-          this.wait(1000);
-          if (!disabled && !stateSavingDisabled )
-          {
-            // save the RUV
-            state.save();
-          }
-        }
-      } catch (InterruptedException e)
-      { }
-    }
-    state.save();
-
-    done = true;
-  }
-
-  /**
    * Shutdown this ReplicationDomain.
    */
   public void shutdown()
@@ -1498,27 +881,19 @@
     // stop the flush thread
     shutdown = true;
 
-    // Stop the listener thread
-    if (listenerThread != null)
+    // stop the thread in charge of flushing the ServerState.
+    if (flushThread != null)
     {
-      listenerThread.shutdown();
+      synchronized (flushThread)
+      {
+        flushThread.notify();
+      }
     }
 
-    synchronized (this)
-    {
-      this.notify();
-    }
-
-    DirectoryServer.deregisterMonitorProvider(monitor.getMonitorInstanceName());
-
     DirectoryServer.deregisterAlertGenerator(this);
 
-    // stop the ReplicationBroker
-    broker.stop();
-
-    // Wait for the listener thread to stop
-    if (listenerThread != null)
-      listenerThread.waitForShutdown();
+    // stop the ReplicationDomain
+    stopDomain();
 
     // wait for completion of the persistentServerState thread.
     try
@@ -1534,26 +909,11 @@
   }
 
   /**
-   * Get the name of the replicationServer to which this domain is currently
-   * connected.
-   *
-   * @return the name of the replicationServer to which this domain
-   *         is currently connected.
-   */
-  public String getReplicationServer()
-  {
-    if (broker != null)
-      return broker.getReplicationServer();
-    else
-      return "Not connected";
-  }
-
-  /**
    * Create and replay a synchronized Operation from an UpdateMsg.
    *
    * @param msg The UpdateMsg to be replayed.
    */
-  public void replay(UpdateMsg msg)
+  public void replay(LDAPUpdateMsg msg)
   {
     Operation op = null;
     boolean done = false;
@@ -1565,6 +925,7 @@
     // whose dependency has been replayed until no more left.
     do
     {
+      String replayErrorMsg = null;
       try
       {
         op = msg.createOperation(conn);
@@ -1572,12 +933,12 @@
 
         while ((!dependency) && (!done) && (retryCount-- > 0))
         {
+          // Try replay the operation
           op.setInternalOperation(true);
           op.setSynchronizationOperation(true);
           changeNumber = OperationContext.getChangeNumber(op);
           ((AbstractOperation) op).run();
 
-          // Try replay the operation
           ResultCode result = op.getResultCode();
 
           if (result != ResultCode.SUCCESS)
@@ -1638,7 +999,7 @@
             op.getErrorMessage().toString());
           logError(message);
           numUnresolvedNamingConflicts.incrementAndGet();
-
+          replayErrorMsg = message.toString();
           updateError(changeNumber);
         }
       } catch (ASN1Exception e)
@@ -1646,16 +1007,19 @@
         Message message = ERR_EXCEPTION_DECODING_OPERATION.get(
           String.valueOf(msg) + stackTraceToSingleLineString(e));
         logError(message);
+        replayErrorMsg = message.toString();
       } catch (LDAPException e)
       {
         Message message = ERR_EXCEPTION_DECODING_OPERATION.get(
           String.valueOf(msg) + stackTraceToSingleLineString(e));
         logError(message);
+        replayErrorMsg = message.toString();
       } catch (DataFormatException e)
       {
         Message message = ERR_EXCEPTION_DECODING_OPERATION.get(
           String.valueOf(msg) + stackTraceToSingleLineString(e));
         logError(message);
+        replayErrorMsg = message.toString();
       } catch (Exception e)
       {
         if (changeNumber != null)
@@ -1669,21 +1033,20 @@
           Message message = ERR_EXCEPTION_REPLAYING_OPERATION.get(
             stackTraceToSingleLineString(e), op.toString());
           logError(message);
+          replayErrorMsg = message.toString();
           updateError(changeNumber);
         } else
         {
           Message message = ERR_EXCEPTION_DECODING_OPERATION.get(
             String.valueOf(msg) + stackTraceToSingleLineString(e));
           logError(message);
+          replayErrorMsg = message.toString();
         }
       } finally
       {
         if (!dependency)
         {
-          broker.updateWindowAfterReplay();
-          if (msg.isAssured())
-            ack(msg.getChangeNumber());
-          incProcessedUpdates();
+          processUpdateDone(msg, replayErrorMsg);
         }
       }
 
@@ -1913,7 +1276,7 @@
   * @return true if the process is completed, false if it must continue..
   */
  private boolean solveNamingConflict(DeleteOperation op,
-     UpdateMsg msg)
+     LDAPUpdateMsg msg)
  {
    ResultCode result = op.getResultCode();
    DeleteContext ctx = (DeleteContext) op.getAttachment(SYNCHROCONTEXT);
@@ -1983,7 +1346,7 @@
  * @throws Exception When the operation is not valid.
  */
 private boolean solveNamingConflict(ModifyDNOperation op,
-    UpdateMsg msg) throws Exception
+    LDAPUpdateMsg msg) throws Exception
 {
   ResultCode result = op.getResultCode();
   ModifyDnContext ctx = (ModifyDnContext) op.getAttachment(SYNCHROCONTEXT);
@@ -2391,83 +1754,6 @@
   }
 
   /**
-   * Check if an operation must be processed as an assured operation.
-   *
-   * @param op the operation to be checked.
-   * @return true if the operations must be processed as an assured operation.
-   */
-  private boolean isAssured(PostOperationOperation op)
-  {
-    // TODO : should have a filtering mechanism for checking
-    // operation that are assured and operations that are not.
-    return false;
-  }
-
-  /**
-   * Get the maximum receive window size.
-   *
-   * @return The maximum receive window size.
-   */
-  public int getMaxRcvWindow()
-  {
-    if (broker != null)
-      return broker.getMaxRcvWindow();
-    else
-      return 0;
-  }
-
-  /**
-   * Get the current receive window size.
-   *
-   * @return The current receive window size.
-   */
-  public int getCurrentRcvWindow()
-  {
-    if (broker != null)
-      return broker.getCurrentRcvWindow();
-    else
-      return 0;
-  }
-
-  /**
-   * Get the maximum send window size.
-   *
-   * @return The maximum send window size.
-   */
-  public int getMaxSendWindow()
-  {
-    if (broker != null)
-      return broker.getMaxSendWindow();
-    else
-      return 0;
-  }
-
-  /**
-   * Get the current send window size.
-   *
-   * @return The current send window size.
-   */
-  public int getCurrentSendWindow()
-  {
-    if (broker != null)
-      return broker.getCurrentSendWindow();
-    else
-      return 0;
-  }
-
-  /**
-   * Get the number of times the replication connection was lost.
-   * @return The number of times the replication connection was lost.
-   */
-  public int getNumLostConnections()
-  {
-    if (broker != null)
-      return broker.getNumLostConnections();
-    else
-      return 0;
-  }
-
-  /**
    * Get the number of modify conflicts successfully resolved.
    * @return The number of modify conflicts successfully resolved.
    */
@@ -2477,7 +1763,7 @@
   }
 
   /**
-   * Get the number of namign conflicts successfully resolved.
+   * Get the number of naming conflicts successfully resolved.
    * @return The number of naming conflicts successfully resolved.
    */
   public int getNumResolvedNamingConflicts()
@@ -2495,18 +1781,9 @@
   }
 
   /**
-   * Get the server ID.
-   * @return The server ID.
-   */
-  public short getServerId()
-  {
-    return serverId;
-  }
-
-  /**
    * Check if the domain solve conflicts.
    *
-   * @return a boolean indicating if the domain should sove conflicts.
+   * @return a boolean indicating if the domain should solve conflicts.
    */
   public boolean solveConflict()
   {
@@ -2526,16 +1803,7 @@
     state.save();
     state.clearInMemory();
     disabled = true;
-
-    // Stop the listener thread
-    if (listenerThread != null)
-      listenerThread.shutdown();
-
-    broker.stop(); // This will cut the session and wake up the listener
-
-    // Wait for the listener thread to stop
-    if (listenerThread != null)
-      listenerThread.waitForShutdown();
+    disableService(); // This will cut the session and wake up the listener
   }
 
   /**
@@ -2578,16 +1846,7 @@
       return;
     }
 
-    // After an on-line import, the value of the generationId is new
-    // and it is necessary for the broker to send this new value as part
-    // of the serverStart message.
-    broker.setGenerationId(generationId);
-
-    broker.start(replicationServers);
-
-    // Create the listener thread
-    listenerThread = new ListenerThread(this, updateToReplayQueue);
-    listenerThread.start();
+    enableService();
 
     disabled = false;
   }
@@ -2600,7 +1859,7 @@
    */
   public long computeGenerationId() throws DirectoryException
   {
-    long genId = exportBackend(true);
+    long genId = exportBackend(null, true);
 
     if (debugEnabled())
       TRACER.debugInfo("Computed generationId: generationId=" + genId);
@@ -2609,11 +1868,9 @@
   }
 
   /**
-   * Returns the generationId set for this domain.
-   *
-   * @return The generationId.
+   * {@inheritDoc}
    */
-  public long getGenerationId()
+  public long getGenerationID()
   {
     return generationId;
   }
@@ -2627,7 +1884,7 @@
   /**
    * Stores the value of the generationId.
    * @param generationId The value of the generationId.
-   * @return a ResultCode indicating if the method was successfull.
+   * @return a ResultCode indicating if the method was successful.
    */
   public ResultCode saveGenerationId(long generationId)
   {
@@ -2792,45 +2049,8 @@
   }
 
   /**
-   * Reset the generationId of this domain in the whole topology.
-   * A message is sent to the Replication Servers for them to reset
-   * their change dbs.
-   *
-   * @param generationIdNewValue The new value of the generation Id.
-   * @throws DirectoryException when an error occurs
-   */
-  public void resetGenerationId(Long generationIdNewValue)
-  throws DirectoryException
-  {
-    if (debugEnabled())
-      TRACER.debugInfo(
-          this.getName() + "resetGenerationId" + generationIdNewValue);
-
-    if (!isConnected())
-    {
-      ResultCode resultCode = ResultCode.OTHER;
-      Message message = ERR_RESET_GENERATION_CONN_ERR_ID.get(
-          baseDn.toNormalizedString());
-      throw new DirectoryException(
-         resultCode, message);
-    }
-
-    ResetGenerationIdMsg genIdMessage = null;
-
-    if (generationIdNewValue == null)
-    {
-      genIdMessage = new ResetGenerationIdMsg(this.generationId);
-    }
-    else
-    {
-      genIdMessage = new ResetGenerationIdMsg(generationIdNewValue);
-    }
-    broker.publish(genIdMessage);
-  }
-
-  /**
    * Do whatever is needed when a backup is started.
-   * We need to make sure that the serverState is correclty save.
+   * We need to make sure that the serverState is correctly save.
    */
   public void backupStart()
   {
@@ -2849,103 +2069,7 @@
    * Total Update >>
    */
 
-  /**
-   * Receives bytes related to an entry in the context of an import to
-   * initialize the domain (called by ReplLDIFInputStream).
-   *
-   * @return The bytes. Null when the Done or Err message has been received
-   */
-  public byte[] receiveEntryBytes()
-  {
-    ReplicationMsg msg;
-    while (true)
-    {
-      try
-      {
-        msg = broker.receive();
 
-        if (debugEnabled())
-          TRACER.debugVerbose(
-              " sid:" + this.serverId +
-              " base DN:" + this.baseDn +
-              " Import EntryBytes received " + msg);
-        if (msg == null)
-        {
-          // The server is in the shutdown process
-          return null;
-        }
-
-        if (msg instanceof EntryMsg)
-        {
-          EntryMsg entryMsg = (EntryMsg)msg;
-          byte[] entryBytes = entryMsg.getEntryBytes();
-          ieContext.updateCounters();
-          return entryBytes;
-        }
-        else if (msg instanceof DoneMsg)
-        {
-          // This is the normal termination of the import
-          // No error is stored and the import is ended
-          // by returning null
-          return null;
-        }
-        else if (msg instanceof ErrorMsg)
-        {
-          // This is an error termination during the import
-          // The error is stored and the import is ended
-          // by returning null
-          ErrorMsg errorMsg = (ErrorMsg)msg;
-          ieContext.exception = new DirectoryException(
-                                      ResultCode.OTHER,
-                                      errorMsg.getDetails());
-          return null;
-        }
-        else
-        {
-          // Other messages received during an import are trashed
-        }
-      }
-      catch(Exception e)
-      {
-        // TODO: i18n
-        ieContext.exception = new DirectoryException(ResultCode.OTHER,
-            Message.raw("received an unexpected message type" +
-                e.getLocalizedMessage()));
-      }
-    }
-  }
-
-  /**
-   * Processes an error message received while an import/export is
-   * on going.
-   * @param errorMsg The error message received.
-   */
-  protected void abandonImportExport(ErrorMsg errorMsg)
-  {
-    // FIXME TBD Treat the case where the error happens while entries
-    // are being exported
-
-    if (debugEnabled())
-      TRACER.debugVerbose(
-          " abandonImportExport:" + this.serverId +
-          " base DN:" + this.baseDn +
-          " Error Msg received " + errorMsg);
-
-    if (ieContext != null)
-    {
-      ieContext.exception = new DirectoryException(ResultCode.OTHER,
-          errorMsg.getDetails());
-
-      if (ieContext.initializeTask instanceof InitializeTask)
-      {
-        // Update the task that initiated the import
-        ((InitializeTask)ieContext.initializeTask).
-        updateTaskCompletionState(ieContext.exception);
-
-        releaseIEContext();
-      }
-    }
-  }
 
   /**
    * Clears all the entries from the JE backend determined by the
@@ -3000,16 +2124,31 @@
   }
 
   /**
+   * This method trigger an export of the replicated data.
+   *
+   * @param output               The OutputStream where the export should
+   *                             be produced.
+   * @throws DirectoryException  When needed.
+   */
+  protected void exportBackend(OutputStream output) throws DirectoryException
+  {
+    exportBackend(output, false);
+  }
+
+  /**
    * Export the entries from the backend and/or compute the generation ID.
    * The ieContext must have been set before calling.
-   * @param checksumOutput true is the exportBackend is called to compute
-   *                       the generationID
    *
-   * @return The computed  generationID.
+   * @param output              The OutputStream where the export should
+   *                            be produced.
+   * @param checksumOutput      A boolean indicating if this export is
+   *                            invoked to perform a checksum only
+   *
+   * @return The computed       GenerationID.
    *
    * @throws DirectoryException when an error occurred
    */
-  protected long exportBackend(boolean checksumOutput)
+  protected long exportBackend(OutputStream output, boolean checksumOutput)
   throws DirectoryException
   {
     long genID = 0;
@@ -3042,7 +2181,7 @@
     }
 
     OutputStream os;
-    ReplLDIFOutputStream ros;
+    ReplLDIFOutputStream ros = null;
 
     if (checksumOutput)
     {
@@ -3060,8 +2199,7 @@
     }
     else
     {
-      ros = new ReplLDIFOutputStream(this, (short)-1);
-      os = ros;
+      os = output;
     }
     LDIFExportConfig exportConfig = new LDIFExportConfig(os);
 
@@ -3096,7 +2234,7 @@
     }
     catch (DirectoryException de)
     {
-      if ((checksumOutput) &&
+      if ((ros != null) &&
           (ros.getNumExportedEntries() >= entryCount))
       {
         // This is the normal end when computing the generationId
@@ -3170,277 +2308,6 @@
   }
 
   /**
-   * Get the internal broker to perform some operations on it.
-   *
-   * @return The broker for this domain.
-   */
-  ReplicationBroker getBroker()
-  {
-    return broker;
-  }
-
-  /**
-   * Exports an entry in LDIF format.
-   *
-   * @param  lDIFEntry The entry to be exported..
-   *
-   * @throws IOException when an error occurred.
-   */
-  public void exportLDIFEntry(String lDIFEntry) throws IOException
-  {
-    // If an error was raised - like receiving an ErrorMsg
-    // we just let down the export.
-    if (ieContext.exception != null)
-    {
-      IOException ioe = new IOException(ieContext.exception.getMessage());
-      ieContext = null;
-      throw ioe;
-    }
-
-    EntryMsg entryMessage = new EntryMsg(
-        serverId, ieContext.exportTarget, lDIFEntry.getBytes());
-    broker.publish(entryMessage);
-
-    try
-    {
-      ieContext.updateCounters();
-    }
-    catch (DirectoryException de)
-    {
-      throw new IOException(de.getMessage());
-    }
-  }
-
-  /**
-   * Initializes this domain from another source server.
-   *
-   * @param source The source from which to initialize
-   * @param initTask The task that launched the initialization
-   *                 and should be updated of its progress.
-   * @throws DirectoryException when an error occurs
-   */
-  public void initializeFromRemote(short source, Task initTask)
-  throws DirectoryException
-  {
-    if (debugEnabled())
-      TRACER.debugInfo("Entering initializeFromRemote");
-
-    acquireIEContext();
-    ieContext.initializeTask = initTask;
-
-    InitializeRequestMsg initializeMsg = new InitializeRequestMsg(
-        baseDn, serverId, source);
-
-    // Publish Init request msg
-    broker.publish(initializeMsg);
-
-    // .. we expect to receive entries or err after that
-  }
-
-  /**
-   * Verifies that the given string represents a valid source
-   * from which this server can be initialized.
-   * @param sourceString The string representing the source
-   * @return The source as a short value
-   * @throws DirectoryException if the string is not valid
-   */
-  public short decodeSource(String sourceString)
-  throws DirectoryException
-  {
-    short  source = 0;
-    Throwable cause = null;
-    try
-    {
-      source = Integer.decode(sourceString).shortValue();
-      if ((source >= -1) && (source != serverId))
-      {
-        // TODO Verifies serverID is in the domain
-        // We shold check here that this is a server implied
-        // in the current domain.
-        return source;
-      }
-    }
-    catch(Exception e)
-    {
-      cause = e;
-    }
-
-    ResultCode resultCode = ResultCode.OTHER;
-    Message message = ERR_INVALID_IMPORT_SOURCE.get();
-    if (cause != null)
-    {
-      throw new DirectoryException(
-          resultCode, message, cause);
-    }
-    else
-    {
-      throw new DirectoryException(
-          resultCode, message);
-    }
-  }
-
-  /**
-   * Verifies that the given string represents a valid source
-   * from which this server can be initialized.
-   * @param targetString The string representing the source
-   * @return The source as a short value
-   * @throws DirectoryException if the string is not valid
-   */
-  public short decodeTarget(String targetString)
-  throws DirectoryException
-  {
-    short  target = 0;
-    Throwable cause;
-    if (targetString.equalsIgnoreCase("all"))
-    {
-      return RoutableMsg.ALL_SERVERS;
-    }
-
-    // So should be a serverID
-    try
-    {
-      target = Integer.decode(targetString).shortValue();
-      if (target >= 0)
-      {
-        // FIXME Could we check now that it is a know server in the domain ?
-      }
-      return target;
-    }
-    catch(Exception e)
-    {
-      cause = e;
-    }
-    ResultCode resultCode = ResultCode.OTHER;
-    Message message = ERR_INVALID_EXPORT_TARGET.get();
-
-    if (cause != null)
-      throw new DirectoryException(
-          resultCode, message, cause);
-    else
-      throw new DirectoryException(
-          resultCode, message);
-
-  }
-
-  private synchronized void acquireIEContext()
-  throws DirectoryException
-  {
-    if (ieContext != null)
-    {
-      // Rejects 2 simultaneous exports
-      Message message = ERR_SIMULTANEOUS_IMPORT_EXPORT_REJECTED.get();
-      throw new DirectoryException(ResultCode.OTHER,
-          message);
-    }
-
-    ieContext = new IEContext();
-  }
-
-  private synchronized void releaseIEContext()
-  {
-    ieContext = null;
-  }
-
-  /**
-   * Process the initialization of some other server or servers in the topology
-   * specified by the target argument.
-   * @param target The target that should be initialized
-   * @param initTask The task that triggers this initialization and that should
-   *                 be updated with its progress.
-   *
-   * @exception DirectoryException When an error occurs.
-   */
-  public void initializeRemote(short target, Task initTask)
-  throws DirectoryException
-  {
-    initializeRemote(target, serverId, initTask);
-  }
-
-  /**
-   * Process the initialization of some other server or servers in the topology
-   * specified by the target argument when this initialization specifying the
-   * server that requests the initialization.
-   *
-   * @param target The target that should be initialized.
-   * @param requestorID The server that initiated the export.
-   * @param initTask The task that triggers this initialization and that should
-   *  be updated with its progress.
-   *
-   * @exception DirectoryException When an error occurs.
-   */
-  public void initializeRemote(short target, short requestorID, Task initTask)
-  throws DirectoryException
-  {
-    Message msg = NOTE_FULL_UPDATE_ENGAGED_FOR_REMOTE_START.get(
-      Short.toString(serverId),
-      baseDn.toString(),
-      Short.toString(requestorID));
-    logError(msg);
-
-    boolean contextAcquired=false;
-
-    try
-    {
-      Backend backend = retrievesBackend(this.baseDn);
-
-      if (!backend.supportsLDIFExport())
-      {
-        Message message = ERR_INIT_EXPORT_NOT_SUPPORTED.get(
-                            backend.getBackendID().toString());
-        logError(message);
-        throw new DirectoryException(ResultCode.OTHER, message);
-      }
-
-      acquireIEContext();
-      contextAcquired = true;
-
-      // The number of entries to be exported is the number of entries under
-      // the base DN entry and the base entry itself.
-      long entryCount = backend.numSubordinates(baseDn, true) + 1;
-      ieContext.exportTarget = target;
-      if (initTask != null)
-      {
-        ieContext.initializeTask = initTask;
-      }
-      ieContext.setCounters(entryCount, entryCount);
-
-      // Send start message to the peer
-      InitializeTargetMsg initializeMessage = new InitializeTargetMsg(
-          baseDn, serverId, ieContext.exportTarget, requestorID, entryCount);
-
-      broker.publish(initializeMessage);
-
-      exportBackend(false);
-
-      // Notify the peer of the success
-      DoneMsg doneMsg = new DoneMsg(serverId,
-          initializeMessage.getDestination());
-      broker.publish(doneMsg);
-
-      releaseIEContext();
-    }
-    catch(DirectoryException de)
-    {
-      // Notify the peer of the failure
-      ErrorMsg errorMsg =
-        new ErrorMsg(target,
-                         de.getMessageObject());
-      broker.publish(errorMsg);
-
-      if (contextAcquired)
-        releaseIEContext();
-
-      throw(de);
-    }
-
-    msg = NOTE_FULL_UPDATE_ENGAGED_FOR_REMOTE_END.get(
-      Short.toString(serverId),
-      baseDn.toString(),
-      Short.toString(requestorID));
-    logError(msg);
-  }
-
-  /**
    * Process backend before import.
    * @param backend The backend.
    * @throws Exception
@@ -3468,51 +2335,16 @@
   }
 
   /**
-   * Initializes the domain's backend with received entries.
-   * @param initializeMessage The message that initiated the import.
-   * @exception DirectoryException Thrown when an error occurs.
+   * This method should trigger an import of the replicated data.
+   *
+   * @param input                The InputStream from which
+   * @throws DirectoryException  When needed.
    */
-  protected void initialize(InitializeTargetMsg initializeMessage)
-  throws DirectoryException
+  public void importBackend(InputStream input) throws DirectoryException
   {
     LDIFImportConfig importConfig = null;
     DirectoryException de = null;
 
-    Message msg = NOTE_FULL_UPDATE_ENGAGED_FROM_REMOTE_START.get(
-      Short.toString(serverId),
-      baseDn.toString(),
-      Long.toString(initializeMessage.getRequestorID()));
-    logError(msg);
-
-    // Go into full update status
-    // Use synchronized as somebody could ask another status change at the same
-    // time
-    synchronized (status)
-    {
-      StatusMachineEvent event = StatusMachineEvent.TO_FULL_UPDATE_STATUS_EVENT;
-      ServerStatus newStatus =
-        StatusMachine.computeNewStatus(status, event);
-
-      if (newStatus == ServerStatus.INVALID_STATUS)
-      {
-        msg = ERR_DS_CANNOT_CHANGE_STATUS.get(baseDn.toString(),
-          Short.toString(serverId), status.toString(), event.toString());
-        logError(msg);
-        return;
-      }
-
-      // Store new status
-      status = newStatus;
-
-      if (debugEnabled())
-      TRACER.debugInfo("Replication domain " + baseDn +
-        " new status is: " + status);
-
-      // Perform whatever actions are needed to apply properties for being
-      // compliant with new status
-      updateDomainForNewStatus();
-    }
-
     Backend backend = retrievesBackend(baseDn);
 
     try
@@ -3526,26 +2358,8 @@
       }
       else
       {
-        if (initializeMessage.getRequestorID() == serverId)
-        {
-          // The import responds to a request we did so the IEContext
-          // is already acquired
-        }
-        else
-        {
-          acquireIEContext();
-        }
-
-        ieContext.importSource = initializeMessage.getsenderID();
-        ieContext.entryLeftCount = initializeMessage.getEntryCount();
-        ieContext.setCounters(initializeMessage.getEntryCount(),
-            initializeMessage.getEntryCount());
-
-        preBackendImport(backend);
-
-        ieContext.ldifImportInputStream = new ReplLDIFInputStream(this);
         importConfig =
-          new LDIFImportConfig(ieContext.ldifImportInputStream);
+          new LDIFImportConfig(input);
         List<DN> includeBranches = new ArrayList<DN>();
         includeBranches.add(this.baseDn);
         importConfig.setIncludeBranches(includeBranches);
@@ -3553,11 +2367,12 @@
 
         // TODO How to deal with rejected entries during the import
         importConfig.writeRejectedEntries(
-          getFileForPath("logs" + File.separator +
-              "replInitRejectedEntries").getAbsolutePath(),
-          ExistingFileBehavior.OVERWRITE);
+            getFileForPath("logs" + File.separator +
+            "replInitRejectedEntries").getAbsolutePath(),
+            ExistingFileBehavior.OVERWRITE);
 
         // Process import
+        preBackendImport(backend);
         backend.importLDIF(importConfig);
 
         stateSavingDisabled = false;
@@ -3570,9 +2385,6 @@
     }
     finally
     {
-      if ((ieContext != null)  && (ieContext.exception != null))
-        de = ieContext.exception;
-
       // Cleanup
       if (importConfig != null)
       {
@@ -3592,7 +2404,6 @@
           TRACER.debugInfo(
               "After import, the replication plugin restarts connections" +
               " to all RSs to provide new generation ID=" + generationId);
-        broker.setGenerationId(generationId);
       }
       catch (DirectoryException fe)
       {
@@ -3604,29 +2415,12 @@
         if (de == null)
           de = fe;
       }
-
-      // Re-exchange generationID and state with RS
-      broker.reStart();
-
-      // Update the task that initiated the import
-      if ((ieContext != null ) && (ieContext.initializeTask != null))
-      {
-        ((InitializeTask)ieContext.initializeTask).
-        updateTaskCompletionState(de);
-      }
-      releaseIEContext();
     }
     // Sends up the root error.
     if (de != null)
     {
       throw de;
     }
-
-    msg = NOTE_FULL_UPDATE_ENGAGED_FROM_REMOTE_END.get(
-      Short.toString(serverId),
-      baseDn.toString(),
-      Long.toString(initializeMessage.getRequestorID()));
-    logError(msg);
   }
 
   /**
@@ -3660,10 +2454,10 @@
    * @throws DirectoryException When an error occurred or no domain
    * match the provided baseDn.
    */
-  public static ReplicationDomain retrievesReplicationDomain(DN baseDn)
+  public static LDAPReplicationDomain retrievesReplicationDomain(DN baseDn)
   throws DirectoryException
   {
-    ReplicationDomain replicationDomain = null;
+    LDAPReplicationDomain replicationDomain = null;
 
     // Retrieves the domain
     DirectoryServer.getSynchronizationProviders();
@@ -3678,7 +2472,7 @@
       }
 
       // From the domainDN retrieves the replication domain
-      ReplicationDomain sdomain =
+      LDAPReplicationDomain sdomain =
         MultimasterReplication.findDomain(baseDn, null);
       if (sdomain == null)
       {
@@ -3714,15 +2508,6 @@
     return retrievesBackend(baseDn);
   }
 
-  /**
-   * Returns a boolean indicating if an import or export is currently
-   * processed.
-   * @return The status
-   */
-  public boolean ieRunning()
-  {
-    return (ieContext != null);
-  }
   /*
    * <<Total Update
    */
@@ -3769,7 +2554,7 @@
   {
     // Check that there is not already a domain with the same DN
     DN dn = configuration.getBaseDN();
-    ReplicationDomain domain = MultimasterReplication.findDomain(dn, null);
+    LDAPReplicationDomain domain = MultimasterReplication.findDomain(dn, null);
     if ((domain != null) && (domain.baseDn.equals(dn)))
     {
       Message message = ERR_SYNC_INVALID_DN.get();
@@ -3793,37 +2578,12 @@
   public ConfigChangeResult applyConfigurationChange(
          ReplicationDomainCfg configuration)
   {
-    // server id and base dn are readonly.
-    // isolationPolicy can be set immediately and will apply
-    // to the next updates.
-    // The other parameters needs to be renegociated with the ReplicationServer
-    // so that requires restarting the session with the ReplicationServer.
-    Boolean needToRestartSession = false;
-    Collection<String> newReplServers = configuration.getReplicationServer();
-
-    // A new session is necessary only when information regarding
-    // the connection is modified
-    if ((!(replicationServers.size() == newReplServers.size()
-        && replicationServers.containsAll(newReplServers))) ||
-        window != configuration.getWindowSize() ||
-        heartbeatInterval != configuration.getHeartbeatInterval())
-      needToRestartSession = true;
-
-    replicationServers = newReplServers;
-    window = configuration.getWindowSize();
-    heartbeatInterval = configuration.getHeartbeatInterval();
-    broker.changeConfig(replicationServers, maxReceiveQueue, maxReceiveDelay,
-        maxSendQueue, maxSendDelay, window, heartbeatInterval);
     isolationpolicy = configuration.getIsolationPolicy();
 
-    // To be able to stop and restart the broker properly just
-    // disable and enable the domain. That way a new session
-    // with the new configuration is available.
-    if (needToRestartSession)
-    {
-      this.disable();
-      this.enable();
-    }
+    changeConfig(
+        configuration.getReplicationServer(),
+        configuration.getWindowSize(),
+        configuration.getHeartbeatInterval());
 
     return new ConfigChangeResult(ResultCode.SUCCESS, false);
   }
@@ -3867,101 +2627,265 @@
   }
 
   /**
-   * Check if the domain is connected to a ReplicationServer.
+   * Starts the Replication Domain.
+   */
+  public void start()
+  {
+    // Create the ServerStateFlush thread
+    flushThread = new ServerStateFlush();
+    flushThread.start();
+
+    startListenService();
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void sessionInitiated(
+      ServerStatus initStatus,
+      ServerState replicationServerState,
+      ProtocolSession session)
+  {
+    super.sessionInitiated(initStatus, replicationServerState, session);
+    try
+    {
+      /*
+       * We must not publish changes to a replicationServer that has
+       * not seen all our previous changes because this could cause
+       * some other ldap servers to miss those changes.
+       * Check that the ReplicationServer has seen all our previous
+       * changes.
+       */
+      ChangeNumber replServerMaxChangeNumber =
+        replicationServerState.getMaxChangeNumber(serverId);
+
+      if (replServerMaxChangeNumber == null)
+      {
+        replServerMaxChangeNumber = new ChangeNumber(0, 0, serverId);
+      }
+      ChangeNumber ourMaxChangeNumber =
+        state.getMaxChangeNumber(serverId);
+
+      if ((ourMaxChangeNumber != null) &&
+          (!ourMaxChangeNumber.olderOrEqual(replServerMaxChangeNumber)))
+      {
+
+        // Replication server is missing some of our changes: let's
+        // send them to him.
+
+        Message message = DEBUG_GOING_TO_SEARCH_FOR_CHANGES.get();
+        logError(message);
+
+        /*
+         * Get all the changes that have not been seen by this
+         * replication server and populate the replayOperations
+         * list.
+         */
+        InternalSearchOperation op = searchForChangedEntries(
+            baseDn, replServerMaxChangeNumber, this);
+        if (op.getResultCode() != ResultCode.SUCCESS)
+        {
+          /*
+           * An error happened trying to search for the updates
+           * This server will start accepting again new updates but
+           * some inconsistencies will stay between servers.
+           * Log an error for the repair tool
+           * that will need to re-synchronize the servers.
+           */
+          message = ERR_CANNOT_RECOVER_CHANGES.get(
+              baseDn.toNormalizedString());
+          logError(message);
+        } else
+        {
+          for (FakeOperation replayOp : replayOperations)
+          {
+            ChangeNumber cn = replayOp.getChangeNumber();
+            /*
+             * Because the entry returned by the search operation
+             * can contain old historical information, it is
+             * possible that some of the FakeOperation are
+             * actually older than the
+             * Only send the Operation if it was newer than
+             * the last ChangeNumber known by the Replication Server.
+             */
+            if (cn.newer(replServerMaxChangeNumber))
+            {
+              message =
+                DEBUG_SENDING_CHANGE.get(
+                    replayOp.getChangeNumber().toString());
+              logError(message);
+              session.publish(replayOp.generateMessage());
+            }
+          }
+          message = DEBUG_CHANGES_SENT.get();
+          logError(message);
+        }
+        replayOperations.clear();
+      }
+    } catch (Exception e)
+    {
+      Message message = ERR_PUBLISHING_FAKE_OPS.get(
+          baseDn.toNormalizedString(),
+          e.getLocalizedMessage() + stackTraceToSingleLineString(e));
+      logError(message);
+    }
+  }
+
+  /**
+   * Search for the changes that happened since fromChangeNumber
+   * based on the historical attribute. The only changes that will
+   * be send will be the one generated on the serverId provided in
+   * fromChangeNumber.
+   * @param baseDn the base DN
+   * @param fromChangeNumber The change number from which we want the changes
+   * @param resultListener that will process the entries returned.
+   * @return the internal search operation
+   * @throws Exception when raised.
+   */
+  public static InternalSearchOperation searchForChangedEntries(
+    DN baseDn,
+    ChangeNumber fromChangeNumber,
+    InternalSearchListener resultListener)
+    throws Exception
+  {
+    InternalClientConnection conn =
+      InternalClientConnection.getRootConnection();
+    Short serverId = fromChangeNumber.getServerId();
+
+    String maxValueForId = "ffffffffffffffff" +
+      String.format("%04x", serverId) + "ffffffff";
+
+    LDAPFilter filter = LDAPFilter.decode(
+       "(&(" + Historical.HISTORICALATTRIBUTENAME + ">=dummy:"
+       + fromChangeNumber + ")(" + Historical.HISTORICALATTRIBUTENAME +
+       "<=dummy:" + maxValueForId + "))");
+
+    LinkedHashSet<String> attrs = new LinkedHashSet<String>(1);
+    attrs.add(Historical.HISTORICALATTRIBUTENAME);
+    attrs.add(Historical.ENTRYUIDNAME);
+    attrs.add("*");
+    return conn.processSearch(
+      new ASN1OctetString(baseDn.toString()),
+      SearchScope.WHOLE_SUBTREE,
+      DereferencePolicy.NEVER_DEREF_ALIASES,
+      0, 0, false, filter,
+      attrs,
+      resultListener);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void handleInternalSearchEntry(
+    InternalSearchOperation searchOperation,
+    SearchResultEntry searchEntry)
+  {
+    /*
+     * This call back is called at session establishment phase
+     * for each entry that has been changed by this server and the changes
+     * have not been sent to any Replication Server.
+     * The role of this method is to build equivalent operation from
+     * the historical information and add them in the replayOperations
+     * table.
+     */
+    Iterable<FakeOperation> updates =
+      Historical.generateFakeOperations(searchEntry);
+    for (FakeOperation op : updates)
+    {
+      replayOperations.add(op);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void handleInternalSearchReference(
+    InternalSearchOperation searchOperation,
+    SearchResultReference searchReference)
+  {
+    // TODO to be implemented
+  }
+
+
+  /**
+   * This method should return the total number of objects in the
+   * replicated domain.
+   * This count will be used for reporting.
    *
-   * @return true if the server is connected, false if not.
+   * @throws DirectoryException when needed.
+   *
+   * @return The number of objects in the replication domain.
    */
-  public boolean isConnected()
+  public long countEntries() throws DirectoryException
   {
-    if (broker != null)
-      return broker.isConnected();
-    else
+    Backend backend = retrievesBackend(baseDn);
+
+    if (!backend.supportsLDIFExport())
+    {
+      Message message = ERR_INIT_EXPORT_NOT_SUPPORTED.get(
+                          backend.getBackendID().toString());
+      logError(message);
+      throw new DirectoryException(ResultCode.OTHER, message);
+    }
+
+    return backend.numSubordinates(baseDn, true) + 1;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean processUpdate(UpdateMsg updateMsg)
+  {
+    if (updateMsg instanceof LDAPUpdateMsg)
+    {
+      LDAPUpdateMsg msg = (LDAPUpdateMsg) updateMsg;
+
+      // put the UpdateMsg in the RemotePendingChanges list.
+      remotePendingChanges.putRemoteUpdate(msg);
+
+      // Put update message into the replay queue
+      // (block until some place in the queue is available)
+      UpdateToReplay updateToReplay = new UpdateToReplay(msg, this);
+      updateToReplayQueue.offer(updateToReplay);
+
       return false;
+    }
+
+    // unknown message type, this should not happen, just ignore it.
+    return true;
   }
 
   /**
-   * Determine whether the connection to the replication server is encrypted.
-   * @return true if the connection is encrypted, false otherwise.
+   * Monitoring information for the LDAPReplicationDomain.
+   *
+   * @return Monitoring attributes specific to the LDAPReplicationDomain.
    */
-  public boolean isSessionEncrypted()
+  public Collection<Attribute> getAdditionalMonitoring()
   {
-    if (broker != null)
-      return broker.isSessionEncrypted();
-    else
-      return false;
-  }
+    ArrayList<Attribute> attributes = new ArrayList<Attribute>();
 
-  /**
-   * Gets the info for DSs in the topology (except us).
-   * @return The info for DSs in the topology (except us)
-   */
-  public List<DSInfo> getDsList()
-  {
-    return dsList;
-  }
+    /* get number of changes in the pending list */
+    ReplicationMonitor.addMonitorData(
+        attributes, "pending-updates", getPendingUpdatesCount());
 
-  /**
-   * Gets the info for RSs in the topology (except the one we are connected
-   * to).
-   * @return The info for RSs in the topology (except the one we are connected
-   * to)
-   */
-  public List<RSInfo> getRsList()
-  {
-    return rsList;
-  }
+    /* get number of changes successfully */
+    ReplicationMonitor.addMonitorData(attributes, "replayed-updates-ok",
+        getNumReplayedPostOpCalled());
 
-  /**
-   * Tells if assured replication is enabled for this domain.
-   * @return True if assured replication is enabled for this domain.
-   */
-  public boolean isAssured()
-  {
-    return assured;
-  }
+    /* get number of modify conflicts */
+    ReplicationMonitor.addMonitorData(attributes, "resolved-modify-conflicts",
+        getNumResolvedModifyConflicts());
 
-  /**
-   * Gives the mode for the assured replication of the domain.
-   * @return The mode for the assured replication of the domain.
-   */
-  public AssuredMode getAssuredMode()
-  {
-    return assuredMode;
-  }
+    /* get number of naming conflicts */
+    ReplicationMonitor.addMonitorData(attributes, "resolved-naming-conflicts",
+        getNumResolvedNamingConflicts());
 
-  /**
-   * Gives the assured level of the replication of the domain.
-   * @return The assured level of the replication of the domain.
-   */
-  public byte getAssuredSdLevel()
-  {
-    return assuredSdLevel;
-  }
+    /* get number of unresolved naming conflicts */
+    ReplicationMonitor.addMonitorData(attributes, "unresolved-naming-conflicts",
+        getNumUnresolvedNamingConflicts());
 
-  /**
-   * Gets the group id for this domain.
-   * @return The group id for this domain.
-   */
-  public byte getGroupId()
-  {
-    return groupId;
-  }
-
-  /**
-   * Gets the referrals URLs this domain publishes.
-   * @return The referrals URLs this domain publishes.
-   */
-  public List<String> getRefUrls()
-  {
-    return refUrls;
-  }
-
-  /**
-   * Gets the status for this domain.
-   * @return The status for this domain.
-   */
-  public ServerStatus getStatus()
-  {
-    return status;
+    return attributes;
   }
 }
diff --git a/opends/src/server/org/opends/server/replication/plugin/MultimasterReplication.java b/opends/src/server/org/opends/server/replication/plugin/MultimasterReplication.java
index 0af98a0..bbc0a82 100644
--- a/opends/src/server/org/opends/server/replication/plugin/MultimasterReplication.java
+++ b/opends/src/server/org/opends/server/replication/plugin/MultimasterReplication.java
@@ -91,8 +91,8 @@
                   ExportTaskListener
 {
   private ReplicationServerListener replicationServerListener = null;
-  private static Map<DN, ReplicationDomain> domains =
-    new HashMap<DN, ReplicationDomain>() ;
+  private static Map<DN, LDAPReplicationDomain> domains =
+    new HashMap<DN, LDAPReplicationDomain>() ;
 
   /**
    * The queue of received update messages, to be treated by the ReplayThread
@@ -122,7 +122,8 @@
    *                   Can be null is the request has no associated operation.
    * @return           The domain for this DN.
    */
-  public static ReplicationDomain findDomain(DN dn, PluginOperation pluginOp)
+  public static LDAPReplicationDomain findDomain(
+      DN dn, PluginOperation pluginOp)
   {
     /*
      * Don't run the special replication code on Operation that are
@@ -163,7 +164,7 @@
     }
 
 
-    ReplicationDomain domain = null;
+    LDAPReplicationDomain domain = null;
     DN temp = dn;
     do
     {
@@ -186,12 +187,12 @@
    * @return The domain created.
    * @throws ConfigException When the configuration is not valid.
    */
-  public static ReplicationDomain createNewDomain(
+  public static LDAPReplicationDomain createNewDomain(
       ReplicationDomainCfg configuration)
       throws ConfigException
   {
-    ReplicationDomain domain;
-    domain = new ReplicationDomain(configuration, updateToReplayQueue);
+    LDAPReplicationDomain domain;
+    domain = new LDAPReplicationDomain(configuration, updateToReplayQueue);
 
     if (domains.size() == 0)
     {
@@ -211,7 +212,7 @@
    */
   public static void deleteDomain(DN dn)
   {
-    ReplicationDomain domain = domains.remove(dn);
+    LDAPReplicationDomain domain = domains.remove(dn);
 
     if (domain != null)
       domain.shutdown();
@@ -310,7 +311,7 @@
   public boolean isConfigurationAddAcceptable(
       ReplicationDomainCfg configuration, List<Message> unacceptableReasons)
   {
-    return ReplicationDomain.isConfigurationAcceptable(
+    return LDAPReplicationDomain.isConfigurationAcceptable(
       configuration, unacceptableReasons);
   }
 
@@ -322,7 +323,7 @@
   {
     try
     {
-      ReplicationDomain rd = createNewDomain(configuration);
+      LDAPReplicationDomain rd = createNewDomain(configuration);
       if (isRegistered)
       {
         rd.start();
@@ -384,7 +385,7 @@
   public SynchronizationProviderResult handleConflictResolution(
       PreOperationModifyOperation modifyOperation)
   {
-    ReplicationDomain domain =
+    LDAPReplicationDomain domain =
       findDomain(modifyOperation.getEntryDN(), modifyOperation);
     if (domain == null)
       return new SynchronizationProviderResult.ContinueProcessing();
@@ -399,7 +400,7 @@
   public SynchronizationProviderResult handleConflictResolution(
       PreOperationAddOperation addOperation) throws DirectoryException
   {
-    ReplicationDomain domain =
+    LDAPReplicationDomain domain =
       findDomain(addOperation.getEntryDN(), addOperation);
     if (domain == null)
       return new SynchronizationProviderResult.ContinueProcessing();
@@ -414,7 +415,7 @@
   public SynchronizationProviderResult handleConflictResolution(
       PreOperationDeleteOperation deleteOperation) throws DirectoryException
   {
-    ReplicationDomain domain =
+    LDAPReplicationDomain domain =
       findDomain(deleteOperation.getEntryDN(), deleteOperation);
     if (domain == null)
       return new SynchronizationProviderResult.ContinueProcessing();
@@ -429,7 +430,7 @@
   public SynchronizationProviderResult handleConflictResolution(
       PreOperationModifyDNOperation modifyDNOperation) throws DirectoryException
   {
-    ReplicationDomain domain =
+    LDAPReplicationDomain domain =
       findDomain(modifyDNOperation.getEntryDN(), modifyDNOperation);
     if (domain == null)
       return new SynchronizationProviderResult.ContinueProcessing();
@@ -445,7 +446,7 @@
          doPreOperation(PreOperationModifyOperation modifyOperation)
   {
     DN operationDN = modifyOperation.getEntryDN();
-    ReplicationDomain domain = findDomain(operationDN, modifyOperation);
+    LDAPReplicationDomain domain = findDomain(operationDN, modifyOperation);
 
     if ((domain == null) || (!domain.solveConflict()))
       return new SynchronizationProviderResult.ContinueProcessing();
@@ -485,7 +486,7 @@
          throws DirectoryException
   {
     DN operationDN = modifyDNOperation.getEntryDN();
-    ReplicationDomain domain = findDomain(operationDN, modifyDNOperation);
+    LDAPReplicationDomain domain = findDomain(operationDN, modifyDNOperation);
 
     if ((domain == null) || (!domain.solveConflict()))
       return new SynchronizationProviderResult.ContinueProcessing();
@@ -513,7 +514,7 @@
   public SynchronizationProviderResult doPreOperation(
          PreOperationAddOperation addOperation)
   {
-    ReplicationDomain domain =
+    LDAPReplicationDomain domain =
       findDomain(addOperation.getEntryDN(), addOperation);
     if (domain == null)
       return new SynchronizationProviderResult.ContinueProcessing();
@@ -534,7 +535,7 @@
     isRegistered = false;
 
     // shutdown all the domains
-    for (ReplicationDomain domain : domains.values())
+    for (LDAPReplicationDomain domain : domains.values())
     {
       domain.shutdown();
     }
@@ -566,7 +567,7 @@
   @Override
   public void processSchemaChange(List<Modification> modifications)
   {
-    ReplicationDomain domain =
+    LDAPReplicationDomain domain =
       findDomain(DirectoryServer.getSchemaDN(), null);
     if (domain != null)
       domain.synchronizeModifications(modifications);
@@ -579,7 +580,7 @@
   {
     for (DN dn : backend.getBaseDNs())
     {
-      ReplicationDomain domain = findDomain(dn, null);
+      LDAPReplicationDomain domain = findDomain(dn, null);
       if (domain != null)
         domain.backupStart();
     }
@@ -593,7 +594,7 @@
   {
     for (DN dn : backend.getBaseDNs())
     {
-      ReplicationDomain domain = findDomain(dn, null);
+      LDAPReplicationDomain domain = findDomain(dn, null);
       if (domain != null)
         domain.backupEnd();
     }
@@ -606,7 +607,7 @@
   {
     for (DN dn : backend.getBaseDNs())
     {
-      ReplicationDomain domain = findDomain(dn, null);
+      LDAPReplicationDomain domain = findDomain(dn, null);
       if (domain != null)
         domain.disable();
     }
@@ -620,7 +621,7 @@
   {
     for (DN dn : backend.getBaseDNs())
     {
-      ReplicationDomain domain = findDomain(dn, null);
+      LDAPReplicationDomain domain = findDomain(dn, null);
       if (domain != null)
         domain.enable();
     }
@@ -633,7 +634,7 @@
   {
     for (DN dn : backend.getBaseDNs())
     {
-      ReplicationDomain domain = findDomain(dn, null);
+      LDAPReplicationDomain domain = findDomain(dn, null);
       if (domain != null)
         domain.disable();
     }
@@ -647,7 +648,7 @@
   {
     for (DN dn : backend.getBaseDNs())
     {
-      ReplicationDomain domain = findDomain(dn, null);
+      LDAPReplicationDomain domain = findDomain(dn, null);
       if (domain != null)
         domain.enable();
     }
@@ -660,7 +661,7 @@
   {
     for (DN dn : backend.getBaseDNs())
     {
-      ReplicationDomain domain = findDomain(dn, null);
+      LDAPReplicationDomain domain = findDomain(dn, null);
       if (domain != null)
         domain.backupStart();
     }
@@ -674,7 +675,7 @@
   {
     for (DN dn : backend.getBaseDNs())
     {
-      ReplicationDomain domain = findDomain(dn, null);
+      LDAPReplicationDomain domain = findDomain(dn, null);
       if (domain != null)
         domain.backupEnd();
     }
@@ -708,7 +709,7 @@
    */
   private void genericPostOperation(PostOperationOperation operation, DN dn)
   {
-    ReplicationDomain domain = findDomain(dn, operation);
+    LDAPReplicationDomain domain = findDomain(dn, operation);
     if (domain == null)
       return;
 
@@ -766,7 +767,7 @@
     isRegistered = true;
 
     // start all the domains
-    for (ReplicationDomain domain : domains.values())
+    for (LDAPReplicationDomain domain : domains.values())
     {
       domain.start();
     }
diff --git a/opends/src/server/org/opends/server/replication/plugin/PendingChange.java b/opends/src/server/org/opends/server/replication/plugin/PendingChange.java
index 2ff4904..0bc994e 100644
--- a/opends/src/server/org/opends/server/replication/plugin/PendingChange.java
+++ b/opends/src/server/org/opends/server/replication/plugin/PendingChange.java
@@ -28,7 +28,7 @@
 
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.replication.common.ServerState;
-import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
 import org.opends.server.types.DN;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.operation.PluginOperation;
@@ -41,7 +41,7 @@
 {
   private ChangeNumber changeNumber;
   private boolean committed;
-  private UpdateMsg msg;
+  private LDAPUpdateMsg msg;
   private PluginOperation op;
   private ServerState dependencyState = null;
   private DN targetDN = null;
@@ -54,7 +54,7 @@
    */
   public PendingChange(ChangeNumber changeNumber,
                        PluginOperation op,
-                       UpdateMsg msg)
+                       LDAPUpdateMsg msg)
   {
     this.changeNumber = changeNumber;
     this.committed = false;
@@ -94,7 +94,7 @@
    * @return the message if operation was a replication operation
    * null if the operation was a local operation
    */
-  public UpdateMsg getMsg()
+  public LDAPUpdateMsg getMsg()
   {
     return msg;
   }
@@ -103,7 +103,7 @@
    * Set the message associated to the PendingChange.
    * @param msg the message
    */
-  public void setMsg(UpdateMsg msg)
+  public void setMsg(LDAPUpdateMsg msg)
   {
     this.msg = msg;
   }
diff --git a/opends/src/server/org/opends/server/replication/plugin/PendingChanges.java b/opends/src/server/org/opends/server/replication/plugin/PendingChanges.java
index 513e41c..a284aae 100644
--- a/opends/src/server/org/opends/server/replication/plugin/PendingChanges.java
+++ b/opends/src/server/org/opends/server/replication/plugin/PendingChanges.java
@@ -26,14 +26,19 @@
  */
 package org.opends.server.replication.plugin;
 
+import static org.opends.messages.ReplicationMessages.*;
+import static org.opends.server.loggers.ErrorLogger.logError;
+
 import java.util.NoSuchElementException;
 import java.util.SortedMap;
 import java.util.TreeMap;
 
+import java.util.concurrent.TimeoutException;
+import org.opends.messages.Message;
+import org.opends.server.replication.service.ReplicationDomain;
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.replication.common.ChangeNumberGenerator;
-import org.opends.server.replication.common.ServerState;
-import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
 import org.opends.server.types.operation.PluginOperation;
 
 /**
@@ -61,32 +66,23 @@
   private ChangeNumberGenerator changeNumberGenerator;
 
   /**
-   * The Replicationbroker that will be used to send UpdateMsg.
+   * The ReplicationDomain that will be used to send UpdateMsg.
    */
-  private ReplicationBroker broker;
-
-  /**
-   * The ServerState that will be updated when UpdateMsg are committed.
-   */
-  private ServerState state;
+  private ReplicationDomain domain;
 
   /**
    * Creates a new PendingChanges using the provided ChangeNumberGenerator.
    *
    * @param changeNumberGenerator The ChangeNumberGenerator to use to create
    *                               new unique ChangeNumbers.
-   * @param broker  The Replicationbroker that will be used to send
+   * @param domain  The ReplicationDomain that will be used to send
    *                UpdateMsg.
-   * @param state   The ServerState that will be updated when UpdateMsg
-   *                are committed.
    */
   public PendingChanges(
-      ChangeNumberGenerator changeNumberGenerator, ReplicationBroker broker,
-      ServerState state)
+      ChangeNumberGenerator changeNumberGenerator, ReplicationDomain domain)
   {
     this.changeNumberGenerator = changeNumberGenerator;
-    this.broker = broker;
-    this.state = state;
+    this.domain = domain;
   }
 
   /**
@@ -96,7 +92,7 @@
    *
    * @return The UpdateMsg that was just removed.
    */
-  public synchronized UpdateMsg remove(ChangeNumber changeNumber)
+  public synchronized LDAPUpdateMsg remove(ChangeNumber changeNumber)
   {
     return pendingChanges.remove(changeNumber).getMsg();
   }
@@ -119,7 +115,7 @@
    * @param msg          The message associated to the update.
    */
   public synchronized void commit(ChangeNumber changeNumber,
-      UpdateMsg msg)
+      LDAPUpdateMsg msg)
   {
     PendingChange curChange = pendingChanges.get(changeNumber);
     if (curChange == null)
@@ -186,9 +182,19 @@
           (firstChange.getOp().isSynchronizationOperation() == false))
       {
         numSentUpdates++;
-        broker.publish(firstChange.getMsg());
-      }
-      state.update(firstChangeNumber);
+        LDAPUpdateMsg updateMsg = firstChange.getMsg();
+          try
+          {
+            domain.publish(updateMsg);
+          } catch (TimeoutException ex) {
+            // This exception may only be raised if assured replication is
+            // enabled
+            Message errorMsg = ERR_DS_ACK_TIMEOUT.get(
+              domain.getServiceID(), Long.toString(domain.getAssuredTimeout()),
+              updateMsg.toString());
+            logError(errorMsg);
+          }
+        }
       pendingChanges.remove(firstChangeNumber);
 
       if (pendingChanges.isEmpty())
diff --git a/opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java b/opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java
index 8d74618..22f526c 100644
--- a/opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java
+++ b/opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java
@@ -66,14 +66,15 @@
  * used to store the synchronized data and that is therefore persistent
  * across server reboot.
  */
-public class PersistentServerState extends ServerState
+public class PersistentServerState
 {
-   private DN baseDn;
-   private boolean savedStatus = true;
-   private InternalClientConnection conn =
+   private final DN baseDn;
+   private final InternalClientConnection conn =
        InternalClientConnection.getRootConnection();
-   private ASN1OctetString asn1BaseDn;
-   private short serverId;
+   private final ASN1OctetString asn1BaseDn;
+   private final short serverId;
+
+   private final ServerState state;
 
    /**
     * The attribute name used to store the state in the backend.
@@ -83,24 +84,45 @@
   /**
    * create a new ServerState.
    * @param baseDn The baseDN for which the ServerState is created
-   *  @param serverId The serverId
+   * @param serverId The serverId
    */
   public PersistentServerState(DN baseDn, short serverId)
   {
     this.baseDn = baseDn;
     this.serverId = serverId;
+    this.state = new ServerState();
     asn1BaseDn = new ASN1OctetString(baseDn.toString());
     loadState();
   }
 
   /**
-   * {@inheritDoc}
+   * Create a new PersistenServerState based on an already existing ServerState.
+   *
+   * @param baseDn    The baseDN for which the ServerState is created.
+   * @param serverId  The serverId.
+   * @param state     The serverState.
    */
-  @Override
+  public PersistentServerState(DN baseDn, short serverId, ServerState state)
+  {
+    this.baseDn = baseDn;
+    this.serverId = serverId;
+    this.state = state;
+    asn1BaseDn = new ASN1OctetString(baseDn.toString());
+    loadState();
+  }
+
+  /**
+   * Update the Server State with a ChangeNumber.
+   * All operations with smaller CSN and the same serverID must be committed
+   * before calling this method.
+   *
+   * @param changeNumber    The committed ChangeNumber.
+   *
+   * @return a boolean indicating if the update was meaningful.
+   */
   public boolean update(ChangeNumber changeNumber)
   {
-    savedStatus = false;
-    return super.update(changeNumber);
+    return state.update(changeNumber);
   }
 
   /**
@@ -108,14 +130,14 @@
    */
   public void save()
   {
-    if (savedStatus)
+    if (state.isSaved())
       return;
 
-    savedStatus = true;
+    state.setSaved(true);
     ResultCode resultCode = updateStateEntry();
     if (resultCode != ResultCode.SUCCESS)
     {
-        savedStatus = false;
+      state.setSaved(false);
     }
   }
 
@@ -310,7 +332,7 @@
    */
   private ResultCode runUpdateStateEntry(DN serverStateEntryDN)
   {
-    ArrayList<ASN1OctetString> values = this.toASN1ArrayList();
+    ArrayList<ASN1OctetString> values = state.toASN1ArrayList();
 
     LDAPAttribute attr =
       new LDAPAttribute(REPLICATION_STATE, values);
@@ -347,8 +369,8 @@
    */
   public void clearInMemory()
   {
-    super.clear();
-    this.savedStatus = false;
+    state.clear();
+    state.setSaved(false);
   }
 
   /**
@@ -384,13 +406,13 @@
     // maxCn stored in the serverState
     synchronized (this)
     {
-      serverStateMaxCn = this.getMaxChangeNumber(serverId);
+      serverStateMaxCn = state.getMaxChangeNumber(serverId);
 
       if (serverStateMaxCn == null)
         return;
 
       try {
-        op = ReplicationBroker.searchForChangedEntries(baseDn,
+        op = LDAPReplicationDomain.searchForChangedEntries(baseDn,
             serverStateMaxCn, null);
       }
       catch (Exception  e)
@@ -448,4 +470,16 @@
       }
     }
   }
+
+  /**
+   * Get the largest ChangeNumber seen for a given LDAP server ID.
+   *
+   * @param serverID    The server ID.
+   *
+   * @return            The largest ChangeNumber seen.
+   */
+  public ChangeNumber getMaxChangeNumber(short serverID)
+  {
+    return state.getMaxChangeNumber(serverID);
+  }
 }
\ No newline at end of file
diff --git a/opends/src/server/org/opends/server/replication/plugin/RemotePendingChanges.java b/opends/src/server/org/opends/server/replication/plugin/RemotePendingChanges.java
index 3552aa6..9c7f7ae 100644
--- a/opends/src/server/org/opends/server/replication/plugin/RemotePendingChanges.java
+++ b/opends/src/server/org/opends/server/replication/plugin/RemotePendingChanges.java
@@ -37,13 +37,12 @@
 import org.opends.server.core.ModifyDNOperationBasis;
 import org.opends.server.core.ModifyOperation;
 import org.opends.server.replication.common.ChangeNumber;
-import org.opends.server.replication.common.ChangeNumberGenerator;
 import org.opends.server.replication.common.ServerState;
 import org.opends.server.replication.protocol.AddMsg;
 import org.opends.server.replication.protocol.DeleteMsg;
 import org.opends.server.replication.protocol.ModifyDNMsg;
 import org.opends.server.replication.protocol.OperationContext;
-import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
 import org.opends.server.types.DN;
 import org.opends.server.types.Operation;
 
@@ -76,42 +75,31 @@
     new TreeSet<PendingChange>();
 
   /**
-   * The ServerState that will be updated when UpdateMsg are fully replayed.
+   * The ServerState that will be updated when LDAPUpdateMsg are fully replayed.
    */
   private ServerState state;
 
   /**
-   * The ChangeNumberGenerator to must be adjusted when new changes
-   * are received from a remote server.
-   */
-  private ChangeNumberGenerator changeNumberGenerator;
-
-  /**
    * Creates a new RemotePendingChanges using the provided ServerState.
    *
-   * @param changeNumberGenerator The ChangeNumberGenerator that should
-   *                              be adjusted when changes are received.
-   * @param state   The ServerState that will be updated when UpdateMsg
+   * @param state   The ServerState that will be updated when LDAPUpdateMsg
    *                have been fully replayed.
    */
-  public RemotePendingChanges(ChangeNumberGenerator changeNumberGenerator,
-                              ServerState state)
+  public RemotePendingChanges(ServerState state)
   {
-    this.changeNumberGenerator = changeNumberGenerator;
     this.state = state;
   }
 
   /**
-   * Add a new UpdateMsg that was received from the replication server
+   * Add a new LDAPUpdateMsg that was received from the replication server
    * to the pendingList.
    *
-   * @param update The UpdateMsg that was received from the replication
+   * @param update The LDAPUpdateMsg that was received from the replication
    *               server and that will be added to the pending list.
    */
-  public synchronized void putRemoteUpdate(UpdateMsg update)
+  public synchronized void putRemoteUpdate(LDAPUpdateMsg update)
   {
     ChangeNumber changeNumber = update.getChangeNumber();
-    changeNumberGenerator.adjust(changeNumber);
     pendingChanges.put(changeNumber, new PendingChange(changeNumber, null,
                                                         update));
   }
@@ -154,9 +142,9 @@
   /**
    * Get the first update in the list that have some dependencies cleared.
    *
-   * @return The UpdateMsg to be handled.
+   * @return The LDAPUpdateMsg to be handled.
    */
-  public synchronized UpdateMsg getNextUpdate()
+  public synchronized LDAPUpdateMsg getNextUpdate()
   {
     /*
      * Parse the list of Update with dependencies and check if the dependencies
@@ -216,7 +204,7 @@
     {
       if (pendingChange.getChangeNumber().older(changeNumber))
       {
-        UpdateMsg pendingMsg = pendingChange.getMsg();
+        LDAPUpdateMsg pendingMsg = pendingChange.getMsg();
         if (pendingMsg != null)
         {
           if (pendingMsg instanceof DeleteMsg)
@@ -297,7 +285,7 @@
     {
       if (pendingChange.getChangeNumber().older(changeNumber))
       {
-        UpdateMsg pendingMsg = pendingChange.getMsg();
+        LDAPUpdateMsg pendingMsg = pendingChange.getMsg();
         if (pendingMsg != null)
         {
           if (pendingMsg instanceof AddMsg)
@@ -349,7 +337,7 @@
     {
       if (pendingChange.getChangeNumber().older(changeNumber))
       {
-        UpdateMsg pendingMsg = pendingChange.getMsg();
+        LDAPUpdateMsg pendingMsg = pendingChange.getMsg();
         if (pendingMsg != null)
         {
           if (pendingMsg instanceof DeleteMsg)
@@ -424,7 +412,7 @@
     {
       if (pendingChange.getChangeNumber().older(changeNumber))
       {
-        UpdateMsg pendingMsg = pendingChange.getMsg();
+        LDAPUpdateMsg pendingMsg = pendingChange.getMsg();
         if (pendingMsg != null)
         {
           if (pendingMsg instanceof DeleteMsg)
@@ -479,7 +467,7 @@
    * @return     A boolean indicating if an operation cannot be replayed
    *             because of dependencies.
    */
-  public boolean checkDependencies(Operation op, UpdateMsg msg)
+  public boolean checkDependencies(Operation op, LDAPUpdateMsg msg)
   {
     if (op instanceof ModifyOperation)
     {
diff --git a/opends/src/server/org/opends/server/replication/plugin/ReplLDIFOutputStream.java b/opends/src/server/org/opends/server/replication/plugin/ReplLDIFOutputStream.java
index a5f6700..402e014 100644
--- a/opends/src/server/org/opends/server/replication/plugin/ReplLDIFOutputStream.java
+++ b/opends/src/server/org/opends/server/replication/plugin/ReplLDIFOutputStream.java
@@ -30,6 +30,7 @@
 import java.io.IOException;
 import java.io.OutputStream;
 
+import org.opends.server.replication.service.ReplicationDomain;
 import org.opends.server.util.ServerConstants;
 
 /**
@@ -103,8 +104,7 @@
           // of entries to export.
           throw(new IOException());
         }
-        if (numEntries<0)
-          domain.exportLDIFEntry(entryBuffer);
+
         numExportedEntries++;
         entryBuffer = "";
 
diff --git a/opends/src/server/org/opends/server/replication/plugin/ReplayThread.java b/opends/src/server/org/opends/server/replication/plugin/ReplayThread.java
index 8993a13..1899935 100644
--- a/opends/src/server/org/opends/server/replication/plugin/ReplayThread.java
+++ b/opends/src/server/org/opends/server/replication/plugin/ReplayThread.java
@@ -25,6 +25,8 @@
  *      Copyright 2006-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.replication.plugin;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
+
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.TimeUnit;
 import org.opends.messages.Message;
@@ -37,7 +39,6 @@
 
 import org.opends.server.api.DirectoryThread;
 import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.replication.protocol.UpdateMsg;
 
 /**
  * Thread that is used to get message from the replication servers (stored
@@ -102,8 +103,8 @@
           TimeUnit.SECONDS)) != null))
         {
           // Find replication domain for that update message
-          UpdateMsg updateMsg = updateToreplay.getUpdateMessage();
-          ReplicationDomain domain = updateToreplay.getReplicationDomain();
+          LDAPUpdateMsg updateMsg = updateToreplay.getUpdateMessage();
+          LDAPReplicationDomain domain = updateToreplay.getReplicationDomain();
           domain.replay(updateMsg);
         }
       } catch (Exception e)
diff --git a/opends/src/server/org/opends/server/replication/plugin/ReplicationMonitor.java b/opends/src/server/org/opends/server/replication/plugin/ReplicationMonitor.java
deleted file mode 100644
index 005747e..0000000
--- a/opends/src/server/org/opends/server/replication/plugin/ReplicationMonitor.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
- */
-package org.opends.server.replication.plugin;
-
-import java.util.ArrayList;
-
-import org.opends.server.admin.std.server.MonitorProviderCfg;
-import org.opends.server.api.MonitorProvider;
-import org.opends.server.core.DirectoryServer;
-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;
-
-/**
- * Class used to generate monitoring information for the replication.
- */
-public class ReplicationMonitor extends MonitorProvider<MonitorProviderCfg>
-{
-  private ReplicationDomain domain;  // the replication plugin
-
-  /**
-   * Create a new replication monitor.
-   * @param domain the plugin which created the monitor
-   */
-  public ReplicationMonitor(ReplicationDomain domain)
-  {
-    super("Replication monitor " + domain.getBaseDN().toString());
-    this.domain = domain;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override()
-  public void initializeMonitorProvider(MonitorProviderCfg configuration)
-  {
-    // no implementation needed.
-  }
-
-  /**
-   * Retrieves the name of this monitor provider.  It should be unique among all
-   * monitor providers, including all instances of the same monitor provider.
-   *
-   * @return  The name of this monitor provider.
-   */
-  @Override
-  public String getMonitorInstanceName()
-  {
-    return "Replication plugin "  + domain.getBaseDN().toString();
-  }
-
-  /**
-   * Retrieves a set of attributes containing monitor data that should be
-   * returned to the client if the corresponding monitor entry is requested.
-   *
-   * @return  A set of attributes containing monitor data that should be
-   *          returned to the client if the corresponding monitor entry is
-   *          requested.
-   */
-  @Override
-  public ArrayList<Attribute> getMonitorData()
-  {
-    ArrayList<Attribute> attributes = new ArrayList<Attribute>();
-
-    /* get the base dn */
-    Attribute attr = Attributes.create("base-dn", domain.getBaseDN()
-        .toString());
-    attributes.add(attr);
-
-    /* get the base dn */
-    attr = Attributes.create("connected-to", domain
-        .getReplicationServer());
-    attributes.add(attr);
-
-    /* get number of lost connections */
-    addMonitorData(attributes, "lost-connections",
-                   domain.getNumLostConnections());
-
-    /* get number of received updates */
-    addMonitorData(attributes, "received-updates", domain.getNumRcvdUpdates());
-
-    /* get number of updates sent */
-    addMonitorData(attributes, "sent-updates", domain.getNumSentUpdates());
-
-    /* get number of changes in the pending list */
-    addMonitorData(attributes, "pending-updates",
-                   domain.getPendingUpdatesCount());
-
-    /* get number of changes replayed */
-    addMonitorData(attributes, "replayed-updates",
-                   domain.getNumProcessedUpdates());
-
-    /* get number of changes successfully */
-    addMonitorData(attributes, "replayed-updates-ok",
-                   domain.getNumReplayedPostOpCalled());
-
-    /* get number of modify conflicts */
-    addMonitorData(attributes, "resolved-modify-conflicts",
-                   domain.getNumResolvedModifyConflicts());
-
-    /* get number of naming conflicts */
-    addMonitorData(attributes, "resolved-naming-conflicts",
-                   domain.getNumResolvedNamingConflicts());
-
-    /* get number of unresolved naming conflicts */
-    addMonitorData(attributes, "unresolved-naming-conflicts",
-                   domain.getNumUnresolvedNamingConflicts());
-
-    /* get server-id */
-    addMonitorData(attributes, "server-id",
-                   domain.getServerId());
-
-    /* get window information */
-    addMonitorData(attributes, "max-rcv-window", domain.getMaxRcvWindow());
-    addMonitorData(attributes, "current-rcv-window",
-                               domain.getCurrentRcvWindow());
-    addMonitorData(attributes, "max-send-window",
-                               domain.getMaxSendWindow());
-    addMonitorData(attributes, "current-send-window",
-                               domain.getCurrentSendWindow());
-
-    /* get the Server State */
-    final String ATTR_SERVER_STATE = "server-state";
-    AttributeType type =
-      DirectoryServer.getDefaultAttributeType(ATTR_SERVER_STATE);
-    AttributeBuilder builder = new AttributeBuilder(type, ATTR_SERVER_STATE);
-    for (String str : domain.getServerState().toStringSet())
-    {
-      builder.add(new AttributeValue(type,str));
-    }
-    attributes.add(builder.toAttribute());
-
-    attributes.add(Attributes.create("ssl-encryption",
-        String.valueOf(domain.isSessionEncrypted())));
-
-    attributes.add(Attributes.create("generation-id",
-        String.valueOf(domain.getGenerationId())));
-
-    return attributes;
-
-  }
-
-  /**
-   * Add an attribute with an integer value to the list of monitoring
-   * attributes.
-   *
-   * @param attributes the list of monitoring attributes
-   * @param name the name of the attribute to add.
-   * @param value The integer value of he attribute to add.
-   */
-  private void addMonitorData(ArrayList<Attribute> attributes, String name,
-      int value)
-  {
-    AttributeType type = DirectoryServer.getDefaultAttributeType(name);
-    attributes.add(Attributes.create(type, new AttributeValue(type,
-        String.valueOf(value))));
-  }
-
-  /**
-   * Retrieves the length of time in milliseconds that should elapse between
-   * calls to the <CODE>updateMonitorData()</CODE> method.  A negative or zero
-   * return value indicates that the <CODE>updateMonitorData()</CODE> method
-   * should not be periodically invoked.
-   *
-   * @return  The length of time in milliseconds that should elapse between
-   *          calls to the <CODE>updateMonitorData()</CODE> method.
-   */
-  @Override
-  public long getUpdateInterval()
-  {
-    /* we don't wont to do polling on this monitor */
-    return 0;
-  }
-
-  /**
-   * Performs any processing periodic processing that may be desired to update
-   * the information associated with this monitor.  Note that best-effort
-   * attempts will be made to ensure that calls to this method come
-   * <CODE>getUpdateInterval()</CODE> milliseconds apart, but no guarantees will
-   * be made.
-   */
-  @Override
-  public void updateMonitorData()
-  {
-    //  As long as getUpdateInterval() returns 0, this will never get called
-  }
-}
diff --git a/opends/src/server/org/opends/server/replication/plugin/UpdateToReplay.java b/opends/src/server/org/opends/server/replication/plugin/UpdateToReplay.java
index 55fcce6..beb9690 100644
--- a/opends/src/server/org/opends/server/replication/plugin/UpdateToReplay.java
+++ b/opends/src/server/org/opends/server/replication/plugin/UpdateToReplay.java
@@ -26,7 +26,7 @@
  */
 package org.opends.server.replication.plugin;
 
-import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
 
 /**
  * This is a bag class to hold an update to replay in the queue of updates to
@@ -36,8 +36,8 @@
  */
 public class UpdateToReplay
 {
-  private UpdateMsg updateMessage = null;
-  private ReplicationDomain replicationDomain = null;
+  private LDAPUpdateMsg updateMessage = null;
+  private LDAPReplicationDomain replicationDomain = null;
 
   /**
    * Construct the object associating the update message with the replication
@@ -46,8 +46,8 @@
    * @param replicationDomain The replication domain to use for replaying the
    * change from the update message
    */
-  public UpdateToReplay(UpdateMsg updateMessage,
-    ReplicationDomain replicationDomain)
+  public UpdateToReplay(LDAPUpdateMsg updateMessage,
+    LDAPReplicationDomain replicationDomain)
   {
     this.updateMessage = updateMessage;
     this.replicationDomain = replicationDomain;
@@ -57,7 +57,7 @@
    * Getter for update message.
    * @return The update message
    */
-  public UpdateMsg getUpdateMessage()
+  public LDAPUpdateMsg getUpdateMessage()
   {
     return updateMessage;
   }
@@ -66,7 +66,7 @@
    * Getter for replication domain.
    * @return The replication domain
    */
-  public ReplicationDomain getReplicationDomain()
+  public LDAPReplicationDomain getReplicationDomain()
   {
     return replicationDomain;
   }
diff --git a/opends/src/server/org/opends/server/replication/protocol/AckMsg.java b/opends/src/server/org/opends/server/replication/protocol/AckMsg.java
index 7f5d0e8..3b3f53e 100644
--- a/opends/src/server/org/opends/server/replication/protocol/AckMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/AckMsg.java
@@ -110,6 +110,42 @@
   }
 
   /**
+   * Sets the timeout marker for this message.
+   * @param hasTimeout True if some timeout occurred
+   */
+  public void setHasTimeout(boolean hasTimeout)
+  {
+    this.hasTimeout = hasTimeout;
+  }
+
+  /**
+   * Sets the wrong status marker for this message.
+   * @param hasWrongStatus True if some servers were in wrong status
+   */
+  public void setHasWrongStatus(boolean hasWrongStatus)
+  {
+    this.hasWrongStatus = hasWrongStatus;
+  }
+
+  /**
+   * Sets the replay error marker for this message.
+   * @param hasReplayError True if some servers had errors replaying the change
+   */
+  public void setHasReplayError(boolean hasReplayError)
+  {
+    this.hasReplayError = hasReplayError;
+  }
+
+  /**
+   * Sets the list of failing servers for this message.
+   * @param failedServers The list of failing servers for this message.
+   */
+  public void setFailedServers(List<Short> failedServers)
+  {
+    this.failedServers = failedServers;
+  }
+
+  /**
    * Creates a new AckMsg by decoding the provided byte array.
    *
    * @param in The byte array containing the encoded form of the AckMsg.
@@ -277,4 +313,35 @@
     return failedServers;
   }
 
+  /**
+   * Transforms the errors information of the ack into human readable string.
+   * @return A human readable string for errors embedded in the message.
+   */
+  public String errorsToString()
+  {
+    String idList = null;
+    if (failedServers.size() > 0)
+    {
+      idList = "[";
+      int size = failedServers.size();
+      for (int i=0 ; i<size ; i++) {
+        idList += failedServers.get(i);
+        if ( i != (size-1) )
+          idList += ", ";
+      }
+      idList += "]";
+    } else
+    {
+      idList="none";
+    }
+
+    String ackErrorStr = "hasTimeout: " + (hasTimeout ? "yes" : "no")  + ", " +
+      "hasWrongStatus: " + (hasWrongStatus ? "yes" : "no")  + ", " +
+      "hasReplayError: " + (hasReplayError ? "yes" : "no")  + ", " +
+      " concerned server ids: " + idList;
+
+    return ackErrorStr;
+  }
+
 }
+
diff --git a/opends/src/server/org/opends/server/replication/protocol/AddMsg.java b/opends/src/server/org/opends/server/replication/protocol/AddMsg.java
index 29059ad..972a40c 100644
--- a/opends/src/server/org/opends/server/replication/protocol/AddMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/AddMsg.java
@@ -58,7 +58,7 @@
  * This class is used to exchange Add operation between LDAP servers
  * and replication servers.
  */
-public class AddMsg extends UpdateMsg
+public class AddMsg extends LDAPUpdateMsg
 {
   private byte[] encodedAttributes;
   private String parentUniqueId;
diff --git a/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java b/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java
index fa8f1c3..df136f2 100644
--- a/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java
@@ -41,7 +41,7 @@
 /**
  * Object used when sending delete information to replication servers.
  */
-public class DeleteMsg extends UpdateMsg
+public class DeleteMsg extends LDAPUpdateMsg
 {
   /**
    * Creates a new delete message.
diff --git a/opends/src/server/org/opends/server/replication/protocol/EntryMsg.java b/opends/src/server/org/opends/server/replication/protocol/EntryMsg.java
index 6e54b2e..c05e2ae 100644
--- a/opends/src/server/org/opends/server/replication/protocol/EntryMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/EntryMsg.java
@@ -43,14 +43,39 @@
   /**
    * Creates a new EntryMsg.
    *
+   * @param sender      The sender of this message.
+   * @param destination The destination of this message.
+   * @param entryBytes  The bytes of the entry.
+   */
+  public EntryMsg(
+      short sender,
+      short destination,
+      byte[] entryBytes)
+  {
+    super(sender, destination);
+    this.entryByteArray = new byte[entryBytes.length];
+    System.arraycopy(entryBytes, 0, this.entryByteArray, 0, entryBytes.length);
+  }
+
+  /**
+   * Creates a new EntryMsg.
+   *
    * @param sender The sender of this message.
    * @param destination The destination of this message.
    * @param entryBytes The bytes of the entry.
+   * @param pos         The starting Position in the array.
+   * @param length      Number of array elements to be copied.
    */
-  public EntryMsg(short sender, short destination, byte[] entryBytes)
+  public EntryMsg(
+      short sender,
+      short destination,
+      byte[] entryBytes,
+      int pos,
+      int length)
   {
     super(sender, destination);
-    this.entryByteArray = entryBytes.clone();
+    this.entryByteArray = new byte[length];
+    System.arraycopy(entryBytes, pos, this.entryByteArray, 0, length);
   }
 
   /**
diff --git a/opends/src/server/org/opends/server/replication/plugin/HeartbeatMonitor.java b/opends/src/server/org/opends/server/replication/protocol/HeartbeatMonitor.java
similarity index 97%
rename from opends/src/server/org/opends/server/replication/plugin/HeartbeatMonitor.java
rename to opends/src/server/org/opends/server/replication/protocol/HeartbeatMonitor.java
index 08c0ed5..865d97c 100644
--- a/opends/src/server/org/opends/server/replication/plugin/HeartbeatMonitor.java
+++ b/opends/src/server/org/opends/server/replication/protocol/HeartbeatMonitor.java
@@ -25,7 +25,7 @@
  *      Copyright 2007-2008 Sun Microsystems, Inc.
  */
 
-package org.opends.server.replication.plugin;
+package org.opends.server.replication.protocol;
 
 import static org.opends.messages.ReplicationMessages.*;
 import static org.opends.server.loggers.ErrorLogger.logError;
@@ -37,7 +37,6 @@
 import java.io.IOException;
 
 import org.opends.server.api.DirectoryThread;
-import org.opends.server.replication.protocol.ProtocolSession;
 
 /**
  * This class implements a thread to monitor heartbeat messages from the
diff --git a/opends/src/server/org/opends/server/replication/protocol/InitializeRequestMsg.java b/opends/src/server/org/opends/server/replication/protocol/InitializeRequestMsg.java
index 668bb2d..ebe3adb 100644
--- a/opends/src/server/org/opends/server/replication/protocol/InitializeRequestMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/InitializeRequestMsg.java
@@ -49,10 +49,10 @@
    * @param destination destination of this message
    * @param senderID serverID of the server that will send this message
    */
-  public InitializeRequestMsg(DN baseDn, short senderID, short destination)
+  public InitializeRequestMsg(String baseDn, short senderID, short destination)
   {
     super(senderID, destination);
-    this.baseDn = baseDn.toNormalizedString();
+    this.baseDn = baseDn;
   }
 
   /**
diff --git a/opends/src/server/org/opends/server/replication/protocol/InitializeTargetMsg.java b/opends/src/server/org/opends/server/replication/protocol/InitializeTargetMsg.java
index 70a80e2..194c4ea 100644
--- a/opends/src/server/org/opends/server/replication/protocol/InitializeTargetMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/InitializeTargetMsg.java
@@ -29,9 +29,6 @@
 import java.io.UnsupportedEncodingException;
 import java.util.zip.DataFormatException;
 
-import org.opends.server.types.DN;
-import org.opends.server.types.DirectoryException;
-
 /**
  * This message is part of the replication protocol.
  * This message is sent by a server to one or several servers as the
@@ -59,12 +56,12 @@
    * @param requestorID The server that initiates this export.
    * @param entryCount The count of entries that will be sent.
    */
-  public InitializeTargetMsg(DN baseDN, short senderID,
+  public InitializeTargetMsg(String baseDN, short senderID,
       short destination, short requestorID, long entryCount)
   {
     super(senderID, destination);
     this.requestorID = requestorID;
-    this.baseDN = baseDN.toNormalizedString();
+    this.baseDN = baseDN;
     this.entryCount = entryCount;
   }
 
@@ -144,17 +141,9 @@
    *
    * @return the base DN
    */
-  public DN getBaseDN()
+  public String getBaseDN()
   {
-    if (baseDN == null)
-      return null;
-    try
-    {
-      return DN.decode(baseDN);
-    } catch (DirectoryException e)
-    {
-      return null;
-    }
+    return baseDN;
   }
 
   /**
diff --git a/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java b/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java
new file mode 100644
index 0000000..c761af5
--- /dev/null
+++ b/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java
@@ -0,0 +1,492 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2006-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.protocol;
+
+import java.io.UnsupportedEncodingException;
+import java.util.zip.DataFormatException;
+
+import org.opends.server.protocols.asn1.ASN1Exception;
+import org.opends.server.protocols.internal.InternalClientConnection;
+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.LDAPException;
+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;
+
+/**
+ * Abstract class that must be extended to define a message
+ * used for sending Updates between servers.
+ */
+public abstract class LDAPUpdateMsg extends UpdateMsg
+{
+  /**
+   * The DN on which the update was originally done.
+   */
+  protected String dn = null;
+
+  /**
+   * The uniqueId of the entry that was updated.
+   */
+  protected String uniqueId;
+
+  /**
+   * Creates a new UpdateMsg.
+   */
+  public LDAPUpdateMsg()
+  {
+  }
+
+  /**
+   * Creates a new UpdateMsg with the given informations.
+   *
+   * @param ctx The replication Context of the operation for which the
+   *            update message must be created,.
+   * @param dn The DN of the entry on which the change
+   *           that caused the creation of this object happened
+   */
+  public LDAPUpdateMsg(OperationContext ctx, String dn)
+  {
+    this.protocolVersion = ProtocolVersion.getCurrentVersion();
+    this.changeNumber = ctx.getChangeNumber();
+    this.uniqueId = ctx.getEntryUid();
+    this.dn = dn;
+  }
+
+  /**
+   * Creates a new UpdateMessage with the given informations.
+   *
+   * @param cn        The ChangeNumber of the operation for which the
+   *                  UpdateMessage is created.
+   * @param entryUUID The Unique identifier of the entry that is updated
+   *                  by the operation for which the UpdateMessage is created.
+   * @param dn        The DN of the entry on which the change
+   *                  that caused the creation of this object happened
+   */
+  public LDAPUpdateMsg(ChangeNumber cn, String entryUUID, String dn)
+  {
+    this.protocolVersion = ProtocolVersion.getCurrentVersion();
+    this.changeNumber = cn;
+    this.uniqueId = entryUUID;
+    this.dn = dn;
+  }
+
+  /**
+   * Generates an Update Message with the provided information.
+   *
+   * @param op The operation for which the message must be created.
+   * @return The generated message.
+   */
+  public static LDAPUpdateMsg generateMsg(PostOperationOperation op)
+  {
+    LDAPUpdateMsg msg = null;
+    switch (op.getOperationType())
+    {
+    case MODIFY :
+      msg = new ModifyMsg((PostOperationModifyOperation) op);
+      break;
+
+    case ADD:
+      msg = new AddMsg((PostOperationAddOperation) op);
+      break;
+
+    case DELETE :
+      msg = new DeleteMsg((PostOperationDeleteOperation) op);
+      break;
+
+    case MODIFY_DN :
+      msg = new ModifyDNMsg( (PostOperationModifyDNOperation) op);
+      break;
+    }
+
+    return msg;
+  }
+
+  /**
+   * Get the DN on which the operation happened.
+   *
+   * @return The DN on which the operations happened.
+   */
+  public String getDn()
+  {
+    return dn;
+  }
+
+  /**
+   * Set the DN.
+   * @param dn The dn that must now be used for this message.
+   */
+  public void setDn(String dn)
+  {
+    this.dn = dn;
+  }
+
+  /**
+   * Get the Unique Identifier of the entry on which the operation happened.
+   *
+   * @return The Unique Identifier of the entry on which the operation happened.
+   */
+  public String getUniqueId()
+  {
+    return uniqueId;
+  }
+
+
+  /**
+   * Create and Operation from the message.
+   *
+   * @param   conn connection to use when creating the message
+   * @return  the created Operation
+   * @throws  LDAPException In case of LDAP decoding exception.
+   * @throws  ASN1Exception In case of ASN1 decoding exception.
+   * @throws DataFormatException In case of bad msg format.
+   */
+  public AbstractOperation createOperation(InternalClientConnection conn)
+         throws LDAPException, ASN1Exception, DataFormatException
+  {
+    return createOperation(conn, dn);
+  }
+
+
+  /**
+   * Create and Operation from the message using the provided DN.
+   *
+   * @param   conn connection to use when creating the message.
+   * @param   newDn the DN to use when creating the operation.
+   * @return  the created Operation.
+   * @throws  LDAPException In case of LDAP decoding exception.
+   * @throws  ASN1Exception In case of ASN1 decoding exception.
+   * @throws DataFormatException In case of bad msg format.
+   */
+  public abstract AbstractOperation createOperation(
+         InternalClientConnection conn, String newDn)
+         throws LDAPException, ASN1Exception, DataFormatException;
+
+  /**
+   * Encode the common header for all the UpdateMsg. This uses the current
+   * protocol version.
+   *
+   * @param type the type of UpdateMsg to encode.
+   * @param additionalLength additional length needed to encode the remaining
+   *                         part of the UpdateMsg.
+   * @return a byte array containing the common header and enough space to
+   *         encode the remaining bytes of the UpdateMsg as was specified
+   *         by the additionalLength.
+   *         (byte array length = common header length + additionalLength)
+   * @throws UnsupportedEncodingException if UTF-8 is not supported.
+   */
+  public byte[] encodeHeader(byte type, int additionalLength)
+    throws UnsupportedEncodingException
+  {
+    byte[] byteDn = dn.getBytes("UTF-8");
+    byte[] changeNumberByte =
+      this.getChangeNumber().toString().getBytes("UTF-8");
+    byte[] byteEntryuuid = getUniqueId().getBytes("UTF-8");
+
+    /* The message header is stored in the form :
+     * <operation type><protocol version><changenumber><dn><entryuuid><assured>
+     * <assured mode> <safe data level>
+     * the length of result byte array is therefore :
+     *   1 + 1 + change number length + 1 + dn length + 1 + uuid length + 1 + 1
+     *   + 1 + 1 + additional_length
+     */
+    int length = 8 + changeNumberByte.length + byteDn.length
+                 + byteEntryuuid.length + additionalLength;
+
+    byte[] encodedMsg = new byte[length];
+
+    /* put the type of the operation */
+    encodedMsg[0] = type;
+
+    /* put the protocol version */
+    encodedMsg[1] = (byte)ProtocolVersion.getCurrentVersion();
+    int pos = 2;
+
+    /* Put the ChangeNumber */
+    pos = addByteArray(changeNumberByte, encodedMsg, pos);
+
+    /* Put the DN and a terminating 0 */
+    pos = addByteArray(byteDn, encodedMsg, pos);
+
+    /* Put the entry uuid and a terminating 0 */
+    pos = addByteArray(byteEntryuuid, encodedMsg, pos);
+
+    /* Put the assured flag */
+    encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0);
+
+    /* Put the assured mode */
+    encodedMsg[pos++] = assuredMode.getValue();
+
+    /* Put the safe data level */
+    encodedMsg[pos++] = safeDataLevel;
+
+    return encodedMsg;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes(short reqProtocolVersion)
+    throws UnsupportedEncodingException
+  {
+
+    // Using current protocol version should normally not be done as we would
+    // normally call the getBytes() method instead for that. So this check
+    // for security
+    if (reqProtocolVersion == ProtocolVersion.getCurrentVersion())
+    {
+      return getBytes();
+    }
+
+    switch (reqProtocolVersion)
+    {
+      case ProtocolVersion.REPLICATION_PROTOCOL_V1:
+        return getBytes_V1();
+      default:
+        // Unsupported requested version
+        throw new UnsupportedEncodingException(getClass().getSimpleName() +
+          " PDU does not support requested protocol version serialization: " +
+          reqProtocolVersion);
+    }
+  }
+
+  /**
+   * 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).
+   *
+   * @param type the type of UpdateMessage to encode.
+   * @param additionalLength additional length needed to encode the remaining
+   *                         part of the UpdateMessage.
+   * @return a byte array containing the common header and enough space to
+   *         encode the remaining bytes of the UpdateMessage as was specified
+   *         by the additionalLength.
+   *         (byte array length = common header length + additionalLength)
+   * @throws UnsupportedEncodingException if UTF-8 is not supported.
+   */
+  public byte[] encodeHeader_V1(byte type, int additionalLength)
+    throws UnsupportedEncodingException
+  {
+    byte[] byteDn = dn.getBytes("UTF-8");
+    byte[] changeNumberByte =
+      this.getChangeNumber().toString().getBytes("UTF-8");
+    byte[] byteEntryuuid = getUniqueId().getBytes("UTF-8");
+
+    /* The message header is stored in the form :
+     * <operation type><changenumber><dn><assured><entryuuid><change>
+     * the length of result byte array is therefore :
+     *   1 + change number length + 1 + dn length + 1  + 1 +
+     *   uuid length + 1 + additional_length
+     */
+    int length = 5 + changeNumberByte.length + byteDn.length
+                 + byteEntryuuid.length + additionalLength;
+
+    byte[] encodedMsg = new byte[length];
+
+    /* put the type of the operation */
+    encodedMsg[0] = type;
+    int pos = 1;
+
+    /* put the ChangeNumber */
+    pos = addByteArray(changeNumberByte, encodedMsg, pos);
+
+    /* put the assured information */
+    encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0);
+
+    /* put the DN and a terminating 0 */
+    pos = addByteArray(byteDn, encodedMsg, pos);
+
+    /* put the entry uuid and a terminating 0 */
+    pos = addByteArray(byteEntryuuid, encodedMsg, pos);
+
+    return encodedMsg;
+  }
+
+  /**
+   * Decode the Header part of this Update Message, and check its type.
+   *
+   * @param types The allowed types of this Update Message.
+   * @param encodedMsg the encoded form of the UpdateMsg.
+   * @return the position at which the remaining part of the message starts.
+   * @throws DataFormatException if the encodedMsg does not contain a valid
+   *         common header.
+   */
+  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]);
+
+    /*
+     * 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];
+
+    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 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 mode */
+      assuredMode = AssuredMode.valueOf(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());
+    }
+  }
+
+  /**
+   * Decode the Header part of this Update Message, and check its type. This
+   * uses the version 1 of the replication protocol (used for compatibility
+   * purpose).
+   *
+   * @param encodedMsg the encoded form of the UpdateMessage.
+   * @return the position at which the remaining part of the message starts.
+   * @throws DataFormatException if the encodedMsg does not contain a valid
+   *         common header.
+   */
+  public int decodeHeader_V1(byte[] encodedMsg)
+                          throws DataFormatException
+  {
+    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))
+      throw new DataFormatException("byte[] is not a valid update msg: expected"
+        + " a V1 PDU, received: " + encodedMsg[0]);
+
+    // Force version to V1 (other new parameters take their default values
+    // (assured stuff...))
+    protocolVersion = ProtocolVersion.REPLICATION_PROTOCOL_V1;
+
+    try
+    {
+      /* read the changeNumber */
+      int pos = 1;
+      int length = getNextLength(encodedMsg, pos);
+      String changenumberStr = new String(encodedMsg, pos, length, "UTF-8");
+      pos += length + 1;
+      changeNumber = new ChangeNumber(changenumberStr);
+
+      /* read the assured information */
+      if (encodedMsg[pos++] == 1)
+        assuredFlag = true;
+      else
+        assuredFlag = false;
+
+      /* 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;
+
+      return pos;
+    } catch (UnsupportedEncodingException e)
+    {
+      throw new DataFormatException("UTF-8 is not supported by this jvm.");
+    }
+  }
+
+  /**
+   * Return the number of bytes used by this message.
+   *
+   * @return The number of bytes used by this message.
+   */
+  public abstract int size();
+}
diff --git a/opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java b/opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java
index 5d1839a..0339bd7 100644
--- a/opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java
@@ -41,7 +41,7 @@
 /**
  * This class holds every common code for the modify messages (mod, moddn).
  */
-public abstract class ModifyCommonMsg extends UpdateMsg {
+public abstract class ModifyCommonMsg extends LDAPUpdateMsg {
 
   /**
    * The modifications kept encoded in the message.
diff --git a/opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java b/opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java
index 5bed97d..f1bdb60 100644
--- a/opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java
@@ -30,8 +30,6 @@
 import java.util.zip.DataFormatException;
 
 import org.opends.server.replication.common.ServerState;
-import org.opends.server.types.DirectoryException;
-import org.opends.server.types.DN;
 
 /**
  * Message sent by a replication server to another replication server
@@ -73,7 +71,7 @@
    * @param groupId The group id of the RS
    * @param degradedStatusThreshold The degraded status threshold
    */
-  public ReplServerStartMsg(short serverId, String serverURL, DN baseDn,
+  public ReplServerStartMsg(short serverId, String serverURL, String baseDn,
                                int windowSize,
                                ServerState serverState,
                                short protocolVersion,
@@ -86,7 +84,7 @@
     this.serverId = serverId;
     this.serverURL = serverURL;
     if (baseDn != null)
-      this.baseDn = baseDn.toNormalizedString();
+      this.baseDn = baseDn;
     else
       this.baseDn = null;
     this.windowSize = windowSize;
@@ -202,17 +200,9 @@
    *
    * @return the base DN from this ReplServerStartMsg.
    */
-  public DN getBaseDn()
+  public String getBaseDn()
   {
-    if (baseDn == null)
-      return null;
-    try
-    {
-      return DN.decode(baseDn);
-    } catch (DirectoryException e)
-    {
-      return null;
-    }
+    return baseDn;
   }
 
   /**
@@ -333,8 +323,8 @@
     return "ReplServerStartMsg content: " +
       "\nprotocolVersion: " + protocolVersion +
       "\ngenerationId: " + generationId +
+      "\nbaseDn: " + baseDn +
       "\ngroupId: " + groupId +
-      "\nbaseDn: " + baseDn.toString() +
       "\nserverId: " + serverId +
       "\nserverState: " + serverState +
       "\nserverURL: " + serverURL +
diff --git a/opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java b/opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java
index a94d979..778ad65 100644
--- a/opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java
+++ b/opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java
@@ -31,8 +31,6 @@
 import static org.opends.messages.ReplicationMessages.*;
 
 import org.opends.messages.Message;
-import org.opends.server.admin.std.server.ReplicationServerCfg;
-import org.opends.server.admin.std.server.ReplicationDomainCfg;
 import org.opends.server.types.DirectoryConfig;
 import org.opends.server.types.CryptoManager;
 import org.opends.server.config.ConfigException;
@@ -132,32 +130,12 @@
   }
 
   /**
-   * Create a ReplSessionSecurity instance from a provided replication server
-   * configuration.
-   *
-   * @param replServerCfg The replication server configuration.
-   *
-   * @throws ConfigException If the supplied configuration was not valid.
-   */
-  public ReplSessionSecurity(ReplicationServerCfg replServerCfg)
-    throws ConfigException
-  {
-    // Currently use global settings from the crypto manager.
-    this(DirectoryConfig.getCryptoManager().getSslCertNickname(),
-      DirectoryConfig.getCryptoManager().getSslProtocols(),
-      DirectoryConfig.getCryptoManager().getSslCipherSuites(),
-      DirectoryConfig.getCryptoManager().isSslEncryption());
-  }
-
-  /**
    * Create a ReplSessionSecurity instance from a provided multimaster domain
    * configuration.
    *
-   * @param multimasterDomainCfg The multimaster domain configuration.
-   *
    * @throws ConfigException If the supplied configuration was not valid.
    */
-  public ReplSessionSecurity(ReplicationDomainCfg multimasterDomainCfg)
+  public ReplSessionSecurity()
     throws ConfigException
   {
     // Currently use global settings from the crypto manager.
diff --git a/opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java b/opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java
index 5a0decd..b65af58 100644
--- a/opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java
@@ -69,6 +69,7 @@
   static final byte MSG_TYPE_TOPOLOGY = 26;
   static final byte MSG_TYPE_START_SESSION = 27;
   static final byte MSG_TYPE_CHANGE_STATUS = 28;
+  static final byte MSG_TYPE_GENERIC_UPDATE = 29;
 
   // Adding a new type of message here probably requires to
   // change accordingly generateMsg method below
@@ -209,6 +210,9 @@
       case MSG_TYPE_CHANGE_STATUS:
         msg = new ChangeStatusMsg(buffer);
       break;
+      case MSG_TYPE_GENERIC_UPDATE:
+        msg = new UpdateMsg(buffer);
+      break;
       default:
         throw new DataFormatException("received message with unknown type");
     }
@@ -224,7 +228,8 @@
    * @param pos the position where to concatenate.
    * @return the next position to use in the resultByteArray.
    */
-  protected int addByteArray(byte[] tail, byte[] resultByteArray, int pos)
+  protected static int addByteArray(byte[] tail, byte[] resultByteArray,
+    int pos)
   {
     for (int i=0; i<tail.length; i++,pos++)
     {
diff --git a/opends/src/server/org/opends/server/replication/protocol/ServerStartMsg.java b/opends/src/server/org/opends/server/replication/protocol/ServerStartMsg.java
index 8e8496b..f5679cc 100644
--- a/opends/src/server/org/opends/server/replication/protocol/ServerStartMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/ServerStartMsg.java
@@ -32,8 +32,6 @@
 import java.util.zip.DataFormatException;
 
 import org.opends.server.replication.common.ServerState;
-import org.opends.server.types.DN;
-import org.opends.server.types.DirectoryException;
 
 /**
  * This message is used by LDAP server when they first connect.
@@ -85,7 +83,7 @@
    *                      after the start messages have been exchanged.
    * @param groupId The group id of the DS for this DN
    */
-  public ServerStartMsg(short serverId, DN baseDn, int maxReceiveDelay,
+  public ServerStartMsg(short serverId, String baseDn, int maxReceiveDelay,
                             int maxReceiveQueue, int maxSendDelay,
                             int maxSendQueue, int windowSize,
                             long heartbeatInterval,
@@ -98,7 +96,7 @@
     super(protocolVersion, generationId);
 
     this.serverId = serverId;
-    this.baseDn = baseDn.toString();
+    this.baseDn = baseDn;
     this.maxReceiveDelay = maxReceiveDelay;
     this.maxReceiveQueue = maxReceiveQueue;
     this.maxSendDelay = maxSendDelay;
@@ -243,15 +241,9 @@
    * Get the baseDn.
    * @return Returns the baseDn.
    */
-  public DN getBaseDn()
+  public String getBaseDn()
   {
-    try
-    {
-      return DN.decode(baseDn);
-    } catch (DirectoryException e)
-    {
-      return null;
-    }
+    return baseDn;
   }
 
   /**
diff --git a/opends/src/server/org/opends/server/replication/protocol/UpdateMsg.java b/opends/src/server/org/opends/server/replication/protocol/UpdateMsg.java
index a2ce835..ca50458 100644
--- a/opends/src/server/org/opends/server/replication/protocol/UpdateMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/UpdateMsg.java
@@ -26,26 +26,17 @@
  */
 package org.opends.server.replication.protocol;
 
-import java.io.UnsupportedEncodingException;
 import java.util.zip.DataFormatException;
 
-import org.opends.server.protocols.asn1.ASN1Exception;
-import org.opends.server.protocols.internal.InternalClientConnection;
+import java.io.UnsupportedEncodingException;
 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.LDAPException;
-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;
 
 /**
  * Abstract class that must be extended to define a message
  * used for sending Updates between servers.
  */
-public abstract class UpdateMsg extends ReplicationMsg
+public class UpdateMsg extends ReplicationMsg
                                     implements Comparable<UpdateMsg>
 {
   /**
@@ -59,11 +50,6 @@
   protected ChangeNumber changeNumber;
 
   /**
-   * The DN on which the update was originally done.
-   */
-  protected String dn = null;
-
-  /**
    * True when the update must use assured replication.
    */
   protected boolean assuredFlag = false;
@@ -76,83 +62,62 @@
   /**
    * When assured mode is safe data, gives the requested level.
    */
-  protected byte safeDataLevel = (byte)-1;
+  protected byte safeDataLevel = (byte)1;
 
   /**
-   * The uniqueId of the entry that was updated.
+   * The payload that must be encoded in this message.
    */
-  protected String uniqueId;
+  private byte[] payload;
+
 
   /**
-   * Creates a new UpdateMsg.
+   * Creates a new empty UpdateMsg.
    */
-  public UpdateMsg()
+  protected UpdateMsg()
+  {}
+
+  /**
+   * Creates a new UpdateMsg with the given informations.
+   *
+   * @param bytes A Byte Array with the encoded form of the message.
+   *
+   * @throws DataFormatException If bytes is not valid.
+   */
+  UpdateMsg(byte[] bytes) throws DataFormatException
   {
+    // Decode header
+    int pos = decodeHeader(MSG_TYPE_GENERIC_UPDATE, bytes);
+
+    /* Read the payload : all the remaining bytes but the terminating 0 */
+    int length = bytes.length - pos;
+    payload = new byte[length];
+    try
+    {
+      System.arraycopy(bytes, pos, payload, 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());
+    }
   }
 
   /**
    * Creates a new UpdateMsg with the given informations.
    *
-   * @param ctx The replication Context of the operation for which the
-   *            update message must be created,.
-   * @param dn The DN of the entry on which the change
-   *           that caused the creation of this object happened
+   * @param changeNumber  The ChangeNumber associated with the change
+   *                      encoded in this message.
+   * @param payload       The payload that must be encoded in this message.
    */
-  public UpdateMsg(OperationContext ctx, String dn)
+  public UpdateMsg(ChangeNumber changeNumber, byte[] payload)
   {
+    this.payload = payload;
     this.protocolVersion = ProtocolVersion.getCurrentVersion();
-    this.changeNumber = ctx.getChangeNumber();
-    this.uniqueId = ctx.getEntryUid();
-    this.dn = dn;
-  }
-
-  /**
-   * Creates a new UpdateMessage with the given informations.
-   *
-   * @param cn        The ChangeNumber of the operation for which the
-   *                  UpdateMessage is created.
-   * @param entryUUID The Unique identifier of the entry that is updated
-   *                  by the operation for which the UpdateMessage is created.
-   * @param dn        The DN of the entry on which the change
-   *                  that caused the creation of this object happened
-   */
-  public UpdateMsg(ChangeNumber cn, String entryUUID, String dn)
-  {
-    this.protocolVersion = ProtocolVersion.getCurrentVersion();
-    this.changeNumber = cn;
-    this.uniqueId = entryUUID;
-    this.dn = dn;
-  }
-
-  /**
-   * Generates an Update Message with the provided information.
-   *
-   * @param op The operation for which the message must be created.
-   * @return The generated message.
-   */
-  public static UpdateMsg generateMsg(PostOperationOperation op)
-  {
-    UpdateMsg msg = null;
-    switch (op.getOperationType())
-    {
-    case MODIFY :
-      msg = new ModifyMsg((PostOperationModifyOperation) op);
-      break;
-
-    case ADD:
-      msg = new AddMsg((PostOperationAddOperation) op);
-      break;
-
-    case DELETE :
-      msg = new DeleteMsg((PostOperationDeleteOperation) op);
-      break;
-
-    case MODIFY_DN :
-      msg = new ModifyDNMsg( (PostOperationModifyDNOperation) op);
-      break;
-    }
-
-    return msg;
+    this.changeNumber = changeNumber;
   }
 
   /**
@@ -165,35 +130,6 @@
   }
 
   /**
-   * Get the DN on which the operation happened.
-   *
-   * @return The DN on which the operations happened.
-   */
-  public String getDn()
-  {
-    return dn;
-  }
-
-  /**
-   * Set the DN.
-   * @param dn The dn that must now be used for this message.
-   */
-  public void setDn(String dn)
-  {
-    this.dn = dn;
-  }
-
-  /**
-   * Get the Unique Identifier of the entry on which the operation happened.
-   *
-   * @return The Unique Identifier of the entry on which the operation happened.
-   */
-  public String getUniqueId()
-  {
-    return uniqueId;
-  }
-
-  /**
    * Get a boolean indicating if the Update must be processed as an
    * Asynchronous or as an assured replication.
    *
@@ -250,96 +186,6 @@
     return changeNumber.compareTo(msg.getChangeNumber());
   }
 
-  /**
-   * Create and Operation from the message.
-   *
-   * @param   conn connection to use when creating the message
-   * @return  the created Operation
-   * @throws  LDAPException In case of LDAP decoding exception.
-   * @throws  ASN1Exception In case of ASN1 decoding exception.
-   * @throws DataFormatException In case of bad msg format.
-   */
-  public AbstractOperation createOperation(InternalClientConnection conn)
-         throws LDAPException, ASN1Exception, DataFormatException
-  {
-    return createOperation(conn, dn);
-  }
-
-
-  /**
-   * Create and Operation from the message using the provided DN.
-   *
-   * @param   conn connection to use when creating the message.
-   * @param   newDn the DN to use when creating the operation.
-   * @return  the created Operation.
-   * @throws  LDAPException In case of LDAP decoding exception.
-   * @throws  ASN1Exception In case of ASN1 decoding exception.
-   * @throws DataFormatException In case of bad msg format.
-   */
-  public abstract AbstractOperation createOperation(
-         InternalClientConnection conn, String newDn)
-         throws LDAPException, ASN1Exception, DataFormatException;
-
-  /**
-   * Encode the common header for all the UpdateMsg. This uses the current
-   * protocol version.
-   *
-   * @param type the type of UpdateMsg to encode.
-   * @param additionalLength additional length needed to encode the remaining
-   *                         part of the UpdateMsg.
-   * @return a byte array containing the common header and enough space to
-   *         encode the remaining bytes of the UpdateMsg as was specified
-   *         by the additionalLength.
-   *         (byte array length = common header length + additionalLength)
-   * @throws UnsupportedEncodingException if UTF-8 is not supported.
-   */
-  public byte[] encodeHeader(byte type, int additionalLength)
-    throws UnsupportedEncodingException
-  {
-    byte[] byteDn = dn.getBytes("UTF-8");
-    byte[] changeNumberByte =
-      this.getChangeNumber().toString().getBytes("UTF-8");
-    byte[] byteEntryuuid = getUniqueId().getBytes("UTF-8");
-
-    /* The message header is stored in the form :
-     * <operation type><protocol version><changenumber><dn><entryuuid><assured>
-     * <assured mode> <safe data level>
-     * the length of result byte array is therefore :
-     *   1 + 1 + change number length + 1 + dn length + 1 + uuid length + 1 + 1
-     *   + 1 + 1 + additional_length
-     */
-    int length = 8 + changeNumberByte.length + byteDn.length
-                 + byteEntryuuid.length + additionalLength;
-
-    byte[] encodedMsg = new byte[length];
-
-    /* put the type of the operation */
-    encodedMsg[0] = type;
-
-    /* put the protocol version */
-    encodedMsg[1] = (byte)ProtocolVersion.getCurrentVersion();
-    int pos = 2;
-
-    /* Put the ChangeNumber */
-    pos = addByteArray(changeNumberByte, encodedMsg, pos);
-
-    /* Put the DN and a terminating 0 */
-    pos = addByteArray(byteDn, encodedMsg, pos);
-
-    /* Put the entry uuid and a terminating 0 */
-    pos = addByteArray(byteEntryuuid, encodedMsg, pos);
-
-    /* Put the assured flag */
-    encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0);
-
-    /* Put the assured mode */
-    encodedMsg[pos++] = assuredMode.getValue();
-
-    /* Put the safe data level */
-    encodedMsg[pos++] = safeDataLevel;
-
-    return encodedMsg;
-  }
 
   /**
    * {@inheritDoc}
@@ -353,226 +199,15 @@
     {
       return getBytes();
     }
-
-    // Supported older protocol versions
-    switch (reqProtocolVersion)
+    else
     {
-      case ProtocolVersion.REPLICATION_PROTOCOL_V1:
-        return getBytes_V1();
-      default:
-        // Unsupported requested version
-        throw new UnsupportedEncodingException(getClass().getSimpleName() +
+      throw new UnsupportedEncodingException(getClass().getSimpleName() +
           " PDU does not support requested protocol version serialization: " +
           reqProtocolVersion);
     }
   }
 
   /**
-   * 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).
-   *
-   * @param type the type of UpdateMessage to encode.
-   * @param additionalLength additional length needed to encode the remaining
-   *                         part of the UpdateMessage.
-   * @return a byte array containing the common header and enough space to
-   *         encode the remaining bytes of the UpdateMessage as was specified
-   *         by the additionalLength.
-   *         (byte array length = common header length + additionalLength)
-   * @throws UnsupportedEncodingException if UTF-8 is not supported.
-   */
-  public byte[] encodeHeader_V1(byte type, int additionalLength)
-    throws UnsupportedEncodingException
-  {
-    byte[] byteDn = dn.getBytes("UTF-8");
-    byte[] changeNumberByte =
-      this.getChangeNumber().toString().getBytes("UTF-8");
-    byte[] byteEntryuuid = getUniqueId().getBytes("UTF-8");
-
-    /* The message header is stored in the form :
-     * <operation type>changenumber><dn><assured><entryuuid><change>
-     * the length of result byte array is therefore :
-     *   1 + change number length + 1 + dn length + 1  + 1 +
-     *   uuid length + 1 + additional_length
-     */
-    int length = 5 + changeNumberByte.length + byteDn.length
-                 + byteEntryuuid.length + additionalLength;
-
-    byte[] encodedMsg = new byte[length];
-
-    /* put the type of the operation */
-    encodedMsg[0] = type;
-    int pos = 1;
-
-    /* put the ChangeNumber */
-    pos = addByteArray(changeNumberByte, encodedMsg, pos);
-
-    /* put the assured information */
-    encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0);
-
-    /* put the DN and a terminating 0 */
-    pos = addByteArray(byteDn, encodedMsg, pos);
-
-    /* put the entry uuid and a terminating 0 */
-    pos = addByteArray(byteEntryuuid, encodedMsg, pos);
-
-    return encodedMsg;
-  }
-
-  /**
-   * Decode the Header part of this Update Message, and check its type.
-   *
-   * @param types The allowed types of this Update Message.
-   * @param encodedMsg the encoded form of the UpdateMsg.
-   * @return the position at which the remaining part of the message starts.
-   * @throws DataFormatException if the encodedMsg does not contain a valid
-   *         common header.
-   */
-  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]);
-
-    /*
-     * 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];
-
-    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 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 mode */
-      assuredMode = AssuredMode.valueOf(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());
-    }
-  }
-
-  /**
-   * Decode the Header part of this Update Message, and check its type. This
-   * uses the version 1 of the replication protocol (used for compatibility
-   * purpose).
-   *
-   * @param encodedMsg the encoded form of the UpdateMessage.
-   * @return the position at which the remaining part of the message starts.
-   * @throws DataFormatException if the encodedMsg does not contain a valid
-   *         common header.
-   */
-  public int decodeHeader_V1(byte[] encodedMsg)
-                          throws DataFormatException
-  {
-    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))
-      throw new DataFormatException("byte[] is not a valid update msg: expected"
-        + " a V1 PDU, received: " + encodedMsg[0]);
-
-    // Force version to V1 (other new parameters take their default values
-    // (assured stuff...))
-    protocolVersion = ProtocolVersion.REPLICATION_PROTOCOL_V1;
-
-    try
-    {
-      /* read the changeNumber */
-      int pos = 1;
-      int length = getNextLength(encodedMsg, pos);
-      String changenumberStr = new String(encodedMsg, pos, length, "UTF-8");
-      pos += length + 1;
-      changeNumber = new ChangeNumber(changenumberStr);
-
-      /* read the assured information */
-      if (encodedMsg[pos++] == 1)
-        assuredFlag = true;
-      else
-        assuredFlag = false;
-
-      /* 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;
-
-      return pos;
-    } catch (UnsupportedEncodingException e)
-    {
-      throw new DataFormatException("UTF-8 is not supported by this jvm.");
-    }
-  }
-
-  /**
    * Get the assured mode in this message.
    * @return The assured mode in this message
    */
@@ -626,5 +261,144 @@
    *
    * @return The number of bytes used by this message.
    */
-  public abstract int size();
+  public int size()
+  {
+    return 10 + payload.length;
+  }
+
+  /**
+   * Encode the common header for all the UpdateMsg. This uses the current
+   * protocol version.
+   *
+   * @param type the type of UpdateMsg to encode.
+   * @param additionalLength additional length needed to encode the remaining
+   *                         part of the UpdateMsg.
+   * @return a byte array containing the common header and enough space to
+   *         encode the remaining bytes of the UpdateMsg as was specified
+   *         by the additionalLength.
+   *         (byte array length = common header length + additionalLength)
+   * @throws UnsupportedEncodingException if UTF-8 is not supported.
+   */
+  protected byte[] encodeHeader(byte type, int additionalLength)
+    throws UnsupportedEncodingException
+  {
+    byte[] changeNumberByte =
+      this.getChangeNumber().toString().getBytes("UTF-8");
+
+    /* The message header is stored in the form :
+     * <operation type><protocol version><changenumber><assured>
+     * <assured mode> <safe data level>
+     * the length of result byte array is therefore :
+     *   1 + 1 + change number length + 1 + 1
+     *   + 1 + 1 + additional_length
+     */
+    int length = 6 + changeNumberByte.length + additionalLength;
+
+    byte[] encodedMsg = new byte[length];
+
+    /* put the type of the operation */
+    encodedMsg[0] = type;
+
+    /* put the protocol version */
+    encodedMsg[1] = (byte)ProtocolVersion.getCurrentVersion();
+    int pos = 2;
+
+    /* Put the ChangeNumber */
+    pos = addByteArray(changeNumberByte, encodedMsg, pos);
+
+    /* Put the assured flag */
+    encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0);
+
+    /* Put the assured mode */
+    encodedMsg[pos++] = assuredMode.getValue();
+
+    /* Put the safe data level */
+    encodedMsg[pos++] = safeDataLevel;
+
+    return encodedMsg;
+  }
+
+  /**
+   * Decode the Header part of this Update Message, and check its type.
+   *
+   * @param type The allowed type of this Update Message.
+   * @param encodedMsg the encoded form of the UpdateMsg.
+   * @return the position at which the remaining part of the message starts.
+   * @throws DataFormatException if the encodedMsg does not contain a valid
+   *         common header.
+   */
+  protected int decodeHeader(byte type, byte[] encodedMsg)
+                          throws DataFormatException
+  {
+    /* The message header is stored in the form :
+     * <operation type><protocol version><changenumber><assured>
+     * <assured mode> <safe data level>
+     */
+    if (!(type == encodedMsg[0]))
+      throw new DataFormatException("byte[] is not a valid update msg: "
+        + encodedMsg[0]);
+
+    /* 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);
+
+      /* Read the assured information */
+      if (encodedMsg[pos++] == 1)
+        assuredFlag = true;
+      else
+        assuredFlag = false;
+
+      /* Read the assured mode */
+      assuredMode = AssuredMode.valueOf(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());
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes() throws UnsupportedEncodingException
+  {
+    /* Encode the header in a byte[] large enough to also contain the payload */
+    byte [] resultByteArray =
+      encodeHeader(MSG_TYPE_GENERIC_UPDATE, payload.length);
+
+    int pos = resultByteArray.length - payload.length;
+
+    /* Add the payload */
+    for (int i=0; i<payload.length; i++,pos++)
+    {
+      resultByteArray[pos] = payload[i];
+    }
+    return resultByteArray;
+  }
+
+  /**
+   * Get the payload of the UpdateMsg.
+   *
+   * @return The payload of the UpdateMsg.
+   */
+  public byte[] getPayload()
+  {
+    return payload;
+  }
 }
diff --git a/opends/src/server/org/opends/server/replication/protocol/WindowMsg.java b/opends/src/server/org/opends/server/replication/protocol/WindowMsg.java
index 7407f1c..57cbac0 100644
--- a/opends/src/server/org/opends/server/replication/protocol/WindowMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/WindowMsg.java
@@ -137,7 +137,6 @@
   @Override
   public String toString()
   {
-    return "ServerStartMsg content: " +
-      "\nnumAck: " + numAck;
+    return "WindowMsg : " + "numAck: " + numAck;
   }
 }
diff --git a/opends/src/server/org/opends/server/replication/server/AckMessageList.java b/opends/src/server/org/opends/server/replication/server/AckMessageList.java
deleted file mode 100644
index f085294..0000000
--- a/opends/src/server/org/opends/server/replication/server/AckMessageList.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
- */
-package org.opends.server.replication.server;
-
-import java.util.LinkedList;
-
-import org.opends.server.replication.common.ChangeNumber;
-
-/**
- * This class is used to store the list of acks received for
- * a Given Update Messages.
- *
- * The acks are kept only for the update that are marked, hopefully this
- * should be a limited number of updates and in all cases, LDAP servers
- * operations are going to be blocked waiting for these acks so they
- * won't be able to generate a huge number of such messages.
- *
- * Therefore, the amount of memory used keeping those changes is not a problem,
- */
-public class AckMessageList
-{
-  // The ChangeNumber of the updates that was acked
-  // or that is waiting acks
-  private ChangeNumber changeNumber;
-
-  // The list of serverIdentifiers for which acks have been received so far
-  // can be empty when no acks have been received
-  private LinkedList<Short> acks;
-
-  private int numExpectedAcks;
-
-  /**
-   * Creates a new AckMessageList for a given ChangeNumber.
-   *
-   * @param changeNumber The ChangeNumber for which the ack list is created.
-   * @param numExpectedAcks The number of acks waited before acking the
-   *                        original change.
-   */
-  public AckMessageList(ChangeNumber changeNumber, int numExpectedAcks)
-  {
-    acks = new LinkedList<Short>();
-    this.changeNumber = changeNumber;
-    this.numExpectedAcks = numExpectedAcks;
-  }
-
-  /**
-   * Get the ChangeNumber of this Ack Message List.
-   * @return Returns the changeNumber.
-   */
-  public ChangeNumber getChangeNumber()
-  {
-    return changeNumber;
-  }
-
-  /**
-   * Add an ack from a given LDAP server to the ack list.
-   *
-   * @param serverId the identifier of the LDAP server.
-   */
-  public synchronized void addAck(short serverId)
-  {
-    acks.add(serverId);
-  }
-
-  /**
-   * This method can be used to check if all acks have been received for the
-   * ChangeNumber managed by this list.
-   * @return A boolean indicating if all acks have been received for the
-   *         ChangeNumber managed by this list.
-   */
-  public boolean completed()
-  {
-    return (acks.size() >= numExpectedAcks);
-  }
-}
diff --git a/opends/src/server/org/opends/server/replication/server/DbHandler.java b/opends/src/server/org/opends/server/replication/server/DbHandler.java
index b16aa28..097638d 100644
--- a/opends/src/server/org/opends/server/replication/server/DbHandler.java
+++ b/opends/src/server/org/opends/server/replication/server/DbHandler.java
@@ -43,7 +43,6 @@
 import org.opends.server.config.ConfigException;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.Attributes;
-import org.opends.server.types.DN;
 import org.opends.server.types.InitializationException;
 import org.opends.server.util.TimeThread;
 import org.opends.server.core.DirectoryServer;
@@ -102,7 +101,7 @@
   private ChangeNumber firstChange = null;
   private ChangeNumber lastChange = null;
   private short serverId;
-  private DN baseDn;
+  private String baseDn;
   private DbMonitorProvider dbMonitor = new DbMonitorProvider();
   private boolean shutdown = false;
   private boolean done = false;
@@ -133,7 +132,7 @@
    * @throws DatabaseException If a database problem happened
    */
   public DbHandler(
-      short id, DN baseDn, ReplicationServer replicationServer,
+      short id, String baseDn, ReplicationServer replicationServer,
       ReplicationDbEnv dbenv, int queueSize)
          throws DatabaseException
   {
diff --git a/opends/src/server/org/opends/server/replication/server/ExpectedAcksInfo.java b/opends/src/server/org/opends/server/replication/server/ExpectedAcksInfo.java
new file mode 100644
index 0000000..296e499
--- /dev/null
+++ b/opends/src/server/org/opends/server/replication/server/ExpectedAcksInfo.java
@@ -0,0 +1,141 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.replication.server;
+
+import org.opends.server.replication.common.AssuredMode;
+import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.replication.protocol.AckMsg;
+
+/**
+ * This class is the mother class for sub-classes holding any information needed
+ * about the acks that the replication server will wait for, when he receives an
+ * update message with the assured flag on (assured replication acknowledgments
+ * expected).
+ * It also includes info/routines for constructing the final ack to be sent to
+ * the sender of the update message.
+ *
+ * It is expected to have one sub-class per assured replication sub mode.
+ */
+public abstract class ExpectedAcksInfo
+{
+  // The server handler of the server that sent the assured update message and
+  // to whow we want to return the final ack
+  private ServerHandler requesterServerHandler = null;
+
+  // The requested assured mode of matcching update message
+  private AssuredMode assuredMode = null;
+
+  /**
+   * The change number of the assured update message we want acks for.
+   */
+  protected ChangeNumber changeNumber = null;
+
+  /**
+   * Is the treatment of the acks for the update message completed or not ?
+   * This is used for concurrent access to this object by either the assured
+   * timeout task or the code for processing an ack for the matching update
+   * message. This should be set to true when the treatment of the expected
+   * acks is completed or an ack timeout has occured and we are going to remove
+   * this object from the map where it is stored.
+   */
+  private boolean completed = false;
+
+  /**
+   * Creates a new ExpectedAcksInfo.
+   * @param changeNumber The change number of the assured update message
+   * @param requesterServerHandler The server handler of the server that sent
+   * the assured update message
+   * @param assuredMode The assured mode requested by the assured update message
+   */
+  protected ExpectedAcksInfo(ChangeNumber changeNumber,
+    ServerHandler requesterServerHandler, AssuredMode assuredMode)
+  {
+    this.requesterServerHandler = requesterServerHandler;
+    this.assuredMode = assuredMode;
+    this.changeNumber = changeNumber;
+  }
+
+  /**
+   * Gets the server handler of the server which requested the acknowledgments.
+   * @return The server handler of the server which requested the
+   * acknowledgments.
+   */
+  public ServerHandler getRequesterServer()
+  {
+    return requesterServerHandler;
+  }
+
+  /**
+   * Gets the requested assured mode for the matching update message.
+   * @return The requested assured mode for the matching update message.
+   */
+  public AssuredMode getAssuredMode()
+  {
+    return assuredMode;
+  }
+
+  /**
+   * Process the received ack from a server we are waiting an ack from.
+   * @param ackingServer The server handler of the server that sent the ack
+   * @param ackMsg The ack message to process
+   * @return True if the expected number of acks has just been reached
+   */
+  public abstract boolean processReceivedAck(ServerHandler ackingServer,
+    AckMsg ackMsg);
+
+  /**
+   * Creates the ack message to be returned to the requester server, taking into
+   * account the information in the received acks from every servers.
+   * @param timeout True if we call this method when the timeout occurred, that
+   * is we did not received every expected acks in time, and thus, the timeout
+   * flag should also be enabled in the returned ack message.
+   * @return The ack message ready to be sent to the requester server
+   */
+  public abstract AckMsg createAck(boolean timeout);
+
+  /**
+   * Has the treatment of this object been completed or not?
+   * If true is returned, one must not modify this object (useless) nor remove
+   * it from the map where it is stored (will be or has already been done by the
+   * other code (ack timeout code, or ack processing code)).
+   * @return True if treatment of this object has been completed.
+   */
+  public boolean isCompleted()
+  {
+    return completed;
+  }
+
+  /**
+   * Signal that treatment of this object has been completed and that it is
+   * going to be removed from the map where it is stored.
+   */
+  public void completed()
+  {
+    completed = true;
+  }
+}
diff --git a/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java b/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java
index 19a4671..ccbf43f 100644
--- a/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java
+++ b/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java
@@ -254,7 +254,7 @@
     attributes.add(Attributes.create("server-id",
         String.valueOf(serverId)));
     attributes.add(Attributes.create("base-dn",
-        rsDomain.getBaseDn().toNormalizedString()));
+        rsDomain.getBaseDn()));
     attributes.add(Attributes.create("connected-to",
         replServerHandler.getMonitorInstanceName()));
 
diff --git a/opends/src/server/org/opends/server/replication/server/MonitorData.java b/opends/src/server/org/opends/server/replication/server/MonitorData.java
index 0801257..25b9992 100644
--- a/opends/src/server/org/opends/server/replication/server/MonitorData.java
+++ b/opends/src/server/org/opends/server/replication/server/MonitorData.java
@@ -69,6 +69,10 @@
   private ConcurrentHashMap<Short, ServerState> LDAPStates =
     new ConcurrentHashMap<Short, ServerState>();
 
+  // A Map containing the ServerStates of each RS.
+  private ConcurrentHashMap<Short, ServerState> RSStates =
+    new ConcurrentHashMap<Short, ServerState>();
+
   // For each LDAP server, the last(max) CN it published
   private ConcurrentHashMap<Short, ChangeNumber> maxCNs =
     new ConcurrentHashMap<Short, ChangeNumber>();
@@ -86,6 +90,9 @@
   private ConcurrentHashMap<Short, Long> fmRSDate =
     new ConcurrentHashMap<Short, Long>();
 
+  private ConcurrentHashMap<Short, Long> missingChangesRS =
+    new ConcurrentHashMap<Short, Long>();
+
 
   /**
    * Get an approximation of the latency delay of the replication.
@@ -129,13 +136,29 @@
   }
 
   /**
+   * Get the number of missing changes for a Replication Server.
+   *
+   * @param serverId   The server ID.
+   *
+   * @return           The number of missing changes.
+   */
+  public long getMissingChangesRS(short serverId)
+  {
+    Long res = missingChangesRS.get(serverId);
+    if (res==null)
+      return 0;
+    else
+      return res;
+  }
+
+  /**
    * Build the monitor data that are computed from the collected ones.
    */
   public void completeComputing()
   {
     String mds = "";
 
-    // Computes the missing changes counters
+    // Computes the missing changes counters for LDAP servers
     // For each LSi ,
     //   Regarding each other LSj
     //    Sum the difference : max(LSj) - state(LSi)
@@ -167,6 +190,36 @@
       }
       mds += "=" + lsiMissingChanges;
       this.missingChanges.put(lsiSid,lsiMissingChanges);
+    }
+
+    // Computes the missing changes counters for RS :
+    // Sum the difference of seqnuence numbers for each element in the States.
+
+    for (short lsiSid : RSStates.keySet())
+    {
+      ServerState lsiState = this.RSStates.get(lsiSid);
+      Long lsiMissingChanges = (long)0;
+      if (lsiState != null)
+      {
+        Iterator<Short> lsjMaxItr = this.maxCNs.keySet().iterator();
+        while (lsjMaxItr.hasNext())
+        {
+          Short lsjSid = lsjMaxItr.next();
+          ChangeNumber lsjMaxCN = this.maxCNs.get(lsjSid);
+          ChangeNumber lsiLastCN = lsiState.getMaxChangeNumber(lsjSid);
+
+          int missingChangesLsiLsj =
+            ChangeNumber.diffSeqNum(lsjMaxCN, lsiLastCN);
+
+          mds +=
+            "+ diff("+lsjMaxCN+"-"
+                     +lsiLastCN+")="+missingChangesLsiLsj;
+
+          lsiMissingChanges += missingChangesLsiLsj;
+        }
+      }
+      mds += "=" + lsiMissingChanges;
+      this.missingChangesRS.put(lsiSid,lsiMissingChanges);
 
       if (debugEnabled())
         TRACER.debugInfo(
@@ -307,6 +360,17 @@
   }
 
   /**
+   * Set the state of the RS with the provided serverId.
+   *
+   * @param serverId   The server ID.
+   * @param state      The server state.
+   */
+  public void setRSState(short serverId, ServerState state)
+  {
+    RSStates.put(serverId, state);
+  }
+
+  /**
    * Set the state of the LDAP server with the provided serverId.
    * @param serverId The server ID.
    * @param newFmd The first missing date.
diff --git a/opends/src/server/org/opends/server/replication/server/NotAssuredUpdateMsg.java b/opends/src/server/org/opends/server/replication/server/NotAssuredUpdateMsg.java
new file mode 100644
index 0000000..961c8f1
--- /dev/null
+++ b/opends/src/server/org/opends/server/replication/server/NotAssuredUpdateMsg.java
@@ -0,0 +1,358 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.server;
+
+import java.io.UnsupportedEncodingException;
+import java.util.zip.DataFormatException;
+import org.opends.server.replication.common.AssuredMode;
+import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
+import org.opends.server.replication.protocol.ProtocolVersion;
+
+/**
+ * This is a facility class that is in fact an hack to optimize replication
+ * server performances in case of assured replication usage:
+ * When received from a server by a server reader, an update message is to be
+ * posted in the queues of the server writers. Thus, they receive the same
+ * reference of update message. As we want to transform an assured update
+ * message to an equivalent not assured one for some servers but not for all,
+ * instead of performing a painful clone of the message, we use this special
+ * class to keep a reference to the real object, but that will overwrite the
+ * assured flag value to false when serializing the message, and return false
+ * when calling the isAssured() method.
+ *
+ */
+public class NotAssuredUpdateMsg extends UpdateMsg
+{
+  // The real update message this message represents
+  private UpdateMsg realUpdateMsg = null;
+
+  /**
+   * Creates a new empty UpdateMsg.
+   * This class is only used by replication server code so constructor is not
+   * public by security.
+   * @param updateMsg The real underlying update message this object represents.
+   */
+  NotAssuredUpdateMsg(UpdateMsg updateMsg)
+  {
+    realUpdateMsg = updateMsg;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public ChangeNumber getChangeNumber()
+  {
+    return realUpdateMsg.getChangeNumber();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean isAssured()
+  {
+    // Always return false as we represent a not assured message
+    return false;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void setAssured(boolean assured)
+  {
+    // No impact for this method as semantic is that assured is always false
+    // and we do not want to change the original real update message settings
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean equals(Object obj)
+  {
+    // Compare with the underlying real update message
+    if (obj != null)
+    {
+      if (obj.getClass() != realUpdateMsg.getClass())
+        return false;
+      return realUpdateMsg.getChangeNumber().
+        equals(((UpdateMsg)obj).getChangeNumber());
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int hashCode()
+  {
+    return realUpdateMsg.hashCode();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int compareTo(UpdateMsg msg)
+  {
+    return realUpdateMsg.compareTo(msg);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes(short reqProtocolVersion)
+    throws UnsupportedEncodingException
+  {
+    // Get the encoding of the real message then overwrite the assured flag byte
+    // to always be false
+    byte[] bytes = realUpdateMsg.getBytes(reqProtocolVersion);
+    int maxLen = bytes.length;
+    int pos = -1;
+    int nZeroFound = 0; // Number of 0 value found
+    boolean found = false;
+
+    /**
+     * Overwrite the assured flag at the right position according to
+     * message type.
+     */
+    // Look for assured flag position
+    if (realUpdateMsg instanceof LDAPUpdateMsg)
+    {
+      // LDAP update message
+      switch (reqProtocolVersion)
+      {
+        case ProtocolVersion.REPLICATION_PROTOCOL_V1:
+          /* The message header is stored in the form :
+           * <operation type><changenumber><dn><assured><entryuuid><change>
+           * the length of result byte array is therefore :
+           *   1 + change number length + 1 + dn length + 1  + 1 +
+           *   uuid length + 1 + additional_length
+           * See LDAPUpdateMsg.encodeHeader_V1() for more information
+           */
+          // Find end of change number then end of dn
+          for (pos = 1 ; pos < maxLen ; pos++ ) {
+            if (bytes[pos] == (byte)0)
+            {
+              nZeroFound++;
+              if (nZeroFound == 2) // 2 end of string to find
+              {
+                found = true;
+                break;
+              }
+            }
+          }
+          if (!found)
+            throw new UnsupportedEncodingException("Could not find end of " +
+              "change number.");
+          pos++;
+          if (pos >= maxLen)
+            throw new UnsupportedEncodingException("Reached end of packet.");
+          // Force assured flag to false
+          bytes[pos] = (byte)0;
+          break;
+        case ProtocolVersion.REPLICATION_PROTOCOL_V2:
+          /* The message header is stored in the form :
+           * <operation type><protocol version><changenumber><dn><entryuuid>
+           * <assured> <assured mode> <safe data level>
+           * the length of result byte array is therefore :
+           *   1 + 1 + change number length + 1 + dn length + 1 + uuid length +
+           *   1 + 1 + 1 + 1 + additional_length
+           * See LDAPUpdateMsg.encodeHeader() for more information
+           */
+          // Find end of change number then end of dn then end of uuid
+          for (pos = 2 ; pos < maxLen ; pos++ ) {
+            if (bytes[pos] == (byte)0)
+            {
+              nZeroFound++;
+              if (nZeroFound == 3) // 3 end of string to find
+              {
+                found = true;
+                break;
+              }
+            }
+          }
+          if (!found)
+            throw new UnsupportedEncodingException("Could not find end of " +
+              "change number.");
+          pos++;
+          if (pos >= maxLen)
+            throw new UnsupportedEncodingException("Reached end of packet.");
+          // Force assured flag to false
+          bytes[pos] = (byte)0;
+          break;
+        default:
+          throw new UnsupportedEncodingException("Unsupported requested " +
+            " protocol version: " + reqProtocolVersion);
+      }
+    } else
+    {
+      if (!(realUpdateMsg instanceof UpdateMsg))
+      {
+        // Should never happen
+        throw new UnsupportedEncodingException(
+          "Unknown underlying real message type.");
+      }
+      // This is a generic update message
+      /* The message header is stored in the form :
+       * <operation type><protocol version><changenumber><assured>
+       * <assured mode> <safe data level>
+       * the length of result byte array is therefore :
+       *   1 + 1 + change number length + 1 + 1
+       *   + 1 + 1 + additional_length
+       * See UpdateMsg.encodeHeader() for more  information
+       */
+      // Find end of change number
+      for (pos = 2 ; pos < maxLen ; pos++ )
+      {
+        if (bytes[pos] == (byte)0)
+        {
+          nZeroFound++;
+          if (nZeroFound == 1) // 1 end of string to find
+          {
+            found = true;
+            break;
+          }
+        }
+      }
+      if (!found)
+        throw new UnsupportedEncodingException("Could not find end of " +
+          "change number.");
+      pos++;
+      if (pos >= maxLen)
+        throw new UnsupportedEncodingException("Reached end of packet.");
+      // Force assured flag to false
+      bytes[pos] = (byte) 0;
+    }
+    return bytes;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getBytes() throws UnsupportedEncodingException
+  {
+    return getBytes(ProtocolVersion.getCurrentVersion());
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public AssuredMode getAssuredMode()
+  {
+    return realUpdateMsg.getAssuredMode();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte getSafeDataLevel()
+  {
+    return realUpdateMsg.getSafeDataLevel();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void setAssuredMode(AssuredMode assuredMode)
+  {
+    // No impact for this method as semantic is that assured is always false
+    // and we do not want to change the original real update message settings
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void setSafeDataLevel(byte safeDataLevel)
+  {
+    // No impact for this method as semantic is that assured is always false
+    // and we do not want to change the original real update message settings
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public short getVersion()
+  {
+    return realUpdateMsg.getVersion();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size()
+  {
+    return realUpdateMsg.size();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected byte[] encodeHeader(byte type, int additionalLength)
+    throws UnsupportedEncodingException
+  {
+    // No called as only used by constructors using bytes
+    return null;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int decodeHeader(byte type, byte[] encodedMsg)
+                          throws DataFormatException
+  {
+    // No called as only used by getBytes methods
+    return -1;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public byte[] getPayload()
+  {
+    return realUpdateMsg.getPayload();
+  }
+}
diff --git a/opends/src/server/org/opends/server/replication/server/ReplServerAckMessageList.java b/opends/src/server/org/opends/server/replication/server/ReplServerAckMessageList.java
deleted file mode 100644
index 8f4ab2a..0000000
--- a/opends/src/server/org/opends/server/replication/server/ReplServerAckMessageList.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
- */
-package org.opends.server.replication.server;
-
-import org.opends.server.replication.common.ChangeNumber;
-
-/**
- * This class is used to store acks for update messages coming from
- * other replication servers.
- */
-public class ReplServerAckMessageList extends AckMessageList
-{
-  private short replicationServerId;
-  private ReplicationServerDomain replicationServerDomain;
-
-  /**
-   * Creates a new AckMessageList for a given ChangeNumber.
-   *
-   * @param changeNumber The ChangeNumber for which the ack list is created.
-   * @param numExpectedAcks The number of acks waited before acking the
-   *                        original change.
-   * @param replicationServerId The Identifier of the replication server
-   *                          from which the change was received.
-   * @param replicationServerDomain The ReplicationServerDomain from which he
-   *                         change was received.
-   */
-  public ReplServerAckMessageList(ChangeNumber changeNumber,
-                                 int numExpectedAcks,
-                                 short replicationServerId,
-                                ReplicationServerDomain replicationServerDomain)
-  {
-    super(changeNumber, numExpectedAcks);
-    this.replicationServerId = replicationServerId;
-    this.replicationServerDomain = replicationServerDomain;
-  }
-
-  /**
-   * Get the Identifier of the replication server from which we received the
-   * change.
-   * @return Returns the Identifier of the replication server from which we
-   *         received the change.
-   */
-  public short getReplicationServerId()
-  {
-    return replicationServerId;
-  }
-
-  /**
-   * Get the replicationServerDomain of the replication server from which we
-   * received the change.
-   * @return Returns the replicationServerDomain of the replication server from
-   *         which we received the change .
-   */
-  public ReplicationServerDomain getChangelogCache()
-  {
-    return replicationServerDomain;
-  }
-
-
-}
diff --git a/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java b/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
index c56863a..053d3aa 100644
--- a/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
+++ b/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
@@ -34,6 +34,8 @@
 import static org.opends.server.loggers.debug.DebugLogger.getTracer;
 import static org.opends.server.util.StaticUtils.*;
 
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
+
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -858,7 +860,7 @@
   /**
    * Export one change.
    */
-  private void processChange(UpdateMsg msg,
+  private void processChange(UpdateMsg updateMsg,
       LDIFExportConfig exportConfig, LDIFWriter ldifWriter,
       SearchOperation searchOperation, String baseDN)
   {
@@ -873,159 +875,165 @@
 
     try
     {
-      if (msg instanceof AddMsg)
+      if (updateMsg instanceof LDAPUpdateMsg)
       {
-        AddMsg addMsg = (AddMsg)msg;
-        AddOperation addOperation = (AddOperation)msg.createOperation(conn);
+        LDAPUpdateMsg msg = (LDAPUpdateMsg) updateMsg;
 
-        dn = DN.decode("puid=" + addMsg.getParentUid() + "+" +
-            CHANGE_NUMBER + "=" + msg.getChangeNumber().toString() + "+" +
-            msg.getDn() + "," + BASE_DN);
-
-        Map<AttributeType,List<Attribute>> attributes =
-          new HashMap<AttributeType,List<Attribute>>();
-        Map<ObjectClass, String> objectclasses =
-          new HashMap<ObjectClass, String>();
-
-        for (RawAttribute a : addOperation.getRawAttributes())
+        if (msg instanceof AddMsg)
         {
-          Attribute attr = a.toAttribute();
-          if (attr.getAttributeType().isObjectClassType())
-          {
-            for (ByteString os : a.getValues())
-            {
-              String ocName = os.toString();
-              ObjectClass oc =
-                DirectoryServer.getObjectClass(toLowerCase(ocName));
-              if (oc == null)
-              {
-                oc = DirectoryServer.getDefaultObjectClass(ocName);
-              }
+          AddMsg addMsg = (AddMsg)msg;
+          AddOperation addOperation = (AddOperation)msg.createOperation(conn);
 
-              objectclasses.put(oc,ocName);
+          dn = DN.decode("puid=" + addMsg.getParentUid() + "+" +
+              CHANGE_NUMBER + "=" + msg.getChangeNumber().toString() + "+" +
+              msg.getDn() + "," + BASE_DN);
+
+          Map<AttributeType,List<Attribute>> attributes =
+            new HashMap<AttributeType,List<Attribute>>();
+          Map<ObjectClass, String> objectclasses =
+            new HashMap<ObjectClass, String>();
+
+          for (RawAttribute a : addOperation.getRawAttributes())
+          {
+            Attribute attr = a.toAttribute();
+            if (attr.getAttributeType().isObjectClassType())
+            {
+              for (ByteString os : a.getValues())
+              {
+                String ocName = os.toString();
+                ObjectClass oc =
+                  DirectoryServer.getObjectClass(toLowerCase(ocName));
+                if (oc == null)
+                {
+                  oc = DirectoryServer.getDefaultObjectClass(ocName);
+                }
+
+                objectclasses.put(oc,ocName);
+              }
             }
+            else
+            {
+              addAttribute(attributes, attr);
+            }
+          }
+
+          Attribute changetype = Attributes.create("changetype", "add");
+          addAttribute(attributes, changetype);
+
+          if (exportConfig != null)
+          {
+            AddChangeRecordEntry changeRecord =
+              new AddChangeRecordEntry(dn, attributes);
+            ldifWriter.writeChangeRecord(changeRecord);
           }
           else
           {
-            addAttribute(attributes, attr);
+            entry = new Entry(dn, objectclasses, attributes, null);
+          }
+        }
+        else if (msg instanceof DeleteMsg)
+        {
+          DeleteMsg delMsg = (DeleteMsg)msg;
+
+          dn = DN.decode("uuid=" + msg.getUniqueId() + "," +
+              CHANGE_NUMBER + "=" + delMsg.getChangeNumber().toString()+ "," +
+              msg.getDn() +","+ BASE_DN);
+
+          DeleteChangeRecordEntry changeRecord =
+            new DeleteChangeRecordEntry(dn);
+          if (exportConfig != null)
+          {
+            ldifWriter.writeChangeRecord(changeRecord);
+          }
+          else
+          {
+            Writer writer = new Writer();
+            LDIFWriter ldifWriter2 = writer.getLDIFWriter();
+            ldifWriter2.writeChangeRecord(changeRecord);
+            LDIFReader reader = writer.getLDIFReader();
+            entry = reader.readEntry();
+          }
+        }
+        else if (msg instanceof ModifyMsg)
+        {
+          ModifyOperation op = (ModifyOperation)msg.createOperation(conn);
+
+          dn = DN.decode("uuid=" + msg.getUniqueId() + "," +
+              CHANGE_NUMBER + "=" + msg.getChangeNumber().toString()+ "," +
+              msg.getDn() +","+ BASE_DN);
+          op.setInternalOperation(true);
+
+          ModifyChangeRecordEntry changeRecord =
+            new ModifyChangeRecordEntry(dn, op.getRawModifications());
+          if (exportConfig != null)
+          {
+            ldifWriter.writeChangeRecord(changeRecord);
+          }
+          else
+          {
+            Writer writer = new Writer();
+            LDIFWriter ldifWriter2 = writer.getLDIFWriter();
+            ldifWriter2.writeChangeRecord(changeRecord);
+            LDIFReader reader = writer.getLDIFReader();
+            entry = reader.readEntry();
+          }
+        }
+        else if (msg instanceof ModifyDNMsg)
+        {
+          ModifyDNOperation op = (ModifyDNOperation)msg.createOperation(conn);
+
+          dn = DN.decode("uuid=" + msg.getUniqueId() + "," +
+              CHANGE_NUMBER + "=" + msg.getChangeNumber().toString()+ "," +
+              msg.getDn() +","+ BASE_DN);
+          op.setInternalOperation(true);
+
+          ModifyDNChangeRecordEntry changeRecord =
+            new ModifyDNChangeRecordEntry(dn, op.getNewRDN(), op.deleteOldRDN(),
+                op.getNewSuperior());
+
+          if (exportConfig != null)
+          {
+            ldifWriter.writeChangeRecord(changeRecord);
+          }
+          else
+          {
+            Writer writer = new Writer();
+            LDIFWriter ldifWriter2 = writer.getLDIFWriter();
+            ldifWriter2.writeChangeRecord(changeRecord);
+            LDIFReader reader = writer.getLDIFReader();
+            Entry modDNEntry = reader.readEntry();
+            entry = modDNEntry;
           }
         }
 
-        Attribute changetype = Attributes.create("changetype", "add");
-        addAttribute(attributes, changetype);
-
         if (exportConfig != null)
         {
-          AddChangeRecordEntry changeRecord =
-            new AddChangeRecordEntry(dn, attributes);
-          ldifWriter.writeChangeRecord(changeRecord);
+          this.exportedCount++;
         }
         else
         {
-          entry = new Entry(dn, objectclasses, attributes, null);
-        }
-      }
-      else if (msg instanceof DeleteMsg)
-      {
-        DeleteMsg delMsg = (DeleteMsg)msg;
+          // Add extensibleObject objectclass and the ChangeNumber
+          // in the entry.
+          if (!entry.getObjectClasses().containsKey(objectclass))
+            entry.addObjectClass(objectclass);
+          Attribute changeNumber =
+            Attributes.create(CHANGE_NUMBER,
+                msg.getChangeNumber().toStringUI());
+          addAttribute(entry.getUserAttributes(), changeNumber);
+          Attribute domain = Attributes.create("replicationDomain", baseDN);
+          addAttribute(entry.getUserAttributes(), domain);
 
-        dn = DN.decode("uuid=" + msg.getUniqueId() + "," +
-            CHANGE_NUMBER + "=" + delMsg.getChangeNumber().toString()+ "," +
-            msg.getDn() +","+ BASE_DN);
+          // Get the base DN, scope, and filter for the search.
+          DN  searchBaseDN = searchOperation.getBaseDN();
+          SearchScope  scope  = searchOperation.getScope();
+          SearchFilter filter = searchOperation.getFilter();
 
-        DeleteChangeRecordEntry changeRecord =
-          new DeleteChangeRecordEntry(dn);
-        if (exportConfig != null)
-        {
-          ldifWriter.writeChangeRecord(changeRecord);
-        }
-        else
-        {
-          Writer writer = new Writer();
-          LDIFWriter ldifWriter2 = writer.getLDIFWriter();
-          ldifWriter2.writeChangeRecord(changeRecord);
-          LDIFReader reader = writer.getLDIFReader();
-          entry = reader.readEntry();
-        }
-      }
-      else if (msg instanceof ModifyMsg)
-      {
-        ModifyOperation op = (ModifyOperation)msg.createOperation(conn);
-
-        dn = DN.decode("uuid=" + msg.getUniqueId() + "," +
-            CHANGE_NUMBER + "=" + msg.getChangeNumber().toString()+ "," +
-            msg.getDn() +","+ BASE_DN);
-        op.setInternalOperation(true);
-
-        ModifyChangeRecordEntry changeRecord =
-          new ModifyChangeRecordEntry(dn, op.getRawModifications());
-        if (exportConfig != null)
-        {
-          ldifWriter.writeChangeRecord(changeRecord);
-        }
-        else
-        {
-          Writer writer = new Writer();
-          LDIFWriter ldifWriter2 = writer.getLDIFWriter();
-          ldifWriter2.writeChangeRecord(changeRecord);
-          LDIFReader reader = writer.getLDIFReader();
-          entry = reader.readEntry();
-        }
-      }
-      else if (msg instanceof ModifyDNMsg)
-      {
-        ModifyDNOperation op = (ModifyDNOperation)msg.createOperation(conn);
-
-        dn = DN.decode("uuid=" + msg.getUniqueId() + "," +
-            CHANGE_NUMBER + "=" + msg.getChangeNumber().toString()+ "," +
-            msg.getDn() +","+ BASE_DN);
-        op.setInternalOperation(true);
-
-        ModifyDNChangeRecordEntry changeRecord =
-          new ModifyDNChangeRecordEntry(dn, op.getNewRDN(), op.deleteOldRDN(),
-              op.getNewSuperior());
-
-        if (exportConfig != null)
-        {
-          ldifWriter.writeChangeRecord(changeRecord);
-        }
-        else
-        {
-          Writer writer = new Writer();
-          LDIFWriter ldifWriter2 = writer.getLDIFWriter();
-          ldifWriter2.writeChangeRecord(changeRecord);
-          LDIFReader reader = writer.getLDIFReader();
-          Entry modDNEntry = reader.readEntry();
-          entry = modDNEntry;
-        }
-      }
-
-      if (exportConfig != null)
-      {
-        this.exportedCount++;
-      }
-      else
-      {
-        // Add extensibleObject objectclass and the ChangeNumber
-        // in the entry.
-        if (!entry.getObjectClasses().containsKey(objectclass))
-          entry.addObjectClass(objectclass);
-        Attribute changeNumber =
-          Attributes.create(CHANGE_NUMBER, msg.getChangeNumber().toStringUI());
-        addAttribute(entry.getUserAttributes(), changeNumber);
-        Attribute domain = Attributes.create("replicationDomain", baseDN);
-        addAttribute(entry.getUserAttributes(), domain);
-
-        // Get the base DN, scope, and filter for the search.
-        DN  searchBaseDN = searchOperation.getBaseDN();
-        SearchScope  scope  = searchOperation.getScope();
-        SearchFilter filter = searchOperation.getFilter();
-
-        boolean ms = entry.matchesBaseAndScope(searchBaseDN, scope);
-        boolean mf = filter.matchesEntry(entry);
-        if ( ms && mf )
-        {
-          searchOperation.returnEntry(entry, new LinkedList<Control>());
+          boolean ms = entry.matchesBaseAndScope(searchBaseDN, scope);
+          boolean mf = filter.matchesEntry(entry);
+          if ( ms && mf )
+          {
+            searchOperation.returnEntry(entry, new LinkedList<Control>());
+          }
         }
       }
     }
diff --git a/opends/src/server/org/opends/server/replication/server/ReplicationDB.java b/opends/src/server/org/opends/server/replication/server/ReplicationDB.java
index 8a69489..f83631a 100644
--- a/opends/src/server/org/opends/server/replication/server/ReplicationDB.java
+++ b/opends/src/server/org/opends/server/replication/server/ReplicationDB.java
@@ -34,7 +34,6 @@
 import java.util.List;
 import java.io.UnsupportedEncodingException;
 
-import org.opends.server.types.DN;
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.replication.protocol.UpdateMsg;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -59,7 +58,7 @@
   private ReplicationDbEnv dbenv = null;
   private ReplicationServer replicationServer;
   private Short serverId;
-  private DN baseDn;
+  private String baseDn;
 
   // The maximum number of retries in case of DatabaseDeadlock Exception.
   private static final int DEADLOCK_RETRIES = 10;
@@ -77,7 +76,7 @@
    * @param dbenv The Db environment to use to create the db.
    * @throws DatabaseException If a database problem happened.
    */
-  public ReplicationDB(Short serverId, DN baseDn,
+  public ReplicationDB(Short serverId, String baseDn,
                      ReplicationServer replicationServer,
                      ReplicationDbEnv dbenv)
                      throws DatabaseException
diff --git a/opends/src/server/org/opends/server/replication/server/ReplicationDbEnv.java b/opends/src/server/org/opends/server/replication/server/ReplicationDbEnv.java
index 01ba275..98aa2ee 100644
--- a/opends/src/server/org/opends/server/replication/server/ReplicationDbEnv.java
+++ b/opends/src/server/org/opends/server/replication/server/ReplicationDbEnv.java
@@ -37,9 +37,6 @@
 import java.io.File;
 import java.io.UnsupportedEncodingException;
 
-import org.opends.server.types.DN;
-import org.opends.server.types.DirectoryException;
-
 import com.sleepycat.je.Cursor;
 import com.sleepycat.je.Database;
 import com.sleepycat.je.DatabaseConfig;
@@ -148,7 +145,7 @@
           {
             long generationId=-1;
 
-            DN baseDn;
+            String baseDn;
 
             try
             {
@@ -165,18 +162,7 @@
                   + "<" + str[1] + ">"));
             }
 
-            // <baseDn>
-            baseDn = null;
-            try
-            {
-              baseDn = DN.decode(str[2]);
-            } catch (DirectoryException e)
-            {
-              Message message =
-                ERR_IGNORE_BAD_DN_IN_DATABASE_IDENTIFIER.get(str[1]);
-              logError(message);
-
-            }
+            baseDn = str[2];
 
             if (debugEnabled())
               TRACER.debugInfo(
@@ -239,16 +225,7 @@
                 + "<" + str[0] + ">"));
           }
           // <baseDn>
-          DN baseDn = null;
-          try
-          {
-            baseDn = DN.decode(str[1]);
-          } catch (DirectoryException e)
-          {
-            Message message =
-              ERR_IGNORE_BAD_DN_IN_DATABASE_IDENTIFIER.get(str[1]);
-            logError(message);
-          }
+          String baseDn = str[1];
 
           if (debugEnabled())
             TRACER.debugInfo(
@@ -287,7 +264,7 @@
      * @return the Database.
      * @throws DatabaseException in case of underlying Exception.
      */
-    public Database getOrAddDb(Short serverId, DN baseDn, Long generationId)
+    public Database getOrAddDb(Short serverId, String baseDn, Long generationId)
     throws DatabaseException
     {
       if (debugEnabled())
@@ -295,8 +272,7 @@
           serverId + " " + baseDn + " " + generationId);
       try
       {
-        String stringId = serverId.toString() + FIELD_SEPARATOR
-                          + baseDn.toNormalizedString();
+        String stringId = serverId.toString() + FIELD_SEPARATOR + baseDn;
 
         // Opens the database for the changes received from this server
         // on this domain. Create it if it does not already exist.
@@ -333,11 +309,9 @@
 
         // Creates the record domain base Dn/ generationId in the stateDb
         // if it does not already exist.
-        stringId = GENERATION_ID_TAG + FIELD_SEPARATOR +
-          baseDn.toNormalizedString();
+        stringId = GENERATION_ID_TAG + FIELD_SEPARATOR + baseDn;
         String dataStringId = GENERATION_ID_TAG + FIELD_SEPARATOR +
-        generationId.toString() + FIELD_SEPARATOR +
-          baseDn.toNormalizedString();
+        generationId.toString() + FIELD_SEPARATOR + baseDn;
         byteId = stringId.getBytes("UTF-8");
         byte[] dataByteId;
         dataByteId = dataStringId.getBytes("UTF-8");
@@ -408,7 +382,7 @@
      * @param baseDn The baseDn for which the generationID must be cleared.
      *
      */
-    public void clearGenerationId(DN baseDn)
+    public void clearGenerationId(String baseDn)
     {
       if (debugEnabled())
         TRACER.debugInfo(
@@ -417,8 +391,7 @@
       try
       {
         // Deletes the record domain base Dn/ generationId in the stateDb
-        String stringId = GENERATION_ID_TAG + FIELD_SEPARATOR +
-          baseDn.toNormalizedString();
+        String stringId = GENERATION_ID_TAG + FIELD_SEPARATOR + baseDn;
         byte[] byteId = stringId.getBytes("UTF-8");
         DatabaseEntry key = new DatabaseEntry();
         key.setData(byteId);
@@ -435,8 +408,7 @@
             if (debugEnabled())
               TRACER.debugInfo(
                 "In " + this.replicationServer.getMonitorInstanceName() +
-                " clearGenerationId (" +
-                baseDn +") succeeded.");
+                " clearGenerationId (" + baseDn +") succeeded.");
           }
           catch (DatabaseException dbe)
           {
@@ -472,7 +444,7 @@
      * @param serverId The serverId to remove from the Db.
      *
      */
-    public void clearServerId(DN baseDn, Short serverId)
+    public void clearServerId(String baseDn, Short serverId)
     {
       if (debugEnabled())
         TRACER.debugInfo(
@@ -480,8 +452,7 @@
             "clearServerId(baseDN=" + baseDn + ", serverId=" + serverId);
       try
       {
-        String stringId = serverId.toString() + FIELD_SEPARATOR
-                         + baseDn.toNormalizedString();
+        String stringId = serverId.toString() + FIELD_SEPARATOR + baseDn;
 
         // Deletes the record serverId/domain base Dn in the stateDb
         byte[] byteId;
diff --git a/opends/src/server/org/opends/server/replication/server/ReplicationServer.java b/opends/src/server/org/opends/server/replication/server/ReplicationServer.java
index 0175414..be59878 100644
--- a/opends/src/server/org/opends/server/replication/server/ReplicationServer.java
+++ b/opends/src/server/org/opends/server/replication/server/ReplicationServer.java
@@ -108,8 +108,8 @@
   /* This table is used to store the list of dn for which we are currently
    * handling servers.
    */
-  private ConcurrentHashMap<DN, ReplicationServerDomain> baseDNs =
-          new ConcurrentHashMap<DN, ReplicationServerDomain>();
+  private ConcurrentHashMap<String, ReplicationServerDomain> baseDNs =
+          new ConcurrentHashMap<String, ReplicationServerDomain>();
 
   private String localURL = "null";
   private boolean shutdown = false;
@@ -202,7 +202,7 @@
     assuredTimeout = configuration.getAssuredTimeout();
     degradedStatusThreshold = configuration.getDegradedStatusThreshold();
 
-    replSessionSecurity = new ReplSessionSecurity(configuration);
+    replSessionSecurity = new ReplSessionSecurity();
     initialize(replicationPort);
     configuration.addChangeListener(this);
     DirectoryServer.registerMonitorProvider(this);
@@ -360,7 +360,7 @@
    *                    colon.
    * @param baseDn     The baseDn of the connection
    */
-  private void connect(String serverURL, DN baseDn)
+  private void connect(String serverURL, String baseDn)
   {
     int separator = serverURL.lastIndexOf(':');
     String port = serverURL.substring(separator + 1);
@@ -484,7 +484,7 @@
    * @return The ReplicationServerDomain associated to the base DN given in
    *         parameter.
    */
-  public ReplicationServerDomain getReplicationServerDomain(DN baseDn,
+  public ReplicationServerDomain getReplicationServerDomain(String baseDn,
           boolean create)
   {
     ReplicationServerDomain replicationServerDomain;
@@ -560,7 +560,7 @@
    *         DN given in parameter.
    * @throws DatabaseException in case of underlying database problem.
    */
-  public DbHandler newDbHandler(short id, DN baseDn)
+  public DbHandler newDbHandler(short id, String baseDn)
   throws DatabaseException
   {
     return new DbHandler(id, baseDn, this, dbEnv, queueSize);
@@ -572,7 +572,7 @@
    * @param  baseDn The baseDn for which to delete the generationId.
    * @throws DatabaseException When it occurs.
    */
-  public void clearGenerationId(DN baseDn)
+  public void clearGenerationId(String baseDn)
   throws DatabaseException
   {
     try
@@ -799,7 +799,7 @@
      * Add all the base DNs that are known by this replication server.
      */
     AttributeBuilder builder = new AttributeBuilder("base-dn");
-    for (DN base : baseDNs.keySet())
+    for (String base : baseDNs.keySet())
     {
       builder.add(base.toString());
     }
@@ -807,7 +807,7 @@
 
     // Publish to monitor the generation ID by replicationServerDomain
     builder = new AttributeBuilder("base-dn-generation-id");
-    for (DN base : baseDNs.keySet())
+    for (String base : baseDNs.keySet())
     {
       long generationId=-1;
       ReplicationServerDomain replicationServerDomain =
@@ -828,7 +828,7 @@
    * @param baseDN The baseDN of the replicationServerDomain.
    * @return The value of the generationID.
    */
-  public long getGenerationId(DN baseDN)
+  public long getGenerationId(String baseDN)
   {
     ReplicationServerDomain rsd =
             this.getReplicationServerDomain(baseDN, false);
diff --git a/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java b/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
index a7c3dae..cf68944 100644
--- a/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
+++ b/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
@@ -50,7 +50,6 @@
 
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.replication.common.ServerState;
-import org.opends.server.replication.protocol.AckMsg;
 import org.opends.server.replication.protocol.ErrorMsg;
 import org.opends.server.replication.protocol.RoutableMsg;
 import org.opends.server.replication.protocol.UpdateMsg;
@@ -58,17 +57,21 @@
 import org.opends.server.replication.protocol.MonitorMsg;
 import org.opends.server.replication.protocol.MonitorRequestMsg;
 import org.opends.server.replication.protocol.ResetGenerationIdMsg;
-import org.opends.server.types.DN;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.ResultCode;
 import org.opends.server.util.TimeThread;
 import com.sleepycat.je.DatabaseException;
+import java.util.Timer;
+import java.util.TimerTask;
 import java.util.concurrent.locks.ReentrantLock;
+import org.opends.server.replication.common.AssuredMode;
 import org.opends.server.replication.common.DSInfo;
 import org.opends.server.replication.common.RSInfo;
 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.ChangeStatusMsg;
+import org.opends.server.replication.protocol.ProtocolVersion;
 
 /**
  * This class define an in-memory cache that will be used to store
@@ -88,9 +91,8 @@
  */
 public class ReplicationServerDomain
 {
-
   private final Object flowControlLock = new Object();
-  private final DN baseDn;
+  private final String baseDn;
   // The Status analyzer that periodically verifis if the connected DSs are
   // late or not
   private StatusAnalyzer statusAnalyzer = null;
@@ -154,16 +156,38 @@
   private MonitorData wrkMonitorData;
 
   /**
+   * The needed info for each received assured update message we are waiting
+   * acks for.
+   * Key: a change number matching a received update message which requested
+   * assured mode usage (either safe read or safe data mode)
+   * Value: The object holding every info needed about the already received acks
+   * as well as the acks to be received.
+   * For more details, see ExpectedAcksInfo and its sub classes javadoc.
+   */
+  private final ConcurrentHashMap<ChangeNumber, ExpectedAcksInfo> waitingAcks =
+    new ConcurrentHashMap<ChangeNumber, ExpectedAcksInfo>();
+
+  // The timer used to run the timeout code (timer tasks) for the assured update
+  // messages we are waiting acks for.
+  private Timer assuredTimeoutTimer = null;
+  // Counter used to purge the timer tasks referemces in assuredTimeoutTimer,
+  // every n number of treated assured messages
+  private int assuredTimeoutTimerPurgeCounter = 0;
+
+  /**
    * Creates a new ReplicationServerDomain associated to the DN baseDn.
    *
    * @param baseDn The baseDn associated to the ReplicationServerDomain.
    * @param replicationServer the ReplicationServer that created this
    *                          replicationServer cache.
    */
-  public ReplicationServerDomain(DN baseDn, ReplicationServer replicationServer)
+  public ReplicationServerDomain(
+      String baseDn, ReplicationServer replicationServer)
   {
     this.baseDn = baseDn;
     this.replicationServer = replicationServer;
+    this.assuredTimeoutTimer = new Timer("Replication Assured Timer for " +
+      baseDn + " in RS " + replicationServer.getServerId(), true);
   }
 
   /**
@@ -179,32 +203,11 @@
   public void put(UpdateMsg update, ServerHandler sourceHandler)
     throws IOException
   {
-    /*
-     * TODO : In case that the source server is a LDAP server this method
-     * should check that change did get pushed to at least one
-     * other replication server before pushing it to the LDAP servers
-     */
-
-    short id = update.getChangeNumber().getServerId();
+    ChangeNumber cn = update.getChangeNumber();
+    short id = cn.getServerId();
     sourceHandler.updateServerState(update);
     sourceHandler.incrementInCount();
 
-    if (update.isAssured())
-    {
-      int count = this.NumServers();
-      if (count > 1)
-      {
-        if (sourceHandler.isReplicationServer())
-          ServerHandler.addWaitingAck(update, sourceHandler.getServerId(),
-            this, count - 1);
-        else
-          sourceHandler.addWaitingAck(update, count - 1);
-      } else
-      {
-        sourceHandler.sendAck(update.getChangeNumber());
-      }
-    }
-
     if (generationId < 0)
     {
       generationId = sourceHandler.getGenerationId();
@@ -244,11 +247,103 @@
     // Publish the messages to the source handler
     dbHandler.add(update);
 
+    /**
+     * If this is an assured message (a message requesting ack), we must
+     * construct the ExpectedAcksInfo object with the right number of expected
+     * acks before posting message to the writers. Otherwise some writers may
+     * have time to post, receive the ack and increment received ack counter
+     * (kept in ExpectedAcksInfo object) and we could think the acknowledgment
+     * is fully processed although it may be not (some other acks from other
+     * servers are not yet arrived). So for that purpose we do a pre-loop
+     * to determine to who we will post an assured message.
+     * Whether the assured mode is safe read or safe data, we anyway do not
+     * support the assured replication feature across topologies with different
+     * group ids. The assured feature insures assured replication based on the
+     * same locality (group id). For instance in double data center deployment
+     * (2 group id usage) with assured replication enabled, an assured message
+     * sent from data center 1 (group id = 1) will be sent to servers of both
+     * data centers, but one will request and wait acks only from servers of the
+     * data center 1.
+     */
+    boolean assuredMessage = update.isAssured();
+    PreparedAssuredInfo preparedAssuredInfo = null;
+    if (assuredMessage)
+    {
+      // Assured feature is supported starting from replication protocol V2
+      if (sourceHandler.getProtocolVersion() >=
+        ProtocolVersion.REPLICATION_PROTOCOL_V2)
+      {
+        // According to assured sub-mode, prepare structures to keep track of
+        // the acks we are interested in.
+        AssuredMode assuredMode = update.getAssuredMode();
+        if (assuredMode != AssuredMode.SAFE_DATA_MODE)
+        {
+          preparedAssuredInfo = processSafeDataUpdateMsg(update, sourceHandler);
+        } else if (assuredMode != AssuredMode.SAFE_READ_MODE)
+        {
+          preparedAssuredInfo = processSafeReadUpdateMsg(update, sourceHandler);
+        } else
+        {
+          // Unknown assured mode: should never happen
+          Message errorMsg = ERR_RS_UNKNOWN_ASSURED_MODE.get(
+            Short.toString(replicationServer.getServerId()),
+            assuredMode.toString(), baseDn, update.toString());
+          logError(errorMsg);
+          assuredMessage = false;
+        }
+      } else
+      {
+        assuredMessage = false;
+      }
+    }
+
+    List<Short> expectedServers = null;
+    if (assuredMessage)
+    {
+      expectedServers = preparedAssuredInfo.expectedServers;
+      if (expectedServers != null)
+      {
+        // Store the expected acks info into the global map.
+        // The code for processing reception of acks for this update will update
+        // info kept in this object and if enough acks received, it will send
+        // back the final ack to the requester and remove the object from this
+        // map
+        // OR
+        // The following timer will time out and send an timeout ack to the
+        // requester if the acks are not received in time. The timer will also
+        // remove the object from this map.
+        waitingAcks.put(cn, preparedAssuredInfo.expectedAcksInfo);
+
+        // Arm timer for this assured update message (wait for acks until it
+        // times out)
+        AssuredTimeoutTask assuredTimeoutTask = new AssuredTimeoutTask(cn);
+        assuredTimeoutTimer.schedule(assuredTimeoutTask,
+          replicationServer.getAssuredTimeout());
+        // Purge timer every 100 treated messages
+        assuredTimeoutTimerPurgeCounter++;
+        if ((assuredTimeoutTimerPurgeCounter % 100) == 0)
+          assuredTimeoutTimer.purge();
+      }
+    }
+
+    /**
+     * The update message equivalent to the originally received update message,
+     * but with assured flag disabled. This message is the one that should be
+     * sent to non elligible servers for assured mode.
+     * We need a clone like of the original message with assured flag off, to be
+     * posted to servers we don't want to wait the ack from (not normal status
+     * servers or servers with different group id). This must be done because
+     * the posted message is a reference so each writer queue gets the same
+     * reference, thus, changing the assured flag of an object is done for every
+     * references posted on every writer queues. That is why we need a message
+     * version with assured flag on and another one with assured flag off.
+     */
+    NotAssuredUpdateMsg notAssuredUpdate = null;
 
     /*
      * Push the message to the replication servers
      */
-    if (!sourceHandler.isReplicationServer())
+    if (sourceHandler.isLDAPserver())
     {
       for (ServerHandler handler : replicationServers.values())
       {
@@ -261,7 +356,7 @@
           if (debugEnabled())
             TRACER.debugInfo("In RS " +
               replicationServer.getServerId() +
-              " for dn " + baseDn.toNormalizedString() + ", update " +
+              " for dn " + baseDn + ", update " +
               update.getChangeNumber().toString() +
               " will not be sent to replication server " +
               Short.toString(handler.getServerId()) + " with generation id " +
@@ -272,7 +367,27 @@
           continue;
         }
 
-        handler.add(update, sourceHandler);
+        if (assuredMessage)
+        {
+          // Assured mode: post an assured or not assured matching update
+          // message according to what has been computed for the destination
+          // server
+          if ((expectedServers != null) && expectedServers.contains(handler.
+            getServerId()))
+          {
+            handler.add(update, sourceHandler);
+          } else
+          {
+            if (notAssuredUpdate == null)
+            {
+              notAssuredUpdate = new NotAssuredUpdateMsg(update);
+            }
+            handler.add(notAssuredUpdate, sourceHandler);
+          }
+        } else
+        {
+          handler.add(update, sourceHandler);
+        }
       }
     }
 
@@ -281,7 +396,7 @@
      */
     for (ServerHandler handler : directoryServers.values())
     {
-      // don't forward the change to the server that just sent it
+      // Don't forward the change to the server that just sent it
       if (handler == sourceHandler)
       {
         continue;
@@ -307,7 +422,7 @@
           if (dsStatus == ServerStatus.BAD_GEN_ID_STATUS)
             TRACER.debugInfo("In RS " +
               replicationServer.getServerId() +
-              " for dn " + baseDn.toNormalizedString() + ", update " +
+              " for dn " + baseDn + ", update " +
               update.getChangeNumber().toString() +
               " will not be sent to directory server " +
               Short.toString(handler.getServerId()) + " with generation id " +
@@ -317,7 +432,7 @@
           if (dsStatus == ServerStatus.FULL_UPDATE_STATUS)
             TRACER.debugInfo("In RS " +
               replicationServer.getServerId() +
-              " for dn " + baseDn.toNormalizedString() + ", update " +
+              " for dn " + baseDn + ", update " +
               update.getChangeNumber().toString() +
               " will not be sent to directory server " +
               Short.toString(handler.getServerId()) +
@@ -327,9 +442,389 @@
         continue;
       }
 
-      handler.add(update, sourceHandler);
+      if (assuredMessage)
+      {
+        // Assured mode: post an assured or not assured matching update
+        // message according to what has been computed for the destination
+        // server
+        if ((expectedServers != null) && expectedServers.contains(handler.
+          getServerId()))
+        {
+          handler.add(update, sourceHandler);
+        } else
+        {
+          if (notAssuredUpdate == null)
+          {
+            notAssuredUpdate = new NotAssuredUpdateMsg(update);
+          }
+          handler.add(notAssuredUpdate, sourceHandler);
+        }
+      } else
+      {
+        handler.add(update, sourceHandler);
+      }
+    }
+  }
+
+  /**
+   * Helper class to be the return type of a method that processes a just
+   * received assured update message:
+   * - processSafeReadUpdateMsg
+   * - processSafeDataUpdateMsg
+   * This is a facility to pack many interesting returned object.
+   */
+  private class PreparedAssuredInfo
+  {
+      /**
+       * The list of servers identified as servers we are interested in
+       * receiving acks from. If this list is not null, then expectedAcksInfo
+       * should be not null.
+       * Servers that are not in this list are servers not elligible for an ack
+       * request.
+       *
+       */
+      public List<Short> expectedServers = null;
+
+      /**
+       * The constructed ExpectedAcksInfo object to be used when acks will be
+       * received. Null if expectedServers is null.
+       */
+      public ExpectedAcksInfo expectedAcksInfo = null;
+  }
+
+  /**
+   * Process a just received assured update message in Safe Read mode. If the
+   * ack can be sent immediately, it is done here. This will also determine to
+   * which suitable servers an ack should be requested from, and which ones are
+   * not elligible for an ack request.
+   * This method is an helper method for the put method. Have a look at the put
+   * method for a better understanding.
+   * @param update The just received assured update to process.
+   * @param sourceHandler The ServerHandler for the server from which the
+   *        update was received
+   * @return A suitable PreparedAssuredInfo object that contains every needed
+   * info to proceed with post to server writers.
+   * @throws IOException When an IO exception happens during the update
+   *         processing.
+   */
+  private PreparedAssuredInfo processSafeReadUpdateMsg(
+    UpdateMsg update, ServerHandler sourceHandler) throws IOException
+  {
+    ChangeNumber cn = update.getChangeNumber();
+    byte groupId = replicationServer.getGroupId();
+    byte sourceGroupId = sourceHandler.getGroupId();
+    List<Short> expectedServers = new ArrayList<Short>();
+    List<Short> wrongStatusServers = new ArrayList<Short>();
+
+    if (sourceGroupId != groupId)
+      // Assured feature does not cross different group ids
+    {
+      if (sourceHandler.isLDAPserver())
+      {
+        // Look for RS elligible for assured
+        for (ServerHandler handler : replicationServers.values())
+        {
+          if (handler.getGroupId() == groupId)
+            expectedServers.add(handler.getServerId());
+        }
+      }
+
+      // Look for DS elligible for assured
+      for (ServerHandler handler : directoryServers.values())
+      {
+        // Don't forward the change to the server that just sent it
+        if (handler == sourceHandler)
+        {
+          continue;
+        }
+        if (handler.getGroupId() == groupId)
+        {
+          if (handler.getStatus() == ServerStatus.NORMAL_STATUS)
+          {
+            expectedServers.add(handler.getServerId());
+          } else
+          {
+            wrongStatusServers.add(handler.getServerId());
+          }
+        }
+      }
     }
 
+    // Return computed structures
+    PreparedAssuredInfo preparedAssuredInfo = new PreparedAssuredInfo();
+    if (expectedServers.size() > 0)
+    {
+      // Some other acks to wait for
+      preparedAssuredInfo.expectedAcksInfo = new SafeReadExpectedAcksInfo(cn,
+        sourceHandler, expectedServers, wrongStatusServers);
+      preparedAssuredInfo.expectedServers = expectedServers;
+    }
+
+    if (preparedAssuredInfo.expectedServers == null)
+    {
+      // No elligible servers found, send the ack immediatly
+      AckMsg ack = new AckMsg(cn);
+      sourceHandler.sendAck(ack);
+    }
+
+    return preparedAssuredInfo;
+  }
+
+  /**
+   * Process a just received assured update message in Safe Data mode. If the
+   * ack can be sent immediately, it is done here. This will also determine to
+   * which suitable servers an ack should be requested from, and which ones are
+   * not elligible for an ack request.
+   * This method is an helper method for the put method. Have a look at the put
+   * method for a better understanding.
+   * @param update The just received assured update to process.
+   * @param sourceHandler The ServerHandler for the server from which the
+   *        update was received
+   * @return A suitable PreparedAssuredInfo object that contains every needed
+   * info to proceed with post to server writers.
+   * @throws IOException When an IO exception happens during the update
+   *         processing.
+   */
+  private PreparedAssuredInfo processSafeDataUpdateMsg(
+    UpdateMsg update, ServerHandler sourceHandler) throws IOException
+  {
+    ChangeNumber cn = update.getChangeNumber();
+    boolean interestedInAcks = true;
+    byte safeDataLevel = update.getSafeDataLevel();
+    byte groupId = replicationServer.getGroupId();
+    byte sourceGroupId = sourceHandler.getGroupId();
+    if (safeDataLevel < (byte) 1)
+    {
+      // Should never happen
+      Message errorMsg = ERR_UNKNOWN_ASSURED_SAFE_DATA_LEVEL.get(
+        Short.toString(replicationServer.getServerId()),
+        Byte.toString(safeDataLevel), baseDn, update.toString());
+      logError(errorMsg);
+      interestedInAcks = false;
+    } else if (sourceGroupId != groupId)
+    {
+      // Assured feature does not cross different group ids
+      interestedInAcks = false;
+    } else
+    {
+      if (sourceHandler.isLDAPserver())
+      {
+        if (safeDataLevel == (byte) 1)
+        {
+          // Immediatly return the ack for an assured message in safe data mode
+          // with safe data level 1, coming from a DS. No need to wait for more
+          // acks
+          AckMsg ack = new AckMsg(cn);
+          sourceHandler.sendAck(ack);
+          interestedInAcks = false; // No further acks to obtain
+        } else
+        {
+          // level > 1 : We need further acks
+          // The message will be posted in assured mode to elligible servers.
+          // The embedded safe data level is not changed, and his value will be
+          // used by a remote RS to determine if he must send an ack (level > 1)
+          // or not (level = 1)
+        }
+      } else
+      { // A RS sent us the safe data message, for sure no futher acks to wait
+        interestedInAcks = false;
+        if (safeDataLevel == (byte) 1)
+        {
+          // The original level was 1 so the RS that sent us this message should
+          // have already sent his ack to the sender DS. Level 1 has already
+          // been reached so no further acks to wait
+          // This should not happen in theory as the sender RS server should
+          // have sent us a matching not assured message so we should not come
+          // to here.
+        } else
+        {
+          // level > 1, so Ack this message to originator RS
+          AckMsg ack = new AckMsg(cn);
+          sourceHandler.sendAck(ack);
+        }
+      }
+    }
+
+    List<Short> expectedServers = new ArrayList<Short>();
+    if (interestedInAcks)
+    {
+      if (sourceHandler.isLDAPserver())
+      {
+        // Look for RS elligible for assured
+        for (ServerHandler handler : replicationServers.values())
+        {
+          if (handler.getGroupId() == groupId)
+            expectedServers.add(handler.getServerId());
+        }
+      }
+
+      // Look for DS elligible for assured
+      for (ServerHandler handler : directoryServers.values())
+      {
+        // Don't forward the change to the server that just sent it
+        if (handler == sourceHandler)
+        {
+          continue;
+        }
+        if (handler.getGroupId() == groupId)
+          expectedServers.add(handler.getServerId());
+      }
+    }
+
+    // Return computed structures
+    PreparedAssuredInfo preparedAssuredInfo = new PreparedAssuredInfo();
+    if (interestedInAcks && (expectedServers.size() > 0))
+    {
+      // Some other acks to wait for
+      preparedAssuredInfo.expectedAcksInfo = new SafeDataExpectedAcksInfo(cn,
+        sourceHandler, update.getSafeDataLevel());
+      preparedAssuredInfo.expectedServers = expectedServers;
+    }
+
+    if (interestedInAcks && (preparedAssuredInfo.expectedServers == null))
+    {
+      // level > 1 and source is a DS but no elligible servers found, send the
+      // ack immediatly
+      AckMsg ack = new AckMsg(cn);
+      sourceHandler.sendAck(ack);
+    }
+
+    return preparedAssuredInfo;
+  }
+
+  /**
+   * Process an ack received from a given server.
+   *
+   * @param ack The ack message received.
+   * @param ackingServer The server handler of the server that sent the ack.
+   */
+  public void processAck(AckMsg ack, ServerHandler ackingServer)
+  {
+    // Retrieve the expected acks info for the update matching the original
+    // sent update.
+    ChangeNumber cn = ack.getChangeNumber();
+    ExpectedAcksInfo expectedAcksInfo = waitingAcks.get(cn);
+
+    if (expectedAcksInfo != null)
+    {
+      // Prevent concurrent access from processAck() or AssuredTimeoutTask.run()
+      synchronized (expectedAcksInfo)
+      {
+        if (expectedAcksInfo.isCompleted())
+        {
+          // Timeout code is sending a timeout ack, do nothing and let him
+          // remove object from the map
+          return;
+        }
+        // If this is the last ack we were waiting from, immediatly create and
+        // send the final ack to the original server
+        if (expectedAcksInfo.processReceivedAck(ackingServer, ack))
+        {
+          // Remove the object from the map as no more needed
+          waitingAcks.remove(cn);
+          AckMsg finalAck = expectedAcksInfo.createAck(false);
+          ServerHandler origServer = expectedAcksInfo.getRequesterServer();
+          try
+          {
+            origServer.sendAck(finalAck);
+          } catch (IOException e)
+          {
+            /*
+             * An error happened trying the send back an ack to the server.
+             * Log an error and close the connection to this server.
+             */
+            MessageBuilder mb = new MessageBuilder();
+            mb.append(ERR_RS_ERROR_SENDING_ACK.get(
+              Short.toString(replicationServer.getServerId()),
+              Short.toString(origServer.getServerId()), cn.toString(), baseDn));
+            mb.append(stackTraceToSingleLineString(e));
+            logError(mb.toMessage());
+            stopServer(origServer);
+          }
+          // Mark the ack info object as completed to prevent potential timeout
+          // code parallel run
+          expectedAcksInfo.completed();
+        }
+      }
+    } else
+    {
+      // The timeout occured for the update matching this change number and the
+      // ack with timeout error has probably already been sent.
+    }
+  }
+
+  /**
+   * The code run when the timeout occurs while waiting for acks of the
+   * elligible servers. This basically sends a timeout ack (with any additional
+   * error info) to the original server that sent an assured update message.
+   */
+  private class AssuredTimeoutTask extends TimerTask
+  {
+    private ChangeNumber cn = null;
+
+    /**
+     * Constructor for the timer task.
+     * @param cn The changenumber of the assured update we are waiting acks for
+     */
+    public AssuredTimeoutTask(ChangeNumber cn)
+    {
+      this.cn = cn;
+    }
+
+    /**
+     * Run when the assured timeout for an assured update message we are waiting
+     * acks for occurs.
+     */
+    public void run()
+    {
+      ExpectedAcksInfo expectedAcksInfo = waitingAcks.get(cn);
+
+      if (expectedAcksInfo != null)
+      {
+        synchronized (expectedAcksInfo)
+        {
+          if (expectedAcksInfo.isCompleted())
+          {
+            // processAck() code is sending the ack, do nothing and let him
+            // remove object from the map
+            return;
+          }
+          // Remove the object from the map as no more needed
+          waitingAcks.remove(cn);
+          // Create the timeout ack and send him to the server the assured
+          // update message came from
+          AckMsg finalAck = expectedAcksInfo.createAck(true);
+          ServerHandler origServer = expectedAcksInfo.getRequesterServer();
+          if (debugEnabled())
+            TRACER.debugInfo(
+              "In RS " + Short.toString(replicationServer.getServerId()) +
+              " for " + baseDn +
+              ", sending timeout for assured update with change " + " number " +
+              cn.toString() + " to server id " +
+              Short.toString(origServer.getServerId()));
+          try
+          {
+            origServer.sendAck(finalAck);
+          } catch (IOException e)
+          {
+            /*
+             * An error happened trying the send back an ack to the server.
+             * Log an error and close the connection to this server.
+             */
+            MessageBuilder mb = new MessageBuilder();
+            mb.append(ERR_RS_ERROR_SENDING_ACK.get(
+              Short.toString(replicationServer.getServerId()),
+              Short.toString(origServer.getServerId()), cn.toString(), baseDn));
+            mb.append(stackTraceToSingleLineString(e));
+            logError(mb.toMessage());
+            stopServer(origServer);
+          }
+          // Mark the ack info object as completed to prevent potential
+          // processAck() code parallel run
+          expectedAcksInfo.completed();
+        }
+      }
+    }
   }
 
   /**
@@ -687,7 +1182,7 @@
    * Get the baseDn.
    * @return Returns the baseDn.
    */
-  public DN getBaseDn()
+  public String getBaseDn()
   {
     return baseDn;
   }
@@ -711,44 +1206,6 @@
   }
 
   /**
-   * Get the number of currently connected servers.
-   *
-   * @return the number of currently connected servers.
-   */
-  private int NumServers()
-  {
-    return replicationServers.size() + directoryServers.size();
-  }
-
-  /**
-   * Add an ack to the list of ack received for a given change.
-   *
-   * @param message The ack message received.
-   * @param fromServerId The identifier of the server that sent the ack.
-   */
-  public void ack(AckMsg message, short fromServerId)
-  {
-    /*
-     * there are 2 possible cases here :
-     *  - the message that was acked comes from a server to which
-     *    we are directly connected.
-     *    In this case, we can find the handler from the directoryServers map
-     *  - the message that was acked comes from a server to which we are not
-     *    connected.
-     *    In this case we need to find the replication server that forwarded
-     *    the change and send back the ack to this server.
-     */
-    ServerHandler handler = directoryServers.get(
-      message.getChangeNumber().getServerId());
-    if (handler != null)
-      handler.ack(message, fromServerId);
-    else
-    {
-      ServerHandler.ackChangelog(message, fromServerId);
-    }
-  }
-
-  /**
    * Retrieves the destination handlers for a routable message.
    *
    * @param msg The message to route.
@@ -975,56 +1432,6 @@
   }
 
   /**
-   * Send back an ack to the server that sent the change.
-   *
-   * @param changeNumber The ChangeNumber of the change that must be acked.
-   * @param isLDAPserver This boolean indicates if the server that sent the
-   *                     change was an LDAP server or a ReplicationServer.
-   */
-  public void sendAck(ChangeNumber changeNumber, boolean isLDAPserver)
-  {
-    short serverId = changeNumber.getServerId();
-    sendAck(changeNumber, isLDAPserver, serverId);
-  }
-
-  /**
-   *
-   * Send back an ack to a server that sent the change.
-   *
-   * @param changeNumber The ChangeNumber of the change that must be acked.
-   * @param isLDAPserver This boolean indicates if the server that sent the
-   *                     change was an LDAP server or a ReplicationServer.
-   * @param serverId     The identifier of the server from which we
-   *                     received the change..
-   */
-  public void sendAck(ChangeNumber changeNumber, boolean isLDAPserver,
-    short serverId)
-  {
-    ServerHandler handler;
-    if (isLDAPserver)
-      handler = directoryServers.get(serverId);
-    else
-      handler = replicationServers.get(serverId);
-
-    // TODO : check for null handler and log error
-    try
-    {
-      handler.sendAck(changeNumber);
-    } catch (IOException e)
-    {
-      /*
-       * An error happened trying the send back an ack to this server.
-       * Log an error and close the connection to this server.
-       */
-      MessageBuilder mb = new MessageBuilder();
-      mb.append(ERR_CHANGELOG_ERROR_SENDING_ACK.get(this.toString()));
-      mb.append(stackTraceToSingleLineString(e));
-      logError(mb.toMessage());
-      stopServer(handler);
-    }
-  }
-
-  /**
    * Shutdown this ReplicationServerDomain.
    */
   public void shutdown()
@@ -1228,13 +1635,8 @@
     {
       // Put RS info
       rsInfos.add(serverHandler.toRSInfo());
-      // Put his DSs info
-      Map<Short, LightweightServerHandler> lsList =
-        serverHandler.getConnectedDSs();
-      for (LightweightServerHandler ls : lsList.values())
-      {
-        dsInfos.add(ls.toDSInfo());
-      }
+
+      serverHandler.addDSInfos(dsInfos);
     }
 
     return new TopologyMsg(dsInfos, rsInfos);
@@ -1641,7 +2043,7 @@
     if (generationId > 0 && (generationId != handler.getGenerationId()))
     {
       Message message = NOTE_BAD_GENERATION_ID_FROM_RS.get(
-        baseDn.toNormalizedString(),
+        baseDn,
         Short.toString(handler.getServerId()),
         Long.toString(handler.getGenerationId()),
         Long.toString(generationId));
@@ -1879,10 +2281,13 @@
       synchronized (wrkMonitorData)
       {
         // Here is the RS state : list <serverID, lastChangeNumber>
-        // For each LDAP Server, we keep the max CN accross the RSes
+        // For each LDAP Server, we keep the max CN across the RSes
         ServerState replServerState = msg.getReplServerDbState();
         wrkMonitorData.setMaxCNs(replServerState);
 
+        // store the remote RS states.
+        wrkMonitorData.setRSState(msg.getsenderID(), replServerState);
+
         // Store the remote LDAP servers states
         Iterator<Short> lsidIterator = msg.ldapIterator();
         while (lsidIterator.hasNext())
@@ -2092,3 +2497,4 @@
     }
   }
 }
+
diff --git a/opends/src/server/org/opends/server/replication/server/SafeDataExpectedAcksInfo.java b/opends/src/server/org/opends/server/replication/server/SafeDataExpectedAcksInfo.java
new file mode 100644
index 0000000..e4ce2c4
--- /dev/null
+++ b/opends/src/server/org/opends/server/replication/server/SafeDataExpectedAcksInfo.java
@@ -0,0 +1,110 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.replication.server;
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.replication.common.AssuredMode;
+import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.replication.protocol.AckMsg;
+
+/**
+ * This class holds every info needed about the expected acks for a received
+ * update message requesting assured replication with Safe Data sub-mode.
+ * It also includes info/routines for constructing the final ack to be sent to
+ * the sender of the update message.
+ */
+public class SafeDataExpectedAcksInfo extends ExpectedAcksInfo
+{
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+  // Requested level of safe data when the update message was received.
+  private byte safeDataLevel = (byte)-1;
+
+  // Number of received acks for the matching update message, up to now
+  // Already set to 1 as the local RS receiving the message from a DS counts.
+  private byte numReceivedAcks = (byte)1;
+
+  /**
+   * Creates a new SafeDataExpectedAcksInfo.
+   * @param changeNumber The change number of the assured update message
+   * @param requesterServerHandler The server that sent the assured update
+   * message
+   * @param safeDataLevel The Safe Data level requested for the assured
+   * update message
+   */
+  public SafeDataExpectedAcksInfo(ChangeNumber changeNumber,
+    ServerHandler requesterServerHandler, byte safeDataLevel)
+  {
+    super(changeNumber, requesterServerHandler, AssuredMode.SAFE_DATA_MODE);
+    this.safeDataLevel = safeDataLevel;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+   public boolean processReceivedAck(ServerHandler ackingServer, AckMsg ackMsg)
+  {
+    /*
+     * Security: although a DS should not respond to an update message sent to
+     * him with assured safe data mode, we double check here that the ack sender
+     * is a RS to take the ack into account.
+     */
+     if (ackingServer.isLDAPserver())
+     {
+       // Sanity check: this should never happen
+        if (debugEnabled())
+          TRACER.debugInfo("Received unexpected SD ack from DS id: "
+          + ackingServer.getServerId() + " ack message: " + ackMsg);
+        return false;
+     }
+
+    numReceivedAcks++;
+    if (numReceivedAcks == safeDataLevel)
+      return true;
+    else
+      return false;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public AckMsg createAck(boolean timeout)
+  {
+    AckMsg ack = new AckMsg(changeNumber);
+
+    if (timeout)
+      ack.setHasTimeout(true);
+
+    return ack;
+  }
+}
diff --git a/opends/src/server/org/opends/server/replication/server/SafeReadExpectedAcksInfo.java b/opends/src/server/org/opends/server/replication/server/SafeReadExpectedAcksInfo.java
new file mode 100644
index 0000000..62fb049
--- /dev/null
+++ b/opends/src/server/org/opends/server/replication/server/SafeReadExpectedAcksInfo.java
@@ -0,0 +1,240 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.replication.server;
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
+import org.opends.server.loggers.debug.DebugTracer;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.opends.server.replication.common.AssuredMode;
+import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.replication.protocol.AckMsg;
+
+/**
+ * This class holds every info needed about the expected acks for a received
+ * update message requesting assured replication with Safe Read sub-mode.
+ * It also includes info/routines for constructing the final ack to be sent to
+ * the sender of the update message.
+ */
+public class SafeReadExpectedAcksInfo extends ExpectedAcksInfo
+{
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+  // Did some servers go in timeout when the matching update was sent ?
+  private boolean hasTimeout = false;
+
+  // Were some servers in wrong status when the matching update was sent ?
+  private boolean hasWrongStatus = false;
+
+  // Did some servers make an error replaying the sent matching update ?
+  private boolean hasReplayError = false;
+
+  // The list of server ids that had errors for the sent matching update
+  // Each server id of the list had one of the
+  // 3 possible errors (timeout, wrong status or replay error)
+  private List<Short> failedServers = null;
+
+  /**
+   * This gives the list of servers we are willing to wait acks from and the
+   * information about the ack from the servers.
+   * key: the id of the server.
+   * value: a boolean true if we received the ack from the server,
+   * false otherwise.
+   * This must not include servers we already identified they are in wrong
+   * status, but just servers that are in normal status.
+   */
+  private Map<Short,Boolean> expectedServersAckStatus =
+    new HashMap<Short,Boolean>();
+
+  /**
+   * Number of servers we want an ack from and from which we received the ack.
+   * Said differently: the number of servers in expectedServersAckStatus whose
+   * value is true. When this value reaches the size of expectedServersAckStatus
+   * we can compute an ack message (based on info in this object), to be
+   * returned to the (requester) server that sent us an assured update message.
+   */
+  private int numKnownAckStatus = 0;
+
+  /**
+   * Creates a new SafeReadExpectedAcksInfo.
+   * @param changeNumber The change number of the assured update message
+   * @param requesterServerHandler The server that sent the assured update
+   * message
+   * @param expectedServers The list of servers we want an ack from (they are
+   * in normal status and have the same group id as us)
+   * @param wrongStatusServers The list of all servers already detected in
+   * wrongStatus (degraded status) to keep trace of the error for the future
+   * returning ack we gonna compute
+   */
+  public SafeReadExpectedAcksInfo(ChangeNumber changeNumber,
+    ServerHandler requesterServerHandler, List<Short> expectedServers,
+    List<Short> wrongStatusServers)
+  {
+    super(changeNumber, requesterServerHandler, AssuredMode.SAFE_READ_MODE);
+
+    // Keep track of potential servers detected in wrong status
+    if (wrongStatusServers.size() > 0)
+    {
+      hasWrongStatus = true;
+      failedServers = wrongStatusServers;
+    }
+
+    // Initialize list of servers we expect acks from
+    for (Short serverId : expectedServers)
+    {
+      expectedServersAckStatus.put(serverId, false);
+    }
+  }
+
+  /**
+   * Sets the timeout marker for the future update ack.
+   * @param hasTimeout True if some timeout occurred
+   */
+  public void setHasTimeout(boolean hasTimeout)
+  {
+    this.hasTimeout = hasTimeout;
+  }
+
+  /**
+   * Sets the wrong status marker for the future update ack.
+   * @param hasWrongStatus True if some servers were in wrong status
+   */
+  public void setHasWrongStatus(boolean hasWrongStatus)
+  {
+    this.hasWrongStatus = hasWrongStatus;
+  }
+
+  /**
+   * Sets the replay error marker for the future update ack.
+   * @param hasReplayError True if some servers had errors replaying the change
+   */
+  public void setHasReplayError(boolean hasReplayError)
+  {
+    this.hasReplayError = hasReplayError;
+  }
+
+  /**
+   * Gets the timeout marker for the future update ack.
+   * @return The timeout marker for the future update ack.
+   */
+  public boolean hasTimeout()
+  {
+    return hasTimeout;
+  }
+
+  /**
+   * Gets the wrong status marker for the future update ack.
+   * @return hasWrongStatus The wrong status marker for the future update ack.
+   */
+  public boolean hasWrongStatus()
+  {
+    return hasWrongStatus;
+  }
+
+  /**
+   * Gets the replay error marker for the future update ack.
+   * @return hasReplayError The replay error marker for the future update ack.
+   */
+  public boolean hasReplayError()
+  {
+    return hasReplayError;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean processReceivedAck(ServerHandler ackingServer, AckMsg ackMsg)
+  {
+    // Get the ack status for the matching server
+    short ackingServerId = ackingServer.getServerId();
+    boolean ackReceived = expectedServersAckStatus.get(ackingServerId);
+    if (ackReceived)
+    {
+      // Sanity check: this should never happen
+      if (debugEnabled())
+        TRACER.debugInfo("Received unexpected ack from server id: "
+          + ackingServerId + " ack message: " + ackMsg);
+        return false;
+    } else
+    {
+      // Analyze received ack and update info for the ack to be later computed
+      // accordingly
+      boolean someErrors = false;
+      if (ackMsg.hasTimeout())
+      {
+        hasTimeout = true;
+        someErrors = true;
+      }
+      if (ackMsg.hasWrongStatus())
+      {
+        hasWrongStatus = true;
+        someErrors = true;
+      }
+      if (ackMsg.hasReplayError())
+      {
+        hasReplayError = true;
+        someErrors = true;
+      }
+      if (someErrors)
+      {
+        failedServers.addAll(ackMsg.getFailedServers());
+      }
+
+      // Mark this ack received for the server
+      expectedServersAckStatus.put(ackingServerId, true);
+      numKnownAckStatus++;
+    }
+
+    return (numKnownAckStatus == expectedServersAckStatus.size());
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public AckMsg createAck(boolean timeout)
+  {
+    AckMsg ack = new AckMsg(changeNumber);
+
+    // Fill collected errors info
+    ack.setHasTimeout(hasTimeout);
+    ack.setHasWrongStatus(hasWrongStatus);
+    ack.setHasReplayError(hasReplayError);
+    ack.setFailedServers(failedServers);
+
+    // Force anyway timeout flag if requested
+    if (timeout)
+      ack.setHasTimeout(true);
+
+    return ack;
+  }
+}
diff --git a/opends/src/server/org/opends/server/replication/server/ServerHandler.java b/opends/src/server/org/opends/server/replication/server/ServerHandler.java
index 238a243..df7211a 100644
--- a/opends/src/server/org/opends/server/replication/server/ServerHandler.java
+++ b/opends/src/server/org/opends/server/replication/server/ServerHandler.java
@@ -40,7 +40,6 @@
 import java.util.Date;
 import java.util.List;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
@@ -67,7 +66,6 @@
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeBuilder;
 import org.opends.server.types.Attributes;
-import org.opends.server.types.DN;
 import org.opends.server.types.InitializationException;
 import org.opends.server.util.TimeThread;
 
@@ -95,16 +93,12 @@
   private ProtocolSession session;
   private final MsgQueue msgQueue = new MsgQueue();
   private MsgQueue lateQueue = new MsgQueue();
-  private final Map<ChangeNumber, AckMessageList> waitingAcks =
-    new HashMap<ChangeNumber, AckMessageList>();
   private ReplicationServerDomain replicationServerDomain = null;
   private String serverURL;
   private int outCount = 0; // number of update sent to the server
 
   private int inCount = 0;  // number of updates received from the server
 
-  private int inAckCount = 0;
-  private int outAckCount = 0;
   private int maxReceiveQueue = 0;
   private int maxSendQueue = 0;
   private int maxReceiveDelay = 0;
@@ -120,7 +114,7 @@
   private ServerState serverState;
   private boolean activeWriter = true;
   private ServerWriter writer = null;
-  private DN baseDn = null;
+  private String baseDn = null;
   private int rcvWindow;
   private int rcvWindowSizeHalf;
   private int maxRcvWindow;
@@ -143,7 +137,7 @@
    * Properties filled only if remote server is a DS
    */
 
-  // Status of this DS
+  // Status of this DS (only used if this server handler represents a DS)
   private ServerStatus status = ServerStatus.INVALID_STATUS;
   // Referrals URLs this DS is exporting
   private List<String> refUrls = new ArrayList<String>();
@@ -182,9 +176,6 @@
    * Set when ServerHandler is stopping.
    */
   private AtomicBoolean shuttingDown = new AtomicBoolean(false);
-  private static final Map<ChangeNumber, ReplServerAckMessageList>
-    changelogsWaitingAcks =
-    new HashMap<ChangeNumber, ReplServerAckMessageList>();
 
   /**
    * Creates a new server handler instance with the provided socket.
@@ -266,7 +257,7 @@
    * @param replicationServer the ReplicationServer that created this server
    *                          handler.
    */
-  public void start(DN baseDn, short replicationServerId,
+  public void start(String baseDn, short replicationServerId,
     String replicationServerURL,
     int windowSize, boolean sslEncryption,
     ReplicationServer replicationServer)
@@ -571,7 +562,7 @@
               {
                 // Timeout
                 Message message = NOTE_TIMEOUT_WHEN_CROSS_CONNECTION.get(
-                  this.baseDn.toNormalizedString(),
+                  this.baseDn,
                   Short.toString(serverId),
                   Short.toString(replicationServer.getServerId()));
                 closeSession(message);
@@ -732,7 +723,7 @@
                     //     gen ID and so won't change without a reset
                     // then  we are just degrading the peer.
                     Message message = NOTE_BAD_GENERATION_ID_FROM_RS.get(
-                      this.baseDn.toNormalizedString(),
+                      this.baseDn,
                       Short.toString(serverId),
                       Long.toString(generationId),
                       Long.toString(localGenerationId));
@@ -759,7 +750,7 @@
                     // replicationServerDomain.
                     // setGenerationId(generationId, false);
                     Message message = NOTE_BAD_GENERATION_ID_FROM_RS.get(
-                      this.baseDn.toNormalizedString(),
+                      this.baseDn,
                       Short.toString(serverId),
                       Long.toString(generationId),
                       Long.toString(localGenerationId));
@@ -859,7 +850,7 @@
             if (generationId != localGenerationId)
             {
               Message message = NOTE_BAD_GENERATION_ID_FROM_DS.get(
-                this.baseDn.toNormalizedString(),
+                this.baseDn,
                 Short.toString(serverId),
                 Long.toString(generationId),
                 Long.toString(localGenerationId));
@@ -873,7 +864,7 @@
               // If the LDAP server has already sent changes
               // it is not expected to connect to an empty RS
               Message message = NOTE_BAD_GENERATION_ID_FROM_DS.get(
-                this.baseDn.toNormalizedString(),
+                this.baseDn,
                 Short.toString(serverId),
                 Long.toString(generationId),
                 Long.toString(localGenerationId));
@@ -957,7 +948,7 @@
                   //     gen ID and so won't change without a reset
                   // then  we are just degrading the peer.
                   Message message = NOTE_BAD_GENERATION_ID_FROM_RS.get(
-                    this.baseDn.toNormalizedString(),
+                    this.baseDn,
                     Short.toString(serverId),
                     Long.toString(generationId),
                     Long.toString(localGenerationId));
@@ -984,7 +975,7 @@
                   // replicationServerDomain.
                   // setGenerationId(generationId, false);
                   Message message = NOTE_BAD_GENERATION_ID_FROM_RS.get(
-                    this.baseDn.toNormalizedString(),
+                    this.baseDn,
                     Short.toString(serverId),
                     Long.toString(generationId),
                     Long.toString(localGenerationId));
@@ -1195,26 +1186,6 @@
   }
 
   /**
-   * Get the number of Ack received from the server managed by this handler.
-   *
-   * @return Returns the inAckCount.
-   */
-  public int getInAckCount()
-  {
-    return inAckCount;
-  }
-
-  /**
-   * Get the number of Ack sent to the server managed by this handler.
-   *
-   * @return Returns the outAckCount.
-   */
-  public int getOutAckCount()
-  {
-    return outAckCount;
-  }
-
-  /**
    * Check is this server is saturated (this server has already been
    * sent a bunch of updates and has not processed them so they are staying
    * in the message queue for this server an the size of the queue
@@ -1763,145 +1734,14 @@
   }
 
   /**
-   * Send the ack to the server that did the original modification.
+   * Sends an ack message to the server represented by this object.
    *
-   * @param changeNumber The ChangeNumber of the update that is acked.
+   * @param ack The ack message to be sent.
    * @throws IOException In case of Exception thrown sending the ack.
    */
-  public void sendAck(ChangeNumber changeNumber) throws IOException
+  public void sendAck(AckMsg ack) throws IOException
   {
-    AckMsg ack = new AckMsg(changeNumber);
     session.publish(ack);
-    outAckCount++;
-  }
-
-  /**
-   * Do the work when an ack message has been received from another server.
-   *
-   * @param message The ack message that was received.
-   * @param ackingServerId The  id of the server that acked the change.
-   */
-  public void ack(AckMsg message, short ackingServerId)
-  {
-    ChangeNumber changeNumber = message.getChangeNumber();
-    AckMessageList ackList;
-    boolean completedFlag;
-    synchronized (waitingAcks)
-    {
-      ackList = waitingAcks.get(changeNumber);
-      if (ackList == null)
-        return;
-      ackList.addAck(ackingServerId);
-      completedFlag = ackList.completed();
-      if (completedFlag)
-      {
-        waitingAcks.remove(changeNumber);
-      }
-    }
-    if (completedFlag)
-    {
-      replicationServerDomain.sendAck(changeNumber, true);
-    }
-  }
-
-  /**
-   * Process reception of an for an update that was received from a
-   * ReplicationServer.
-   *
-   * @param message the ack message that was received.
-   * @param ackingServerId The  id of the server that acked the change.
-   */
-  public static void ackChangelog(AckMsg message, short ackingServerId)
-  {
-    ChangeNumber changeNumber = message.getChangeNumber();
-    ReplServerAckMessageList ackList;
-    boolean completedFlag;
-    synchronized (changelogsWaitingAcks)
-    {
-      ackList = changelogsWaitingAcks.get(changeNumber);
-      if (ackList == null)
-        return;
-      ackList.addAck(ackingServerId);
-      completedFlag = ackList.completed();
-      if (completedFlag)
-      {
-        changelogsWaitingAcks.remove(changeNumber);
-      }
-    }
-    if (completedFlag)
-    {
-      ReplicationServerDomain replicationServerDomain =
-        ackList.getChangelogCache();
-      replicationServerDomain.sendAck(changeNumber, false,
-        ackList.getReplicationServerId());
-    }
-  }
-
-  /**
-   * Add an update to the list of update waiting for acks.
-   *
-   * @param update the update that must be added to the list
-   * @param nbWaitedAck  The number of ack that must be received before
-   *               the update is fully acked.
-   */
-  public void addWaitingAck(UpdateMsg update, int nbWaitedAck)
-  {
-    AckMessageList ackList = new AckMessageList(update.getChangeNumber(),
-      nbWaitedAck);
-    synchronized (waitingAcks)
-    {
-      waitingAcks.put(update.getChangeNumber(), ackList);
-    }
-  }
-
-  /**
-   * Add an update to the list of update received from a replicationServer and
-   * waiting for acks.
-   *
-   * @param update The update that must be added to the list.
-   * @param ChangelogServerId The identifier of the replicationServer that sent
-   *                          the update.
-   * @param replicationServerDomain The ReplicationServerDomain from which the
-   *                                change was processed and to which the ack
-   *                                must later be sent.
-   * @param nbWaitedAck The number of ack that must be received before
-   *                    the update is fully acked.
-   */
-  public static void addWaitingAck(
-    UpdateMsg update,
-    short ChangelogServerId, ReplicationServerDomain replicationServerDomain,
-    int nbWaitedAck)
-  {
-    ReplServerAckMessageList ackList =
-      new ReplServerAckMessageList(update.getChangeNumber(),
-      nbWaitedAck,
-      ChangelogServerId,
-      replicationServerDomain);
-    synchronized (changelogsWaitingAcks)
-    {
-      changelogsWaitingAcks.put(update.getChangeNumber(), ackList);
-    }
-  }
-
-  /**
-   * Get the size of the list of update waiting for acks.
-   *
-   * @return the size of the list of update waiting for acks.
-   */
-  public int getWaitingAckSize()
-  {
-    synchronized (waitingAcks)
-    {
-      return waitingAcks.size();
-    }
-  }
-
-  /**
-   * Increment the count of Acks received from this server.
-   */
-  public void incrementInAckCount()
-  {
-    inAckCount++;
   }
 
   /**
@@ -2001,13 +1841,13 @@
         .valueOf(serverId)));
     attributes.add(Attributes.create("base-dn", baseDn.toString()));
 
-    if (serverIsLDAPserver)
+    try
     {
       MonitorData md;
-      try
-      {
-        md = replicationServerDomain.getMonitorData();
+      md = replicationServerDomain.getMonitorData();
 
+      if (serverIsLDAPserver)
+      {
         // Oldest missing update
         Long approxFirstMissingDate = md.getApproxFirstMissingDate(serverId);
         if ((approxFirstMissingDate != null) && (approxFirstMissingDate > 0))
@@ -2017,7 +1857,7 @@
               "approx-older-change-not-synchronized", date.toString()));
           attributes.add(Attributes.create(
               "approx-older-change-not-synchronized-millis", String
-                  .valueOf(approxFirstMissingDate)));
+              .valueOf(approxFirstMissingDate)));
         }
 
         // Missing changes
@@ -2030,14 +1870,21 @@
         attributes.add(Attributes.create("approximate-delay", String
             .valueOf(delay)));
       }
-      catch (Exception e)
+      else
       {
-        // TODO: improve the log
-        // We failed retrieving the remote monitor data.
-        attributes.add(Attributes.create("error",
-            stackTraceToSingleLineString(e)));
+        // Missing changes
+        long missingChanges = md.getMissingChangesRS(serverId);
+        attributes.add(Attributes.create("missing-changes", String
+            .valueOf(missingChanges)));
       }
     }
+    catch (Exception e)
+    {
+      // TODO: improve the log
+      // We failed retrieving the remote monitor data.
+      attributes.add(Attributes.create("error",
+          stackTraceToSingleLineString(e)));
+    }
 
     attributes.add(
         Attributes.create("queue-size", String.valueOf(msgQueue.count())));
@@ -2056,14 +1903,6 @@
     attributes.add(Attributes.create("update-received", String
         .valueOf(getInCount())));
 
-    // Deprecated as long as assured is not exposed
-    attributes.add(Attributes.create("update-waiting-acks", String
-        .valueOf(getWaitingAckSize())));
-    attributes.add(Attributes.create("ack-sent", String
-        .valueOf(getOutAckCount())));
-    attributes.add(Attributes.create("ack-received", String
-        .valueOf(getInAckCount())));
-
     // Window stats
     attributes.add(Attributes.create("max-send-window", String
         .valueOf(sendWindowSize)));
@@ -2125,11 +1964,14 @@
     /*
      * Stop the remote LSHandler
      */
-    for (LightweightServerHandler lsh : directoryServers.values())
+    synchronized (directoryServers)
     {
-      lsh.stopHandler();
+      for (LightweightServerHandler lsh : directoryServers.values())
+      {
+        lsh.stopHandler();
+      }
+      directoryServers.clear();
     }
-    directoryServers.clear();
 
     /*
      * Stop the heartbeat thread.
@@ -2217,7 +2059,6 @@
       {
         WindowMsg msg = new WindowMsg(rcvWindowSizeHalf);
         session.publish(msg);
-        outAckCount++;
         rcvWindow += rcvWindowSizeHalf;
       }
     }
@@ -2302,23 +2143,26 @@
      */
     List<DSInfo> dsInfos = topoMsg.getDsList();
 
-    // Removes the existing structures
-    for (LightweightServerHandler lsh : directoryServers.values())
+    synchronized (directoryServers)
     {
-      lsh.stopHandler();
-    }
-    directoryServers.clear();
+      // Removes the existing structures
+      for (LightweightServerHandler lsh : directoryServers.values())
+      {
+        lsh.stopHandler();
+      }
+      directoryServers.clear();
 
-    // Creates the new structure according to the message received.
-    for (DSInfo dsInfo : dsInfos)
-    {
-      LightweightServerHandler lsh = new LightweightServerHandler(this,
-        serverId, dsInfo.getDsId(), dsInfo.getGenerationId(),
-        dsInfo.getGroupId(), dsInfo.getStatus(), dsInfo.getRefUrls(),
-        dsInfo.isAssured(), dsInfo.getAssuredMode(),
-        dsInfo.getSafeDataLevel());
-      lsh.startHandler();
-      directoryServers.put(lsh.getServerId(), lsh);
+      // Creates the new structure according to the message received.
+      for (DSInfo dsInfo : dsInfos)
+      {
+        LightweightServerHandler lsh = new LightweightServerHandler(this,
+            serverId, dsInfo.getDsId(), dsInfo.getGenerationId(),
+            dsInfo.getGroupId(), dsInfo.getStatus(), dsInfo.getRefUrls(),
+            dsInfo.isAssured(), dsInfo.getAssuredMode(),
+            dsInfo.getSafeDataLevel());
+        lsh.startHandler();
+        directoryServers.put(lsh.getServerId(), lsh);
+      }
     }
   }
 
@@ -2445,7 +2289,10 @@
    */
   public boolean hasRemoteLDAPServers()
   {
-    return !directoryServers.isEmpty();
+    synchronized (directoryServers)
+    {
+      return !directoryServers.isEmpty();
+    }
   }
 
   /**
@@ -2496,7 +2343,6 @@
       // TODO also log an error message.
       WindowMsg msg = new WindowMsg(rcvWindow);
       session.publish(msg);
-      outAckCount++;
     } else
     {
       // Both the LDAP server and the replication server believes that the
@@ -2556,17 +2402,10 @@
    */
   public Set<Short> getConnectedDirectoryServerIds()
   {
-    return directoryServers.keySet();
-  }
-
-  /**
-   * Get the map of connected DSs
-   * (to the RS represented by this server handler).
-   * @return The map of connected DSs
-   */
-  public Map<Short, LightweightServerHandler> getConnectedDSs()
-  {
-    return directoryServers;
+    synchronized (directoryServers)
+    {
+      return directoryServers.keySet();
+    }
   }
 
   /**
@@ -2716,4 +2555,32 @@
   {
     return protocolVersion;
   }
+
+  /**
+   * Add the DSinfos of the connected Directory Servers
+   * to the List of DSInfo provided as a parameter.
+   *
+   * @param dsInfos The List of DSInfo that should be updated
+   *                with the DSInfo for the directoryServers
+   *                connected to this ServerHandler.
+   */
+  public void addDSInfos(List<DSInfo> dsInfos)
+  {
+    synchronized (directoryServers)
+    {
+      for (LightweightServerHandler ls : directoryServers.values())
+      {
+        dsInfos.add(ls.toDSInfo());
+      }
+    }
+  }
+
+  /**
+   * Gets the group id of the server represented by this object.
+   * @return The group id of the server represented by this object.
+   */
+  public byte getGroupId()
+  {
+    return groupId;
+  }
 }
diff --git a/opends/src/server/org/opends/server/replication/server/ServerReader.java b/opends/src/server/org/opends/server/replication/server/ServerReader.java
index 78c70fa..bea8995 100644
--- a/opends/src/server/org/opends/server/replication/server/ServerReader.java
+++ b/opends/src/server/org/opends/server/replication/server/ServerReader.java
@@ -141,7 +141,7 @@
           {
             AckMsg ack = (AckMsg) msg;
             handler.checkWindow();
-            replicationServerDomain.ack(ack, serverId);
+            replicationServerDomain.processAck(ack, handler);
           } else if (msg instanceof UpdateMsg)
           {
             boolean filtered = false;
@@ -171,7 +171,7 @@
                   logError(ERR_IGNORING_UPDATE_FROM_DS_BADGENID.get(
                     Short.toString(replicationServerDomain.
                     getReplicationServer().getServerId()),
-                    replicationServerDomain.getBaseDn().toNormalizedString(),
+                    replicationServerDomain.getBaseDn(),
                     ((UpdateMsg) msg).getChangeNumber().toString(),
                     Short.toString(handler.getServerId()),
                     Long.toString(referenceGenerationId),
@@ -180,7 +180,7 @@
                   logError(ERR_IGNORING_UPDATE_FROM_DS_FULLUP.get(
                     Short.toString(replicationServerDomain.
                     getReplicationServer().getServerId()),
-                    replicationServerDomain.getBaseDn().toNormalizedString(),
+                    replicationServerDomain.getBaseDn(),
                     ((UpdateMsg) msg).getChangeNumber().toString(),
                     Short.toString(handler.getServerId())));
                 filtered = true;
@@ -199,7 +199,7 @@
                 logError(ERR_IGNORING_UPDATE_FROM_RS.get(
                   Short.toString(replicationServerDomain.getReplicationServer().
                   getServerId()),
-                  replicationServerDomain.getBaseDn().toNormalizedString(),
+                  replicationServerDomain.getBaseDn(),
                   ((UpdateMsg) msg).getChangeNumber().toString(),
                   Short.toString(handler.getServerId()),
                   Long.toString(referenceGenerationId),
diff --git a/opends/src/server/org/opends/server/replication/server/ServerWriter.java b/opends/src/server/org/opends/server/replication/server/ServerWriter.java
index 7ad2f45..90492e8 100644
--- a/opends/src/server/org/opends/server/replication/server/ServerWriter.java
+++ b/opends/src/server/org/opends/server/replication/server/ServerWriter.java
@@ -138,7 +138,7 @@
               logError(ERR_IGNORING_UPDATE_TO_DS_BADGENID.get(
                 Short.toString(replicationServerDomain.getReplicationServer().
                 getServerId()),
-                replicationServerDomain.getBaseDn().toNormalizedString(),
+                replicationServerDomain.getBaseDn(),
                 update.getChangeNumber().toString(),
                 Short.toString(handler.getServerId()),
                 Long.toString(handler.getGenerationId()),
@@ -147,7 +147,7 @@
               logError(ERR_IGNORING_UPDATE_TO_DS_FULLUP.get(
                 Short.toString(replicationServerDomain.getReplicationServer().
                 getServerId()),
-                replicationServerDomain.getBaseDn().toNormalizedString(),
+                replicationServerDomain.getBaseDn(),
                 update.getChangeNumber().toString(),
                 Short.toString(handler.getServerId())));
             continue;
@@ -166,7 +166,7 @@
             logError(ERR_IGNORING_UPDATE_TO_RS.get(
               Short.toString(replicationServerDomain.getReplicationServer().
               getServerId()),
-              replicationServerDomain.getBaseDn().toNormalizedString(),
+              replicationServerDomain.getBaseDn(),
               update.getChangeNumber().toString(),
               Short.toString(handler.getServerId()),
               Long.toString(handler.getGenerationId()),
diff --git a/opends/src/server/org/opends/server/replication/server/StatusAnalyzer.java b/opends/src/server/org/opends/server/replication/server/StatusAnalyzer.java
index 547f23c..0353276 100644
--- a/opends/src/server/org/opends/server/replication/server/StatusAnalyzer.java
+++ b/opends/src/server/org/opends/server/replication/server/StatusAnalyzer.java
@@ -75,7 +75,7 @@
     int degradedStatusThreshold)
   {
     super("Replication Server Status Analyzer for " +
-      replicationServerDomain.getBaseDn().toNormalizedString() + " in RS " +
+      replicationServerDomain.getBaseDn() + " in RS " +
       replicationServerDomain.getReplicationServer().getServerId());
 
     this.replicationServerDomain = replicationServerDomain;
diff --git a/opends/src/server/org/opends/server/tasks/InitializeTargetTask.java b/opends/src/server/org/opends/server/replication/service/InitializeTargetTask.java
similarity index 83%
rename from opends/src/server/org/opends/server/tasks/InitializeTargetTask.java
rename to opends/src/server/org/opends/server/replication/service/InitializeTargetTask.java
index 4882bdf..17c4231 100644
--- a/opends/src/server/org/opends/server/tasks/InitializeTargetTask.java
+++ b/opends/src/server/org/opends/server/replication/service/InitializeTargetTask.java
@@ -24,7 +24,12 @@
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
  */
-package org.opends.server.tasks;
+package org.opends.server.replication.service;
+import org.opends.server.tasks.TaskUtils;
+
+import org.opends.messages.TaskMessages;
+import org.opends.server.types.ResultCode;
+
 import org.opends.messages.MessageBuilder;
 
 import static org.opends.server.config.ConfigConstants.*;
@@ -36,18 +41,12 @@
 
 import org.opends.server.backends.task.Task;
 import org.opends.server.backends.task.TaskState;
-import org.opends.messages.TaskMessages;
-import org.opends.messages.Message;
-import org.opends.server.replication.plugin.ReplicationDomain;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeType;
-import org.opends.server.types.DN;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
 
 
-import org.opends.server.types.ResultCode;
-
 /**
  * This class provides an implementation of a Directory Server task that can
  * be used to import data from an LDIF file into a backend.
@@ -60,22 +59,10 @@
   private static final DebugTracer TRACER = getTracer();
 
   // Config properties
-  boolean append                  = false;
-  boolean isCompressed            = false;
-  boolean isEncrypted             = false;
-  boolean skipSchemaValidation    = false;
-  String  domainString            = null;
-  ReplicationDomain domain = null;
-  short target;
-  long total;
-  long left;
-
-  /**
-   * {@inheritDoc}
-   */
-  public Message getDisplayName() {
-    return TaskMessages.INFO_TASK_INITIALIZE_TARGET_NAME.get();
-  }
+  private String  domainString            = null;
+  private ReplicationDomain domain = null;
+  private short target;
+  private long total;
 
   /**
    * {@inheritDoc}
@@ -102,20 +89,17 @@
     attrList = taskEntry.getAttribute(typeDomainBase);
     domainString = TaskUtils.getSingleValueString(attrList);
 
-    DN domainDN = DN.nullDN();
     try
     {
-      domainDN = DN.decode(domainString);
+      domain = ReplicationDomain.retrievesReplicationDomain(domainString);
     }
-    catch(Exception e)
+    catch(DirectoryException e)
     {
       MessageBuilder mb = new MessageBuilder();
       mb.append(TaskMessages.ERR_TASK_INITIALIZE_INVALID_DN.get());
-      mb.append(e.getLocalizedMessage());
-      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
-              mb.toMessage());
+      mb.append(e.getMessage());
+      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX, e);
     }
-    domain=ReplicationDomain.retrievesReplicationDomain(domainDN);
 
     attrList = taskEntry.getAttribute(typeScope);
     String targetString = TaskUtils.getSingleValueString(attrList);
@@ -170,7 +154,6 @@
    */
   public void setLeft(long left)  throws DirectoryException
   {
-    this.left = left;
     replaceAttributeValue(ATTR_TASK_INITIALIZE_LEFT, String.valueOf(left));
     replaceAttributeValue(ATTR_TASK_INITIALIZE_DONE,String.valueOf(total-left));
   }
diff --git a/opends/src/server/org/opends/server/tasks/InitializeTask.java b/opends/src/server/org/opends/server/replication/service/InitializeTask.java
similarity index 90%
rename from opends/src/server/org/opends/server/tasks/InitializeTask.java
rename to opends/src/server/org/opends/server/replication/service/InitializeTask.java
index 88333b2..08c9412 100644
--- a/opends/src/server/org/opends/server/tasks/InitializeTask.java
+++ b/opends/src/server/org/opends/server/replication/service/InitializeTask.java
@@ -24,10 +24,16 @@
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
  */
-package org.opends.server.tasks;
-import org.opends.messages.Message;
+package org.opends.server.replication.service;
+import org.opends.server.tasks.TaskUtils;
+
+import org.opends.server.types.ResultCode;
+
 import org.opends.messages.MessageBuilder;
 
+
+import org.opends.messages.Message;
+
 import static org.opends.server.config.ConfigConstants.*;
 import static org.opends.server.core.DirectoryServer.getAttributeType;
 import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
@@ -39,13 +45,10 @@
 import org.opends.server.backends.task.Task;
 import org.opends.server.backends.task.TaskState;
 import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.replication.plugin.ReplicationDomain;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeType;
-import org.opends.server.types.DN;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
-import org.opends.server.types.ResultCode;
 
 /**
  * This class provides an implementation of a Directory Server task that can
@@ -59,13 +62,10 @@
    */
   private static final DebugTracer TRACER = getTracer();
 
-  boolean isCompressed            = false;
-  boolean isEncrypted             = false;
-  boolean skipSchemaValidation    = false;
-  String  domainString            = null;
-  short  source;
-  ReplicationDomain domain = null;
-  TaskState initState;
+  private String  domainString            = null;
+  private short  source;
+  private ReplicationDomain domain        = null;
+  private TaskState initState;
 
   // The total number of entries expected to be processed when this import
   // will end successfully
@@ -108,22 +108,19 @@
     List<Attribute> attrList;
     attrList = taskEntry.getAttribute(typeDomainBase);
     domainString = TaskUtils.getSingleValueString(attrList);
-    DN domainDN = DN.nullDN();
+
     try
     {
-      domainDN = DN.decode(domainString);
+      domain = ReplicationDomain.retrievesReplicationDomain(domainString);
     }
-    catch(Exception e)
+    catch(DirectoryException e)
     {
       MessageBuilder mb = new MessageBuilder();
       mb.append(TaskMessages.ERR_TASK_INITIALIZE_INVALID_DN.get());
       mb.append(e.getMessage());
-      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
-          mb.toMessage());
+      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX, e);
     }
 
-    domain = ReplicationDomain.retrievesReplicationDomain(domainDN);
-
     attrList = taskEntry.getAttribute(typeSourceScope);
     String sourceString = TaskUtils.getSingleValueString(attrList);
     source = domain.decodeSource(sourceString);
@@ -140,7 +137,7 @@
     if (debugEnabled())
     {
       TRACER.debugInfo("InitializeTask is starting domain: %s source:%d",
-                domain.getBaseDN(), source);
+                domain.getServiceID(), source);
     }
     initState = getTaskState();
     try
diff --git a/opends/src/server/org/opends/server/replication/plugin/ListenerThread.java b/opends/src/server/org/opends/server/replication/service/ListenerThread.java
similarity index 72%
rename from opends/src/server/org/opends/server/replication/plugin/ListenerThread.java
rename to opends/src/server/org/opends/server/replication/service/ListenerThread.java
index bb5f214..dd48f8a 100644
--- a/opends/src/server/org/opends/server/replication/plugin/ListenerThread.java
+++ b/opends/src/server/org/opends/server/replication/service/ListenerThread.java
@@ -24,9 +24,7 @@
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
  */
-package org.opends.server.replication.plugin;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
+package org.opends.server.replication.service;
 import org.opends.messages.Message;
 
 import static org.opends.server.loggers.ErrorLogger.logError;
@@ -53,23 +51,18 @@
   private ReplicationDomain repDomain;
   private boolean shutdown = false;
   private boolean done = false;
-  private LinkedBlockingQueue<UpdateToReplay> updateToReplayQueue;
 
 
   /**
    * Constructor for the ListenerThread.
    *
    * @param repDomain the replication domain that created this thread
-   * @param updateToReplayQueue The update messages queue we must
-   * store messages in
    */
-  public ListenerThread(ReplicationDomain repDomain,
-    LinkedBlockingQueue<UpdateToReplay> updateToReplayQueue)
+  public ListenerThread(ReplicationDomain repDomain)
   {
      super("Replication Listener for server id " + repDomain.getServerId() +
-         " and domain " + repDomain.getBaseDN());
+         " and domain " + repDomain.getServiceID());
      this.repDomain = repDomain;
-     this.updateToReplayQueue = updateToReplayQueue;
   }
 
   /**
@@ -101,24 +94,8 @@
         // queue
         while ((!shutdown) && ((updateMsg = repDomain.receive()) != null))
         {
-          // Put update message into the queue (block until some place in the
-          // queue is available)
-          UpdateToReplay updateToReplay =
-            new UpdateToReplay(updateMsg, repDomain);
-          boolean queued = false;
-          while (!queued && !shutdown)
-          {
-            // Use timedout method (offer) instead of put for being able to
-            // shutdown the thread
-            queued = updateToReplayQueue.offer(updateToReplay,
-              1L, TimeUnit.SECONDS);
-          }
-          if (!queued)
-          {
-            // Shutdown requested but could not push message: ensure this one is
-            // not lost and put it in the queue before dying
-            updateToReplayQueue.offer(updateToReplay);
-          }
+          if (repDomain.processUpdate(updateMsg) == true)
+            repDomain.processUpdateDoneSynchronous(updateMsg);
         }
         if (updateMsg == null)
           shutdown = true;
@@ -134,9 +111,6 @@
       }
     }
 
-    // Stop the HeartBeat thread
-    repDomain.getBroker().stopHeartBeat();
-
     done = true;
 
     if (debugEnabled())
@@ -161,7 +135,7 @@
         if (n >= FACTOR)
         {
           TRACER.debugInfo("Interrupting listener thread for dn " +
-            repDomain.getBaseDN() + " in DS " + repDomain.getServerId());
+            repDomain.getServiceID() + " in DS " + repDomain.getServerId());
           this.interrupt();
         }
       }
diff --git a/opends/src/server/org/opends/server/replication/plugin/ReplLDIFInputStream.java b/opends/src/server/org/opends/server/replication/service/ReplInputStream.java
similarity index 94%
rename from opends/src/server/org/opends/server/replication/plugin/ReplLDIFInputStream.java
rename to opends/src/server/org/opends/server/replication/service/ReplInputStream.java
index 3e66ee9..7e0bd4b 100644
--- a/opends/src/server/org/opends/server/replication/plugin/ReplLDIFInputStream.java
+++ b/opends/src/server/org/opends/server/replication/service/ReplInputStream.java
@@ -24,7 +24,7 @@
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
  */
-package org.opends.server.replication.plugin;
+package org.opends.server.replication.service;
 
 
 
@@ -36,7 +36,7 @@
  * This class creates an input stream that can be used to read entries generated
  * by SynchroLDIF as if they were being read from another source like a file.
  */
-public class ReplLDIFInputStream
+public class ReplInputStream
 extends InputStream
 {
   // Indicates whether this input stream has been closed.
@@ -51,14 +51,14 @@
 
   /**
    * Creates a new ReplLDIFInputStream that will import entries
-   * for a synchronzation domain.
+   * for a synchronization domain.
    *
    * @param domain The replication domain
    */
-  public ReplLDIFInputStream(ReplicationDomain domain)
+  public ReplInputStream(ReplicationDomain domain)
   {
     this.domain = domain;
-    closed       = false;
+    closed      = false;
   }
 
   /**
diff --git a/opends/src/server/org/opends/server/replication/service/ReplOutputStream.java b/opends/src/server/org/opends/server/replication/service/ReplOutputStream.java
new file mode 100644
index 0000000..76dcf92
--- /dev/null
+++ b/opends/src/server/org/opends/server/replication/service/ReplOutputStream.java
@@ -0,0 +1,82 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2006-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.service;
+
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This class creates an output stream that can be used to export entries
+ * to a synchronization domain.
+ */
+public class ReplOutputStream
+       extends OutputStream
+{
+  // The synchronization domain on which the export is done
+  ReplicationDomain domain;
+
+  // The current number of entries exported
+  private long numExportedEntries;
+  String entryBuffer = "";
+
+  /**
+   * Creates a new ReplLDIFOutputStream related to a replication
+   * domain.
+   *
+   * @param domain The replication domain
+   */
+  public ReplOutputStream(ReplicationDomain domain)
+  {
+    this.domain = domain;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void write(int i) throws IOException
+  {
+    throw new IOException("Invalid call");
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void write(byte b[], int off, int len) throws IOException
+  {
+    domain.exportLDIFEntry(b, off, len);
+  }
+
+  /**
+   * Return the number of exported entries.
+   * @return the numExportedEntries
+   */
+  public long getNumExportedEntries() {
+    return numExportedEntries;
+  }
+}
diff --git a/opends/src/server/org/opends/server/replication/plugin/ReplicationBroker.java b/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java
similarity index 81%
rename from opends/src/server/org/opends/server/replication/plugin/ReplicationBroker.java
rename to opends/src/server/org/opends/server/replication/service/ReplicationBroker.java
index 27a09c7..16e428d 100644
--- a/opends/src/server/org/opends/server/replication/plugin/ReplicationBroker.java
+++ b/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java
@@ -24,12 +24,15 @@
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
  */
-package org.opends.server.replication.plugin;
+package org.opends.server.replication.service;
+
+import org.opends.server.replication.protocol.HeartbeatMonitor;
 
 import org.opends.messages.*;
 
 import static org.opends.server.loggers.ErrorLogger.logError;
 import static org.opends.server.loggers.debug.DebugLogger.*;
+
 import org.opends.server.loggers.debug.DebugTracer;
 import static org.opends.messages.ReplicationMessages.*;
 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
@@ -45,34 +48,22 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.TreeSet;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
 import org.opends.server.api.DirectoryThread;
-import org.opends.server.protocols.asn1.ASN1OctetString;
-import org.opends.server.protocols.internal.InternalClientConnection;
-import org.opends.server.protocols.internal.InternalSearchListener;
-import org.opends.server.protocols.internal.InternalSearchOperation;
-import org.opends.server.protocols.ldap.LDAPFilter;
 import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.replication.common.DSInfo;
 import org.opends.server.replication.common.RSInfo;
 import org.opends.server.replication.common.ServerState;
 import org.opends.server.replication.common.ServerStatus;
 import org.opends.server.replication.protocol.*;
-import org.opends.server.types.DN;
-import org.opends.server.types.DereferencePolicy;
-import org.opends.server.types.ResultCode;
-import org.opends.server.types.SearchResultEntry;
-import org.opends.server.types.SearchResultReference;
-import org.opends.server.types.SearchScope;
 
 /**
  * The broker for Multi-master Replication.
  */
-public class ReplicationBroker implements InternalSearchListener
+public class ReplicationBroker
 {
 
   /**
@@ -83,23 +74,17 @@
   private Collection<String> servers;
   private boolean connected = false;
   private String replicationServer = "Not connected";
-  private TreeSet<FakeOperation> replayOperations;
   private ProtocolSession session = null;
   private final ServerState state;
-  private final DN baseDn;
+  private final String baseDn;
   private final short serverId;
-  private int maxSendDelay;
-  private int maxReceiveDelay;
-  private int maxSendQueue;
-  private int maxReceiveQueue;
   private Semaphore sendWindow;
   private int maxSendWindow;
-  private int rcvWindow;
-  private int halfRcvWindow;
-  private int maxRcvWindow;
+  private int rcvWindow = 100;
+  private int halfRcvWindow = rcvWindow/2;
+  private int maxRcvWindow = rcvWindow;
   private int timeout = 0;
   private short protocolVersion;
-  private long generationId = -1;
   private ReplSessionSecurity replSessionSecurity;
   // My group id
   private byte groupId = (byte) -1;
@@ -110,7 +95,7 @@
   // The server URL of the RS we are connected to
   private String rsServerUrl = null;
   // Our replication domain
-  private ReplicationDomain replicationDomain = null;
+  private ReplicationDomain domain = null;
 
   // Trick for avoiding a inner class for many parameters return for
   // performPhaseOneHandshake method.
@@ -142,6 +127,17 @@
   // Same group id poller thread
   private SameGroupIdPoller sameGroupIdPoller = null;
 
+  /*
+   * Properties for the last topology info received from the network.
+   */
+  // Info for other DSs.
+  // Warning: does not contain info for us (for our server id)
+  private List<DSInfo> dsList = new ArrayList<DSInfo>();
+  // Info for other RSs.
+  private List<RSInfo> rsList = new ArrayList<RSInfo>();
+
+  private long generationID;
+
   /**
    * Creates a new ReplicationServer Broker for a particular ReplicationDomain.
    *
@@ -152,13 +148,6 @@
    *              when negotiating the session with the replicationServer.
    * @param serverId The server ID that should be used by this broker
    *              when negotiating the session with the replicationServer.
-   * @param maxReceiveQueue The maximum size of the receive queue to use on
-   *                         the replicationServer.
-   * @param maxReceiveDelay The maximum replication delay to use on the
-   *                        replicationServer.
-   * @param maxSendQueue The maximum size of the send queue to use on
-   *                     the replicationServer.
-   * @param maxSendDelay The maximum send delay to use on the replicationServer.
    * @param window The size of the send and receive window to use.
    * @param heartbeatInterval The interval between heartbeats requested of the
    * replicationServer, or zero if no heartbeats are requested.
@@ -169,29 +158,32 @@
    * @param groupId The group id of our domain.
    */
   public ReplicationBroker(ReplicationDomain replicationDomain,
-    ServerState state, DN baseDn, short serverId, int maxReceiveQueue,
-    int maxReceiveDelay, int maxSendQueue, int maxSendDelay, int window,
-    long heartbeatInterval, long generationId,
+    ServerState state, String baseDn, short serverId, int window,
+    long generationId, long heartbeatInterval,
     ReplSessionSecurity replSessionSecurity, byte groupId)
   {
-    this.replicationDomain = replicationDomain;
+    this.domain = replicationDomain;
     this.baseDn = baseDn;
     this.serverId = serverId;
-    this.maxReceiveDelay = maxReceiveDelay;
-    this.maxSendDelay = maxSendDelay;
-    this.maxReceiveQueue = maxReceiveQueue;
-    this.maxSendQueue = maxSendQueue;
     this.state = state;
-    replayOperations =
-      new TreeSet<FakeOperation>(new FakeOperationComparator());
-    this.rcvWindow = window;
-    this.maxRcvWindow = window;
-    this.halfRcvWindow = window / 2;
-    this.heartbeatInterval = heartbeatInterval;
     this.protocolVersion = ProtocolVersion.getCurrentVersion();
-    this.generationId = generationId;
     this.replSessionSecurity = replSessionSecurity;
     this.groupId = groupId;
+    this.generationID = generationId;
+    this.heartbeatInterval = heartbeatInterval;
+    this.maxRcvWindow = window;
+    this.maxRcvWindow = window;
+    this.halfRcvWindow = window /2;
+  }
+
+  /**
+   * Start the ReplicationBroker.
+   */
+  public void start()
+  {
+    shutdown = false;
+    this.rcvWindow = this.maxRcvWindow;
+    this.connect();
   }
 
   /**
@@ -207,6 +199,7 @@
      */
     shutdown = false;
     this.servers = servers;
+
     if (servers.size() < 1)
     {
       Message message = NOTE_NEED_MORE_THAN_ONE_CHANGELOG_SERVER.get();
@@ -245,6 +238,18 @@
   }
 
   /**
+   * Gets the server id.
+   * @return The server id
+   */
+  private long getGenerationID()
+  {
+    if (domain != null)
+      return domain.getGenerationID();
+    else
+      return generationID;
+  }
+
+  /**
    * Gets the server url of the RS we are connected to.
    * @return The server url of the RS we are connected to
    */
@@ -324,12 +329,12 @@
 
     // May have created a broker with null replication domain for
     // unit test purpose.
-    if (replicationDomain != null)
+    if (domain != null)
     {
       // If a first connect or a connection failure occur, we go through here.
       // force status machine to NOT_CONNECTED_STATUS so that monitoring can
       // see that we are not connected.
-      replicationDomain.toNotConnectedStatus();
+      domain.toNotConnectedStatus();
     }
 
     // Stop any existing poller and heartbeat monitor from a previous session.
@@ -380,7 +385,8 @@
           ServerStatus initStatus =
             computeInitialServerStatus(replServerStartMsg.getGenerationId(),
             bestServerInfo.getServerState(),
-            replServerStartMsg.getDegradedStatusThreshold(), generationId);
+            replServerStartMsg.getDegradedStatusThreshold(),
+            this.getGenerationID());
 
           // Perfom session start (handshake phase 2)
           TopologyMsg topologyMsg = performPhaseTwoHandshake(bestServer,
@@ -414,81 +420,6 @@
               if ((tmpRsGroupId == groupId) ||
                 ((tmpRsGroupId != groupId) && !someServersWithSameGroupId))
               {
-                /*
-                 * We must not publish changes to a replicationServer that has
-                 * not seen all our previous changes because this could cause
-                 * some other ldap servers to miss those changes.
-                 * Check that the ReplicationServer has seen all our previous
-                 * changes.
-                 */
-                ChangeNumber replServerMaxChangeNumber =
-                  replServerStartMsg.getServerState().
-                  getMaxChangeNumber(serverId);
-
-                if (replServerMaxChangeNumber == null)
-                {
-                  replServerMaxChangeNumber = new ChangeNumber(0, 0, serverId);
-                }
-                ChangeNumber ourMaxChangeNumber =
-                  state.getMaxChangeNumber(serverId);
-
-                if ((ourMaxChangeNumber != null) &&
-                  (!ourMaxChangeNumber.olderOrEqual(replServerMaxChangeNumber)))
-                {
-
-                  // Replication server is missing some of our changes: let's
-                  // send them to him.
-
-                  Message message = DEBUG_GOING_TO_SEARCH_FOR_CHANGES.get();
-                  logError(message);
-
-                  /*
-                   * Get all the changes that have not been seen by this
-                   * replication server and populate the replayOperations
-                   * list.
-                   */
-                  InternalSearchOperation op = searchForChangedEntries(
-                    baseDn, replServerMaxChangeNumber, this);
-                  if (op.getResultCode() != ResultCode.SUCCESS)
-                  {
-                    /*
-                     * An error happened trying to search for the updates
-                     * This server will start acepting again new updates but
-                     * some inconsistencies will stay between servers.
-                     * Log an error for the repair tool
-                     * that will need to resynchronize the servers.
-                     */
-                    message = ERR_CANNOT_RECOVER_CHANGES.get(
-                      baseDn.toNormalizedString());
-                    logError(message);
-                  } else
-                  {
-                    for (FakeOperation replayOp : replayOperations)
-                    {
-                      ChangeNumber cn = replayOp.getChangeNumber();
-                      /*
-                       * Because the entry returned by the search operation
-                       * can contain old historical information, it is
-                       * possible that some of the FakeOperation are
-                       * actually older than the
-                       * Only send the Operation if it was newer than
-                       * the last ChangeNumber known by the Replication Server.
-                       */
-                      if (cn.newer(replServerMaxChangeNumber))
-                      {
-                        message =
-                          DEBUG_SENDING_CHANGE.get(
-                              replayOp.getChangeNumber().toString());
-                        logError(message);
-                        session.publish(replayOp.generateMessage());
-                      }
-                    }
-                    message = DEBUG_CHANGES_SENT.get();
-                    logError(message);
-                  }
-                  replayOperations.clear();
-                }
-
                 replicationServer = tmpReadableServerName;
                 maxSendWindow = replServerStartMsg.getWindowSize();
                 rsGroupId = replServerStartMsg.getGroupId();
@@ -497,11 +428,13 @@
 
                 // May have created a broker with null replication domain for
                 // unit test purpose.
-                if (replicationDomain != null)
+                if (domain != null)
                 {
-                  replicationDomain.setInitialStatus(initStatus);
-                  replicationDomain.receiveTopo(topologyMsg);
+                  domain.sessionInitiated(
+                      initStatus, replServerStartMsg.getServerState(),
+                      session);
                 }
+                receiveTopo(topologyMsg);
                 connected = true;
                 if (getRsGroupId() != groupId)
                 {
@@ -528,16 +461,10 @@
                 // Do not log connection error
                 newServerWithSameGroupId = true;
               }
-            } catch (IOException e)
-            {
-              Message message = ERR_PUBLISHING_FAKE_OPS.get(
-                baseDn.toNormalizedString(), bestServer,
-                e.getLocalizedMessage() + stackTraceToSingleLineString(e));
-              logError(message);
             } catch (Exception e)
             {
               Message message = ERR_COMPUTING_FAKE_OPS.get(
-                baseDn.toNormalizedString(), bestServer,
+                baseDn, bestServer,
                 e.getLocalizedMessage() + stackTraceToSingleLineString(e));
               logError(message);
             } finally
@@ -577,7 +504,7 @@
         this.sendWindow = new Semaphore(maxSendWindow);
         connectPhaseLock.notify();
 
-        if ((replServerStartMsg.getGenerationId() == this.generationId) ||
+        if ((replServerStartMsg.getGenerationId() == this.getGenerationID()) ||
           (replServerStartMsg.getGenerationId() == -1))
         {
           Message message =
@@ -586,7 +513,7 @@
             Short.toString(rsServerId),
             replicationServer,
             Short.toString(serverId),
-            Long.toString(this.generationId));
+            Long.toString(this.getGenerationID()));
           logError(message);
         } else
         {
@@ -594,7 +521,7 @@
             NOTE_NOW_FOUND_BAD_GENERATION_CHANGELOG.get(
             baseDn.toString(),
             replicationServer,
-            Long.toString(this.generationId),
+            Long.toString(this.getGenerationID()),
             Long.toString(replServerStartMsg.getGenerationId()));
           logError(message);
         }
@@ -664,7 +591,7 @@
 
         if (debugEnabled())
         {
-          TRACER.debugInfo("RB for dn " + baseDn.toNormalizedString() +
+          TRACER.debugInfo("RB for dn " + baseDn +
             " and with server id " + Short.toString(serverId) + " computed " +
             Integer.toString(nChanges) + " changes late.");
         }
@@ -744,10 +671,11 @@
       /*
        * Send our ServerStartMsg.
        */
-      ServerStartMsg serverStartMsg = new ServerStartMsg(serverId, baseDn,
-        maxReceiveDelay, maxReceiveQueue, maxSendDelay, maxSendQueue,
-        halfRcvWindow * 2, heartbeatInterval, state,
-        ProtocolVersion.getCurrentVersion(), generationId, isSslEncryption,
+      ServerStartMsg serverStartMsg = new ServerStartMsg(serverId,
+          baseDn, 0, 0, 0, 0,
+        maxRcvWindow, heartbeatInterval, state,
+        ProtocolVersion.getCurrentVersion(), this.getGenerationID(),
+        isSslEncryption,
         groupId);
       localSession.publish(serverStartMsg);
 
@@ -764,11 +692,11 @@
       }
 
       // Sanity check
-      DN repDn = replServerStartMsg.getBaseDn();
+      String repDn = replServerStartMsg.getBaseDn();
       if (!(this.baseDn.equals(repDn)))
       {
         Message message = ERR_DS_DN_DOES_NOT_MATCH.get(repDn.toString(),
-          this.baseDn.toString());
+          this.baseDn);
         logError(message);
         error = true;
       }
@@ -811,10 +739,10 @@
       if ( (e instanceof SocketTimeoutException) && debugEnabled() )
       {
         TRACER.debugInfo("Timeout trying to connect to RS " + server +
-          " for dn: " + baseDn.toNormalizedString());
+          " for dn: " + baseDn);
       }
       Message message = ERR_EXCEPTION_STARTING_SESSION_PHASE.get("1",
-        baseDn.toNormalizedString(), server, e.getLocalizedMessage() +
+        baseDn, server, e.getLocalizedMessage() +
         stackTraceToSingleLineString(e));
       if (keepConnection) // Log error message only for final connection
       {
@@ -880,13 +808,15 @@
       StartSessionMsg startSessionMsg = null;
       // May have created a broker with null replication domain for
       // unit test purpose.
-      if (replicationDomain != null)
+      if (domain != null)
       {
-        startSessionMsg = new StartSessionMsg(initStatus,
-          replicationDomain.getRefUrls(),
-          replicationDomain.isAssured(),
-          replicationDomain.getAssuredMode(),
-          replicationDomain.getAssuredSdLevel());
+        startSessionMsg =
+          new StartSessionMsg(
+              initStatus,
+              domain.getRefUrls(),
+              domain.isAssured(),
+              domain.getAssuredMode(),
+              domain.getAssuredSdLevel());
       } else
       {
         startSessionMsg =
@@ -912,7 +842,7 @@
     } catch (Exception e)
     {
       Message message = ERR_EXCEPTION_STARTING_SESSION_PHASE.get("2",
-        baseDn.toNormalizedString(), server, e.getLocalizedMessage() +
+        baseDn, server, e.getLocalizedMessage() +
         stackTraceToSingleLineString(e));
       logError(message);
 
@@ -950,7 +880,7 @@
    * @return The computed best replication server.
    */
   public static String computeBestReplicationServer(ServerState myState,
-    HashMap<String, ServerInfo> rsInfos, short serverId, DN baseDn,
+    HashMap<String, ServerInfo> rsInfos, short serverId, String baseDn,
     byte groupId)
   {
     /*
@@ -997,7 +927,7 @@
    * @return The computed best replication server.
    */
   private static String searchForBestReplicationServer(ServerState myState,
-    HashMap<String, ServerInfo> rsInfos, short serverId, DN baseDn)
+    HashMap<String, ServerInfo> rsInfos, short serverId, String baseDn)
   {
     /*
      * Find replication servers who are up to date (or more up to date than us,
@@ -1072,9 +1002,7 @@
        */
 
       Message message = NOTE_FOUND_CHANGELOGS_WITH_MY_CHANGES.get(
-        upToDateServers.size(),
-        baseDn.toNormalizedString(),
-        Short.toString(serverId));
+        upToDateServers.size(), baseDn, Short.toString(serverId));
       logError(message);
 
       /*
@@ -1154,7 +1082,7 @@
        */
       // lateOnes cannot be empty
       Message message = NOTE_COULD_NOT_FIND_CHANGELOG_WITH_MY_CHANGES.get(
-        baseDn.toNormalizedString(), lateOnes.size());
+        baseDn, lateOnes.size());
       logError(message);
 
       // Min of the shifts
@@ -1190,48 +1118,6 @@
   }
 
   /**
-   * Search for the changes that happened since fromChangeNumber
-   * based on the historical attribute. The only changes that will
-   * be send will be the one generated on the serverId provided in
-   * fromChangeNumber.
-   * @param baseDn the base DN
-   * @param fromChangeNumber The change number from which we want the changes
-   * @param resultListener that will process the entries returned.
-   * @return the internal search operation
-   * @throws Exception when raised.
-   */
-  public static InternalSearchOperation searchForChangedEntries(
-    DN baseDn,
-    ChangeNumber fromChangeNumber,
-    InternalSearchListener resultListener)
-    throws Exception
-  {
-    InternalClientConnection conn =
-      InternalClientConnection.getRootConnection();
-    Short serverId = fromChangeNumber.getServerId();
-
-    String maxValueForId = "ffffffffffffffff" +
-      String.format("%04x", serverId) + "ffffffff";
-
-    LDAPFilter filter = LDAPFilter.decode(
-       "(&(" + Historical.HISTORICALATTRIBUTENAME + ">=dummy:"
-       + fromChangeNumber + ")(" + Historical.HISTORICALATTRIBUTENAME +
-       "<=dummy:" + maxValueForId + "))");
-
-    LinkedHashSet<String> attrs = new LinkedHashSet<String>(1);
-    attrs.add(Historical.HISTORICALATTRIBUTENAME);
-    attrs.add(Historical.ENTRYUIDNAME);
-    attrs.add("*");
-    return conn.processSearch(
-      new ASN1OctetString(baseDn.toString()),
-      SearchScope.WHOLE_SUBTREE,
-      DereferencePolicy.NEVER_DEREF_ALIASES,
-      0, 0, false, filter,
-      attrs,
-      resultListener);
-  }
-
-  /**
    * Start the heartbeat monitor thread.
    */
   private void startHeartBeat()
@@ -1325,7 +1211,7 @@
       {
         MessageBuilder mb = new MessageBuilder();
         mb.append(NOTE_EXCEPTION_RESTARTING_SESSION.get(
-          baseDn.toNormalizedString(), e.getLocalizedMessage()));
+          baseDn, e.getLocalizedMessage()));
         mb.append(stackTraceToSingleLineString(e));
         logError(mb.toMessage());
       }
@@ -1413,7 +1299,7 @@
             }
           }
         }
-        if (!credit)
+        if ((!credit) && (currentWindowSemaphore.availablePermits() == 0))
         {
           // the window is still closed.
           // Send a WindowProbeMsg message to wakeup the receiver in case the
@@ -1436,7 +1322,7 @@
             if (debugEnabled())
             {
               debugInfo("ReplicationBroker.publish() " +
-                "IO exception raised : " + e.getLocalizedMessage());
+                "Interrupted exception raised : " + e.getLocalizedMessage());
             }
           }
         }
@@ -1479,7 +1365,13 @@
         {
           WindowMsg windowMsg = (WindowMsg) msg;
           sendWindow.release(windowMsg.getNumAck());
-        } else
+        }
+        else if (msg instanceof TopologyMsg)
+        {
+          TopologyMsg topoMsg = (TopologyMsg)msg;
+          receiveTopo(topoMsg);
+        }
+        else
         {
           return msg;
         }
@@ -1580,20 +1472,6 @@
   }
 
   /**
-   * Set the value of the generationId for that broker. Normally the
-   * generationId is set through the constructor but there are cases
-   * where the value of the generationId must be changed while the broker
-   * already exist for example after an on-line import.
-   *
-   * @param generationId The value of the generationId.
-   *
-   */
-  public void setGenerationId(long generationId)
-  {
-    this.generationId = generationId;
-  }
-
-  /**
    * Get the name of the replicationServer to which this broker is currently
    * connected.
    *
@@ -1606,39 +1484,6 @@
   }
 
   /**
-   * {@inheritDoc}
-   */
-  public void handleInternalSearchEntry(
-    InternalSearchOperation searchOperation,
-    SearchResultEntry searchEntry)
-  {
-    /*
-     * This call back is called at session establishment phase
-     * for each entry that has been changed by this server and the changes
-     * have not been sent to any Replication Server.
-     * The role of this method is to build equivalent operation from
-     * the historical information and add them in the replayOperations
-     * table.
-     */
-    Iterable<FakeOperation> updates =
-      Historical.generateFakeOperations(searchEntry);
-    for (FakeOperation op : updates)
-    {
-      replayOperations.add(op);
-    }
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public void handleInternalSearchReference(
-    InternalSearchOperation searchOperation,
-    SearchResultReference searchReference)
-  {
-    // TODO to be implemented
-  }
-
-  /**
    * Get the maximum receive window size.
    *
    * @return The maximum receive window size.
@@ -1694,31 +1539,41 @@
   }
 
   /**
-   * Change some config parameters.
+   * Change some configuration parameters.
    *
-   * @param replicationServers    The new list of replication servers.
-   * @param maxReceiveQueue     The max size of receive queue.
-   * @param maxReceiveDelay     The max receive delay.
-   * @param maxSendQueue        The max send queue.
-   * @param maxSendDelay        The max Send Delay.
+   * @param replicationServers  The new list of replication servers.
    * @param window              The max window size.
-   * @param heartbeatInterval   The heartbeat interval.
+   * @param heartbeatInterval   The heartBeat interval.
+   *
+   * @return                    A boolean indicating if the changes
+   *                            requires to restart the service.
    */
-  public void changeConfig(Collection<String> replicationServers,
-    int maxReceiveQueue, int maxReceiveDelay, int maxSendQueue,
-    int maxSendDelay, int window, long heartbeatInterval)
+  public boolean changeConfig(
+      Collection<String> replicationServers, int window, long heartbeatInterval)
   {
-    this.servers = replicationServers;
-    this.maxRcvWindow = window;
-    this.heartbeatInterval = heartbeatInterval;
-    this.maxReceiveDelay = maxReceiveDelay;
-    this.maxReceiveQueue = maxReceiveQueue;
-    this.maxSendDelay = maxSendDelay;
-    this.maxSendQueue = maxSendQueue;
+    // These parameters needs to be renegociated with the ReplicationServer
+    // so if they have changed, that requires restarting the session with
+    // the ReplicationServer.
+    Boolean needToRestartSession = false;
 
-    // For info, a new session with the replicationServer
-    // will be recreated in the replication domain
-    // to take into account the new configuration.
+    // A new session is necessary only when information regarding
+    // the connection is modified
+    if ((servers == null) ||
+        (!(replicationServers.size() == servers.size()
+        && replicationServers.containsAll(servers))) ||
+        window != this.maxRcvWindow  ||
+        heartbeatInterval != this.heartbeatInterval)
+    {
+      needToRestartSession = true;
+    }
+
+    this.servers = replicationServers;
+    this.rcvWindow = window;
+    this.maxRcvWindow = window;
+    this.halfRcvWindow = window / 2;
+    this.heartbeatInterval = heartbeatInterval;
+
+    return needToRestartSession;
   }
 
   /**
@@ -1900,14 +1755,13 @@
   {
     try
     {
-
       ChangeStatusMsg csMsg = new ChangeStatusMsg(ServerStatus.INVALID_STATUS,
         newStatus);
       session.publish(csMsg);
     } catch (IOException ex)
     {
       Message message = ERR_EXCEPTION_SENDING_CS.get(
-        baseDn.toNormalizedString(),
+        baseDn,
         Short.toString(serverId),
         ex.getLocalizedMessage() + stackTraceToSingleLineString(ex));
       logError(message);
@@ -1922,4 +1776,48 @@
   {
     this.groupId = groupId;
   }
+
+  /**
+   * Gets the info for DSs in the topology (except us).
+   * @return The info for DSs in the topology (except us)
+   */
+  public List<DSInfo> getDsList()
+  {
+    return dsList;
+  }
+
+  /**
+   * Gets the info for RSs in the topology (except the one we are connected
+   * to).
+   * @return The info for RSs in the topology (except the one we are connected
+   * to)
+   */
+  public List<RSInfo> getRsList()
+  {
+    return rsList;
+  }
+
+  /**
+   * Processes an incoming TopologyMsg.
+   * Updates the structures for the local view of the topology.
+   *
+   * @param topoMsg The topology information received from RS.
+   */
+  public void receiveTopo(TopologyMsg topoMsg)
+  {
+
+    if (debugEnabled())
+      TRACER.debugInfo("Replication domain " + baseDn
+        + " received topology info update:\n" + topoMsg);
+
+    // Store new lists
+    synchronized(getDsList())
+    {
+      synchronized(getRsList())
+      {
+        dsList = topoMsg.getDsList();
+        rsList = topoMsg.getRsList();
+      }
+    }
+  }
 }
diff --git a/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java b/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java
new file mode 100644
index 0000000..9586e00
--- /dev/null
+++ b/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java
@@ -0,0 +1,2433 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.service;
+
+import static org.opends.messages.ReplicationMessages.*;
+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.replication.common.StatusMachine.*;
+
+import org.opends.server.replication.common.ChangeNumberGenerator;
+
+import java.io.BufferedOutputStream;
+
+import org.opends.server.types.Attribute;
+
+import org.opends.server.core.DirectoryServer;
+
+
+import java.util.Set;
+
+import org.opends.server.replication.common.DSInfo;
+import org.opends.server.replication.common.RSInfo;
+
+import java.util.HashMap;
+
+import java.util.Map;
+
+import org.opends.server.config.ConfigException;
+import java.util.Collection;
+import org.opends.server.replication.protocol.ReplSessionSecurity;
+import org.opends.server.replication.protocol.ResetGenerationIdMsg;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+import org.opends.server.api.DirectoryThread;
+import org.opends.server.backends.task.Task;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.replication.common.AssuredMode;
+import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.replication.common.ServerState;
+import org.opends.server.replication.common.ServerStatus;
+import org.opends.server.replication.common.StatusMachine;
+import org.opends.server.replication.common.StatusMachineEvent;
+import org.opends.server.replication.protocol.AckMsg;
+import org.opends.server.replication.protocol.ChangeStatusMsg;
+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.HeartbeatMsg;
+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.ProtocolVersion;
+import org.opends.server.replication.protocol.ReplicationMsg;
+import org.opends.server.replication.protocol.RoutableMsg;
+import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.ResultCode;
+
+/**
+ * This class should be used as a base for Replication implementations.
+ * <p>
+ * It is intended that developer in need of a replication mechanism
+ * subclass this class with their own implementation.
+ * <p>
+ *   The startup phase of the ReplicationDomain subclass,
+ *   should read the list of replication servers from the configuration,
+ *   instantiate a {@link ServerState} then start the publish service
+ *   by calling
+ *   {@link #startPublishService(Collection, ServerState, int, long)}.
+ *   At this point it can start calling the {@link #publish(UpdateMsg)}
+ *   method if needed.
+ * <p>
+ *   When the startup phase reach the point when the subclass is ready
+ *   to handle updates the Replication Domain implementation should call the
+ *   {@link #startListenService()} method.
+ *   At this point a Listener thread is created on the Replication Service
+ *   and which can start receiving updates.
+ * <p>
+ *   When updates are received the Replication Service calls the
+ *   {@link #processUpdate(UpdateMsg)} method.
+ *   ReplicationDomain implementation should implement the appropriate code
+ *   for replaying the update on the local repository.
+ *   When fully done the subclass must call the
+ *   {@link #processUpdateDone(UpdateMsg)} method.
+ *   This allows to process the update asynchronously if necessary.
+ *
+ * <p>
+ *   To propagate changes to other replica, a ReplicationDomain implementation
+ *   must use the {@link #publish(UpdateMsg)} method.
+ * <p>
+ *   If the Full Initialization process is needed then implementation
+ *   for {@link #importBackend(InputStream)} and
+ *   {@link #exportBackend(OutputStream)} must be
+ *   provided.
+ * <p>
+ *   Full Initialization of a replica can be triggered by LDAP clients
+ *   by creating InitializeTasks or InitializeTargetTask.
+ *   Full initialization can also by triggered from the ReplicationDomain
+ *   implementation using methods {@link #initializeRemote(short, Task)}
+ *   or {@link #initializeFromRemote(short, Task)}.
+ * <p>
+ *   At shutdown time, the {@link #stopDomain()} method should be called to
+ *   cleanly stop the replication service.
+ */
+public abstract class ReplicationDomain
+{
+  /**
+   * Current status for this replicated domain.
+   */
+  private ServerStatus status = ServerStatus.NOT_CONNECTED_STATUS;
+
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+  /**
+   *  An identifier for the Replication Service.
+   *  All Replication Domain using this identifier will be connected
+   *  through the Replication Service.
+   */
+  private final String serviceID;
+
+  /**
+   * The identifier of this Replication Domain inside the
+   * Replication Service.
+   * Each Domain must use a unique ServerID.
+   */
+  private final short serverID;
+
+  /**
+   * The ReplicationBroker that is used by this ReplicationDomain to
+   * connect to the ReplicationService.
+   */
+  private ReplicationBroker broker;
+
+  /**
+   * This Map is used to store all outgoing assured messages in order
+   * to be able to correlate all the coming back acks to the original
+   * operation.
+   */
+  private final SortedMap<ChangeNumber, UpdateMsg> waitingAckMsgs =
+    new TreeMap<ChangeNumber, UpdateMsg>();
+
+
+  /**
+   * The context related to an import or export being processed
+   * Null when none is being processed.
+   */
+  private IEContext ieContext = null;
+
+  /**
+   * The Thread waiting for incoming update messages for this domain and pushing
+   * them to the global incoming update message queue for later processing by
+   * replay threads.
+   */
+  private ListenerThread listenerThread;
+
+  /**
+   * A Map used to store all the ReplicationDomains created on this server.
+   */
+  private static Map<String, ReplicationDomain> domains =
+    new HashMap<String, ReplicationDomain>();
+
+  /**
+   * The Monitor in charge of replication monitoring.
+   */
+  private ReplicationMonitor monitor;
+
+  /*
+   * Assured mode properties
+   */
+  // Is assured mode enabled or not for this domain ?
+  private boolean assured = false;
+  // Assured sub mode (used when assured is true)
+  private AssuredMode assuredMode = AssuredMode.SAFE_DATA_MODE;
+  // Safe Data level (used when assuredMode is SAFE_DATA)
+  private byte assuredSdLevel = (byte)1;
+  // The timeout in ms that should be used, when waiting for assured acks
+  private long assuredTimeout = 1000;
+
+  // Group id
+  private byte groupId = (byte)1;
+  // Referrals urls to be published to other servers of the topology
+  // TODO: fill that with all currently opened urls if no urls configured
+  private List<String> refUrls = new ArrayList<String>();
+
+  /**
+   * A set of counters used for Monitoring.
+   */
+  private AtomicInteger numProcessedUpdates = new AtomicInteger();
+  private AtomicInteger numRcvdUpdates = new AtomicInteger(0);
+  private AtomicInteger numSentUpdates = new AtomicInteger(0);
+
+  /* Assured replication monitoring counters */
+
+  // Number of updates sent in Assured Mode, Safe Read
+  private AtomicInteger assuredSrSentUpdates = new AtomicInteger(0);
+  // Number of updates sent in Assured Mode, Safe Read, that have been
+  // successfully acknowledged
+  private AtomicInteger assuredSrAcknowledgedUpdates = new AtomicInteger(0);
+  // Number of updates sent in Assured Mode, Safe Read, that have not been
+  // successfully acknowledged (either because of timeout, wrong status or error
+  // at replay)
+  private AtomicInteger assuredSrNotAcknowledgedUpdates =
+    new AtomicInteger(0);
+  // Number of updates sent in Assured Mode, Safe Read, that have not been
+  // successfully acknowledged because of timeout
+  private AtomicInteger assuredSrTimeoutUpdates = new AtomicInteger(0);
+  // Number of updates sent in Assured Mode, Safe Read, that have not been
+  // successfully acknowledged because of wrong status
+  private AtomicInteger assuredSrWrongStatusUpdates = new AtomicInteger(0);
+  // Number of updates sent in Assured Mode, Safe Read, that have not been
+  // successfully acknowledged because of replay error
+  private AtomicInteger assuredSrReplayErrorUpdates = new AtomicInteger(0);
+  // Multiple values allowed: number of updates sent in Assured Mode, Safe Read,
+  // that have not been successfully acknowledged (either because of timeout,
+  // wrong status or error at replay) for a particular server (DS or RS). String
+  // format: <server id>:<number of failed updates>
+  private Map<Short,Integer> assuredSrServerNotAcknowledgedUpdates =
+    new HashMap<Short,Integer>();
+  // Number of updates sent in Assured Mode, Safe Data
+  private AtomicInteger assuredSdSentUpdates = new AtomicInteger(0);
+  // Number of updates sent in Assured Mode, Safe Data, that have been
+  // successfully acknowledged
+  private AtomicInteger assuredSdAcknowledgedUpdates = new AtomicInteger(0);
+  // Number of updates sent in Assured Mode, Safe Data, that have not been
+  // successfully acknowledged because of timeout
+  private AtomicInteger assuredSdTimeoutUpdates = new AtomicInteger(0);
+  // Multiple values allowed: number of updates sent in Assured Mode, Safe Data,
+  // that have not been successfully acknowledged because of timeout for a
+  // particular RS. String format: <server id>:<number of failed updates>
+  private Map<Short,Integer> assuredSdServerTimeoutUpdates =
+    new HashMap<Short,Integer>();
+
+  /* Status related monitoring fields */
+
+  // Indicates the date when the status changed. This may be used to indicate
+  // the date the session with the current replication server started (when
+  // status is NORMAL for instance). All the above assured monitoring fields
+  // are also resetted each time the status is changed
+  private Date lastStatusChangeDate = new Date();
+
+  /**
+   * The state maintained by the Concrete Class.
+   */
+  private final ServerState state;
+
+  /**
+   * The generator that will be used to generate {@link ChangeNumber}
+   * for this domain.
+   */
+  private final ChangeNumberGenerator generator;
+
+  /**
+   * Returns the {@link ChangeNumberGenerator} that will be used to
+   * generate {@link ChangeNumber} for this domain.
+   *
+   * @return The {@link ChangeNumberGenerator} that will be used to
+   *         generate {@link ChangeNumber} for this domain.
+   */
+  public ChangeNumberGenerator getGenerator()
+  {
+    return generator;
+  }
+
+  /**
+   * Creates a ReplicationDomain with the provided parameters.
+   *
+   * @param serviceID  The identifier of the Replication Domain to which
+   *                   this object is participating.
+   * @param serverID   The identifier of the server that is participating
+   *                   to the Replication Domain.
+   *                   This identifier should be different for each server that
+   *                   is participating to a given Replication Domain.
+   */
+  public ReplicationDomain(String serviceID, short serverID)
+  {
+    this.serviceID = serviceID;
+    this.serverID = serverID;
+    this.state = new ServerState();
+    this.generator = new ChangeNumberGenerator(serverID, state);
+
+    domains.put(serviceID, this);
+  }
+
+  /**
+   * Set the initial status of the domain and perform necessary initializations.
+   * This method will be called by the Broker each time the ReplicationBroker
+   * establish a new session to a Replication Server.
+   *
+   * Implementations may override this method when they need to perform
+   * additional computing after session establishment.
+   * The default implementation should be sufficient for ReplicationDomains
+   * that don't need to perform additional computing.
+   *
+   * @param initStatus              The status to enter the state machine with.
+   * @param replicationServerState  The ServerState of the ReplicationServer
+   *                                with which the session was established.
+   * @param session                 The ProtocolSession that is currently used.
+   */
+  public void sessionInitiated(
+      ServerStatus initStatus,
+      ServerState replicationServerState,
+      ProtocolSession session)
+  {
+    // Sanity check: is it a valid initial status?
+    if (!isValidInitialStatus(initStatus))
+    {
+      Message msg = ERR_DS_INVALID_INIT_STATUS.get(initStatus.toString(),
+        serviceID, Short.toString(serverID));
+      logError(msg);
+    } else
+    {
+      status = initStatus;
+    }
+  }
+
+  /**
+   * Processes an incoming ChangeStatusMsg. Compute new status according to
+   * given order. Then update domain for being compliant with new status
+   * definition.
+   * @param csMsg The received status message
+   */
+  private void receiveChangeStatus(ChangeStatusMsg csMsg)
+  {
+    if (debugEnabled())
+      TRACER.debugInfo("Replication domain " + serviceID +
+        " received change status message:\n" + csMsg);
+
+    ServerStatus reqStatus = csMsg.getRequestedStatus();
+
+    // Translate requested status to a state machine event
+    StatusMachineEvent event = StatusMachineEvent.statusToEvent(reqStatus);
+    if (event == StatusMachineEvent.INVALID_EVENT)
+    {
+      Message msg = ERR_DS_INVALID_REQUESTED_STATUS.get(reqStatus.toString(),
+        serviceID, Short.toString(serverID));
+      logError(msg);
+      return;
+    }
+
+    // Set the new status to the requested one
+    setNewStatus(event);
+  }
+
+  /**
+   * Called when first connection or disconnection detected.
+   */
+  void toNotConnectedStatus()
+  {
+    // Go into not connected status
+    setNewStatus(StatusMachineEvent.TO_NOT_CONNECTED_STATUS_EVENT);
+  }
+
+  /**
+   * Perform whatever actions are needed to apply properties for being
+   * compliant with new status. Must be called in synchronized section for
+   * status. The new status is already set in status variable.
+   */
+  private void updateDomainForNewStatus()
+  {
+    switch (status)
+    {
+      case NOT_CONNECTED_STATUS:
+        break;
+      case NORMAL_STATUS:
+        break;
+      case DEGRADED_STATUS:
+        break;
+      case FULL_UPDATE_STATUS:
+        // Signal RS we just entered the full update status
+        broker.signalStatusChange(status);
+        break;
+      case BAD_GEN_ID_STATUS:
+        break;
+      default:
+        if (debugEnabled())
+          TRACER.debugInfo("updateDomainForNewStatus: unexpected status: " +
+            status);
+    }
+  }
+
+  /**
+   * Gets the status for this domain.
+   * @return The status for this domain.
+   */
+  public ServerStatus getStatus()
+  {
+    return status;
+  }
+
+  /**
+   * Gets the identifier of this domain.
+   *
+   * @return The identifier for this domain.
+   */
+  public String getServiceID()
+  {
+    return serviceID;
+  }
+
+  /**
+   * Get the server ID.
+   * @return The server ID.
+   */
+  public short getServerId()
+  {
+    return serverID;
+  }
+
+  /**
+   * Tells if assured replication is enabled for this domain.
+   * @return True if assured replication is enabled for this domain.
+   */
+  public boolean isAssured()
+  {
+    return assured;
+  }
+
+  /**
+   * Gives the mode for the assured replication of the domain.
+   * @return The mode for the assured replication of the domain.
+   */
+  public AssuredMode getAssuredMode()
+  {
+    return assuredMode;
+  }
+
+  /**
+   * Gives the assured level of the replication of the domain.
+   * @return The assured level of the replication of the domain.
+   */
+  public byte getAssuredSdLevel()
+  {
+    return assuredSdLevel;
+  }
+
+  /**
+   * Gives the assured timeout of the replication of the domain (in ms).
+   * @return The assured timeout of the replication of the domain.
+   */
+  public long getAssuredTimeout()
+  {
+    return assuredTimeout;
+  }
+
+  /**
+   * Gets the group id for this domain.
+   * @return The group id for this domain.
+   */
+  public byte getGroupId()
+  {
+    return groupId;
+  }
+
+  /**
+   * Gets the referrals URLs this domain publishes.
+   * @return The referrals URLs this domain publishes.
+   */
+  public List<String> getRefUrls()
+  {
+    return refUrls;
+  }
+
+  /**
+   * Gets the info for DSs in the topology (except us).
+   * @return The info for DSs in the topology (except us)
+   */
+  public List<DSInfo> getDsList()
+  {
+    return broker.getDsList();
+  }
+
+  /**
+   * Gets the info for RSs in the topology (except the one we are connected
+   * to).
+   * @return The info for RSs in the topology (except the one we are connected
+   * to)
+   */
+  public List<RSInfo> getRsList()
+  {
+    return broker.getRsList();
+  }
+
+
+  /**
+   * Gets the server ID of the Replication Server to which the domain
+   * is currently connected.
+   *
+   * @return The server ID of the Replication Server to which the domain
+   *         is currently connected.
+   */
+  public short getRsServerId()
+  {
+    return broker.getRsServerId();
+  }
+
+  /**
+   * Increment the number of processed updates.
+   */
+  private void incProcessedUpdates()
+  {
+    numProcessedUpdates.incrementAndGet();
+  }
+
+  /**
+   * get the number of updates replayed by the replication.
+   *
+   * @return The number of updates replayed by the replication
+   */
+  int getNumProcessedUpdates()
+  {
+    if (numProcessedUpdates != null)
+      return numProcessedUpdates.get();
+    else
+      return 0;
+  }
+
+  /**
+   * get the number of updates received by the replication plugin.
+   *
+   * @return the number of updates received
+   */
+  int getNumRcvdUpdates()
+  {
+    if (numRcvdUpdates != null)
+      return numRcvdUpdates.get();
+    else
+      return 0;
+  }
+
+  /**
+   * Get the number of updates sent by the replication plugin.
+   *
+   * @return the number of updates sent
+   */
+  int getNumSentUpdates()
+  {
+    if (numSentUpdates != null)
+      return numSentUpdates.get();
+    else
+      return 0;
+  }
+
+  /**
+   * Set the list of Referrals that should be returned when an
+   * operation needs to be redirected to this server.
+   *
+   * @param referralsUrl The list of referrals.
+   */
+  public void setURLs(Set<String> referralsUrl)
+  {
+    for (String url : referralsUrl)
+      this.refUrls.add(url);
+  }
+
+  /**
+   * Set the timeout of the assured replication.
+   *
+   * @param assuredTimeout the timeout of the assured replication.
+   */
+  public void setAssuredTimeout(long assuredTimeout)
+  {
+    this.assuredTimeout = assuredTimeout;
+  }
+
+  /**
+   * Sets the groupID.
+   *
+   * @param groupId The groupID.
+   */
+  public void setGroupId(byte groupId)
+  {
+    this.groupId = groupId;
+  }
+
+  /**
+   * Sets the level of assured replication.
+   *
+   * @param assuredSdLevel The level of assured replication.
+   */
+  public void setAssuredSdLevel(byte assuredSdLevel)
+  {
+    this.assuredSdLevel = assuredSdLevel;
+  }
+
+  /**
+   * Sets the assured replication mode.
+   *
+   * @param dataMode The assured replication mode.
+   */
+  public void setAssuredMode(AssuredMode dataMode)
+  {
+    this.assuredMode = dataMode;
+  }
+
+  /**
+   * Sets assured replication.
+   *
+   * @param assured A boolean indicating if assured replication should be used.
+   */
+  public void setAssured(boolean assured)
+  {
+    this.assured = assured;
+  }
+
+  /**
+   * Receives an update message from the replicationServer.
+   * also responsible for updating the list of pending changes
+   * @return the received message - null if none
+   */
+  UpdateMsg receive()
+  {
+    UpdateMsg update = null;
+
+    while (update == null)
+    {
+      InitializeRequestMsg initMsg = null;
+      ReplicationMsg msg;
+      try
+      {
+        msg = broker.receive();
+        if (msg == null)
+        {
+          // The server is in the shutdown process
+          return null;
+        }
+
+        if (debugEnabled())
+          if (!(msg instanceof HeartbeatMsg))
+            TRACER.debugVerbose("Message received <" + msg + ">");
+
+        if (msg instanceof AckMsg)
+        {
+          AckMsg ack = (AckMsg) msg;
+          receiveAck(ack);
+        }
+        else if (msg instanceof InitializeRequestMsg)
+        {
+          // Another server requests us to provide entries
+          // for a total update
+          initMsg = (InitializeRequestMsg)msg;
+        }
+        else if (msg instanceof InitializeTargetMsg)
+        {
+          // Another server is exporting its entries to us
+          InitializeTargetMsg importMsg = (InitializeTargetMsg) msg;
+
+          try
+          {
+            // This must be done while we are still holding the
+            // broker lock because we are now going to receive a
+            // bunch of entries from the remote server and we
+            // want the import thread to catch them and
+            // not the ListenerThread.
+            initialize(importMsg);
+          }
+          catch(DirectoryException de)
+          {
+            // Returns an error message to notify the sender
+            ErrorMsg errorMsg =
+              new ErrorMsg(importMsg.getsenderID(),
+                  de.getMessageObject());
+            MessageBuilder mb = new MessageBuilder();
+            mb.append(de.getMessageObject());
+            TRACER.debugInfo(Message.toString(mb.toMessage()));
+            broker.publish(errorMsg);
+          }
+        }
+        else if (msg instanceof ErrorMsg)
+        {
+          if (ieContext != null)
+          {
+            // This is an error termination for the 2 following cases :
+            // - either during an export
+            // - or before an import really started
+            //   For example, when we publish a request and the
+            //  replicationServer did not find any import source.
+            abandonImportExport((ErrorMsg)msg);
+          }
+          else
+          {
+            /*
+             * Log error message
+             */
+            ErrorMsg errorMsg = (ErrorMsg)msg;
+            logError(ERR_ERROR_MSG_RECEIVED.get(
+                errorMsg.getDetails()));
+          }
+        }
+        else if (msg instanceof ChangeStatusMsg)
+        {
+          ChangeStatusMsg csMsg = (ChangeStatusMsg)msg;
+          receiveChangeStatus(csMsg);
+        }
+        else if (msg instanceof UpdateMsg)
+        {
+          generator.adjust(((UpdateMsg) msg).getChangeNumber());
+          update = (UpdateMsg) msg;
+          generator.adjust(update.getChangeNumber());
+        }
+      }
+      catch (SocketTimeoutException e)
+      {
+        // just retry
+      }
+      // Test if we have received and export request message and
+      // if that's the case handle it now.
+      // This must be done outside of the portion of code protected
+      // by the broker lock so that we keep receiveing update
+      // when we are doing and export and so that a possible
+      // closure of the socket happening when we are publishing the
+      // entries to the remote can be handled by the other
+      // replay thread when they call this method and therefore the
+      // broker.receive() method.
+      if (initMsg != null)
+      {
+        // Do this work in a thread to allow replay thread continue working
+        ExportThread exportThread = new ExportThread(initMsg.getsenderID());
+        exportThread.start();
+      }
+    }
+
+    numRcvdUpdates.incrementAndGet();
+    return update;
+  }
+
+  /**
+   * Wait for the processing of an assured message.
+   *
+   * @param msg The UpdateMsg for which we are waiting for an ack.
+   * @throws TimeoutException When the configured timeout occurs waiting for the
+   * ack.
+   */
+  private void waitForAck(UpdateMsg msg) throws TimeoutException
+  {
+    // Wait for the ack to be received, timing out if necessary
+    long startTime = System.currentTimeMillis();
+    synchronized (msg)
+    {
+      ChangeNumber cn = msg.getChangeNumber();
+      while (waitingAckMsgs.containsKey(cn))
+      {
+        try
+        {
+          // WARNING: this timeout may be difficult to optimize: too low, it
+          // may use too much CPU, too high, it may penalize performance...
+          msg.wait(10);
+        } catch (InterruptedException e)
+        {
+          if (debugEnabled())
+          {
+            TRACER.debugInfo("waitForAck method interrupted for replication " +
+              "serviceID: " + serviceID);
+          }
+          break;
+        }
+        // Timeout ?
+        if ( (System.currentTimeMillis() - startTime) >= assuredTimeout )
+        {
+          // Timeout occured, be sure that ack is not being received and if so,
+          // remove the update from the wait list, log the timeout error and
+          // also update assured monitoring counters
+          UpdateMsg update;
+          synchronized (waitingAckMsgs)
+          {
+            update = waitingAckMsgs.remove(cn);
+          }
+
+          if (update != null)
+          {
+            // No luck, this is a real timeout
+            // Increment assured replication monitoring counters
+            switch (msg.getAssuredMode())
+            {
+              case SAFE_READ_MODE:
+                assuredSrNotAcknowledgedUpdates.incrementAndGet();
+                assuredSrTimeoutUpdates.incrementAndGet();
+                // Increment number of errors for our RS
+                updateAssuredErrorsByServer(
+                  assuredSrServerNotAcknowledgedUpdates,
+                  broker.getRsServerId());
+                break;
+              case SAFE_DATA_MODE:
+                assuredSdTimeoutUpdates.incrementAndGet();
+                // Increment number of errors for our RS
+                updateAssuredErrorsByServer(assuredSdServerTimeoutUpdates,
+                  broker.getRsServerId());
+                break;
+              default:
+              // Should no happen
+            }
+
+            throw new TimeoutException("No ack received for message cn: " + cn +
+              " and replication servceID: " + serviceID + " after " +
+              assuredTimeout + " ms.");
+          } else
+          {
+            // Ack received just before timeout limit: we can exit
+            break;
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Updates the passed monitoring list of errors received for assured messages
+   * (safe data or safe read, depending of the passed list to update) for a
+   * particular server in the list. This increments the counter of error for the
+   * passed server, or creates an initial value of 1 error for it if the server
+   * is not yet present in the map.
+   * @param errorList
+   * @param serverId
+   */
+  private void updateAssuredErrorsByServer(Map<Short,Integer> errorsByServer,
+    Short serverId)
+  {
+    synchronized (errorsByServer)
+    {
+      Integer serverErrCount = errorsByServer.get(serverId);
+      if (serverErrCount == null)
+      {
+        // Server not present in list, create an entry with an
+        // initial number of errors set to 1
+        errorsByServer.put(serverId, 1);
+      } else
+      {
+        // Server already present in list, just increment number of
+        // errors for the server
+        int val = serverErrCount.intValue();
+        val++;
+        errorsByServer.put(serverId, val);
+      }
+    }
+  }
+
+  /**
+   * Do the necessary processing when an AckMsg is received.
+   *
+   * @param ack The AckMsg that was received.
+   */
+  private void receiveAck(AckMsg ack)
+  {
+    UpdateMsg update;
+    ChangeNumber changeNumber = ack.getChangeNumber();
+
+    // Remove the message for pending ack list (this may already make the thread
+    // that is waiting for the ack be aware of its reception)
+    synchronized (waitingAckMsgs)
+    {
+      update = waitingAckMsgs.remove(changeNumber);
+    }
+
+    // Signal waiting thread ack has been received
+    if (update != null)
+    {
+      synchronized (update)
+      {
+        update.notify();
+      }
+
+      // Analyze status of embedded in the ack to see if everything went well
+      boolean hasTimeout = ack.hasTimeout();
+      boolean hasReplayErrors = ack.hasReplayError();
+      boolean hasWrongStatus = ack.hasWrongStatus();
+
+      AssuredMode updateAssuredMode = update.getAssuredMode();
+
+      if ( hasTimeout || hasReplayErrors || hasWrongStatus)
+      {
+        // Some problems detected: message not correclty reached every requested
+        // servers. Log problem
+        Message errorMsg = ERR_DS_RECEIVED_ACK_ERROR.get(serviceID,
+          update.toString(), ack.errorsToString());
+        logError(errorMsg);
+
+        List<Short> failedServers = ack.getFailedServers();
+
+        // Increment assured replication monitoring counters
+        switch (updateAssuredMode)
+        {
+          case SAFE_READ_MODE:
+            assuredSrNotAcknowledgedUpdates.incrementAndGet();
+            if (hasTimeout)
+              assuredSrTimeoutUpdates.incrementAndGet();
+            if (hasReplayErrors)
+              assuredSrReplayErrorUpdates.incrementAndGet();
+            if (hasWrongStatus)
+              assuredSrWrongStatusUpdates.incrementAndGet();
+            if (failedServers != null) // This should always be the case !
+            {
+              for(Short sid : failedServers)
+              {
+                updateAssuredErrorsByServer(
+                  assuredSrServerNotAcknowledgedUpdates, sid);
+              }
+            }
+            break;
+          case SAFE_DATA_MODE:
+            // The only possible cause of ack error in safe data mode is timeout
+            if (hasTimeout) // So should always be the case
+              assuredSdTimeoutUpdates.incrementAndGet();
+            if (failedServers != null) // This should always be the case !
+            {
+              for(Short sid : failedServers)
+              {
+                updateAssuredErrorsByServer(
+                  assuredSdServerTimeoutUpdates, sid);
+              }
+            }
+            break;
+          default:
+          // Should no happen
+        }
+      } else
+      {
+        // Update has been acknowledged without errors
+        // Increment assured replication monitoring counters
+        switch (updateAssuredMode)
+        {
+          case SAFE_READ_MODE:
+            assuredSrAcknowledgedUpdates.incrementAndGet();
+            break;
+          case SAFE_DATA_MODE:
+            assuredSdAcknowledgedUpdates.incrementAndGet();
+            break;
+          default:
+          // Should no happen
+        }
+      }
+    }
+  }
+
+  /**
+   * Retrieves a replication domain based on the baseDn.
+   *
+   * @param serviceID           The identifier of the domain to retrieve.
+   *
+   * @return                    The domain retrieved.
+   *
+   * @throws DirectoryException When an error occurred or no domain
+   *                            match the provided baseDn.
+   */
+  static ReplicationDomain retrievesReplicationDomain(String serviceID)
+  throws DirectoryException
+  {
+    ReplicationDomain replicationDomain = domains.get(serviceID);
+    if (replicationDomain == null)
+    {
+      MessageBuilder mb = new MessageBuilder(ERR_NO_MATCHING_DOMAIN.get());
+      mb.append(" ");
+      mb.append(serviceID);
+      throw new DirectoryException(ResultCode.OTHER,
+         mb.toMessage());
+    }
+    return replicationDomain;
+  }
+
+  /*
+   * After this point the code is related to the Total Update.
+   */
+
+  /**
+   * This thread is launched when we want to export data to another server that
+   * has requested to be initialized with the data of our backend.
+   */
+  private class ExportThread extends DirectoryThread
+  {
+    // Id of server that will receive updates
+    private short target;
+
+    /**
+     * Constructor for the ExportThread.
+     *
+     * @param target Id of server that will receive updates
+     */
+    public ExportThread(short target)
+    {
+      super("Export thread " + serverID);
+      this.target = target;
+    }
+
+    /**
+     * Run method for this class.
+     */
+    public void run()
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugInfo("Export thread starting.");
+      }
+
+      try
+      {
+        initializeRemote(target, target, null);
+      } catch (DirectoryException de)
+      {
+      // An error message has been sent to the peer
+      // Nothing more to do locally
+      }
+      if (debugEnabled())
+      {
+        TRACER.debugInfo("Export thread stopping.");
+      }
+    }
+  }
+
+  /**
+   * This class contain the context related to an import or export
+   * launched on the domain.
+   */
+  private class IEContext
+  {
+    // The task that initiated the operation.
+    Task initializeTask;
+    // The input stream for the import
+    ReplInputStream ldifImportInputStream = null;
+    // The target in the case of an export
+    short exportTarget = RoutableMsg.UNKNOWN_SERVER;
+    // The source in the case of an import
+    short importSource = RoutableMsg.UNKNOWN_SERVER;
+
+    // The total entry count expected to be processed
+    long entryCount = 0;
+    // The count for the entry not yet processed
+    long entryLeftCount = 0;
+
+    // The exception raised when any
+    DirectoryException exception = null;
+
+    // A boolean indicating if the context is related to an
+    // import or an export.
+    boolean importInProgress;
+
+    /**
+     * Creates a new IEContext.
+     *
+     * @param importInProgress true if the IEContext will be used
+     *                         for and import, false if the IEContext
+     *                         will be used for and export.
+     */
+    public IEContext(boolean importInProgress)
+    {
+      this.importInProgress = importInProgress;
+    }
+
+    /**
+     * Initializes the import/export counters with the provider value.
+     * @param total
+     * @param left
+     * @throws DirectoryException
+     */
+    public void setCounters(long total, long left)
+      throws DirectoryException
+    {
+      entryCount = total;
+      entryLeftCount = left;
+
+      if (initializeTask != null)
+      {
+        if (initializeTask instanceof InitializeTask)
+        {
+          ((InitializeTask)initializeTask).setTotal(entryCount);
+          ((InitializeTask)initializeTask).setLeft(entryCount);
+        }
+        else if (initializeTask instanceof InitializeTargetTask)
+        {
+          ((InitializeTargetTask)initializeTask).setTotal(entryCount);
+          ((InitializeTargetTask)initializeTask).setLeft(entryCount);
+        }
+      }
+    }
+
+    /**
+     * Update the counters of the task for each entry processed during
+     * an import or export.
+     * @throws DirectoryException
+     */
+    public void updateCounters()
+      throws DirectoryException
+    {
+      entryLeftCount--;
+
+      if (initializeTask != null)
+      {
+        if (initializeTask instanceof InitializeTask)
+        {
+          ((InitializeTask)initializeTask).setLeft(entryLeftCount);
+        }
+        else if (initializeTask instanceof InitializeTargetTask)
+        {
+          ((InitializeTargetTask)initializeTask).setLeft(entryLeftCount);
+        }
+      }
+    }
+
+    /**
+     * Update the counters of the task for each entry processed during
+     * an import or export.
+     *
+     * @param  entriesDone The number of entries that were processed
+     *                     since the last time this method was called.
+     *
+     * @throws DirectoryException
+     */
+    public void updateCounters(int entriesDone)
+      throws DirectoryException
+    {
+      entryLeftCount -= entriesDone;
+
+      if (initializeTask != null)
+      {
+        if (initializeTask instanceof InitializeTask)
+        {
+          ((InitializeTask)initializeTask).setLeft(entryLeftCount);
+        }
+        else if (initializeTask instanceof InitializeTargetTask)
+        {
+          ((InitializeTargetTask)initializeTask).setLeft(entryLeftCount);
+        }
+      }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString()
+    {
+      return new String("[ Entry count=" + this.entryCount +
+                        ", Entry left count=" + this.entryLeftCount + "]");
+    }
+  }
+  /**
+   * Verifies that the given string represents a valid source
+   * from which this server can be initialized.
+   *
+   * @param targetString The string representing the source
+   * @return The source as a short value
+   * @throws DirectoryException if the string is not valid
+   */
+  short decodeTarget(String targetString)
+  throws DirectoryException
+  {
+    short  target = 0;
+    Throwable cause;
+    if (targetString.equalsIgnoreCase("all"))
+    {
+      return RoutableMsg.ALL_SERVERS;
+    }
+
+    // So should be a serverID
+    try
+    {
+      target = Integer.decode(targetString).shortValue();
+      if (target >= 0)
+      {
+        // FIXME Could we check now that it is a know server in the domain ?
+      }
+      return target;
+    }
+    catch(Exception e)
+    {
+      cause = e;
+    }
+    ResultCode resultCode = ResultCode.OTHER;
+    Message message = ERR_INVALID_EXPORT_TARGET.get();
+
+    if (cause != null)
+      throw new DirectoryException(
+          resultCode, message, cause);
+    else
+      throw new DirectoryException(
+          resultCode, message);
+
+  }
+
+  /**
+   * Process the initialization of some other server or servers in the topology
+   * specified by the target argument.
+   * @param target The target that should be initialized
+   * @param initTask The task that triggers this initialization and that should
+   *                 be updated with its progress.
+   *
+   * @exception DirectoryException When an error occurs.
+   */
+  void initializeRemote(short target, Task initTask)
+  throws DirectoryException
+  {
+    initializeRemote(target, serverID, initTask);
+  }
+
+  /**
+   * Process the initialization of some other server or servers in the topology
+   * specified by the target argument when this initialization specifying the
+   * server that requests the initialization.
+   *
+   * @param target The target that should be initialized.
+   * @param requestorID The server that initiated the export.
+   * @param initTask The task that triggers this initialization and that should
+   *  be updated with its progress.
+   *
+   * @exception DirectoryException When an error occurs.
+   */
+  void initializeRemote(short target, short requestorID, Task initTask)
+  throws DirectoryException
+  {
+    Message msg = NOTE_FULL_UPDATE_ENGAGED_FOR_REMOTE_START.get(
+      Short.toString(serverID),
+      serviceID,
+      Short.toString(requestorID));
+    logError(msg);
+
+    boolean contextAcquired=false;
+
+    acquireIEContext(false);
+    contextAcquired = true;
+    ieContext.exportTarget = target;
+
+    if (initTask != null)
+    {
+      ieContext.initializeTask = initTask;
+    }
+
+    // The number of entries to be exported is the number of entries under
+    // the base DN entry and the base entry itself.
+    long entryCount = this.countEntries();
+
+
+    ieContext.setCounters(entryCount, entryCount);
+
+    // Send start message to the peer
+    InitializeTargetMsg initializeMessage = new InitializeTargetMsg(
+        serviceID, serverID, target, requestorID, entryCount);
+
+    broker.publish(initializeMessage);
+
+    try
+    {
+      exportBackend(new BufferedOutputStream(new ReplOutputStream(this)));
+
+      // Notify the peer of the success
+      DoneMsg doneMsg = new DoneMsg(serverID,
+          initializeMessage.getDestination());
+      broker.publish(doneMsg);
+
+      releaseIEContext();
+    }
+    catch(DirectoryException de)
+    {
+      // Notify the peer of the failure
+      ErrorMsg errorMsg =
+        new ErrorMsg(target,
+                         de.getMessageObject());
+      broker.publish(errorMsg);
+
+      if (contextAcquired)
+        releaseIEContext();
+
+      throw(de);
+    }
+
+    msg = NOTE_FULL_UPDATE_ENGAGED_FOR_REMOTE_END.get(
+      Short.toString(serverID),
+      serviceID,
+      Short.toString(requestorID));
+    logError(msg);
+  }
+
+  /**
+   * Get the ServerState maintained by the Concrete class.
+   *
+   * @return the ServerState maintained by the Concrete class.
+   */
+  public ServerState getServerState()
+  {
+    return state;
+  }
+
+
+  private synchronized void acquireIEContext(boolean importInProgress)
+  throws DirectoryException
+  {
+    if (ieContext != null)
+    {
+      // Rejects 2 simultaneous exports
+      Message message = ERR_SIMULTANEOUS_IMPORT_EXPORT_REJECTED.get();
+      throw new DirectoryException(ResultCode.OTHER,
+          message);
+    }
+
+    ieContext = new IEContext(importInProgress);
+  }
+
+  private synchronized void releaseIEContext()
+  {
+    ieContext = null;
+  }
+
+  /**
+   * Processes an error message received while an import/export is
+   * on going.
+   * @param errorMsg The error message received.
+   */
+  void abandonImportExport(ErrorMsg errorMsg)
+  {
+    // FIXME TBD Treat the case where the error happens while entries
+    // are being exported
+
+    if (debugEnabled())
+      TRACER.debugVerbose(
+          " abandonImportExport:" + this.serverID +
+          " serviceID: " + this.serviceID +
+          " Error Msg received: " + errorMsg);
+
+    if (ieContext != null)
+    {
+      ieContext.exception = new DirectoryException(ResultCode.OTHER,
+          errorMsg.getDetails());
+
+      if (ieContext.initializeTask instanceof InitializeTask)
+      {
+        // Update the task that initiated the import
+        ((InitializeTask)ieContext.initializeTask).
+        updateTaskCompletionState(ieContext.exception);
+
+        releaseIEContext();
+      }
+    }
+  }
+
+  /**
+   * Receives bytes related to an entry in the context of an import to
+   * initialize the domain (called by ReplLDIFInputStream).
+   *
+   * @return The bytes. Null when the Done or Err message has been received
+   */
+  byte[] receiveEntryBytes()
+  {
+    ReplicationMsg msg;
+    while (true)
+    {
+      try
+      {
+        msg = broker.receive();
+
+        if (debugEnabled())
+          TRACER.debugVerbose(
+              " sid:" + serverID +
+              " base DN:" + serviceID +
+              " Import EntryBytes received " + msg);
+        if (msg == null)
+        {
+          // The server is in the shutdown process
+          return null;
+        }
+
+        if (msg instanceof EntryMsg)
+        {
+          EntryMsg entryMsg = (EntryMsg)msg;
+          byte[] entryBytes = entryMsg.getEntryBytes();
+          ieContext.updateCounters(countEntryLimits(entryBytes));
+          return entryBytes;
+        }
+        else if (msg instanceof DoneMsg)
+        {
+          // This is the normal termination of the import
+          // No error is stored and the import is ended
+          // by returning null
+          return null;
+        }
+        else if (msg instanceof ErrorMsg)
+        {
+          // This is an error termination during the import
+          // The error is stored and the import is ended
+          // by returning null
+          ErrorMsg errorMsg = (ErrorMsg)msg;
+          ieContext.exception = new DirectoryException(
+                                      ResultCode.OTHER,
+                                      errorMsg.getDetails());
+          return null;
+        }
+        else
+        {
+          // Other messages received during an import are trashed
+        }
+      }
+      catch(Exception e)
+      {
+        // TODO: i18n
+        ieContext.exception = new DirectoryException(ResultCode.OTHER,
+            Message.raw("received an unexpected message type" +
+                e.getLocalizedMessage()));
+      }
+    }
+  }
+
+  /**
+   * Count the number of entries in the provided byte[].
+   * This is based on the hypothesis that the entries are separated
+   * by a "\n\n" String.
+   *
+   * @param   entryBytes
+   * @return  The number of entries in the provided byte[].
+   */
+  private int countEntryLimits(byte[] entryBytes)
+  {
+    return countEntryLimits(entryBytes, 0, entryBytes.length);
+  }
+
+  /**
+   * Count the number of entries in the provided byte[].
+   * This is based on the hypothesis that the entries are separated
+   * by a "\n\n" String.
+   *
+   * @param   entryBytes
+   * @return  The number of entries in the provided byte[].
+   */
+  private int countEntryLimits(byte[] entryBytes, int pos, int length)
+  {
+    int entryCount = 0;
+    int count = 0;
+    while (count<=length-2)
+    {
+      if ((entryBytes[pos+count] == '\n') && (entryBytes[pos+count+1] == '\n'))
+      {
+        entryCount++;
+        count++;
+      }
+      count++;
+    }
+    return entryCount;
+  }
+
+  /**
+   * Exports an entry in LDIF format.
+   *
+   * @param lDIFEntry The entry to be exported in byte[] form.
+   * @param pos       The starting Position in the array.
+   * @param length    Number of array elements to be copied.
+   *
+   * @throws IOException when an error occurred.
+   */
+  void exportLDIFEntry(byte[] lDIFEntry, int pos, int length) throws IOException
+  {
+    // If an error was raised - like receiving an ErrorMsg
+    // we just let down the export.
+    if (ieContext.exception != null)
+    {
+      IOException ioe = new IOException(ieContext.exception.getMessage());
+      ieContext = null;
+      throw ioe;
+    }
+
+    EntryMsg entryMessage = new EntryMsg(
+        serverID, ieContext.exportTarget, lDIFEntry, pos, length);
+    broker.publish(entryMessage);
+
+    try
+    {
+      ieContext.updateCounters(countEntryLimits(lDIFEntry, pos, length));
+    }
+    catch (DirectoryException de)
+    {
+      throw new IOException(de.getMessage());
+    }
+  }
+
+  /**
+   * Initializes this domain from another source server.
+   *
+   * @param source The source from which to initialize
+   * @param initTask The task that launched the initialization
+   *                 and should be updated of its progress.
+   * @throws DirectoryException when an error occurs
+   */
+  void initializeFromRemote(short source, Task initTask)
+  throws DirectoryException
+  {
+    if (debugEnabled())
+      TRACER.debugInfo("Entering initializeFromRemote");
+
+    acquireIEContext(true);
+    ieContext.initializeTask = initTask;
+
+    InitializeRequestMsg initializeMsg = new InitializeRequestMsg(
+        serviceID, serverID, source);
+
+    // Publish Init request msg
+    broker.publish(initializeMsg);
+
+    // .. we expect to receive entries or err after that
+  }
+
+  /**
+   * Initializes the domain's backend with received entries.
+   * @param initializeMessage The message that initiated the import.
+   * @exception DirectoryException Thrown when an error occurs.
+   */
+  void initialize(InitializeTargetMsg initializeMessage)
+  throws DirectoryException
+  {
+    DirectoryException de = null;
+
+    Message msg = NOTE_FULL_UPDATE_ENGAGED_FROM_REMOTE_START.get(
+      Short.toString(serverID),
+      serviceID,
+      Long.toString(initializeMessage.getRequestorID()));
+    logError(msg);
+
+    // Go into full update status
+    setNewStatus(StatusMachineEvent.TO_FULL_UPDATE_STATUS_EVENT);
+
+    if (initializeMessage.getRequestorID() == serverID)
+    {
+      // The import responds to a request we did so the IEContext
+      // is already acquired
+    }
+    else
+    {
+      acquireIEContext(true);
+    }
+
+    ieContext.importSource = initializeMessage.getsenderID();
+    ieContext.entryLeftCount = initializeMessage.getEntryCount();
+    ieContext.setCounters(
+        initializeMessage.getEntryCount(),
+        initializeMessage.getEntryCount());
+    ieContext.ldifImportInputStream = new ReplInputStream(this);
+
+    try
+    {
+      importBackend(new ReplInputStream(this));
+      broker.reStart();
+    }
+    catch (DirectoryException e)
+    {
+      de = e;
+    }
+    finally
+    {
+      if ((ieContext != null)  && (ieContext.exception != null))
+        de = ieContext.exception;
+
+      // Update the task that initiated the import
+      if ((ieContext != null ) && (ieContext.initializeTask != null))
+      {
+        ((InitializeTask)ieContext.initializeTask).
+        updateTaskCompletionState(de);
+      }
+      releaseIEContext();
+    }
+
+    // Sends up the root error.
+    if (de != null)
+    {
+      throw de;
+    }
+
+    msg = NOTE_FULL_UPDATE_ENGAGED_FROM_REMOTE_END.get(
+      Short.toString(serverID),
+      serviceID,
+      Long.toString(initializeMessage.getRequestorID()));
+    logError(msg);
+  }
+
+  /**
+   * Sets the status to a new value depending of the passed status machine
+   * event.
+   * @param event The event that may make the status be changed
+   */
+  private void setNewStatus(StatusMachineEvent event)
+  {
+    ServerStatus newStatus =
+      StatusMachine.computeNewStatus(status, event);
+
+    if (newStatus == ServerStatus.INVALID_STATUS)
+    {
+      Message msg = ERR_DS_CANNOT_CHANGE_STATUS.get(serviceID,
+        Short.toString(serverID), status.toString(), event.toString());
+      logError(msg);
+      return;
+    }
+
+    if (newStatus != status)
+    {
+      // Store new status
+      status = newStatus;
+      lastStatusChangeDate = new Date();
+      // Reset assured monitoring counters (they are valid for a session with
+      // the same status/RS)
+      resetAssuredMonitoringCounters();
+
+      if (debugEnabled())
+        TRACER.debugInfo("Replication domain " + serviceID +
+          " new status is: " + status);
+
+      // Perform whatever actions are needed to apply properties for being
+      // compliant with new status
+      updateDomainForNewStatus();
+    }
+  }
+
+  /**
+   * Returns a boolean indicating if an import or export is currently
+   * processed.
+   *
+   * @return The status
+   */
+  public boolean ieRunning()
+  {
+    return (ieContext != null);
+  }
+
+  /**
+   * Verifies that the given string represents a valid source
+   * from which this server can be initialized.
+   * @param sourceString The string representing the source
+   * @return The source as a short value
+   * @throws DirectoryException if the string is not valid
+   */
+  short decodeSource(String sourceString)
+  throws DirectoryException
+  {
+    short  source = 0;
+    Throwable cause = null;
+    try
+    {
+      source = Integer.decode(sourceString).shortValue();
+      if ((source >= -1) && (source != serverID))
+      {
+        // TODO Verifies serverID is in the domain
+        // We should check here that this is a server implied
+        // in the current domain.
+        return source;
+      }
+    }
+    catch(Exception e)
+    {
+      cause = e;
+    }
+
+    ResultCode resultCode = ResultCode.OTHER;
+    Message message = ERR_INVALID_IMPORT_SOURCE.get();
+    if (cause != null)
+    {
+      throw new DirectoryException(
+          resultCode, message, cause);
+    }
+    else
+    {
+      throw new DirectoryException(
+          resultCode, message);
+    }
+  }
+
+  /**
+   * Reset the generationId of this domain in the whole topology.
+   * A message is sent to the Replication Servers for them to reset
+   * their change dbs.
+   *
+   * @param generationIdNewValue The new value of the generation Id.
+   * @throws DirectoryException when an error occurs
+   */
+  void resetGenerationId(Long generationIdNewValue)
+  throws DirectoryException
+  {
+    if (debugEnabled())
+      TRACER.debugInfo(
+          "Server id " + serverID + " and domain " + serviceID
+          + "resetGenerationId" + generationIdNewValue);
+
+    if (!isConnected())
+    {
+      ResultCode resultCode = ResultCode.OTHER;
+      Message message = ERR_RESET_GENERATION_CONN_ERR_ID.get(
+          serviceID);
+      throw new DirectoryException(
+         resultCode, message);
+    }
+
+    ResetGenerationIdMsg genIdMessage = null;
+
+    if (generationIdNewValue == null)
+    {
+      genIdMessage = new ResetGenerationIdMsg(this.getGenerationID());
+    }
+    else
+    {
+      genIdMessage = new ResetGenerationIdMsg(generationIdNewValue);
+    }
+    broker.publish(genIdMessage);
+  }
+
+
+  /*
+   ******** End of The total Update code *********
+   */
+
+  /*
+   ******* Start of Monitoring Code **********
+   */
+
+  /**
+   * Get the maximum receive window size.
+   *
+   * @return The maximum receive window size.
+   */
+  int getMaxRcvWindow()
+  {
+    if (broker != null)
+      return broker.getMaxRcvWindow();
+    else
+      return 0;
+  }
+
+  /**
+   * Get the current receive window size.
+   *
+   * @return The current receive window size.
+   */
+  int getCurrentRcvWindow()
+  {
+    if (broker != null)
+      return broker.getCurrentRcvWindow();
+    else
+      return 0;
+  }
+
+  /**
+   * Get the maximum send window size.
+   *
+   * @return The maximum send window size.
+   */
+  int getMaxSendWindow()
+  {
+    if (broker != null)
+      return broker.getMaxSendWindow();
+    else
+      return 0;
+  }
+
+  /**
+   * Get the current send window size.
+   *
+   * @return The current send window size.
+   */
+  int getCurrentSendWindow()
+  {
+    if (broker != null)
+      return broker.getCurrentSendWindow();
+    else
+      return 0;
+  }
+
+  /**
+   * Get the number of times the replication connection was lost.
+   * @return The number of times the replication connection was lost.
+   */
+  int getNumLostConnections()
+  {
+    if (broker != null)
+      return broker.getNumLostConnections();
+    else
+      return 0;
+  }
+
+  /**
+   * Determine whether the connection to the replication server is encrypted.
+   * @return true if the connection is encrypted, false otherwise.
+   */
+  boolean isSessionEncrypted()
+  {
+    if (broker != null)
+      return broker.isSessionEncrypted();
+    else
+      return false;
+  }
+
+  /**
+   * This method is called when the ReplicationDomain has completed the
+   * processing of a received update synchronously.
+   * In such cases the processUpdateDone () is called and the state
+   * is updated automatically.
+   *
+   * @param msg The UpdateMessage that was processed.
+   */
+  void processUpdateDoneSynchronous(UpdateMsg msg)
+  {
+    // Warning: in synchronous mode, no way to tell the replay of an update went
+    // wrong Just put null in processUpdateDone so that if assured replication
+    // is used the ack is sent without error at replay flag.
+    processUpdateDone(msg, null);
+    state.update(msg.getChangeNumber());
+  }
+
+  /**
+   * Check if the domain is connected to a ReplicationServer.
+   *
+   * @return true if the server is connected, false if not.
+   */
+  public boolean isConnected()
+  {
+    if (broker != null)
+      return broker.isConnected();
+    else
+      return false;
+  }
+
+  /**
+   * Get the name of the replicationServer to which this domain is currently
+   * connected.
+   *
+   * @return the name of the replicationServer to which this domain
+   *         is currently connected.
+   */
+  public String getReplicationServer()
+  {
+    if (broker != null)
+      return broker.getReplicationServer();
+    else
+      return "Not connected";
+  }
+
+  /**
+   * Gets the number of updates sent in assured safe read mode.
+   * @return The number of updates sent in assured safe read mode.
+   */
+  public int getAssuredSrSentUpdates()
+  {
+    return assuredSrSentUpdates.get();
+  }
+
+  /**
+   * Gets the number of updates sent in assured safe read mode that have been
+   * acknowledged without errors.
+   * @return The number of updates sent in assured safe read mode that have been
+   * acknowledged without errors.
+   */
+  public int getAssuredSrAcknowledgedUpdates()
+  {
+    return assuredSrAcknowledgedUpdates.get();
+  }
+
+  /**
+   * Gets the number of updates sent in assured safe read mode that have not
+   * been acknowledged.
+   * @return The number of updates sent in assured safe read mode that have not
+   * been acknowledged.
+   */
+  public int getAssuredSrNotAcknowledgedUpdates()
+  {
+    return assuredSrNotAcknowledgedUpdates.get();
+  }
+
+  /**
+   * Gets the number of updates sent in assured safe read mode that have not
+   * been acknowledged due to timeout error.
+   * @return The number of updates sent in assured safe read mode that have not
+   * been acknowledged due to timeout error.
+   */
+  public int getAssuredSrTimeoutUpdates()
+  {
+    return assuredSrTimeoutUpdates.get();
+  }
+
+  /**
+   * Gets the number of updates sent in assured safe read mode that have not
+   * been acknowledged due to wrong status error.
+   * @return The number of updates sent in assured safe read mode that have not
+   * been acknowledged due to wrong status error.
+   */
+  public int getAssuredSrWrongStatusUpdates()
+  {
+    return assuredSrWrongStatusUpdates.get();
+  }
+
+  /**
+   * Gets the number of updates sent in assured safe read mode that have not
+   * been acknowledged due to replay error.
+   * @return The number of updates sent in assured safe read mode that have not
+   * been acknowledged due to replay error.
+   */
+  public int getAssuredSrReplayErrorUpdates()
+  {
+    return assuredSrReplayErrorUpdates.get();
+  }
+
+  /**
+   * Gets the number of updates sent in assured safe read mode that have not
+   * been acknowledged per server.
+   * @return The number of updates sent in assured safe read mode that have not
+   * been acknowledged per server.
+   */
+  public Map<Short, Integer> getAssuredSrServerNotAcknowledgedUpdates()
+  {
+    return assuredSrServerNotAcknowledgedUpdates;
+  }
+
+  /**
+   * Gets the number of updates sent in assured safe data mode.
+   * @return The number of updates sent in assured safe data mode.
+   */
+  public int getAssuredSdSentUpdates()
+  {
+    return assuredSdSentUpdates.get();
+  }
+
+  /**
+   * Gets the number of updates sent in assured safe data mode that have been
+   * acknowledged without errors.
+   * @return The number of updates sent in assured safe data mode that have been
+   * acknowledged without errors.
+   */
+  public int getAssuredSdAcknowledgedUpdates()
+  {
+    return assuredSdAcknowledgedUpdates.get();
+  }
+
+  /**
+   * Gets the number of updates sent in assured safe data mode that have not
+   * been acknowledged due to timeout error.
+   * @return The number of updates sent in assured safe data mode that have not
+   * been acknowledged due to timeout error.
+   */
+  public int getAssuredSdTimeoutUpdates()
+  {
+    return assuredSdTimeoutUpdates.get();
+  }
+
+  /**
+   * Gets the number of updates sent in assured safe data mode that have not
+   * been acknowledged due to timeout error per server.
+   * @return The number of updates sent in assured safe data mode that have not
+   * been acknowledged due to timeout error per server.
+   */
+  public Map<Short, Integer> getAssuredSdServerTimeoutUpdates()
+  {
+    return assuredSdServerTimeoutUpdates;
+  }
+
+  /**
+   * Gets the date of the last status change.
+   * @return The date of the last status change.
+   */
+  public Date getLastStatusChangeDate()
+  {
+    return lastStatusChangeDate;
+  }
+
+  /**
+   * Resets the values of the monitoring counters for assured replication.
+   */
+  private void resetAssuredMonitoringCounters()
+  {
+    assuredSrSentUpdates = new AtomicInteger(0);
+    assuredSrAcknowledgedUpdates = new AtomicInteger(0);
+    assuredSrNotAcknowledgedUpdates = new AtomicInteger(0);
+    assuredSrTimeoutUpdates = new AtomicInteger(0);
+    assuredSrWrongStatusUpdates = new AtomicInteger(0);
+    assuredSrReplayErrorUpdates = new AtomicInteger(0);
+    assuredSrServerNotAcknowledgedUpdates = new HashMap<Short,Integer>();
+    assuredSdSentUpdates = new AtomicInteger(0);
+    assuredSdAcknowledgedUpdates = new AtomicInteger(0);
+    assuredSdTimeoutUpdates = new AtomicInteger(0);
+    assuredSdServerTimeoutUpdates = new HashMap<Short,Integer>();
+  }
+
+  /*
+   ********** End of Monitoring Code **************
+   */
+
+  /**
+   * Start the publish mechanism of the Replication Service.
+   * After this method has been called, the publish service can be used
+   * by calling the {@link #publish(UpdateMsg)} method.
+   *
+   * @param replicationServers   The replication servers that should be used.
+   * @param window               The window size of this replication domain.
+   * @param heartbeatInterval    The heartbeatInterval that should be used
+   *                             to check the availability of the replication
+   *                             servers.
+   *
+   * @throws ConfigException     If the DirectoryServer configuration was
+   *                             incorrect.
+   */
+  public void startPublishService(
+      Collection<String> replicationServers, int window,
+      long heartbeatInterval) throws ConfigException
+  {
+    /*
+     * create the broker object used to publish and receive changes
+     */
+    broker = new ReplicationBroker(
+        this, state, serviceID,
+        serverID, window,
+        getGenerationID(),
+        heartbeatInterval,
+        new ReplSessionSecurity(),
+        getGroupId());
+
+    broker.start(replicationServers);
+
+   /*
+    * Create a replication monitor object responsible for publishing
+    * monitoring information below cn=monitor.
+    */
+   monitor = new ReplicationMonitor(this);
+
+   DirectoryServer.registerMonitorProvider(monitor);
+  }
+
+  /**
+   * Starts the receiver side of the Replication Service.
+   * <p>
+   * After this method has been called, the Replication Service will start
+   * calling the {@link #processUpdate(UpdateMsg)}.
+   */
+  public void startListenService()
+  {
+    // Create the listener thread
+    listenerThread = new ListenerThread(this);
+    listenerThread.start();
+  }
+
+  /**
+   * Temporarily disable the Replication Service.
+   * The Replication Service can be enabled again using
+   * {@link #enableService()}.
+   * <p>
+   * It can be useful to disable the Replication Service when the
+   * repository where the replicated information is stored becomes
+   * temporarily unavailable and replicated updates can therefore not
+   * be replayed during a while.
+   */
+  public void disableService()
+  {
+    // Stop the listener thread
+    if (listenerThread != null)
+    {
+      listenerThread.shutdown();
+    }
+
+    if (broker != null)
+    {
+      broker.stop();
+    }
+
+    // Wait for the listener thread to stop
+    if (listenerThread != null)
+      listenerThread.waitForShutdown();
+  }
+
+  /**
+   * Restart the Replication service after a {@link #disableService()}.
+   * <p>
+   * The Replication Service will restart from the point indicated by the
+   * {@link ServerState} that was given as a parameter to the
+   * {@link #startPublishService(Collection, ServerState, int, long)}
+   * at startup time.
+   * If some data have changed in the repository during the period of time when
+   * the Replication Service was disabled, this {@link ServerState} should
+   * therefore be updated by the Replication Domain subclass before calling
+   * this method.
+   */
+  public void enableService()
+  {
+    broker.start();
+
+    // Create the listener thread
+    listenerThread = new ListenerThread(this);
+    listenerThread.start();
+  }
+
+  /**
+   * Definitively stops the Replication Service.
+   */
+  public void stopDomain()
+  {
+    DirectoryServer.deregisterMonitorProvider(monitor.getMonitorInstanceName());
+
+    disableService();
+    domains.remove(serviceID);
+  }
+
+  /**
+   * Change the ReplicationDomain parameters.
+   *
+   * @param replicationServers  The new list of Replication Servers that this
+   *                           domain should now use.
+   * @param windowSize         The window size that this domain should use.
+   * @param heartbeatInterval  The heartbeatInterval that this domain should
+   *                           use.
+   */
+  public void changeConfig(
+      Collection<String> replicationServers,
+      int windowSize,
+      long heartbeatInterval)
+  {
+    if (broker != null)
+    {
+      if (broker.changeConfig(
+          replicationServers, windowSize, heartbeatInterval))
+      {
+        disableService();
+        enableService();
+      }
+    }
+  }
+
+
+  /**
+   * This method should trigger an export of the replicated data.
+   * to the provided outputStream.
+   * When finished the outputStream should be flushed and closed.
+   *
+   * @param output               The OutputStream where the export should
+   *                             be produced.
+   * @throws DirectoryException  When needed.
+   */
+  abstract protected void exportBackend(OutputStream output)
+           throws DirectoryException;
+
+  /**
+   * This method should trigger an import of the replicated data.
+   *
+   * @param input                The InputStream from which
+   *                             the import should be reading entries.
+   *
+   * @throws DirectoryException  When needed.
+   */
+  abstract protected void importBackend(InputStream input)
+           throws DirectoryException;
+
+  /**
+   * This method should return the total number of objects in the
+   * replicated domain.
+   * This count will be used for reporting.
+   *
+   * @throws DirectoryException when needed.
+   *
+   * @return The number of objects in the replication domain.
+   */
+  public abstract long countEntries() throws DirectoryException;
+
+  /**
+   * This method should handle the processing of {@link UpdateMsg} receive
+   * from remote replication entities.
+   * <p>
+   * This method will be called by a single thread and should therefore
+   * should not be blocking.
+   *
+   * @param updateMsg The {@link UpdateMsg} that was received.
+   *
+   * @return A boolean indicating if the processing is completed at return
+   *                   time.
+   *                   If <code> true </code> is returned, no further
+   *                   processing is necessary.
+   *                   If <code> false </code> is returned, the subclass should
+   *                   call the method
+   *                   {@link #processUpdateDone(UpdateMsg)}
+   *                   and update the ServerState
+   *                   When this processing is complete.
+   *
+   */
+  public abstract boolean processUpdate(UpdateMsg updateMsg);
+
+  /**
+   * This method must be called after each call to
+   * {@link #processUpdate(UpdateMsg)} when the processing of the update is
+   * completed.
+   * <p>
+   * It is useful for implementation needing to process the update in an
+   * asynchronous way or using several threads, but must be called even
+   * by implementation doing it in a synchronous, single-threaded way.
+   *
+   * @param  msg The UpdateMsg whose processing was completed.
+   * @param replayErrorMsg if not null, this means an error occurred during the
+   * replay of this update, and this is the matching human readable message
+   * describing the problem.
+   */
+  public void processUpdateDone(UpdateMsg msg, String replayErrorMsg)
+  {
+    broker.updateWindowAfterReplay();
+
+    // Send an ack if it was requested and the group id is the same of the RS
+    // one. Only Safe Read mode makes sense in DS for returning an ack.
+    byte rsGroupId = broker.getRsGroupId();
+    if (msg.isAssured())
+    {
+      // Assured feature is supported starting from replication protocol V2
+      if (broker.getProtocolVersion() >=
+        ProtocolVersion.REPLICATION_PROTOCOL_V2)
+      {
+        AssuredMode msgAssuredMode = msg.getAssuredMode();
+        if (msgAssuredMode == AssuredMode.SAFE_READ_MODE)
+        {
+          if (rsGroupId == groupId)
+          {
+            // Send the ack
+            AckMsg ackMsg = new AckMsg(msg.getChangeNumber());
+            if (replayErrorMsg != null)
+            {
+              // Mark the error in the ack
+              //   -> replay error occured
+              ackMsg.setHasReplayError(true);
+              //   -> replay error occured in our server
+              List<Short> idList = new ArrayList<Short>();
+              idList.add(serverID);
+              ackMsg.setFailedServers(idList);
+            }
+            broker.publish(ackMsg);
+          }
+        } else if (assuredMode != AssuredMode.SAFE_DATA_MODE)
+        {
+          Message errorMsg = ERR_DS_UNKNOWN_ASSURED_MODE.get(
+            Short.toString(serverID), msgAssuredMode.toString(), serviceID,
+            msg.toString());
+          logError(errorMsg);
+        } else
+        {
+          // In safe data mode assured update that comes up to a DS requires no
+          // ack from a destinator DS. Safe data mode is based on RS acks only
+        }
+      }
+    }
+
+    incProcessedUpdates();
+  }
+
+  /**
+   * Publish an {@link UpdateMsg} to the Replication Service.
+   * <p>
+   * The Replication Service will handle the delivery of this {@link UpdateMsg}
+   * to all the participants of this Replication Domain.
+   * These members will be receive this {@link UpdateMsg} through a call
+   * of the {@link #processUpdate(UpdateMsg)} message.
+   *
+   * @param msg The UpdateMsg that should be pushed.
+   * @throws TimeoutException When assured replication is enabled and the
+   * configured timeout occurs when blocked waiting for the ack.
+   */
+  public void publish(UpdateMsg msg) throws TimeoutException
+  {
+    byte rsGroupId = broker.getRsGroupId();
+
+    // If assured configured, set message accordingly to request an ack in the
+    // right assured mode.
+    // No ack requested for a RS with a different group id. Assured replication
+    // suported for the same locality, i.e: a topology working in the same
+    // geographical location). If we are connected to a RS which is not in our
+    // locality, no need to ask for an ack.
+    if ( assured && ( rsGroupId == groupId ) )
+    {
+      msg.setAssured(true);
+      msg.setAssuredMode(assuredMode);
+      if (assuredMode == AssuredMode.SAFE_DATA_MODE)
+        msg.setSafeDataLevel(assuredSdLevel);
+
+      // Add the assured message to the list of update that are
+      // waiting for acks
+      synchronized (waitingAckMsgs)
+      {
+        waitingAckMsgs.put(msg.getChangeNumber(), msg);
+      }
+    }
+
+    // Publish the update
+    broker.publish(msg);
+    state.update(msg.getChangeNumber());
+    numSentUpdates.incrementAndGet();
+
+    // If assured mode configured, wait for acknowledgement for the just sent
+    // message
+    if ( assured && ( rsGroupId == groupId ) )
+    {
+      // Increment assured replication monitoring counters
+      switch(assuredMode)
+      {
+        case SAFE_READ_MODE:
+          assuredSrSentUpdates.incrementAndGet();
+          break;
+        case SAFE_DATA_MODE:
+          assuredSdSentUpdates.incrementAndGet();
+          break;
+        default:
+          // Should no happen
+      }
+
+      // Now wait for ack matching the sent assured update
+      waitForAck(msg);
+    }
+  }
+
+  /**
+   * Publish informations to the Replication Service (not assured mode).
+   *
+   * @param msg  The byte array containing the informations that should
+   *             be sent to the remote entities.
+   */
+  public void publish(byte[] msg)
+  {
+    UpdateMsg update = new UpdateMsg(generator.newChangeNumber(), msg);
+    try
+    {
+      publish(update);
+    } catch (TimeoutException e)
+    {
+      // Should never happen as assured mode not requested
+    }
+  }
+
+  /**
+   * This method should return the generationID to use for this
+   * ReplicationDomain.
+   * This method can be called at any time after the ReplicationDomain
+   * has been started.
+   *
+   * @return The GenerationID.
+   */
+  public abstract long getGenerationID();
+
+  /**
+   * Subclasses should use this method to add additional monitoring
+   * information in the ReplicationDomain.
+   *
+   * @return Additional monitoring attributes that will be added in the
+   *         ReplicationDomain monitoring entry.
+   */
+  public Collection<Attribute> getAdditionalMonitoring()
+  {
+    return new ArrayList<Attribute>();
+  }
+
+  /**
+   * Returns a boolean indicating if a total update import is currently
+   * in Progress.
+   *
+   * @return A boolean indicating if a total update import is currently
+   *         in Progress.
+   */
+  boolean importInProgress()
+  {
+    if (ieContext == null)
+      return false;
+    else
+      return ieContext.importInProgress;
+  }
+
+  /**
+   * Returns a boolean indicating if a total update export is currently
+   * in Progress.
+   *
+   * @return A boolean indicating if a total update export is currently
+   *         in Progress.
+   */
+  boolean exportInProgress()
+  {
+    if (ieContext == null)
+      return false;
+    else
+      return !ieContext.importInProgress;
+  }
+
+  /**
+   * Returns the number of entries still to be processed when a total update
+   * is in progress.
+   *
+   * @return The number of entries still to be processed when a total update
+   *         is in progress.
+   */
+  long getLeftEntryCount()
+  {
+    if (ieContext != null)
+      return ieContext.entryLeftCount;
+    else
+      return 0;
+  }
+
+  /**
+   * Returns the total number of entries to be processed when a total update
+   * is in progress.
+   *
+   * @return The total number of entries to be processed when a total update
+   *         is in progress.
+   */
+  long getTotalEntryCount()
+  {
+    if (ieContext != null)
+      return ieContext.entryCount;
+    else
+      return 0;
+  }
+}
diff --git a/opends/src/server/org/opends/server/replication/service/ReplicationMonitor.java b/opends/src/server/org/opends/server/replication/service/ReplicationMonitor.java
new file mode 100644
index 0000000..e677827
--- /dev/null
+++ b/opends/src/server/org/opends/server/replication/service/ReplicationMonitor.java
@@ -0,0 +1,331 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2006-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.service;
+
+import java.util.Collection;
+
+import java.util.ArrayList;
+
+import java.util.Map;
+import org.opends.server.admin.std.server.MonitorProviderCfg;
+import org.opends.server.api.MonitorProvider;
+import org.opends.server.core.DirectoryServer;
+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;
+
+/**
+ * Class used to generate monitoring information for the replication.
+ */
+public class ReplicationMonitor extends MonitorProvider<MonitorProviderCfg>
+{
+  private ReplicationDomain domain;  // the replication plugin
+
+  /**
+   * Create a new replication monitor.
+   * @param domain the plugin which created the monitor
+   */
+  public ReplicationMonitor(ReplicationDomain domain)
+  {
+    super("Replication monitor " + domain.getServiceID()
+        + " " + domain.getServerId());
+    this.domain = domain;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public void initializeMonitorProvider(MonitorProviderCfg configuration)
+  {
+    // no implementation needed.
+  }
+
+  /**
+   * Retrieves the name of this monitor provider.  It should be unique among all
+   * monitor providers, including all instances of the same monitor provider.
+   *
+   * @return  The name of this monitor provider.
+   */
+  @Override
+  public String getMonitorInstanceName()
+  {
+    return "Replication Domain "  + domain.getServiceID()
+       + " " + domain.getServerId();
+  }
+
+  /**
+   * Retrieves a set of attributes containing monitor data that should be
+   * returned to the client if the corresponding monitor entry is requested.
+   *
+   * @return  A set of attributes containing monitor data that should be
+   *          returned to the client if the corresponding monitor entry is
+   *          requested.
+   */
+  @Override
+  public ArrayList<Attribute> getMonitorData()
+  {
+    ArrayList<Attribute> attributes = new ArrayList<Attribute>();
+
+    /* get the base dn */
+    Attribute attr = Attributes.create("base-dn", domain.getServiceID());
+    attributes.add(attr);
+
+    /* get the base dn */
+    attr = Attributes.create("connected-to", domain
+        .getReplicationServer());
+    attributes.add(attr);
+
+    /* get number of lost connections */
+    addMonitorData(attributes, "lost-connections",
+                   domain.getNumLostConnections());
+
+    /* get number of received updates */
+    addMonitorData(attributes, "received-updates", domain.getNumRcvdUpdates());
+
+    /* get number of updates sent */
+    addMonitorData(attributes, "sent-updates", domain.getNumSentUpdates());
+
+    /* get number of changes replayed */
+    addMonitorData(attributes, "replayed-updates",
+                   domain.getNumProcessedUpdates());
+
+    /* get server-id */
+    addMonitorData(attributes, "server-id",
+                   domain.getServerId());
+
+    /* get window information */
+    addMonitorData(attributes, "max-rcv-window", domain.getMaxRcvWindow());
+    addMonitorData(attributes, "current-rcv-window",
+                               domain.getCurrentRcvWindow());
+    addMonitorData(attributes, "max-send-window",
+                               domain.getMaxSendWindow());
+    addMonitorData(attributes, "current-send-window",
+                               domain.getCurrentSendWindow());
+
+    /* get the Server State */
+    final String ATTR_SERVER_STATE = "server-state";
+    AttributeType type =
+      DirectoryServer.getDefaultAttributeType(ATTR_SERVER_STATE);
+    AttributeBuilder builder = new AttributeBuilder(type, ATTR_SERVER_STATE);
+    for (String str : domain.getServerState().toStringSet())
+    {
+      builder.add(new AttributeValue(type,str));
+    }
+    attributes.add(builder.toAttribute());
+
+    attributes.add(Attributes.create("ssl-encryption",
+        String.valueOf(domain.isSessionEncrypted())));
+
+    attributes.add(Attributes.create("generation-id",
+        String.valueOf(domain.getGenerationID())));
+
+    /*
+     * Add import/export monitoring attribute
+     */
+    if (domain.importInProgress())
+    {
+      addMonitorData(attributes, "total-update", "import");
+      addMonitorData(
+          attributes, "total-update-entry-count", domain.getTotalEntryCount());
+      addMonitorData(
+          attributes, "total-update-entry-left", domain.getLeftEntryCount());
+    }
+    if (domain.exportInProgress())
+    {
+      addMonitorData(attributes, "total-update", "export");
+      addMonitorData(
+          attributes, "total-update-entry-count", domain.getTotalEntryCount());
+      addMonitorData(
+          attributes, "total-update-entry-left", domain.getLeftEntryCount());
+    }
+
+
+    /* Add the concrete Domain attributes */
+    Collection<Attribute> additionalMonitoring =
+      domain.getAdditionalMonitoring();
+    attributes.addAll(additionalMonitoring);
+
+    /*
+     * Add assured replication related monitoring fields
+     * (see domain.getXXX() method comment for field meaning)
+     */
+
+    addMonitorData(attributes, "assured-sr-sent-updates",
+      domain.getAssuredSrSentUpdates());
+
+    addMonitorData(attributes, "assured-sr-acknowledged-updates",
+      domain.getAssuredSrAcknowledgedUpdates());
+
+    addMonitorData(attributes, "assured-sr-not-acknowledged-updates",
+      domain.getAssuredSrNotAcknowledgedUpdates());
+
+    addMonitorData(attributes, "assured-sr-timeout-updates",
+      domain.getAssuredSrTimeoutUpdates());
+
+    addMonitorData(attributes, "assured-sr-wrong-status-updates",
+      domain.getAssuredSrWrongStatusUpdates());
+
+    addMonitorData(attributes, "assured-sr-replay-error-updates",
+      domain.getAssuredSrReplayErrorUpdates());
+
+    final String ATTR_ASS_SR_SRV = "assured-sr-server-not-acknowledged-updates";
+    type = DirectoryServer.getDefaultAttributeType(ATTR_ASS_SR_SRV);
+    builder = new AttributeBuilder(type, ATTR_ASS_SR_SRV);
+    Map<Short, Integer> srSrvNotAckUps =
+      domain.getAssuredSrServerNotAcknowledgedUpdates();
+    if (srSrvNotAckUps.size() > 0)
+    {
+      for (Short serverId : srSrvNotAckUps.keySet())
+      {
+        String str = serverId + ":" + srSrvNotAckUps.get(serverId);
+        builder.add(new AttributeValue(type, str));
+      }
+      attributes.add(builder.toAttribute());
+    }
+
+    addMonitorData(attributes, "assured-sd-sent-updates",
+      domain.getAssuredSdSentUpdates());
+
+    addMonitorData(attributes, "assured-sd-acknowledged-updates",
+      domain.getAssuredSdAcknowledgedUpdates());
+
+    addMonitorData(attributes, "assured-sd-timeout-updates",
+      domain.getAssuredSdTimeoutUpdates());
+
+    final String ATTR_ASS_SD_SRV = "assured-sd-server-timeout-updates";
+    type = DirectoryServer.getDefaultAttributeType(ATTR_ASS_SD_SRV);
+    builder = new AttributeBuilder(type, ATTR_ASS_SD_SRV);
+    Map<Short, Integer> sdSrvTimUps =
+      domain.getAssuredSdServerTimeoutUpdates();
+    if (sdSrvTimUps.size() > 0)
+    {
+      for (Short serverId : sdSrvTimUps.keySet())
+      {
+        String str = serverId + ":" + sdSrvTimUps.get(serverId);
+        builder.add(new AttributeValue(type, str));
+      }
+      attributes.add(builder.toAttribute());
+    }
+
+    /*
+     * Status related monitoring fields
+     */
+
+    addMonitorData(attributes, "last-status-change-date",
+      domain.getLastStatusChangeDate().toString());
+
+    addMonitorData(attributes, "status", domain.getStatus().toString());
+
+    return attributes;
+
+  }
+
+  /**
+   * Add an attribute with an integer value to the list of monitoring
+   * attributes.
+   *
+   * @param attributes the list of monitoring attributes
+   * @param name the name of the attribute to add.
+   * @param value The integer value of he attribute to add.
+   */
+  public static void addMonitorData(
+      ArrayList<Attribute> attributes,
+      String name,
+      int value)
+  {
+    AttributeType type = DirectoryServer.getDefaultAttributeType(name);
+    attributes.add(Attributes.create(type, new AttributeValue(type,
+        String.valueOf(value))));
+  }
+
+  /**
+   * Add an attribute with an integer value to the list of monitoring
+   * attributes.
+   *
+   * @param attributes the list of monitoring attributes
+   * @param name the name of the attribute to add.
+   * @param value The integer value of he attribute to add.
+   */
+  public static void addMonitorData(
+      ArrayList<Attribute> attributes,
+      String name,
+      long value)
+  {
+    AttributeType type = DirectoryServer.getDefaultAttributeType(name);
+    attributes.add(Attributes.create(type, new AttributeValue(type,
+        String.valueOf(value))));
+  }
+
+  /**
+   * Add an attribute with an integer value to the list of monitoring
+   * attributes.
+   *
+   * @param attributes the list of monitoring attributes
+   * @param name the name of the attribute to add.
+   * @param value The String value of he attribute to add.
+   */
+  public static void addMonitorData(
+      ArrayList<Attribute> attributes,
+      String name,
+      String value)
+  {
+    AttributeType type = DirectoryServer.getDefaultAttributeType(name);
+    attributes.add(Attributes.create(type, new AttributeValue(type, value)));
+  }
+
+  /**
+   * Retrieves the length of time in milliseconds that should elapse between
+   * calls to the <CODE>updateMonitorData()</CODE> method.  A negative or zero
+   * return value indicates that the <CODE>updateMonitorData()</CODE> method
+   * should not be periodically invoked.
+   *
+   * @return  The length of time in milliseconds that should elapse between
+   *          calls to the <CODE>updateMonitorData()</CODE> method.
+   */
+  @Override
+  public long getUpdateInterval()
+  {
+    /* we don't wont to do polling on this monitor */
+    return 0;
+  }
+
+  /**
+   * Performs any processing periodic processing that may be desired to update
+   * the information associated with this monitor.  Note that best-effort
+   * attempts will be made to ensure that calls to this method come
+   * <CODE>getUpdateInterval()</CODE> milliseconds apart, but no guarantees will
+   * be made.
+   */
+  @Override
+  public void updateMonitorData()
+  {
+    //  As long as getUpdateInterval() returns 0, this will never get called
+  }
+}
diff --git a/opends/src/server/org/opends/server/tasks/SetGenerationIdTask.java b/opends/src/server/org/opends/server/replication/service/SetGenerationIdTask.java
similarity index 87%
rename from opends/src/server/org/opends/server/tasks/SetGenerationIdTask.java
rename to opends/src/server/org/opends/server/replication/service/SetGenerationIdTask.java
index f6b9a84..e465800 100644
--- a/opends/src/server/org/opends/server/tasks/SetGenerationIdTask.java
+++ b/opends/src/server/org/opends/server/replication/service/SetGenerationIdTask.java
@@ -24,10 +24,13 @@
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
  */
-package org.opends.server.tasks;
+package org.opends.server.replication.service;
 import static org.opends.server.config.ConfigConstants.*;
 import static org.opends.server.core.DirectoryServer.getAttributeType;
 
+import org.opends.server.tasks.TaskUtils;
+
+
 import java.util.List;
 
 import org.opends.messages.MessageBuilder;
@@ -37,10 +40,8 @@
 import org.opends.server.backends.task.TaskState;
 import static org.opends.server.loggers.debug.DebugLogger.*;
 import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.replication.plugin.ReplicationDomain;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeType;
-import org.opends.server.types.DN;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
 import org.opends.server.types.ResultCode;
@@ -56,14 +57,9 @@
    * The tracer object for the debug logger.
    */
   private static final DebugTracer TRACER = getTracer();
-
-  boolean isCompressed            = false;
-  boolean isEncrypted             = false;
-  boolean skipSchemaValidation    = false;
-  String  domainString            = null;
-  ReplicationDomain domain        = null;
-  TaskState initState;
-  Long generationId = null;
+  private String  domainString            = null;
+  private ReplicationDomain domain        = null;
+  private Long generationId = null;
 
   private static final void debugInfo(String s)
   {
@@ -124,22 +120,18 @@
 
     attrList = taskEntry.getAttribute(typeDomainBase);
     domainString = TaskUtils.getSingleValueString(attrList);
-    DN domainDN = DN.nullDN();
+
     try
     {
-      domainDN = DN.decode(domainString);
+      domain = ReplicationDomain.retrievesReplicationDomain(domainString);
     }
-    catch(Exception e)
+    catch(DirectoryException e)
     {
       MessageBuilder mb = new MessageBuilder();
       mb.append(TaskMessages.ERR_TASK_INITIALIZE_INVALID_DN.get());
       mb.append(e.getMessage());
-      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
-          mb.toMessage());
+      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX, e);
     }
-
-    domain = ReplicationDomain.retrievesReplicationDomain(domainDN);
-
   }
 
   /**
@@ -148,7 +140,7 @@
   protected TaskState runTask()
   {
     debugInfo("setGenerationIdTask is starting on domain%s" +
-        domain.getBaseDN());
+        domain.getServiceID());
 
     try
     {
diff --git a/opends/src/server/org/opends/server/replication/server/AckMessageListComparator.java b/opends/src/server/org/opends/server/replication/service/package-info.java
similarity index 65%
rename from opends/src/server/org/opends/server/replication/server/AckMessageListComparator.java
rename to opends/src/server/org/opends/server/replication/service/package-info.java
index fa93ed5..d8708ed 100644
--- a/opends/src/server/org/opends/server/replication/server/AckMessageListComparator.java
+++ b/opends/src/server/org/opends/server/replication/service/package-info.java
@@ -22,22 +22,20 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Copyright 2008 Sun Microsystems, Inc.
  */
-package org.opends.server.replication.server;
 
-import java.util.Comparator;
+
 
 /**
- * This comparator is used to build TreeSet of AckMessageLists.
+ * This package contains the generic of the Multi-Master
+ * replication code that works on the Directory Server side.
+ * <br>
+ * Developers planning to use the Replication Service should
+ * subClass the <A> HREF="ReplicationDomain.html"><B>ReplicationDomain</B></A>
+ * class
  */
-public class AckMessageListComparator implements Comparator<AckMessageList>
-{
-  /**
-   * {@inheritDoc}
-   */
-  public int compare(AckMessageList a1, AckMessageList a2)
-  {
-    return a1.getChangeNumber().compareTo(a2.getChangeNumber());
-  }
-}
+@org.opends.server.types.PublicAPI(
+     stability=org.opends.server.types.StabilityLevel.PRIVATE)
+package org.opends.server.replication.service;
+
diff --git a/opends/src/server/org/opends/server/tools/dsreplication/ReplicationCliMain.java b/opends/src/server/org/opends/server/tools/dsreplication/ReplicationCliMain.java
index 9631da3..b26e558 100644
--- a/opends/src/server/org/opends/server/tools/dsreplication/ReplicationCliMain.java
+++ b/opends/src/server/org/opends/server/tools/dsreplication/ReplicationCliMain.java
@@ -6129,7 +6129,7 @@
     oc.add("ds-task-reset-generation-id");
     attrs.put(oc);
     attrs.put("ds-task-class-name",
-        "org.opends.server.tasks.SetGenerationIdTask");
+        "org.opends.server.replication.service.SetGenerationIdTask");
     if (isPre)
     {
       if (!localOnly)
@@ -6300,7 +6300,7 @@
     oc.add("ds-task-initialize-remote-replica");
     attrs.put(oc);
     attrs.put("ds-task-class-name",
-        "org.opends.server.tasks.InitializeTargetTask");
+        "org.opends.server.replication.service.InitializeTargetTask");
     attrs.put("ds-task-initialize-domain-dn", baseDN);
     attrs.put("ds-task-initialize-replica-server-id", "all");
     while (!taskCreated)
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ChangeNumberControlPluginTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ChangeNumberControlPluginTestCase.java
index b0bd7b4..43847cf 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ChangeNumberControlPluginTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ChangeNumberControlPluginTestCase.java
@@ -31,16 +31,12 @@
 import java.io.FileReader;
 import java.net.ServerSocket;
 import org.opends.server.TestCaseUtils;
-import org.opends.server.core.AddOperationBasis;
-import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.replication.ReplicationTestCase;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 import org.opends.server.tools.LDAPModify;
 import org.opends.server.types.DN;
-import org.opends.server.types.Entry;
-import org.opends.server.types.ResultCode;
 import static org.opends.server.util.ServerConstants.*;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
@@ -53,19 +49,17 @@
    * The port of the replicationServer.
    */
   private int replServerPort;
-  
+
   /**
    * The replicationServer that will be used in this test.
    */
-  private static final int WINDOW_SIZE = 10;
-  private static final int REPLICATION_QUEUE_SIZE = 100;
   private DN baseDn;
-  
+
   /**
    * Before starting the tests, start the server and configure a
    * replicationServer.
    */
-  
+
   @BeforeClass(alwaysRun=true)
   public void setUp() throws Exception {
     super.setUp();
@@ -80,9 +74,9 @@
     // replication server
     String replServerLdif =
         "dn: cn=Replication Server, " + SYNCHRO_PLUGIN_DN + "\n"
-        + "objectClass: top\n" 
+        + "objectClass: top\n"
         + "objectClass: ds-cfg-replication-server\n"
-        + "cn: Replication Server\n" 
+        + "cn: Replication Server\n"
         + "ds-cfg-replication-port: " + replServerPort + "\n"
         + "ds-cfg-replication-db-directory: ChangeNumberControlDbTest\n"
         + "ds-cfg-replication-server-id: 103\n";
@@ -128,7 +122,7 @@
          + "changetype: delete"}
     };
   }
-  
+
   @Test(dataProvider="operations")
   public void ChangeNumberControlTest(String request) throws Exception {
 
@@ -146,13 +140,13 @@
     };
 
     String resultPath = TestCaseUtils.createTempFile();
-    
+
     FileOutputStream fos = new FileOutputStream(resultPath);
 
     assertEquals(LDAPModify.mainModify(args, false, fos, System.err), 0);
     //fos.flush();
     fos.close();
-    
+
     assertTrue(isCsnLinePresent(resultPath));
   }
 
@@ -168,27 +162,5 @@
     }
     return (found);
   }
-  
-  /**
-   * Utility function. Can be used to create and add and entry
-   * in the local DS from its ldif description.
-   *
-   * @param entryString  The entry in ldif from.
-   * @return             The ResultCode of the operation.
-   * @throws Exception   If something went wrong.
-   */
-  private ResultCode addEntry(String entryString) throws Exception
-  {
-    Entry entry;
-    AddOperationBasis addOp;
-    entry = TestCaseUtils.entryFromLdifString(entryString);
-    addOp = new AddOperationBasis(InternalClientConnection.getRootConnection(),
-       InternalClientConnection.nextOperationID(), InternalClientConnection
-       .nextMessageID(), null, entry.getDN(), entry.getObjectClasses(),
-       entry.getUserAttributes(), entry.getOperationalAttributes());
-    addOp.setInternalOperation(true);
-    addOp.run();
 
-    return addOp.getResultCode();
-  }
 }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/DependencyTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/DependencyTest.java
index 06c1deb..f73adac 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/DependencyTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/DependencyTest.java
@@ -39,11 +39,11 @@
 import org.opends.server.api.SynchronizationProvider;
 import org.opends.server.backends.MemoryBackend;
 import org.opends.server.core.DirectoryServer;
+import org.opends.server.replication.service.ReplicationBroker;
 import org.opends.server.replication.common.ChangeNumberGenerator;
 import org.opends.server.replication.plugin.DomainFakeCfg;
 import org.opends.server.replication.plugin.MultimasterReplication;
-import org.opends.server.replication.plugin.ReplicationBroker;
-import org.opends.server.replication.plugin.ReplicationDomain;
+import org.opends.server.replication.plugin.LDAPReplicationDomain;
 import org.opends.server.replication.protocol.AddMsg;
 import org.opends.server.replication.protocol.DeleteMsg;
 import org.opends.server.replication.protocol.ModifyDNMsg;
@@ -90,7 +90,7 @@
   public void addModDelDependencyTest() throws Exception
   {
     ReplicationServer replServer = null;
-    ReplicationDomain domain = null;
+    LDAPReplicationDomain domain = null;
     DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
     SynchronizationProvider replicationPlugin = null;
     short brokerId = 2;
@@ -255,7 +255,7 @@
   public void moddnDelDependencyTest() throws Exception
   {
     ReplicationServer replServer = null;
-    ReplicationDomain domain = null;
+    LDAPReplicationDomain domain = null;
     DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
     SynchronizationProvider replicationPlugin = null;
     short brokerId = 2;
@@ -302,7 +302,7 @@
       Thread.sleep(2000);
       domain = MultimasterReplication.createNewDomain(domainConf);
       replicationPlugin.completeSynchronizationProvider();
-      
+
       ReplicationBroker broker =
         openReplicationSession(baseDn, brokerId, 1000, replServerPort, 1000,
                                false, CLEAN_DB_GENERATION_ID);
@@ -330,7 +330,7 @@
       assertTrue(found, "The initial entry add failed");
 
 
-      // disable the domain to make sure that the messages are 
+      // disable the domain to make sure that the messages are
       // all sent in a row.
       domain.disable();
 
@@ -413,7 +413,7 @@
   public void addDelAddDependencyTest() throws Exception
   {
     ReplicationServer replServer = null;
-    ReplicationDomain domain = null;
+    LDAPReplicationDomain domain = null;
     DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
     SynchronizationProvider replicationPlugin = null;
     short brokerId = 2;
@@ -546,7 +546,7 @@
   public void addModdnDependencyTest() throws Exception
   {
     ReplicationServer replServer = null;
-    ReplicationDomain domain = null;
+    LDAPReplicationDomain domain = null;
     DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
     SynchronizationProvider replicationPlugin = null;
     short brokerId = 2;
@@ -555,7 +555,7 @@
     int AddSequenceLength = 30;
 
     cleanDB();
-    
+
 
     try
     {
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/GenerationIdTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/GenerationIdTest.java
index db46c73..2bdca58 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/GenerationIdTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/GenerationIdTest.java
@@ -54,10 +54,10 @@
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.replication.service.ReplicationBroker;
 import org.opends.server.replication.common.ChangeNumberGenerator;
 import org.opends.server.replication.common.ServerStatus;
-import org.opends.server.replication.plugin.ReplicationBroker;
-import org.opends.server.replication.plugin.ReplicationDomain;
+import org.opends.server.replication.plugin.LDAPReplicationDomain;
 import org.opends.server.replication.protocol.AddMsg;
 import org.opends.server.replication.protocol.ChangeStatusMsg;
 import org.opends.server.replication.protocol.DoneMsg;
@@ -66,7 +66,6 @@
 import org.opends.server.replication.protocol.InitializeTargetMsg;
 import org.opends.server.replication.protocol.ReplicationMsg;
 import org.opends.server.replication.protocol.SocketSession;
-import org.opends.server.replication.protocol.TopologyMsg;
 import org.opends.server.replication.server.ReplServerFakeConfiguration;
 import org.opends.server.replication.server.ReplicationBackend;
 import org.opends.server.replication.server.ReplicationServer;
@@ -118,7 +117,7 @@
   private ReplicationServer replServer2 = null;
   private ReplicationServer replServer3 = null;
   private boolean emptyOldChanges = true;
-  ReplicationDomain replDomain = null;
+  LDAPReplicationDomain replDomain = null;
   private Entry taskInitRemoteS2;
   SocketSession ssSession = null;
   boolean ssShutdownRequested = false;
@@ -210,7 +209,7 @@
         "objectclass: top",
         "objectclass: ds-task",
         "objectclass: ds-task-initialize-remote-replica",
-        "ds-task-class-name: org.opends.server.tasks.InitializeTargetTask",
+        "ds-task-class-name: org.opends.server.replication.service.InitializeTargetTask",
         "ds-task-initialize-domain-dn: " + baseDn,
         "ds-task-initialize-replica-server-id: " + server2ID);
   }
@@ -440,7 +439,7 @@
         "Unable to add the synchronized server");
       configEntryList.add(synchroServerEntry.getDN());
 
-      replDomain = ReplicationDomain.retrievesReplicationDomain(baseDn);
+      replDomain = LDAPReplicationDomain.retrievesReplicationDomain(baseDn);
 
 
       if (replDomain != null)
@@ -668,7 +667,7 @@
           }
         }
       }
-      assertEquals(searchOperation.getSearchEntries().size(), expectedCount);      
+      assertEquals(searchOperation.getSearchEntries().size(), expectedCount);
     }
     catch(Exception e)
     {
@@ -719,14 +718,14 @@
 
       debugInfo(testCase + " Expect genId to be set in memory on the replication " +
       " server side (not wrote on disk/db since no change occurred).");
-      rgenId = replServer1.getGenerationId(baseDn);
+      rgenId = replServer1.getGenerationId(baseDn.toNormalizedString());
       assertEquals(rgenId, EMPTY_DN_GENID);
 
       // Clean for next test
       debugInfo(testCase + " Unconfiguring DS1 to replicate to RS1(" + changelog1ID + ")");
       disconnectFromReplServer(changelog1ID);
 
-      
+
       //===========================================================
       debugInfo(testCase + " ** TEST ** Non empty backend");
 
@@ -742,7 +741,7 @@
       assertTrue(genId != EMPTY_DN_GENID);
 
       debugInfo(testCase + " Test that the generationId is set on RS1");
-      rgenId = replServer1.getGenerationId(baseDn);
+      rgenId = replServer1.getGenerationId(baseDn.toNormalizedString());
       assertEquals(genId, rgenId);
 
       //===========================================================
@@ -771,22 +770,6 @@
         fail("Broker connection is expected to be accepted.");
       }
 
-      // Broker 2 should receive an update topo message to signal broker 3
-      // arrival in topology
-      try
-      {
-        ReplicationMsg msg = broker2.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 2 connection is expected to receive a TopologyMsg."
-              + msg);
-        }
-      }
-      catch(SocketTimeoutException se)
-      {
-        fail("DS2 is expected to receive a TopologyMsg.");
-      }
-
       //===========================================================
       debugInfo(testCase + " ** TEST ** DS2 (bad genID) changes must be ignored.");
 
@@ -826,7 +809,8 @@
       //===========================================================
       debugInfo(testCase + " ** TEST ** Persistence of the generation ID in RS1");
 
-      long genIdBeforeShut = replServer1.getGenerationId(baseDn);
+      long genIdBeforeShut =
+        replServer1.getGenerationId(baseDn.toNormalizedString());
 
       debugInfo("Shutdown replServer1");
       broker2.stop();
@@ -847,7 +831,8 @@
 
       debugInfo("Delay to allow DS to reconnect to replServer1");
 
-      long genIdAfterRestart = replServer1.getGenerationId(baseDn);
+      long genIdAfterRestart =
+        replServer1.getGenerationId(baseDn.toNormalizedString());
       debugInfo("Aft restart / replServer.genId=" + genIdAfterRestart);
       assertTrue(replServer1!=null, "Replication server creation failed.");
       assertTrue(genIdBeforeShut == genIdAfterRestart,
@@ -880,21 +865,6 @@
         fail("Broker connection is expected to be accepted.");
       }
 
-      // Broker 2 should receive an update topo message to signal broker 3
-      // arrival in topology
-      try
-      {
-        ReplicationMsg msg = broker2.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 2 connection is expected to receive a TopologyMsg."
-              + msg);
-        }
-      }
-      catch(SocketTimeoutException se)
-      {
-        fail("DS2 is expected to receive a TopologyMsg.");
-      }
 
       debugInfo("Launch on-line import on DS1");
       long oldGenId = genId;
@@ -903,69 +873,6 @@
       performLdifImport();
       connectServer1ToChangelog(changelog1ID);
 
-      // Broker 2 and 3 should receive 2 update topo messages to signal broker 1
-      // has disconnected from topology then reconnected
-      try
-      {
-        ReplicationMsg msg = broker2.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 2 connection is expected to receive a first TopologyMsg" +
-            " for Broker 1 import + reconnect."
-              + msg);
-        }
-      }
-      catch(SocketTimeoutException se)
-      {
-        fail("DS2 is expected to receive a first TopologyMsg for DS1 import + " +
-          "reconnect.");
-      }
-      try
-      {
-        ReplicationMsg msg = broker3.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 3 connection is expected to receive a first TopologyMsg" +
-            " for Broker 1 import + reconnect."
-              + msg);
-        }
-      }
-      catch(SocketTimeoutException se)
-      {
-        fail("DS3 is expected to receive a first TopologyMsg for DS1 import + " +
-          "reconnect.");
-      }
-      try
-      {
-        ReplicationMsg msg = broker2.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 2 connection is expected to receive a second TopologyMsg" +
-            " for Broker 1 import + reconnect."
-              + msg);
-        }
-      }
-      catch(SocketTimeoutException se)
-      {
-        fail("DS2 is expected to receive a second TopologyMsg for DS1 import + " +
-          "reconnect.");
-      }
-      try
-      {
-        ReplicationMsg msg = broker3.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 3 connection is expected to receive a second TopologyMsg" +
-            " for Broker 1 import + reconnect."
-              + msg);
-        }
-      }
-      catch(SocketTimeoutException se)
-      {
-        fail("DS3 is expected to receive a second TopologyMsg for DS1 import + " +
-          "reconnect.");
-      }
-
       debugInfo("Create Reset task on DS1 to propagate the new gen ID as the reference");
       Entry taskReset = TestCaseUtils.makeEntry(
           "dn: ds-task-id=resetgenid"+genId+ UUID.randomUUID() +
@@ -973,10 +880,10 @@
           "objectclass: top",
           "objectclass: ds-task",
           "objectclass: ds-task-reset-generation-id",
-          "ds-task-class-name: org.opends.server.tasks.SetGenerationIdTask",
+          "ds-task-class-name: org.opends.server.replication.service.SetGenerationIdTask",
           "ds-task-reset-generation-id-domain-base-dn: " + baseDnStr);
       addTask(taskReset, ResultCode.SUCCESS, null);
-      waitTaskState(taskReset, TaskState.COMPLETED_SUCCESSFULLY, null);      
+      waitTaskState(taskReset, TaskState.COMPLETED_SUCCESSFULLY, null);
 
       // Broker 2 and 3 should receive 1 change status message to order them
       // to enter the bad gen id status
@@ -1025,66 +932,6 @@
           "bad gen id status.");
       }
 
-      // Broker 2 and 3 should receive 1 update topo message to signal other broker gen id
-      // has been resetted, and 2 topo messages to signal DS1 has been disconnected
-      // then reconnected
-      try
-      {
-        ReplicationMsg msg = broker2.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 2 connection is expected to receive 1 TopologyMsg" +
-            " for gen id reset of broker 3."
-              + msg);
-        }
-        msg = broker2.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 2 connection is expected to receive 1 TopologyMsg" +
-            " for DS1 disconnected."
-              + msg);
-        }
-        msg = broker2.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 2 connection is expected to receive 1 TopologyMsg" +
-            " for DS1 reconnected."
-              + msg);
-        }
-      }
-      catch(SocketTimeoutException se)
-      {
-        fail("DS2 is expected to receive 3 TopologyMsg for gen id reset.");
-      }
-      try
-      {
-        ReplicationMsg msg = broker3.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 3 connection is expected to receive 1 TopologyMsg" +
-            " for gen id reset of broker 2."
-              + msg);
-        }
-        msg = broker3.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 3 connection is expected to receive 1 TopologyMsg" +
-            " for DS1 disconnected."
-              + msg);
-        }
-        msg = broker3.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 3 connection is expected to receive 1 TopologyMsg" +
-            " for DS1 reconnected"
-              + msg);
-        }
-      }
-      catch(SocketTimeoutException se)
-      {
-        fail("DS3 is expected to receive 3 TopologyMsg for gen id reset.");
-      }
-
       debugInfo("DS1 root entry must contain the new gen ID");
       genId = readGenIdFromSuffixRootEntry();
       assertTrue(genId != -1, "DS is expected to have a new genID computed " +
@@ -1093,13 +940,14 @@
         + "is expected to be diffrent from previous one");
 
       debugInfo("RS1 must have the new gen ID");
-      rgenId = replServer1.getGenerationId(baseDn);
+      rgenId = replServer1.getGenerationId(baseDn.toNormalizedString());
       assertEquals(genId, rgenId, "DS and replServer are expected to have same genId.");
 
       debugInfo("RS1 must have been cleared since it has not the proper generation ID");
       checkChangelogSize(0);
 
-      assertTrue(!replServer1.getReplicationServerDomain(baseDn, false).
+      assertTrue(!replServer1.getReplicationServerDomain(
+          baseDn.toNormalizedString(), false).
           isDegradedDueToGenerationId(server1ID),
       "Expecting that DS1 status in RS1 is : not in bad gen id.");
 
@@ -1107,10 +955,12 @@
       debugInfo(testCase + " ** TEST ** Previous test set a new gen ID on the "+
           "topology, verify degradation of DS2 and DS3");
 
-      assertTrue(replServer1.getReplicationServerDomain(baseDn, false).
+      assertTrue(replServer1.getReplicationServerDomain(
+          baseDn.toNormalizedString(), false).
           isDegradedDueToGenerationId(server2ID),
       "Expecting that DS2 with old gen ID is in bad gen id from RS1");
-      assertTrue(replServer1.getReplicationServerDomain(baseDn, false).
+      assertTrue(replServer1.getReplicationServerDomain(
+          baseDn.toNormalizedString(), false).
           isDegradedDueToGenerationId(server3ID),
       "Expecting that DS3 with old gen ID is in bad gen id from RS1");
 
@@ -1173,35 +1023,6 @@
       broker3 = openReplicationSession(baseDn,
           server3ID, 100, getChangelogPort(changelog1ID), 1000, emptyOldChanges, genId);
 
-      // Broker 2 should receive 2 update topo messages to signal broker 3
-      // stopped and restarted
-      try
-      {
-        ReplicationMsg msg = broker2.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 2 connection is expected to receive a first TopologyMsg."
-              + msg);
-        }
-      }
-      catch(SocketTimeoutException se)
-      {
-        fail("DS2 is expected to receive a first TopologyMsg.");
-      }
-      try
-      {
-        ReplicationMsg msg = broker2.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 2 connection is expected to receive a second TopologyMsg."
-              + msg);
-        }
-      }
-      catch(SocketTimeoutException se)
-      {
-        fail("DS2 is expected to receive a second TopologyMsg.");
-      }
-
       debugInfo("Adding reset task to DS1");
       taskReset = TestCaseUtils.makeEntry(
           "dn: ds-task-id=resetgenid"+ UUID.randomUUID() +
@@ -1209,47 +1030,15 @@
           "objectclass: top",
           "objectclass: ds-task",
           "objectclass: ds-task-reset-generation-id",
-          "ds-task-class-name: org.opends.server.tasks.SetGenerationIdTask",
+          "ds-task-class-name: org.opends.server.replication.service.SetGenerationIdTask",
           "ds-task-reset-generation-id-domain-base-dn: " + baseDnStr);
 
       addTask(taskReset, ResultCode.SUCCESS, null);
       waitTaskState(taskReset, TaskState.COMPLETED_SUCCESSFULLY, null);
       Thread.sleep(200);
 
-      // Broker 2 and 3 should not receive a change status as they are connected with
-      // the right genid, but should anyway receive a topo message to update them with potential
-      // updates.
-      try
-      {
-        ReplicationMsg msg = broker2.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 2 connection is expected to receive 1 TopologyMsg" +
-            " for topo update on reset gen id."
-              + msg);
-        }
-      }
-      catch(SocketTimeoutException se)
-      {
-        fail("DS2 is expected to receive 1 TopologyMsg for topo update on reset gen id.");
-      }
-      try
-      {
-        ReplicationMsg msg = broker3.receive();
-        if (!(msg instanceof TopologyMsg))
-        {
-          fail("Broker 3 connection is expected to receive 1 TopologyMsg" +
-            " for topo update on reset gen id."
-              + msg);
-        }
-      }
-      catch(SocketTimeoutException se)
-      {
-        fail("DS3 is expected to receive 1 TopologyMsg for topo update on reset gen id.");
-      }
-
       debugInfo("Verify that RS1 has still the right genID");
-      assertEquals(replServer1.getGenerationId(baseDn), rgenId);
+      assertEquals(replServer1.getGenerationId(baseDn.toNormalizedString()), rgenId);
 
       // Updates count in RS1 must stay unchanged = to 1
       Thread.sleep(500);
@@ -1257,13 +1046,15 @@
 
       debugInfo("Verifying that DS2 is not in bad gen id any more");
 
-      assertTrue(!replServer1.getReplicationServerDomain(baseDn, false).
+      assertTrue(!replServer1.getReplicationServerDomain(
+          baseDn.toNormalizedString(), false).
           isDegradedDueToGenerationId(server2ID),
       "Expecting that DS2 is not in bad gen id from RS1");
 
       debugInfo("Verifying that DS3 is not in bad gen id any more");
 
-      assertTrue(!replServer1.getReplicationServerDomain(baseDn, false).
+      assertTrue(!replServer1.getReplicationServerDomain(
+          baseDn.toNormalizedString(), false).
           isDegradedDueToGenerationId(server3ID),
       "Expecting that DS3 is not in bad gen id from RS1");
 
@@ -1281,7 +1072,7 @@
         /* expected */
         AddMsg rcvmsg = (AddMsg)msg;
         assertEquals(rcvmsg.getChangeNumber(), emsg.getChangeNumber());
-      } 
+      }
       catch(SocketTimeoutException e)
       {
         fail("The msg send by DS2 is expected to be received by DS3)");
@@ -1339,11 +1130,11 @@
       Thread.sleep(1500);
 
       debugInfo("Expect genId are set in all replServers.");
-      assertEquals(replServer1.getGenerationId(baseDn), EMPTY_DN_GENID,
+      assertEquals(replServer1.getGenerationId(baseDn.toNormalizedString()), EMPTY_DN_GENID,
         " in replServer1");
-      assertEquals(replServer2.getGenerationId(baseDn), EMPTY_DN_GENID,
+      assertEquals(replServer2.getGenerationId(baseDn.toNormalizedString()), EMPTY_DN_GENID,
         " in replServer2");
-      assertEquals(replServer3.getGenerationId(baseDn), EMPTY_DN_GENID,
+      assertEquals(replServer3.getGenerationId(baseDn.toNormalizedString()), EMPTY_DN_GENID,
         " in replServer3");
 
       debugInfo("Disconnect DS from replServer1.");
@@ -1352,9 +1143,9 @@
 
       debugInfo(
         "Expect genIds to be resetted in all servers to -1 as no more DS in topo");
-      assertEquals(replServer1.getGenerationId(baseDn), -1);
-      assertEquals(replServer2.getGenerationId(baseDn), -1);
-      assertEquals(replServer3.getGenerationId(baseDn), -1);
+      assertEquals(replServer1.getGenerationId(baseDn.toNormalizedString()), -1);
+      assertEquals(replServer2.getGenerationId(baseDn.toNormalizedString()), -1);
+      assertEquals(replServer3.getGenerationId(baseDn.toNormalizedString()), -1);
 
       debugInfo("Add entries to DS");
       this.addTestEntriesToDB(updatedEntries);
@@ -1367,9 +1158,9 @@
         "Expect genIds to be set in all servers based on the added entries.");
       genId = readGenIdFromSuffixRootEntry();
       assertTrue(genId != -1);
-      assertEquals(replServer1.getGenerationId(baseDn), genId);
-      assertEquals(replServer2.getGenerationId(baseDn), genId);
-      assertEquals(replServer3.getGenerationId(baseDn), genId);
+      assertEquals(replServer1.getGenerationId(baseDn.toNormalizedString()), genId);
+      assertEquals(replServer2.getGenerationId(baseDn.toNormalizedString()), genId);
+      assertEquals(replServer3.getGenerationId(baseDn.toNormalizedString()), genId);
 
       debugInfo("Connecting broker2 to replServer3 with a good genId");
       try
@@ -1385,7 +1176,7 @@
 
       debugInfo(
         "Expecting that broker2 is not in bad gen id since it has a correct genId");
-      assertTrue(!replServer1.getReplicationServerDomain(baseDn, false).
+      assertTrue(!replServer1.getReplicationServerDomain(baseDn.toNormalizedString(), false).
         isDegradedDueToGenerationId(server2ID));
 
       debugInfo("Disconnecting DS from replServer1");
@@ -1393,9 +1184,9 @@
 
       debugInfo(
         "Expect all genIds to keep their value since broker2 is still connected.");
-      assertEquals(replServer1.getGenerationId(baseDn), genId);
-      assertEquals(replServer2.getGenerationId(baseDn), genId);
-      assertEquals(replServer3.getGenerationId(baseDn), genId);
+      assertEquals(replServer1.getGenerationId(baseDn.toNormalizedString()), genId);
+      assertEquals(replServer2.getGenerationId(baseDn.toNormalizedString()), genId);
+      assertEquals(replServer3.getGenerationId(baseDn.toNormalizedString()), genId);
 
       debugInfo("Connecting broker2 to replServer1 with a bad genId");
       try
@@ -1412,7 +1203,7 @@
 
       debugInfo(
         "Expecting that broker3 is in bad gen id since it has a bad genId");
-      assertTrue(replServer1.getReplicationServerDomain(baseDn, false).
+      assertTrue(replServer1.getReplicationServerDomain(baseDn.toNormalizedString(), false).
         isDegradedDueToGenerationId(server3ID));
 
       int found = testEntriesInDb();
@@ -1432,7 +1223,7 @@
         "objectclass: top",
         "objectclass: ds-task",
         "objectclass: ds-task-reset-generation-id",
-        "ds-task-class-name: org.opends.server.tasks.SetGenerationIdTask",
+        "ds-task-class-name: org.opends.server.replication.service.SetGenerationIdTask",
         "ds-task-reset-generation-id-domain-base-dn: " + baseDnStr);
       addTask(taskReset, ResultCode.SUCCESS, null);
       waitTaskState(taskReset, TaskState.COMPLETED_SUCCESSFULLY, null);
@@ -1440,9 +1231,9 @@
 
       debugInfo("Verifying that all replservers genIds have been reset.");
       genId = readGenIdFromSuffixRootEntry();
-      assertEquals(replServer1.getGenerationId(baseDn), genId);
-      assertEquals(replServer2.getGenerationId(baseDn), genId);
-      assertEquals(replServer3.getGenerationId(baseDn), genId);
+      assertEquals(replServer1.getGenerationId(baseDn.toNormalizedString()), genId);
+      assertEquals(replServer2.getGenerationId(baseDn.toNormalizedString()), genId);
+      assertEquals(replServer3.getGenerationId(baseDn.toNormalizedString()), genId);
 
       debugInfo("Adding reset task to DS.");
       taskReset = TestCaseUtils.makeEntry(
@@ -1451,7 +1242,7 @@
         "objectclass: top",
         "objectclass: ds-task",
         "objectclass: ds-task-reset-generation-id",
-        "ds-task-class-name: org.opends.server.tasks.SetGenerationIdTask",
+        "ds-task-class-name: org.opends.server.replication.service.SetGenerationIdTask",
         "ds-task-reset-generation-id-domain-base-dn: " + baseDnStr,
         "ds-task-reset-generation-id-new-value: -1");
       addTask(taskReset, ResultCode.SUCCESS, null);
@@ -1460,9 +1251,9 @@
 
       debugInfo("Verifying that all replservers genIds have been reset.");
       genId = readGenIdFromSuffixRootEntry();
-      assertEquals(replServer1.getGenerationId(baseDn), -1);
-      assertEquals(replServer2.getGenerationId(baseDn), -1);
-      assertEquals(replServer3.getGenerationId(baseDn), -1);
+      assertEquals(replServer1.getGenerationId(baseDn.toNormalizedString()), -1);
+      assertEquals(replServer2.getGenerationId(baseDn.toNormalizedString()), -1);
+      assertEquals(replServer3.getGenerationId(baseDn.toNormalizedString()), -1);
 
       debugInfo(
         "Disconnect DS from replServer1 (required in order to DEL entries).");
@@ -1579,8 +1370,8 @@
   }
 
   /**
-   * Loop opening sessions to the Replication Server 
-   * to check that it handle correctly deconnection and reconnection. 
+   * Loop opening sessions to the Replication Server
+   * to check that it handle correctly deconnection and reconnection.
    */
   @Test(enabled=false, groups="slow")
   public void testLoop() throws Exception
@@ -1604,10 +1395,10 @@
         long generationId = 1000+i;
         broker = openReplicationSession(baseDn,
             server2ID, 100, getChangelogPort(changelog1ID),
-            1000, !emptyOldChanges, generationId);        
+            1000, !emptyOldChanges, generationId);
         debugInfo(testCase + " Expect genId to be set in memory on the replication " +
-        " server side even if not wrote on disk/db since no change occurred.");                
-        rgenId = replServer1.getGenerationId(baseDn);
+        " server side even if not wrote on disk/db since no change occurred.");
+        rgenId = replServer1.getGenerationId(baseDn.toNormalizedString());
         assertEquals(rgenId, generationId);
         broker.stop();
         broker = null;
@@ -1620,9 +1411,9 @@
       postTest();
     }
   }
-  
+
   /**
-   * This is used to make sure that the 3 tests are run in the 
+   * This is used to make sure that the 3 tests are run in the
    * specified order since this is necessary.
    */
   @Test(enabled=true, groups="slow")
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java
index 7713a48..443b601 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java
@@ -60,8 +60,8 @@
 import org.opends.messages.Severity;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.protocols.internal.InternalSearchOperation;
-import org.opends.server.replication.plugin.ReplicationBroker;
-import org.opends.server.replication.plugin.ReplicationDomain;
+import org.opends.server.replication.service.ReplicationBroker;
+import org.opends.server.replication.plugin.LDAPReplicationDomain;
 import org.opends.server.replication.protocol.DoneMsg;
 import org.opends.server.replication.protocol.EntryMsg;
 import org.opends.server.replication.protocol.ErrorMsg;
@@ -144,7 +144,7 @@
   ReplicationServer changelog2 = null;
   ReplicationServer changelog3 = null;
   boolean emptyOldChanges = true;
-  ReplicationDomain replDomain = null;
+  LDAPReplicationDomain replDomain = null;
 
   private void log(String s)
   {
@@ -181,7 +181,7 @@
     // re-enabled and this clears the backend reference and thus the underlying
     // data. So for this particular test, we use a classical backend. Let's
     // clear it.
-    ReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
+    LDAPReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
 
     updatedEntries = newLDIFEntries();
 
@@ -190,7 +190,7 @@
     connection = InternalClientConnection.getRootConnection();
 
     synchroServerEntry = null;
-    replServerEntry = null;   
+    replServerEntry = null;
 
     taskInitFromS2 = TestCaseUtils.makeEntry(
         "dn: ds-task-id=" + UUID.randomUUID() +
@@ -198,7 +198,7 @@
         "objectclass: top",
         "objectclass: ds-task",
         "objectclass: ds-task-initialize-from-remote-replica",
-        "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+        "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
         "ds-task-initialize-domain-dn: " + EXAMPLE_DN,
         "ds-task-initialize-replica-server-id: " + server2ID);
 
@@ -208,7 +208,7 @@
         "objectclass: top",
         "objectclass: ds-task",
         "objectclass: ds-task-initialize-remote-replica",
-        "ds-task-class-name: org.opends.server.tasks.InitializeTargetTask",
+        "ds-task-class-name: org.opends.server.replication.service.InitializeTargetTask",
         "ds-task-initialize-domain-dn: " + EXAMPLE_DN,
         "ds-task-initialize-replica-server-id: " + server2ID);
 
@@ -218,7 +218,7 @@
         "objectclass: top",
         "objectclass: ds-task",
         "objectclass: ds-task-initialize-remote-replica",
-        "ds-task-class-name: org.opends.server.tasks.InitializeTargetTask",
+        "ds-task-class-name: org.opends.server.replication.service.InitializeTargetTask",
         "ds-task-initialize-domain-dn: " + EXAMPLE_DN,
         "ds-task-initialize-replica-server-id: all");
   }
@@ -321,7 +321,7 @@
           {
             break;
           }
-          Thread.sleep(10);
+          Thread.sleep(100);
         }
       } while (completionTime == null);
 
@@ -485,8 +485,10 @@
     // Send entries
     try
     {
-      RoutableMsg initTargetMessage = new InitializeTargetMsg(
-          baseDn, server2ID, destinationServerID, requestorID, updatedEntries.length);
+      RoutableMsg initTargetMessage =
+        new InitializeTargetMsg(
+          EXAMPLE_DN, server2ID, destinationServerID, requestorID,
+          updatedEntries.length);
       broker.publish(initTargetMessage);
 
       for (String entry : updatedEntries)
@@ -536,7 +538,7 @@
         {
           EntryMsg em = (EntryMsg)msg;
           log("Broker " + serverID + " receives entry " + new String(em.getEntryBytes()));
-          entriesReceived++;
+          entriesReceived+=countEntryLimits(em.getEntryBytes());
         }
         else if (msg instanceof DoneMsg)
         {
@@ -572,6 +574,30 @@
   }
 
   /**
+   * Count the number of entries in the provided byte[].
+   * This is based on the hypothesis that the entries are separated
+   * by a "\n\n" String.
+   *
+   * @param   entryBytes
+   * @return  The number of entries in the provided byte[].
+   */
+  private int countEntryLimits(byte[] entryBytes)
+  {
+    int entryCount = 0;
+    int count = 0;
+    while (count<=entryBytes.length-2)
+    {
+      if ((entryBytes[count] == '\n') && (entryBytes[count+1] == '\n'))
+      {
+        entryCount++;
+        count++;
+      }
+      count++;
+    }
+    return entryCount;
+  }
+
+  /**
    * Creates a new replicationServer.
    * @param changelogId The serverID of the replicationServer to create.
    * @return The new replicationServer.
@@ -592,7 +618,7 @@
       ReplServerFakeConfiguration conf =
         new ReplServerFakeConfiguration(
             getChangelogPort(changelogId),
-            "initOnlineTest" + getChangelogPort(changelogId) + testCase + "Db", 
+            "initOnlineTest" + getChangelogPort(changelogId) + testCase + "Db",
             0,
             changelogId,
             0,
@@ -638,7 +664,7 @@
 
 
       // Clear the backend
-      ReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
+      LDAPReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
 
       synchroServerEntry = TestCaseUtils.entryFromLdifString(synchroServerLdif);
       DirectoryServer.getConfigHandler().addEntry(synchroServerEntry, null);
@@ -646,7 +672,7 @@
         "Unable to add the synchronized server");
       configEntryList.add(synchroServerEntry.getDN());
 
-      replDomain = ReplicationDomain.retrievesReplicationDomain(baseDn);
+      replDomain = LDAPReplicationDomain.retrievesReplicationDomain(baseDn);
 
       assertTrue(!replDomain.ieRunning(),
         "ReplicationDomain: Import/Export is not expected to be running");
@@ -699,7 +725,7 @@
         server2 = openReplicationSession(DN.decode(EXAMPLE_DN),
           server2ID, 100, getChangelogPort(changelog1ID), 1000, emptyOldChanges);
 
-      Thread.sleep(2000);
+      // Thread.sleep(2000);
 
       // In S1 launch the total update
       addTask(taskInitFromS2, ResultCode.SUCCESS, null);
@@ -729,7 +755,7 @@
     catch(Exception e)
     {
       fail(testCase + " Exception:"+ e.getMessage() + " " + stackTraceToSingleLineString(e));
-    } finally 
+    } finally
     {
       afterTest();
     }
@@ -744,7 +770,7 @@
     String testCase = "initializeExport";
 
     log("Starting "+testCase);
-    
+
     try
     {
       changelog1 = createChangelogServer(changelog1ID, testCase);
@@ -758,9 +784,11 @@
         server2 = openReplicationSession(DN.decode(EXAMPLE_DN),
           server2ID, 100, getChangelogPort(changelog1ID), 1000, emptyOldChanges);
 
-      Thread.sleep(3000);
+      // Not needed anymore since OpenReplicationSession
+      // checks for session establishment ?
+      // Thread.sleep(3000);
 
-      InitializeRequestMsg initMsg = new InitializeRequestMsg(baseDn,
+      InitializeRequestMsg initMsg = new InitializeRequestMsg(EXAMPLE_DN,
         server2ID, server1ID);
       server2.publish(initMsg);
 
@@ -782,7 +810,7 @@
     String testCase = "initializeTargetExport";
 
     log("Starting " + testCase);
-    
+
     try
     {
 
@@ -799,7 +827,7 @@
         server2 = openReplicationSession(DN.decode(EXAMPLE_DN),
           server2ID, 100, getChangelogPort(changelog1ID), 1000, emptyOldChanges);
 
-      Thread.sleep(1000);
+      // Thread.sleep(1000);
 
       // Launch in S1 the task that will initialize S2
       addTask(taskInitTargetS2, ResultCode.SUCCESS, null);
@@ -846,7 +874,7 @@
         server3 = openReplicationSession(DN.decode(EXAMPLE_DN),
           server3ID, 100, getChangelogPort(changelog1ID), 1000, emptyOldChanges);
 
-      Thread.sleep(1000);
+      // Thread.sleep(1000);
 
       // Launch in S1 the task that will initialize S2
       addTask(taskInitTargetAll, ResultCode.SUCCESS, null);
@@ -891,7 +919,14 @@
       // S2 publishes entries to S1
       makeBrokerPublishEntries(server2, server2ID, server1ID, server2ID);
 
-      Thread.sleep(10000); // FIXME - how to know the import is done
+      // wait until the replication domain has expected generationID
+      // this should indicate that the import occured correctly.
+      for (int count = 0; count < 100; count++)
+      {
+        if (replDomain.getGenerationID() == 56869)
+          break;
+        Thread.sleep(200);
+      }
 
       // Test that entries have been imported in S1
       testEntriesInDb();
@@ -926,7 +961,7 @@
           "objectclass: top",
           "objectclass: ds-task",
           "objectclass: ds-task-initialize-remote-replica",
-          "ds-task-class-name: org.opends.server.tasks.InitializeTargetTask",
+          "ds-task-class-name: org.opends.server.replication.service.InitializeTargetTask",
           "ds-task-initialize-domain-dn: foo",
           "ds-task-initialize-remote-replica-server-id: " + server2ID);
       addTask(taskInitTarget, ResultCode.INVALID_DN_SYNTAX,
@@ -939,7 +974,7 @@
           "objectclass: top",
           "objectclass: ds-task",
           "objectclass: ds-task-initialize-remote-replica",
-          "ds-task-class-name: org.opends.server.tasks.InitializeTargetTask",
+          "ds-task-class-name: org.opends.server.replication.service.InitializeTargetTask",
           "ds-task-initialize-domain-dn: dc=foo",
           "ds-task-initialize-remote-replica-server-id: " + server2ID);
       addTask(taskInitTarget, ResultCode.OTHER,
@@ -987,11 +1022,11 @@
           "objectclass: top",
           "objectclass: ds-task",
           "objectclass: ds-task-initialize-from-remote-replica",
-          "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+          "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
           "ds-task-initialize-domain-dn: foo",
           "ds-task-initialize-replica-server-id: " + server2ID);
       addTask(taskInit, ResultCode.INVALID_DN_SYNTAX,
-          ERR_TASK_INITIALIZE_INVALID_DN.get());
+          ERR_NO_MATCHING_DOMAIN.get());
 
       // Domain base dn not related to any domain
       taskInit = TestCaseUtils.makeEntry(
@@ -1000,10 +1035,11 @@
           "objectclass: top",
           "objectclass: ds-task",
           "objectclass: ds-task-initialize-from-remote-replica",
-          "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+          "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
           "ds-task-initialize-domain-dn: dc=foo",
           "ds-task-initialize-replica-server-id: " + server2ID);
-      addTask(taskInit, ResultCode.OTHER, ERR_NO_MATCHING_DOMAIN.get());
+      addTask(taskInit, ResultCode.INVALID_DN_SYNTAX,
+          ERR_NO_MATCHING_DOMAIN.get());
 
       // Invalid Source
       taskInit = TestCaseUtils.makeEntry(
@@ -1012,7 +1048,7 @@
           "objectclass: top",
           "objectclass: ds-task",
           "objectclass: ds-task-initialize-from-remote-replica",
-          "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+          "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
           "ds-task-initialize-domain-dn: " + baseDn,
           "ds-task-initialize-replica-server-id: -3");
       addTask(taskInit, ResultCode.OTHER,
@@ -1083,25 +1119,29 @@
 
       // Check that the list of connected LDAP servers is correct
       // in each replication servers
-      List<String> l1 = changelog1.getReplicationServerDomain(baseDn, false).
+      List<String> l1 = changelog1.getReplicationServerDomain(
+          baseDn.toNormalizedString(), false).
         getConnectedLDAPservers();
       assertEquals(l1.size(), 1);
       assertEquals(l1.get(0), String.valueOf(server1ID));
 
       List<String> l2;
-    l2 = changelog2.getReplicationServerDomain(baseDn, false).getConnectedLDAPservers();
+    l2 = changelog2.getReplicationServerDomain(
+        baseDn.toNormalizedString(), false).getConnectedLDAPservers();
       assertEquals(l2.size(), 2);
       assertTrue(l2.contains(String.valueOf(server2ID)));
       assertTrue(l2.contains(String.valueOf(server3ID)));
 
       List<String> l3;
-    l3 = changelog3.getReplicationServerDomain(baseDn, false).getConnectedLDAPservers();
+    l3 = changelog3.getReplicationServerDomain(
+        baseDn.toNormalizedString(), false).getConnectedLDAPservers();
       assertEquals(l3.size(), 0);
 
       // Test updates
       broker3.stop();
       Thread.sleep(1000);
-    l2 = changelog2.getReplicationServerDomain(baseDn, false).getConnectedLDAPservers();
+    l2 = changelog2.getReplicationServerDomain(
+        baseDn.toNormalizedString(), false).getConnectedLDAPservers();
       assertEquals(l2.size(), 1);
       assertEquals(l2.get(0), String.valueOf(server2ID));
 
@@ -1109,7 +1149,8 @@
         server3ID, 100, getChangelogPort(changelog2ID), 1000, emptyOldChanges);
       broker2.stop();
       Thread.sleep(1000);
-    l2 = changelog2.getReplicationServerDomain(baseDn, false).getConnectedLDAPservers();
+    l2 = changelog2.getReplicationServerDomain(
+        baseDn.toNormalizedString(), false).getConnectedLDAPservers();
       assertEquals(l2.size(), 1);
       assertEquals(l2.get(0), String.valueOf(server3ID));
 
@@ -1124,7 +1165,7 @@
       afterTest();
     }
   }
-  
+
   @Test(enabled=true, groups="slow")
   public void initializeTargetExportMultiSS() throws Exception
   {
@@ -1153,7 +1194,7 @@
             server2ID, 100, getChangelogPort(changelog2ID), 1000, emptyOldChanges);
       }
 
-      Thread.sleep(1000);
+     // Thread.sleep(1000);
 
       // Launch in S1 the task that will initialize S2
       addTask(taskInitTargetS2, ResultCode.SUCCESS, null);
@@ -1200,7 +1241,8 @@
         log(testCase + " Will connect server 2 to " + changelog2ID);
         server2 = openReplicationSession(DN.decode(EXAMPLE_DN),
           server2ID, 100, getChangelogPort(changelog2ID),
-          1000, emptyOldChanges, changelog1.getGenerationId(baseDn));
+          1000, emptyOldChanges,
+          changelog1.getGenerationId(baseDn.toNormalizedString()));
       }
 
       // Connect a broker acting as server 3 to Repl Server 3
@@ -1212,15 +1254,16 @@
         log(testCase + " Will connect server 3 to " + changelog3ID);
         server3 = openReplicationSession(DN.decode(EXAMPLE_DN),
           server3ID, 100, getChangelogPort(changelog3ID),
-          1000, emptyOldChanges, changelog1.getGenerationId(baseDn));
+          1000, emptyOldChanges,
+          changelog1.getGenerationId(baseDn.toNormalizedString()));
       }
 
-      Thread.sleep(500);
+      // Thread.sleep(500);
 
       // S3 sends init request
       log(testCase + " server 3 Will send reqinit to " + server1ID);
       InitializeRequestMsg initMsg =
-        new InitializeRequestMsg(baseDn, server3ID, server1ID);
+        new InitializeRequestMsg(EXAMPLE_DN, server3ID, server1ID);
       server3.publish(initMsg);
 
       // S3 should receive target, entries & done
@@ -1268,7 +1311,7 @@
         "objectclass: top",
         "objectclass: ds-task",
         "objectclass: ds-task-initialize-from-remote-replica",
-        "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+        "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
         "ds-task-initialize-domain-dn: " + baseDn,
         "ds-task-initialize-replica-server-id: " + 20);
 
@@ -1284,7 +1327,7 @@
         "objectclass: top",
         "objectclass: ds-task",
         "objectclass: ds-task-initialize-from-remote-replica",
-        "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+        "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
         "ds-task-initialize-domain-dn: " + baseDn,
         "ds-task-initialize-replica-server-id: " + server1ID);
 
@@ -1326,7 +1369,7 @@
         "objectclass: top",
         "objectclass: ds-task",
         "objectclass: ds-task-initialize-remote-replica",
-        "ds-task-class-name: org.opends.server.tasks.InitializeTargetTask",
+        "ds-task-class-name: org.opends.server.replication.service.InitializeTargetTask",
         "ds-task-initialize-domain-dn: " + baseDn,
         "ds-task-initialize-replica-server-id: " + 0);
 
@@ -1400,7 +1443,7 @@
         "objectclass: top",
         "objectclass: ds-task",
         "objectclass: ds-task-initialize-from-remote-replica",
-        "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+        "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
         "ds-task-initialize-domain-dn: " + baseDn,
         "ds-task-initialize-replica-server-id: " + server2ID);
 
@@ -1414,7 +1457,7 @@
         "objectclass: top",
         "objectclass: ds-task",
         "objectclass: ds-task-initialize-from-remote-replica",
-        "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+        "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
         "ds-task-initialize-domain-dn: " + baseDn,
         "ds-task-initialize-replica-server-id: " + server2ID);
 
@@ -1537,8 +1580,8 @@
     super.classCleanUp();
 
     // Clear the backend
-    ReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
-    
+    LDAPReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
+
     paranoiaCheck();
   }
 }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ProtocolWindowTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ProtocolWindowTest.java
index 8382644..db2083f 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ProtocolWindowTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ProtocolWindowTest.java
@@ -31,6 +31,8 @@
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
+import org.opends.server.replication.service.ReplicationBroker;
+
 import java.net.ServerSocket;
 import java.net.SocketTimeoutException;
 import java.util.Iterator;
@@ -49,7 +51,6 @@
 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.plugin.ReplicationBroker;
 import org.opends.server.replication.protocol.AddMsg;
 import org.opends.server.replication.protocol.ReplicationMsg;
 import org.opends.server.replication.server.ReplServerFakeConfiguration;
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReSyncTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReSyncTest.java
index eb65fe1..2e4dfbc 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReSyncTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReSyncTest.java
@@ -39,14 +39,13 @@
 import org.opends.server.core.AddOperationBasis;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.protocols.internal.InternalClientConnection;
-import org.opends.server.replication.plugin.ReplicationDomain;
+import org.opends.server.replication.plugin.LDAPReplicationDomain;
 import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
 import org.opends.server.types.ResultCode;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
-import static org.opends.server.TestCaseUtils.*;
 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;
@@ -97,7 +96,7 @@
     // data. So for this particular test, we use a classical backend. Let's
     // clear it and create the root entry
 
-    ReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
+    LDAPReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
     addEntry("dn: dc=example,dc=com\n" + "objectClass: top\n"
         + "objectClass: domain\n");
 
@@ -108,7 +107,7 @@
         + "objectClass: ds-cfg-replication-server\n"
         + "cn: Replication Server\n"
         + "ds-cfg-replication-port:" + replServerPort + "\n"
-        + "ds-cfg-replication-db-directory: ReSyncTest\n"    
+        + "ds-cfg-replication-db-directory: ReSyncTest\n"
         + "ds-cfg-replication-server-id: 104\n";
     replServerEntry = TestCaseUtils.entryFromLdifString(replServerLdif);
 
@@ -153,7 +152,7 @@
        entry.getUserAttributes(), entry.getOperationalAttributes());
     addOp.setInternalOperation(true);
     addOp.run();
-    
+
     entryList.add(entry.getDN());
     return addOp.getResultCode();
   }
@@ -258,7 +257,7 @@
    if (getEntry(DN.decode("dc=fooUniqueName2," + EXAMPLE_DN), 30000, true) == null)
      fail("The Directory has not been resynchronized after the restore.");
   }
-  
+
   /**
    * Clean up the environment.
    *
@@ -272,7 +271,7 @@
     super.classCleanUp();
 
     // Clear the backend
-    ReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
+    LDAPReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
 
     paranoiaCheck();
   }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
index 880b11f..f2c0de2 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
@@ -58,10 +58,10 @@
 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.ReplicationBroker;
-import org.opends.server.replication.plugin.ReplicationDomain;
+import org.opends.server.replication.plugin.LDAPReplicationDomain;
 import org.opends.server.replication.protocol.ErrorMsg;
 import org.opends.server.replication.protocol.ReplSessionSecurity;
 import org.opends.server.replication.protocol.ReplicationMsg;
@@ -104,7 +104,7 @@
   // TestCaseUtils.initializeTestBackend(true).
   // (using the default TestCaseUtils.TEST_ROOT_DN_STRING suffix)
   protected static final long TEST_DN_WITH_ROOT_ENTRY_GENID = 5095L;
-  
+
   /**
    * Generation id for a fully empty domain.
    */
@@ -139,8 +139,8 @@
 
   // Call the paranoiaCheck at test cleanup or not.
   // Must not been touched except if sub class has its own clean up code,
-  // for instance: 
-  // @AfterClass  
+  // for instance:
+  // @AfterClass
   // public void classCleanUp() throws Exception
   // {
   //   callParanoiaCheck = false;
@@ -202,8 +202,8 @@
     long genId = TEST_DN_WITH_ROOT_ENTRY_GENID;
     try
     {
-      ReplicationDomain replDomain = ReplicationDomain.retrievesReplicationDomain(baseDn);
-      genId = replDomain.getGenerationId();
+      LDAPReplicationDomain replDomain = LDAPReplicationDomain.retrievesReplicationDomain(baseDn);
+      genId = replDomain.getGenerationID();
     }
     catch(Exception e) {}
     return genId;
@@ -233,15 +233,14 @@
         long generationId)
   throws Exception, SocketException
   {
-    ServerState state;
+    ServerState state = new ServerState();
+
     if (emptyOldChanges)
-       state = new PersistentServerState(baseDn, serverId);
-    else
-       state = new ServerState();
+       new PersistentServerState(baseDn, serverId, new ServerState());
 
     ReplicationBroker broker = new ReplicationBroker(null,
-        state, baseDn, serverId, 0, 0, 0, 0,
-        window_size, 0, generationId, getReplSessionSecurity(), (byte)1);
+        state, baseDn.toNormalizedString(), serverId, window_size,
+        generationId, 100000, getReplSessionSecurity(), (byte)1);
     ArrayList<String> servers = new ArrayList<String>(1);
     servers.add("localhost:" + port);
     broker.start(servers);
@@ -347,8 +346,8 @@
           throws Exception, SocketException
   {
     ReplicationBroker broker = new ReplicationBroker(null,
-        state, baseDn, serverId, 0, 0, 0, 0, window_size, 0, generationId,
-        getReplSessionSecurity(), (byte)1);
+        state, baseDn.toNormalizedString(), serverId, window_size, generationId,
+        100000, getReplSessionSecurity(), (byte)1);
     ArrayList<String> servers = new ArrayList<String>(1);
     servers.add("localhost:" + port);
     broker.start(servers);
@@ -380,16 +379,14 @@
         boolean emptyOldChanges, long generationId)
             throws Exception, SocketException
   {
-    ServerState state;
+    ServerState state = new ServerState();
+
     if (emptyOldChanges)
-       state = new PersistentServerState(baseDn, serverId);
-    else
-       state = new ServerState();
+       new PersistentServerState(baseDn, serverId, new ServerState());
 
     ReplicationBroker broker = new ReplicationBroker(null,
-        state, baseDn, serverId, maxRcvQueue, 0,
-        maxSendQueue, 0, window_size, 0, generationId,
-        getReplSessionSecurity(), (byte)1);
+        state, baseDn.toNormalizedString(), serverId, window_size,
+        generationId, 0, getReplSessionSecurity(), (byte)1);
     ArrayList<String> servers = new ArrayList<String>(1);
     servers.add("localhost:" + port);
     broker.start(servers);
@@ -652,7 +649,7 @@
   protected long getMonitorAttrValue(DN baseDn, String attr) throws Exception
   {
     String monitorFilter =
-         "(&(cn=replication plugin*)(base-dn=" + baseDn + "))";
+         "(&(cn=replication Domain*)(base-dn=" + baseDn + "))";
 
     InternalSearchOperation op;
     int count = 0;
@@ -1111,5 +1108,5 @@
     {
       fail("addEntries Exception:"+ e.getMessage() + " " + stackTraceToSingleLineString(e));
     }
-  }  
+  }
 }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java
index b388847..e2e27bf 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java
@@ -46,9 +46,8 @@
 import org.opends.server.core.ModifyOperation;
 import org.opends.server.core.ModifyOperationBasis;
 import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.replication.service.ReplicationBroker;
 import org.opends.server.replication.common.ChangeNumberGenerator;
-import org.opends.server.replication.plugin.ReplicationBroker;
-import org.opends.server.replication.protocol.DeleteMsg;
 import org.opends.server.replication.protocol.ModifyMsg;
 import org.opends.server.replication.protocol.ReplicationMsg;
 import org.opends.server.types.Attribute;
@@ -100,7 +99,7 @@
         + "objectClass: ds-cfg-replication-server\n"
         + "cn: Replication Server\n"
         + "ds-cfg-replication-port: " + replServerPort + "\n"
-        + "ds-cfg-replication-db-directory: SchemaReplicationTest\n"    
+        + "ds-cfg-replication-db-directory: SchemaReplicationTest\n"
         + "ds-cfg-replication-server-id: 105\n";
     replServerEntry = TestCaseUtils.entryFromLdifString(replServerLdif);
 
@@ -247,9 +246,6 @@
    * Checks that changes done to the schema files are pushed to the
    * ReplicationServers and that the ServerState is updated in the schema
    * file.
-   * FIXME: This test is disabled because it has side effects.
-   * It causes schema tests in org.opends.server.core.AddOperationTestCase
-   * to fail when running the build test target.
    */
   @Test(enabled=true, dependsOnMethods = { "replaySchemaChange" })
   public void pushSchemaFilesChange() throws Exception
@@ -261,7 +257,7 @@
 
     ReplicationBroker broker =
       openReplicationSession(baseDn, (short) 3, 100, replServerPort, 5000, true);
-    
+
     try
     {
       // create a schema change Notification
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/StressTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/StressTest.java
index a9f7815..6fb9fde 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/StressTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/StressTest.java
@@ -47,7 +47,7 @@
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ModifyOperation;
 import org.opends.server.protocols.internal.InternalClientConnection;
-import org.opends.server.replication.plugin.ReplicationBroker;
+import org.opends.server.replication.service.ReplicationBroker;
 import org.opends.server.replication.protocol.AddMsg;
 import org.opends.server.replication.protocol.ReplicationMsg;
 import org.opends.server.types.Attribute;
@@ -203,7 +203,7 @@
         + "objectClass: ds-cfg-replication-server\n"
         + "cn: Replication Server\n"
         + "ds-cfg-replication-port: " + replServerPort + "\n"
-        + "ds-cfg-replication-db-directory: StressTest\n"    
+        + "ds-cfg-replication-db-directory: StressTest\n"
         + "ds-cfg-replication-server-id: 106\n";
     replServerEntry = TestCaseUtils.entryFromLdifString(replServerLdif);
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
index 4bf87f1..175339a 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
@@ -58,8 +58,8 @@
 import org.opends.server.protocols.ldap.LDAPModification;
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.replication.common.ChangeNumberGenerator;
-import org.opends.server.replication.plugin.ReplicationBroker;
-import org.opends.server.replication.plugin.ReplicationDomain;
+import org.opends.server.replication.service.ReplicationBroker;
+import org.opends.server.replication.plugin.LDAPReplicationDomain;
 import org.opends.server.replication.protocol.AddMsg;
 import org.opends.server.replication.protocol.DeleteMsg;
 import org.opends.server.replication.protocol.HeartbeatThread;
@@ -160,7 +160,7 @@
         + "objectClass: ds-cfg-replication-server\n"
         + "cn: Replication Server\n"
         + "ds-cfg-replication-port: " + replServerPort + "\n"
-        + "ds-cfg-replication-db-directory: UpdateOperationTest\n"    
+        + "ds-cfg-replication-db-directory: UpdateOperationTest\n"
         + "ds-cfg-replication-server-id: 107\n";
     replServerEntry = TestCaseUtils.entryFromLdifString(replServerLdif);
 
@@ -676,32 +676,32 @@
     // because the conflict has been automatically resolved.
     assertEquals(DummyAlertHandler.getAlertCount(), AlertCount,
         "An alert was incorrectly generated when resolving conflicts");
-    
+
     /*
-     * Test that modify conflict resolution is able to detect that 
+     * Test that modify conflict resolution is able to detect that
      * because there is a conflict between a MODIFYDN and a MODIFY,
      * when a MODIFY is replayed the attribute that is being modified is
      * now the RDN of the entry and therefore should not be deleted.
      */
-    // send a modify operation attempting to replace the RDN entry 
+    // send a modify operation attempting to replace the RDN entry
     // with a new value
     mods = generatemods("uid", "AnotherUid");
     modMsg = new ModifyMsg(gen.newChangeNumber(),
         personWithUUIDEntry.getDN(), mods,
         user1entryUUID);
-    
+
     updateMonitorCount(baseDn, resolvedMonitorAttr);
     AlertCount = DummyAlertHandler.getAlertCount();
     broker.publish(modMsg);
-    
+
     // check that the modify has been applied.
     found = checkEntryHasAttribute(personWithUUIDEntry.getDN(),
                            "uid", "AnotherUid", 10000, true);
-    
+
     if (found == false)
       fail("The modification has not been correctly replayed.");
     assertEquals(getMonitorDelta(), 1);
-    
+
     /*
      * Test that the conflict resolution code is able to detect
      * that an entry has been renamed and that a new entry has
@@ -1105,19 +1105,19 @@
     assertNotNull(resultEntry,
         "The ADD replication message was NOT applied under ou=baseDn2,"+baseDn);
     assertEquals(getMonitorDelta(), 1);
-    
+
     // Check that there was no administrative alert generated
     // because the conflict has been automatically resolved.
     assertEquals(DummyAlertHandler.getAlertCount(), AlertCount,
         "An alert was incorrectly generated when resolving conflicts");
 
-    
+
     //
     // Check that when a delete is conflicting with Add of some entries
     // below the deleted entries, the child entry that have been added
     // before the deleted is replayed gets renamed correctly.
     //
-    
+
     // add domain1 entry with 2 children : domain2 and domain3
     addEntry(domain1);
     domain1uid = getEntryUUID(DN.decode(domain1dn));
@@ -1129,10 +1129,10 @@
         "entryUUID = " + domain2uid + "+dc=domain2,ou=people," + TEST_ROOT_DN_STRING);
     DN conflictDomain3dn = DN.decode(
         "entryUUID = " + domain3uid + "+dc=domain3,ou=people," + TEST_ROOT_DN_STRING);
- 
+
     updateMonitorCount(baseDn, unresolvedMonitorAttr);
     AlertCount = DummyAlertHandler.getAlertCount();
-    
+
     // delete domain1
     delMsg = new DeleteMsg(domain1dn, gen.newChangeNumber(), domain1uid);
     broker.publish(delMsg);
@@ -1140,7 +1140,7 @@
     // check that the domain1 has correctly been deleted
     assertNull(getEntry(DN.decode(domain1dn), 10000, false),
         "The DELETE replication message was not replayed");
-    
+
     // check that domain2 and domain3 have been renamed
     assertNotNull(getEntry(conflictDomain2dn, 1000, true),
         "The conflicting entries were not created");
@@ -1149,23 +1149,23 @@
 
     // check that the 2 conflicting entries have been correctly marked
     assertTrue(checkEntryHasAttribute(conflictDomain2dn,
-        ReplicationDomain.DS_SYNC_CONFLICT, domain1dn, 1000, true));
+        LDAPReplicationDomain.DS_SYNC_CONFLICT, domain1dn, 1000, true));
     assertTrue(checkEntryHasAttribute(conflictDomain3dn,
-        ReplicationDomain.DS_SYNC_CONFLICT, domain1dn, 1000, true));
-    
+        LDAPReplicationDomain.DS_SYNC_CONFLICT, domain1dn, 1000, true));
+
     // check that unresolved conflict count has been incremented
     assertEquals(getMonitorDelta(), 1);
-    
+
     // Check that an administrative alert was generated
     // because the conflict has not been automatically resolved.
     assertEquals(DummyAlertHandler.getAlertCount(), AlertCount+2,
         "An alert was incorrectly generated when resolving conflicts");
 
-    
+
     // delete the resulting entries for the next test
     delEntry(conflictDomain2dn);
     delEntry(conflictDomain3dn);
-    
+
     //
     // Check that when an entry is added on one master below an entry
     // that is currently deleted on another master, the replay of the
@@ -1176,18 +1176,18 @@
         domain2.getObjectClassAttribute(),
         domain2.getAttributes(), new ArrayList<Attribute>());
     broker.publish(addMsg);
-    
+
     // check that conflict entry was created
     assertNotNull(getEntry(conflictDomain2dn, 1000, true),
       "The conflicting entries were not created");
-    
+
     // check that the entry have been correctly marked as conflicting.
     assertTrue(checkEntryHasAttribute(conflictDomain2dn,
-        ReplicationDomain.DS_SYNC_CONFLICT, domain2dn, 1000, true));
-    
+        LDAPReplicationDomain.DS_SYNC_CONFLICT, domain2dn, 1000, true));
+
     // check that unresolved conflict count has been incremented
     assertEquals(getMonitorDelta(), 1);
-    
+
     // Check that when an entry is deleted on a first master and
     // renamed on a second master and the rename is replayed last
     // this is correctly detected as a resolved conflict.
@@ -1215,14 +1215,14 @@
     // if the monitor counter did not get incremented after 200sec
     // then something got wrong.
     assertTrue(count < 200);
-    
+
     // Check that there was no administrative alert generated
     // because the conflict has been automatically resolved.
     assertEquals(DummyAlertHandler.getAlertCount(), AlertCount,
         "An alert was incorrectly generated when resolving conflicts");
 
     /*
-     * Check that a conflict is detected when an entry is 
+     * Check that a conflict is detected when an entry is
      * moved below an entry that does not exist.
      */
     updateMonitorCount(baseDn, unresolvedMonitorAttr);
@@ -1234,7 +1234,7 @@
         "uid=wrong, ou=people," + TEST_ROOT_DN_STRING,
         "uid=newrdn");
     broker.publish(modDnMsg);
-    
+
     count = 0;
     while ((count<2000) && getMonitorDelta() == 0)
     {
@@ -1246,11 +1246,11 @@
     // if the monitor counter did not get incremented after 200sec
     // then something got wrong.
     assertTrue(count < 200);
-    
+
     // check that the entry have been correctly marked as conflicting.
     assertTrue(checkEntryHasAttribute(
         DN.decode("uid=new person,ou=baseDn2,"+baseDn),
-        ReplicationDomain.DS_SYNC_CONFLICT,
+        LDAPReplicationDomain.DS_SYNC_CONFLICT,
         "uid=newrdn,ou=baseDn2,ou=People," + TEST_ROOT_DN_STRING, 1000, true));
 
     broker.stop();
@@ -1426,7 +1426,7 @@
 
       if (found == false)
         fail("The modification has not been correctly replayed.");
-      
+
       // Test that replication is able to add attribute that do
       // not exist in the schema.
       List<Modification> invalidMods = generatemods("badattribute", "value");
@@ -1569,7 +1569,7 @@
         "Starting replication test : infiniteReplayLoop"));
 
     final DN baseDn = DN.decode("ou=People," + TEST_ROOT_DN_STRING);
-    
+
     Thread.sleep(2000);
     ReplicationBroker broker =
       openReplicationSession(baseDn, (short) 11, 100, replServerPort, 1000, true);
@@ -1709,7 +1709,7 @@
         "Starting synchronization test : CNGeneratorAdjust"));
 
     final DN baseDn = DN.decode("ou=People," + TEST_ROOT_DN_STRING);
-    
+
     /*
      * Open a session to the replicationServer using the broker API.
      * This must use a different serverId to that of the directory server.
@@ -1731,7 +1731,7 @@
         user3UUID,
         baseUUID,
         user3Entry.getObjectClassAttribute(),
-        user3Entry.getAttributes(), 
+        user3Entry.getAttributes(),
         new ArrayList<Attribute>());
     broker.publish(addMsg);
 
@@ -1745,8 +1745,8 @@
     List<Modification> mods = generatemods("telephonenumber", "01 02 45");
     ModifyOperationBasis modOp = new ModifyOperationBasis(
         connection,
-        InternalClientConnection.nextOperationID(), 
-        InternalClientConnection.nextMessageID(), 
+        InternalClientConnection.nextOperationID(),
+        InternalClientConnection.nextMessageID(),
         null,
         user3Entry.getDN(),
         mods);
@@ -1758,10 +1758,10 @@
     assertTrue(msg instanceof ModifyMsg,
       "The received replication message is not a MODIFY msg");
     ModifyMsg modMsg = (ModifyMsg) msg;
-    assertEquals(addMsg.getChangeNumber().getTimeSec(), 
+    assertEquals(addMsg.getChangeNumber().getTimeSec(),
                  modMsg.getChangeNumber().getTimeSec(),
                 "The MOD timestamp should have been adjusted to the ADD one");
-    
+
     // Delete the entries to clean the database.
     DeleteMsg delMsg =
       new DeleteMsg(
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/AssuredReplicationPluginTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/AssuredReplicationPluginTest.java
new file mode 100644
index 0000000..d643ab0
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/AssuredReplicationPluginTest.java
@@ -0,0 +1,1535 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2006-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.plugin;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+import org.opends.server.types.ResultCode;
+import org.opends.messages.Category;
+import org.opends.messages.Message;
+import org.opends.messages.Severity;
+import org.opends.server.TestCaseUtils;
+import org.opends.server.core.AddOperationBasis;
+import org.opends.server.core.DeleteOperationBasis;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.common.AssuredMode;
+import org.opends.server.replication.common.DSInfo;
+import org.opends.server.replication.common.RSInfo;
+import org.opends.server.replication.common.ServerState;
+import org.opends.server.replication.common.ServerStatus;
+import org.opends.server.replication.protocol.ProtocolSession;
+import org.opends.server.replication.protocol.ProtocolVersion;
+import org.opends.server.replication.protocol.ReplServerStartMsg;
+import org.opends.server.replication.protocol.ReplSessionSecurity;
+import org.opends.server.replication.protocol.ServerStartMsg;
+import org.opends.server.replication.protocol.StartSessionMsg;
+import org.opends.server.replication.protocol.TopologyMsg;
+import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.protocols.ldap.LDAPFilter;
+import org.opends.server.replication.protocol.AckMsg;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteStringFactory;
+import org.testng.annotations.BeforeClass;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchScope;
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.opends.server.TestCaseUtils.*;
+import static org.testng.Assert.fail;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+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;
+
+/**
+ * Test the client part (plugin) of the assured feature in both safe data and
+ * safe read modes. We use a fake RS and control the behaviour of the client
+ * DS (timeout, wait for acks, error handling...)
+ * Also check for monitoring values for assured replication
+ */
+public class AssuredReplicationPluginTest
+  extends ReplicationTestCase
+{
+
+  /**
+   * The port of the replicationServer.
+   */
+  private int replServerPort;
+  private byte RS_SERVER_ID = (byte) 90;
+  // Sleep time of the RS, before sending an ack
+  private static final long NO_TIMEOUT_RS_SLEEP_TIME = 2000;
+  private String testName = this.getClass().getSimpleName();
+
+  // Create to distincts base dn, one for safe data replication, the other one
+  // for safe read replication
+  private String SAFE_DATA_DN = "ou=safe-data," + TEST_ROOT_DN_STRING;
+  private String SAFE_READ_DN = "ou=safe-read," + TEST_ROOT_DN_STRING;
+  private String NOT_ASSURED_DN = "ou=not-assured," + TEST_ROOT_DN_STRING;
+  private Entry safeDataDomainCfgEntry = null;
+  private Entry safeReadDomainCfgEntry = null;
+  private Entry notAssuredDomainCfgEntry = null;
+
+  // The fake replication server
+  private FakeReplicationServer replicationServer = null;
+
+  // Definitions for the scenario the RS supports
+  private static final int NOT_ASSURED_SCENARIO = 1;
+  private static final int TIMEOUT_SCENARIO = 2;
+  private static final int NO_TIMEOUT_SCENARIO = 3;
+  private static final int SAFE_READ_MANY_ERRORS = 4;
+  private static final int SAFE_DATA_MANY_ERRORS = 5;
+
+  // The tracer object for the debug logger
+  private static final DebugTracer TRACER = getTracer();
+
+  private void debugInfo(String s)
+  {
+    logError(Message.raw(Category.SYNC, Severity.NOTICE, s));
+    if (debugEnabled())
+    {
+      TRACER.debugInfo("** TEST **" + s);
+    }
+  }
+
+  /**
+   * Before starting the tests configure some stuff
+   */
+  @BeforeClass
+  @Override
+  public void setUp() throws Exception
+  {
+    super.setUp();
+
+    // Find  a free port for the replicationServer
+    ServerSocket socket = TestCaseUtils.bindFreePort();
+    replServerPort = socket.getLocalPort();
+    socket.close();
+
+    // Create base dns for each tested modes
+    String topEntry = "dn: " + SAFE_DATA_DN + "\n" + "objectClass: top\n" +
+      "objectClass: organizationalUnit\n";
+    addEntry(TestCaseUtils.entryFromLdifString(topEntry));
+    topEntry = "dn: " + SAFE_READ_DN + "\n" + "objectClass: top\n" +
+      "objectClass: organizationalUnit\n";
+    addEntry(TestCaseUtils.entryFromLdifString(topEntry));
+    topEntry = "dn: " + NOT_ASSURED_DN + "\n" + "objectClass: top\n" +
+      "objectClass: organizationalUnit\n";
+    addEntry(TestCaseUtils.entryFromLdifString(topEntry));
+  }
+
+  /**
+   * Add an entry in the database
+   */
+  private void addEntry(Entry entry) throws Exception
+  {
+    AddOperationBasis addOp = new AddOperationBasis(connection,
+      InternalClientConnection.nextOperationID(), InternalClientConnection.
+      nextMessageID(), null, entry.getDN(), entry.getObjectClasses(),
+      entry.getUserAttributes(), entry.getOperationalAttributes());
+    addOp.setInternalOperation(true);
+    addOp.run();
+    assertNotNull(getEntry(entry.getDN(), 1000, true));
+  }
+
+  /**
+   * Creates a domain using the passed assured settings.
+   * Returns the matching config entry added to the config backend.
+   */
+  private Entry createAssuredDomain(AssuredMode assuredMode, int safeDataLevel,
+    long assuredTimeout) throws Exception
+  {
+    String baseDn = null;
+    switch (assuredMode)
+    {
+      case SAFE_READ_MODE:
+        baseDn = SAFE_READ_DN;
+        break;
+      case SAFE_DATA_MODE:
+        baseDn = SAFE_DATA_DN;
+        break;
+      default:
+        fail("Unexpected assured level.");
+    }
+    // Create an assured config entry ldif, matching passed assured settings
+    String prefixLdif = "dn: cn=" + testName + ", cn=domains, " +
+      SYNCHRO_PLUGIN_DN + "\n" + "objectClass: top\n" +
+      "objectClass: ds-cfg-replication-domain\n" + "cn: " + testName + "\n" +
+      "ds-cfg-base-dn: " + baseDn + "\n" +
+      "ds-cfg-replication-server: localhost:" + replServerPort + "\n" +
+      "ds-cfg-server-id: 1\n" + "ds-cfg-receive-status: true\n" +
+      // heartbeat = 10 min so no need to emulate heartbeat in fake RS: session
+      // not closed by client
+      "ds-cfg-heartbeat-interval: 600000ms\n";
+
+    String configEntryLdif = null;
+    switch (assuredMode)
+    {
+      case SAFE_READ_MODE:
+        configEntryLdif = prefixLdif + "ds-cfg-assured-type: safe-read\n" +
+          "ds-cfg-assured-timeout: " + assuredTimeout + "ms\n";
+        break;
+      case SAFE_DATA_MODE:
+        configEntryLdif = prefixLdif + "ds-cfg-assured-type: safe-data\n" +
+          "ds-cfg-assured-sd-level: " + safeDataLevel + "\n" +
+          "ds-cfg-assured-timeout: " + assuredTimeout + "ms\n";
+        break;
+      default:
+        fail("Unexpected assured level.");
+    }
+    Entry domainCfgEntry = TestCaseUtils.entryFromLdifString(configEntryLdif);
+
+    // Add the config entry to create the replicated domain
+    DirectoryServer.getConfigHandler().addEntry(domainCfgEntry, null);
+    assertNotNull(DirectoryServer.getConfigEntry(domainCfgEntry.getDN()),
+      "Unable to add the domain config entry: " + configEntryLdif);
+
+    return domainCfgEntry;
+  }
+
+  /**
+   * Creates a domain without assured mode
+   * Returns the matching config entry added to the config backend.
+   */
+  private Entry createNotAssuredDomain() throws Exception
+  {
+
+    // Create a not assured config entry ldif
+    String configEntryLdif = "dn: cn=" + testName + ", cn=domains, " +
+      SYNCHRO_PLUGIN_DN + "\n" + "objectClass: top\n" +
+      "objectClass: ds-cfg-replication-domain\n" + "cn: " + testName + "\n" +
+      "ds-cfg-base-dn: " + NOT_ASSURED_DN + "\n" +
+      "ds-cfg-replication-server: localhost:" + replServerPort + "\n" +
+      "ds-cfg-server-id: 1\n" + "ds-cfg-receive-status: true\n" +
+      // heartbeat = 10 min so no need to emulate heartbeat in fake RS: session
+      // not closed by client
+      "ds-cfg-heartbeat-interval: 600000ms\n";
+
+    Entry domainCfgEntry = TestCaseUtils.entryFromLdifString(configEntryLdif);
+
+    // Add the config entry to create the replicated domain
+    DirectoryServer.getConfigHandler().addEntry(domainCfgEntry, null);
+    assertNotNull(DirectoryServer.getConfigEntry(domainCfgEntry.getDN()),
+      "Unable to add the domain config entry: " + configEntryLdif);
+
+    return domainCfgEntry;
+  }
+
+  /**
+   * Removes a domain deleting the passed config entry
+   */
+  private void removeDomain(Entry domainCfgEntry)
+  {
+    DeleteOperationBasis op;
+    // Delete entries
+    try
+    {
+      DN dn = domainCfgEntry.getDN();
+
+      logError(Message.raw(Category.SYNC, Severity.NOTICE,
+        "cleaning config entry " + dn));
+
+      op = new DeleteOperationBasis(connection, InternalClientConnection.
+        nextOperationID(), InternalClientConnection.nextMessageID(), null,
+        dn);
+      op.run();
+      if ((op.getResultCode() != ResultCode.SUCCESS) &&
+        (op.getResultCode() != ResultCode.NO_SUCH_OBJECT))
+      {
+        fail("Deleting config entry" + dn +
+          " failed: " + op.getResultCode().getResultCodeName());
+      }
+    } catch (NoSuchElementException e)
+    {
+      // done
+    }
+  }
+
+  /**
+   * The fake replication server used to emulate RS behaviour the way we want
+   * for assured features test
+   */
+  private class FakeReplicationServer extends Thread
+  {
+
+    private ServerSocket listenSocket;
+    private boolean shutdown = false;
+    private ProtocolSession session = null;
+
+    // Parameters given at constructor time
+    private int port;
+    private short serverId = -1;
+    boolean isAssured = false; // Default value for config
+    AssuredMode assuredMode = AssuredMode.SAFE_DATA_MODE; // Default value for config
+    byte safeDataLevel = (byte) 1; // Default value for config
+
+    // Predefined config parameters
+    private int degradedStatusThreshold = 5000;
+
+    // Parameters set with received server start message
+    private String baseDn = null;
+    private long generationId = -1L;
+    private ServerState serverState = null;
+    private int windowSize = -1;
+    private byte groupId = (byte) -1;
+    private boolean sslEncryption = false;
+    // The scenario this RS is expecting
+    private int scenario = -1;
+
+    // parameters at handshake are ok
+    private boolean handshakeOk = false;
+    // signal that the current scenario the RS must execute reached the point
+    // where the main code can perform test assertion
+    private boolean scenarioExecuted = false;
+
+    // Constructor for RS receiving updates in SR assured mode or not assured
+    // The assured boolean means:
+    // - true: SR mode
+    // - false: not assured
+    public FakeReplicationServer(int port, short serverId, boolean assured)
+    {
+
+      this.port = port;
+      this.serverId = serverId;
+
+      if (assured)
+      {
+        this.isAssured = true;
+        this.assuredMode = AssuredMode.SAFE_READ_MODE;
+      }
+    }
+
+    // Constructor for RS receiving updates in SD assured mode
+    public FakeReplicationServer(int port, short serverId, int safeDataLevel)
+    {
+
+      this.port = port;
+      this.serverId = serverId;
+
+      this.isAssured = true;
+      this.assuredMode = AssuredMode.SAFE_DATA_MODE;
+      this.safeDataLevel = (byte) safeDataLevel;
+    }
+
+    /**
+     * Starts the fake RS, expecting and testing the passed scenario.
+     */
+    public void start(int scenario)
+    {
+
+      // Store expected test case
+      this.scenario = scenario;
+
+      // Start listening
+      start();
+    }
+
+    /**
+     * Wait for DS connections
+     */
+    @Override
+    public void run()
+    {
+
+      // Create server socket
+      try
+      {
+        listenSocket = new ServerSocket();
+        listenSocket.bind(new InetSocketAddress(port));
+      } catch (IOException e)
+      {
+        fail("Fake replication server could not bind to port:" + port);
+      }
+
+      Socket newSocket = null;
+      // Loop waiting for DS connections
+      while (!shutdown)
+      {
+        try
+        {
+          newSocket = listenSocket.accept();
+          newSocket.setTcpNoDelay(true);
+          newSocket.setKeepAlive(true);
+          // Create client session
+          ReplSessionSecurity replSessionSecurity = new ReplSessionSecurity();
+          session = replSessionSecurity.createServerSession(newSocket,
+            ReplSessionSecurity.HANDSHAKE_TIMEOUT);
+          if (session == null) // Error, go back to accept
+          {
+            continue;
+          }
+          // Ok, now call connection handling code + special code for the
+          // configured test
+          handleClientConnection();
+        } catch (Exception e)
+        {
+          // The socket has probably been closed as part of the
+          // shutdown
+        }
+      }
+    }
+
+    /**
+     * Shutdown the Replication Server service and all its connections.
+     */
+    public void shutdown()
+    {
+      if (shutdown)
+      {
+        return;
+      }
+
+      shutdown = true;
+
+      // Shutdown the listener thread
+      try
+      {
+        if (listenSocket != null)
+        {
+          listenSocket.close();
+        }
+      } catch (IOException e)
+      {
+        // replication Server service is closing anyway.
+      }
+
+      /*
+       * Shutdown any current client handling code
+       */
+      try
+      {
+        if (session != null)
+        {
+          session.close();
+        }
+      } catch (IOException e)
+      {
+        // ignore.
+      }
+
+      try
+      {
+        join();
+      } catch (InterruptedException ie)
+      {
+      }
+    }
+
+    /**
+     * Handle the handshake processing with the connecting DS
+     * returns true if handshake was performed without errors
+     */
+    private boolean performHandshake()
+    {
+
+      try
+      {
+        // Receive server start
+        ServerStartMsg serverStartMsg = (ServerStartMsg) session.receive();
+
+        baseDn = serverStartMsg.getBaseDn();
+        serverState = serverStartMsg.getServerState();
+        generationId = serverStartMsg.getGenerationId();
+        windowSize = serverStartMsg.getWindowSize();
+        groupId = serverStartMsg.getGroupId();
+        sslEncryption = serverStartMsg.getSSLEncryption();
+
+        // Send replication server start
+        String serverURL = ("localhost:" + port);
+        ReplServerStartMsg replServerStartMsg = new ReplServerStartMsg(serverId,
+          serverURL, baseDn, windowSize, serverState,
+          ProtocolVersion.getCurrentVersion(), generationId, sslEncryption,
+          groupId, degradedStatusThreshold);
+        session.publish(replServerStartMsg);
+
+        if (!sslEncryption)
+        {
+          session.stopEncryption();
+        }
+
+        // Read start session
+        StartSessionMsg startSessionMsg = (StartSessionMsg) session.receive();
+
+        // Sanity checking for assured parameters
+        boolean receivedIsAssured = startSessionMsg.isAssured();
+        assertEquals(receivedIsAssured, isAssured);
+        AssuredMode receivedAssuredMode = startSessionMsg.getAssuredMode();
+        assertEquals(receivedAssuredMode, assuredMode);
+        byte receivedSafeDataLevel = startSessionMsg.getSafeDataLevel();
+        assertEquals(receivedSafeDataLevel, safeDataLevel);
+        ServerStatus receivedServerStatus = startSessionMsg.getStatus();
+        assertEquals(receivedServerStatus, ServerStatus.NORMAL_STATUS);
+        List<String> receivedReferralsURLs = startSessionMsg.getReferralsURLs();
+        assertEquals(receivedReferralsURLs.size(), 0);
+
+        debugInfo("Received start session assured parameters are ok.");
+
+        // Send topo view
+        List<RSInfo> rsList = new ArrayList<RSInfo>();
+        RSInfo rsInfo = new RSInfo(serverId, generationId, groupId);
+        rsList.add(rsInfo);
+        TopologyMsg topologyMsg = new TopologyMsg(new ArrayList<DSInfo>(),
+          rsList);
+        session.publish(topologyMsg);
+
+      } catch (IOException e)
+      {
+        // Probably un-connection of DS looking for best server
+        return false;
+      } catch (Exception e)
+      {
+        fail("Unexpected exception in fake replication server handshake " +
+          "processing: " + e);
+        return false;
+      }
+      return true;
+    }
+
+    /**
+     * Tells if the received handshake parameters regarding assured config were
+     * ok and handshake phase is terminated.
+     */
+    public boolean isHandshakeOk()
+    {
+
+      return handshakeOk;
+    }
+
+    /**
+     * Tells the main code that the fake RS executed enough of the expected
+     * scenario and can perform test assertion
+     */
+    public boolean isScenarioExecuted()
+    {
+
+      return scenarioExecuted;
+    }
+
+    /**
+     * Handle client connection then call code specific to configured test
+     */
+    private void handleClientConnection()
+    {
+
+      // Handle DS connexion
+      if (!performHandshake())
+      {
+        try
+        {
+          session.close();
+        } catch (IOException e)
+        {
+          // nothing to do
+        }
+        return;
+      }
+      // If we come here, assured parameters sent by DS are as expected and
+      // handshake phase is terminated
+      handshakeOk = true;
+
+      // Now execute the requested scenario
+      switch (scenario)
+      {
+        case NOT_ASSURED_SCENARIO:
+          executeNotAssuredScenario();
+          break;
+        case TIMEOUT_SCENARIO:
+          executeTimeoutScenario();
+          break;
+        case NO_TIMEOUT_SCENARIO:
+          executeNoTimeoutScenario();
+          break;
+        case SAFE_READ_MANY_ERRORS:
+          executeSafeReadManyErrorsScenario();
+          break;
+        case SAFE_DATA_MANY_ERRORS:
+          executeSafeDataManyErrorsScenario();
+          break;
+        default:
+          fail("Unknown scenario: " + scenario);
+      }
+    }
+
+    /**
+     * Read the coming update and check parameters are not assured
+     */
+    private void executeNotAssuredScenario()
+    {
+
+      try
+      {
+        UpdateMsg updateMsg = (UpdateMsg) session.receive();
+        checkUpdateAssuredParameters(updateMsg);
+
+        scenarioExecuted = true;
+      } catch (Exception e)
+      {
+        fail("Unexpected exception in fake replication server executeNotAssuredScenario " +
+          "processing: " + e);
+      }
+    }
+
+    /**
+     * Read the coming update and make the client time out by not sending back
+     * the ack
+     */
+    private void executeTimeoutScenario()
+    {
+
+      try
+      {
+        UpdateMsg updateMsg = (UpdateMsg) session.receive();
+        checkUpdateAssuredParameters(updateMsg);
+
+        scenarioExecuted = true;
+
+        // We do not send back an ack and the client code is expected to be
+        // blocked at least for the programmed timeout time.
+
+      } catch (Exception e)
+      {
+        fail("Unexpected exception in fake replication server executeTimeoutScenario " +
+          "processing: " + e);
+      }
+    }
+
+    /**
+     * Read the coming update, sleep some time then send back an ack
+     */
+    private void executeNoTimeoutScenario()
+    {
+
+      try
+      {
+        UpdateMsg updateMsg = (UpdateMsg) session.receive();
+        checkUpdateAssuredParameters(updateMsg);
+
+        // Sleep before sending back the ack
+        sleep(NO_TIMEOUT_RS_SLEEP_TIME);
+
+        // Send the ack without errors
+        AckMsg ackMsg = new AckMsg(updateMsg.getChangeNumber());
+        session.publish(ackMsg);
+
+        scenarioExecuted = true;
+
+      } catch (Exception e)
+      {
+        fail("Unexpected exception in fake replication server executeNoTimeoutScenario " +
+          "processing: " + e);
+      }
+    }
+
+    /**
+     * Check that received update assured parameters are as defined at RS start
+     */
+    private void checkUpdateAssuredParameters(UpdateMsg updateMsg)
+    {
+      assertEquals(updateMsg.isAssured(), isAssured);
+      assertEquals(updateMsg.getAssuredMode(), assuredMode);
+      assertEquals(updateMsg.getSafeDataLevel(), safeDataLevel);
+      debugInfo("Received update assured parameters are ok.");
+    }
+
+    /**
+     * Read the coming seaf read mode updates and send back acks with errors
+     */
+    private void executeSafeReadManyErrorsScenario()
+    {
+
+      try
+      {
+        // Read first update
+        UpdateMsg updateMsg = (UpdateMsg) session.receive();
+        checkUpdateAssuredParameters(updateMsg);
+
+        // Sleep before sending back the ack
+        sleep(NO_TIMEOUT_RS_SLEEP_TIME);
+
+        // Send an ack with errors:
+        // - replay error
+        // - server 10 error, server 20 error
+        List<Short> serversInError = new ArrayList<Short>();
+        serversInError.add((short)10);
+        serversInError.add((short)20);
+        AckMsg ackMsg = new AckMsg(updateMsg.getChangeNumber(), false, false, true, serversInError);
+        session.publish(ackMsg);
+
+        // Read second update
+        updateMsg = (UpdateMsg) session.receive();
+        checkUpdateAssuredParameters(updateMsg);
+
+        // Sleep before sending back the ack
+        sleep(NO_TIMEOUT_RS_SLEEP_TIME);
+
+        // Send an ack with errors:
+        // - timeout error
+        // - wrong status error
+        // - replay error
+        // - server 10 error, server 20 error, server 30 error
+        serversInError = new ArrayList<Short>();
+        serversInError.add((short)10);
+        serversInError.add((short)20);
+        serversInError.add((short)30);
+        ackMsg = new AckMsg(updateMsg.getChangeNumber(), true, true, true, serversInError);
+        session.publish(ackMsg);
+
+        // Read third update
+        updateMsg = (UpdateMsg) session.receive();
+        checkUpdateAssuredParameters(updateMsg);
+
+        // let timeout occur
+        
+        scenarioExecuted = true;
+
+      } catch (Exception e)
+      {
+        fail("Unexpected exception in fake replication server executeSafeReadManyErrorsScenario " +
+          "processing: " + e);
+      }
+    }
+
+    /**
+     * Read the coming seaf data mode updates and send back acks with errors
+     */
+    private void executeSafeDataManyErrorsScenario()
+    {
+
+      try
+      {
+        // Read first update
+        UpdateMsg updateMsg = (UpdateMsg) session.receive();
+        checkUpdateAssuredParameters(updateMsg);
+
+        // Sleep before sending back the ack
+        sleep(NO_TIMEOUT_RS_SLEEP_TIME);
+
+        // Send an ack with errors:
+        // - timeout error
+        // - server 10 error
+        List<Short> serversInError = new ArrayList<Short>();
+        serversInError.add((short)10);
+        AckMsg ackMsg = new AckMsg(updateMsg.getChangeNumber(), true, false, false, serversInError);
+        session.publish(ackMsg);
+
+        // Read second update
+        updateMsg = (UpdateMsg) session.receive();
+        checkUpdateAssuredParameters(updateMsg);
+
+        // Sleep before sending back the ack
+        sleep(NO_TIMEOUT_RS_SLEEP_TIME);
+
+        // Send an ack with errors:
+        // - timeout error
+        // - server 10 error, server 20 error
+        serversInError = new ArrayList<Short>();
+        serversInError.add((short)10);
+        serversInError.add((short)20);
+        ackMsg = new AckMsg(updateMsg.getChangeNumber(), true, false, false, serversInError);
+        session.publish(ackMsg);
+
+        // Read third update
+        updateMsg = (UpdateMsg) session.receive();
+        checkUpdateAssuredParameters(updateMsg);
+
+        // let timeout occur
+
+        scenarioExecuted = true;
+
+      } catch (Exception e)
+      {
+        fail("Unexpected exception in fake replication server executeSafeDataManyErrorsScenario " +
+          "processing: " + e);
+      }
+    }
+  }
+
+  /**
+   * Sleep a while
+   */
+  private void sleep(long time)
+  {
+    try
+    {
+      Thread.sleep(time);
+    } catch (InterruptedException ex)
+    {
+      fail("Error sleeping " + ex);
+    }
+  }
+
+  /**
+   * Tests that a DS performing a modification in safe data mode waits for
+   * the ack of the RS for the configured timeout time, then times out.
+   */
+  @Test
+  public void testSafeDataModeTimeout() throws Exception
+  {
+
+    int TIMEOUT = 5000;
+    String testcase = "testSafeDataModeTimeout";
+    try
+    {
+      // Create and start a RS expecting clients in safe data assured mode with
+      // safe data level 2
+      replicationServer = new FakeReplicationServer(replServerPort, RS_SERVER_ID,
+        1);
+      replicationServer.start(TIMEOUT_SCENARIO);
+
+      // Create a safe data assured domain
+      safeDataDomainCfgEntry = createAssuredDomain(AssuredMode.SAFE_DATA_MODE, 1,
+        TIMEOUT);
+      // Wait for connection of domain to RS
+      waitForConnectionToRs(testcase, replicationServer);
+
+      // Make an LDAP update (add an entry)
+      long startTime = System.currentTimeMillis(); // Time the update has been initiated
+      String entry = "dn: ou=assured-sd-timeout-entry," + SAFE_DATA_DN + "\n" +
+        "objectClass: top\n" +
+        "objectClass: organizationalUnit\n";
+      addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+      // In this scenario, the fake RS will not send back an ack so we expect
+      // the add entry code (LDAP client code emulation) to be blocked for the
+      // timeout value at least. If the time we have slept is lower, timeout
+      // handling code is not working...
+      long endTime = System.currentTimeMillis();
+      assertTrue((endTime - startTime) >= TIMEOUT);
+      assertTrue(replicationServer.isScenarioExecuted());
+
+      // Check monitoring values
+      DN baseDn = DN.decode(SAFE_DATA_DN);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 0);
+      Map<Short, Integer> errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+      assertTrue(errorsByServer.isEmpty());
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 1);
+      errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+      //  errors by server list for sd mode should be [[rsId:1]]
+      assertEquals(errorsByServer.size(), 1);
+      Integer nError = errorsByServer.get((short)RS_SERVER_ID);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 1);
+
+    } finally
+    {
+      endTest();
+    }
+  }
+
+  /**
+   * Tests that a DS performing a modification in safe read mode waits for
+   * the ack of the RS for the configured timeout time, then times out.
+   */
+  @Test
+  public void testSafeReadModeTimeout() throws Exception
+  {
+
+    int TIMEOUT = 5000;
+    String testcase = "testSafeReadModeTimeout";
+    try
+    {
+      // Create and start a RS expecting clients in safe read assured mode
+      replicationServer = new FakeReplicationServer(replServerPort, RS_SERVER_ID,
+        true);
+      replicationServer.start(TIMEOUT_SCENARIO);
+
+      // Create a safe read assured domain
+      safeReadDomainCfgEntry = createAssuredDomain(AssuredMode.SAFE_READ_MODE, 0,
+        TIMEOUT);
+      // Wait for connection of domain to RS
+      waitForConnectionToRs(testcase, replicationServer);
+      
+      // Make an LDAP update (add an entry)
+      long startTime = System.currentTimeMillis(); // Time the update has been initiated
+      String entry = "dn: ou=assured-sr-timeout-entry," + SAFE_READ_DN + "\n" +
+        "objectClass: top\n" +
+        "objectClass: organizationalUnit\n";
+      addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+      // In this scenario, the fake RS will not send back an ack so we expect
+      // the add entry code (LDAP client code emulation) to be blocked for the
+      // timeout value at least. If the time we have slept is lower, timeout
+      // handling code is not working...
+      long endTime = System.currentTimeMillis();
+      assertTrue((endTime - startTime) >= TIMEOUT);
+      assertTrue(replicationServer.isScenarioExecuted());
+
+      // Check monitoring values
+      DN baseDn = DN.decode(SAFE_READ_DN);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 0);
+      Map<Short, Integer> errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+      //  errors by server list for sr mode should be [[rsId:1]]
+      assertEquals(errorsByServer.size(), 1);
+      Integer nError = errorsByServer.get((short)RS_SERVER_ID);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 0);
+      errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+      assertTrue(errorsByServer.isEmpty());
+
+    } finally
+    {
+      endTest();
+    }
+  }
+
+  /**
+   * Tests parameters sent in session handshake an updates, when not using
+   * assured replication
+   */
+  @Test
+  public void testNotAssuredSession() throws Exception
+  {
+
+    String testcase = "testNotAssuredSession";
+    try
+    {
+      // Create and start a RS expecting not assured clients
+      replicationServer = new FakeReplicationServer(replServerPort, RS_SERVER_ID,
+        false);
+      replicationServer.start(NOT_ASSURED_SCENARIO);
+
+      // Create a not assured domain
+      notAssuredDomainCfgEntry = createNotAssuredDomain();
+      // Wait for connection of domain to RS
+      waitForConnectionToRs(testcase, replicationServer);
+
+      // Make an LDAP update (add an entry)
+      String entry = "dn: ou=not-assured-entry," + NOT_ASSURED_DN + "\n" +
+        "objectClass: top\n" +
+        "objectClass: organizationalUnit\n";
+      addEntry(TestCaseUtils.entryFromLdifString(entry));
+      
+      // Wait for entry received by RS
+      waitForScenarioExecutedOnRs(testcase, replicationServer);
+
+      // No more test to do here
+    } finally
+    {
+      endTest();
+    }
+  }
+
+  /**
+   * Wait for connection to the fake replication server or times out with error
+   * after some seconds
+   */
+  private void waitForConnectionToRs(String testCase, FakeReplicationServer rs)
+  {
+    int nsec = -1;
+    do
+    {
+      nsec++;
+      if (nsec == 10) // 10 seconds timeout
+        fail(testCase + ": timeout waiting for domain connection to fake RS after " + nsec + " seconds.");
+      sleep(1000);
+    } while (!rs.isHandshakeOk());
+  }
+
+  /**
+   * Wait for the scenario to be executed by the fake replication server or
+   * times out with error after some seconds
+   */
+  private void waitForScenarioExecutedOnRs(String testCase, FakeReplicationServer rs)
+  {
+    int nsec = -1;
+    do
+    {
+      nsec++;
+      if (nsec == 10) // 10 seconds timeout
+        fail(testCase + ": timeout waiting for scenario to be exectued on fake RS after " + nsec + " seconds.");
+      sleep(1000);
+    } while (!rs.isScenarioExecuted());
+  }
+
+  private void endTest()
+  {
+    if (replicationServer != null)
+    {
+      replicationServer.shutdown();
+    }
+
+    if (safeDataDomainCfgEntry != null)
+    {
+      removeDomain(safeDataDomainCfgEntry);
+    }
+
+    if (safeReadDomainCfgEntry != null)
+    {
+      removeDomain(safeReadDomainCfgEntry);
+    }
+
+    if (notAssuredDomainCfgEntry != null)
+    {
+      removeDomain(notAssuredDomainCfgEntry);
+    }
+  }
+
+  /**
+   * Tests that a DS performing a modification in safe data mode receives the RS
+   * ack and does not return before returning it.
+   */
+  @Test
+  public void testSafeDataModeAck() throws Exception
+  {
+
+    int TIMEOUT = 5000;
+    String testcase = "testSafeDataModeAck";
+    try
+    {
+      // Create and start a RS expecting clients in safe data assured mode with
+      // safe data level 2
+      replicationServer = new FakeReplicationServer(replServerPort, RS_SERVER_ID,
+        2);
+      replicationServer.start(NO_TIMEOUT_SCENARIO);
+
+      // Create a safe data assured domain
+      safeDataDomainCfgEntry = createAssuredDomain(AssuredMode.SAFE_DATA_MODE, 2,
+        TIMEOUT);
+      // Wait for connection of domain to RS
+      waitForConnectionToRs(testcase, replicationServer);
+
+      // Make an LDAP update (add an entry)
+      long startTime = System.currentTimeMillis(); // Time the update has been initiated
+      String entry = "dn: ou=assured-sd-no-timeout-entry," + SAFE_DATA_DN + "\n" +
+        "objectClass: top\n" +
+        "objectClass: organizationalUnit\n";
+      addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+      // In this scenario, the fake RS will send back an ack after NO_TIMEOUT_RS_SLEEP_TIME
+      // seconds, so we expect the add entry code (LDAP client code emulation) to be blocked
+      // for more than NO_TIMEOUT_RS_SLEEP_TIME seconds but no more than the timeout value.
+      long endTime = System.currentTimeMillis();
+      long callTime = endTime - startTime;
+      assertTrue( (callTime >= NO_TIMEOUT_RS_SLEEP_TIME) && (callTime <= TIMEOUT));
+      assertTrue(replicationServer.isScenarioExecuted());
+
+      // Check monitoring values
+      DN baseDn = DN.decode(SAFE_DATA_DN);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 0);
+      Map<Short, Integer> errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+      assertTrue(errorsByServer.isEmpty());
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 0);
+      errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+      assertTrue(errorsByServer.isEmpty());
+      
+    } finally
+    {
+      endTest();
+    }
+  }
+
+  /**
+   * Tests that a DS performing a modification in safe read mode receives the RS
+   * ack and does not return before returning it.
+   */
+  @Test
+  public void testSafeReadModeAck() throws Exception
+  {
+
+    int TIMEOUT = 5000;
+    String testcase = "testSafeReadModeAck";
+    try
+    {
+      // Create and start a RS expecting clients in safe read assured mode
+      replicationServer = new FakeReplicationServer(replServerPort, RS_SERVER_ID,
+        true);
+      replicationServer.start(NO_TIMEOUT_SCENARIO);
+
+      // Create a safe read assured domain
+      safeReadDomainCfgEntry = createAssuredDomain(AssuredMode.SAFE_READ_MODE, 0,
+        TIMEOUT);
+      // Wait for connection of domain to RS
+      waitForConnectionToRs(testcase, replicationServer);
+
+      // Make an LDAP update (add an entry)
+      long startTime = System.currentTimeMillis(); // Time the update has been initiated
+      String entry = "dn: ou=assured-sr-no-timeout-entry," + SAFE_READ_DN + "\n" +
+        "objectClass: top\n" +
+        "objectClass: organizationalUnit\n";
+      addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+      // In this scenario, the fake RS will send back an ack after NO_TIMEOUT_RS_SLEEP_TIME
+      // seconds, so we expect the add entry code (LDAP client code emulation) to be blocked
+      // for more than NO_TIMEOUT_RS_SLEEP_TIME seconds but no more than the timeout value.
+      long endTime = System.currentTimeMillis();
+      long callTime = endTime - startTime;
+      assertTrue( (callTime >= NO_TIMEOUT_RS_SLEEP_TIME) && (callTime <= TIMEOUT));
+      assertTrue(replicationServer.isScenarioExecuted());
+
+      // Check monitoring values
+      DN baseDn = DN.decode(SAFE_READ_DN);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 0);
+      Map<Short, Integer> errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+      assertTrue(errorsByServer.isEmpty());
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 0);
+      errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+      assertTrue(errorsByServer.isEmpty());
+
+    } finally
+    {
+      endTest();
+    }
+  }
+
+  /**
+   * DS performs many successive modifications in safe data mode and receives RS
+   * acks with various errors. Check for monitoring right errors
+   */
+  @Test
+  public void testSafeDataManyErrors() throws Exception
+  {
+
+    int TIMEOUT = 5000;
+    String testcase = "testSafeDataManyErrors";
+    try
+    {
+      // Create and start a RS expecting clients in safe data assured mode with
+      // safe data level 3
+      replicationServer = new FakeReplicationServer(replServerPort, RS_SERVER_ID,
+        3);
+      replicationServer.start(SAFE_DATA_MANY_ERRORS);
+
+      // Create a safe data assured domain
+      safeDataDomainCfgEntry = createAssuredDomain(AssuredMode.SAFE_DATA_MODE, 3,
+        TIMEOUT);
+      // Wait for connection of domain to RS
+      waitForConnectionToRs(testcase, replicationServer);
+
+      // Make a first LDAP update (add an entry)
+      long startTime = System.currentTimeMillis(); // Time the update has been initiated
+      String entryDn = "ou=assured-sd-many-errors-entry," + SAFE_DATA_DN;
+      String entry = "dn: " + entryDn + "\n" +
+        "objectClass: top\n" +
+        "objectClass: organizationalUnit\n";
+      addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+      // In this scenario, the fake RS will send back an ack after NO_TIMEOUT_RS_SLEEP_TIME
+      // seconds, so we expect the add entry code (LDAP client code emulation) to be blocked
+      // for more than NO_TIMEOUT_RS_SLEEP_TIME seconds but no more than the timeout value.
+      long endTime = System.currentTimeMillis();
+      long callTime = endTime - startTime;
+      assertTrue( (callTime >= NO_TIMEOUT_RS_SLEEP_TIME) && (callTime <= TIMEOUT));
+
+      // Check monitoring values
+      // The expected ack for the first update is:
+      // - timeout error
+      // - server 10 error
+      DN baseDn = DN.decode(SAFE_DATA_DN);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 0);
+      Map<Short, Integer> errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+      assertTrue(errorsByServer.isEmpty());
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 1);
+      errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+      //  errors by server list for sd mode should be [[10:1]]
+      assertEquals(errorsByServer.size(), 1);
+      Integer nError = errorsByServer.get((short)10);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 1);
+
+      // Make a second LDAP update (delete the entry)
+      startTime = System.currentTimeMillis(); // Time the update has been initiated
+      deleteEntry(entryDn);
+
+      // In this scenario, the fake RS will send back an ack after NO_TIMEOUT_RS_SLEEP_TIME
+      // seconds, so we expect the delete entry code (LDAP client code emulation) to be blocked
+      // for more than NO_TIMEOUT_RS_SLEEP_TIME seconds but no more than the timeout value.
+      endTime = System.currentTimeMillis();
+      callTime = endTime - startTime;
+      assertTrue( (callTime >= NO_TIMEOUT_RS_SLEEP_TIME) && (callTime <= TIMEOUT));
+
+      // Check monitoring values
+      // The expected ack for the second update is:
+      // - timeout error
+      // - server 10 error, server 20 error
+      baseDn = DN.decode(SAFE_DATA_DN);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 0);
+      errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+      assertTrue(errorsByServer.isEmpty());
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 2);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 2);
+      errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+      //  errors by server list for sd mode should be [[10:2],[20:1]]
+      assertEquals(errorsByServer.size(), 2);
+      nError = errorsByServer.get((short)10);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 2);
+      nError = errorsByServer.get((short)20);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 1);
+
+      // Make a third LDAP update (re-add the entry)
+      startTime = System.currentTimeMillis(); // Time the update has been initiated
+      addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+      // In this scenario, the fake RS will not send back an ack so we expect
+      // the add entry code (LDAP client code emulation) to be blocked for the
+      // timeout value at least. If the time we have slept is lower, timeout
+      // handling code is not working...
+      endTime = System.currentTimeMillis();
+      assertTrue((endTime - startTime) >= TIMEOUT);
+      assertTrue(replicationServer.isScenarioExecuted());
+
+      // Check monitoring values
+      // No ack should have comen back, so timeout incremented (flag and error for rs)
+      baseDn = DN.decode(SAFE_DATA_DN);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 0);
+      errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+      assertTrue(errorsByServer.isEmpty());
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 3);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 3);
+      errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+      //  errors by server list for sd mode should be [[10:2],[20:1],[rsId:1]]
+      assertEquals(errorsByServer.size(), 3);
+      nError = errorsByServer.get((short)10);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 2);
+      nError = errorsByServer.get((short)20);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 1);
+      nError = errorsByServer.get((short)RS_SERVER_ID);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 1);
+
+    } finally
+    {
+      endTest();
+    }
+  }
+
+  /**
+   * DS performs many successive modifications in safe read mode and receives RS
+   * acks with various errors. Check for monitoring right errors
+   */
+  @Test
+  public void testSafeReadManyErrors() throws Exception
+  {
+
+    int TIMEOUT = 5000;
+    String testcase = "testSafeReadManyErrors";
+    try
+    {
+      // Create and start a RS expecting clients in safe read assured mode
+      replicationServer = new FakeReplicationServer(replServerPort, RS_SERVER_ID,
+        true);
+      replicationServer.start(SAFE_READ_MANY_ERRORS);
+
+      // Create a safe read assured domain
+      safeReadDomainCfgEntry = createAssuredDomain(AssuredMode.SAFE_READ_MODE, 0,
+        TIMEOUT);
+      // Wait for connection of domain to RS
+      waitForConnectionToRs(testcase, replicationServer);
+
+      // Make a first LDAP update (add an entry)
+      long startTime = System.currentTimeMillis(); // Time the update has been initiated
+      String entryDn = "ou=assured-sr-many-errors-entry," + SAFE_READ_DN;
+      String entry = "dn: " + entryDn + "\n" +
+        "objectClass: top\n" +
+        "objectClass: organizationalUnit\n";
+      addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+      // In this scenario, the fake RS will send back an ack after NO_TIMEOUT_RS_SLEEP_TIME
+      // seconds, so we expect the add entry code (LDAP client code emulation) to be blocked
+      // for more than NO_TIMEOUT_RS_SLEEP_TIME seconds but no more than the timeout value.
+      long endTime = System.currentTimeMillis();
+      long callTime = endTime - startTime;
+      assertTrue( (callTime >= NO_TIMEOUT_RS_SLEEP_TIME) && (callTime <= TIMEOUT));
+
+      // Check monitoring values
+      // The expected ack for the first update is:
+      // - replay error
+      // - server 10 error, server 20 error
+      DN baseDn = DN.decode(SAFE_READ_DN);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 1);
+      Map<Short, Integer> errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+      //  errors by server list for sr mode should be [[10:1],[20:1]]
+      assertEquals(errorsByServer.size(), 2);
+      Integer nError = errorsByServer.get((short)10);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 1);
+      nError = errorsByServer.get((short)20);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 0);
+      errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+      assertTrue(errorsByServer.isEmpty());
+    
+      // Make a second LDAP update (delete the entry)
+      startTime = System.currentTimeMillis(); // Time the update has been initiated
+      deleteEntry(entryDn);
+
+      // In this scenario, the fake RS will send back an ack after NO_TIMEOUT_RS_SLEEP_TIME
+      // seconds, so we expect the delete entry code (LDAP client code emulation) to be blocked
+      // for more than NO_TIMEOUT_RS_SLEEP_TIME seconds but no more than the timeout value.
+      endTime = System.currentTimeMillis();
+      callTime = endTime - startTime;
+      assertTrue( (callTime >= NO_TIMEOUT_RS_SLEEP_TIME) && (callTime <= TIMEOUT));
+
+      // Check monitoring values
+      // The expected ack for the second update is:
+      // - timeout error
+      // - wrong status error
+      // - replay error
+      // - server 10 error, server 20 error, server 30 error
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 2);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 2);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 2);
+      errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+      //  errors by server list for sr mode should be [[10:2],[20:2],[30:1]]
+      assertEquals(errorsByServer.size(), 3);
+      nError = errorsByServer.get((short)10);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 2);
+      nError = errorsByServer.get((short)20);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 2);
+      nError = errorsByServer.get((short)30);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 0);
+      errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+      assertTrue(errorsByServer.isEmpty());
+
+      // Make a third LDAP update (re-add the entry)
+      startTime = System.currentTimeMillis(); // Time the update has been initiated
+      addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+      // In this scenario, the fake RS will not send back an ack so we expect
+      // the add entry code (LDAP client code emulation) to be blocked for the
+      // timeout value at least. If the time we have slept is lower, timeout
+      // handling code is not working...
+      endTime = System.currentTimeMillis();
+      assertTrue((endTime - startTime) >= TIMEOUT);
+      assertTrue(replicationServer.isScenarioExecuted());
+
+      // Check monitoring values
+      // No ack should have comen back, so timeout incremented (flag and error for rs)
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 3);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 3);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 2);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 2);
+      errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+      //  errors by server list for sr mode should be [[10:2],[20:2],[30:1],[rsId:1]]
+      assertEquals(errorsByServer.size(), 4);
+      nError = errorsByServer.get((short)10);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 2);
+      nError = errorsByServer.get((short)20);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 2);
+      nError = errorsByServer.get((short)30);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 1);
+      nError = errorsByServer.get((short)RS_SERVER_ID);
+      assertNotNull(nError);
+      assertEquals(nError.intValue(), 1);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+      assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 0);
+      errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+      assertTrue(errorsByServer.isEmpty());
+
+    } finally
+    {
+      endTest();
+    }
+  }
+
+  /**
+   * Delete an entry from the database
+   */
+  private void deleteEntry(String dn) throws Exception
+  {
+    DN realDn = DN.decode(dn);
+    DeleteOperationBasis delOp = new DeleteOperationBasis(connection,
+      InternalClientConnection.nextOperationID(), InternalClientConnection.
+      nextMessageID(), null, realDn);
+    delOp.setInternalOperation(true);
+    delOp.run();
+    assertNull(DirectoryServer.getEntry(realDn));
+  }
+
+  /**
+   * Get the errors by servers from cn=monitor, according to the requested base dn
+   * and the requested mode
+   * This corresponds to the values for multi valued attributes:
+   * - assured-sr-server-not-acknowledged-updates in SR mode
+   * - assured-sd-server-timeout-updates in SD mode
+   */
+  protected Map<Short,Integer> getErrorsByServers(DN baseDn,
+    AssuredMode assuredMode) throws Exception
+  {
+    /*
+     * Find monitoring entry for requested base DN
+     */
+    String monitorFilter =
+         "(&(cn=replication Domain*)(base-dn=" + baseDn + "))";
+
+    InternalSearchOperation op;
+    int count = 0;
+    do
+    {
+      if (count++>0)
+        Thread.sleep(100);
+      op = connection.processSearch(
+                                    ByteStringFactory.create("cn=monitor"),
+                                    SearchScope.SINGLE_LEVEL,
+                                    LDAPFilter.decode(monitorFilter));
+    }
+    while (op.getSearchEntries().isEmpty() && (count<100));
+    if (op.getSearchEntries().isEmpty())
+      throw new Exception("Could not read monitoring information");
+
+    SearchResultEntry entry = op.getSearchEntries().getFirst();
+
+    if (entry == null)
+      throw new Exception("Could not find monitoring entry");
+
+    /*
+     * Find the multi valued attribute matching the requested assured mode
+     */
+    String assuredAttr = null;
+    switch(assuredMode)
+    {
+      case SAFE_READ_MODE:
+        assuredAttr = "assured-sr-server-not-acknowledged-updates";
+        break;
+      case SAFE_DATA_MODE:
+        assuredAttr = "assured-sd-server-timeout-updates";
+        break;
+      default:
+        throw new Exception("Unknown assured type");
+    }
+
+    List<Attribute> attrs = entry.getAttribute(assuredAttr);
+
+    Map<Short,Integer> resultMap = new HashMap<Short,Integer>();
+    if ( (attrs == null) || (attrs.isEmpty()) )
+      return resultMap; // Empty map
+
+    Attribute attr = attrs.get(0);
+    Iterator<AttributeValue> attValIt = attr.iterator();
+    // Parse and store values
+    while (attValIt.hasNext())
+    {
+      String srvStr = attValIt.next().getStringValue();
+      StringTokenizer strtok = new StringTokenizer(srvStr, ":");
+      String token = strtok.nextToken();
+      if (token != null)
+      {
+        Short serverId = Short.valueOf(token);
+        token = strtok.nextToken();
+        if (token != null)
+        {
+          Integer nerrors = Integer.valueOf(token);
+          resultMap.put(serverId, nerrors);
+        }
+      }
+    }
+
+    return resultMap;
+  }
+}
+
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ComputeBestServerTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ComputeBestServerTest.java
index 5057812..38b1bf4 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ComputeBestServerTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ComputeBestServerTest.java
@@ -27,7 +27,7 @@
 package org.opends.server.replication.plugin;
 
 import java.util.HashMap;
-import static org.opends.server.replication.plugin.ReplicationBroker.*;
+import static org.opends.server.replication.service.ReplicationBroker.*;
 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;
@@ -40,8 +40,6 @@
 import org.opends.server.replication.ReplicationTestCase;
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.replication.common.ServerState;
-import org.opends.server.replication.plugin.ReplicationBroker.ServerInfo;
-import org.opends.server.types.DN;
 import org.testng.annotations.Test;
 
 /**
@@ -85,7 +83,7 @@
     final String WINNER = "winner";
 
     // Create my state
-    ServerState mySt = new ServerState();     
+    ServerState mySt = new ServerState();
     ChangeNumber cn = new ChangeNumber(2L, 0, myId2); // Should not be used inside algo
     mySt.update(cn);
     cn = new ChangeNumber(3L, 0, myId3); // Should not be used inside algo
@@ -103,7 +101,7 @@
     rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
 
     String bestServer =
-      computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+      computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
 
     assertEquals(bestServer, WINNER, "Wrong best replication server.");
   }
@@ -148,7 +146,7 @@
     rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
 
     String bestServer =
-      computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+      computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
 
     assertEquals(bestServer, WINNER, "Wrong best replication server.");
   }
@@ -170,7 +168,7 @@
     short myId1 = 1;
     short myId2 = 2;
     short myId3 = 3;
-    
+
     // definitions for server names
     final String WINNER = "winner";
 
@@ -195,7 +193,7 @@
     rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
 
     String bestServer =
-      computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+      computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
 
     assertEquals(bestServer, WINNER, "Wrong best replication server.");
   }
@@ -242,7 +240,7 @@
     rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
 
     String bestServer =
-      computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+      computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
 
     assertEquals(bestServer, WINNER, "Wrong best replication server.");
   }
@@ -300,7 +298,7 @@
     rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
 
     String bestServer =
-      computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+      computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
 
     assertEquals(bestServer, WINNER, "Wrong best replication server.");
   }
@@ -360,11 +358,11 @@
     rsInfos.put(LOOSER1, new ServerInfo(aState, (byte)2));
 
     String bestServer =
-      computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+      computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
 
     assertEquals(bestServer, WINNER, "Wrong best replication server.");
   }
-  
+
   /**
    * Test with 2 replication servers, none of them from our group id.
    *
@@ -418,7 +416,7 @@
     rsInfos.put(WINNER, new ServerInfo(aState, (byte)2));
 
     String bestServer =
-      computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+      computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
 
     assertEquals(bestServer, WINNER, "Wrong best replication server.");
   }
@@ -487,11 +485,11 @@
     rsInfos.put(LOOSER2, new ServerInfo(aState, (byte)1));
 
     String bestServer =
-      computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+      computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
 
     assertEquals(bestServer, WINNER, "Wrong best replication server.");
   }
-  
+
   /**
    * Test with 3 replication servers, up to date, but 2 different group ids.
    *
@@ -558,7 +556,7 @@
     rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
 
     String bestServer =
-      computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+      computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
 
     assertEquals(bestServer, WINNER, "Wrong best replication server.");
   }
@@ -605,7 +603,7 @@
     rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
 
     String bestServer =
-      computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+      computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
 
     assertEquals(bestServer, WINNER, "Wrong best replication server.");
   }
@@ -663,11 +661,11 @@
     rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
 
     String bestServer =
-      computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+      computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
 
     assertEquals(bestServer, WINNER, "Wrong best replication server.");
   }
-  
+
   /**
    * Test with 3 replication servers, late.
    *
@@ -732,11 +730,11 @@
     rsInfos.put(LOOSER2, new ServerInfo(aState, (byte)1));
 
     String bestServer =
-      computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+      computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
 
     assertEquals(bestServer, WINNER, "Wrong best replication server.");
   }
-  
+
   /**
    * Test with 6 replication servers, some up, some late, one null
    *
@@ -752,8 +750,8 @@
     // definitions for server ids
     short myId1 = 1;
     short myId2 = 2;
-    short myId3 = 3;    
-    
+    short myId3 = 3;
+
     // definitions for server names
     final String WINNER = "winner";
     final String LOOSER1 = "looser1";
@@ -803,7 +801,7 @@
     cn = new ChangeNumber(10L, 0, myId3);
     aState.update(cn);
     rsInfos.put(LOOSER3, new ServerInfo(aState, (byte)1));
-    
+
     // State for server 4
     aState = new ServerState();
     cn = new ChangeNumber(6L, 0, myId1);
@@ -813,7 +811,7 @@
     cn = new ChangeNumber(8L, 0, myId3);
     aState.update(cn);
     rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
-    
+
     // State for server 5 (null one for our serverid)
     aState = new ServerState();
     cn = new ChangeNumber(5L, 0, myId2);
@@ -821,7 +819,7 @@
     cn = new ChangeNumber(5L, 0, myId3);
     aState.update(cn);
     rsInfos.put(LOOSER4, new ServerInfo(aState, (byte)1));
-    
+
     // State for server 6
     aState = new ServerState();
     cn = new ChangeNumber(5L, 0, myId1);
@@ -833,7 +831,7 @@
     rsInfos.put(LOOSER5, new ServerInfo(aState, (byte)1));
 
     String bestServer =
-      computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+      computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
 
     assertEquals(bestServer, WINNER, "Wrong best replication server.");
   }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/GroupIdHandshakeTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/GroupIdHandshakeTest.java
index 92fba1a..5da9444 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/GroupIdHandshakeTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/GroupIdHandshakeTest.java
@@ -30,7 +30,7 @@
 import java.net.ServerSocket;
 import java.util.SortedSet;
 import java.util.TreeSet;
-import static org.opends.server.replication.plugin.ReplicationBroker.*;
+
 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;
@@ -64,8 +64,8 @@
   private int rs1Port = -1;
   private int rs2Port = -1;
   private int rs3Port = -1;
-  private ReplicationDomain rd1 = null;
-  private ReplicationDomain rd2 = null;
+  private LDAPReplicationDomain rd1 = null;
+  private LDAPReplicationDomain rd2 = null;
   private ReplicationServer rs1 = null;
   private ReplicationServer rs2 = null;
   private ReplicationServer rs3 = null;
@@ -147,16 +147,6 @@
     rs3Port = -1;
   }
 
-  private void sleep(long time)
-  {
-    try
-    {
-      Thread.sleep(time);
-    } catch (InterruptedException ex)
-    {
-      fail("Error sleeping " + stackTraceToSingleLineString(ex));
-    }
-  }
 
   /**
    * Check connection of the provided replication domain to the provided
@@ -167,7 +157,7 @@
   {
 
     int rsPort = -1;
-    ReplicationDomain rd = null;
+    LDAPReplicationDomain rd = null;
     switch (dsId)
     {
       case DS1_ID:
@@ -281,7 +271,7 @@
   private SortedSet<String> createRSListForTestCase(String testCase)
   {
     SortedSet<String> replServers = new TreeSet<String>();
-    
+
     if (testCase.equals("testRSWithSameGroupIds"))
     {
       // 2 servers used for this test case.
@@ -369,7 +359,7 @@
   /**
    * Creates a new ReplicationDomain.
    */
-  private ReplicationDomain createReplicationDomain(short serverId,
+  private LDAPReplicationDomain createReplicationDomain(short serverId,
     int groupId, String testCase)
   {
 
@@ -380,7 +370,7 @@
       DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
       DomainFakeCfg domainConf =
         new DomainFakeCfg(baseDn, serverId, replServers, groupId);
-      ReplicationDomain replicationDomain =
+      LDAPReplicationDomain replicationDomain =
         MultimasterReplication.createNewDomain(domainConf);
       replicationDomain.start();
       return replicationDomain;
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalCsnOrderingTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalCsnOrderingTest.java
index f9c2ecf..4789ca0 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalCsnOrderingTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalCsnOrderingTest.java
@@ -158,7 +158,7 @@
   throws Exception
   {
     // Creates a rule
-    HistoricalCsnOrderingMatchingRule r = 
+    HistoricalCsnOrderingMatchingRule r =
       new HistoricalCsnOrderingMatchingRule();
 
     ChangeNumber del1 = new ChangeNumber(1, (short) 0, (short) 1);
@@ -178,7 +178,7 @@
   }
 
   /**
-   * Test that we can retrieve the entries that were missed by 
+   * Test that we can retrieve the entries that were missed by
    * a replication server and can  re-build operations from the historical
    * informations.
    */
@@ -227,7 +227,7 @@
     logError(Message.raw(Category.SYNC, Severity.INFORMATION,
         "First historical value:" + histValue));
 
-    // Perform a 2nd modification to update the hist attribute with 
+    // Perform a 2nd modification to update the hist attribute with
     // a second value
     resultCode = TestCaseUtils.applyModifications(false,
         "dn: cn=test1," + baseDn.toString(),
@@ -256,7 +256,8 @@
 
     // Retrieves the entries that have changed since the first modification
     InternalSearchOperation op =
-      ReplicationBroker.searchForChangedEntries(baseDn, fromChangeNumber, null);
+      LDAPReplicationDomain.searchForChangedEntries(
+          baseDn, fromChangeNumber, null);
 
     // The expected result is one entry .. the one previously modified
     assertEquals(op.getResultCode(), ResultCode.SUCCESS);
@@ -279,6 +280,6 @@
         updatesCnt++;
       }
     }
-    assertTrue(updatesCnt == 3);    
+    assertTrue(updatesCnt == 3);
   }
 }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalTest.java
index b756b5a..8f0d85f 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalTest.java
@@ -27,11 +27,13 @@
 
 package org.opends.server.replication.plugin;
 
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
+
 import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.service.ReplicationBroker;
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.replication.protocol.AddMsg;
 import org.opends.server.replication.protocol.ModifyMsg;
-import org.opends.server.replication.protocol.UpdateMsg;
 import org.opends.server.TestCaseUtils;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.tools.LDAPModify;
@@ -335,7 +337,7 @@
     ModifyMsg modMsg = new ModifyMsg(changeNum, dn, mods, entryuuid);
     broker.publish(modMsg);
   }
-  
+
   /**
    * Test that historical information is correctly added when performaing ADD,
    * MOD and MODDN operations.
@@ -416,7 +418,7 @@
 
     for (FakeOperation fake : ops)
     {
-      UpdateMsg msg = (UpdateMsg) fake.generateMessage();
+      LDAPUpdateMsg msg = (LDAPUpdateMsg) fake.generateMessage();
       AbstractOperation op =
         msg.createOperation(InternalClientConnection.getRootConnection());
       op.setInternalOperation(true);
@@ -449,7 +451,7 @@
         AddMsg addmsg = addOp.generateMessage();
         assertTrue(dn1.equals(DN.decode(addmsg.getDn())));
         assertTrue(addmsg.getUniqueId().equals(Historical.getEntryUuid(entry)));
-        String parentId = ReplicationDomain.findEntryId(dn1.getParent());
+        String parentId = LDAPReplicationDomain.findEntryId(dn1.getParent());
         assertTrue(addmsg.getParentUid().equals(parentId));
         addmsg.createOperation(InternalClientConnection.getRootConnection());
       } else
@@ -462,7 +464,7 @@
         }
       }
     }
-  
+
       assertEquals(count, assertCount);
     }
 }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/IsolationTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/IsolationTest.java
index 611257d..6a0f891 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/IsolationTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/IsolationTest.java
@@ -26,15 +26,10 @@
  */
 package org.opends.server.replication.plugin;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
+
 import java.net.ServerSocket;
 import java.util.SortedSet;
 import java.util.TreeSet;
-import java.util.UUID;
 
 import static org.testng.Assert.*;
 
@@ -65,7 +60,7 @@
   @Test()
   public void noUpdateIsolationPolicyTest() throws Exception
   {
-    ReplicationDomain domain = null;
+    LDAPReplicationDomain domain = null;
     DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
     SynchronizationProvider replicationPlugin = null;
     short serverId = 1;
@@ -106,7 +101,7 @@
       op = conn.processModify(baseDn, generatemods("description", "test"));
 
       // check that the operation was successful.
-      assertEquals(op.getResultCode(), ResultCode.SUCCESS, 
+      assertEquals(op.getResultCode(), ResultCode.SUCCESS,
           op.getAdditionalLogMessage().toString());
     }
     finally
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ModifyConflictTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ModifyConflictTest.java
index e2f7039..b0bf2d4 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ModifyConflictTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ModifyConflictTest.java
@@ -26,6 +26,8 @@
  */
 package org.opends.server.replication.plugin;
 
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -49,7 +51,6 @@
 import org.opends.server.replication.plugin.Historical;
 import org.opends.server.replication.protocol.ModifyContext;
 import org.opends.server.replication.protocol.ReplicationMsg;
-import org.opends.server.replication.protocol.UpdateMsg;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeBuilder;
 import org.opends.server.types.AttributeType;
@@ -1137,9 +1138,9 @@
         assertTrue(new FakeOperationComparator().compare(fk, fk) == 0);
         assertTrue(new FakeOperationComparator().compare(null , fk) < 0);
         ReplicationMsg generatedMsg = fk.generateMessage() ;
-        if (generatedMsg instanceof UpdateMsg)
+        if (generatedMsg instanceof LDAPUpdateMsg)
         {
-          UpdateMsg new_name = (UpdateMsg) generatedMsg;
+          LDAPUpdateMsg new_name = (LDAPUpdateMsg) generatedMsg;
           assertEquals(new_name.getUniqueId(),uuid);
 
         }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/PersistentServerStateTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/PersistentServerStateTest.java
index e447cb8..0d1ba91 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/PersistentServerStateTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/PersistentServerStateTest.java
@@ -28,6 +28,8 @@
 
 import static org.testng.Assert.assertEquals;
 
+import org.opends.server.replication.common.ServerState;
+
 import org.opends.server.replication.ReplicationTestCase;
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.replication.common.ChangeNumberGenerator;
@@ -69,9 +71,13 @@
      * 2 ChangeNumbers have been saved in this new PersistentServerState.
      */
     DN baseDn = DN.decode(dn);
-    PersistentServerState state = new PersistentServerState(baseDn, (short) 1);
-    ChangeNumberGenerator gen1 = new ChangeNumberGenerator((short) 1, state);
-    ChangeNumberGenerator gen2 = new ChangeNumberGenerator((short) 2, state);
+    ServerState origState = new ServerState();
+    PersistentServerState state =
+      new PersistentServerState(baseDn, (short) 1, origState);
+    ChangeNumberGenerator gen1 =
+      new ChangeNumberGenerator((short) 1, origState);
+    ChangeNumberGenerator gen2 =
+      new ChangeNumberGenerator((short) 2, origState);
 
     ChangeNumber cn1 = gen1.newChangeNumber();
     ChangeNumber cn2 = gen2.newChangeNumber();
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ReplicationServerFailoverTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ReplicationServerFailoverTest.java
index 004c798..442257b 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ReplicationServerFailoverTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ReplicationServerFailoverTest.java
@@ -63,8 +63,8 @@
   private static final short RS2_ID = 32;
   private int rs1Port = -1;
   private int rs2Port = -1;
-  private ReplicationDomain rd1 = null;
-  private ReplicationDomain rd2 = null;
+  private LDAPReplicationDomain rd1 = null;
+  private LDAPReplicationDomain rd2 = null;
   private ReplicationServer rs1 = null;
   private ReplicationServer rs2 = null;
 
@@ -298,7 +298,7 @@
   {
 
     int rsPort = -1;
-    ReplicationDomain rd = null;
+    LDAPReplicationDomain rd = null;
     switch (dsId)
     {
       case DS1_ID:
@@ -439,7 +439,7 @@
   /**
    * Creates a new ReplicationDomain.
    */
-  private ReplicationDomain createReplicationDomain(DN baseDn, short serverId)
+  private LDAPReplicationDomain createReplicationDomain(DN baseDn, short serverId)
   {
 
     SortedSet<String> replServers = new TreeSet<String>();
@@ -452,7 +452,7 @@
       DomainFakeCfg domainConf =
         new DomainFakeCfg(baseDn, serverId, replServers);
       //domainConf.setHeartbeatInterval(500);
-      ReplicationDomain replicationDomain =
+      LDAPReplicationDomain replicationDomain =
         MultimasterReplication.createNewDomain(domainConf);
       replicationDomain.start();
 
@@ -465,7 +465,7 @@
     return null;
   }
 
-  private int findReplServerConnected(ReplicationDomain rd)
+  private int findReplServerConnected(LDAPReplicationDomain rd)
   {
     int rsPort = -1;
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/StateMachineTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/StateMachineTest.java
index e1d614b..0a103da 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/StateMachineTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/StateMachineTest.java
@@ -35,7 +35,7 @@
 import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicBoolean;
-import static org.opends.server.replication.plugin.ReplicationBroker.*;
+
 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;
@@ -48,6 +48,7 @@
 import org.opends.server.TestCaseUtils;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.service.ReplicationBroker;
 import org.opends.server.replication.common.ChangeNumberGenerator;
 import org.opends.server.replication.common.DSInfo;
 import org.opends.server.replication.common.ServerState;
@@ -60,7 +61,6 @@
 import org.opends.server.replication.protocol.ReplicationMsg;
 import org.opends.server.replication.protocol.ResetGenerationIdMsg;
 import org.opends.server.replication.protocol.RoutableMsg;
-import org.opends.server.replication.protocol.TopologyMsg;
 import org.opends.server.replication.server.ReplServerFakeConfiguration;
 import org.opends.server.replication.server.ReplicationServer;
 import org.opends.server.types.Attribute;
@@ -71,7 +71,6 @@
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 import static org.testng.Assert.*;
-import static org.opends.server.TestCaseUtils.*;
 
 /**
  * Some tests to go through the DS state machine and validate we get the
@@ -87,7 +86,7 @@
   private static final short DS3_ID = 3;
   private static final short RS1_ID = 41;
   private int rs1Port = -1;
-  private ReplicationDomain ds1 = null;
+  private LDAPReplicationDomain ds1 = null;
   private ReplicationBroker ds2 = null;
   private ReplicationBroker ds3 = null;
   private ReplicationServer rs1 = null;
@@ -176,7 +175,7 @@
   {
 
     ReplicationBroker rb = null;
-    ReplicationDomain rd = null;
+    LDAPReplicationDomain rd = null;
     switch (dsId)
     {
       case DS1_ID:
@@ -215,14 +214,14 @@
       // Sleep 1 second
       try
       {
-        Thread.sleep(1000);
+        Thread.sleep(100);
       } catch (InterruptedException ex)
       {
         fail("Error sleeping " + stackTraceToSingleLineString(ex));
       }
       nSec++;
 
-      if (nSec > secTimeout)
+      if (nSec > secTimeout*10)
       {
         // Timeout reached, end with error
         fail("checkConnection: DS " + dsId + " is not connected to the RS after "
@@ -276,7 +275,7 @@
    * Creates and starts a new ReplicationDomain configured for the replication
    * server
    */
-  private ReplicationDomain createReplicationDomain(short dsId)
+  private LDAPReplicationDomain createReplicationDomain(short dsId)
   {
     try
     {
@@ -286,7 +285,7 @@
       DN baseDn = DN.decode(EXAMPLE_DN);
       DomainFakeCfg domainConf =
         new DomainFakeCfg(baseDn, dsId, replServers);
-      ReplicationDomain replicationDomain =
+      LDAPReplicationDomain replicationDomain =
         MultimasterReplication.createNewDomain(domainConf);
       replicationDomain.start();
 
@@ -308,7 +307,7 @@
     throws Exception, SocketException
   {
     ReplicationBroker broker = new ReplicationBroker(null,
-      state, DN.decode(EXAMPLE_DN), dsId, 0, 0, 0, 0, window, 0, generationId,
+      state, EXAMPLE_DN, dsId, 100, generationId, 0,
       new ReplSessionSecurity(null, null, null, true), (byte) 1);
     ArrayList<String> servers = new ArrayList<String>(1);
     servers.add("localhost:" + rs1Port);
@@ -413,7 +412,6 @@
 
     try
     {
-
       /**
        * RS1 starts with specified threshold value
        */
@@ -440,7 +438,7 @@
       // having sent them to TCP receive queue of DS2.
       bw = new BrokerWriter(ds3, DS3_ID, false);
       bw.followAndPause(11);
-      sleep(1000);
+      // sleep(1000);
 
       /**
        * DS3 sends changes (less than threshold): DS2 should still be in normal
@@ -452,10 +450,10 @@
       {
         nChangesSent = thresholdValue - 1;
         bw.followAndPause(nChangesSent);
-        sleep(7000); // Be sure status analyzer has time to test
+        sleep(1000); // Be sure status analyzer has time to test
         ReplicationMsg msg = br3.getLastMsg();
         debugInfo(testCase + " Step 1: last message from writer: " + msg);
-        assertTrue(msg == null);
+        assertTrue(msg == null, (msg != null) ? msg.toString() : "null" );
       }
 
       /**
@@ -463,17 +461,29 @@
        * update topo message with status of DS2: degraded status
        */
       bw.followAndPause(thresholdValue - nChangesSent);
-      sleep(7000); // Be sure status analyzer has time to test
-      ReplicationMsg lastMsg = br3.getLastMsg();
-      assertTrue(lastMsg != null);
-      debugInfo(testCase + " Step 2: last message from writer: " + lastMsg);
-      assertTrue(lastMsg instanceof TopologyMsg);
-      TopologyMsg topoMsg = (TopologyMsg) lastMsg;
-      List<DSInfo> dsList = topoMsg.getDsList();
-      assertEquals(dsList.size(), 1);
-      DSInfo ds3Info = dsList.get(0);
-      assertEquals(ds3Info.getDsId(), DS2_ID);
-      assertEquals(ds3Info.getStatus(), ServerStatus.DEGRADED_STATUS);
+      // wait for a status MSG status analyzer to broker 3
+      ReplicationMsg lastMsg = null;
+      for (int count = 0; count< 50; count++)
+      {
+        List<DSInfo> dsList = ds3.getDsList();
+        DSInfo ds3Info = null;
+        if (dsList.size() > 0)
+        {
+          ds3Info = dsList.get(0);
+        }
+        if ((ds3Info != null) && (ds3Info.getDsId() == DS2_ID) &&
+            (ds3Info.getStatus()== ServerStatus.DEGRADED_STATUS) )
+        {
+          break;
+        }
+        else
+        {
+          if (count < 50)
+            sleep(200); // Be sure status analyzer has time to test
+          else
+            fail("DS2 did not get degraded : " + ds3Info);
+        }
+      }
 
       /**
        * DS3 sends 10 additional changes after threshold value, DS2 should still be
@@ -481,7 +491,7 @@
        */
       bw.followAndPause(10);
       bw.shutdown();
-      sleep(7000); // Be sure status analyzer has time to test
+      sleep(1000); // Be sure status analyzer has time to test
       lastMsg = br3.getLastMsg();
       ReplicationMsg msg = br3.getLastMsg();
       debugInfo(testCase + " Step 3: last message from writer: " + msg);
@@ -492,17 +502,28 @@
        * (create a reader to emulate replay of messages (messages read from queue))
        */
       br2 = new BrokerReader(ds2, DS2_ID);
-      sleep(7000); // Be sure messages are read and status analyzer has time to test
-      lastMsg = br3.getLastMsg();
-      assertTrue(lastMsg != null);
-      debugInfo(testCase + " Step 4: last message from writer: " + lastMsg);
-      assertTrue(lastMsg instanceof TopologyMsg);
-      topoMsg = (TopologyMsg) lastMsg;
-      dsList = topoMsg.getDsList();
-      assertEquals(dsList.size(), 1);
-      ds3Info = dsList.get(0);
-      assertEquals(ds3Info.getDsId(), DS2_ID);
-      assertEquals(ds3Info.getStatus(), ServerStatus.NORMAL_STATUS);
+      // wait for a status MSG status analyzer to broker 3
+      for (int count = 0; count< 50; count++)
+      {
+        List<DSInfo> dsList = ds3.getDsList();
+        DSInfo ds3Info = null;
+        if (dsList.size() > 0)
+        {
+          ds3Info = dsList.get(0);
+        }
+        if ((ds3Info != null) && (ds3Info.getDsId() == DS2_ID) &&
+            (ds3Info.getStatus()== ServerStatus.DEGRADED_STATUS) )
+        {
+          break;
+        }
+        else
+        {
+          if (count < 50)
+            sleep(200); // Be sure status analyzer has time to test
+          else
+            fail("DS2 did not get degraded.");
+        }
+      }
 
     } finally
     {
@@ -617,8 +638,8 @@
       rs1.remove();
       bw.pause();
       sleepAssertStatusEquals(30, ds1, ServerStatus.NOT_CONNECTED_STATUS);
-      
-      
+
+
       /**
        * DS2 restarts with up to date server state (this allows to have
        * restarting RS1 not sending him some updates he already sent)
@@ -680,7 +701,7 @@
        * DS2 sends reset gen id order with bad gen id: DS1 should go in bad gen id
        * status (from degraded status this time)
        */
-      resetGenId(ds2, -1); // -1 to allow next step full update and flush RS db so that DS1 can reconnect after full update        
+      resetGenId(ds2, -1); // -1 to allow next step full update and flush RS db so that DS1 can reconnect after full update
       sleepAssertStatusEquals(30, ds1, ServerStatus.BAD_GEN_ID_STATUS);
       bw.pause();
 
@@ -705,7 +726,7 @@
       ds2.stop(); // will need a new broker with another gen id restart it
       bw.shutdown();
       br.shutdown();
-      long newGen = ds1.getGenerationId();
+      long newGen = ds1.getGenerationID();
       curState = ds1.getServerState();
       ds2 = createReplicationBroker(DS2_ID, curState, newGen);
       checkConnection(30, DS2_ID);
@@ -738,7 +759,7 @@
       ds2.stop(); // will need a new broker with another gen id restart it
       bw.shutdown();
       br.shutdown();
-      newGen = ds1.getGenerationId();
+      newGen = ds1.getGenerationID();
       curState = ds1.getServerState();
       ds2 = createReplicationBroker(DS2_ID, curState, newGen);
       checkConnection(30, DS2_ID);
@@ -798,7 +819,7 @@
     // a backend backed up with a file
 
     // Clear the backend
-    ReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
+    LDAPReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
 
   }
 
@@ -815,8 +836,8 @@
     super.classCleanUp();
 
     // Clear the backend
-    ReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
-    
+    LDAPReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
+
     paranoiaCheck();
   }
 
@@ -846,7 +867,7 @@
     private short destId = -1; // Server id of server to initialize
     private long nEntries = -1; // Number of entries to send to dest
     private boolean createReader = false;
-    
+
     /**
      * If the BrokerInitializer is to be used for a lot of entries to send
      * (which is often the case), the reader thread should be enabled to make
@@ -855,7 +876,7 @@
      * method of the broker himself.
      */
     private BrokerReader reader = null;
-    
+
     /**
      * Creates a broker initializer with a reader
      */
@@ -874,7 +895,7 @@
       this.serverId = serverId;
       this.createReader = createReader;
     }
-    
+
     /**
      * Initializes a full update session by sending InitializeTargetMsg
      */
@@ -893,15 +914,11 @@
 
       // Send init msg to warn dest server it is going do be initialized
       RoutableMsg initTargetMsg = null;
-      try
-      {
-        initTargetMsg =
-          new InitializeTargetMsg(DN.decode(EXAMPLE_DN), serverId, destId,
+
+      initTargetMsg =
+          new InitializeTargetMsg(EXAMPLE_DN, serverId, destId,
           serverId, nEntries);
-      } catch (DirectoryException ex)
-      {
-        fail("Unable to create init message.");
-      }
+
       rb.publish(initTargetMsg);
 
       // Send top entry for the domain
@@ -951,27 +968,27 @@
     public void runFullUpdate()
     {
       debugInfo("Broker " + serverId + " initializer starting sending entries to server " + destId);
-     
+
       for(long i = 0 ; i<nEntries ; i++) {
           EntryMsg entryMsg = createNextEntryMsg();
           rb.publish(entryMsg);
       }
 
       debugInfo("Broker " + serverId + " initializer stopping sending entries");
-      
+
       debugInfo("Broker " + serverId + " initializer sending EntryDoneMsg");
       DoneMsg doneMsg = new DoneMsg(serverId, destId);
       rb.publish(doneMsg);
-      
+
       if (createReader)
       {
         reader.shutdown();
       }
-      
+
       debugInfo("Broker " + serverId + " initializer thread is dying");
     }
   }
-  
+
   /**
    * Thread for sending a lot of changes through a broker.
    */
@@ -992,6 +1009,7 @@
     private int nChangesSent = 0; // Number of sent changes
     private int nChangesSentLimit = 0;
     ChangeNumberGenerator gen = null;
+    private Object sleeper = new Object();
     /**
      * If the BrokerWriter is to be used for a lot of changes to send (which is
      * often the case), the reader thread should be enabled to make the window
@@ -1084,7 +1102,10 @@
         try
         {
           // Writer in pause, sleep a while to let other threads work
-          Thread.sleep(1000);
+          synchronized(sleeper)
+          {
+            sleeper.wait(1000);
+          }
         } catch (InterruptedException ex)
         {
           /* Don't care */
@@ -1100,6 +1121,10 @@
     {
       suspended.set(true); // If were working
       shutdown.set(true);
+      synchronized (sleeper)
+      {
+        sleeper.notify();
+      }
       try
       {
         join();
@@ -1107,7 +1132,7 @@
       {
         /* Don't care */
       }
-      
+
       // Stop reader if any
       if (reader != null)
       {
@@ -1128,12 +1153,12 @@
       {
         try
         {
-          Thread.sleep(1000);
+          Thread.sleep(200);
         } catch (InterruptedException ex)
         {
           /* Don't care */
         }
-      }      
+      }
     }
 
     /**
@@ -1183,7 +1208,7 @@
      */
     public void followAndPause(int nChanges)
     {
-      debugInfo("Requested broker writer " + serverId + " to write " + nChanges + " change(s).");      
+      debugInfo("Requested broker writer " + serverId + " to write " + nChanges + " change(s).");
       pause(); // If however we were already working
 
       // Initialize counter system variables
@@ -1341,7 +1366,7 @@
    * @param testedValue The value we want to test
    * @param expectedValue The value the tested value should be equal to
    */
-  private void sleepAssertStatusEquals(int secTimeout, ReplicationDomain testedValue,
+  private void sleepAssertStatusEquals(int secTimeout, LDAPReplicationDomain testedValue,
     ServerStatus expectedValue)
   {
     int nSec = 0;
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java
index da99b8e..5acfae9 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java
@@ -32,7 +32,7 @@
 import java.util.List;
 import java.util.SortedSet;
 import java.util.TreeSet;
-import static org.opends.server.replication.plugin.ReplicationBroker.*;
+
 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;
@@ -114,20 +114,20 @@
   static
   {
     DS2_RU.add("ldap://fake_url_for_ds2");
-    
+
     DS6_RU.add("ldap://fake_url_for_ds6_A");
     DS6_RU.add("ldap://fake_url_for_ds6_B");
   }
-  
+
   private int rs1Port = -1;
   private int rs2Port = -1;
   private int rs3Port = -1;
-  private ReplicationDomain rd1 = null;
-  private ReplicationDomain rd2 = null;
-  private ReplicationDomain rd3 = null;
-  private ReplicationDomain rd4 = null;
-  private ReplicationDomain rd5 = null;
-  private ReplicationDomain rd6 = null;
+  private LDAPReplicationDomain rd1 = null;
+  private LDAPReplicationDomain rd2 = null;
+  private LDAPReplicationDomain rd3 = null;
+  private LDAPReplicationDomain rd4 = null;
+  private LDAPReplicationDomain rd5 = null;
+  private LDAPReplicationDomain rd6 = null;
   private ReplicationServer rs1 = null;
   private ReplicationServer rs2 = null;
   private ReplicationServer rs3 = null;
@@ -203,7 +203,7 @@
       rd6.shutdown();
       rd6 = null;
     }
-  
+
     try
     {
       // Clear any reference to a domain in synchro plugin
@@ -257,7 +257,7 @@
   private void checkConnection(int secTimeout, short dsId, short rsId)
   {
     int rsPort = -1;
-    ReplicationDomain rd = null;
+    LDAPReplicationDomain rd = null;
     switch (dsId)
     {
       case DS1_ID:
@@ -398,7 +398,7 @@
     }
 
     return replServers;
-  }   
+  }
 
   /**
    * Creates a new ReplicationServer.
@@ -447,7 +447,7 @@
    * Creates and starts a new ReplicationDomain with the correct list of
    * know RSs according to DS id
    */
-  private ReplicationDomain createReplicationDomain(short dsId)
+  private LDAPReplicationDomain createReplicationDomain(short dsId)
   {
     try
     {
@@ -523,7 +523,7 @@
       DomainFakeCfg domainConf =
         new DomainFakeCfg(baseDn, dsId, replServers, assuredType,
         assuredSdLevel, groupId, 0, refUrls);
-      ReplicationDomain replicationDomain =
+      LDAPReplicationDomain replicationDomain =
         MultimasterReplication.createNewDomain(domainConf);
       replicationDomain.start();
 
@@ -1047,7 +1047,7 @@
 
     return new TopoView(dsList, rsList);
   }
-  
+
   /**
    * Get the topo view each DS in the provided ds list has and compares it
    * with the theoretical topology view that every body should have at the time
@@ -1057,8 +1057,8 @@
   {
    for(short currentDsId : dsIdList)
    {
-     ReplicationDomain rd = null;
-     
+     LDAPReplicationDomain rd = null;
+
      switch (currentDsId)
      {
        case DS1_ID:
@@ -1082,7 +1082,7 @@
        default:
          fail("Unknown replication domain server id.");
      }
-   
+
      /**
       * Get the topo view of the current analyzed DS
       */
@@ -1096,7 +1096,7 @@
        dsList.add(aDsInfo);
      }
      short dsId = rd.getServerId();
-     short rsId = rd.getBroker().getRsServerId();
+     short rsId = rd.getRsServerId();
      ServerStatus status = rd.getStatus();
      boolean assuredFlag = rd.isAssured();
      AssuredMode assuredMode = rd.getAssuredMode();
@@ -1106,17 +1106,17 @@
      DSInfo dsInfo = new DSInfo(dsId, rsId, TEST_DN_WITH_ROOT_ENTRY_GENID, status, assuredFlag, assuredMode,
        safeDataLevel, groupId, refUrls);
      dsList.add(dsInfo);
-     
+
      TopoView dsTopoView = new TopoView(dsList, rd.getRsList());
-     
+
      /**
       * Compare to what is the expected view
       */
-     
+
      assertEquals(dsTopoView, theoricalTopoView);
    }
   }
-  
+
   /**
    * Bag class representing a view of the topology at a given time
    * (who is connected to who, what are the config parameters...)
@@ -1124,22 +1124,22 @@
   private class TopoView
   {
     private List<DSInfo> dsList = null;
-    private List<RSInfo> rsList = null;    
-    
+    private List<RSInfo> rsList = null;
+
     public TopoView(List<DSInfo> dsList, List<RSInfo> rsList)
     {
       assertNotNull(dsList);
       assertNotNull(rsList);
-        
+
       this.dsList = dsList;
       this.rsList = rsList;
     }
-    
+
     public boolean equals(Object obj)
     {
       assertNotNull(obj);
       assertFalse(obj.getClass() != this.getClass());
-        
+
       TopoView topoView = (TopoView) obj;
 
       // Check dsList
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java
index 519824d..9efdd80 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java
@@ -94,12 +94,12 @@
   @DataProvider(name="createReplServerStartData")
   public Object [][] createReplServerStartData() throws Exception
   {
-    DN baseDN = DN.decode("o=test");
+    String baseDN = "o=test";
     ServerState state = new ServerState();
     state.update(new ChangeNumber((long)0, 0,(short)0));
     Object[] set1 = new Object[] {(short)1, baseDN, 0, "localhost:8989", state, 0L, (byte)0, 0};
 
-    baseDN = DN.decode("dc=example,dc=com");
+    baseDN = "dc=example,dc=com";
     state = new ServerState();
     state.update(new ChangeNumber((long)75, 5,(short)263));
     Object[] set2 = new Object[] {(short)16, baseDN, 100, "anotherHost:1025", state, 1245L, (byte)25, 3456};
@@ -112,7 +112,7 @@
    * using protocol V1 and V2 are working.
    */
   @Test(dataProvider="createReplServerStartData")
-  public void replServerStartMsgTest(short serverId, DN baseDN, int window,
+  public void replServerStartMsgTest(short serverId, String baseDN, int window,
          String url, ServerState state, long genId, byte groupId, int degTh) throws Exception
   {
     // Create V2 message
@@ -167,7 +167,7 @@
     assertEquals(msg.getGroupId(), v2Msg.getGroupId());
     assertEquals(msg.getDegradedStatusThreshold(), v2Msg.getDegradedStatusThreshold());
   }
-  
+
   @DataProvider(name = "createAddData")
   public Object[][] createAddData() {
     return new Object[][] {
@@ -250,7 +250,7 @@
 
     // Check default value for only V2 fields
     assertEquals(newMsg.getAssuredMode(), AssuredMode.SAFE_DATA_MODE);
-    assertEquals(newMsg.getSafeDataLevel(), (byte)-1);
+    assertEquals(newMsg.getSafeDataLevel(), (byte)1);
 
     // Set again only V2 fields
     newMsg.setAssuredMode(assuredMode);
@@ -336,7 +336,7 @@
 
     // Check default value for only V2 fields
     assertEquals(newMsg.getAssuredMode(), AssuredMode.SAFE_DATA_MODE);
-    assertEquals(newMsg.getSafeDataLevel(), (byte)-1);
+    assertEquals(newMsg.getSafeDataLevel(), (byte)1);
 
     // Set again only V2 fields
     newMsg.setAssuredMode(assuredMode);
@@ -356,7 +356,7 @@
     assertEquals(msg.getAssuredMode(), v2Msg.getAssuredMode());
     assertEquals(msg.getSafeDataLevel(), v2Msg.getSafeDataLevel());
   }
-  
+
   /**
    * Build some data for the ModifyMsg test below.
    */
@@ -469,7 +469,7 @@
 
     // Check default value for only V2 fields
     assertEquals(newMsg.getAssuredMode(), AssuredMode.SAFE_DATA_MODE);
-    assertEquals(newMsg.getSafeDataLevel(), (byte)-1);
+    assertEquals(newMsg.getSafeDataLevel(), (byte)1);
 
     // Set again only V2 fields
     newMsg.setAssuredMode(assuredMode);
@@ -503,7 +503,7 @@
                   genModOpBasis.getAttachment(SYNCHROCONTEXT));
     assertEquals(modOpBasis.getModifications(), genModOpBasis.getModifications());
   }
-  
+
   @DataProvider(name = "createModifyDnData")
   public Object[][] createModifyDnData() {
     
@@ -606,7 +606,7 @@
 
     // Check default value for only V2 fields
     assertEquals(newMsg.getAssuredMode(), AssuredMode.SAFE_DATA_MODE);
-    assertEquals(newMsg.getSafeDataLevel(), (byte)-1);
+    assertEquals(newMsg.getSafeDataLevel(), (byte)1);
     assertEquals(modDnOpBasis.getModifications(), mods);
     assertTrue(genModDnOpBasis.getModifications() == null);
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
index 04d1417..00afcce 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
@@ -26,11 +26,8 @@
  */
 package org.opends.server.replication.protocol;
 
-import static org.opends.server.replication.protocol.OperationContext.SYNCHROCONTEXT;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
+import static org.opends.server.replication.protocol.OperationContext.*;
+import static org.testng.Assert.*;
 
 import java.util.Iterator;
 import java.util.ArrayList;
@@ -227,7 +224,7 @@
     assertEquals(msg.getAssuredMode(), assuredMode);
 
     // Check safe data level
-    assertTrue(msg.getSafeDataLevel() == -1);
+    assertTrue(msg.getSafeDataLevel() == 1);
     msg.setSafeDataLevel(safeDataLevel);
     assertTrue(msg.getSafeDataLevel() == safeDataLevel);
 
@@ -312,7 +309,7 @@
     assertEquals(op.getRawEntryDN(), mod2.getRawEntryDN());
 
     // Create an update message from this op
-    DeleteMsg updateMsg = (DeleteMsg) UpdateMsg.generateMsg(op);
+    DeleteMsg updateMsg = (DeleteMsg) LDAPUpdateMsg.generateMsg(op);
     assertEquals(msg.getChangeNumber(), updateMsg.getChangeNumber());
   }
 
@@ -415,7 +412,7 @@
     assertEquals(moddn1.getModifications(), moddn2.getModifications());
 
     // Create an update message from this op
-    ModifyDNMsg updateMsg = (ModifyDNMsg) UpdateMsg.generateMsg(localOp);
+    ModifyDNMsg updateMsg = (ModifyDNMsg) LDAPUpdateMsg.generateMsg(localOp);
     assertEquals(msg.getChangeNumber(), updateMsg.getChangeNumber());
   }
 
@@ -518,7 +515,7 @@
 
 
     // Create an update message from this op
-    AddMsg updateMsg = (AddMsg) UpdateMsg.generateMsg(addOp);
+    AddMsg updateMsg = (AddMsg) LDAPUpdateMsg.generateMsg(addOp);
     assertEquals(msg.getChangeNumber(), updateMsg.getChangeNumber());
   }
 
@@ -615,12 +612,11 @@
   @DataProvider(name="createServerStartData")
   public Object [][] createServerStartData() throws Exception
   {
-    DN baseDN = DN.decode(TEST_ROOT_DN_STRING);
+    String baseDN = TEST_ROOT_DN_STRING;
     ServerState state = new ServerState();
     state.update(new ChangeNumber((long)0, 0,(short)0));
     Object[] set1 = new Object[] {(short)1, baseDN, 0, state, 0L, false, (byte)0};
 
-    baseDN = DN.decode(TEST_ROOT_DN_STRING);
     state = new ServerState();
     state.update(new ChangeNumber((long)75, 5,(short)263));
     Object[] set2 = new Object[] {(short)16, baseDN, 100, state, 1248L, true, (byte)31};
@@ -633,7 +629,7 @@
    * by checking that : msg == new ServerStartMsg(msg.getBytes()).
    */
   @Test(dataProvider="createServerStartData")
-  public void serverStartMsgTest(short serverId, DN baseDN, int window,
+  public void serverStartMsgTest(short serverId, String baseDN, int window,
          ServerState state, long genId, boolean sslEncryption, byte groupId) throws Exception
   {
     ServerStartMsg msg = new ServerStartMsg(serverId, baseDN,
@@ -659,12 +655,11 @@
   @DataProvider(name="createReplServerStartData")
   public Object [][] createReplServerStartData() throws Exception
   {
-    DN baseDN = DN.decode(TEST_ROOT_DN_STRING);
+    String baseDN = TEST_ROOT_DN_STRING;
     ServerState state = new ServerState();
     state.update(new ChangeNumber((long)0, 0,(short)0));
     Object[] set1 = new Object[] {(short)1, baseDN, 0, "localhost:8989", state, 0L, (byte)0, 0};
 
-    baseDN = DN.decode(TEST_ROOT_DN_STRING);
     state = new ServerState();
     state.update(new ChangeNumber((long)75, 5,(short)263));
     Object[] set2 = new Object[] {(short)16, baseDN, 100, "anotherHost:1025", state, 1245L, (byte)25, 3456};
@@ -677,7 +672,7 @@
    * by checking that : msg == new ReplServerStartMsg(msg.getBytes()).
    */
   @Test(dataProvider="createReplServerStartData")
-  public void replServerStartMsgTest(short serverId, DN baseDN, int window,
+  public void replServerStartMsgTest(short serverId, String baseDN, int window,
          String url, ServerState state, long genId, byte groupId, int degTh) throws Exception
   {
     ReplServerStartMsg msg = new ReplServerStartMsg(serverId,
@@ -734,7 +729,7 @@
 
     List<String> urls3 = new ArrayList<String>();
     urls3.add("ldaps://host:port/dc=foo??sub?(sn=One Entry)");
-    
+
     List<String> urls4 = new ArrayList<String>();
     urls4.add("ldaps://host:port/dc=foobar1??sub?(sn=Another Entry 1)");
     urls4.add("ldaps://host:port/dc=foobar2??sub?(sn=Another Entry 2)");
@@ -747,7 +742,7 @@
 
     DSInfo dsInfo3 = new DSInfo((short)2436, (short)591, (long)0, ServerStatus.NORMAL_STATUS,
       false, AssuredMode.SAFE_READ_MODE, (byte)17, (byte)0, urls3);
-    
+
     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);
 
@@ -862,7 +857,7 @@
     assertTrue(msg.getSafeDataLevel() == newMsg.getSafeDataLevel());
     assertEquals(msg.getReferralsURLs(), newMsg.getReferralsURLs());
   }
-  
+
   /**
    * Provider for the ChangeStatusMsg test
    */
@@ -897,6 +892,7 @@
   {
     HeartbeatMsg msg = new HeartbeatMsg();
     HeartbeatMsg newMsg = new HeartbeatMsg(msg.getBytes());
+    assertNotNull(newMsg);
   }
 
   /**
@@ -909,7 +905,7 @@
     ResetGenerationIdMsg newMsg = new ResetGenerationIdMsg(msg.getBytes());
     assertEquals(msg.getGenerationId(), newMsg.getGenerationId());
   }
-  
+
   /**
    * Test MonitorRequestMsg encoding and decoding.
    */
@@ -918,6 +914,8 @@
   {
     MonitorRequestMsg msg = new MonitorRequestMsg((short)1,(short)2);
     MonitorRequestMsg newMsg = new MonitorRequestMsg(msg.getBytes());
+    assertEquals(newMsg.getDestination(), 2);
+    assertEquals(newMsg.getsenderID(), 1);
   }
 
   /**
@@ -963,14 +961,14 @@
     msg.setServerState(sid1, s1, now+1, true);
     msg.setServerState(sid2, s2, now+2, true);
     msg.setServerState(sid3, s3, now+3, false);
-    
+
     byte[] b = msg.getBytes();
     MonitorMsg newMsg = new MonitorMsg(b);
 
     assertEquals(rsState, msg.getReplServerDbState());
-    assertEquals(newMsg.getReplServerDbState().toString(), 
+    assertEquals(newMsg.getReplServerDbState().toString(),
         msg.getReplServerDbState().toString());
-    
+
     Iterator<Short> it = newMsg.ldapIterator();
     while (it.hasNext())
     {
@@ -983,7 +981,7 @@
       }
       else if (sid == sid2)
       {
-        assertEquals(s.toString(), s2.toString());        
+        assertEquals(s.toString(), s2.toString());
         assertEquals((Long)(now+2), newMsg.getLDAPApproxFirstMissingDate(sid), "");
       }
       else
@@ -1025,7 +1023,7 @@
         "objectclass: top\n" +
         "objectclass: ds-task\n" +
         "objectclass: ds-task-initialize\n" +
-        "ds-task-class-name: org.opends.server.tasks.InitializeTask\n" +
+        "ds-task-class-name: org.opends.server.replication.api.InitializeTask\n" +
         "ds-task-initialize-domain-dn: " + TEST_ROOT_DN_STRING  + "\n" +
         "ds-task-initialize-source: 1\n");
     short sender = 1;
@@ -1047,7 +1045,7 @@
     short sender = 1;
     short target = 2;
     InitializeRequestMsg msg = new InitializeRequestMsg(
-        DN.decode(TEST_ROOT_DN_STRING), sender, target);
+        TEST_ROOT_DN_STRING, sender, target);
     InitializeRequestMsg newMsg = new InitializeRequestMsg(msg.getBytes());
     assertEquals(msg.getsenderID(), newMsg.getsenderID());
     assertEquals(msg.getDestination(), newMsg.getDestination());
@@ -1064,10 +1062,9 @@
     short targetID = 2;
     short requestorID = 3;
     long entryCount = 4;
-    DN baseDN = DN.decode(TEST_ROOT_DN_STRING);
 
     InitializeTargetMsg msg = new InitializeTargetMsg(
-        baseDN, senderID, targetID, requestorID, entryCount);
+        TEST_ROOT_DN_STRING, senderID, targetID, requestorID, entryCount);
     InitializeTargetMsg newMsg = new InitializeTargetMsg(msg.getBytes());
     assertEquals(msg.getsenderID(), newMsg.getsenderID());
     assertEquals(msg.getDestination(), newMsg.getDestination());
@@ -1079,7 +1076,7 @@
     assertEquals(targetID, newMsg.getDestination());
     assertEquals(requestorID, newMsg.getRequestorID());
     assertEquals(entryCount, newMsg.getEntryCount());
-    assertTrue(baseDN.equals(newMsg.getBaseDN())) ;
+    assertTrue(TEST_ROOT_DN_STRING.equals(newMsg.getBaseDN())) ;
 
   }
 
@@ -1108,4 +1105,19 @@
     assertEquals(msg.getMsgID(), newMsg.getMsgID());
     assertEquals(msg.getDetails(), newMsg.getDetails());
   }
+
+  /**
+   * Test Generic UpdateMsg
+   */
+  @Test
+  public void UpdateMsgTest() throws Exception
+  {
+    final String test = "string used for test";
+    UpdateMsg msg =
+      new UpdateMsg(
+          new ChangeNumber((long) 1, 2 , (short)3),
+          test.getBytes());
+    UpdateMsg newMsg = new UpdateMsg(msg.getBytes());
+    assertEquals(test.getBytes(), newMsg.getPayload());
+  }
 }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/DbHandlerTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/DbHandlerTest.java
index 6861761..ff51863 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/DbHandlerTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/DbHandlerTest.java
@@ -34,7 +34,6 @@
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.replication.common.ChangeNumberGenerator;
 import org.opends.server.replication.protocol.DeleteMsg;
-import org.opends.server.types.DN;
 import org.testng.annotations.Test;
 import static org.testng.Assert.*;
 import static org.opends.server.TestCaseUtils.*;
@@ -79,7 +78,7 @@
 
       dbEnv = new ReplicationDbEnv(path, replicationServer);
 
-      handler = new DbHandler((short) 1, DN.decode(TEST_ROOT_DN_STRING),
+      handler = new DbHandler((short) 1, TEST_ROOT_DN_STRING,
         replicationServer, dbEnv, 5000);
 
       ChangeNumberGenerator gen = new ChangeNumberGenerator((short) 1, 0);
@@ -173,7 +172,7 @@
       dbEnv = new ReplicationDbEnv(path, replicationServer);
 
       handler =
-        new DbHandler((short) 1, DN.decode(TEST_ROOT_DN_STRING),
+        new DbHandler((short) 1, TEST_ROOT_DN_STRING,
         replicationServer, dbEnv, 5000);
 
       // Creates changes added to the dbHandler
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/MonitorTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/MonitorTest.java
index 1a38260..962af1c 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/MonitorTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/MonitorTest.java
@@ -50,9 +50,9 @@
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.service.ReplicationBroker;
 import org.opends.server.replication.common.ChangeNumberGenerator;
-import org.opends.server.replication.plugin.ReplicationBroker;
-import org.opends.server.replication.plugin.ReplicationDomain;
+import org.opends.server.replication.plugin.LDAPReplicationDomain;
 import org.opends.server.replication.protocol.AddMsg;
 import org.opends.server.replication.protocol.ReplicationMsg;
 import org.opends.server.replication.protocol.SocketSession;
@@ -93,7 +93,7 @@
   private ReplicationServer replServer2 = null;
   private ReplicationServer replServer3 = null;
   private boolean emptyOldChanges = true;
-  ReplicationDomain replDomain = null;
+  LDAPReplicationDomain replDomain = null;
   SocketSession ssSession = null;
   boolean ssShutdownRequested = false;
   protected String[] updatedEntries;
@@ -240,7 +240,7 @@
         "Unable to add the synchronized server");
       configEntryList.add(synchroServerEntry.getDN());
 
-      replDomain = ReplicationDomain.retrievesReplicationDomain(baseDn);
+      replDomain = LDAPReplicationDomain.retrievesReplicationDomain(baseDn);
 
       if (replDomain != null)
       {
@@ -393,7 +393,6 @@
 
       debugInfo("Connecting DS to replServer1");
       connectServer1ToChangelog(changelog1ID);
-      Thread.sleep(1500);
 
       try
       {
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerDynamicConfTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerDynamicConfTest.java
index 028601c..d1aedcb 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerDynamicConfTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerDynamicConfTest.java
@@ -32,13 +32,13 @@
 
 import org.opends.server.TestCaseUtils;
 import org.opends.server.replication.ReplicationTestCase;
-import org.opends.server.replication.plugin.ReplicationBroker;
+import org.opends.server.replication.service.ReplicationBroker;
 import org.opends.server.types.DN;
 import org.testng.annotations.Test;
 import static org.opends.server.TestCaseUtils.*;
 
 /**
- * Tests that we can dynamically modify the configuration of replicationServer 
+ * Tests that we can dynamically modify the configuration of replicationServer
  */
 
 public class ReplicationServerDynamicConfTest extends ReplicationTestCase
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java
index 45560d0..948444d 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java
@@ -63,12 +63,12 @@
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.protocols.ldap.LDAPFilter;
 import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.service.ReplicationBroker;
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.replication.common.ChangeNumberGenerator;
 import org.opends.server.replication.common.ServerState;
 import org.opends.server.replication.common.ServerStatus;
 import org.opends.server.replication.plugin.MultimasterReplication;
-import org.opends.server.replication.plugin.ReplicationBroker;
 import org.opends.server.replication.plugin.ReplicationServerListener;
 import org.opends.server.replication.protocol.AddMsg;
 import org.opends.server.replication.protocol.DeleteMsg;
@@ -192,14 +192,12 @@
   {
     replicationServer.clearDb();
     changelogBasic();
-    multipleWriterMultipleReader();
     newClientLateServer1();
     newClient();
     newClientWithFirstChanges();
     newClientWithChangefromServer1();
     newClientWithChangefromServer2();
     newClientWithUnknownChanges();
-    oneWriterMultipleReader();
     changelogChaining();
     stopChangelog();
     exportBackend();
@@ -207,7 +205,7 @@
     windowProbeTest();
     replicationServerConnected();
   }
-  
+
   /**
    * This test allows to check the behavior of the Replication Server
    * when the DS disconnect and reconnect again.
@@ -255,7 +253,7 @@
       server2 = openReplicationSession(
           DN.decode(TEST_ROOT_DN_STRING), (short) 2, 100, replicationServerPort,
           1000, true);
-      
+
       assertTrue(server1.isConnected());
       assertTrue(server2.isConnected());
 
@@ -328,10 +326,6 @@
                       "other-uid");
       server2.publish(msg);
       msg2 = server1.receive();
-      if (!(msg2 instanceof TopologyMsg))
-        fail("ReplicationServer basic : incorrect message type received: " +
-          msg2.getClass().toString() + ": content: " + msg2.toString());
-      msg2 = server1.receive();
       server1.updateWindowAfterReplay();
       if (msg2 instanceof DeleteMsg)
       {
@@ -359,7 +353,7 @@
       else
         fail("ReplicationServer basic : incorrect message type received: " +
           msg2.getClass().toString() + ": content: " + msg2.toString());
-      
+
       debugInfo("Ending changelogBasic");
     }
     finally
@@ -564,9 +558,14 @@
    * This test is configured by a relatively low stress
    * but can be changed using TOTAL_MSG and CLIENT_THREADS consts.
    */
-  private void oneWriterMultipleReader() throws Exception
+  @Test(enabled=true)
+  public void oneWriterMultipleReader() throws Exception
   {
     debugInfo("Starting oneWriterMultipleReader");
+
+    replicationServer.clearDb();
+    TestCaseUtils.initializeTestBackend(true);
+
     ReplicationBroker server = null;
     BrokerReader reader = null;
     int TOTAL_MSG = 1000;     // number of messages to send during the test
@@ -638,6 +637,9 @@
         if (clientBroker[i] != null)
           clientBroker[i].stop();
       }
+
+      replicationServer.clearDb();
+      TestCaseUtils.initializeTestBackend(true);
     }
   }
 
@@ -652,7 +654,8 @@
    * This test is configured for a relatively low stress
    * but can be changed using TOTAL_MSG and THREADS consts.
    */
-  private void multipleWriterMultipleReader() throws Exception
+  @Test()
+  public void multipleWriterMultipleReader() throws Exception
   {
     debugInfo("Starting multipleWriterMultipleReader");
     final int TOTAL_MSG = 1000;   // number of messages to send during the test
@@ -663,6 +666,9 @@
     BrokerReader reader[] = new BrokerReader[THREADS];
     ReplicationBroker broker[] = new ReplicationBroker[THREADS];
 
+    replicationServer.clearDb();
+    TestCaseUtils.initializeTestBackend(true);
+
     try
     {
       /*
@@ -711,6 +717,9 @@
         if (broker[i] != null)
           broker[i].stop();
       }
+
+      replicationServer.clearDb();
+      TestCaseUtils.initializeTestBackend(true);
     }
   }
 
@@ -789,7 +798,7 @@
         //              client2 will be created later
         broker1 = openReplicationSession(DN.decode(TEST_ROOT_DN_STRING),
              brokerIds[0], 100, changelogPorts[0], 1000, !emptyOldChanges);
-        
+
         assertTrue(broker1.isConnected());
 
         if (itest == 0)
@@ -975,7 +984,7 @@
     {
       // send a ServerStartMsg with an empty ServerState.
       ServerStartMsg msg =
-        new ServerStartMsg((short) 1723, DN.decode(TEST_ROOT_DN_STRING),
+        new ServerStartMsg((short) 1723, TEST_ROOT_DN_STRING,
             0, 0, 0, 0, WINDOW, (long) 5000, new ServerState(),
             ProtocolVersion.getCurrentVersion(), 0, sslEncryption, (byte)-1);
       session.publish(msg);
@@ -1021,7 +1030,7 @@
       // received.
       DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
       msg = new ServerStartMsg(
-          (short) 1724, baseDn,
+          (short) 1724, TEST_ROOT_DN_STRING,
           0, 0, 0, 0, WINDOW, (long) 5000, replServerState,
           ProtocolVersion.getCurrentVersion(),
           ReplicationTestCase.getGenerationId(baseDn),
@@ -1072,7 +1081,7 @@
    * @throws Exception If the environment could not be set up.
    */
   @AfterClass
- 
+
   public void classCleanUp() throws Exception
   {
     callParanoiaCheck = false;
@@ -1410,12 +1419,7 @@
     * Testing searches on the backend of the replication server.
     * @throws Exception
     */
-   // TODO: this test disabled as testReplicationBackendACIs() is failing
-   // : anonymous search returns entries from replication backend whereas it
-   // should not. Probably a previous test in the nightlytests suite is
-   // removing/modifying some ACIs...When problem foound, we have to re-enable
-   // this test.
-   @Test(enabled=false)
+   @Test(enabled=true)
    public void searchBackend() throws Exception
    {
      debugInfo("Starting searchBackend");
@@ -1494,7 +1498,13 @@
            LDAPFilter.decode("(changetype=*)"));
        assertEquals(op.getResultCode(), ResultCode.NO_SUCH_OBJECT);
 
-       testReplicationBackendACIs();
+       // TODO:  testReplicationBackendACIs() is disabled because it
+       // is currently failing when run in the nightly target.
+       // anonymous search returns entries from replication backend whereas it
+       // should not. Probably a previous test in the nightlytests suite is
+       // removing/modifying some ACIs...When problem foound, we have to re-enable
+       // this test.
+       // testReplicationBackendACIs();
 
        // General search
        op = connection.processSearch(
@@ -1555,10 +1565,10 @@
        assertEquals(op.getSearchEntries().size(), 1);
 
        /*
-        * It would be nice to be have the abilities to search for 
-        * entries in the replication backend using the DN on which the 
+        * It would be nice to be have the abilities to search for
+        * entries in the replication backend using the DN on which the
         * operation was done as the search criteria.
-        * This is not possible yet, this part of the test is therefore 
+        * This is not possible yet, this part of the test is therefore
         * disabled.
         *
         * debugInfo("Query / searchBase");
@@ -1604,7 +1614,7 @@
              attrs2);
        assertEquals(op.getResultCode(), ResultCode.SUCCESS);
        assertEquals(op.getSearchEntries().size(), 5);
-       
+
        debugInfo("Successfully ending searchBackend");
 
      } finally {
@@ -1759,7 +1769,7 @@
 
          broker2 = openReplicationSession(DN.decode(TEST_ROOT_DN_STRING),
               brokerIds[1], 100, changelogPorts[1], 1000, emptyOldChanges);
-         
+
          assertTrue(broker1.isConnected());
          assertTrue(broker2.isConnected());
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeReplicationDomain.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeReplicationDomain.java
new file mode 100644
index 0000000..76c2cae
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeReplicationDomain.java
@@ -0,0 +1,149 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.service;
+
+import org.opends.server.types.ResultCode;
+import java.io.IOException;
+import java.util.concurrent.BlockingQueue;
+import org.opends.server.config.ConfigException;
+import java.util.Collection;
+import java.io.InputStream;
+import java.io.OutputStream;
+import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.types.DirectoryException;
+import static org.opends.messages.ReplicationMessages.*;
+
+/**
+ * This class is the minimum implementation of a Concrete ReplicationDomain
+ * used to test the Generic Replication Service.
+ */
+public class FakeReplicationDomain extends ReplicationDomain
+{
+  // A blocking queue that is used to send the UpdateMsg received from
+  // the Replication Service.
+  BlockingQueue<UpdateMsg> queue = null;
+
+  // A string that will be exported should exportBackend be called.
+  String exportString = null;
+
+  // A StringBuilder that will be used to build a build a new String should the
+  // import be called.
+  StringBuilder importString = null;
+
+  private int exportedEntryCount;
+
+  public FakeReplicationDomain(
+      String serviceID,
+      short serverID,
+      Collection<String> replicationServers,
+      int window,
+      long heartbeatInterval,
+      BlockingQueue<UpdateMsg> queue) throws ConfigException
+  {
+    super(serviceID, serverID);
+    startPublishService(replicationServers, window, heartbeatInterval);
+    startListenService();
+    this.queue = queue;
+  }
+
+  public FakeReplicationDomain(
+      String serviceID,
+      short serverID,
+      Collection<String> replicationServers,
+      int window,
+      long heartbeatInterval,
+      String exportString,
+      StringBuilder importString,
+      int exportedEntryCount) throws ConfigException
+  {
+    super(serviceID, serverID);
+    startPublishService(replicationServers, window, heartbeatInterval);
+    startListenService();
+    this.exportString = exportString;
+    this.importString = importString;
+    this.exportedEntryCount = exportedEntryCount;
+  }
+
+  @Override
+  public long countEntries() throws DirectoryException
+  {
+    return exportedEntryCount;
+  }
+
+  @Override
+  protected void exportBackend(OutputStream output) throws DirectoryException
+  {
+    try
+    {
+      output.write(exportString.getBytes());
+      output.flush();
+      output.close();
+    }
+    catch (IOException e)
+    {
+      throw new DirectoryException(ResultCode.OPERATIONS_ERROR,
+          ERR_BACKEND_EXPORT_ENTRY.get("", ""));
+    }
+
+  }
+
+  @Override
+  public long getGenerationID()
+  {
+    return 1;
+  }
+
+  @Override
+  protected void importBackend(InputStream input) throws DirectoryException
+  {
+    byte[] buffer = new byte[1000];
+
+    int ret;
+    do
+    {
+      try
+      {
+        ret = input.read(buffer, 0, 1000);
+      } catch (IOException e)
+      {
+        throw new DirectoryException(
+            ResultCode.OPERATIONS_ERROR,
+            ERR_BACKEND_EXPORT_ENTRY.get("", ""));
+      }
+      importString.append(new String(buffer, 0, ret));
+    }
+    while (ret >= 0);
+  }
+
+  @Override
+  public boolean processUpdate(UpdateMsg updateMsg)
+  {
+    if (queue != null)
+      queue.add(updateMsg);
+    return true;
+  }
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeStressReplicationDomain.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeStressReplicationDomain.java
new file mode 100644
index 0000000..86f6263
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeStressReplicationDomain.java
@@ -0,0 +1,159 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.service;
+
+import org.opends.server.types.ResultCode;
+import java.io.IOException;
+import java.util.concurrent.BlockingQueue;
+import org.opends.server.config.ConfigException;
+import java.util.Collection;
+import java.io.InputStream;
+import java.io.OutputStream;
+import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.types.DirectoryException;
+import static org.opends.messages.ReplicationMessages.*;
+
+/**
+ * This class is the minimum implementation of a Concrete ReplicationDomain
+ * used to test the Generic Replication Service.
+ */
+public class FakeStressReplicationDomain extends ReplicationDomain
+{
+  // A blocking queue that is used to send the UpdateMsg received from
+  // the Replication Service.
+  BlockingQueue<UpdateMsg> queue = null;
+
+  // A string that will be exported should exportBackend be called.
+  String exportString = null;
+
+  // A StringBuilder that will be used to build a build a new String should the
+  // import be called.
+  StringBuilder importString = null;
+
+  public FakeStressReplicationDomain(
+      String serviceID,
+      short serverID,
+      Collection<String> replicationServers,
+      int window,
+      long heartbeatInterval,
+      BlockingQueue<UpdateMsg> queue) throws ConfigException
+  {
+    super(serviceID, serverID);
+    startPublishService(replicationServers, window, heartbeatInterval);
+    startListenService();
+    this.queue = queue;
+  }
+
+  public FakeStressReplicationDomain(
+      String serviceID,
+      short serverID,
+      Collection<String> replicationServers,
+      int window,
+      long heartbeatInterval,
+      String exportString,
+      StringBuilder importString) throws ConfigException
+  {
+    super(serviceID, serverID);
+    startPublishService(replicationServers, window, heartbeatInterval);
+    startListenService();
+    this.exportString = exportString;
+    this.importString = importString;
+  }
+
+  final int IMPORT_SIZE = 100000000;
+  @Override
+  public long countEntries() throws DirectoryException
+  {
+    return IMPORT_SIZE;
+  }
+
+  @Override
+  protected void exportBackend(OutputStream output) throws DirectoryException
+  {
+    System.out.println("export started");
+    try
+    {
+      for (int i=0; i< IMPORT_SIZE; i++)
+      {
+        output.write("this is a long key like a dn or something similar: value\n\n".getBytes());
+      }
+      output.flush();
+      output.close();
+    }
+    catch (IOException e)
+    {
+      throw new DirectoryException(ResultCode.OPERATIONS_ERROR,
+          ERR_BACKEND_EXPORT_ENTRY.get("", ""));
+    }
+    System.out.println("export finished");
+  }
+
+  @Override
+  public long getGenerationID()
+  {
+    return 1;
+  }
+
+  @Override
+  protected void importBackend(InputStream input) throws DirectoryException
+  {
+    long startDate = System.currentTimeMillis();
+    System.out.println("import started : " + startDate);
+
+    byte[] buffer = new byte[10000];
+    int ret;
+    int count = 0;
+    do
+    {
+      try
+      {
+        ret = input.read(buffer, 0, 10000);
+      } catch (IOException e)
+      {
+        e.printStackTrace();
+        throw new DirectoryException(
+            ResultCode.OPERATIONS_ERROR,
+            ERR_BACKEND_EXPORT_ENTRY.get("", ""));
+      }
+      count++;
+    }
+    while (ret >= 0);
+
+    long endDate = System.currentTimeMillis();
+    System.out.println("import end : " + endDate);
+    System.out.println("import duration (sec): " + (endDate - startDate) / 1000);
+    System.out.println("import count: " + count);
+  }
+
+  @Override
+  public boolean processUpdate(UpdateMsg updateMsg)
+  {
+    if (queue != null)
+      queue.add(updateMsg);
+    return true;
+  }
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java
new file mode 100644
index 0000000..1bac2f7
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java
@@ -0,0 +1,317 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.service;
+
+import static org.testng.Assert.*;
+
+import java.util.TreeSet;
+
+import java.util.concurrent.LinkedBlockingQueue;
+
+import java.net.ServerSocket;
+import java.util.ArrayList;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import org.opends.server.TestCaseUtils;
+import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.common.DSInfo;
+import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.replication.server.ReplServerFakeConfiguration;
+import org.opends.server.replication.server.ReplicationServer;
+import org.testng.annotations.Test;
+
+/**
+ * Test the Generic Replication Service.
+ */
+public class ReplicationDomainTest extends ReplicationTestCase
+{
+  /**
+   * Test that a ReplicationDomain is able to publish and receive UpdateMsg.
+   */
+  @Test(enabled=true)
+  public void publishAndReceive() throws Exception
+  {
+    String testService = "test";
+    ReplicationServer replServer = null;
+    int replServerID = 10;
+    FakeReplicationDomain domain1 = null;
+    FakeReplicationDomain domain2 = null;
+
+    try
+    {
+      // find  a free port for the replicationServer
+      ServerSocket socket = TestCaseUtils.bindFreePort();
+      int replServerPort = socket.getLocalPort();
+      socket.close();
+
+      ReplServerFakeConfiguration conf =
+        new ReplServerFakeConfiguration(
+            replServerPort, "ReplicationDomainTestDb",
+            0, replServerID, 0, 100, null);
+
+      replServer = new ReplicationServer(conf);
+      ArrayList<String> servers = new ArrayList<String>(1);
+      servers.add("localhost:" + replServerPort);
+
+      BlockingQueue<UpdateMsg> rcvQueue1 = new LinkedBlockingQueue<UpdateMsg>();
+      domain1 = new FakeReplicationDomain(
+          testService, (short) 1, servers, 100, 1000, rcvQueue1);
+
+      BlockingQueue<UpdateMsg> rcvQueue2 = new LinkedBlockingQueue<UpdateMsg>();
+      domain2 = new FakeReplicationDomain(
+          testService, (short) 2, servers, 100, 1000, rcvQueue2);
+
+      /*
+       * Publish a message from domain1,
+       * Check that domain2 receives it shortly after.
+       */
+      byte[] test = {1, 2, 3 ,4, 0, 1, 2, 3, 4, 5};
+      domain1.publish(test);
+
+      UpdateMsg rcvdMsg = rcvQueue2.poll(1, TimeUnit.SECONDS);
+      assertNotNull(rcvdMsg);
+      assertEquals(test, rcvdMsg.getPayload());
+    }
+    finally
+    {
+      if (domain1 != null)
+        domain1.disableService();
+
+      if (domain2 != null)
+        domain2.disableService();
+
+      if (replServer != null)
+        replServer.remove();
+    }
+  }
+
+  /**
+   * Test that a ReplicationDomain is able to export and import its database.
+   */
+  @Test(enabled=true)
+  public void exportAndImport() throws Exception
+  {
+    final int ENTRYCOUNT=5000;
+    String testService = "test";
+    ReplicationServer replServer = null;
+    int replServerID = 11;
+    FakeReplicationDomain domain1 = null;
+    FakeReplicationDomain domain2 = null;
+
+    try
+    {
+      // find  a free port for the replicationServer
+      ServerSocket socket = TestCaseUtils.bindFreePort();
+      int replServerPort = socket.getLocalPort();
+      socket.close();
+
+      ReplServerFakeConfiguration conf =
+        new ReplServerFakeConfiguration(
+            replServerPort, "ReplicationDomainTestDb",
+            0, replServerID, 0, 100, null);
+
+      replServer = new ReplicationServer(conf);
+      ArrayList<String> servers = new ArrayList<String>(1);
+      servers.add("localhost:" + replServerPort);
+
+      StringBuilder exportedDataBuilder = new StringBuilder();
+      for (int i =0; i<ENTRYCOUNT; i++)
+      {
+        exportedDataBuilder.append("key : value"+i+"\n\n");
+      }
+      String exportedData=exportedDataBuilder.toString();
+      domain1 = new FakeReplicationDomain(
+          testService, (short) 1, servers,
+          100, 0, exportedData, null, ENTRYCOUNT);
+
+      StringBuilder importedData = new StringBuilder();
+      domain2 = new FakeReplicationDomain(
+          testService, (short) 2, servers, 100, 0,
+          null, importedData, 0);
+
+      /*
+       * Trigger a total update from domain1 to domain2.
+       * Check that the exported data is correctly received on domain2.
+       */
+      for (DSInfo remoteDS : domain2.getDsList())
+      {
+        if (remoteDS.getDsId() != domain2.getServerId())
+        {
+          domain2.initializeFromRemote(remoteDS.getDsId() , null);
+          break;
+        }
+      }
+
+      int count = 0;
+      while ((importedData.length() < exportedData.length()) && (count < 500))
+      {
+        count ++;
+        Thread.sleep(100);
+      }
+      assertTrue(domain2.getLeftEntryCount() == 0,
+          "LeftEntryCount for export is " + domain2.getLeftEntryCount());
+      assertTrue(domain1.getLeftEntryCount() == 0,
+          "LeftEntryCount for import is " + domain1.getLeftEntryCount());
+      assertEquals(importedData.length(), exportedData.length());
+      assertEquals(importedData.toString(), exportedData);
+    }
+    finally
+    {
+      if (domain1 != null)
+        domain1.disableService();
+
+      if (domain2 != null)
+        domain2.disableService();
+
+      if (replServer != null)
+        replServer.remove();
+    }
+  }
+
+  /**
+   * Sender side of the Total Update Perf test.
+   * The goal of this test is to measure the performance
+   * of the total update code.
+   * It is not intended to be run as part of the daily unit test but
+   * should only be used manually by developer in need of testing the
+   * performance improvement or non-regression of the total update code.
+   * Use this test in combination with the receiverInitialize() :
+   *   - enable the test
+   *   - start the senderInitialize first using
+   *     ./build.sh \
+   *        -Dorg.opends.test.suppressOutput=false \
+   *        -Dtest.methods=org.opends.server.replication.service.ReplicationDomainTest.senderInitialize test
+   *   - start the receiverInitialize second.
+   *   - you may want to change  HOST1 and HOST2 to use 2 different hosts
+   *     if you don't want to do a loopback test.
+   *   - don't forget to disable again the tests after running them
+   */
+  final String HOST1 = "localhost:";
+  final String HOST2 = "localhost:";
+  final int SENDERPORT = 10102;
+  final int RECEIVERPORT = 10101;
+
+  @Test(enabled=false)
+  public void senderInitialize() throws Exception
+  {
+    String testService = "test";
+    ReplicationServer replServer = null;
+    int replServerID = 12;
+    FakeStressReplicationDomain domain1 = null;
+
+    try
+    {
+      TreeSet<String> servers = new TreeSet<String>();
+      servers.add(HOST1 + SENDERPORT);
+      servers.add(HOST2 + RECEIVERPORT);
+
+      ReplServerFakeConfiguration conf =
+        new ReplServerFakeConfiguration(
+            SENDERPORT, "ReplicationDomainTestDb",
+            0, replServerID, 0, 100, servers);
+
+      replServer = new ReplicationServer(conf);
+
+      BlockingQueue<UpdateMsg> rcvQueue1 = new LinkedBlockingQueue<UpdateMsg>();
+      domain1 = new FakeStressReplicationDomain(
+          testService, (short) 2, servers, 100, 1000, rcvQueue1);
+
+      System.out.println("waiting");
+      Thread.sleep(1000000000);
+    }
+    finally
+    {
+      if (domain1 != null)
+        domain1.disableService();
+
+      if (replServer != null)
+        replServer.remove();
+    }
+  }
+
+  /**
+   * See comments in senderInitialize() above
+   */
+  @Test(enabled=false)
+  public void receiverInitialize() throws Exception
+  {
+    String testService = "test";
+    ReplicationServer replServer = null;
+    int replServerID = 11;
+    FakeStressReplicationDomain domain1 = null;
+
+    try
+    {
+      TreeSet<String> servers = new TreeSet<String>();
+      servers.add(HOST1 + SENDERPORT);
+      servers.add(HOST2 + RECEIVERPORT);
+
+      ReplServerFakeConfiguration conf =
+        new ReplServerFakeConfiguration(
+            RECEIVERPORT, "ReplicationDomainTestDb",
+            0, replServerID, 0, 100, servers);
+
+      replServer = new ReplicationServer(conf);
+
+      BlockingQueue<UpdateMsg> rcvQueue1 = new LinkedBlockingQueue<UpdateMsg>();
+      domain1 = new FakeStressReplicationDomain(
+          testService, (short) 1, servers, 100, 100000, rcvQueue1);
+      /*
+       * Trigger a total update from domain1 to domain2.
+       * Check that the exported data is correctly received on domain2.
+       */
+      boolean alone = true;
+      while (alone)
+      {
+        for (DSInfo remoteDS : domain1.getDsList())
+        {
+          if (remoteDS.getDsId() != domain1.getServerId())
+          {
+            alone = false;
+            domain1.initializeFromRemote(remoteDS.getDsId() , null);
+            break;
+          }
+        }
+        if (alone)
+        {
+          System.out.println("trying...");
+          Thread.sleep(1000);
+        }
+      }
+      System.out.println("waiting");
+      Thread.sleep(10000000);
+    }
+    finally
+    {
+      if (domain1 != null)
+        domain1.disableService();
+
+      if (replServer != null)
+        replServer.remove();
+    }
+  }
+}

--
Gitblit v1.10.0