/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008 Sun Microsystems, Inc. */ package org.opends.server.replication.plugin; import static org.opends.server.loggers.ErrorLogger.logError; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; import java.net.ServerSocket; import java.util.Iterator; import java.util.List; import org.opends.messages.Category; import org.opends.messages.Message; import org.opends.messages.Severity; import org.opends.server.TestCaseUtils; import org.opends.server.core.AddOperationBasis; import org.opends.server.core.DirectoryServer; import org.opends.server.protocols.asn1.ASN1OctetString; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.protocols.internal.InternalSearchOperation; import org.opends.server.replication.ReplicationTestCase; import org.opends.server.replication.common.ChangeNumber; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; import org.opends.server.types.AttributeValue; import org.opends.server.types.ByteString; import org.opends.server.types.DN; import org.opends.server.types.Entry; import org.opends.server.types.ResultCode; import org.opends.server.types.SearchResultEntry; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** * Test the usage of the historical data of the replication. */ public class HistoricalCsnOrderingTest extends ReplicationTestCase { /** * A "person" entry */ protected Entry personEntry; private int replServerPort; /** * Set up the environment for performing the tests in this Class. * * @throws Exception * If the environment could not be set up. */ @BeforeClass @Override public void setUp() throws Exception { super.setUp(); // Create backend top level entries String[] topEntries = new String[2]; topEntries[0] = "dn: dc=example,dc=com\n" + "objectClass: top\n" + "objectClass: domain\n"; topEntries[1] = "dn: ou=People,dc=example,dc=com\n" + "objectClass: top\n" + "objectClass: organizationalUnit\n" + "entryUUID: 11111111-1111-1111-1111-111111111111\n"; for (String entryStr : topEntries) { addEntry(TestCaseUtils.entryFromLdifString(entryStr)); } // top level synchro provider String synchroStringDN = "cn=Synchronization Providers,cn=config"; // Multimaster Synchro plugin synchroPluginStringDN = "cn=Multimaster Synchronization, " + synchroStringDN; // find a free port for the replicationServer ServerSocket socket = TestCaseUtils.bindFreePort(); replServerPort = socket.getLocalPort(); socket.close(); // replication server String replServerLdif = "dn: cn=Replication Server, " + synchroPluginStringDN + "\n" + "objectClass: top\n" + "objectClass: ds-cfg-replication-server\n" + "cn: Replication Server\n" + "ds-cfg-replication-port: " + replServerPort + "\n" + "ds-cfg-replication-server-id: 1\n"; replServerEntry = TestCaseUtils.entryFromLdifString(replServerLdif); // suffix synchronized String synchroServerLdif = "dn: cn=example, cn=domains, " + synchroPluginStringDN + "\n" + "objectClass: top\n" + "objectClass: ds-cfg-replication-domain\n" + "cn: example\n" + "ds-cfg-base-dn: ou=People,dc=example,dc=com\n" + "ds-cfg-replication-server: localhost:" + replServerPort + "\n" + "ds-cfg-server-id: 1\n" + "ds-cfg-receive-status: true\n"; synchroServerEntry = TestCaseUtils.entryFromLdifString(synchroServerLdif); String personLdif = "dn: uid=user.1,ou=People,dc=example,dc=com\n" + "objectClass: top\n" + "objectClass: person\n" + "objectClass: organizationalPerson\n" + "objectClass: inetOrgPerson\n" + "uid: user.1\n" + "homePhone: 951-245-7634\n" + "description: This is the description for Aaccf Amar.\n" + "st: NC\n" + "mobile: 027-085-0537\n" + "postalAddress: Aaccf Amar$17984 Thirteenth Street" + "$Rockford, NC 85762\n" + "mail: user.1@example.com\n" + "cn: Aaccf Amar\n" + "l: Rockford\n" + "pager: 508-763-4246\n" + "street: 17984 Thirteenth Street\n" + "telephoneNumber: 216-564-6748\n" + "employeeNumber: 1\n" + "sn: Amar\n" + "givenName: Aaccf\n" + "postalCode: 85762\n" + "userPassword: password\n" + "initials: AA\n"; personEntry = TestCaseUtils.entryFromLdifString(personLdif); configureReplication(); } /** * Add an entry in the datatbase * */ private void addEntry(Entry entry) throws Exception { AddOperationBasis addOp = new AddOperationBasis(connection, InternalClientConnection.nextOperationID(), InternalClientConnection .nextMessageID(), null, entry.getDN(), entry.getObjectClasses(), entry.getUserAttributes(), entry.getOperationalAttributes()); addOp.setInternalOperation(true); addOp.run(); assertNotNull(getEntry(entry.getDN(), 1000, true)); } /** * Check the basic comparator on the HistoricalCsnOrderingMatchingRule */ @Test() public void basicRuleTest() throws Exception { // Creates a rule HistoricalCsnOrderingMatchingRule r = new HistoricalCsnOrderingMatchingRule(); ChangeNumber del1 = new ChangeNumber(1, (short) 0, (short) 1); ChangeNumber del2 = new ChangeNumber(1, (short) 1, (short) 1); ByteString v1 = new ASN1OctetString("a"+":"+del1.toString()); ByteString v2 = new ASN1OctetString("a"+":"+del2.toString()); int cmp = r.compareValues(v1, v1); assertTrue(cmp == 0); cmp = r.compareValues(v1, v2); assertTrue(cmp == -1); cmp = r.compareValues(v2, v1); assertTrue(cmp == 1); } /** * Test that we can retrieve the entries that were missed by * a replication server and can re-build operations from the historical * informations. */ @Test() public void changesCmpTest() throws Exception { final DN baseDn = DN.decode("ou=People,dc=example,dc=com"); final DN dn1 = DN.decode("cn=test1," + baseDn.toString()); final AttributeType histType = DirectoryServer.getAttributeType(Historical.HISTORICALATTRIBUTENAME); logError(Message.raw(Category.SYNC, Severity.INFORMATION, "Starting replication test : changesCmpTest")); // Add the first test entry. TestCaseUtils.addEntry( "dn: cn=test1," + baseDn.toString(), "displayname: Test1", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "cn: test1", "sn: test" ); // Perform a first modification to update the historical attribute int resultCode = TestCaseUtils.applyModifications( "dn: cn=test1," + baseDn.toString(), "changetype: modify", "add: description", "description: foo"); assertEquals(resultCode, 0); // Read the entry back to get its historical and included changeNumber Entry entry = DirectoryServer.getEntry(dn1); List attrs1 = entry.getAttribute(histType); assertTrue(attrs1 != null); assertTrue(attrs1.isEmpty() != true); String histValue = attrs1.get(0).getValues().iterator().next().getStringValue(); logError(Message.raw(Category.SYNC, Severity.INFORMATION, "First historical value:" + histValue)); // Perform a 2nd modification to update the hist attribute with // a second value resultCode = TestCaseUtils.applyModifications( "dn: cn=test1," + baseDn.toString(), "changetype: modify", "add: description", "description: bar"); assertEquals(resultCode, 0); Entry entry2 = DirectoryServer.getEntry(dn1); List attrs2 = entry2.getAttribute(histType); assertTrue(attrs2 != null); assertTrue(attrs2.isEmpty() != true); Iterator iav = attrs2.get(0).getValues().iterator(); try { while (true) { AttributeValue av = iav.next(); logError(Message.raw(Category.SYNC, Severity.INFORMATION, "Secnd historical value:" + av.getNormalizedStringValue())); } } catch(Exception e) { } // Build a change number from the first modification String hv[] = histValue.split(":"); logError(Message.raw(Category.SYNC, Severity.INFORMATION, hv[1])); ChangeNumber fromChangeNumber = new ChangeNumber(hv[1]); // Retrieves the entries that have changed since the first modification InternalSearchOperation op = ReplicationBroker.searchForChangedEntries(baseDn, fromChangeNumber, null); // The expected result is one entry .. the one previously modified assertTrue(op.getResultCode() == ResultCode.SUCCESS); assertTrue(op.getSearchEntries().size()==1); // From the historical of this entry, rebuild operations // Since there have been 2 modifications, there should be 2 // operations rebuild from this state. int updatesCnt = 0; for (SearchResultEntry searchEntry : op.getSearchEntries()) { logError(Message.raw(Category.SYNC, Severity.INFORMATION, searchEntry.toString())); Iterable updates = Historical.generateFakeOperations(searchEntry); for (FakeOperation fop : updates) { logError(Message.raw(Category.SYNC, Severity.INFORMATION, fop.generateMessage().toString())); updatesCnt++; } } assertTrue(updatesCnt == 2); } }