/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions copyright 2011 ForgeRock AS */ package org.opends.server.replication; import static org.opends.messages.ReplicationMessages.*; import static org.opends.server.TestCaseUtils.*; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.server.replication.protocol.OperationContext.*; import static org.opends.server.util.StaticUtils.*; import static org.testng.Assert.*; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.net.ServerSocket; import java.net.Socket; import java.util.*; import org.opends.server.TestCaseUtils; import org.opends.server.api.Backend; import org.opends.server.api.ConnectionHandler; import org.opends.server.backends.MemoryBackend; import org.opends.server.config.ConfigException; import org.opends.server.controls.ExternalChangelogRequestControl; import org.opends.server.controls.PersistentSearchChangeType; import org.opends.server.controls.PersistentSearchControl; import org.opends.server.core.AddOperationBasis; import org.opends.server.core.DeleteOperationBasis; import org.opends.server.core.DirectoryServer; import org.opends.server.core.ModifyDNOperationBasis; import org.opends.server.core.ModifyOperationBasis; import org.opends.server.loggers.debug.DebugTracer; import org.opends.server.plugins.InvocationCounterPlugin; import org.opends.server.protocols.asn1.ASN1Exception; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.protocols.internal.InternalSearchOperation; import org.opends.server.protocols.ldap.BindRequestProtocolOp; import org.opends.server.protocols.ldap.BindResponseProtocolOp; import org.opends.server.protocols.ldap.LDAPAttribute; import org.opends.server.protocols.ldap.LDAPConnectionHandler; import org.opends.server.protocols.ldap.LDAPConstants; import org.opends.server.protocols.ldap.LDAPFilter; import org.opends.server.protocols.ldap.LDAPMessage; import org.opends.server.protocols.ldap.LDAPResultCode; import org.opends.server.protocols.ldap.LDAPStatistics; import org.opends.server.protocols.ldap.SearchRequestProtocolOp; 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; import org.opends.server.replication.plugin.ExternalChangelogDomainFakeCfg; import org.opends.server.replication.plugin.LDAPReplicationDomain; import org.opends.server.replication.plugin.MultimasterReplication; import org.opends.server.replication.protocol.AddMsg; import org.opends.server.replication.protocol.DeleteMsg; import org.opends.server.replication.protocol.DoneMsg; import org.opends.server.replication.protocol.ECLUpdateMsg; 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.StartECLSessionMsg; import org.opends.server.replication.protocol.UpdateMsg; import org.opends.server.replication.server.DraftCNDbHandler; import org.opends.server.replication.server.ReplServerFakeConfiguration; import org.opends.server.replication.server.ReplicationServer; import org.opends.server.replication.server.ReplicationServerDomain; import org.opends.server.replication.service.ReplicationBroker; import org.opends.server.tools.LDAPSearch; import org.opends.server.tools.LDAPWriter; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeBuilder; import org.opends.server.types.AttributeValue; import org.opends.server.types.Attributes; import org.opends.server.types.ByteString; import org.opends.server.types.Control; import org.opends.server.types.DN; import org.opends.server.types.DereferencePolicy; import org.opends.server.types.DirectoryException; import org.opends.server.types.Entry; import org.opends.server.types.InitializationException; import org.opends.server.types.LDAPException; import org.opends.server.types.LDIFExportConfig; import org.opends.server.types.Modification; import org.opends.server.types.ModificationType; import org.opends.server.types.Operation; 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.ServerConstants; import org.opends.server.util.StaticUtils; import org.opends.server.util.TimeThread; import org.opends.server.workflowelement.externalchangelog.ECLSearchOperation; import org.opends.server.workflowelement.externalchangelog.ECLWorkflowElement; import org.opends.server.workflowelement.localbackend.LocalBackendModifyDNOperation; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** * Tests for the replicationServer code. */ public class ExternalChangeLogTest extends ReplicationTestCase { // The tracer object for the debug logger private static final DebugTracer TRACER = getTracer(); // The replicationServer that will be used in this test. private ReplicationServer replicationServer = null; // The port of the replicationServer. private int replicationServerPort; 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_STRING3 = "o=test3"; private static final String TEST_BACKEND_ID3 = "test3"; // The LDAPStatistics object associated with the LDAP connection handler. private LDAPStatistics ldapStatistics; private ChangeNumber gblCN; List<Control> NO_CONTROL = null; private int brokerSessionTimeout = 5000; private int maxWindow = 100; /** * Set up the environment for performing the tests in this Class. * Replication * * @throws Exception * If the environment could not be set up. */ @BeforeClass @Override public void setUp() throws Exception { super.setUp(); // This test suite depends on having the schema available. configure(); } /** * Utility : configure a replicationServer. */ protected void configure() throws Exception { // Find a free port for the replicationServer ServerSocket socket = TestCaseUtils.bindFreePort(); replicationServerPort = socket.getLocalPort(); socket.close(); ReplServerFakeConfiguration conf1 = new ReplServerFakeConfiguration( replicationServerPort, "ExternalChangeLogTestDb", 0, 71, 0, maxWindow, null); replicationServer = new ReplicationServer(conf1); debugInfo("configure", "ReplicationServer created"+replicationServer); } /** * Launcher. */ @Test(enabled=true) public void ECLReplicationServerPreTest() throws Exception { // No RSDomain created yet => RS only case => ECL is not a supported ECLIsNotASupportedSuffix(); } @Test(enabled=true, dependsOnMethods = { "ECLReplicationServerPreTest"}) public void ECLReplicationServerTest() throws Exception { // Following test does not create RSDomain (only broker) but want to test // ECL .. so let's enable ECl manually // Now that we tested that ECl is not available try { ECLWorkflowElement wfe = (ECLWorkflowElement) DirectoryServer.getWorkflowElement( ECLWorkflowElement.ECL_WORKFLOW_ELEMENT); if (wfe!=null) wfe.getReplicationServer().enableECL(); } catch(DirectoryException de) { fail("Ending test " + " with exception:" + stackTraceToSingleLineString(de)); } // Test all types of ops. ECLAllOps(); // Do not clean the db for the next test // First and last should be ok whenever a request has been done or not // in compat mode. ECLCompatTestLimits(1,4,true); replicationServer.clearDb(); } @Test(enabled=true, dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerTest1() { // Test with a mix of domains, a mix of DSes ECLTwoDomains(); } @Test(enabled=true, dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerTest2() { // Test ECL after changelog triming ECLAfterChangelogTrim(); } @Test(enabled=true, dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerTest3() { // Write changes and read ECL from start int ts = ECLCompatWriteReadAllOps(1); ECLCompatNoControl(1); // Write additional changes and read ECL from a provided draft change number ts = ECLCompatWriteReadAllOps(5); replicationServer.clearDb(); } @Test(enabled=true, dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerTest4() { ECLIncludeAttributes(); } @Test(enabled=true, dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerTest5() { ChangeTimeHeartbeatTest(); } @Test(enabled=true, dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerTest6() { // Test that ECL Operational, virtual attributes are not visible // outside rootDSE. Next test will test access in RootDSE. // This one checks in data. ECLOperationalAttributesFailTest(); } @Test(enabled=true, dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest() { // *********************************************** // First set of test are in the cookie mode // *********************************************** // Test that private backend is excluded from ECL ECLOnPrivateBackend(); } @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest1() { // Test remote API (ECL through replication protocol) with empty ECL ECLRemoteEmpty(); } @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest2() { // Test with empty changelog ECLEmpty(); } @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest3() { // Test all types of ops. ECLAllOps(); // Do not clean the db for the next test // Test that ECL Operational, virtual attributes are not visible // outside rootDSE. Next test will test access in RootDSE. // This one checks in data. ECLOperationalAttributesFailTest(); // First and last should be ok whenever a request has been done or not // in compat mode. ECLCompatTestLimits(1,4, true); replicationServer.clearDb(); } @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest4() { // Test remote API (ECL through replication protocol) with NON empty ECL ECLRemoteNonEmpty(); } @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest7() { // Persistent search with changesOnly request ECLPsearch(true, false); replicationServer.clearDb(); } @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest8() { // Persistent search with init values request ECLPsearch(false, false); replicationServer.clearDb(); } @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest9() { // Simultaneous psearches ECLSimultaneousPsearches(); } @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest10() { // Test eligible count method. ECLGetEligibleCountTest(); 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 the attributes list and values returned in ECL entries // TODO:ECL Test search -s base, -s one @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest11() { // Test directly from the java obect that the changeTimeHeartbeatState // stored are ok. ChangeTimeHeartbeatTest(); } @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest12() { // Test the different forms of filter that are parsed in order to // optimize the request. ECLFilterTest(); } @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest13() { // *********************************************** // Second set of test are in the draft compat mode // *********************************************** // Empty replication changelog ECLCompatEmpty(); } @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest14() { // Request from an invalid draft change number ECLCompatBadSeqnum(); } @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest15() { // Write 4 changes and read ECL from start int ts = ECLCompatWriteReadAllOps(1); // Write 4 additional changes and read ECL from a provided draft change number ts = ECLCompatWriteReadAllOps(5); // Test request from a provided change number - read 6 ECLCompatReadFrom(6); // Test request from a provided change number interval - read 5-7 ECLCompatReadFromTo(5,7); // Test first and last draft changenumber ECLCompatTestLimits(1,8, true); // Test first and last draft changenumber, a dd a new change, do not // search again the ECL, but search for first and last ECLCompatTestLimitsAndAdd(1,8, ts); // Test DraftCNDb is purged when replication change log is purged ECLPurgeDraftCNDbAfterChangelogClear(); // Test first and last are updated ECLCompatTestLimits(0,0, true); // Persistent search in changesOnly mode ECLPsearch(true, true); replicationServer.clearDb(); } @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) public void ECLReplicationServerFullTest16() { // Persistent search in init + changes mode ECLPsearch(false, true); // Test Filter on replication csn // TODO: test with optimization when code done. ECLFilterOnReplicationCsn(); replicationServer.clearDb(); } private void ECLIsNotASupportedSuffix() { ECLCompatTestLimits(0,0, false); } //======================================================= // 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"; debugInfo(tn, "Starting test\n\n"); ReplicationBroker server1 = null; ReplicationBroker server2 = null; ReplicationBroker server3 = null; try { // Create 3 ECL broker server1 = openReplicationSession( DN.decode("cn=changelog"), 1111, 100, replicationServerPort, brokerSessionTimeout, false); assertTrue(server1.isConnected()); server2 = openReplicationSession( DN.decode("cn=changelog"), 2222, 100, replicationServerPort,brokerSessionTimeout, false); assertTrue(server2.isConnected()); server3 = openReplicationSession( DN.decode("cn=changelog"), 3333, 100, replicationServerPort,brokerSessionTimeout, false); assertTrue(server3.isConnected()); // Test broker1 receives only Done ReplicationMsg msg; int msgc=0; do { msg = server1.receive(); msgc++; } while(!(msg instanceof DoneMsg)); assertTrue(msgc==1, "Ending " + tn + " with incorrect message number :" + msg.getClass().getCanonicalName()); assertTrue(msg instanceof DoneMsg, "Ending " + tn + " with incorrect message type :" + msg.getClass().getCanonicalName()); // Test broker2 receives only Done msgc=0; do { msg = server2.receive(); msgc++; } while(!(msg instanceof DoneMsg)); assertTrue(msgc==1, "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; do { msg = server3.receive(); msgc++; } while(!(msg instanceof DoneMsg)); assertTrue(msgc==1, "Ending " + tn + " with incorrect message number :" + msg.getClass().getCanonicalName()); assertTrue(msg instanceof DoneMsg, "Ending " + tn + " with incorrect message type :" + msg.getClass().getCanonicalName()); debugInfo(tn, "Ending test successfully\n\n"); } catch(Exception e) { fail("Ending test " + tn + " with exception:" + stackTraceToSingleLineString(e)); } finally { if (server1 != null) server1.stop(); if (server2 != null) server2.stop(); if (server3 != null) server3.stop(); replicationServer.clearDb(); } } //======================================================= // 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"; debugInfo(tn, "Starting test\n\n"); replicationServer.clearDb(); // create a broker ReplicationBroker server01 = null; ReplicationBroker server02 = null; ReplicationBroker serverECL = null; try { // create 2 regular brokers on the 2 suffixes server01 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), 1201, 100, replicationServerPort, brokerSessionTimeout, true); server02 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING2), 1202, 100, replicationServerPort, brokerSessionTimeout, true, EMPTY_DN_GENID); // create and publish 1 change on each suffix long time = TimeThread.getTime(); int ts = 1; ChangeNumber cn1 = new ChangeNumber(time, ts++, 1201); DeleteMsg delMsg1 = new DeleteMsg("o=" + tn + "1," + TEST_ROOT_DN_STRING, cn1, "ECLBasicMsg1uid"); server01.publish(delMsg1); debugInfo(tn, "publishes:" + delMsg1); ChangeNumber cn2 = new ChangeNumber(time, ts++, 1202); DeleteMsg delMsg2 = new DeleteMsg("o=" + tn + "2," + TEST_ROOT_DN_STRING2, cn2, "ECLBasicMsg2uid"); server02.publish(delMsg2); debugInfo(tn, "publishes:" + delMsg2); // wait for the server to take these changes into account sleep(500); // open ECL broker serverECL = openReplicationSession( DN.decode("cn=changelog"), 10, 100, replicationServerPort, brokerSessionTimeout, false); assertTrue(serverECL.isConnected()); // receive change 1 from suffix 1 ReplicationMsg msg; msg = serverECL.receive(); ECLUpdateMsg eclu = (ECLUpdateMsg)msg; UpdateMsg u = eclu.getUpdateMsg(); 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=test2:;"))); // receive change 2 from suffix 2 msg = serverECL.receive(); eclu = (ECLUpdateMsg)msg; u = eclu.getUpdateMsg(); 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()+";"))); // receive Done msg = serverECL.receive(); debugInfo(tn, "RESULT:" + msg); assertTrue(msg instanceof DoneMsg, "RESULT:" + msg); debugInfo(tn, "Ending test successfully"); } catch(Exception e) { fail("Ending test " + tn + " with exception:" + stackTraceToSingleLineString(e)); } finally { // clean if (serverECL != null) serverECL.stop(); if (server01 != null) server01.stop(); if (server02 != null) server02.stop(); replicationServer.clearDb(); } } /** * From embedded ECL (no remote session) * With empty RS, simple search should return only root entry. */ private void ECLEmpty() { String tn = "ECLEmpty"; debugInfo(tn, "Starting test\n\n"); replicationServer.clearDb(); try { // search on 'cn=changelog' InternalSearchOperation op2 = connection.processSearch( ByteString.valueOf("cn=changelog"), SearchScope.WHOLE_SUBTREE, DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false, LDAPFilter.decode("(objectclass=*)"), new LinkedHashSet<String>(0), createControls(""), null); // success waitOpResult(op2, ResultCode.SUCCESS); // root entry returned assertEquals(op2.getEntriesSent(), 1); debugInfo(tn, "Ending test successfully"); } catch(Exception e) { fail("Ending test " + tn + " with exception e=" + stackTraceToSingleLineString(e)); } } /** * 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) throws DirectoryException { ExternalChangelogRequestControl control = new ExternalChangelogRequestControl(true, new MultiDomainServerState(cookie)); ArrayList<Control> controls = new ArrayList<Control>(1); controls.add(control); return controls; } /** * Utility - creates an LDIFWriter to dump result entries. */ private static LDIFWriter getLDIFWriter() { LDIFWriter ldifWriter = null; ByteArrayOutputStream stream = new ByteArrayOutputStream(); LDIFExportConfig exportConfig = new LDIFExportConfig(stream); try { ldifWriter = new LDIFWriter(exportConfig); } catch (Exception e) { assert(e==null); } return ldifWriter; } // Add an entry in the database private void addEntry(Entry entry) throws Exception { AddOperationBasis addOp = new AddOperationBasis(connection, InternalClientConnection.nextOperationID(), InternalClientConnection .nextMessageID(), null, entry.getDN(), entry.getObjectClasses(), entry.getUserAttributes(), entry.getOperationalAttributes()); addOp.setInternalOperation(true); addOp.run(); waitOpResult(addOp, ResultCode.SUCCESS); assertNotNull(getEntry(entry.getDN(), 1000, true)); } private void ECLOnPrivateBackend() { String tn = "ECLOnPrivateBackend"; debugInfo(tn, "Starting test"); replicationServer.clearDb(); ReplicationBroker server01 = null; LDAPReplicationDomain domain2 = null; Backend backend2 = null; DN baseDn2 = null; try { baseDn2 = DN.decode(TEST_ROOT_DN_STRING2); server01 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), 1201, 100, replicationServerPort, brokerSessionTimeout, true); // create and publish 1 change on each suffix long time = TimeThread.getTime(); int ts = 1; ChangeNumber cn1 = new ChangeNumber(time, ts++, 1201); DeleteMsg delMsg1 = new DeleteMsg("o=" + tn + "1," + TEST_ROOT_DN_STRING, cn1, "ECLBasicMsg1uid"); server01.publish(delMsg1); debugInfo(tn, "publishes:" + delMsg1); // Initialize a second test backend o=test2, in addtion to o=test // Configure replication on this backend // Add the root entry in the backend backend2 = initializeTestBackend(false, TEST_ROOT_DN_STRING2, TEST_BACKEND_ID2); backend2.setPrivateBackend(true); SortedSet<String> replServers = new TreeSet<String>(); replServers.add("localhost:"+replicationServerPort); DomainFakeCfg domainConf = new DomainFakeCfg(baseDn2, 1602, replServers); ExternalChangelogDomainFakeCfg eclCfg = new ExternalChangelogDomainFakeCfg(true, null, null); domainConf.setExternalChangelogDomain(eclCfg); domain2 = MultimasterReplication.createNewDomain(domainConf); domain2.start(); sleep(1000); Entry e = createEntry(baseDn2); addEntry(e); // Search on ECL from start on all suffixes String cookie = ""; ExternalChangelogRequestControl control = new ExternalChangelogRequestControl(true, new MultiDomainServerState(cookie)); ArrayList<Control> controls = new ArrayList<Control>(0); controls.add(control); 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, DereferencePolicy.NEVER_DEREF_ALIASES, 0, // Size limit 0, // Time limit false, // Types only LDAPFilter.decode("(targetDN=*)"), attributes, controls, null); // Expect SUCCESS and root entry returned waitOpResult(searchOp, ResultCode.SUCCESS); LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries(); assertEquals(entries.size(),2, "Entries number returned by search"); assertTrue(entries != null); if (entries != null) { int i = 0; for (SearchResultEntry resultEntry : entries) { i++; // Expect debugInfo(tn, "Entry returned when test2 is public =" + resultEntry.toLDIFString()); // Test entry attributes //if (i==2) //{ // checkPossibleValues(resultEntry,"targetobjectclass","top","organization"); //} } } eclCfg = new ExternalChangelogDomainFakeCfg(false, null, null); domainConf.setExternalChangelogDomain(eclCfg); domain2.applyConfigurationChange(domainConf); debugInfo(tn, "Search with cookie=" + cookie); 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=*)"), attributes, controls, null); // Expect success and only entry from o=test returned waitOpResult(searchOp, ResultCode.SUCCESS); entries = searchOp.getSearchEntries(); assertTrue(entries != null, "Entries returned when test2 is ECL disabled."); assertTrue(entries.size()==1, "#Entry="+entries.size()+"when expected is 1"); if (entries != null) for (SearchResultEntry resultEntry : entries) { // Expect debugInfo(tn, "Entry returned when test2 is private =" + resultEntry.toLDIFString()); } // // Test lastExternalChangelogCookie attribute of the ECL // (does only refer to non private backend) MultiDomainServerState expectedLastCookie = new MultiDomainServerState("o=test:"+cn1+";"); String lastCookie = readLastCookie(tn); assertTrue(expectedLastCookie.equalsTo(new MultiDomainServerState(lastCookie)), " Expected last cookie attribute value:" + expectedLastCookie + " Read from server: " + lastCookie + " are equal :"); } catch(Exception e) { fail("Ending test " + tn + " with exception:" + stackTraceToSingleLineString(e)); } finally { // Cleaning if (domain2 != null && baseDn2 != null) MultimasterReplication.deleteDomain(baseDn2); if (backend2 != null) removeTestBackend2(backend2); if (server01 != null) server01.stop(); replicationServer.clearDb(); } debugInfo(tn, "Ending test successfully"); } /** * From embebbded ECL * Search ECL with 4 messages on 2 suffixes from 2 brokers * */ private void ECLTwoDomains() { String tn = "ECLTwoDomains"; debugInfo(tn, "Starting test"); ReplicationBroker s1test = null; ReplicationBroker s1test2 = null; ReplicationBroker s2test = null; ReplicationBroker s2test2 = null; try { // Initialize a second test backend Backend backend2 = initializeTestBackend(true, TEST_ROOT_DN_STRING2, TEST_BACKEND_ID2); // LDIFWriter ldifWriter = getLDIFWriter(); // -- s1test = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), 1201, 100, replicationServerPort, brokerSessionTimeout, true); s2test2 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING2), 1202, 100, replicationServerPort, brokerSessionTimeout, 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(1500); // Changes are : // s1 s2 // o=test msg1/msg4 // o=test2 msg2/msg2 String 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=*"+tn+"*)"), attributes, createControls(cookie), null); waitOpResult(searchOp, ResultCode.SUCCESS); cookie=""; LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries(); if (entries != null) { int i=0; for (SearchResultEntry entry : entries) { debugInfo(tn, " RESULT entry returned:" + entry.toSingleLineString()); ldifWriter.writeEntry(entry); if (i++==2) { // 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(); } } } assertEquals(searchOp.getSearchEntries().size(), 4); // Now start from last cookie and expect to get ONLY the 4th change attributes = new LinkedHashSet<String>(); attributes.add("+"); attributes.add("*"); 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, DereferencePolicy.NEVER_DEREF_ALIASES, 0, // Size limit 0, // Time limit false, // Types only LDAPFilter.decode("(targetDN=*"+tn+"*)"), attributes, controls, null); // We expect SUCCESS and the 4th change waitOpResult(searchOp, ResultCode.SUCCESS); entries = searchOp.getSearchEntries(); cookie=""; if (entries != null) { for (SearchResultEntry entry : entries) { debugInfo(tn, "Result entry=\n" + entry.toLDIFString()); ldifWriter.writeEntry(entry); try { // 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.getSearchEntries().size(), 1); // 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"); s1test.publish(delMsg); sleep(500); // Changes are : // s1 s2 // o=test msg1,msg5 msg4 // o=test2 msg3 msg2 control = new ExternalChangelogRequestControl(true, new MultiDomainServerState(cookie)); controls = new ArrayList<Control>(0); controls.add(control); debugInfo(tn, "Search with cookie=" + cookie + "\""); 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+"*)"), attributes, controls, null); waitOpResult(searchOp, ResultCode.SUCCESS); entries = searchOp.getSearchEntries(); if (entries != null) { for (SearchResultEntry resultEntry : entries) { debugInfo(tn, "Result entry=\n" + resultEntry.toLDIFString()); ldifWriter.writeEntry(resultEntry); try { cookie = resultEntry.getAttribute("changelogcookie").get(0).iterator().next().toString(); } catch(NullPointerException e) {} } } assertEquals(searchOp.getSearchEntries().size(), 1); cookie=""; control = new ExternalChangelogRequestControl(true, new MultiDomainServerState(cookie)); 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, DereferencePolicy.NEVER_DEREF_ALIASES, 0, // Size limit 0, // Time limit false, // Types only LDAPFilter.decode("(targetDN=*"+tn+"*,o=test)"), attributes, controls, null); waitOpResult(searchOp, ResultCode.SUCCESS); entries = searchOp.getSearchEntries(); if (entries != null) { for (SearchResultEntry resultEntry : entries) { debugInfo(tn, "Result entry=\n" + resultEntry.toLDIFString()); ldifWriter.writeEntry(resultEntry); try { cookie = resultEntry.getAttribute("changelogcookie").get(0).iterator().next().toString(); } catch(NullPointerException e) {} } } // we expect msg1 + msg4 + msg5 assertEquals(searchOp.getSearchEntries().size(), 3); // // Test startState ("first cookie") of the ECL // // -- s1test2 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING2), 1203, 100, replicationServerPort, brokerSessionTimeout, true, EMPTY_DN_GENID); s2test = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), 1204, 100, replicationServerPort, brokerSessionTimeout, true); sleep(500); 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 rsd = replicationServer.getReplicationServerDomain(TEST_ROOT_DN_STRING, false); ServerState startState = rsd.getStartState(); assertTrue(startState.getMaxChangeNumber(s1test.getServerId()).getSeqnum()==1); assertTrue(startState.getMaxChangeNumber(s2test.getServerId()) != null); assertTrue(startState.getMaxChangeNumber(s2test.getServerId()).getSeqnum()==7); rsd = replicationServer.getReplicationServerDomain(TEST_ROOT_DN_STRING2, false); startState = rsd.getStartState(); assertTrue(startState.getMaxChangeNumber(s2test2.getServerId()).getSeqnum()==2); assertTrue(startState.getMaxChangeNumber(s1test2.getServerId()).getSeqnum()==6); // // Test lastExternalChangelogCookie attribute of the ECL // MultiDomainServerState expectedLastCookie = new MultiDomainServerState("o=test:"+cn5+" "+cn9+";o=test2:"+cn3+" "+cn8+";"); String lastCookie = readLastCookie(tn); assertTrue(expectedLastCookie.equalsTo(new MultiDomainServerState(lastCookie)), " Expected last cookie attribute value:" + expectedLastCookie + " Read from server: " + lastCookie + " are equal :"); // Test invalid cookie cookie += ";o=test6:"; control = new ExternalChangelogRequestControl(true, new MultiDomainServerState(cookie)); controls = new ArrayList<Control>(0); controls.add(control); debugInfo(tn, "Search with bad domain in cookie=" + cookie); 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); waitOpResult(searchOp, ResultCode.PROTOCOL_ERROR); assertEquals(searchOp.getSearchEntries().size(), 0); assertTrue(searchOp.getErrorMessage().toString().equals( ERR_INVALID_COOKIE_SYNTAX.get().toString()), searchOp.getErrorMessage().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 ?) // let's do a very quick test here. String newCookie = lastCookie + "o=test6:"; control = new ExternalChangelogRequestControl(true, new MultiDomainServerState(newCookie)); controls = new ArrayList<Control>(0); controls.add(control); debugInfo(tn, "Search with bad domain in cookie=" + cookie); 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); waitOpResult(searchOp, ResultCode.UNWILLING_TO_PERFORM); assertEquals(searchOp.getSearchEntries().size(), 0); // Test missing domain in provided cookie newCookie = lastCookie.substring(lastCookie.indexOf(';')+1); control = new ExternalChangelogRequestControl(true, new MultiDomainServerState(newCookie)); controls = new ArrayList<Control>(0); controls.add(control); debugInfo(tn, "Search with bad domain in cookie=" + cookie); 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); waitOpResult(searchOp, ResultCode.UNWILLING_TO_PERFORM); assertEquals(searchOp.getSearchEntries().size(), 0); String expectedError = ERR_RESYNC_REQUIRED_MISSING_DOMAIN_IN_PROVIDED_COOKIE .get("o=test:;","<"+ newCookie + "o=test:;>").toString(); assertTrue(searchOp.getErrorMessage().toString().equalsIgnoreCase(expectedError), "Expected: " + expectedError + "Server output:" + searchOp.getErrorMessage().toString()); } catch(Exception e) { fail("Ending test " + tn + "with exception:\n" + stackTraceToSingleLineString(e)); } finally { if (s1test != null) s1test.stop(); if (s1test2 != null) s1test2.stop(); if (s2test != null) s2test.stop(); if (s2test2 != null) s2test2.stop(); replicationServer.clearDb(); } debugInfo(tn, "Ending test successfully"); } //======================================================= // Test ECL content after replication changelogdb triming private void ECLAfterChangelogTrim() { String tn = "ECLAfterChangelogTrim"; debugInfo(tn, "Starting test"); ReplicationBroker server01 = null; ReplicationServerDomain d1 = null; ReplicationServerDomain d2 = null; try { // --- // 1. Populate the changelog and read the cookie // Creates broker on o=test server01 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), 1201, 100, replicationServerPort, brokerSessionTimeout, true); int ts = 1; ChangeNumber cn1 = new ChangeNumber(TimeThread.getTime(), ts++, 1201); DeleteMsg delMsg = new DeleteMsg("uid="+tn+"1," + TEST_ROOT_DN_STRING, cn1, tn+"uuid1"); server01.publish(delMsg); debugInfo(tn, " publishes " + delMsg.getChangeNumber()); Thread.sleep(1000); // Test that last cookie has been updated String cookieNotEmpty = readLastCookie(tn); debugInfo(tn, "Store cookie not empty=\"" + cookieNotEmpty + "\""); cn1 = new ChangeNumber(TimeThread.getTime(), ts++, 1201); delMsg = new DeleteMsg("uid="+tn+"2," + TEST_ROOT_DN_STRING, cn1, tn+"uuid2"); server01.publish(delMsg); debugInfo(tn, " publishes " + delMsg.getChangeNumber()); cn1 = new ChangeNumber(TimeThread.getTime(), ts++, 1201); delMsg = new DeleteMsg("uid="+tn+"3," + TEST_ROOT_DN_STRING, cn1, tn+"uuid3"); server01.publish(delMsg); debugInfo(tn, " publishes " + delMsg.getChangeNumber()); // Sleep longer than this delay - the changelog will be trimmed Thread.sleep(1000); // --- // 2. Now set up a very short purge delay on the replication changelogs // so that this test can play with a trimmed changelog. d1 = replicationServer.getReplicationServerDomain("o=test", false); d2 = replicationServer.getReplicationServerDomain("o=test2", false); d1.setPurgeDelay(1); d2.setPurgeDelay(1); // Sleep longer than this delay - so that the changelog is trimmed Thread.sleep(1000); // LDIFWriter ldifWriter = getLDIFWriter(); // --- // 3. Assert that a request with an empty cookie returns nothing String cookie= ""; // search on 'cn=changelog' LinkedHashSet<String> attributes = new LinkedHashSet<String>(); attributes.add("+"); attributes.add("*"); debugInfo(tn, "1. 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=*)"), attributes, createControls(cookie), null); waitOpResult(searchOp, ResultCode.SUCCESS); cookie=""; LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries(); if (entries != null) { for (SearchResultEntry entry : entries) { debugInfo(tn, " RESULT entry returned:" + entry.toSingleLineString()); ldifWriter.writeEntry(entry); } } // Assert ECL is empty since replication changelog has been trimmed assertEquals(searchOp.getSearchEntries().size(), 0); // 4. Assert that a request with the current last cookie returns nothing cookie = readLastCookie(tn); debugInfo(tn, "2. Search with last cookie=" + cookie + "\""); 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=*)"), attributes, createControls(cookie), null); waitOpResult(searchOp, ResultCode.SUCCESS); entries = searchOp.getSearchEntries(); if (entries != null) { for (SearchResultEntry entry : entries) { debugInfo(tn, " RESULT entry returned:" + entry.toSingleLineString()); ldifWriter.writeEntry(entry); } } // Assert ECL is empty since replication changelog has been trimmed assertEquals(searchOp.getSearchEntries().size(), 0); // --- // 5. Assert that a request with an "old" cookie - one that refers to // changes that have been removed by the replication changelog trimming // returns the appropriate error. cn1 = new ChangeNumber(TimeThread.getTime(), ts++, 1201); delMsg = new DeleteMsg("uid="+tn+"1," + TEST_ROOT_DN_STRING, cn1, tn+"uuid1"); server01.publish(delMsg); debugInfo(tn, " publishes " + delMsg.getChangeNumber()); attributes = new LinkedHashSet<String>(); attributes.add("+"); attributes.add("*"); debugInfo(tn, "3. Search with cookie=\"" + cookieNotEmpty + "\""); debugInfo(tn, "d1 trimdate" + d1.getStartState()); debugInfo(tn, "d2 trimdate" + d2.getStartState()); 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=*)"), attributes, createControls(cookieNotEmpty), null); waitOpResult(searchOp, ResultCode.UNWILLING_TO_PERFORM); assertEquals(searchOp.getSearchEntries().size(), 0); assertTrue(searchOp.getErrorMessage().toString().startsWith( ERR_RESYNC_REQUIRED_TOO_OLD_DOMAIN_IN_PROVIDED_COOKIE.get("o=test") .toString()), searchOp.getErrorMessage().toString()); } catch(Exception e) { fail("Ending test " + tn + "with exception:\n" + stackTraceToSingleLineString(e)); } finally { if (server01 != null) server01.stop(); // And reset changelog purge delay for the other tests. if (d1 != null) d1.setPurgeDelay(15 * 1000); if (d2 != null) d2.setPurgeDelay(15 * 1000); replicationServer.clearDb(); } debugInfo(tn, "Ending test successfully"); } private String readLastCookie(String tn) { String cookie = ""; LDIFWriter ldifWriter = getLDIFWriter(); // LinkedHashSet<String> lastcookieattribute = new LinkedHashSet<String>(); lastcookieattribute.add("lastExternalChangelogCookie"); try { // Root DSE 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=*)"), lastcookieattribute, NO_CONTROL, null); waitOpResult(searchOp, ResultCode.SUCCESS); LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries(); if (entries != null) { for (SearchResultEntry resultEntry : entries) { ldifWriter.writeEntry(resultEntry); try { List<Attribute> l = resultEntry.getAttribute("lastexternalchangelogcookie"); cookie = l.get(0).iterator().next().toString(); } catch(NullPointerException e) {} } } } catch(Exception e) { fail("Ending test " + tn + " with exception:\n" + stackTraceToSingleLineString(e)); } return cookie; } // simple update to be received private void ECLAllOps() { String tn = "ECLAllOps"; debugInfo(tn, "Starting test\n\n"); try { LDIFWriter ldifWriter = getLDIFWriter(); // Creates broker on o=test ReplicationBroker server01 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), 1201, 100, replicationServerPort, brokerSessionTimeout, true); int ts = 1; // Creates broker on o=test2 ReplicationBroker server02 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING2), 1202, 100, replicationServerPort, brokerSessionTimeout, true); String user1entryUUID = "11111111-1111-1111-1111-111111111111"; String baseUUID = "22222222-2222-2222-2222-222222222222"; // Publish DEL ChangeNumber cn1 = new ChangeNumber(TimeThread.getTime(), ts++, 1201); DeleteMsg delMsg = new DeleteMsg("uid="+tn+"1," + TEST_ROOT_DN_STRING, cn1, tn+"uuid1"); server01.publish(delMsg); debugInfo(tn, " publishes " + delMsg.getChangeNumber()); // Publish ADD ChangeNumber cn2 = new ChangeNumber(TimeThread.getTime(), ts++, 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( cn2, "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 DEL /* ChangeNumber cn12 = new ChangeNumber(TimeThread.getTime(), ts++, 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++, 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, tn+"uuid3"); server01.publish(modMsg); debugInfo(tn, " publishes " + modMsg.getChangeNumber()); // Publish modDN DN newSuperior = DN.decode(TEST_ROOT_DN_STRING2); ChangeNumber cn4 = new ChangeNumber(TimeThread.getTime(), ts++, 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 newSuperior); op.setAttachment(SYNCHROCONTEXT, new ModifyDnContext(cn4, tn+"uuid4", "newparentId")); LocalBackendModifyDNOperation localOp = new LocalBackendModifyDNOperation(op); ModifyDNMsg modDNMsg = new ModifyDNMsg(localOp); server01.publish(modDNMsg); debugInfo(tn, " publishes " + modDNMsg.getChangeNumber()); sleep(1000); String cookie= ""; // search on 'cn=changelog' LinkedHashSet<String> attributes = new LinkedHashSet<String>(); attributes.add("+"); attributes.add("*"); ExternalChangelogRequestControl control = new ExternalChangelogRequestControl(true, new MultiDomainServerState()); 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); // test success waitOpResult(searchOp, ResultCode.SUCCESS); // test 4 entries returned String cookie1 = "o=test:"+cn1.toString()+";"; String cookie2 = "o=test:"+cn2.toString()+";"; String cookie3 = "o=test:"+cn3.toString()+";"; String cookie4 = "o=test:"+cn4.toString()+";"; assertEquals(searchOp.getSearchEntries().size(), 4); LinkedList<SearchResultEntry> 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( "replicationcsn=" + 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",cookie1); checkValue(resultEntry,"targetentryuuid",tn+"uuid1"); checkValue(resultEntry,"changenumber","0"); } else if (i==2) { // check the ADD entry has the right content assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase( "replicationcsn=" + cn2 + "," + TEST_ROOT_DN_STRING + ",cn=changelog")); String expectedValue1 = "objectClass: domain\nobjectClass: top\n" + "entryUUID: 11111111-1111-1111-1111-111111111111\n"; String expectedValue2 = "entryUUID: 11111111-1111-1111-1111-111111111111\n" + "objectClass: domain\nobjectClass: top\n"; checkPossibleValues(resultEntry,"changes",expectedValue1, expectedValue2); checkValue(resultEntry,"replicationcsn",cn2.toString()); checkValue(resultEntry,"replicaidentifier","1201"); checkValue(resultEntry,"targetdn","uid="+tn+"2," + TEST_ROOT_DN_STRING); checkValue(resultEntry,"changetype","add"); checkValue(resultEntry,"changelogcookie",cookie2); checkValue(resultEntry,"targetentryuuid",user1entryUUID); checkValue(resultEntry,"changenumber","0"); } else if (i==3) { // check the MOD entry has the right content assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase( "replicationcsn=" + cn3 + "," + TEST_ROOT_DN_STRING + ",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",cookie3); checkValue(resultEntry,"targetentryuuid",tn+"uuid3"); checkValue(resultEntry,"changenumber","0"); } else if (i==4) { // check the MODDN entry has the right content assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase( "replicationcsn=" + 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",cookie4); checkValue(resultEntry,"targetentryuuid",tn+"uuid4"); checkValue(resultEntry,"newrdn","uid=ECLAllOpsnew4"); if (newSuperior != null) checkValue(resultEntry,"newsuperior",TEST_ROOT_DN_STRING2); checkValue(resultEntry,"deleteoldrdn","true"); checkValue(resultEntry,"changenumber","0"); } } } // Test the response control with ldapsearch tool String result = ldapsearch("cn=changelog"); debugInfo(tn, "Entries:" + result); ArrayList<String> ctrlList = getControls(result); assertTrue(ctrlList.get(0).equals(cookie1)); assertTrue(ctrlList.get(1).equals(cookie2)); assertTrue(ctrlList.get(2).equals(cookie3)); assertTrue(ctrlList.get(3).equals(cookie4)); server01.stop(); if (server02 != null) server02.stop(); } catch(Exception e) { fail("Ending test " + tn + " with exception:\n" + stackTraceToSingleLineString(e)); } debugInfo(tn, "Ending test with success"); } protected ArrayList<String> getControls(String resultString) { StringReader r=new StringReader(resultString); BufferedReader br=new BufferedReader(r); ArrayList<String> ctrlList = new ArrayList<String>(); try { while(true) { String s = br.readLine(); if(s == null) break; if(!s.startsWith("#")) continue; String[] a=s.split(": "); if(a.length != 2) break; ctrlList.add(a[1]); } } catch (IOException e) { Assert.assertEquals(0, 1, e.getMessage()); } return ctrlList; } private static final ByteArrayOutputStream oStream = new ByteArrayOutputStream(); private static final ByteArrayOutputStream eStream = new ByteArrayOutputStream(); private String ldapsearch(String baseDN) { // test search as directory manager returns content String[] args3 = { "-h", "127.0.0.1", "-p", String.valueOf(TestCaseUtils.getServerAdminPort()), "-Z", "-X", "-D", "cn=Directory Manager", "-w", "password", "-b", baseDN, "-s", "sub", "--control", "1.3.6.1.4.1.26027.1.5.4:false:;", "(objectclass=*)" }; oStream.reset(); eStream.reset(); int retVal = LDAPSearch.mainSearch(args3, false, oStream, eStream); Assert.assertEquals(0, retVal, "Returned error: " + eStream.toString()); return oStream.toString(); } private static void checkValue(Entry entry, String attrName, String expectedValue) { AttributeValue av = null; try { 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); } catch(Exception e) { assertTrue(false, "In entry " + entry + " attr <" + attrName + "> equals " + av + " instead of expected value " + expectedValue); } } private static String getAttributeValue(Entry entry, String attrName) { AttributeValue av = null; try { List<Attribute> attrs = entry.getAttribute(attrName); Attribute a = attrs.iterator().next(); av = a.iterator().next(); return av.toString(); } catch(Exception e) { } return null; } 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(); assertTrue( (encodedValue.equalsIgnoreCase(expectedValue1) || encodedValue.equalsIgnoreCase(expectedValue2)), "In entry " + entry + " attr <" + attrName + "> equals " + av + " instead of one of the expected values " + expectedValue1 + " or " + expectedValue2); } catch(Exception e) { assertTrue(false, "In entry " + entry + " attr <" + attrName + "> equals " + av + " instead of one of the expected values " + expectedValue1 + " or " + expectedValue2); } } private static void checkValues(Entry entry, String attrName, Set<String> expectedValues) { AttributeValue av = null; int i=0; try { List<Attribute> attrs = entry.getAttribute(attrName); Attribute a; Iterator<Attribute> iat = attrs.iterator(); while ((a=iat.next())!=null) { Iterator<AttributeValue> iatv = a.iterator(); while ((av = iatv.next())!=null) { String encodedValue = av.toString(); assertTrue( expectedValues.contains(encodedValue), "In entry " + entry + " attr <" + attrName + "> equals " + av + " instead of one of the expected values " + expectedValues); i++; } } } catch(NoSuchElementException e) { assertTrue(i==expectedValues.size()); } } /** * Test persistent search */ private void ECLPsearch(boolean changesOnly, boolean compatMode) { String tn = "ECLPsearch_" + String.valueOf(changesOnly) + "_" + String.valueOf(compatMode); debugInfo(tn, "Starting test \n\n"); Socket s =null; // 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 suffix ReplicationBroker server01 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), 1201, 100, replicationServerPort, brokerSessionTimeout, true); int ts = 1; // Produce update on this suffix ChangeNumber cn = new ChangeNumber(TimeThread.getTime(), ts++, 1201); DeleteMsg delMsg = 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 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); // Creates request SearchRequestProtocolOp searchRequest = 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=test)"), 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); s.setSoTimeout(5000); bindAsManager(w, r); // 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(); debugInfo(tn, "Search Persistent filter=(targetDN=*"+tn+"*,o=test)"); LDAPMessage message; message = new LDAPMessage(2, searchRequest, controls); w.writeMessage(message); this.sleep(500); SearchResultEntryProtocolOp searchResultEntry = null; SearchResultDoneProtocolOp searchResultDone = null; if (changesOnly == false) { // Wait for 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, "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 1 is really the valid value here. checkValue(searchResultEntry.toSearchResultEntry(),"changenumber", (compatMode?"1":"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("init search failed with e=" + stackTraceToSingleLineString(e)); } debugInfo(tn, "INIT search done with success. searchEntries=" + searchEntries + " #searchesDone="+ searchesDone); } // Produces change 2 cn = new ChangeNumber(TimeThread.getTime(), ts++, 1201); String expectedDn = "uid=" + tn + "2," + TEST_ROOT_DN_STRING; 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 , psearch will now wait for new entries"); // wait for the 1 new entry searchEntries = 0; searchResultEntry = null; searchResultDone = null; message = null; while ((searchEntries<1) && (message = r.readMessage()) != null) { debugInfo(tn, "psearch search 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 2 for (LDAPAttribute a : searchResultEntry.getAttributes()) { if (a.getAttributeType().equalsIgnoreCase("targetDN")) { for (ByteString av : a.getValues()) { assertTrue(av.toString().equalsIgnoreCase(expectedDn), "Entry returned by psearch is " + av.toString() + " when expected is " + expectedDn); } } } debugInfo(tn, "Second search done successfully : " + searchResultEntry); server01.stop(); try { s.close(); } catch (Exception e) {}; while (!s.isClosed()) sleep(100); // TODO: Testing ACI is disabled because it is currently failing when // ran in the precommit target while it works well when running alone. // anonymous search returns entries from cn=changelog whereas it // should not. Probably a previous test in the nightlytests suite is // removing/modifying some ACIs... // 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(5000); 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) {}; while (!s.isClosed()) sleep(100); } catch(Exception e) { fail("Test " + tn + " fails with " + stackTraceToSingleLineString(e)); } debugInfo(tn, "Ends test successfully"); } /** * Test parallel simultaneous psearch with different filters. */ private void ECLSimultaneousPsearches() { String tn = "ECLSimultaneousPsearches"; debugInfo(tn, "Starting test \n\n"); Socket s1 = null, s2 = null, s3 = null; ReplicationBroker server01 = null; ReplicationBroker server02 = 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 server01 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), 1201, 100, replicationServerPort, brokerSessionTimeout, true); server01.setChangeTimeHeartbeatInterval(100); //ms int ts = 1; // Create broker on o=test2 server02 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING2), 1202, 100, replicationServerPort, brokerSessionTimeout, true, EMPTY_DN_GENID); server02.setChangeTimeHeartbeatInterval(100); //ms // Produce update 1 ChangeNumber cn1 = new ChangeNumber(TimeThread.getTime(), ts++, 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++, 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++, 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, DereferencePolicy.NEVER_DEREF_ALIASES, Integer.MAX_VALUE, Integer.MAX_VALUE, false, LDAPFilter.decode("(targetDN=*"+tn+"*,o=test)"), attributes); // 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); // 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(15000); 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(30000); 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(15000); 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, 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, 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, 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; message = null; while ((searchEntries<1) && (message = r1.readMessage()) != null) { debugInfo(tn, "Search 11 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); debugInfo(tn, "Search 1 successfully receives additional changes"); // 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); 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"); } catch(Exception e) { fail("Test " + tn + " fails with " + stackTraceToSingleLineString(e)); } finally { if (server01 != null) server01.stop(); if (server02 != null) server02.stop(); if (s1 != null) { try { s1.close(); } catch (Exception ignored) {}; while (!s1.isClosed()) sleep(100); } if (s2 != null) { try { s2.close(); } catch (Exception e) {}; while (!s2.isClosed()) sleep(100); } if (s3 != null) { try { s3.close(); } catch (Exception e) {}; while (!s3.isClosed()) sleep(100); } replicationServer.clearDb(); } debugInfo(tn, "Ends test successfully"); } // utility - bind as required private void bindAsManager(LDAPWriter w, org.opends.server.tools.LDAPReader r) throws IOException, LDAPException, ASN1Exception, InterruptedException { bindAsWhoEver(w, r, "cn=Directory Manager", "password", LDAPResultCode.SUCCESS); } // utility - bind as required private void bindAsWhoEver(LDAPWriter w, org.opends.server.tools.LDAPReader r, String bindDN, String password, int expected) throws IOException, LDAPException, ASN1Exception, InterruptedException { // 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(); BindRequestProtocolOp bindRequest = new BindRequestProtocolOp( ByteString.valueOf(bindDN), 3, ByteString.valueOf(password)); LDAPMessage message = new LDAPMessage(1, bindRequest); w.writeMessage(message); message = r.readMessage(); BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp(); // assertEquals(InvocationCounterPlugin.waitForPostResponse(), 1); assertEquals(bindResponse.getResultCode(), expected); } /** * Clean up the environment. * * @throws Exception If the environment could not be set up. */ @Override @AfterClass public void classCleanUp() throws Exception { callParanoiaCheck = false; super.classCleanUp(); shutdown(); paranoiaCheck(); } /** * After the tests stop the replicationServer. */ protected void shutdown() throws Exception { if (replicationServer != null) { replicationServer.remove(); StaticUtils.recursiveDelete(new File(DirectoryServer.getInstanceRoot(), replicationServer.getDbDirName())); } replicationServer = null; } /** * Utility - sleeping as long as required */ private void sleep(long time) { try { Thread.sleep(time); } catch (InterruptedException ex) { fail("Error sleeping " + ex.getMessage()); } } /** * Utility - log debug message - highlight it is from the test and not * from the server code. Makes easier to observe the test steps. */ private void debugInfo(String tn, String s) { if (debugEnabled()) { TRACER.debugInfo("** TEST " + tn + " ** " + s); } // logError(Message.raw(Category.SYNC, Severity.NOTICE, // "** TEST " + tn + " ** " + s)); } /** * Utility - create a second backend in order to test ECL with 2 suffixes. */ private static Backend initializeTestBackend( boolean createBaseEntry, String rootDN, String backendId) throws IOException, InitializationException, ConfigException, DirectoryException { DN baseDN = DN.decode(rootDN); // 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) { Entry e = createEntry(baseDN); memoryBackend.addEntry(e, null); } return memoryBackend; } private static void removeTestBackend2(Backend backend) { MemoryBackend memoryBackend = (MemoryBackend)backend; memoryBackend.clearMemoryBackend(); memoryBackend.finalizeBackend(); DirectoryServer.deregisterBackend(memoryBackend); } //======================================================= private void ChangeTimeHeartbeatTest() { String tn = "ChangeTimeHeartbeatTest"; debugInfo(tn, "Starting test"); ReplicationBroker s1test = null; ReplicationBroker s2test = null; ReplicationBroker s1test2 = null; ReplicationBroker s2test2 = null; // Initialize a second test backend Backend backend2 = null; try { backend2 = initializeTestBackend(true, TEST_ROOT_DN_STRING2, TEST_BACKEND_ID2); // -- s1test = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), 1201, 100, replicationServerPort, brokerSessionTimeout, true); s2test2 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING2), 1202, 100, replicationServerPort, brokerSessionTimeout, 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); // -- s1test2 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING2), 1203, 100, replicationServerPort, brokerSessionTimeout, true, EMPTY_DN_GENID); s2test = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), 1204, 100, replicationServerPort, brokerSessionTimeout, 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, rsd1.getBaseDn() + " DbServerState=" + rsd1.getDbServerState() + " ChangeTimeHeartBeatState=" + rsd1.getChangeTimeHeartbeatState() + " eligibleCN=" + rsd1.getEligibleCN() + " rs eligibleCN=" + replicationServer.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, rsd2.getBaseDn() + " DbServerState=" + rsd2.getDbServerState() + " ChangeTimeHeartBeatState=" + rsd2.getChangeTimeHeartbeatState() + " eligibleCN=" + rsd2.getEligibleCN() + " rs eligibleCN=" + replicationServer.getEligibleCN()); // FIXME:ECL Enable this test by adding an assert on the right value } catch(Exception e) { fail("Ending test " + tn + " with exception:" + stackTraceToSingleLineString(e)); } finally { if (s1test2 != null) s1test2.stop(); if (s2test2 != null) s2test2.stop(); if (backend2 != null) removeTestBackend2(backend2); if (s1test != null) s1test.stop(); if (s2test != null) s2test.stop(); replicationServer.clearDb(); } 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 int ECLCompatWriteReadAllOps(int firstDraftChangeNumber) { String tn = "ECLCompatWriteReadAllOps/" + String.valueOf(firstDraftChangeNumber); debugInfo(tn, "Starting test\n\n"); int ts = 1; try { LDIFWriter ldifWriter = getLDIFWriter(); // Creates broker on o=test ReplicationBroker server01 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), 1201, 100, replicationServerPort, brokerSessionTimeout, true); String user1entryUUID = "11111111-1112-1113-1114-111111111115"; String baseUUID = "22222222-2222-2222-2222-222222222222"; // Publish DEL ChangeNumber cn1 = new ChangeNumber(TimeThread.getTime(), ts++, 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++, 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++, 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++, 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); waitOpResult(searchOp, ResultCode.SUCCESS); // 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( "changenumber="+String.valueOf(firstDraftChangeNumber+0)+",cn=changelog"), "Result entry DN : actual=" + resultEntry.getDN().toNormalizedString() + " expected=" + "changenumber="+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()+";"); checkValue(resultEntry,"targetentryuuid",user1entryUUID); checkValue(resultEntry,"changenumber",String.valueOf(firstDraftChangeNumber+0)); checkValue(resultEntry,"targetuniqueid",user1entryUUID); } else if (i==2) { // check the ADD entry has the right content assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase( "changenumber="+String.valueOf(firstDraftChangeNumber+1)+",cn=changelog")); String expectedValue1 = "objectClass: domain\nobjectClass: top\n" + "entryUUID: "+user1entryUUID+"\n"; String expectedValue2 = "entryUUID: "+user1entryUUID+"\n" + "objectClass: domain\nobjectClass: top\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()+";"); 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( "changenumber="+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()+";"); 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( "changenumber="+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()+";"); 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(); // Test with filter on draft changenumber 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); waitOpResult(searchOp, ResultCode.SUCCESS); 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( "changenumber="+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()+";"); checkValue(resultEntry,"targetentryuuid",user1entryUUID); checkValue(resultEntry,"changenumber",String.valueOf(firstDraftChangeNumber+0)); checkValue(resultEntry,"targetuniqueid",user1entryUUID); } else if (i==2) { // check the ADD entry has the right content assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase( "changenumber="+String.valueOf(firstDraftChangeNumber+1)+",cn=changelog")); String expectedValue1 = "objectClass: domain\nobjectClass: top\n" + "entryUUID: "+user1entryUUID+"\n"; String expectedValue2 = "entryUUID: "+user1entryUUID+"\n" + "objectClass: domain\nobjectClass: top\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()+";"); 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( "changenumber="+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()+";"); 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( "changenumber="+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()+";"); 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"); return ts; } 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), 1201, 100, replicationServerPort, brokerSessionTimeout, 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); waitOpResult(searchOp, ResultCode.SUCCESS); LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries(); assertEquals(searchOp.getSearchEntries().size(), 1); if (entries != null) { for (SearchResultEntry resultEntry : entries) { debugInfo(tn, "Result entry returned:" + resultEntry.toLDIFString()); ldifWriter.writeEntry(resultEntry); // check the entry has the right content assertTrue(resultEntry.getDN().toNormalizedString().equalsIgnoreCase( "changenumber=6,cn=changelog")); checkValue(resultEntry,"replicationcsn",gblCN.toString()); checkValue(resultEntry,"replicaidentifier","1201"); checkValue(resultEntry,"changetype","add"); checkValue(resultEntry,"changelogcookie","o=test:"+gblCN.toString()+";"); 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"); } // Process similar search as but only check that there's no control // returned as part of the entry. private void ECLCompatNoControl(int firstDraftChangeNumber) { String tn = "ECLCompatNoControl/" + String.valueOf(firstDraftChangeNumber); debugInfo(tn, "Starting test\n\n"); try { // Creates broker on o=test ReplicationBroker server01 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), 1201, 100, replicationServerPort, brokerSessionTimeout, 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); waitOpResult(searchOp, ResultCode.SUCCESS); LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries(); assertEquals(searchOp.getSearchEntries().size(), 1); if (entries != null) { for (SearchResultEntry resultEntry : entries) { // Just verify that no entry contains the ChangeLogCookie control List<Control> controls = resultEntry.getControls(); assertTrue(controls.isEmpty()); } } 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); waitOpResult(searchOp, ResultCode.SUCCESS); assertEquals(searchOp.getSearchEntries().size(), lastDraftChangeNumber-firstDraftChangeNumber+1); if (searchOp.getSearchEntries() != null) { for (SearchResultEntry resultEntry : searchOp.getSearchEntries()) { debugInfo(tn, "Result entry returned:" + resultEntry.toLDIFString()); } } } 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); waitOpResult(searchOp, ResultCode.SUCCESS); 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); waitOpResult(searchOp, ResultCode.SUCCESS); assertEquals(searchOp.getSearchEntries().size(), 1); LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries(); assertEquals(searchOp.getSearchEntries().size(), 1); if (entries != null) { for (SearchResultEntry resultEntry : entries) { 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(); DN baseDN = DN.decode("cn=changelog"); // ECLSearchOperation.evaluateSearchParameters(startCLmsg, baseDN, SearchFilter.createFilterFromString("(objectclass=*)")); assertEquals(startCLmsg.getFirstDraftChangeNumber(),-1); assertEquals(startCLmsg.getLastDraftChangeNumber(),-1); // ECLSearchOperation.evaluateSearchParameters(startCLmsg, baseDN, SearchFilter.createFilterFromString("(changenumber>=2)")); assertEquals(startCLmsg.getFirstDraftChangeNumber(),2); assertEquals(startCLmsg.getLastDraftChangeNumber(),-1); // ECLSearchOperation.evaluateSearchParameters(startCLmsg, baseDN, SearchFilter.createFilterFromString("(&(changenumber>=2)(changenumber<=5))")); assertEquals(startCLmsg.getFirstDraftChangeNumber(),2); assertEquals(startCLmsg.getLastDraftChangeNumber(),5); // try { ECLSearchOperation.evaluateSearchParameters(startCLmsg, baseDN, SearchFilter.createFilterFromString("(&(changenumber>=2)(changenumber<+5))")); assertTrue((startCLmsg.getFirstDraftChangeNumber()==1)); } catch(DirectoryException de) { assertTrue(de != null); } // ECLSearchOperation.evaluateSearchParameters(startCLmsg, baseDN, SearchFilter.createFilterFromString("(&(dc=x)(&(changenumber>=2)(changenumber<=5)))")); assertEquals(startCLmsg.getFirstDraftChangeNumber(),2); assertEquals(startCLmsg.getLastDraftChangeNumber(),5); ECLSearchOperation.evaluateSearchParameters(startCLmsg, baseDN, SearchFilter.createFilterFromString("(&(&(changenumber>=3)(changenumber<=4))(&(|(dc=y)(dc=x))(&(changenumber>=2)(changenumber<=5))))")); assertEquals(startCLmsg.getFirstDraftChangeNumber(),3); assertEquals(startCLmsg.getLastDraftChangeNumber(),4); // ECLSearchOperation.evaluateSearchParameters(startCLmsg, baseDN, SearchFilter.createFilterFromString("(|(objectclass=*)(&(changenumber>=2)(changenumber<=5)))")); assertEquals(startCLmsg.getFirstDraftChangeNumber(),-1); assertEquals(startCLmsg.getLastDraftChangeNumber(),-1); // ECLSearchOperation.evaluateSearchParameters(startCLmsg, baseDN, SearchFilter.createFilterFromString("(changenumber=8)")); assertEquals(startCLmsg.getFirstDraftChangeNumber(),8); assertEquals(startCLmsg.getLastDraftChangeNumber(),8); // ChangeNumberGenerator gen = new ChangeNumberGenerator( 1, 0); ChangeNumber changeNumber1 = gen.newChangeNumber(); ECLSearchOperation.evaluateSearchParameters(startCLmsg, baseDN, SearchFilter.createFilterFromString("(replicationcsn="+changeNumber1+")")); assertEquals(startCLmsg.getFirstDraftChangeNumber(),-1); assertEquals(startCLmsg.getLastDraftChangeNumber(),-1); assertEquals(startCLmsg.getChangeNumber(), changeNumber1); // Use change number as base object. baseDN = DN.decode("changeNumber=8,cn=changelog"); // ECLSearchOperation.evaluateSearchParameters(startCLmsg, baseDN, SearchFilter.createFilterFromString("(objectclass=*)")); assertEquals(startCLmsg.getFirstDraftChangeNumber(),8); assertEquals(startCLmsg.getLastDraftChangeNumber(),8); // The base DN should take preference. ECLSearchOperation.evaluateSearchParameters(startCLmsg, baseDN, SearchFilter.createFilterFromString("(changenumber>=2)")); assertEquals(startCLmsg.getFirstDraftChangeNumber(),8); assertEquals(startCLmsg.getLastDraftChangeNumber(),8); } catch(Exception e) { fail("Ending "+tn+" test with exception:\n" + stackTraceToSingleLineString(e)); } debugInfo(tn, "Ending test with success"); } /** * Put a short purge delay to the draftCNDB, clear the changelogDB, * expect the draftCNDb to be purged accordingly. */ private void ECLPurgeDraftCNDbAfterChangelogClear() { String tn = "ECLPurgeDraftCNDbAfterChangelogClear"; 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 ECLOperationalAttributesFailTest() { String tn = "ECLOperationalAttributesFailTest"; // The goal is to verify that the Changelog attributes are not // available in other entries. We u debugInfo(tn, "Starting test \n\n"); try { LinkedHashSet<String> attributes = new LinkedHashSet<String>(); attributes.add("firstchangenumber"); attributes.add("lastchangenumber"); attributes.add("changelog"); attributes.add("lastExternalChangelogCookie"); debugInfo(tn, " Search: "+ TEST_ROOT_DN_STRING); InternalSearchOperation searchOp = connection.processSearch( ByteString.valueOf(TEST_ROOT_DN_STRING), SearchScope.BASE_OBJECT, DereferencePolicy.NEVER_DEREF_ALIASES, 0, // Size limit 0, // Time limit false, // Types only LDAPFilter.decode("(objectclass=*)"), attributes, NO_CONTROL, null); waitOpResult(searchOp, ResultCode.SUCCESS); assertEquals(searchOp.getSearchEntries().size(), 1); LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries(); assertEquals(entries.size(), 1); for (SearchResultEntry resultEntry : entries) { debugInfo(tn, "Result entry returned:" + resultEntry.toLDIFString()); assertEquals(getAttributeValue(resultEntry, "firstchangenumber"), null); assertEquals(getAttributeValue(resultEntry, "lastchangenumber"), null); assertEquals(getAttributeValue(resultEntry, "changelog"), null); assertEquals(getAttributeValue(resultEntry, "lastExternalChangelogCookie"), null); } debugInfo(tn, "Ending test with success"); } catch(Exception e) { fail("Ending "+tn+" test with exception:\n" + stackTraceToSingleLineString(e)); } } private void ECLCompatTestLimits(int expectedFirst, int expectedLast, boolean eclEnabled) { String tn = "ECLCompatTestLimits"; debugInfo(tn, "Starting test\n\n"); try { LDIFWriter ldifWriter = getLDIFWriter(); // search on 'cn=changelog' LinkedHashSet<String> attributes = new LinkedHashSet<String>(); if (expectedFirst>0) attributes.add("firstchangenumber"); attributes.add("lastchangenumber"); attributes.add("changelog"); attributes.add("lastExternalChangelogCookie"); 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); waitOpResult(searchOp, ResultCode.SUCCESS); assertEquals(searchOp.getSearchEntries().size(), 1); LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries(); assertEquals(searchOp.getSearchEntries().size(), 1); if (entries != null) { for (SearchResultEntry resultEntry : entries) { debugInfo(tn, "Result entry returned:" + resultEntry.toLDIFString()); ldifWriter.writeEntry(resultEntry); if (eclEnabled) { if (expectedFirst>0) checkValue(resultEntry,"firstchangenumber", String.valueOf(expectedFirst)); checkValue(resultEntry,"lastchangenumber", String.valueOf(expectedLast)); checkValue(resultEntry,"changelog", String.valueOf("cn=changelog")); } else { if (expectedFirst>0) assertEquals(getAttributeValue(resultEntry, "firstchangenumber"), null); assertEquals(getAttributeValue(resultEntry, "lastchangenumber"), null); assertEquals(getAttributeValue(resultEntry, "changelog"), null); assertEquals(getAttributeValue(resultEntry, "lastExternalChangelogCookie"), null); } } } } catch(Exception e) { fail("Ending "+tn+" test with exception:\n" + stackTraceToSingleLineString(e)); } debugInfo(tn, "Ending test with success"); } private void ECLCompatTestLimitsAndAdd(int expectedFirst, int expectedLast, int ts) { String tn = "ECLCompatTestLimitsAndAdd"; debugInfo(tn, "Starting test\n\n"); try { ECLCompatTestLimits(expectedFirst, expectedLast, true); // Creates broker on o=test ReplicationBroker server01 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), 1201, 100, replicationServerPort, brokerSessionTimeout, true); String user1entryUUID = "11111111-1112-1113-1114-111111111115"; // Publish DEL ChangeNumber cn1 = new ChangeNumber(TimeThread.getTime(), ts++, 1201); DeleteMsg delMsg = new DeleteMsg("uid="+tn+"1," + TEST_ROOT_DN_STRING, cn1, user1entryUUID); server01.publish(delMsg); debugInfo(tn, " publishes " + delMsg.getChangeNumber()); sleep(500); server01.stop(); ECLCompatTestLimits(expectedFirst, expectedLast+1, true); } catch(Exception e) { fail("Ending "+tn+" test with exception:\n" + stackTraceToSingleLineString(e)); } debugInfo(tn, "Ending test with success"); } private void ECLGetEligibleCountTest() { String tn = "ECLGetEligibleCountTest"; debugInfo(tn, "Starting test\n\n"); String user1entryUUID = "11111111-1112-1113-1114-111111111115"; try { ReplicationServerDomain rsdtest = replicationServer.getReplicationServerDomain(TEST_ROOT_DN_STRING, false); // The replication changelog is empty long count = rsdtest.getEligibleCount( new ServerState(), new ChangeNumber(TimeThread.getTime(), 1, 1201)); assertEquals(count, 0); // Creates broker on o=test ReplicationBroker server01 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), 1201, 1000, replicationServerPort, brokerSessionTimeout, true); // Publish one first message ChangeNumber cn1 = new ChangeNumber(TimeThread.getTime(), 1, 1201); DeleteMsg delMsg = new DeleteMsg("uid="+tn+"1," + TEST_ROOT_DN_STRING, cn1, user1entryUUID); server01.publish(delMsg); debugInfo(tn, " publishes " + delMsg.getChangeNumber()); sleep(300); // From begin to now : 1 change count = rsdtest.getEligibleCount( new ServerState(), new ChangeNumber(TimeThread.getTime(), 1, 1201)); assertEquals(count, 1); // Publish one second message ChangeNumber cn2 = new ChangeNumber(TimeThread.getTime(), 2, 1201); delMsg = new DeleteMsg("uid="+tn+"1," + TEST_ROOT_DN_STRING, cn2, user1entryUUID); server01.publish(delMsg); debugInfo(tn, " publishes " + delMsg.getChangeNumber()); sleep(300); // From begin to now : 2 changes count = rsdtest.getEligibleCount( new ServerState(), new ChangeNumber(TimeThread.getTime(), 1, 1201)); assertEquals(count, 2); // From begin to first change (inclusive) : 1 change = cn1 count = rsdtest.getEligibleCount( new ServerState(), cn1); assertEquals(count, 1); ServerState ss = new ServerState(); ss.update(cn1); // From state/cn1(exclusive) to cn1 (inclusive) : 0 change count = rsdtest.getEligibleCount(ss, cn1); assertEquals(count, 0); // From state/cn1(exclusive) to cn2 (inclusive) : 1 change = cn2 count = rsdtest.getEligibleCount(ss, cn2); assertEquals(count, 1); ss.update(cn2); // From state/cn2(exclusive) to now (inclusive) : 0 change count = rsdtest.getEligibleCount(ss, new ChangeNumber(TimeThread.getTime(), 4, 1201)); assertEquals(count, 0); // Publish one third message ChangeNumber cn3 = new ChangeNumber(TimeThread.getTime(), 3, 1201); delMsg = new DeleteMsg("uid="+tn+"1," + TEST_ROOT_DN_STRING, cn3, user1entryUUID); server01.publish(delMsg); debugInfo(tn, " publishes " + delMsg.getChangeNumber()); sleep(300); ss.update(cn2); // From state/cn2(exclusive) to now : 1 change = cn3 count = rsdtest.getEligibleCount(ss, new ChangeNumber(TimeThread.getTime(), 4, 1201)); assertEquals(count, 1); boolean perfs=false; if (perfs) { // number of msgs used by the test int maxMsg = 999999; // We need an RS configured with a window size bigger than the number // of msg used by the test. assertTrue(maxMsg<maxWindow); debugInfo(tn, "Perf test in compat mode - will generate " + maxMsg + " msgs."); for (int i=4; i<=maxMsg; i++) { ChangeNumber cnx = new ChangeNumber(TimeThread.getTime(), i, 1201); delMsg = new DeleteMsg("uid="+tn+i+"," + TEST_ROOT_DN_STRING, cnx, user1entryUUID); server01.publish(delMsg); } sleep(1000); debugInfo(tn, "Perfs test in compat - search lastChangeNumber"); ArrayList<String> excludedDomains = MultimasterReplication.getECLDisabledDomains(); if (!excludedDomains.contains( ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT)) excludedDomains.add(ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT); ECLWorkflowElement eclwe = (ECLWorkflowElement) DirectoryServer.getWorkflowElement("EXTERNAL CHANGE LOG"); ReplicationServer rs = eclwe.getReplicationServer(); rs.disableEligibility(excludedDomains); long t1 = TimeThread.getTime(); int[] limitss = replicationServer.getECLDraftCNLimits( replicationServer.getEligibleCN(), excludedDomains); assertEquals(limitss[1], maxMsg); long t2 = TimeThread.getTime(); debugInfo(tn, "Perfs - " + maxMsg + " counted in (ms):" + (t2 - t1)); try { // search on 'cn=changelog' LinkedHashSet<String> attributes = new LinkedHashSet<String>(); attributes.add("+"); attributes.add("*"); String filter = "(changenumber>="+maxMsg+")"; 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); waitOpResult(searchOp, ResultCode.SUCCESS); long t3 = TimeThread.getTime(); assertEquals(searchOp.getSearchEntries().size(), 1); debugInfo(tn, "Perfs - last change searched in (ms):" + (t3 - t2)); filter = "(changenumber>="+maxMsg+")"; 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); waitOpResult(searchOp, ResultCode.SUCCESS); long t4 = TimeThread.getTime(); assertEquals(searchOp.getSearchEntries().size(), 1); debugInfo(tn, "Perfs - last change searched in (ms):" + (t4 - t3)); filter = "(changenumber>="+(maxMsg-2)+")"; 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); waitOpResult(searchOp, ResultCode.SUCCESS); long t5 = TimeThread.getTime(); assertEquals(searchOp.getSearchEntries().size(), 3); debugInfo(tn, "Perfs - last 3 changes searched in (ms):" + (t5 - t4)); if (searchOp.getSearchEntries() != null) { for (SearchResultEntry resultEntry : searchOp.getSearchEntries()) { debugInfo(tn, "Result entry returned:" + resultEntry.toLDIFString()); } } } catch(Exception e) { fail("Ending test "+tn+" with exception:\n" + stackTraceToSingleLineString(e)); } } server01.stop(); } catch(Exception e) { fail("Ending "+tn+" test with exception:\n" + stackTraceToSingleLineString(e)); } debugInfo(tn, "Ending test with success"); } /** * Test ECl entry attributes, and there configuration. * */ private void ECLIncludeAttributes() { String tn = "ECLIncludeAttributes"; debugInfo(tn, "Starting test\n\n"); Backend backend2 = null; Backend backend3 = null; DeleteOperationBasis delOp =null; LDAPReplicationDomain domain2 = null; LDAPReplicationDomain domain3 = null; LDAPReplicationDomain domain21 = null; DN baseDn2 = null; DN baseDn3 = null; try { // Initialize a second test backend o=test2, in addtion to o=test // Configure replication on this backend // Add the root entry in the backend backend2 = initializeTestBackend(false, TEST_ROOT_DN_STRING2, TEST_BACKEND_ID2); baseDn2 = DN.decode(TEST_ROOT_DN_STRING2); SortedSet<String> replServers = new TreeSet<String>(); replServers.add("localhost:"+replicationServerPort); DomainFakeCfg domainConf = new DomainFakeCfg(baseDn2, 1702, replServers); // on o=test2,sid=1702 include attrs set to : 'sn' SortedSet<String> eclInclude = new TreeSet<String>(); eclInclude.add("sn"); eclInclude.add("roomnumber"); ExternalChangelogDomainFakeCfg eclCfg = new ExternalChangelogDomainFakeCfg(true, eclInclude, eclInclude); domainConf.setExternalChangelogDomain(eclCfg); // Set a Changetime heartbeat interval low enough (less than default // value that is 1000 ms) for the test to be sure to consider all changes // as eligible. domainConf.setChangetimeHeartbeatInterval(10); domain2 = MultimasterReplication.createNewDomain(domainConf); domain2.start(); backend3 = initializeTestBackend(false, TEST_ROOT_DN_STRING3, TEST_BACKEND_ID3); baseDn3 = DN.decode(TEST_ROOT_DN_STRING3); domainConf = new DomainFakeCfg(baseDn3, 1703, replServers); // on o=test3,sid=1703 include attrs set to : 'objectclass' eclInclude = new TreeSet<String>(); eclInclude.add("objectclass"); TreeSet<String> eclIncludeForDeletes = new TreeSet<String>(); eclIncludeForDeletes.add("*"); eclCfg = new ExternalChangelogDomainFakeCfg(true, eclInclude, eclIncludeForDeletes); domainConf.setExternalChangelogDomain(eclCfg); // Set a Changetime heartbeat interval low enough (less than default // value that is 1000 ms) for the test to be sure to consider all changes // as eligible. domainConf.setChangetimeHeartbeatInterval(10); domain3 = MultimasterReplication.createNewDomain(domainConf); domain3.start(); // on o=test2,sid=1704 include attrs set to : 'cn' domainConf = new DomainFakeCfg(baseDn2, 1704, replServers); eclInclude = new TreeSet<String>(); eclInclude.add("cn"); eclCfg = new ExternalChangelogDomainFakeCfg(true, eclInclude, eclInclude); domainConf.setExternalChangelogDomain(eclCfg); // Set a Changetime heartbeat interval low enough (less than default // value that is 1000 ms) for the test to be sure to consider all changes // as eligible. domainConf.setChangetimeHeartbeatInterval(10); domain21 = MultimasterReplication.createNewDomain(domainConf); domain21.start(); sleep(1000); Entry e2 = createEntry(baseDn2); addEntry(e2); Entry e3 = createEntry(baseDn3); addEntry(e3); String lentry = new String( "dn: cn=Fiona Jensen," + TEST_ROOT_DN_STRING2 + "\n" + "objectclass: top\n" + "objectclass: person\n" + "objectclass: organizationalPerson\n" + "objectclass: inetOrgPerson\n" + "cn: Fiona Jensen\n" + "sn: Jensen\n" + "uid: fiona\n" + "telephonenumber: 12121212"); Entry uentry1 = TestCaseUtils.entryFromLdifString(lentry); addEntry(uentry1); // add fiona in o=test2 lentry = new String( "dn: cn=Robert Hue," + TEST_ROOT_DN_STRING3 + "\n" + "objectclass: top\n" + "objectclass: person\n" + "objectclass: organizationalPerson\n" + "objectclass: inetOrgPerson\n" + "cn: Robert Hue\n" + "sn: Robby\n" + "uid: robert\n" + "telephonenumber: 131313"); Entry uentry2 = TestCaseUtils.entryFromLdifString(lentry); addEntry(uentry2); // add robert in o=test3 // mod 'sn' of fiona (o=test2) with 'sn' configured as ecl-incl-att AttributeBuilder builder = new AttributeBuilder("sn"); builder.add("newsn"); Modification mod = new Modification(ModificationType.REPLACE, builder.toAttribute()); List<Modification> mods = new ArrayList<Modification>(); mods.add(mod); ModifyOperationBasis modOpBasis = new ModifyOperationBasis(connection, 1, 1, null, uentry1.getDN(), mods); modOpBasis.run(); waitOpResult(modOpBasis, ResultCode.SUCCESS); // mod 'telephonenumber' of robert (o=test3) builder = new AttributeBuilder("telephonenumber"); builder.add("555555"); mod = new Modification(ModificationType.REPLACE, builder.toAttribute()); mods = new ArrayList<Modification>(); mods.add(mod); ModifyOperationBasis modOpBasis2 = new ModifyOperationBasis(connection, 1, 1, null, uentry2.getDN(), mods); modOpBasis2.run(); waitOpResult(modOpBasis2, ResultCode.SUCCESS); // moddn robert (o=test3) to robert2 (o=test3) ModifyDNOperationBasis modDNOp = new ModifyDNOperationBasis(connection, InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, DN.decode("cn=Robert Hue," + TEST_ROOT_DN_STRING3), RDN.decode("cn=Robert Hue2"), true, DN.decode(TEST_ROOT_DN_STRING3)); modDNOp.run(); waitOpResult(modDNOp, ResultCode.SUCCESS); // del robert (o=test3) delOp = new DeleteOperationBasis(connection, InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, DN.decode("cn=Robert Hue2," + TEST_ROOT_DN_STRING3)); delOp.run(); waitOpResult(delOp, ResultCode.SUCCESS); getEntry(DN.decode("cn=Robert Hue2," + TEST_ROOT_DN_STRING3),5000,false); // Search on ECL from start on all suffixes String cookie = ""; ExternalChangelogRequestControl control = new ExternalChangelogRequestControl(true, new MultiDomainServerState(cookie)); ArrayList<Control> controls = new ArrayList<Control>(0); controls.add(control); LinkedHashSet<String> attributes = new LinkedHashSet<String>(); attributes.add("+"); attributes.add("*"); sleep(1000); 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=*)"), attributes, controls, null); waitOpResult(searchOp, ResultCode.SUCCESS); LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries(); sleep(2000); assertTrue(entries != null); String s = tn + " entries returned= "; if (entries != null) { for (SearchResultEntry resultEntry : entries) { // Expect debugInfo(tn, "Entry returned =" + resultEntry.toLDIFString()); s += "Entry:" + resultEntry.toLDIFString(); String targetdn = getAttributeValue(resultEntry, "targetdn"); if ((targetdn.endsWith("cn=robert hue,o=test3")) ||(targetdn.endsWith("cn=robert hue2,o=test3"))) { Entry targetEntry = parseIncludedAttributes(resultEntry, targetdn); HashSet<String> eoc = new HashSet<String>(); eoc.add("person");eoc.add("inetOrgPerson");eoc.add("organizationalPerson");eoc.add("top"); checkValues(targetEntry,"objectclass",eoc); String changeType = getAttributeValue(resultEntry, "changetype"); if (changeType.equals("delete")) { // We are using "*" for deletes so should get back 4 attributes. assertEquals(targetEntry.getAttributes().size(), 4); checkValue(targetEntry, "uid", "robert"); checkValue(targetEntry, "cn", "Robert Hue2"); checkValue(targetEntry, "telephonenumber", "555555"); checkValue(targetEntry, "sn", "Robby"); } else { assertEquals(targetEntry.getAttributes().size(), 0); } } if (targetdn.endsWith("cn=fiona jensen,o=test2")) { Entry targetEntry = parseIncludedAttributes(resultEntry, targetdn); assertEquals(targetEntry.getAttributes().size(), 2); checkValue(targetEntry,"sn","jensen"); checkValue(targetEntry,"cn","Fiona Jensen"); } checkValue(resultEntry,"changeinitiatorsname", "cn=Internal Client,cn=Root DNs,cn=config"); } } assertEquals(entries.size(),8, "Entries number returned by search" + s); } catch(Exception e) { fail("End test "+tn+" with exception:\n" + stackTraceToSingleLineString(e)); } finally { try { delOp = new DeleteOperationBasis(connection, InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, DN.decode("cn=Fiona Jensen," + TEST_ROOT_DN_STRING2)); delOp.run(); waitOpResult(delOp, ResultCode.SUCCESS); delOp = new DeleteOperationBasis(connection, InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, DN.decode(TEST_ROOT_DN_STRING2)); delOp.run(); waitOpResult(delOp, ResultCode.SUCCESS); delOp = new DeleteOperationBasis(connection, InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, DN.decode(TEST_ROOT_DN_STRING3)); delOp.run(); waitOpResult(delOp, ResultCode.SUCCESS); // Cleaning if (domain21 != null) domain21.shutdown(); if (domain2 != null) MultimasterReplication.deleteDomain(baseDn2); removeTestBackend2(backend2); if (domain3 != null) MultimasterReplication.deleteDomain(baseDn3); removeTestBackend2(backend3); } catch(Exception e) { fail("Ending test "+tn+" with exception in test cleanup:\n" + stackTraceToSingleLineString(e)); } finally { replicationServer.clearDb(); } } debugInfo(tn, "Ending test with success"); } private Entry parseIncludedAttributes( SearchResultEntry resultEntry, String targetdn) throws Exception { // Parse includedAttributes as an entry. String includedAttributes = getAttributeValue(resultEntry, "includedattributes"); String[] ldifAttributeLines = includedAttributes.split("\\n"); String[] ldif = new String[ldifAttributeLines.length + 1]; System.arraycopy(ldifAttributeLines, 0, ldif, 1, ldifAttributeLines.length); ldif[0] = "dn: " + targetdn; Entry targetEntry = TestCaseUtils.makeEntry(ldif); return targetEntry; } private void waitOpResult(Operation operation, ResultCode expectedResult) { int ii=0; while((operation.getResultCode()==ResultCode.UNDEFINED) || (operation.getResultCode()!=expectedResult)) { sleep(50); ii++; if (ii>10) assertEquals(operation.getResultCode(), expectedResult, operation.getErrorMessage().toString()); } } }