From 4fe72a4bef946169b0f50bc05bd9dc3b4b1131d3 Mon Sep 17 00:00:00 2001
From: pgamba <pgamba@localhost>
Date: Fri, 14 Aug 2009 12:37:19 +0000
Subject: [PATCH] Support for External change log compatible with draft-good-ldap-changelog-04.txt , March 2003

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java | 1884 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 1,637 insertions(+), 247 deletions(-)

diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java
index c0a82ef..785d5f6 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java
@@ -82,6 +82,7 @@
 import org.opends.server.protocols.ldap.SearchResultDoneProtocolOp;
 import org.opends.server.protocols.ldap.SearchResultEntryProtocolOp;
 import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.replication.common.ChangeNumberGenerator;
 import org.opends.server.replication.common.MultiDomainServerState;
 import org.opends.server.replication.common.ServerState;
 import org.opends.server.replication.plugin.DomainFakeCfg;
@@ -95,7 +96,9 @@
 import org.opends.server.replication.protocol.ModifyDnContext;
 import org.opends.server.replication.protocol.ModifyMsg;
 import org.opends.server.replication.protocol.ReplicationMsg;
+import org.opends.server.replication.protocol.StartECLSessionMsg;
 import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.replication.server.DraftCNDbHandler;
 import org.opends.server.replication.server.ExternalChangeLogSessionImpl;
 import org.opends.server.replication.server.ReplServerFakeConfiguration;
 import org.opends.server.replication.server.ReplicationServer;
@@ -118,15 +121,18 @@
 import org.opends.server.types.ModificationType;
 import org.opends.server.types.RDN;
 import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
 import org.opends.server.types.SearchResultEntry;
 import org.opends.server.types.SearchScope;
 import org.opends.server.util.LDIFWriter;
 import org.opends.server.util.TimeThread;
+import org.opends.server.workflowelement.externalchangelog.ECLSearchOperation;
 import org.opends.server.workflowelement.localbackend.LocalBackendModifyDNOperation;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+
 /**
  * Tests for the replicationServer code.
  */
@@ -142,12 +148,16 @@
   // The port of the replicationServer.
   private int replicationServerPort;
 
-  public static final String TEST_ROOT_DN_STRING2 = "o=test2";
-  public static final String TEST_BACKEND_ID2 = "test2";
+  private static final String TEST_ROOT_DN_STRING2 = "o=test2";
+  private static final String TEST_BACKEND_ID2 = "test2";
 
   // The LDAPStatistics object associated with the LDAP connection handler.
-  protected LDAPStatistics ldapStatistics;
-  
+  private LDAPStatistics ldapStatistics;
+
+  private ChangeNumber gblCN;
+
+  List<Control> NO_CONTROL = null;
+
   /**
    * Set up the environment for performing the tests in this Class.
    * Replication
@@ -181,7 +191,7 @@
 
     replicationServer = new ReplicationServer(conf1);;
     debugInfo("configure", "ReplicationServer created"+replicationServer);
-    
+
   }
 
   /**
@@ -190,25 +200,47 @@
   @Test(enabled=true)
   public void ECLReplicationServerTest()
   {
+    ECLOnPrivateBackend();replicationServer.clearDb();
     ECLRemoteEmpty();replicationServer.clearDb();
-    ECLDirectEmpty();replicationServer.clearDb();
-    ECLDirectAllOps();replicationServer.clearDb();
+    ECLEmpty();replicationServer.clearDb();
+    ECLAllOps();replicationServer.clearDb();
     ECLRemoteNonEmpty();replicationServer.clearDb();
-    ECLDirectMsg();replicationServer.clearDb();
-    ECLDirectPsearch(true);replicationServer.clearDb();
-    ECLDirectPsearch(false);replicationServer.clearDb();
-    ECLPrivateDirectMsg();replicationServer.clearDb();
+    ECLTwoDomains();replicationServer.clearDb();
+    ECLPsearch(true, false);replicationServer.clearDb();
+    ECLPsearch(false, false);replicationServer.clearDb();
+    ECLSimulPsearches();replicationServer.clearDb();
+
     // TODO:ECL Test SEARCH abandon and check everything shutdown and cleaned
     // TODO:ECL Test PSEARCH abandon and check everything shutdown and cleaned
     // TODO:ECL Test invalid DN in cookie returns UNWILLING + message
     // TODO:ECL Test notif control returned contains the cookie
     // TODO:ECL Test the attributes list and values returned in ECL entries
     // TODO:ECL Test search -s base, -s one
+    ChangeTimeHeartbeatTest();replicationServer.clearDb();
+    ECLFilterTest();
+
+    ECLCompatEmpty();
+    ECLCompatBadSeqnum();
+    ECLCompatWriteReadAllOps(1);
+    ECLCompatWriteReadAllOps(5);
+    ECLCompatReadFrom(6);
+    ECLCompatReadFromTo(5,7);
+    ECLCompatTestLimits(1,8);
+    ECLCompatPurge();
+    ECLCompatTestLimits(0,0);
+    ECLPsearch(true, true);replicationServer.clearDb();
+    ECLPsearch(false, true);
+    ECLFilterOnReplicationCsn();replicationServer.clearDb();
+    ECLSimulPsearches();replicationServer.clearDb();
+    
   }
 
   //=======================================================
-  // From 3 remote ECL session,
-  // Test DoneMsg is received from 1 suffix on each session.
+  // Objectives
+  //   - Test that everything id ok with no changes
+  // Procedure
+  //   - Does a SEARCH from 3 different remote ECL session,
+  //   - Verify  DoneMsg is received on each session.
   private void ECLRemoteEmpty()
   {
     String tn = "ECLRemoteEmpty";
@@ -244,9 +276,11 @@
       }
       while(!(msg instanceof DoneMsg));
       assertTrue(msgc==1,
-          "Ending " + tn + " with incorrect message type :" +
+          "Ending " + tn + " with incorrect message number :" +
           msg.getClass().getCanonicalName());
-      assertTrue(msg instanceof DoneMsg);        
+      assertTrue(msg instanceof DoneMsg,
+      "Ending " + tn + " with incorrect message type :" +
+      msg.getClass().getCanonicalName());
 
       // Test broker2 receives only Done
       msgc=0;
@@ -257,8 +291,11 @@
       }
       while(!(msg instanceof DoneMsg));
       assertTrue(msgc==1,
-          "Ending " + tn + " with incorrect message type :" +
+          "Ending " + tn + " with incorrect message number :" +
           msg.getClass().getCanonicalName());
+      assertTrue(msg instanceof DoneMsg,
+      "Ending " + tn + " with incorrect message type :" +
+      msg.getClass().getCanonicalName());
 
       // Test broker3 receives only Done
       msgc=0;
@@ -269,26 +306,31 @@
       }
       while(!(msg instanceof DoneMsg));
       assertTrue(msgc==1,
-          "Ending " + tn + " with incorrect message type :" +
+          "Ending " + tn + " with incorrect message number :" +
           msg.getClass().getCanonicalName());
+      assertTrue(msg instanceof DoneMsg,
+      "Ending " + tn + " with incorrect message type :" +
+      msg.getClass().getCanonicalName());
 
       server1.stop();
       server2.stop();
       server3.stop();
       sleep(500);
-      debugInfo(tn, "Ending test successfully\n\n");      
+      debugInfo(tn, "Ending test successfully\n\n");    
     }
     catch(Exception e)
     {
-      debugInfo(tn, "Ending test with exception:"
+      fail("Ending test " + tn +  " with exception:"
           +  stackTraceToSingleLineString(e));      
-      assertTrue(e == null);
     }
   }
-  
+
   //=======================================================
-  // From 1 remote ECL session,
-  // test simple update to be received from 2 suffixes
+  // Objectives
+  //   - Test that everything id ok with changes on 2 suffixes
+  // Procedure
+  //   - From 1 remote ECL session,
+  //   - Test simple update to be received from 2 suffixes
   private void ECLRemoteNonEmpty()
   {
     String tn = "ECLRemoteNonEmpty";
@@ -331,7 +373,7 @@
 
       // wait for the server to take these changes into account
       sleep(500);
-      
+
       // open ECL broker
       serverECL = openReplicationSession(
           DN.decode("cn=changelog"), (short)10,
@@ -343,11 +385,11 @@
       msg = serverECL.receive();
       ECLUpdateMsg eclu = (ECLUpdateMsg)msg;
       UpdateMsg u = eclu.getUpdateMsg();
-      debugInfo(tn, "RESULT:" + u.getChangeNumber());
+      debugInfo(tn, "RESULT:" + u.getChangeNumber() + " " + eclu.getCookie());
       assertTrue(u.getChangeNumber().equals(cn1), "RESULT:" + u.getChangeNumber());
       assertTrue(eclu.getCookie().equalsTo(new MultiDomainServerState(
-          "o=test:"+delMsg1.getChangeNumber()+";")));
-      
+          "o=test:"+delMsg1.getChangeNumber()+";o=test2:;")));
+
       // receive change 2 from suffix 2
       msg = serverECL.receive();
       eclu = (ECLUpdateMsg)msg;
@@ -355,8 +397,8 @@
       debugInfo(tn, "RESULT:" + u.getChangeNumber());
       assertTrue(u.getChangeNumber().equals(cn2), "RESULT:" + u.getChangeNumber());
       assertTrue(eclu.getCookie().equalsTo(new MultiDomainServerState(
-      "o=test2:"+delMsg2.getChangeNumber()+";"+
-      "o=test:"+delMsg1.getChangeNumber()+";")));
+          "o=test2:"+delMsg2.getChangeNumber()+";"+
+          "o=test:"+delMsg1.getChangeNumber()+";")));
 
       // receive Done
       msg = serverECL.receive();
@@ -372,35 +414,23 @@
     }
     catch(Exception e)
     {
-      debugInfo(tn, "Ending test with exception:"
+      fail("Ending test " + tn + " with exception:"
           +  stackTraceToSingleLineString(e));      
-      assertTrue(e == null);
     }
   }
-  
+
   /**
    * From embedded ECL (no remote session)
    * With empty RS, simple search should return only root entry.
    */
-  private void ECLDirectEmpty()
+  private void ECLEmpty()
   {
-    String tn = "ECLDirectEmpty";
+    String tn = "ECLEmpty";
     debugInfo(tn, "Starting test\n\n");
 
     try
     {
       // search on 'cn=changelog'
-      InternalSearchOperation op = connection.processSearch(
-        ByteString.valueOf("cn=changelog"),
-        SearchScope.WHOLE_SUBTREE,
-        LDAPFilter.decode("(objectclass=*)"));
-
-      // Error because no cookie
-      assertEquals(
-          op.getResultCode(), ResultCode.OPERATIONS_ERROR,
-          op.getErrorMessage().toString());
-
-      // search on 'cn=changelog'
       InternalSearchOperation op2 = connection.processSearch(
           ByteString.valueOf("cn=changelog"),
           SearchScope.WHOLE_SUBTREE,
@@ -409,7 +439,7 @@
           false,
           LDAPFilter.decode("(objectclass=*)"),
           new LinkedHashSet<String>(0),
-          getControls(""),
+          createControls(""),
           null);
 
       // success
@@ -423,13 +453,17 @@
     }
     catch(LDAPException e)
     {
-      debugInfo(tn, "Ending test with exception e="
+      fail("Ending test " + tn + " with exception e="
           +  stackTraceToSingleLineString(e));      
-      assertTrue(e == null);
     }
   }
 
-  private ArrayList<Control> getControls(String cookie)
+  /**
+   * Build a list of controls including the cookie provided.
+   * @param cookie The provided cookie.
+   * @return The built list of controls.
+   */
+  private ArrayList<Control> createControls(String cookie)
   {
     ExternalChangelogRequestControl control =
       new ExternalChangelogRequestControl(true, 
@@ -439,7 +473,9 @@
     return controls;
   }
 
-  // Utility - creates an LDIFWriter to dump result entries
+  /**
+   * Utility - creates an LDIFWriter to dump result entries.
+   */
   private static LDIFWriter getLDIFWriter()
   {
     LDIFWriter ldifWriter = null;
@@ -470,46 +506,41 @@
     assertNotNull(getEntry(entry.getDN(), 1000, true));
   }
 
-  //=======================================================
-  // From embebbded ECL
-  // Search ECL with 1 domain, public then private backend
-  private void ECLPrivateDirectMsg()
+  private void ECLOnPrivateBackend()
   {
-    ReplicationServer replServer = null;
-    String tn = "ECLPrivateDirectMsg";
+    String tn = "ECLOnPrivateBackend";
     debugInfo(tn, "Starting test");
-
     try
     {
-      // Initialize a second test backend
+      // Initialize a second test backend o=test2, in addtion to o=test
+      // Configure replication on this backend
+      // Add the root entry in the backend
       Backend backend2 = initializeTestBackend2(false);
-
-      DN baseDn = DN.decode(TEST_ROOT_DN_STRING2);
-
-      // configure and start replication of TEST_ROOT_DN_STRING on the server
+      DN baseDn2 = DN.decode(TEST_ROOT_DN_STRING2);
       SortedSet<String> replServers = new TreeSet<String>();
       replServers.add("localhost:"+replicationServerPort);
       DomainFakeCfg domainConf =
-        new DomainFakeCfg(baseDn, (short) 1602, replServers);
-      LDAPReplicationDomain domain = MultimasterReplication.createNewDomain(domainConf);
+        new DomainFakeCfg(baseDn2, (short) 1602, replServers);
+      LDAPReplicationDomain domain2 = MultimasterReplication.createNewDomain(domainConf);
       SynchronizationProvider replicationPlugin = new MultimasterReplication();
       replicationPlugin.completeSynchronizationProvider();
       sleep(1000);
-
-      Entry e = createEntry(baseDn);
+      Entry e = createEntry(baseDn2);
       addEntry(e);
-      sleep(1000);
 
+      // Search on ECL from start on all suffixes
+      String cookie = "";
       ExternalChangelogRequestControl control =
         new ExternalChangelogRequestControl(true, 
-            new MultiDomainServerState(""));
+            new MultiDomainServerState(cookie));
       ArrayList<Control> controls = new ArrayList<Control>(0);
       controls.add(control);
-
-      // search on 'cn=changelog'
       LinkedHashSet<String> attributes = new LinkedHashSet<String>();
       attributes.add("+");
       attributes.add("*");
+
+      debugInfo(tn, "Search with cookie=" + cookie);
+      sleep(2000);
       InternalSearchOperation searchOp = connection.processSearch(
           ByteString.valueOf("cn=changelog"),
           SearchScope.WHOLE_SUBTREE,
@@ -521,23 +552,27 @@
           attributes,
           controls,
           null);
+
+      // Expect SUCCESS and root entry returned
       assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS,
           searchOp.getErrorMessage().toString() + searchOp.getAdditionalLogMessage());
-
       LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries();
       assertTrue(entries != null);
       if (entries != null)
-      {
         for (SearchResultEntry resultEntry : entries)
         {
-          debugInfo(tn, "Result private entry=" + resultEntry.toLDIFString());
+          // Expect 
+          debugInfo(tn, "Entry returned=" + resultEntry.toLDIFString());
         }
-      }
       assertEquals(entries.size(),1, "Entries number returned by search");
-      
-      // Same with private backend
-      domain.getBackend().setPrivateBackend(true);
 
+      //
+      // Set the backend private and do again a search on ECL that should
+      // now not return the entry
+      //
+      domain2.getBackend().setPrivateBackend(true);
+
+      debugInfo(tn, "Search with cookie=" + cookie);
       searchOp = connection.processSearch(
           ByteString.valueOf("cn=changelog"),
           SearchScope.WHOLE_SUBTREE,
@@ -549,41 +584,37 @@
           attributes,
           controls,
           null);
+      
+      // Expect success but no entry returned
       assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS,
           searchOp.getErrorMessage().toString() + searchOp.getAdditionalLogMessage());
-
       entries = searchOp.getSearchEntries();
       assertTrue(entries != null);
       assertTrue(entries.size()==0);
-      
-      if (replServer != null)
-        replServer.remove();
 
-      if (domain != null)
-        MultimasterReplication.deleteDomain(baseDn);
-
+      // Cleaning
+      if (domain2 != null)
+        MultimasterReplication.deleteDomain(baseDn2);
       if (replicationPlugin != null)
         DirectoryServer.deregisterSynchronizationProvider(replicationPlugin);
-
       removeTestBackend2(backend2);
     }
     catch(Exception e)
     {
-      debugInfo(tn, "Ending test ECLDirectMsg with exception:\n"
+      fail("Ending test " + tn + " with exception:"
           +  stackTraceToSingleLineString(e));      
-      assertTrue(e == null);
     }
     debugInfo(tn, "Ending test successfully");
   }
-  
+
   //=======================================================
   // From embebbded ECL
   // Search ECL with 4 messages on 2 suffixes from 2 brokers
-  private void ECLDirectMsg()
+  private void ECLTwoDomains()
   {
-    String tn = "ECLDirectMsg";
+    String tn = "ECLTwoDomains";
     debugInfo(tn, "Starting test");
- 
+
     try
     {
       // Initialize a second test backend
@@ -630,7 +661,7 @@
         new DeleteMsg("uid="+tn+"4," + TEST_ROOT_DN_STRING, cn, tn+"uuid4");
       s1test.publish(delMsg);
       debugInfo(tn, " publishes " + delMsg.getChangeNumber());
-      sleep(500);
+      sleep(1500);
 
       // Changes are :
       //               s1          s2
@@ -638,29 +669,28 @@
       // o=test2                 msg2/msg2
       String cookie= "";
 
-      debugInfo(tn, "STEP 1 - from empty cookie("+cookie+")");
-
       // search on 'cn=changelog'
       LinkedHashSet<String> attributes = new LinkedHashSet<String>();
       attributes.add("+");
       attributes.add("*");
+
+      debugInfo(tn, "Search with cookie=" + cookie + "\"");
       InternalSearchOperation searchOp = 
         connection.processSearch(
             ByteString.valueOf("cn=changelog"),
-          SearchScope.WHOLE_SUBTREE,
-          DereferencePolicy.NEVER_DEREF_ALIASES, 
-          0, // Size limit
-          0, // Time limit
-          false, // Types only
-          LDAPFilter.decode("(targetDN=*direct*)"),
-          attributes,
-          getControls(cookie),
-          null);
+            SearchScope.WHOLE_SUBTREE,
+            DereferencePolicy.NEVER_DEREF_ALIASES, 
+            0, // Size limit
+            0, // Time limit
+            false, // Types only
+            LDAPFilter.decode("(targetDN=*"+tn+"*)"),
+            attributes,
+            createControls(cookie),
+            null);
 
-      // We expect SUCCESS and the 4 changes
       assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS,
           searchOp.getErrorMessage().toString());
-      
+      cookie="";
       LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries();
       if (entries != null)
       {
@@ -669,35 +699,30 @@
         {
           debugInfo(tn, " RESULT entry returned:" + entry.toSingleLineString());
           ldifWriter.writeEntry(entry);
-          try
+          if (i++==2)
           {
-            if (i++==2)
-              cookie =
-                entry.getAttribute("changelogcookie").get(0).iterator().next().toString();
+            // Store the cookie returned with the 3rd ECL entry returned to use
+            // it in the test below.
+            cookie =
+              entry.getAttribute("changelogcookie").get(0).iterator().next().toString();
           }
-          catch(NullPointerException e)
-          {}
         }
       }
       assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS);
-      
-      // We expect the 4 changes
       assertEquals(searchOp.getSearchEntries().size(), 4);
 
-      // Now start from last cookie and expect to get the last change
-      // search on 'cn=changelog'
+      // Now start from last cookie and expect to get ONLY the 4th change
       attributes = new LinkedHashSet<String>();
       attributes.add("+");
       attributes.add("*");
 
-      debugInfo(tn, "STEP 2 - from cookie" + cookie);
-
       ExternalChangelogRequestControl control =
         new ExternalChangelogRequestControl(true, 
             new MultiDomainServerState(cookie));
       ArrayList<Control> controls = new ArrayList<Control>(0);
       controls.add(control);
 
+      debugInfo(tn, "Search with cookie=" + cookie);
       searchOp = connection.processSearch(
           ByteString.valueOf("cn=changelog"),
           SearchScope.WHOLE_SUBTREE,
@@ -705,18 +730,16 @@
           0, // Size limit
           0, // Time limit
           false, // Types only
-          LDAPFilter.decode("(targetDN=*direct*)"),
+          LDAPFilter.decode("(targetDN=*"+tn+"*)"),
           attributes,
           controls,
           null);
-      
-      
+
       // We expect SUCCESS and the 4th change
       assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS,
           searchOp.getErrorMessage().toString() + searchOp.getAdditionalLogMessage());
-      
-      cookie= "";
       entries = searchOp.getSearchEntries();
+      cookie="";
       if (entries != null)
       {
         for (SearchResultEntry entry : entries)
@@ -725,21 +748,18 @@
           ldifWriter.writeEntry(entry);
           try
           {
-          cookie =
-            entry.getAttribute("changelogcookie").get(0).iterator().next().toString();
+            // Store the cookie returned with the 4rd ECL entry returned to use
+            // it in the test below.
+            cookie =
+              entry.getAttribute("changelogcookie").get(0).iterator().next().toString();
           }
           catch(NullPointerException e)
           {}
         }
       }
-
-      assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS);
-      
-      // we expect msg4
       assertEquals(searchOp.getSearchEntries().size(), 1);
-      
-      debugInfo(tn, "STEP 3 - from cookie" + cookie);
 
+      // Now publishes a new change and search from the previous cookie
       ChangeNumber cn5 = new ChangeNumber(time++, ts++, s1test.getServerId());
       delMsg =
         new DeleteMsg("uid="+tn+"5," + TEST_ROOT_DN_STRING, cn5, tn+"uuid5");
@@ -757,6 +777,7 @@
       controls = new ArrayList<Control>(0);
       controls.add(control);
 
+      debugInfo(tn, "Search with cookie=" + cookie + "\"");
       searchOp = connection.processSearch(
           ByteString.valueOf("cn=changelog"),
           SearchScope.WHOLE_SUBTREE,
@@ -764,15 +785,13 @@
           0, // Size limit
           0, // Time limit
           false, // Types only
-          LDAPFilter.decode("(targetDN=*direct*)"),
+          LDAPFilter.decode("(targetDN=*"+tn+"*)"),
           attributes,
           controls,
           null);
 
       assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS,
           searchOp.getErrorMessage().toString() + searchOp.getAdditionalLogMessage());
-      
-      cookie= "";
       entries = searchOp.getSearchEntries();
       if (entries != null)
       {
@@ -782,20 +801,17 @@
           ldifWriter.writeEntry(resultEntry);
           try
           {
-          cookie =
-            resultEntry.getAttribute("changelogcookie").get(0).iterator().next().toString();
+            cookie =
+              resultEntry.getAttribute("changelogcookie").get(0).iterator().next().toString();
           }
           catch(NullPointerException e)
           {}
         }
       }
       assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS);
-
-      // we expect 1 entries : msg5
       assertEquals(searchOp.getSearchEntries().size(), 1);
 
       cookie="";
-      debugInfo(tn, "STEP 4 - [filter:o=test cookie:" + cookie + "]");
 
       control =
         new ExternalChangelogRequestControl(true, 
@@ -803,6 +819,8 @@
       controls = new ArrayList<Control>(0);
       controls.add(control);
 
+      debugInfo(tn, "Search with cookie=" + cookie + "\" and filter on domain=" + 
+          "(targetDN=*direct*,o=test)");
       searchOp = connection.processSearch(
           ByteString.valueOf("cn=changelog"),
           SearchScope.WHOLE_SUBTREE,
@@ -810,15 +828,15 @@
           0, // Size limit
           0, // Time limit
           false, // Types only
-          LDAPFilter.decode("(targetDN=*direct*,o=test)"),
+          LDAPFilter.decode("(targetDN=*"+tn+"*,o=test)"),
           attributes,
           controls,
           null);
-      
-      
+
+
       assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS,
           searchOp.getErrorMessage().toString() + searchOp.getAdditionalLogMessage());
-      
+
       entries = searchOp.getSearchEntries();
       if (entries != null)
       {
@@ -828,18 +846,20 @@
           ldifWriter.writeEntry(resultEntry);
           try
           {
-          cookie =
-            resultEntry.getAttribute("changelogcookie").get(0).iterator().next().toString();
+            cookie =
+              resultEntry.getAttribute("changelogcookie").get(0).iterator().next().toString();
           }
           catch(NullPointerException e)
           {}
         }
       }
       assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS);
-
       // we expect msg1 + msg4 + msg5
       assertEquals(searchOp.getSearchEntries().size(), 3);
 
+      //
+      // Test startState ("first cookie") of the ECL
+      //
       // --
       ReplicationBroker s1test2 = openReplicationSession(
           DN.decode(TEST_ROOT_DN_STRING2), (short) 1203, 
@@ -852,7 +872,6 @@
           1000, true);
       sleep(500);
 
-      // Test startState ("first cookie") of the ECL
       time = TimeThread.getTime();
       cn = new ChangeNumber(time++, ts++, s1test2.getServerId());
       delMsg =
@@ -887,8 +906,9 @@
       assertTrue(startState.getMaxChangeNumber(s2test2.getServerId()).getSeqnum()==2);
       assertTrue(startState.getMaxChangeNumber(s1test2.getServerId()).getSeqnum()==6);
 
-      // Test lastState ("last cookie") of the ECL
-      // create an ECL sessionm and request lastCookie
+      //
+      // Test lastExternalChangelogCookie attribute of the ECL
+      //
       ExternalChangeLogSessionImpl session = 
         new ExternalChangeLogSessionImpl(replicationServer);
       MultiDomainServerState expectedLastCookie =
@@ -911,13 +931,12 @@
           false, // Types only
           LDAPFilter.decode("(objectclass=*)"),
           lastcookieattribute,
-          null,
+          NO_CONTROL,
           null);
-            
+
       assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS,
           searchOp.getErrorMessage().toString()
           + searchOp.getAdditionalLogMessage());
-      
       cookie = "";
       entries = searchOp.getSearchEntries();
       if (entries != null)
@@ -943,24 +962,23 @@
       s1test2.stop();
       s2test.stop();
       s2test2.stop();
-      
+
       removeTestBackend2(backend2);
     }
     catch(Exception e)
     {
-      debugInfo(tn, "Ending test ECLDirectMsg with exception:\n"
+      fail("Ending test " + tn + "with exception:\n"
           +  stackTraceToSingleLineString(e));      
-      assertTrue(e == null);
     }
     debugInfo(tn, "Ending test successfully");
   }
 
   // simple update to be received
-  private void ECLDirectAllOps()
+  private void ECLAllOps()
   {
-    String tn = "ECLDirectAllOps";
+    String tn = "ECLAllOps";
     debugInfo(tn, "Starting test\n\n");
- 
+
     try
     {
       LDIFWriter ldifWriter = getLDIFWriter();
@@ -972,6 +990,12 @@
           1000, true);
       int ts = 1;
 
+      // Creates broker on o=test2
+      ReplicationBroker server02 = openReplicationSession(
+          DN.decode(TEST_ROOT_DN_STRING2), (short) 1202, 
+          100, replicationServerPort,
+          1000, true); 
+
       String user1entryUUID = "11111111-1111-1111-1111-111111111111";
       String baseUUID       = "22222222-2222-2222-2222-222222222222";
 
@@ -1000,6 +1024,15 @@
       server01.publish(addMsg);
       debugInfo(tn, " publishes " + addMsg.getChangeNumber());
 
+      // Publish DEL
+      /*
+      ChangeNumber cn12 = new ChangeNumber(TimeThread.getTime(), ts++, (short)1202);
+      DeleteMsg delMsg2 =
+        new DeleteMsg("uid="+tn+"12," + TEST_ROOT_DN_STRING2, cn12, tn+"uuid12");
+      server02.publish(delMsg2);
+      debugInfo(tn, " publishes " + delMsg2.getChangeNumber());
+      */
+
       // Publish MOD
       ChangeNumber cn3 = new ChangeNumber(TimeThread.getTime(), ts++, (short)1201);
       Attribute attr1 = Attributes.create("description", "new value");
@@ -1024,12 +1057,11 @@
       ModifyDNMsg modDNMsg = new ModifyDNMsg(localOp);
       server01.publish(modDNMsg);
       debugInfo(tn, " publishes " + modDNMsg.getChangeNumber());
-      sleep(600);
+      sleep(1000);
 
       String cookie= "";
 
       // search on 'cn=changelog'
-      debugInfo(tn, "STEP 1 - from empty cookie("+cookie+")");
       LinkedHashSet<String> attributes = new LinkedHashSet<String>();
       attributes.add("+");
       attributes.add("*");
@@ -1040,28 +1072,28 @@
       ArrayList<Control> controls = new ArrayList<Control>(0);
       controls.add(control);
 
+      debugInfo(tn, "Search with cookie=" + cookie + "\" filter=" + 
+          "(targetdn=*"+tn+"*,o=test)");
       InternalSearchOperation searchOp = 
         connection.processSearch(
             ByteString.valueOf("cn=changelog"),
-          SearchScope.WHOLE_SUBTREE,
-          DereferencePolicy.NEVER_DEREF_ALIASES, 
-          0, // Size limit
-          0, // Time limit
-          false, // Types only
-          LDAPFilter.decode("(targetdn=*"+tn+"*,o=test)"),
-          attributes,
-          controls,
-          null);
+            SearchScope.WHOLE_SUBTREE,
+            DereferencePolicy.NEVER_DEREF_ALIASES, 
+            0, // Size limit
+            0, // Time limit
+            false, // Types only
+            LDAPFilter.decode("(targetdn=*"+tn+"*,o=test)"),
+            attributes,
+            controls,
+            null);
       sleep(500);
 
       // test success
       assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS,
           searchOp.getErrorMessage().toString());
-      
       // test 4 entries returned
-      LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries();
-      // 4 entries expected
       assertEquals(searchOp.getSearchEntries().size(), 4);
+      LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries();
       if (entries != null)
       {
         int i=0;
@@ -1073,16 +1105,20 @@
           if (i==1)
           {
             // check the DEL entry has the right content
+            assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase(
+                "cn=" + cn1 + "," + TEST_ROOT_DN_STRING + ",cn=changelog"));
             checkValue(resultEntry,"replicationcsn",cn1.toString());
             checkValue(resultEntry,"replicaidentifier","1201");
             checkValue(resultEntry,"targetdn","uid="+tn+"1," + TEST_ROOT_DN_STRING);
             checkValue(resultEntry,"changetype","delete");
-            checkValue(resultEntry,"changelogcookie","o=test:"+cn1.toString()+";");
+            checkValue(resultEntry,"changelogcookie","o=test:"+cn1.toString()+";o=test2:;");
             checkValue(resultEntry,"targetentryuuid",tn+"uuid1");
-            checkValue(resultEntry,"changenumber","-1");
+            checkValue(resultEntry,"changenumber","0");
           } else if (i==2)
           {
             // check the ADD entry has the right content
+            assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase(
+                "cn=" + cn2 + "," + TEST_ROOT_DN_STRING + ",cn=changelog"));
             String expectedValue1 = "objectClass: domain\nobjectClass: top\n" +
             "entryUUID: 11111111-1111-1111-1111-111111111111\n\n";
             String expectedValue2 = "entryUUID: 11111111-1111-1111-1111-111111111111\n" +
@@ -1092,12 +1128,14 @@
             checkValue(resultEntry,"replicaidentifier","1201");
             checkValue(resultEntry,"targetdn","uid="+tn+"2," + TEST_ROOT_DN_STRING);
             checkValue(resultEntry,"changetype","add");
-            checkValue(resultEntry,"changelogcookie","o=test:"+cn2.toString()+";");
+            checkValue(resultEntry,"changelogcookie","o=test:"+cn2.toString()+";o=test2:;");
             checkValue(resultEntry,"targetentryuuid",user1entryUUID);
-            checkValue(resultEntry,"changenumber","-1");
+            checkValue(resultEntry,"changenumber","0");
           } else if (i==3)
           {
             // check the MOD entry has the right content
+            assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase(
+                "cn=" + cn3 + "," + TEST_ROOT_DN_STRING + ",cn=changelog"));
             String expectedValue = "replace: description\n" +
             "description: new value\n-\n";
             checkValue(resultEntry,"changes",expectedValue);
@@ -1105,32 +1143,35 @@
             checkValue(resultEntry,"replicaidentifier","1201");
             checkValue(resultEntry,"targetdn","uid="+tn+"3," + TEST_ROOT_DN_STRING);
             checkValue(resultEntry,"changetype","modify");
-            checkValue(resultEntry,"changelogcookie","o=test:"+cn3.toString()+";");
+            checkValue(resultEntry,"changelogcookie","o=test:"+cn3.toString()+";o=test2:;");
             checkValue(resultEntry,"targetentryuuid",tn+"uuid3");
-            checkValue(resultEntry,"changenumber","-1");
+            checkValue(resultEntry,"changenumber","0");
           } else if (i==4)
           {
             // check the MODDN entry has the right content
+            assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase(
+                "cn=" + cn4 + "," + TEST_ROOT_DN_STRING + ",cn=changelog"));
             checkValue(resultEntry,"replicationcsn",cn4.toString());
             checkValue(resultEntry,"replicaidentifier","1201");
             checkValue(resultEntry,"targetdn","uid="+tn+"4," + TEST_ROOT_DN_STRING);
             checkValue(resultEntry,"changetype","modrdn");
-            checkValue(resultEntry,"changelogcookie","o=test:"+cn4.toString()+";");
+            checkValue(resultEntry,"changelogcookie","o=test:"+cn4.toString()+";o=test2:;");
             checkValue(resultEntry,"targetentryuuid",tn+"uuid4");
-            checkValue(resultEntry,"newrdn","uid=ECLDirectAllOpsnew4");            
+            checkValue(resultEntry,"newrdn","uid=ECLAllOpsnew4");            
             checkValue(resultEntry,"newsuperior",TEST_ROOT_DN_STRING2);
             checkValue(resultEntry,"deleteoldrdn","true");
-            checkValue(resultEntry,"changenumber","-1");
+            checkValue(resultEntry,"changenumber","0");
           }
         }
       }
       server01.stop();
+      if (server02 != null)
+        server02.stop();
     }
     catch(Exception e)
     {
-      debugInfo(tn, "Ending test with exception:\n"
+      fail("Ending test " + tn + " with exception:\n"
           +  stackTraceToSingleLineString(e));      
-      assertTrue(e == null);
     }
     debugInfo(tn, "Ending test with success");
   }
@@ -1140,13 +1181,13 @@
     AttributeValue av = null;
     try
     {
-    List<Attribute> attrs = entry.getAttribute(attrName);
-    Attribute a = attrs.iterator().next();
-    av = a.iterator().next();
-    String encodedValue = av.toString();
+      List<Attribute> attrs = entry.getAttribute(attrName);
+      Attribute a = attrs.iterator().next();
+      av = a.iterator().next();
+      String encodedValue = av.toString();
       assertTrue(encodedValue.equalsIgnoreCase(expectedValue),
-        "In entry " + entry + " attr <" + attrName + "> equals " +
-        av + " instead of expected value " + expectedValue); 
+          "In entry " + entry + " attr <" + attrName + "> equals " +
+          av + " instead of expected value " + expectedValue); 
     }
     catch(Exception e)
     {
@@ -1155,23 +1196,23 @@
           av + " instead of expected value " + expectedValue);       
     }
   }
-  
+
   private static void checkPossibleValues(Entry entry, String attrName, 
       String expectedValue1, String expectedValue2)
   {
     AttributeValue av = null;
     try
     {
-    List<Attribute> attrs = entry.getAttribute(attrName);
-    Attribute a = attrs.iterator().next();
-    av = a.iterator().next();
-    String encodedValue = av.toString();
+      List<Attribute> attrs = entry.getAttribute(attrName);
+      Attribute a = attrs.iterator().next();
+      av = a.iterator().next();
+      String encodedValue = av.toString();
       assertTrue(
           (encodedValue.equalsIgnoreCase(expectedValue1) ||
               encodedValue.equalsIgnoreCase(expectedValue2)),
-        "In entry " + entry + " attr <" + attrName + "> equals " +
-        av + " instead of one of the expected values " + expectedValue1
-        + " or " + expectedValue2); 
+              "In entry " + entry + " attr <" + attrName + "> equals " +
+              av + " instead of one of the expected values " + expectedValue1
+              + " or " + expectedValue2); 
     }
     catch(Exception e)
     {
@@ -1181,13 +1222,14 @@
           + " or " + expectedValue2); 
     }
   }
-  
+
   /**
    * Test persistent search
    */
-  private void ECLDirectPsearch(boolean changesOnly)
+  private void ECLPsearch(boolean changesOnly, boolean compatMode)
   {
-    String tn = "ECLDirectPsearch_" + String.valueOf(changesOnly);
+    String tn = "ECLPsearch_" + String.valueOf(changesOnly) + "_" + 
+      String.valueOf(compatMode);
     debugInfo(tn, "Starting test \n\n");
     Socket s =null;
 
@@ -1217,13 +1259,20 @@
       // Produce update on this suffix
       ChangeNumber cn = new ChangeNumber(TimeThread.getTime(), ts++, (short)1201);
       DeleteMsg delMsg =
-        new DeleteMsg("uid=" + tn + "1," + TEST_ROOT_DN_STRING, cn, tn+"uuid1");
+        new DeleteMsg("uid=" + tn + "1," + TEST_ROOT_DN_STRING, cn, 
+            "11111111-1112-1113-1114-111111111114");
       debugInfo(tn, " publishing " + delMsg.getChangeNumber());
       server01.publish(delMsg);
       this.sleep(500); // let's be sure the message is in the RS
 
       // Creates cookie control
-      ArrayList<Control> controls = getControls("");
+      String cookie = "";
+      ArrayList<Control> controls = createControls(cookie);
+      if (compatMode)
+      {
+        cookie = null;
+        controls = new ArrayList<Control>(0);
+      }
 
       // Creates psearch control
       HashSet<PersistentSearchChangeType> changeTypes =
@@ -1250,6 +1299,7 @@
             null);
 
       // Connects and bind
+      debugInfo(tn, "Search with cookie=" + cookie + "\"");
       s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
       org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
       LDAPWriter w = new LDAPWriter(s);
@@ -1269,7 +1319,7 @@
       long searchReferences = ldapStatistics.getSearchResultReferences();
       long searchesDone     = ldapStatistics.getSearchResultsDone();
 
-      debugInfo(tn, "Sending the PSearch request filter=(targetDN=*"+tn+"*,o=test)");
+      debugInfo(tn, "Search Persistent filter=(targetDN=*"+tn+"*,o=test)");
       LDAPMessage message;
       message = new LDAPMessage(2, searchRequest, controls);
       w.writeMessage(message);
@@ -1281,21 +1331,24 @@
       if (changesOnly == false)
       {
         // Wait for change 1
-        debugInfo(tn, "Waiting for : INIT search expected to return change 1");
+        debugInfo(tn, "Waiting for init search expected to return change 1");
         searchEntries = 0;
         message = null;
-        
+
         try
         {
           while ((searchEntries<1) && (message = r.readMessage()) != null)
           {
-            debugInfo(tn, "First search returns " + 
+            debugInfo(tn, "Init search Result=" + 
                 message.getProtocolOpType() + message + " " + searchEntries);
             switch (message.getProtocolOpType())
             {
             case LDAPConstants.OP_TYPE_SEARCH_RESULT_ENTRY:
               searchResultEntry = message.getSearchResultEntryProtocolOp();
               searchEntries++;
+              // FIXME:ECL Double check 10 is really the valid value here.
+              checkValue(searchResultEntry.toSearchResultEntry(),"changenumber",
+                  (compatMode?"10":"0"));
               break;
 
             case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE:
@@ -1307,7 +1360,6 @@
               assertEquals(
                   searchResultDone.getResultCode(), ResultCode.SUCCESS,
                   searchResultDone.getErrorMessage().toString());
-//            assertEquals(InvocationCounterPlugin.waitForPostResponse(), 1);
               searchesDone++;
               break;
             }
@@ -1315,23 +1367,24 @@
         }
         catch(Exception e)
         {
-          debugInfo(tn, "INIT search failed with e=" +
-              stackTraceToSingleLineString(e));        
+          fail("init search failed with e=" + stackTraceToSingleLineString(e));        
         }
         debugInfo(tn, "INIT search done with success. searchEntries="
-            + searchEntries + " searchesDone="+ searchesDone);
+            + searchEntries + " #searchesDone="+ searchesDone);
       }
 
       // Produces change 2
       cn = new ChangeNumber(TimeThread.getTime(), ts++, (short)1201);
       String expectedDn = "uid=" + tn + "2," +  TEST_ROOT_DN_STRING;
-      delMsg = new DeleteMsg(expectedDn, cn, tn + "uuid2");
+      delMsg = new DeleteMsg(expectedDn, cn,
+         "11111111-1112-1113-1114-111111111115");
       debugInfo(tn, " publishing " + delMsg.getChangeNumber());
       server01.publish(delMsg);
+      this.gblCN = cn;
       this.sleep(1000);
 
       debugInfo(tn, delMsg.getChangeNumber() +
-          " published , will wait for new entries (Persist)");
+      " published , psearch will now wait for new entries");
 
       // wait for the 1 new entry
       searchEntries = 0;
@@ -1340,7 +1393,7 @@
       message = null;
       while ((searchEntries<1) && (message = r.readMessage()) != null)
       {
-        debugInfo(tn, "2nd search returns " + 
+        debugInfo(tn, "psearch search  Result=" + 
             message.getProtocolOpType() + message);
         switch (message.getProtocolOpType())
         {
@@ -1364,7 +1417,7 @@
         }
       }
       sleep(1000);
-      
+
       // Check we received change 2
       for (LDAPAttribute a : searchResultEntry.getAttributes())
       {
@@ -1391,16 +1444,172 @@
       // When problem found, we have to re-enable this test.
       if (false)
       {
-    
-      // ACI step
-      debugInfo(tn, "Starting ACI step");
-      s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
-      r = new org.opends.server.tools.LDAPReader(s);
-      w = new LDAPWriter(s);
-      s.setSoTimeout(1500000);
-      bindAsWhoEver(w, r, "toto", "tutu", LDAPResultCode.OPERATIONS_ERROR);
-      
-      searchRequest =
+
+        // ACI step
+        debugInfo(tn, "Starting ACI step");
+        s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+        r = new org.opends.server.tools.LDAPReader(s);
+        w = new LDAPWriter(s);
+        s.setSoTimeout(1500000);
+        bindAsWhoEver(w, r, "toto", "tutu", LDAPResultCode.OPERATIONS_ERROR);
+
+        searchRequest =
+          new SearchRequestProtocolOp(
+              ByteString.valueOf("cn=changelog"),
+              SearchScope.WHOLE_SUBTREE,
+              DereferencePolicy.NEVER_DEREF_ALIASES,
+              Integer.MAX_VALUE,
+              Integer.MAX_VALUE,
+              false,
+              LDAPFilter.decode("(targetDN=*directpsearch*,o=test)"),
+              null);
+
+        debugInfo(tn, "ACI test : sending search");
+        message = new LDAPMessage(2, searchRequest, createControls(""));
+        w.writeMessage(message);
+
+        searchesDone=0;
+        searchEntries = 0;
+        searchResultEntry = null;
+        searchResultDone = null;
+        while ((searchesDone==0) && (message = r.readMessage()) != null)
+        {
+          debugInfo(tn, "ACI test : message returned " + 
+              message.getProtocolOpType() + message);
+          switch (message.getProtocolOpType())
+          {
+          case LDAPConstants.OP_TYPE_SEARCH_RESULT_ENTRY:
+            searchResultEntry = message.getSearchResultEntryProtocolOp();
+            //assertTrue(false, "Unexpected entry returned in ACI test of " + tn + searchResultEntry);
+            searchEntries++;
+            break;
+
+          case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE:
+            searchReferences++;
+            break;
+
+          case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE:
+            searchResultDone = message.getSearchResultDoneProtocolOp();
+            assertEquals(searchResultDone.getResultCode(), 
+                ResultCode.SUCCESS.getIntValue());
+//          assertEquals(InvocationCounterPlugin.waitForPostResponse(), 1);
+            searchesDone++;
+            break;
+          }
+        }
+        // search should end with success
+        assertTrue(searchesDone==1);
+        // but returning no entry
+        assertEquals(searchEntries,0, "Bad search entry# in ACI test of " + tn);
+      }      
+
+      try { s.close(); } catch (Exception e) {};
+      sleep(1000);
+    }
+    catch(Exception e)
+    {
+      fail("Test " + tn + " fails with " +  stackTraceToSingleLineString(e));
+    }
+    debugInfo(tn, "Ends test successfully");
+  }
+
+  /**
+   * Test parallel simultaneous psearch with different filters.
+   */
+  private void ECLSimulPsearches()
+  {
+    String tn = "ECLSimulPsearches";
+    debugInfo(tn, "Starting test \n\n");
+    Socket s1, s2, s3 = null;
+    boolean compatMode = false;
+    boolean changesOnly = false;
+
+    // create stats
+    for (ConnectionHandler ch : DirectoryServer.getConnectionHandlers())
+    {
+      if (ch instanceof LDAPConnectionHandler)
+      {
+        LDAPConnectionHandler lch = (LDAPConnectionHandler) ch;
+        if (!lch.useSSL())
+        {
+          ldapStatistics = lch.getStatTracker();
+        }
+      }
+    }
+    assertNotNull(ldapStatistics);
+
+    try
+    {
+      // Create broker on o=test
+      ReplicationBroker server01 = openReplicationSession(
+          DN.decode(TEST_ROOT_DN_STRING), (short) 1201, 
+          100, replicationServerPort,
+          1000, true);
+      int ts = 1;
+
+      // Create broker on o=test2
+      ReplicationBroker server02 = openReplicationSession(
+          DN.decode(TEST_ROOT_DN_STRING2), (short) 1202, 
+          100, replicationServerPort,
+          1000, true, EMPTY_DN_GENID);
+
+      // Produce update 1
+      ChangeNumber cn1 = 
+        new ChangeNumber(TimeThread.getTime(), ts++, (short)1201);
+      DeleteMsg delMsg1 =
+        new DeleteMsg("uid=" + tn + "1," + TEST_ROOT_DN_STRING, cn1, 
+            "11111111-1111-1111-1111-111111111111");
+      debugInfo(tn, " publishing " + delMsg1);
+      server01.publish(delMsg1);
+      this.sleep(500); // let's be sure the message is in the RS
+
+      // Produce update 2
+      ChangeNumber cn2 = 
+        new ChangeNumber(TimeThread.getTime(), ts++, (short)1202);
+      DeleteMsg delMsg2 =
+        new DeleteMsg("uid=" + tn + "2," + TEST_ROOT_DN_STRING2, cn2, 
+            "22222222-2222-2222-2222-222222222222");
+      debugInfo(tn, " publishing " + delMsg2);
+      server02.publish(delMsg2);
+      this.sleep(500); // let's be sure the message is in the RS
+
+      // Produce update 3
+      ChangeNumber cn3 = 
+        new ChangeNumber(TimeThread.getTime(), ts++, (short)1202);
+      DeleteMsg delMsg3 =
+        new DeleteMsg("uid=" + tn + "3," + TEST_ROOT_DN_STRING2, cn3, 
+            "33333333-3333-3333-3333-333333333333");
+      debugInfo(tn, " publishing " + delMsg3);
+      server02.publish(delMsg3);
+      this.sleep(500); // let's be sure the message is in the RS
+
+      // Creates cookie control
+      String cookie = "";
+      ArrayList<Control> controls = createControls(cookie);
+      if (compatMode)
+      {
+        cookie = null;
+        controls = new ArrayList<Control>(0);
+      }
+
+      // Creates psearch control
+      HashSet<PersistentSearchChangeType> changeTypes =
+        new HashSet<PersistentSearchChangeType>();
+      changeTypes.add(PersistentSearchChangeType.ADD);
+      changeTypes.add(PersistentSearchChangeType.DELETE);
+      changeTypes.add(PersistentSearchChangeType.MODIFY);
+      changeTypes.add(PersistentSearchChangeType.MODIFY_DN);
+      boolean returnECs = true;
+      PersistentSearchControl persSearchControl = new PersistentSearchControl(
+          changeTypes, changesOnly, returnECs);
+      controls.add(persSearchControl);
+
+      LinkedHashSet<String> attributes = new LinkedHashSet<String>();
+      attributes.add("+");
+      attributes.add("*");
+
+      // Creates request 1
+      SearchRequestProtocolOp searchRequest1 =
         new SearchRequestProtocolOp(
             ByteString.valueOf("cn=changelog"),
             SearchScope.WHOLE_SUBTREE,
@@ -1408,26 +1617,255 @@
             Integer.MAX_VALUE,
             Integer.MAX_VALUE,
             false,
-            LDAPFilter.decode("(targetDN=*directpsearch*,o=test)"),
-            null);
+            LDAPFilter.decode("(targetDN=*"+tn+"*,o=test)"),
+            attributes);
 
-      debugInfo(tn, "ACI test : sending search");
-      message = new LDAPMessage(2, searchRequest, getControls(""));
-      w.writeMessage(message);
+      // Creates request 2
+      SearchRequestProtocolOp searchRequest2 =
+        new SearchRequestProtocolOp(
+            ByteString.valueOf("cn=changelog"),
+            SearchScope.WHOLE_SUBTREE,
+            DereferencePolicy.NEVER_DEREF_ALIASES,
+            Integer.MAX_VALUE,
+            Integer.MAX_VALUE,
+            false,
+            LDAPFilter.decode("(targetDN=*"+tn+"*,o=test2)"),
+            attributes);
 
-      searchesDone=0;
+      // Creates request 3
+      SearchRequestProtocolOp searchRequest3 =
+        new SearchRequestProtocolOp(
+            ByteString.valueOf("cn=changelog"),
+            SearchScope.WHOLE_SUBTREE,
+            DereferencePolicy.NEVER_DEREF_ALIASES,
+            Integer.MAX_VALUE,
+            Integer.MAX_VALUE,
+            false,
+            LDAPFilter.decode("objectclass=*"),
+            attributes);
+
+      // Connects and bind
+      s1 = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+      org.opends.server.tools.LDAPReader r1 = new org.opends.server.tools.LDAPReader(s1);
+      LDAPWriter w1 = new LDAPWriter(s1);
+      s1.setSoTimeout(1500000);
+      bindAsManager(w1, r1);
+
+      // Connects and bind
+      s2 = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+      org.opends.server.tools.LDAPReader r2 = new org.opends.server.tools.LDAPReader(s2);
+      LDAPWriter w2 = new LDAPWriter(s2);
+      s2.setSoTimeout(1500000);
+      bindAsManager(w2, r2);
+
+      // Connects and bind
+      s3 = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+      org.opends.server.tools.LDAPReader r3 = new org.opends.server.tools.LDAPReader(s3);
+      LDAPWriter w3 = new LDAPWriter(s3);
+      s3.setSoTimeout(1500000);
+      bindAsManager(w3, r3);
+
+      // Since we are going to be watching the post-response count, we need to
+      // wait for the server to become idle before kicking off the next request
+      // to ensure that any remaining post-response processing from the previous
+      // operation has completed.
+      assertTrue(DirectoryServer.getWorkQueue().waitUntilIdle(10000));
+
+      InvocationCounterPlugin.resetAllCounters();
+
+      long searchRequests   = ldapStatistics.getSearchRequests();
+      long searchEntries    = ldapStatistics.getSearchResultEntries();
+      long searchReferences = ldapStatistics.getSearchResultReferences();
+      long searchesDone     = ldapStatistics.getSearchResultsDone();
+
+      LDAPMessage message;
+      message = new LDAPMessage(2, searchRequest1, controls);
+      w1.writeMessage(message);
+      this.sleep(500);
+
+      message = new LDAPMessage(2, searchRequest2, controls);
+      w2.writeMessage(message);
+      this.sleep(500);
+
+      message = new LDAPMessage(2, searchRequest3, controls);
+      w3.writeMessage(message);
+      this.sleep(500);
+
+      SearchResultEntryProtocolOp searchResultEntry = null;
+      SearchResultDoneProtocolOp searchResultDone = null;
+
+      if (changesOnly == false)
+      {
+        debugInfo(tn, "Search1  Persistent filter="+searchRequest1.getFilter().toString()
+                  + " expected to return change " + cn1);
+        searchEntries = 0;
+        message = null;
+
+        try
+        {
+          while ((searchEntries<1) && (message = r1.readMessage()) != null)
+          {
+            debugInfo(tn, "Search1 Result=" + 
+                message.getProtocolOpType() + " " + message);
+            switch (message.getProtocolOpType())
+            {
+            case LDAPConstants.OP_TYPE_SEARCH_RESULT_ENTRY:
+              searchResultEntry = message.getSearchResultEntryProtocolOp();
+              searchEntries++;
+              if (searchEntries==1)
+              {
+                checkValue(searchResultEntry.toSearchResultEntry(),"replicationcsn",cn1.toString());
+                checkValue(searchResultEntry.toSearchResultEntry(),"changenumber",
+                    (compatMode?"10":"0"));
+              }
+              break;
+
+            case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE:
+              searchReferences++;
+              break;
+
+            case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE:
+              searchResultDone = message.getSearchResultDoneProtocolOp();
+              assertEquals(
+                  searchResultDone.getResultCode(), ResultCode.SUCCESS,
+                  searchResultDone.getErrorMessage().toString());
+              searchesDone++;
+              break;
+            }
+          }
+        }
+        catch(Exception e)
+        {
+          fail("Search1 failed with e=" + stackTraceToSingleLineString(e));        
+        }
+        debugInfo(tn, "Search1 done with success. searchEntries="
+            + searchEntries + " #searchesDone="+ searchesDone);
+        
+        searchEntries = 0;
+        message = null;
+        try
+        {
+          debugInfo(tn, "Search 2  Persistent filter="+searchRequest2.getFilter().toString()
+              + " expected to return change " + cn2 + " & " + cn3);
+          while ((searchEntries<2) && (message = r2.readMessage()) != null)
+          {
+            debugInfo(tn, "Search 2 Result=" + 
+                message.getProtocolOpType() + message);
+            switch (message.getProtocolOpType())
+            {
+            case LDAPConstants.OP_TYPE_SEARCH_RESULT_ENTRY:
+              searchResultEntry = message.getSearchResultEntryProtocolOp();
+              searchEntries++;
+              checkValue(searchResultEntry.toSearchResultEntry(),"changenumber",
+                  (compatMode?"10":"0"));
+              break;
+
+            case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE:
+              searchReferences++;
+              break;
+
+            case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE:
+              searchResultDone = message.getSearchResultDoneProtocolOp();
+              assertEquals(
+                  searchResultDone.getResultCode(), ResultCode.SUCCESS,
+                  searchResultDone.getErrorMessage().toString());
+              searchesDone++;
+              break;
+            }
+          }
+        }
+        catch(Exception e)
+        {
+          fail("Search2 failed with e=" + stackTraceToSingleLineString(e));        
+        }
+        debugInfo(tn, "Search2 done with success. searchEntries="
+            + searchEntries + " #searchesDone="+ searchesDone);
+
+        
+        searchEntries = 0;
+        message = null;
+        try
+        {
+          debugInfo(tn, "Search3  Persistent filter="+searchRequest3.getFilter().toString()
+              + " expected to return change top + " + cn1 + " & " + cn2 + " & " + cn3);
+          while ((searchEntries<4) && (message = r3.readMessage()) != null)
+          {
+            debugInfo(tn, "Search3 Result=" + 
+                message.getProtocolOpType() + " " + message);
+
+            switch (message.getProtocolOpType())
+            {
+            case LDAPConstants.OP_TYPE_SEARCH_RESULT_ENTRY:
+              searchResultEntry = message.getSearchResultEntryProtocolOp();
+              searchEntries++;
+              break;
+
+            case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE:
+              searchReferences++;
+              break;
+
+            case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE:
+              searchResultDone = message.getSearchResultDoneProtocolOp();
+              assertEquals(
+                  searchResultDone.getResultCode(), ResultCode.SUCCESS,
+                  searchResultDone.getErrorMessage().toString());
+              searchesDone++;
+              break;
+            }
+          }
+        }
+        catch(Exception e)
+        {
+          fail("Search3 failed with e=" + stackTraceToSingleLineString(e));        
+        }
+        debugInfo(tn, "Search3 done with success. searchEntries="
+            + searchEntries + " #searchesDone="+ searchesDone);
+
+      }
+
+      // Produces additional change
+      ChangeNumber cn11 = new ChangeNumber(TimeThread.getTime(), 11, (short)1201);
+      String expectedDn11 = "uid=" + tn + "11," +  TEST_ROOT_DN_STRING;
+      DeleteMsg delMsg11 = new DeleteMsg(expectedDn11, cn11,
+         "44444444-4444-4444-4444-444444444444");
+      debugInfo(tn, " publishing " + delMsg11);
+      server01.publish(delMsg11);
+      this.sleep(500);
+      debugInfo(tn, delMsg11.getChangeNumber() + " published additionally ");
+
+      // Produces additional change
+      ChangeNumber cn12 = new ChangeNumber(TimeThread.getTime(), 12, (short)1202);
+      String expectedDn12 = "uid=" + tn + "12," +  TEST_ROOT_DN_STRING2;
+      DeleteMsg delMsg12 = new DeleteMsg(expectedDn12, cn12,
+         "55555555-5555-5555-5555-555555555555");
+      debugInfo(tn, " publishing " + delMsg12 );
+      server02.publish(delMsg12);
+      this.sleep(500);
+      debugInfo(tn, delMsg12.getChangeNumber()  + " published additionally ");
+
+      // Produces additional change
+      ChangeNumber cn13 = new ChangeNumber(TimeThread.getTime(), 13, (short)1202);
+      String expectedDn13 = "uid=" + tn + "13," +  TEST_ROOT_DN_STRING2;
+      DeleteMsg delMsg13 = new DeleteMsg(expectedDn13, cn13,
+         "66666666-6666-6666-6666-666666666666");
+      debugInfo(tn, " publishing " + delMsg13);
+      server02.publish(delMsg13);
+      this.sleep(500);
+      debugInfo(tn, delMsg13.getChangeNumber()  + " published additionally ");
+
+      // wait 11
       searchEntries = 0;
       searchResultEntry = null;
       searchResultDone = null;
-      while ((searchesDone==0) && (message = r.readMessage()) != null)
+      message = null;
+      while ((searchEntries<1) && (message = r1.readMessage()) != null)
       {
-        debugInfo(tn, "ACI test : message returned " + 
-            message.getProtocolOpType() + message);
+        debugInfo(tn, "Search 11 Result=" + 
+            message.getProtocolOpType() + " " + message);
         switch (message.getProtocolOpType())
         {
         case LDAPConstants.OP_TYPE_SEARCH_RESULT_ENTRY:
           searchResultEntry = message.getSearchResultEntryProtocolOp();
-          //assertTrue(false, "Unexpected entry returned in ACI test of " + tn + searchResultEntry);
           searchEntries++;
           break;
 
@@ -1437,27 +1875,141 @@
 
         case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE:
           searchResultDone = message.getSearchResultDoneProtocolOp();
-          assertEquals(searchResultDone.getResultCode(), 
-              ResultCode.SUCCESS.getIntValue());
+          assertEquals(
+              searchResultDone.getResultCode(), ResultCode.SUCCESS,
+              searchResultDone.getErrorMessage().toString());
 //        assertEquals(InvocationCounterPlugin.waitForPostResponse(), 1);
           searchesDone++;
           break;
         }
       }
-      // search should end with success
-      assertTrue(searchesDone==1);
-      // but returning no entry
-      assertEquals(searchEntries,0, "Bad search entry# in ACI test of " + tn);
-      }      
+      sleep(1000);
+      /*
+      // Check we received change 11
+      for (LDAPAttribute a : searchResultEntry.getAttributes())
+      {
+        if (a.getAttributeType().equalsIgnoreCase("targetDN"))
+        {
+          for (ByteString av : a.getValues())
+          {
+            assertTrue(av.toString().equalsIgnoreCase(expectedDn11),
+                "Entry returned by psearch11 is " + av.toString() +
+                " when expected is " + expectedDn11);
+          }
+        }
+      }
+      */
+      debugInfo(tn, "Search 1 successfully receives additional changes");
 
-      try { s.close(); } catch (Exception e) {};
+      // wait 12 & 13
+      searchEntries = 0;
+      searchResultEntry = null;
+      searchResultDone = null;
+      message = null;
+      while ((searchEntries<2) && (message = r2.readMessage()) != null)
+      {
+        debugInfo(tn, "psearch search 12 Result=" + 
+            message.getProtocolOpType() + " " + message);
+        switch (message.getProtocolOpType())
+        {
+        case LDAPConstants.OP_TYPE_SEARCH_RESULT_ENTRY:
+          searchResultEntry = message.getSearchResultEntryProtocolOp();
+          searchEntries++;
+          break;
+
+        case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE:
+          searchReferences++;
+          break;
+
+        case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE:
+          searchResultDone = message.getSearchResultDoneProtocolOp();
+          assertEquals(
+              searchResultDone.getResultCode(), ResultCode.SUCCESS,
+              searchResultDone.getErrorMessage().toString());
+//        assertEquals(InvocationCounterPlugin.waitForPostResponse(), 1);
+          searchesDone++;
+          break;
+        }
+      }
+      sleep(1000);
+      /*
+      // Check we received change 12
+      for (LDAPAttribute a : searchResultEntry.getAttributes())
+      {
+        if (a.getAttributeType().equalsIgnoreCase("targetDN"))
+        {
+          for (ByteString av : a.getValues())
+          {
+            assertTrue(av.toString().equalsIgnoreCase(expectedDn12),
+                "Entry returned by psearch 12 is " + av.toString() +
+                " when expected is " + expectedDn12);
+          }
+        }
+      }
+      */
+      debugInfo(tn, "Search 2 successfully receives additional changes");
+
+      // wait 11 & 12 & 13
+      searchEntries = 0;
+      searchResultEntry = null;
+      searchResultDone = null;
+      message = null;
+      while ((searchEntries<3) && (message = r3.readMessage()) != null)
+      {
+        debugInfo(tn, "psearch search 13 Result=" + 
+            message.getProtocolOpType() + " " + message);
+        switch (message.getProtocolOpType())
+        {
+        case LDAPConstants.OP_TYPE_SEARCH_RESULT_ENTRY:
+          searchResultEntry = message.getSearchResultEntryProtocolOp();
+          searchEntries++;
+          break;
+
+        case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE:
+          searchReferences++;
+          break;
+
+        case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE:
+          searchResultDone = message.getSearchResultDoneProtocolOp();
+          assertEquals(
+              searchResultDone.getResultCode(), ResultCode.SUCCESS,
+              searchResultDone.getErrorMessage().toString());
+//        assertEquals(InvocationCounterPlugin.waitForPostResponse(), 1);
+          searchesDone++;
+          break;
+        }
+      }
+      sleep(1000);
+
+      // Check we received change 13
+      for (LDAPAttribute a : searchResultEntry.getAttributes())
+      {
+        if (a.getAttributeType().equalsIgnoreCase("targetDN"))
+        {
+          for (ByteString av : a.getValues())
+          {
+            assertTrue(av.toString().equalsIgnoreCase(expectedDn13),
+                "Entry returned by psearch 13 is " + av.toString() +
+                " when expected is " + expectedDn13);
+          }
+        }
+      }
+      debugInfo(tn, "Search 3 successfully receives additional changes");
+
+      server01.stop();
+      server02.stop();
+
+      try { s1.close(); } catch (Exception e) {};
+      try { s2.close(); } catch (Exception e) {};
+      try { s3.close(); } catch (Exception e) {};
+
       sleep(1000);
     }
     catch(Exception e)
     {
-      assertTrue(e==null, stackTraceToSingleLineString(e));
+      fail("Test " + tn + " fails with " +  stackTraceToSingleLineString(e));
     }
-    debugInfo(tn, "Ends test successfuly");
+    debugInfo(tn, "Ends test successfully");
   }
 
   // utility - bind as required
@@ -1522,7 +2074,7 @@
     TestCaseUtils.dsconfig(
         "delete-replication-server",
         "--provider-name", "Multimaster Synchronization");
-        */
+     */
     replicationServer = null;
   }
   /**
@@ -1549,7 +2101,7 @@
     {
       logError(Message.raw(Category.SYNC, Severity.NOTICE,
           "** TEST " + tn + " ** " + s));
-      TRACER.debugInfo("** TEST " + tn + " ** " + s);
+      //TRACER.debugInfo("** TEST " + tn + " ** " + s);
     }
   }
 
@@ -1558,7 +2110,7 @@
    */
   private static Backend initializeTestBackend2(boolean createBaseEntry)
   throws IOException, InitializationException, ConfigException,
-         DirectoryException
+  DirectoryException
   {
 
     DN baseDN = DN.decode(TEST_ROOT_DN_STRING2);
@@ -1590,11 +2142,849 @@
     }
     return memoryBackend;
   }
-  
+
   private static void removeTestBackend2(Backend backend)
   {
     MemoryBackend memoryBackend = (MemoryBackend)backend;
     memoryBackend.finalizeBackend();
     DirectoryServer.deregisterBackend(memoryBackend);
   }
+
+  //=======================================================
+  private void ChangeTimeHeartbeatTest()
+  {
+    String tn = "ChangeTimeHeartbeatTest";
+    debugInfo(tn, "Starting test");
+
+    try
+    {
+      // Initialize a second test backend
+      Backend backend2 = initializeTestBackend2(true);
+
+      // --
+      ReplicationBroker s1test = openReplicationSession(
+          DN.decode(TEST_ROOT_DN_STRING), (short) 1201, 
+          100, replicationServerPort,
+          1000, true);
+
+      ReplicationBroker s2test2 = openReplicationSession(
+          DN.decode(TEST_ROOT_DN_STRING2), (short) 1202, 
+          100, replicationServerPort,
+          1000, true, EMPTY_DN_GENID);
+      sleep(500);
+
+      // Produce updates
+      long time = TimeThread.getTime();
+      int ts = 1;
+      ChangeNumber cn = new ChangeNumber(time, ts++, s1test.getServerId());
+      DeleteMsg delMsg =
+        new DeleteMsg("uid="+tn+"1," + TEST_ROOT_DN_STRING, cn, tn+"uuid1");
+      s1test.publish(delMsg);
+      debugInfo(tn, " publishes " + delMsg.getChangeNumber());
+
+      cn = new ChangeNumber(time++, ts++, s2test2.getServerId());
+      delMsg =
+        new DeleteMsg("uid="+tn+"2," + TEST_ROOT_DN_STRING2, cn, tn+"uuid2");
+      s2test2.publish(delMsg);
+      debugInfo(tn, " publishes " + delMsg.getChangeNumber());
+
+      ChangeNumber cn3 = new ChangeNumber(time++, ts++, s2test2.getServerId());
+      delMsg =
+        new DeleteMsg("uid="+tn+"3," + TEST_ROOT_DN_STRING2, cn3, tn+"uuid3");
+      s2test2.publish(delMsg);
+      debugInfo(tn, " publishes " + delMsg.getChangeNumber());
+
+      cn = new ChangeNumber(time++, ts++, s1test.getServerId());
+      delMsg =
+        new DeleteMsg("uid="+tn+"4," + TEST_ROOT_DN_STRING, cn, tn+"uuid4");
+      s1test.publish(delMsg);
+      debugInfo(tn, " publishes " + delMsg.getChangeNumber());
+      sleep(500);
+
+      // --
+      ReplicationBroker s1test2 = openReplicationSession(
+          DN.decode(TEST_ROOT_DN_STRING2), (short) 1203, 
+          100, replicationServerPort,
+          1000, true, EMPTY_DN_GENID);
+
+      ReplicationBroker s2test = openReplicationSession(
+          DN.decode(TEST_ROOT_DN_STRING), (short) 1204, 
+          100, replicationServerPort,
+          1000, true);
+      sleep(500);
+
+      // Test startState ("first cookie") of the ECL
+      time = TimeThread.getTime();
+      cn = new ChangeNumber(time++, ts++, s1test2.getServerId());
+      delMsg =
+        new DeleteMsg("uid="+tn+"6," + TEST_ROOT_DN_STRING2, cn, tn+"uuid6");
+      s1test2.publish(delMsg);
+
+      cn = new ChangeNumber(time++, ts++, s2test.getServerId());
+      delMsg =
+        new DeleteMsg("uid="+tn+"7," + TEST_ROOT_DN_STRING, cn, tn+"uuid7");
+      s2test.publish(delMsg);
+
+      ChangeNumber cn8 = new ChangeNumber(time++, ts++, s1test2.getServerId());
+      delMsg =
+        new DeleteMsg("uid="+tn+"8," + TEST_ROOT_DN_STRING2, cn8, tn+"uuid8");
+      s1test2.publish(delMsg);
+
+      ChangeNumber cn9 = new ChangeNumber(time++, ts++, s2test.getServerId());
+      delMsg =
+        new DeleteMsg("uid="+tn+"9," + TEST_ROOT_DN_STRING, cn9, tn+"uuid9");
+      s2test.publish(delMsg);
+      sleep(500);
+
+      ReplicationServerDomain rsd1 =
+        replicationServer.getReplicationServerDomain(TEST_ROOT_DN_STRING, false);
+      rsd1.getDbServerState();
+      rsd1.getChangeTimeHeartbeatState();
+      debugInfo(tn, " DbServerState=" + rsd1.getDbServerState()
+          + " ChangeTimeHeartBeatState=" + rsd1.getChangeTimeHeartbeatState()
+          + " eligibleCN=" + rsd1.getEligibleCN());
+      // FIXME:ECL Enable this test by adding an assert on the right value
+
+      ReplicationServerDomain rsd2 =
+        replicationServer.getReplicationServerDomain(TEST_ROOT_DN_STRING2, false);
+      rsd2.getDbServerState();
+      rsd2.getChangeTimeHeartbeatState();
+      debugInfo(tn, " DbServerState=" + rsd2.getDbServerState()
+          + " ChangeTimeHeartBeatState=" + rsd2.getChangeTimeHeartbeatState()
+          + " eligibleCN=" + rsd2.getEligibleCN());
+      // FIXME:ECL Enable this test by adding an assert on the right value
+
+      s1test.stop();
+      s1test2.stop();
+      s2test.stop();
+      s2test2.stop();
+
+      removeTestBackend2(backend2);
+    }
+    catch(Exception e)
+    {
+      fail("Ending test " + tn + " with exception:"
+          +  stackTraceToSingleLineString(e));      
+    }
+    debugInfo(tn, "Ending test successfully");
+  }
+
+  /**
+   * From embedded ECL (no remote session)
+   * With empty RS, simple search should return only root entry.
+   */
+  private void ECLCompatEmpty()
+  {
+    String tn = "ECLCompatEmpty";
+    debugInfo(tn, "Starting test\n\n");
+
+    try
+    {
+      // search on 'cn=changelog'
+      String filter = "(objectclass=*)";
+      debugInfo(tn, " Search: " + filter);
+      InternalSearchOperation op = connection.processSearch(
+          ByteString.valueOf("cn=changelog"),
+          SearchScope.WHOLE_SUBTREE,
+          LDAPFilter.decode(filter));
+
+      // success
+      assertEquals(
+          op.getResultCode(), ResultCode.SUCCESS,
+          op.getErrorMessage().toString());
+
+      // root entry returned
+      assertEquals(op.getEntriesSent(), 1);
+      debugInfo(tn, "Ending test successfully");
+    }
+    catch(LDAPException e)
+    {
+      fail("Ending test " + tn + " with exception="
+          +  stackTraceToSingleLineString(e));      
+    }
+  }
+
+  private void ECLCompatWriteReadAllOps(int firstDraftChangeNumber)
+  {
+    String tn = "ECLCompatWriteReadAllOps/" + String.valueOf(firstDraftChangeNumber);
+    debugInfo(tn, "Starting test\n\n");
+
+    try
+    {
+      LDIFWriter ldifWriter = getLDIFWriter();
+
+      // Creates broker on o=test
+      ReplicationBroker server01 = openReplicationSession(
+          DN.decode(TEST_ROOT_DN_STRING), (short) 1201, 
+          100, replicationServerPort,
+          1000, true);
+      int ts = 1;
+
+      String user1entryUUID = "11111111-1112-1113-1114-111111111115";
+      String baseUUID       = "22222222-2222-2222-2222-222222222222";
+
+
+      // Publish DEL
+      ChangeNumber cn1 = new ChangeNumber(TimeThread.getTime(), ts++, (short)1201);
+      DeleteMsg delMsg =
+        new DeleteMsg("uid="+tn+"1," + TEST_ROOT_DN_STRING, cn1, 
+            user1entryUUID);
+      server01.publish(delMsg);
+      debugInfo(tn, " publishes " + delMsg.getChangeNumber());
+
+      // Publish ADD
+      gblCN = new ChangeNumber(TimeThread.getTime(), ts++, (short)1201);
+      String lentry = new String(
+          "dn: uid="+tn+"2," + TEST_ROOT_DN_STRING + "\n"
+          + "objectClass: top\n" 
+          + "objectClass: domain\n"
+          + "entryUUID: "+user1entryUUID+"\n");
+      Entry entry = TestCaseUtils.entryFromLdifString(lentry);
+      AddMsg addMsg = new AddMsg(
+          gblCN, 
+          "uid="+tn+"2," + TEST_ROOT_DN_STRING,
+          user1entryUUID, 
+          baseUUID, 
+          entry.getObjectClassAttribute(), 
+          entry.getAttributes(), 
+          new ArrayList<Attribute>());
+      server01.publish(addMsg);
+      debugInfo(tn, " publishes " + addMsg.getChangeNumber());
+
+      // Publish MOD
+      ChangeNumber cn3 = new ChangeNumber(TimeThread.getTime(), ts++, (short)1201);
+      Attribute attr1 = Attributes.create("description", "new value");
+      Modification mod1 = new Modification(ModificationType.REPLACE, attr1);
+      List<Modification> mods = new ArrayList<Modification>();
+      mods.add(mod1);
+      ModifyMsg modMsg = new ModifyMsg(cn3, DN
+          .decode("uid="+tn+"3," + TEST_ROOT_DN_STRING), mods, user1entryUUID);
+      server01.publish(modMsg);
+      debugInfo(tn, " publishes " + modMsg.getChangeNumber());
+
+      // Publish modDN
+      ChangeNumber cn4 = new ChangeNumber(TimeThread.getTime(), ts++, (short)1201);
+      ModifyDNOperationBasis op = new ModifyDNOperationBasis(connection, 1, 1, null,
+          DN.decode("uid="+tn+"4," + TEST_ROOT_DN_STRING), // entryDN
+          RDN.decode("uid="+tn+"new4"), // new rdn
+          true,  // deleteoldrdn
+          DN.decode(TEST_ROOT_DN_STRING2)); // new superior
+      op.setAttachment(SYNCHROCONTEXT, new ModifyDnContext(cn4, user1entryUUID,
+      "newparentId"));
+      LocalBackendModifyDNOperation localOp = new LocalBackendModifyDNOperation(op);
+      ModifyDNMsg modDNMsg = new ModifyDNMsg(localOp);
+      server01.publish(modDNMsg);
+      debugInfo(tn, " publishes " + modDNMsg.getChangeNumber());
+      sleep(1000);
+
+      // search on 'cn=changelog'
+      LinkedHashSet<String> attributes = new LinkedHashSet<String>();
+      attributes.add("+");
+      attributes.add("*");
+
+      String filter = "(targetdn=*"+tn.toLowerCase()+"*,o=test)";
+      debugInfo(tn, " Search: " + filter);
+      InternalSearchOperation searchOp = 
+        connection.processSearch(
+            ByteString.valueOf("cn=changelog"),
+            SearchScope.WHOLE_SUBTREE,
+            DereferencePolicy.NEVER_DEREF_ALIASES, 
+            0, // Size limit
+            0, // Time limit
+            false, // Types only
+            LDAPFilter.decode(filter),
+            attributes,
+            NO_CONTROL,
+            null);
+      sleep(500);
+
+      // test success
+      assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS,
+          searchOp.getErrorMessage().toString());
+
+      // test 4 entries returned
+      LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries();
+      // 4 entries expected
+      assertEquals(searchOp.getSearchEntries().size(), 4);
+      if (entries != null)
+      {
+        int i=0;
+        for (SearchResultEntry resultEntry : entries)
+        {
+          i++;
+          debugInfo(tn, "Result entry returned:" + resultEntry.toLDIFString());
+          ldifWriter.writeEntry(resultEntry);
+          if (i==1)
+          {
+            // check the DEL entry has the right content
+            assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase(
+                "cn="+String.valueOf(firstDraftChangeNumber+0)+",cn=changelog"));
+            checkValue(resultEntry,"replicationcsn",cn1.toString());
+            checkValue(resultEntry,"replicaidentifier","1201");
+            checkValue(resultEntry,"targetdn","uid="+tn+"1," + TEST_ROOT_DN_STRING);
+            checkValue(resultEntry,"changetype","delete");
+            checkValue(resultEntry,"changelogcookie","o=test:"+cn1.toString()+";o=test2:;");
+            checkValue(resultEntry,"targetentryuuid",user1entryUUID);
+            checkValue(resultEntry,"changenumber",String.valueOf(firstDraftChangeNumber+0));
+            checkValue(resultEntry,"targetuniqueid","11111111-11121113-11141111-11111115");
+          } else if (i==2)
+          {
+            // check the ADD entry has the right content
+            assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase(
+                "cn="+String.valueOf(firstDraftChangeNumber+1)+",cn=changelog"));
+            String expectedValue1 = "objectClass: domain\nobjectClass: top\n" +
+            "entryUUID: "+user1entryUUID+"\n\n";
+            String expectedValue2 = "entryUUID: "+user1entryUUID+"\n" +
+            "objectClass: domain\nobjectClass: top\n\n";
+            checkPossibleValues(resultEntry,"changes",expectedValue1, expectedValue2);
+            checkValue(resultEntry,"replicationcsn",gblCN.toString());
+            checkValue(resultEntry,"replicaidentifier","1201");
+            checkValue(resultEntry,"targetdn","uid="+tn+"2," + TEST_ROOT_DN_STRING);
+            checkValue(resultEntry,"changetype","add");
+            checkValue(resultEntry,"changelogcookie","o=test:"+gblCN.toString()+";o=test2:;");
+            checkValue(resultEntry,"targetentryuuid",user1entryUUID);
+            checkValue(resultEntry,"changenumber",String.valueOf(firstDraftChangeNumber+1));
+          } else if (i==3)
+          {
+            // check the MOD entry has the right content
+            assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase(
+                "cn="+String.valueOf(firstDraftChangeNumber+2)+",cn=changelog"));
+            String expectedValue = "replace: description\n" +
+            "description: new value\n-\n";
+            checkValue(resultEntry,"changes",expectedValue);
+            checkValue(resultEntry,"replicationcsn",cn3.toString());
+            checkValue(resultEntry,"replicaidentifier","1201");
+            checkValue(resultEntry,"targetdn","uid="+tn+"3," + TEST_ROOT_DN_STRING);
+            checkValue(resultEntry,"changetype","modify");
+            checkValue(resultEntry,"changelogcookie","o=test:"+cn3.toString()+";o=test2:;");
+            checkValue(resultEntry,"targetentryuuid",user1entryUUID);
+            checkValue(resultEntry,"changenumber",String.valueOf(firstDraftChangeNumber+2));
+          } else if (i==4)
+          {
+            // check the MODDN entry has the right content
+            assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase(
+                "cn="+String.valueOf(firstDraftChangeNumber+3)+",cn=changelog"));
+            checkValue(resultEntry,"replicationcsn",cn4.toString());
+            checkValue(resultEntry,"replicaidentifier","1201");
+            checkValue(resultEntry,"targetdn","uid="+tn+"4," + TEST_ROOT_DN_STRING);
+            checkValue(resultEntry,"changetype","modrdn");
+            checkValue(resultEntry,"changelogcookie","o=test:"+cn4.toString()+";o=test2:;");
+            checkValue(resultEntry,"targetentryuuid",user1entryUUID);
+            checkValue(resultEntry,"newrdn","uid="+tn+"new4");            
+            checkValue(resultEntry,"newsuperior",TEST_ROOT_DN_STRING2);
+            checkValue(resultEntry,"deleteoldrdn","true");
+            checkValue(resultEntry,"changenumber",String.valueOf(firstDraftChangeNumber+3));
+          }
+        }
+      }
+      server01.stop();
+
+      filter = "(&(targetdn=*"+tn.toLowerCase()+"*,o=test)(&(changenumber>="+
+      firstDraftChangeNumber+")(changenumber<="+(firstDraftChangeNumber+3)+")))";
+      debugInfo(tn, " Search: " + filter);
+      searchOp = 
+        connection.processSearch(
+            ByteString.valueOf("cn=changelog"),
+            SearchScope.WHOLE_SUBTREE,
+            DereferencePolicy.NEVER_DEREF_ALIASES, 
+            0, // Size limit
+            0, // Time limit
+            false, // Types only
+            LDAPFilter.decode(filter),
+            attributes,
+            NO_CONTROL,
+            null);
+      sleep(500);
+
+      // test success
+      assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS,
+          searchOp.getErrorMessage().toString());
+
+      entries = searchOp.getSearchEntries();
+
+      if (entries != null)
+      {
+        int i=0;
+        for (SearchResultEntry resultEntry : entries)
+        {
+          i++;
+          debugInfo(tn, "Result entry returned:" + resultEntry.toLDIFString());
+          ldifWriter.writeEntry(resultEntry);
+          if (i==1)
+          {
+            // check the DEL entry has the right content
+            assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase(
+                "cn="+String.valueOf(firstDraftChangeNumber+0)+",cn=changelog"));
+            checkValue(resultEntry,"replicationcsn",cn1.toString());
+            checkValue(resultEntry,"replicaidentifier","1201");
+            checkValue(resultEntry,"targetdn","uid="+tn+"1," + TEST_ROOT_DN_STRING);
+            checkValue(resultEntry,"changetype","delete");
+            checkValue(resultEntry,"changelogcookie","o=test:"+cn1.toString()+";o=test2:;");
+            checkValue(resultEntry,"targetentryuuid",user1entryUUID);
+            checkValue(resultEntry,"changenumber",String.valueOf(firstDraftChangeNumber+0));
+            checkValue(resultEntry,"targetuniqueid","11111111-11121113-11141111-11111115");
+          } else if (i==2)
+          {
+            // check the ADD entry has the right content
+            assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase(
+                "cn="+String.valueOf(firstDraftChangeNumber+1)+",cn=changelog"));
+            String expectedValue1 = "objectClass: domain\nobjectClass: top\n" +
+            "entryUUID: "+user1entryUUID+"\n\n";
+            String expectedValue2 = "entryUUID: "+user1entryUUID+"\n" +
+            "objectClass: domain\nobjectClass: top\n\n";
+            checkPossibleValues(resultEntry,"changes",expectedValue1, expectedValue2);
+            checkValue(resultEntry,"replicationcsn",gblCN.toString());
+            checkValue(resultEntry,"replicaidentifier","1201");
+            checkValue(resultEntry,"targetdn","uid="+tn+"2," + TEST_ROOT_DN_STRING);
+            checkValue(resultEntry,"changetype","add");
+            checkValue(resultEntry,"changelogcookie","o=test:"+gblCN.toString()+";o=test2:;");
+            checkValue(resultEntry,"targetentryuuid",user1entryUUID);
+            checkValue(resultEntry,"changenumber",String.valueOf(firstDraftChangeNumber+1));
+          } else if (i==3)
+          {
+            // check the MOD entry has the right content
+            assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase(
+                "cn="+String.valueOf(firstDraftChangeNumber+2)+",cn=changelog"));
+            String expectedValue = "replace: description\n" +
+            "description: new value\n-\n";
+            checkValue(resultEntry,"changes",expectedValue);
+            checkValue(resultEntry,"replicationcsn",cn3.toString());
+            checkValue(resultEntry,"replicaidentifier","1201");
+            checkValue(resultEntry,"targetdn","uid="+tn+"3," + TEST_ROOT_DN_STRING);
+            checkValue(resultEntry,"changetype","modify");
+            checkValue(resultEntry,"changelogcookie","o=test:"+cn3.toString()+";o=test2:;");
+            checkValue(resultEntry,"targetentryuuid",user1entryUUID);
+            checkValue(resultEntry,"changenumber",String.valueOf(firstDraftChangeNumber+2));
+          } else if (i==4)
+          {
+            // check the MODDN entry has the right content
+            assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase(
+                "cn="+String.valueOf(firstDraftChangeNumber+3)+",cn=changelog"));
+            checkValue(resultEntry,"replicationcsn",cn4.toString());
+            checkValue(resultEntry,"replicaidentifier","1201");
+            checkValue(resultEntry,"targetdn","uid="+tn+"4," + TEST_ROOT_DN_STRING);
+            checkValue(resultEntry,"changetype","modrdn");
+            checkValue(resultEntry,"changelogcookie","o=test:"+cn4.toString()+";o=test2:;");
+            checkValue(resultEntry,"targetentryuuid",user1entryUUID);
+            checkValue(resultEntry,"newrdn","uid="+tn+"new4");            
+            checkValue(resultEntry,"newsuperior",TEST_ROOT_DN_STRING2);
+            checkValue(resultEntry,"deleteoldrdn","true");
+            checkValue(resultEntry,"changenumber",String.valueOf(firstDraftChangeNumber+3));
+          }
+        }
+      }
+      assertEquals(searchOp.getSearchEntries().size(), 4);
+
+
+    }
+    catch(Exception e)
+    {
+      fail("Ending test " + tn + " with exception:"
+          +  stackTraceToSingleLineString(e));      
+    }
+    debugInfo(tn, "Ending test with success");
+  }
+
+  private void ECLCompatReadFrom(int firstDraftChangeNumber)
+  {
+    String tn = "ECLCompatReadFrom/" + String.valueOf(firstDraftChangeNumber);
+    debugInfo(tn, "Starting test\n\n");
+
+    try
+    {
+      LDIFWriter ldifWriter = getLDIFWriter();
+
+      // Creates broker on o=test
+      ReplicationBroker server01 = openReplicationSession(
+          DN.decode(TEST_ROOT_DN_STRING), (short) 1201, 
+          100, replicationServerPort,
+          1000, true);
+
+      String user1entryUUID = "11111111-1112-1113-1114-111111111115";
+
+      LinkedHashSet<String> attributes = new LinkedHashSet<String>();
+      attributes.add("+");
+      attributes.add("*");
+
+      String filter = "(changenumber="+firstDraftChangeNumber+")";
+      debugInfo(tn, " Search: " + filter);
+      InternalSearchOperation searchOp = 
+        connection.processSearch(
+            ByteString.valueOf("cn=changelog"),
+            SearchScope.WHOLE_SUBTREE,
+            DereferencePolicy.NEVER_DEREF_ALIASES, 
+            0, // Size limit
+            0, // Time limit
+            false, // Types only
+            LDAPFilter.decode(filter),
+            attributes,
+            NO_CONTROL,
+            null);
+      sleep(500);
+
+      // test success
+      assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS,
+          searchOp.getErrorMessage().toString());
+
+      LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries();
+      assertEquals(searchOp.getSearchEntries().size(), 1);
+      if (entries != null)
+      {
+        int i=0;
+        for (SearchResultEntry resultEntry : entries)
+        {
+          i++;
+          debugInfo(tn, "Result entry returned:" + resultEntry.toLDIFString());
+          ldifWriter.writeEntry(resultEntry);
+          // check the entry has the right content
+          assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase(
+              "cn=6,cn=changelog"));
+          checkValue(resultEntry,"replicationcsn",gblCN.toString());
+          checkValue(resultEntry,"replicaidentifier","1201");
+          checkValue(resultEntry,"changetype","add");
+          checkValue(resultEntry,"changelogcookie","o=test:"+gblCN.toString()+";o=test2:;");
+          checkValue(resultEntry,"targetentryuuid",user1entryUUID);
+          checkValue(resultEntry,"changenumber","6");
+        }
+      }
+      server01.stop();
+    }
+    catch(Exception e)
+    {
+      fail("Ending test " + tn + " with exception:\n"
+          +  stackTraceToSingleLineString(e));      
+    }
+    debugInfo(tn, "Ending test with success");
+  }
+
+  /**
+   * Read the ECL in compat mode from firstDraftChangeNumber and to
+   * lastDraftChangeNumber.
+   * @param firstDraftChangeNumber
+   * @param lastDraftChangeNumber
+   */
+  private void ECLCompatReadFromTo(int firstDraftChangeNumber,
+      int lastDraftChangeNumber)
+  {
+    String tn = "ECLCompatReadFromTo/" +
+    String.valueOf(firstDraftChangeNumber) + "/" +
+    String.valueOf(lastDraftChangeNumber);
+
+    debugInfo(tn, "Starting test\n\n");
+
+    try
+    {
+      // search on 'cn=changelog'
+      LinkedHashSet<String> attributes = new LinkedHashSet<String>();
+      attributes.add("+");
+      attributes.add("*");
+
+      String filter = "(&(changenumber>="+firstDraftChangeNumber+")(changenumber<="+lastDraftChangeNumber+"))";
+      debugInfo(tn, " Search: " + filter);
+      InternalSearchOperation searchOp = 
+        connection.processSearch(
+            ByteString.valueOf("cn=changelog"),
+            SearchScope.WHOLE_SUBTREE,
+            DereferencePolicy.NEVER_DEREF_ALIASES, 
+            0, // Size limit
+            0, // Time limit
+            false, // Types only
+            LDAPFilter.decode(filter),
+            attributes,
+            NO_CONTROL,
+            null);
+      sleep(500);
+
+      // test success
+      assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS,
+          searchOp.getErrorMessage().toString());      
+      assertEquals(searchOp.getSearchEntries().size(), 
+          lastDraftChangeNumber-firstDraftChangeNumber+1);
+    }
+    catch(Exception e)
+    {
+      fail("Ending test " + tn + " with exception:\n"
+          +  stackTraceToSingleLineString(e));      
+    }
+    debugInfo(tn, "Ending test with success");
+  }
+
+  /**
+   * Read the ECL in compat mode providing an unknown draft changenumber.
+   */
+  private void ECLCompatBadSeqnum()
+  {
+    String tn = "ECLCompatBadSeqnum";
+    debugInfo(tn, "Starting test\n\n");
+
+    try
+    {
+      // search on 'cn=changelog'
+      LinkedHashSet<String> attributes = new LinkedHashSet<String>();
+      attributes.add("+");
+      attributes.add("*");
+
+      String filter = "(changenumber=1000)";
+      debugInfo(tn, " Search: " + filter);
+      InternalSearchOperation searchOp = 
+        connection.processSearch(
+            ByteString.valueOf("cn=changelog"),
+            SearchScope.WHOLE_SUBTREE,
+            DereferencePolicy.NEVER_DEREF_ALIASES, 
+            0, // Size limit
+            0, // Time limit
+            false, // Types only
+            LDAPFilter.decode(filter),
+            attributes,
+            NO_CONTROL,
+            null);
+      sleep(500);
+
+      // test success and no entries returned
+      assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS, 
+          searchOp.getErrorMessage().toString());      
+      assertEquals(searchOp.getSearchEntries().size(), 0);
+    }
+    catch(Exception e)
+    {
+      fail("Ending test "+tn+" with exception:\n"
+          +  stackTraceToSingleLineString(e));      
+    }
+    debugInfo(tn, "Ending test with success");
+  }
+
+  /**
+   * Read the ECL in compat mode providing an unknown draft changenumber.
+   */
+  private void ECLFilterOnReplicationCsn()
+  {
+    String tn = "ECLFilterOnReplicationCsn";
+    debugInfo(tn, "Starting test\n\n");
+
+    try
+    {
+      LDIFWriter ldifWriter = getLDIFWriter();
+
+      // search on 'cn=changelog'
+      LinkedHashSet<String> attributes = new LinkedHashSet<String>();
+      attributes.add("+");
+      attributes.add("*");
+
+      String filter = "(replicationcsn="+this.gblCN+")";
+      debugInfo(tn, " Search: " + filter);
+      InternalSearchOperation searchOp = 
+        connection.processSearch(
+            ByteString.valueOf("cn=changelog"),
+            SearchScope.WHOLE_SUBTREE,
+            DereferencePolicy.NEVER_DEREF_ALIASES, 
+            0, // Size limit
+            0, // Time limit
+            false, // Types only
+            LDAPFilter.decode(filter),
+            attributes,
+            NO_CONTROL,
+            null);
+      sleep(500);
+
+      // test success and no entries returned
+      assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS, 
+          searchOp.getErrorMessage().toString());      
+      assertEquals(searchOp.getSearchEntries().size(), 1);
+      
+      LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries();
+      assertEquals(searchOp.getSearchEntries().size(), 1);
+      if (entries != null)
+      {
+        int i=0;
+        for (SearchResultEntry resultEntry : entries)
+        {
+          i++;
+          debugInfo(tn, "Result entry returned:" + resultEntry.toLDIFString());
+          ldifWriter.writeEntry(resultEntry);
+          // check the DEL entry has the right content
+          checkValue(resultEntry,"replicationcsn",gblCN.toString());
+          // TODO:ECL check values of the other attributes
+        }
+      }
+
+    }
+    catch(Exception e)
+    {
+      fail("Ending test "+tn+" with exception:\n"
+          +  stackTraceToSingleLineString(e));      
+    }
+    debugInfo(tn, "Ending test with success");
+  }
+
+  /**
+   * Test that different values of filter are correctly decoded
+   * to find if the search op on the ECL can be optimized
+   * regarding the Draft changenumbers.
+   */
+  private void ECLFilterTest()
+  {
+    String tn = "ECLFilterTest";
+    debugInfo(tn, "Starting test\n\n");
+    try
+    {
+      StartECLSessionMsg startCLmsg = new StartECLSessionMsg();
+
+      //
+      ECLSearchOperation.evaluateFilter(startCLmsg,
+          SearchFilter.createFilterFromString("(objectclass=*)"));
+      assertEquals(startCLmsg.getFirstDraftChangeNumber(),-1);
+      assertEquals(startCLmsg.getLastDraftChangeNumber(),-1);
+
+      //
+      ECLSearchOperation.evaluateFilter(startCLmsg,
+          SearchFilter.createFilterFromString("(changenumber>=2)"));
+      assertEquals(startCLmsg.getFirstDraftChangeNumber(),2);
+      assertEquals(startCLmsg.getLastDraftChangeNumber(),-1);
+
+      //
+      ECLSearchOperation.evaluateFilter(startCLmsg,
+          SearchFilter.createFilterFromString("(&(changenumber>=2)(changenumber<=5))"));
+      assertEquals(startCLmsg.getFirstDraftChangeNumber(),2);
+      assertEquals(startCLmsg.getLastDraftChangeNumber(),5);
+
+      //
+      try
+      {
+        ECLSearchOperation.evaluateFilter(startCLmsg,
+            SearchFilter.createFilterFromString("(&(changenumber>=2)(changenumber<+5))"));
+        assertTrue((startCLmsg.getFirstDraftChangeNumber()==1));
+      }
+      catch(DirectoryException de)
+      {
+        assertTrue(de != null);
+      }
+
+      //
+      ECLSearchOperation.evaluateFilter(startCLmsg,
+          SearchFilter.createFilterFromString("(&(dc=x)(&(changenumber>=2)(changenumber<=5)))"));
+      assertEquals(startCLmsg.getFirstDraftChangeNumber(),2);
+      assertEquals(startCLmsg.getLastDraftChangeNumber(),5);
+
+      ECLSearchOperation.evaluateFilter(startCLmsg,
+          SearchFilter.createFilterFromString("(&(&(changenumber>=3)(changenumber<=4))(&(|(dc=y)(dc=x))(&(changenumber>=2)(changenumber<=5))))"));
+      assertEquals(startCLmsg.getFirstDraftChangeNumber(),3);
+      assertEquals(startCLmsg.getLastDraftChangeNumber(),4);
+
+      //
+      ECLSearchOperation.evaluateFilter(startCLmsg,
+          SearchFilter.createFilterFromString("(|(objectclass=*)(&(changenumber>=2)(changenumber<=5)))"));
+      assertEquals(startCLmsg.getFirstDraftChangeNumber(),-1);
+      assertEquals(startCLmsg.getLastDraftChangeNumber(),-1);
+
+      //
+      ECLSearchOperation.evaluateFilter(startCLmsg,
+          SearchFilter.createFilterFromString("(changenumber=8)"));
+      assertEquals(startCLmsg.getFirstDraftChangeNumber(),8);
+      assertEquals(startCLmsg.getLastDraftChangeNumber(),8);
+
+      //
+      ChangeNumberGenerator gen = new ChangeNumberGenerator((short) 1, 0);
+      ChangeNumber changeNumber1 = gen.newChangeNumber();      
+      ECLSearchOperation.evaluateFilter(startCLmsg,
+          SearchFilter.createFilterFromString("(replicationcsn="+changeNumber1+")"));
+      assertEquals(startCLmsg.getFirstDraftChangeNumber(),-1);
+      assertEquals(startCLmsg.getLastDraftChangeNumber(),-1);
+      assertEquals(startCLmsg.getChangeNumber(), changeNumber1);
+
+      
+    }
+    catch(Exception e)
+    {
+      fail("Ending "+tn+" test with exception:\n"
+          +  stackTraceToSingleLineString(e));
+    }
+    debugInfo(tn, "Ending test with success");
+  }
+
+  private void ECLCompatPurge()
+  {
+    String tn = "ECLCompatPurge";
+    debugInfo(tn, "Starting test\n\n");
+    try
+    {
+      DraftCNDbHandler draftdb = replicationServer.getDraftCNDbHandler();
+      assertEquals(draftdb.count(), 8);
+      draftdb.setPurgeDelay(1000);
+
+      // Now Purge the changelog db
+      this.replicationServer.clearDb();
+
+      // Expect changes purged from the changelog db to be sometimes
+      // also purged from the DraftCNDb.
+      while(draftdb.count()>0)
+      {
+        debugInfo(tn, "draftdb.count="+draftdb.count());
+        sleep(200);
+      }
+    }
+    catch(Exception e)
+    {
+      fail("Ending "+tn+" test with exception:\n"
+          +  stackTraceToSingleLineString(e));
+    }
+    debugInfo(tn, "Ending test with success");
+  }
+
+  private void ECLCompatTestLimits(int expectedFirst, int expectedLast)
+  {
+    String tn = "ECLCompatTestLimits";
+    debugInfo(tn, "Starting test\n\n");
+    try
+    {
+      LDIFWriter ldifWriter = getLDIFWriter();
+
+      // search on 'cn=changelog'
+      LinkedHashSet<String> attributes = new LinkedHashSet<String>();
+      attributes.add("*");
+      attributes.add("+");
+
+      debugInfo(tn, " Search: rootDSE");
+      InternalSearchOperation searchOp = 
+        connection.processSearch(
+            ByteString.valueOf(""),
+            SearchScope.BASE_OBJECT,
+            DereferencePolicy.NEVER_DEREF_ALIASES, 
+            0, // Size limit
+            0, // Time limit
+            false, // Types only
+            LDAPFilter.decode("(objectclass=*)"),
+            attributes,
+            NO_CONTROL,
+            null);
+      sleep(500);
+
+      // test success and no entries returned
+      assertEquals(searchOp.getResultCode(), ResultCode.SUCCESS, 
+          searchOp.getErrorMessage().toString());      
+      assertEquals(searchOp.getSearchEntries().size(), 1);
+
+      LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries();
+      assertEquals(searchOp.getSearchEntries().size(), 1);
+      if (entries != null)
+      {
+        int i=0;
+        for (SearchResultEntry resultEntry : entries)
+        {
+          i++;
+          debugInfo(tn, "Result entry returned:" + resultEntry.toLDIFString());
+          ldifWriter.writeEntry(resultEntry);
+          checkValue(resultEntry,"firstchangenumber", 
+              String.valueOf(expectedFirst));
+          checkValue(resultEntry,"lastchangenumber", 
+              String.valueOf(expectedLast));
+        }
+      }
+    }
+    catch(Exception e)
+    {
+      fail("Ending "+tn+" test with exception:\n"
+          +  stackTraceToSingleLineString(e));
+    }
+    debugInfo(tn, "Ending test with success");
+  }
 }
\ No newline at end of file

--
Gitblit v1.10.0