From 3c028d4d56ba0a2ebd9bc86e39bcdee2e666fec7 Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Thu, 28 Aug 2014 09:02:40 +0000
Subject: [PATCH] Checkpoint commit for OPENDJ-1206 : Create a new ReplicationBackend/ChangelogBackend   to support cn=changelog

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java |  453 ++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 402 insertions(+), 51 deletions(-)

diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java
index 6e552a3..6964778 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java
@@ -32,6 +32,7 @@
 import static org.opends.server.replication.protocol.OperationContext.*;
 import static org.opends.server.types.ResultCode.*;
 import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
 import static org.testng.Assert.*;
 
 import java.io.ByteArrayOutputStream;
@@ -43,9 +44,12 @@
 import java.util.Set;
 import java.util.SortedSet;
 
+import org.assertj.core.api.Assertions;
 import org.opends.server.TestCaseUtils;
+import org.opends.server.api.Backend;
 import org.opends.server.backends.ChangelogBackend.SearchParams;
 import org.opends.server.controls.ExternalChangelogRequestControl;
+import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ModifyDNOperation;
 import org.opends.server.core.ModifyDNOperationBasis;
 import org.opends.server.loggers.debug.DebugTracer;
@@ -56,6 +60,7 @@
 import org.opends.server.replication.common.CSN;
 import org.opends.server.replication.common.CSNGenerator;
 import org.opends.server.replication.common.MultiDomainServerState;
+import org.opends.server.replication.common.ServerState;
 import org.opends.server.replication.plugin.DomainFakeCfg;
 import org.opends.server.replication.plugin.ExternalChangelogDomainFakeCfg;
 import org.opends.server.replication.plugin.LDAPReplicationDomain;
@@ -107,9 +112,12 @@
 
   private static final String USER1_ENTRY_UUID = "11111111-1111-1111-1111-111111111111";
   private static final long CHANGENUMBER_ZERO = 0L;
+
   private static final int SERVER_ID_1 = 1201;
   private static final int SERVER_ID_2 = 1202;
-  private static final String TEST_ROOT_DN_STRING2 = "o=test2";
+
+  private static final String TEST_BACKEND_ID2 = "test2";
+  private static final String TEST_ROOT_DN_STRING2 = "o=" + TEST_BACKEND_ID2;
   private static DN ROOT_DN_OTEST;
   private static DN ROOT_DN_OTEST2;
 
@@ -181,7 +189,7 @@
       @Override
       public boolean isECLEnabledDomain(DN baseDN)
       {
-        return baseDN.equals(ROOT_DN_OTEST);
+        return baseDN.equals(ROOT_DN_OTEST) || baseDN.equals(ROOT_DN_OTEST2);
       }
     });
     debugInfo("configure", "ReplicationServer created:" + replicationServer);
@@ -227,28 +235,230 @@
   }
 
   @Test(enabled=false)
-  public void searchChangesOnOneSuffixUsingEmptyCookie() throws Exception
+  public void searchInCookieModeOnOneSuffixUsingEmptyCookie() throws Exception
   {
-    String testName = "FourChangesCookie";
-    debugInfo(testName, "Starting test\n\n");
+    String test = "EmptyCookie";
+    debugInfo(test, "Starting test\n\n");
 
-    CSN[] csns = generateAndPublishChangesForEachOperationType(testName);
+    final CSN[] csns = generateAndPublishUpdateMsgForEachOperationType(test, true);
+    final String[] cookies = buildCookiesFromCsns(csns);
 
-    searchChangesForEachOperationTypeUsingEmptyCookie(csns, testName);
+    int nbEntries = 4;
+    String cookie = "";
+    InternalSearchOperation searchOp =
+        searchChangelogUsingCookie("(targetdn=*" + test + "*,o=test)", cookie, nbEntries, SUCCESS, test);
+
+    final List<SearchResultEntry> searchEntries = searchOp.getSearchEntries();
+    assertDelEntry(searchEntries.get(0), test + 1, test + "uuid1", CHANGENUMBER_ZERO, csns[0], cookies[0]);
+    assertAddEntry(searchEntries.get(1), test + 2, USER1_ENTRY_UUID, CHANGENUMBER_ZERO, csns[1], cookies[1]);
+    assertModEntry(searchEntries.get(2), test + 3, test + "uuid3", CHANGENUMBER_ZERO, csns[2], cookies[2]);
+    assertModDNEntry(searchEntries.get(3), test + 4, test + "new4", test+"uuid4", CHANGENUMBER_ZERO,
+        csns[3], cookies[3]);
+    assertResultsContainCookieControl(searchOp, cookies);
 
     assertChangelogAttributesInRootDSE(true, 1, 4);
 
-    debugInfo(testName, "Ending search with success");
+    debugInfo(test, "Ending search with success");
   }
 
   @Test(enabled=false)
-  public void searchChangesOnOneSuffixUsingDraftMode() throws Exception
+  public void searchInCookieModeOnOneSuffix() throws Exception
+  {
+    String test = "CookieOneSuffix";
+    debugInfo(test, "Starting test\n\n");
+    InternalSearchOperation searchOp = null;
+
+    final CSN[] csns = generateAndPublishUpdateMsgForEachOperationType(test, true);
+    final String[] cookies = buildCookiesFromCsns(csns);
+
+    // check querying with cookie of delete entry : should return  3 entries
+    int nbEntries = 3;
+    searchOp = searchChangelogUsingCookie("(targetdn=*" + test + "*,o=test)", cookies[0], nbEntries, SUCCESS, test);
+
+    List<SearchResultEntry> searchEntries = searchOp.getSearchEntries();
+    assertAddEntry(searchEntries.get(0), test + 2, USER1_ENTRY_UUID, CHANGENUMBER_ZERO, csns[1], cookies[1]);
+    assertModEntry(searchEntries.get(1), test + 3, test + "uuid3", CHANGENUMBER_ZERO, csns[2], cookies[2]);
+    assertModDNEntry(searchEntries.get(2), test + 4, test + "new4", test+"uuid4", CHANGENUMBER_ZERO,
+        csns[3], cookies[3]);
+
+    // check querying with cookie of add entry : should return 2 entries
+    nbEntries = 2;
+    searchOp = searchChangelogUsingCookie("(targetdn=*" + test + "*,o=test)", cookies[1], nbEntries, SUCCESS, test);
+
+    // check querying with cookie of mod entry : should return 1 entry
+    nbEntries = 1;
+    searchOp = searchChangelogUsingCookie("(targetdn=*" + test + "*,o=test)", cookies[2], nbEntries, SUCCESS, test);
+
+    searchEntries = searchOp.getSearchEntries();
+    assertModDNEntry(searchEntries.get(0), test + 4, test + "new4", test+"uuid4", CHANGENUMBER_ZERO,
+        csns[3], cookies[3]);
+
+    // check querying with cookie of mod dn entry : should return 0 entry
+    nbEntries = 0;
+    searchOp = searchChangelogUsingCookie("(targetdn=*" + test + "*,o=test)", cookies[3], nbEntries, SUCCESS, test);
+
+    debugInfo(test, "Ending search with success");
+
+  }
+
+  @Test(enabled=false)
+  public void searchInCookieModeAfterDomainIsRemoved() throws Exception
+  {
+    // TODO
+    // see testECLAfterDomainIsRemoved
+  }
+
+  @Test(enabled=false)
+  public void searchInCookieModeWithPrivateBackend() throws Exception
+  {
+    // TODO
+    // see ExternalChangeLogTest#ECLOnPrivateBackend
+  }
+
+  /**
+   * This test enables a second suffix. It will break all tests using search on
+   * one suffix if run before them, so it is necessary to add them as
+   * dependencies.
+   */
+  @Test(enabled=false, dependsOnMethods = {
+    "searchInCookieModeOnOneSuffixUsingEmptyCookie",
+    "searchInCookieModeOnOneSuffix",
+    "searchInCookieModeWithPrivateBackend",
+    "searchInCookieModeAfterDomainIsRemoved",
+    "searchInDraftModeOnOneSuffixMultipleTimes",
+    "searchInDraftModeOnOneSuffix",
+    "searchInDraftModeWithInvalidChangeNumber" })
+  public void searchInCookieModeOnTwoSuffixes() throws Exception
+  {
+    String test = "CookieTwoSuffixes";
+    debugInfo(test, "Starting test\n\n");
+    Backend<?> backendForSecondSuffix = null;
+    try
+    {
+      backendForSecondSuffix = initializeMemoryBackend(true, TEST_BACKEND_ID2);
+
+      // publish 4 changes (2 on each suffix)
+      long time = TimeThread.getTime();
+      int seqNum = 1;
+      CSN csn1 = new CSN(time, seqNum++, SERVER_ID_1);
+      CSN csn2 = new CSN(time, seqNum++, SERVER_ID_2);
+      CSN csn3 = new CSN(time, seqNum++, SERVER_ID_2);
+      CSN csn4 = new CSN(time, seqNum++, SERVER_ID_1);
+
+      publishUpdateMessagesInOTest(test, false, new UpdateMsg[] {
+        generateDeleteMsg(TEST_ROOT_DN_STRING,  csn1, test, 1) });
+
+      publishUpdateMessagesInOTest2(test, false, new UpdateMsg[] {
+        generateDeleteMsg(TEST_ROOT_DN_STRING2,  csn2, test, 2),
+        generateDeleteMsg(TEST_ROOT_DN_STRING2,  csn3, test, 3) });
+
+      publishUpdateMessagesInOTest(test, false, new UpdateMsg[] {
+        generateDeleteMsg(TEST_ROOT_DN_STRING,  csn4, test, 4) });
+
+      // search on all suffixes using empty cookie
+      String cookie = "";
+      InternalSearchOperation searchOp =
+          searchChangelogUsingCookie("(targetDN=*" + test + "*)", cookie, 4, SUCCESS, test);
+      cookie = readCookieFromNthEntry(searchOp.getSearchEntries(), 2);
+
+      // search using previous cookie and expect to get ONLY the 4th change
+      LDIFWriter ldifWriter = getLDIFWriter();
+      searchOp = searchChangelogUsingCookie("(targetDN=*" + test + "*)", cookie, 1, SUCCESS, test);
+      cookie = assertEntriesContainsCSNsAndReadLastCookie(test, searchOp.getSearchEntries(), ldifWriter, csn4);
+
+      // publish a new change on first suffix
+      CSN csn5 = new CSN(time, seqNum++, SERVER_ID_1);
+      publishUpdateMessagesInOTest(test, false, new UpdateMsg[] {
+        generateDeleteMsg(TEST_ROOT_DN_STRING,  csn5, test, 5) });
+
+      // search using last cookie and expect to get the last change
+      searchOp = searchChangelogUsingCookie("(targetDN=*" + test + "*)", cookie, 1, SUCCESS, test);
+      assertEntriesContainsCSNsAndReadLastCookie(test, searchOp.getSearchEntries(), ldifWriter, csn5);
+
+      // search on first suffix only, with empty cookie
+      cookie = "";
+      searchOp = searchChangelogUsingCookie("(targetDN=*" + test + "*,o=test)", cookie, 3, SUCCESS, test);
+      cookie = assertEntriesContainsCSNsAndReadLastCookie(test, searchOp.getSearchEntries(), ldifWriter,
+          csn1, csn4, csn5);
+
+      // publish 4 more changes (2 on each suffix, on differents server id)
+      time = TimeThread.getTime();
+      seqNum = 6;
+      int serverId11 = 1203;
+      int serverId22 = 1204;
+      CSN csn6 = new CSN(time, seqNum++, serverId11);
+      CSN csn7 = new CSN(time, seqNum++, serverId22);
+      CSN csn8 = new CSN(time, seqNum++, serverId11);
+      CSN csn9 = new CSN(time, seqNum++, serverId22);
+
+      publishUpdateMessages(test, ROOT_DN_OTEST2, serverId11, false,  new UpdateMsg[] {
+        generateDeleteMsg(TEST_ROOT_DN_STRING2,  csn6, test, 6) });
+
+      publishUpdateMessages(test, ROOT_DN_OTEST, serverId22, false,  new UpdateMsg[] {
+        generateDeleteMsg(TEST_ROOT_DN_STRING,  csn7, test, 7) });
+
+      publishUpdateMessages(test, ROOT_DN_OTEST2, serverId11, false,  new UpdateMsg[] {
+        generateDeleteMsg(TEST_ROOT_DN_STRING2,  csn8, test, 8) });
+
+      publishUpdateMessages(test, ROOT_DN_OTEST, serverId22, false,  new UpdateMsg[] {
+        generateDeleteMsg(TEST_ROOT_DN_STRING,  csn9, test, 9) });
+
+      // ensure oldest state is correct for each suffix and for each server id
+      final ServerState oldestState = getDomainOldestState(ROOT_DN_OTEST);
+      assertEquals(oldestState.getCSN(SERVER_ID_1), csn1);
+      assertEquals(oldestState.getCSN(serverId22), csn7);
+
+      final ServerState oldestState2 = getDomainOldestState(ROOT_DN_OTEST2);
+      assertEquals(oldestState2.getCSN(SERVER_ID_2), csn2);
+      assertEquals(oldestState2.getCSN(serverId11), csn6);
+
+      // test last cookie on root DSE
+      MultiDomainServerState expectedLastCookie =
+          new MultiDomainServerState("o=test:" + csn5 + " " + csn9 + ";o=test2:" + csn3 + " " + csn8 + ";");
+      final String lastCookie = readLastCookieFromRootDSE();
+      assertThat(lastCookie).isEqualTo(expectedLastCookie.toString());
+
+      // test unknown domain in provided cookie
+      // This case seems to be very hard to obtain in the real life
+      // (how to remove a domain from a RS topology ?)
+      final String cookie2 = lastCookie + "o=test6:";
+      debugInfo(test, "Search with bad domain in cookie=" + cookie);
+      searchOp = searchChangelogUsingCookie("(targetDN=*" + test + "*,o=test)", cookie2, 0, UNWILLING_TO_PERFORM, test);
+
+      // test missing domain in provided cookie
+      final String cookie3 = lastCookie.substring(lastCookie.indexOf(';')+1);
+      debugInfo(test, "Search with bad domain in cookie=" + cookie);
+      searchOp = searchChangelogUsingCookie("(targetDN=*" + test + "*,o=test)", cookie3, 0, UNWILLING_TO_PERFORM, test);
+      String expectedError = ERR_RESYNC_REQUIRED_MISSING_DOMAIN_IN_PROVIDED_COOKIE
+          .get("o=test:;","<"+ cookie3 + "o=test:;>").toString();
+      assertThat(searchOp.getErrorMessage().toString()).isEqualToIgnoringCase(expectedError);
+    }
+    finally
+    {
+      removeBackend(backendForSecondSuffix);
+      replicationServer.getChangelogDB().getReplicationDomainDB().removeDomain(ROOT_DN_OTEST2);
+    }
+  }
+
+  @Test(enabled=false)
+  public void searchInDraftModeWithInvalidChangeNumber() throws Exception
+  {
+    String testName = "UnknownChangeNumber";
+    debugInfo(testName, "Starting test\n\n");
+
+    searchChangelog("(changenumber=1000)", 0, SUCCESS, testName);
+
+    debugInfo(testName, "Ending test with success");
+  }
+
+  @Test(enabled=false)
+  public void searchInDraftModeOnOneSuffix() throws Exception
   {
     long firstChangeNumber = 1;
     String testName = "FourChanges/" + firstChangeNumber;
     debugInfo(testName, "Starting test\n\n");
 
-    CSN[] csns = generateAndPublishChangesForEachOperationType(testName);
+    CSN[] csns = generateAndPublishUpdateMsgForEachOperationType(testName, false);
 
     searchChangesForEachOperationTypeUsingDraftMode(firstChangeNumber, csns, testName);
 
@@ -258,18 +468,18 @@
   }
 
   @Test(enabled=false)
-  public void searchChangesOnOneSuffixMultipleTimesUsingDraftMode() throws Exception
+  public void searchInDraftModeOnOneSuffixMultipleTimes() throws Exception
   {
     replicationServer.getChangelogDB().setPurgeDelay(0);
 
     // write 4 changes starting from changenumber 1, and search them
     String testName = "Multiple/1";
-    CSN[] csns = generateAndPublishChangesForEachOperationType(testName);
+    CSN[] csns = generateAndPublishUpdateMsgForEachOperationType(testName, false);
     searchChangesForEachOperationTypeUsingDraftMode(1, csns, testName);
 
     // write 4 more changes starting from changenumber 5, and search them
     testName = "Multiple/5";
-    csns = generateAndPublishChangesForEachOperationType(testName);
+    csns = generateAndPublishUpdateMsgForEachOperationType(testName, false);
     searchChangesForEachOperationTypeUsingDraftMode(5, csns, testName);
 
     // search from the provided change number: 6 (should be the add msg)
@@ -284,7 +494,7 @@
 
     // add a new change, then check again first and last change number without previous search
     CSN csn = new CSN(TimeThread.getTime(), 10, SERVER_ID_1);
-    publishChanges(testName, generateDeleteMsg(TEST_ROOT_DN_STRING, csn, testName, 1));
+    publishUpdateMessagesInOTest(testName, false, generateDeleteMsg(TEST_ROOT_DN_STRING, csn, testName, 1));
 
     assertChangelogAttributesInRootDSE(true, 1, 9);
   }
@@ -294,7 +504,7 @@
    */
   // TODO : enable when code is checking the privileges correctly
   @Test(enabled=false)
-  public void searchingChangelogWithoutPrivilegeShouldFail() throws Exception
+  public void searchingWithoutPrivilegeShouldFail() throws Exception
   {
     AuthenticationInfo nonPrivilegedUser = new AuthenticationInfo();
 
@@ -305,6 +515,33 @@
     assertEquals(op.getErrorMessage().toMessage(), NOTE_SEARCH_CHANGELOG_INSUFFICIENT_PRIVILEGES.get());
   }
 
+  @Test(enabled=false)
+  public void persistentSearch() throws Exception
+  {
+   // TODO
+   // ExternalChangeLogTest#FullTestPersistentSearchWithChangesOnlyRequest
+   // ExternalChangeLogTest#FullTestPersistentSearchWithInitValuesRequest
+   // ExternalChangeLogTest#ECLReplicationServerFullTest16
+  }
+
+  @Test(enabled=false)
+  public void simultaneousPersistentSearches() throws Exception
+  {
+    // TODO
+    // see testECLAfterDomainIsRemoved
+  }
+
+  // TODO : other tests methods in ECLTest
+  // test search after a purge ? cf testECLAfterChangelogTrim
+  // testChangeTimeHeartbeat
+  // TestECLWithIncludeAttributes
+
+  // TODO: list of todos extracted from ExternalChangeLogTest
+  // ECL Test SEARCH abandon and check everything shutdown and cleaned
+  // ECL Test PSEARCH abandon and check everything shutdown and cleaned
+  // ECL Test the attributes list and values returned in ECL entries
+  // ECL Test search -s base, -s one
+
   /**
    * With an empty RS, a search should return only root entry.
    */
@@ -320,17 +557,6 @@
   }
 
   @Test(enabled=false)
-  public void searchWithUnknownChangeNumberShouldReturnNoResult() throws Exception
-  {
-    String testName = "UnknownChangeNumber";
-    debugInfo(testName, "Starting test\n\n");
-
-    searchChangelog("(changenumber=1000)", 0, SUCCESS, testName);
-
-    debugInfo(testName, "Ending test with success");
-  }
-
-  @Test(enabled=false)
   public void operationalAndVirtualAttributesShouldNotBeVisibleOutsideRootDSE() throws Exception
   {
     String testName = "attributesVisibleOutsideRootDSE";
@@ -455,6 +681,70 @@
     throw error;
   }
 
+  private String readLastCookieFromRootDSE() throws Exception
+  {
+    String cookie = "";
+    LDIFWriter ldifWriter = getLDIFWriter();
+
+    InternalSearchOperation searchOp = searchDNWithBaseScope("", newSet("lastExternalChangelogCookie"));
+    List<SearchResultEntry> entries = searchOp.getSearchEntries();
+    if (entries != null)
+    {
+      for (SearchResultEntry resultEntry : entries)
+      {
+        ldifWriter.writeEntry(resultEntry);
+        cookie = getAttributeValue(resultEntry, "lastexternalchangelogcookie");
+      }
+    }
+    return cookie;
+  }
+
+  private String assertLastCookieDifferentThanLastValue(final String lastCookie) throws Exception
+  {
+    int count = 0;
+    while (count < 100)
+    {
+      final String newCookie = readLastCookieFromRootDSE();
+      if (!newCookie.equals(lastCookie))
+      {
+        return newCookie;
+      }
+      count++;
+      Thread.sleep(10);
+    }
+    Assertions.fail("Expected last cookie should have been updated, but it always stayed at value '" + lastCookie + "'");
+    return null;// dead code
+  }
+
+  private String readCookieFromNthEntry(List<SearchResultEntry> entries, int i)
+  {
+    SearchResultEntry entry = entries.get(i);
+    return entry.getAttribute("changelogcookie").get(0).iterator().next().toString();
+  }
+
+  private String assertEntriesContainsCSNsAndReadLastCookie(String test, List<SearchResultEntry> entries,
+      LDIFWriter ldifWriter, CSN... csns) throws Exception
+  {
+    assertThat(getCSNsFromEntries(entries)).containsExactly(csns);
+    debugAndWriteEntries(ldifWriter, entries, test);
+    return readCookieFromNthEntry(entries, csns.length - 1);
+  }
+
+  private List<CSN> getCSNsFromEntries(List<SearchResultEntry> entries)
+  {
+    List<CSN> results = new ArrayList<CSN>(entries.size());
+    for (SearchResultEntry entry : entries)
+    {
+      results.add(new CSN(getAttributeValue(entry, "replicationCSN")));
+    }
+    return results;
+  }
+
+  private ServerState getDomainOldestState(DN baseDN)
+  {
+    return replicationServer.getReplicationServerDomain(baseDN).getOldestState();
+  }
+
   private void assertSearchParameters(SearchParams searchParams, long firstChangeNumber,
       long lastChangeNumber, CSN csn) throws Exception
   {
@@ -463,32 +753,58 @@
     assertEquals(searchParams.getCSN(), csn == null ? new CSN(0, 0, 0) : csn);
   }
 
-  private CSN[] generateAndPublishChangesForEachOperationType(String testName) throws Exception
+  private CSN[] generateAndPublishUpdateMsgForEachOperationType(String testName, boolean checkLastCookie)
+      throws Exception
   {
     CSN[] csns = generateCSNs(4, SERVER_ID_1);
 
-    List<UpdateMsg> messages = new ArrayList<UpdateMsg>();
-    messages.add(generateDeleteMsg(TEST_ROOT_DN_STRING, csns[0], testName, 1));
-    messages.add(generateAddMsg(TEST_ROOT_DN_STRING, csns[1], USER1_ENTRY_UUID, testName));
-    messages.add(generateModMsg(TEST_ROOT_DN_STRING, csns[2], testName));
-    messages.add(generateModDNMsg(TEST_ROOT_DN_STRING, csns[3], testName));
+    List<UpdateMsg> updateMsgs = new ArrayList<UpdateMsg>();
+    updateMsgs.add(generateDeleteMsg(TEST_ROOT_DN_STRING, csns[0], testName, 1));
+    updateMsgs.add(generateAddMsg(TEST_ROOT_DN_STRING, csns[1], USER1_ENTRY_UUID, testName));
+    updateMsgs.add(generateModMsg(TEST_ROOT_DN_STRING, csns[2], testName));
+    updateMsgs.add(generateModDNMsg(TEST_ROOT_DN_STRING, csns[3], testName));
 
-    publishChanges(testName, messages.toArray(new UpdateMsg[4]));
+    publishUpdateMessagesInOTest(testName, checkLastCookie, updateMsgs.toArray(new UpdateMsg[4]));
     return csns;
   }
 
-  /** Publish a list changes to the default replication broker used by tests. */
-  private void publishChanges(String testName, UpdateMsg...messages) throws Exception
+  // shortcut method for default base DN and server id used in tests
+  private void publishUpdateMessagesInOTest(String testName, boolean checkLastCookie, UpdateMsg...messages)
+      throws Exception
+  {
+    publishUpdateMessages(testName, ROOT_DN_OTEST, SERVER_ID_1, checkLastCookie, messages);
+  }
+
+  private void publishUpdateMessagesInOTest2(String testName, boolean checkLastCookie, UpdateMsg...messages)
+      throws Exception
+  {
+    publishUpdateMessages(testName, ROOT_DN_OTEST2, SERVER_ID_2, checkLastCookie, messages);
+  }
+
+  /**
+   * Publish a list of update messages to the replication broker corresponding to given baseDN and server id.
+   *
+   * @param checkLastCookie if true, checks that last cookie is update after each message publication
+   */
+  private void publishUpdateMessages(String testName, DN baseDN, int serverId, boolean checkLastCookie,
+      UpdateMsg...messages) throws Exception
   {
     Pair<ReplicationBroker, LDAPReplicationDomain> replicationObjects = null;
     try
     {
-      replicationObjects = enableReplication(ROOT_DN_OTEST, SERVER_ID_1, replicationServerPort, brokerSessionTimeout);
+      replicationObjects = enableReplication(baseDN, serverId, replicationServerPort, brokerSessionTimeout);
       ReplicationBroker broker = replicationObjects.getFirst();
+      String cookie = "";
       for (UpdateMsg msg : messages)
       {
         debugInfo(testName, " publishes " + msg.getCSN());
+
         broker.publish(msg);
+
+        if (checkLastCookie)
+        {
+          cookie = assertLastCookieDifferentThanLastValue(cookie);
+        }
       }
     }
     finally
@@ -501,26 +817,14 @@
     }
   }
 
-  private void searchChangesForEachOperationTypeUsingEmptyCookie(CSN[] csns, String testName) throws Exception
+  private String[] buildCookiesFromCsns(CSN[] csns)
   {
-    int nbEntries = 4;
-    String cookie= "";
-
-    InternalSearchOperation searchOp =
-        searchChangelogUsingCookie("(targetdn=*" + testName + "*,o=test)", cookie, nbEntries, SUCCESS, testName);
-
-    final String[] cookies = new String[nbEntries];
+    final String[] cookies = new String[csns.length];
     for (int j = 0; j < cookies.length; j++)
     {
       cookies[j] = "o=test:" + csns[j] + ";";
     }
-    final List<SearchResultEntry> searchEntries = searchOp.getSearchEntries();
-    assertDelEntry(searchEntries.get(0), testName + 1, testName + "uuid1", CHANGENUMBER_ZERO, csns[0], cookies[0]);
-    assertAddEntry(searchEntries.get(1), testName + 2, USER1_ENTRY_UUID, CHANGENUMBER_ZERO, csns[1], cookies[1]);
-    assertModEntry(searchEntries.get(2), testName + 3, testName + "uuid3", CHANGENUMBER_ZERO, csns[2], cookies[2]);
-    assertModDNEntry(searchEntries.get(3), testName + 4, testName + "new4", testName+"uuid4", CHANGENUMBER_ZERO,
-        csns[3], cookies[3]);
-    assertResultsContainCookieControl(searchOp, cookies);
+    return cookies;
   }
 
   private void searchChangesForEachOperationTypeUsingDraftMode(long firstChangeNumber, CSN[] csns, String testName)
@@ -954,6 +1258,53 @@
   }
 
   /**
+   * Creates a memory backend, to be used as additional backend in tests.
+   */
+  private static Backend<?> initializeMemoryBackend(boolean createBaseEntry, String backendId) throws Exception
+  {
+    DN baseDN = DN.decode("o=" + backendId);
+
+    //  Retrieve backend. Warning: it is important to perform this each time,
+    //  because a test may have disabled then enabled the backend (i.e a test
+    //  performing an import task). As it is a memory backend, when the backend
+    //  is re-enabled, a new backend object is in fact created and old reference
+    //  to memory backend must be invalidated. So to prevent this problem, we
+    //  retrieve the memory backend reference each time before cleaning it.
+    MemoryBackend memoryBackend = (MemoryBackend) DirectoryServer.getBackend(backendId);
+
+    if (memoryBackend == null)
+    {
+      memoryBackend = new MemoryBackend();
+      memoryBackend.setBackendID(backendId);
+      memoryBackend.setBaseDNs(new DN[] {baseDN});
+      memoryBackend.initializeBackend();
+      DirectoryServer.registerBackend(memoryBackend);
+    }
+
+    memoryBackend.clearMemoryBackend();
+
+    if (createBaseEntry)
+    {
+      memoryBackend.addEntry(createEntry(baseDN), null);
+    }
+    return memoryBackend;
+  }
+
+  private static void removeBackend(Backend<?>... backends)
+  {
+    for (Backend<?> backend : backends)
+    {
+      if (backend != null)
+      {
+        MemoryBackend memoryBackend = (MemoryBackend) backend;
+        memoryBackend.clearMemoryBackend();
+        memoryBackend.finalizeBackend();
+        DirectoryServer.deregisterBackend(memoryBackend);
+      }
+    }
+  }
+
+  /**
    * Utility - log debug message - highlight it is from the test and not
    * from the server code. Makes easier to observe the test steps.
    */

--
Gitblit v1.10.0