From 411f30e5407cba4c5a045c5a5613966e01fe0a63 Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Fri, 29 Aug 2014 08:20:20 +0000
Subject: [PATCH] Checkpoint commit for OPENDJ-1206 : Create a new ReplicationBackend/ChangelogBackend   to support cn=changelog

---
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java |  177 ++++++++++++++++++++++++++++++++------------
 opendj-sdk/opends/src/server/org/opends/server/backends/ChangelogBackend.java                                 |   45 +++++++---
 2 files changed, 159 insertions(+), 63 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/ChangelogBackend.java b/opendj-sdk/opends/src/server/org/opends/server/backends/ChangelogBackend.java
index 1a1ec5c..0f0da2a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/ChangelogBackend.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/ChangelogBackend.java
@@ -389,20 +389,9 @@
   @Override
   public void search(final SearchOperation searchOperation) throws DirectoryException
   {
-    final Set<String> excludedDomains = MultimasterReplication.getECLDisabledDomains();
-    excludedDomains.add(DN_EXTERNAL_CHANGELOG_ROOT);
-    SearchParams params = new SearchParams(searchOperation.toString(), excludedDomains);
-    final ExternalChangelogRequestControl eclRequestControl =
-        searchOperation.getRequestControl(ExternalChangelogRequestControl.DECODER);
-    if (eclRequestControl == null)
-    {
-      params.requestType = REQUEST_TYPE_FROM_CHANGE_NUMBER;
-    }
-    else
-    {
-      params.requestType = REQUEST_TYPE_FROM_COOKIE;
-      params.multiDomainServerState = eclRequestControl.getCookie();
-    }
+    checkChangelogReadPrivilege(searchOperation);
+
+    final SearchParams params = buildSearchParameters(searchOperation);
 
     optimizeSearchParameters(params, searchOperation.getBaseDN(), searchOperation.getFilter());
     try
@@ -418,6 +407,25 @@
     }
   }
 
+  private SearchParams buildSearchParameters(final SearchOperation searchOperation) throws DirectoryException
+  {
+    final Set<String> excludedDomains = MultimasterReplication.getECLDisabledDomains();
+    excludedDomains.add(DN_EXTERNAL_CHANGELOG_ROOT);
+    final SearchParams params = new SearchParams(searchOperation.toString(), excludedDomains);
+    final ExternalChangelogRequestControl eclRequestControl =
+        searchOperation.getRequestControl(ExternalChangelogRequestControl.DECODER);
+    if (eclRequestControl == null)
+    {
+      params.requestType = REQUEST_TYPE_FROM_CHANGE_NUMBER;
+    }
+    else
+    {
+      params.requestType = REQUEST_TYPE_FROM_COOKIE;
+      params.multiDomainServerState = eclRequestControl.getCookie();
+    }
+    return params;
+  }
+
   /** {@inheritDoc} */
   @Override
   public Set<String> getSupportedControls()
@@ -1198,6 +1206,15 @@
         + entryDN + "\" into an External Change Log entry: " + exception.getMessage()));
   }
 
+  private void checkChangelogReadPrivilege(SearchOperation searchOp) throws DirectoryException
+  {
+    if (!searchOp.getClientConnection().hasPrivilege(Privilege.CHANGELOG_READ, searchOp))
+    {
+      throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
+          NOTE_SEARCH_CHANGELOG_INSUFFICIENT_PRIVILEGES.get());
+    }
+  }
+
   /**
    * Create a changelog entry from a set of provided information. This is the part of
    * entry creation common to all types of msgs (ADD, DEL, MOD, MODDN).
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java
index 6964778..1e667a9 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java
@@ -46,6 +46,7 @@
 
 import org.assertj.core.api.Assertions;
 import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.std.server.ExternalChangelogDomainCfg;
 import org.opends.server.api.Backend;
 import org.opends.server.backends.ChangelogBackend.SearchParams;
 import org.opends.server.controls.ExternalChangelogRequestControl;
@@ -70,6 +71,8 @@
 import org.opends.server.replication.protocol.ModifyDNMsg;
 import org.opends.server.replication.protocol.ModifyDnContext;
 import org.opends.server.replication.protocol.ModifyMsg;
+import org.opends.server.replication.protocol.ReplicationMsg;
+import org.opends.server.replication.protocol.ResetGenerationIdMsg;
 import org.opends.server.replication.protocol.UpdateMsg;
 import org.opends.server.replication.server.ReplServerFakeConfiguration;
 import org.opends.server.replication.server.ReplicationServer;
@@ -117,9 +120,12 @@
   private static final int SERVER_ID_2 = 1202;
 
   private static final String TEST_BACKEND_ID2 = "test2";
+  private static final String TEST_BACKEND_ID3 = "test3";
   private static final String TEST_ROOT_DN_STRING2 = "o=" + TEST_BACKEND_ID2;
-  private static DN ROOT_DN_OTEST;
-  private static DN ROOT_DN_OTEST2;
+  private static final String TEST_ROOT_DN_STRING3 = "o=" + TEST_BACKEND_ID3;
+  private static DN DN_OTEST;
+  private static DN DN_OTEST2;
+  private static DN DN_OTEST3;
 
   private final int brokerSessionTimeout = 5000;
   private final int maxWindow = 100;
@@ -142,8 +148,9 @@
   public void setUp() throws Exception
   {
     super.setUp();
-    ROOT_DN_OTEST = DN.decode(TEST_ROOT_DN_STRING);
-    ROOT_DN_OTEST2 = DN.decode(TEST_ROOT_DN_STRING2);
+    DN_OTEST = DN.decode(TEST_ROOT_DN_STRING);
+    DN_OTEST2 = DN.decode(TEST_ROOT_DN_STRING2);
+    DN_OTEST3 = DN.decode(TEST_ROOT_DN_STRING3);
 
     // This test suite depends on having the schema available.
     configureReplicationServer();
@@ -189,7 +196,7 @@
       @Override
       public boolean isECLEnabledDomain(DN baseDN)
       {
-        return baseDN.equals(ROOT_DN_OTEST) || baseDN.equals(ROOT_DN_OTEST2);
+        return baseDN.equals(DN_OTEST) || baseDN.equals(DN_OTEST2) || baseDN.equals(DN_OTEST3);
       }
     });
     debugInfo("configure", "ReplicationServer created:" + replicationServer);
@@ -304,15 +311,36 @@
   @Test(enabled=false)
   public void searchInCookieModeAfterDomainIsRemoved() throws Exception
   {
-    // TODO
-    // see testECLAfterDomainIsRemoved
-  }
+    String test = "CookieAfterDomainIsRemoved";
+    debugInfo(test, "Starting test");
 
-  @Test(enabled=false)
-  public void searchInCookieModeWithPrivateBackend() throws Exception
-  {
-    // TODO
-    // see ExternalChangeLogTest#ECLOnPrivateBackend
+    final CSN[] csns = generateCSNs(3, SERVER_ID_1);
+
+    publishUpdateMessagesInOTest(test, true,
+        generateDeleteMsg(TEST_ROOT_DN_STRING,  csns[0], test, 1),
+        generateDeleteMsg(TEST_ROOT_DN_STRING,  csns[1], test, 2),
+        generateDeleteMsg(TEST_ROOT_DN_STRING,  csns[2], test, 3));
+
+    InternalSearchOperation searchOp = searchChangelogUsingCookie("(targetDN=*)", "", 3, SUCCESS, test);
+    String firstCookie = readCookieFromNthEntry(searchOp.getSearchEntries(), 0);
+
+    // remove the domain by sending a reset message
+    publishUpdateMessages(test, DN_OTEST, SERVER_ID_1, false, new ResetGenerationIdMsg(23657));
+
+
+    // replication changelog must have been cleared
+    String cookie= "";
+    searchChangelogUsingCookie("(targetDN=*)", cookie, 0, SUCCESS, test);
+
+    cookie = readLastCookieFromRootDSE();
+    searchChangelogUsingCookie("(targetDN=*)", cookie, 0, SUCCESS, test);
+
+    // search with an old cookie
+    searchOp = searchChangelogUsingCookie("(targetDN=*)", firstCookie, 0, UNWILLING_TO_PERFORM, test);
+    assertThat(searchOp.getErrorMessage().toString()).
+      contains("unknown replicated domain", TEST_ROOT_DN_STRING.toString());
+
+    debugInfo(test, "Ending test successfully");
   }
 
   /**
@@ -323,7 +351,6 @@
   @Test(enabled=false, dependsOnMethods = {
     "searchInCookieModeOnOneSuffixUsingEmptyCookie",
     "searchInCookieModeOnOneSuffix",
-    "searchInCookieModeWithPrivateBackend",
     "searchInCookieModeAfterDomainIsRemoved",
     "searchInDraftModeOnOneSuffixMultipleTimes",
     "searchInDraftModeOnOneSuffix",
@@ -345,15 +372,13 @@
       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) });
+      publishUpdateMessagesInOTest(test, false, generateDeleteMsg(TEST_ROOT_DN_STRING,  csn1, test, 1));
 
-      publishUpdateMessagesInOTest2(test, false, new UpdateMsg[] {
+      publishUpdateMessagesInOTest2(test, false,
         generateDeleteMsg(TEST_ROOT_DN_STRING2,  csn2, test, 2),
-        generateDeleteMsg(TEST_ROOT_DN_STRING2,  csn3, test, 3) });
+        generateDeleteMsg(TEST_ROOT_DN_STRING2,  csn3, test, 3));
 
-      publishUpdateMessagesInOTest(test, false, new UpdateMsg[] {
-        generateDeleteMsg(TEST_ROOT_DN_STRING,  csn4, test, 4) });
+      publishUpdateMessagesInOTest(test, false, generateDeleteMsg(TEST_ROOT_DN_STRING,  csn4, test, 4));
 
       // search on all suffixes using empty cookie
       String cookie = "";
@@ -368,8 +393,7 @@
 
       // 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) });
+      publishUpdateMessagesInOTest(test, false, 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);
@@ -391,24 +415,24 @@
       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, DN_OTEST2, serverId11, false,
+          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, DN_OTEST, serverId22, false,
+          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, DN_OTEST2, serverId11, false,
+          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) });
+      publishUpdateMessages(test, DN_OTEST, serverId22, false,
+          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);
+      final ServerState oldestState = getDomainOldestState(DN_OTEST);
       assertEquals(oldestState.getCSN(SERVER_ID_1), csn1);
       assertEquals(oldestState.getCSN(serverId22), csn7);
 
-      final ServerState oldestState2 = getDomainOldestState(ROOT_DN_OTEST2);
+      final ServerState oldestState2 = getDomainOldestState(DN_OTEST2);
       assertEquals(oldestState2.getCSN(SERVER_ID_2), csn2);
       assertEquals(oldestState2.getCSN(serverId11), csn6);
 
@@ -436,10 +460,66 @@
     finally
     {
       removeBackend(backendForSecondSuffix);
-      replicationServer.getChangelogDB().getReplicationDomainDB().removeDomain(ROOT_DN_OTEST2);
+      //replicationServer.getChangelogDB().getReplicationDomainDB().removeDomain(ROOT_DN_OTEST2);
     }
   }
 
+  @Test(enabled=false, dependsOnMethods = { "searchInCookieModeOnTwoSuffixes" })
+  public void searchInCookieModeOnTwoSuffixesWithPrivateBackend() throws Exception
+  {
+      String test = "CookiePrivateBackend";
+      debugInfo(test, "Starting test");
+
+      // Use o=test3 to avoid collision with o=test2 used by searchInCookieModeOnTwoSuffixes test
+      Backend<?> backend3 = null;
+      Pair<ReplicationBroker,LDAPReplicationDomain> replication1 = null;
+      LDAPReplicationDomain domain2 = null;
+      try {
+        replication1 = enableReplication(DN_OTEST, SERVER_ID_1, replicationServerPort, brokerSessionTimeout);
+
+        // create and publish 1 change on each suffix
+        long time = TimeThread.getTime();
+        CSN csn1 = new CSN(time, 1, SERVER_ID_1);
+        ReplicationBroker broker = replication1.getFirst();
+        broker.publish(generateDeleteMsg(TEST_ROOT_DN_STRING,  csn1, test, 1));
+
+        // create backend and configure replication for it
+        backend3 = initializeMemoryBackend(false, TEST_BACKEND_ID3);
+        backend3.setPrivateBackend(true);
+        DomainFakeCfg domainConf2 = new DomainFakeCfg(DN_OTEST3, 1602,
+            newSortedSet("localhost:" + replicationServerPort));
+        domain2 = startNewReplicationDomain(domainConf2, null, null);
+
+        // add a root entry to the backend
+        Thread.sleep(1000);
+        addEntry(createEntry(DN_OTEST3));
+
+        // expect entry from o=test2 to be returned
+        String cookie = "";
+        searchChangelogUsingCookie("(targetDN=*)", cookie, 2, SUCCESS, test);
+
+        ExternalChangelogDomainCfg eclCfg = new ExternalChangelogDomainFakeCfg(false, null, null);
+        domainConf2.setExternalChangelogDomain(eclCfg);
+        domain2.applyConfigurationChange(domainConf2);
+
+        // expect only entry from o=test returned
+        searchChangelogUsingCookie("(targetDN=*)", cookie, 1, SUCCESS, test);
+
+        // test the lastExternalChangelogCookie attribute of the ECL
+        // (does only refer to non private backend)
+        String expectedLastCookie = "o=test:" + csn1 + ";";
+        String lastCookie = readLastCookieFromRootDSE();
+        assertThat(expectedLastCookie.toString()).isEqualTo(lastCookie);
+      }
+      finally
+      {
+        removeReplicationDomains(replication1.getSecond(), domain2);
+        removeBackend(backend3);
+        stop(replication1.getFirst());
+      }
+      debugInfo(test, "Ending test successfully");
+  }
+
   @Test(enabled=false)
   public void searchInDraftModeWithInvalidChangeNumber() throws Exception
   {
@@ -502,7 +582,6 @@
   /**
    * Verifies that is not possible to read the changelog without the changelog-read privilege
    */
-  // TODO : enable when code is checking the privileges correctly
   @Test(enabled=false)
   public void searchingWithoutPrivilegeShouldFail() throws Exception
   {
@@ -581,7 +660,7 @@
   }
 
   @DataProvider()
-  public Object[][] getFilters()
+  Object[][] getFilters()
   {
     return new Object[][] {
       // base DN, filter, expected first change number, expected last change number
@@ -757,14 +836,11 @@
       throws Exception
   {
     CSN[] csns = generateCSNs(4, SERVER_ID_1);
-
-    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));
-
-    publishUpdateMessagesInOTest(testName, checkLastCookie, updateMsgs.toArray(new UpdateMsg[4]));
+    publishUpdateMessagesInOTest(testName, checkLastCookie,
+        generateDeleteMsg(TEST_ROOT_DN_STRING, csns[0], testName, 1),
+        generateAddMsg(TEST_ROOT_DN_STRING, csns[1], USER1_ENTRY_UUID, testName),
+        generateModMsg(TEST_ROOT_DN_STRING, csns[2], testName),
+        generateModDNMsg(TEST_ROOT_DN_STRING, csns[3], testName));
     return csns;
   }
 
@@ -772,13 +848,13 @@
   private void publishUpdateMessagesInOTest(String testName, boolean checkLastCookie, UpdateMsg...messages)
       throws Exception
   {
-    publishUpdateMessages(testName, ROOT_DN_OTEST, SERVER_ID_1, checkLastCookie, messages);
+    publishUpdateMessages(testName, 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);
+    publishUpdateMessages(testName, DN_OTEST2, SERVER_ID_2, checkLastCookie, messages);
   }
 
   /**
@@ -787,7 +863,7 @@
    * @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
+      ReplicationMsg...messages) throws Exception
   {
     Pair<ReplicationBroker, LDAPReplicationDomain> replicationObjects = null;
     try
@@ -795,9 +871,12 @@
       replicationObjects = enableReplication(baseDN, serverId, replicationServerPort, brokerSessionTimeout);
       ReplicationBroker broker = replicationObjects.getFirst();
       String cookie = "";
-      for (UpdateMsg msg : messages)
+      for (ReplicationMsg msg : messages)
       {
-        debugInfo(testName, " publishes " + msg.getCSN());
+        if (msg instanceof UpdateMsg)
+        {
+          debugInfo(testName, " publishes " + ((UpdateMsg)msg).getCSN());
+        }
 
         broker.publish(msg);
 
@@ -1008,7 +1087,7 @@
 
   private UpdateMsg generateModDNMsg(String baseDn, CSN csn, String testName) throws Exception
   {
-    final DN newSuperior = ROOT_DN_OTEST2;
+    final DN newSuperior = DN_OTEST2;
     ModifyDNOperation op = new ModifyDNOperationBasis(connection, 1, 1, null,
         DN.decode("uid=" + testName + "4," + baseDn), // entryDN
         RDN.decode("uid=" + testName + "new4"), // new rdn

--
Gitblit v1.10.0