/*
* 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 2013 ForgeRock As.
*/
package org.opends.server.replication;
import static org.opends.server.loggers.ErrorLogger.logError;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.net.ServerSocket;
import java.util.LinkedList;
import java.util.List;
import org.opends.server.TestCaseUtils;
import org.opends.messages.Category;
import org.opends.messages.Severity;
import org.opends.messages.Message;
import org.opends.server.admin.std.server.MonitorProviderCfg;
import org.opends.server.api.MonitorProvider;
import org.opends.server.config.ConfigException;
import org.opends.server.core.AddOperationBasis;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.replication.service.ReplicationBroker;
import org.opends.server.replication.protocol.AddMsg;
import org.opends.server.replication.protocol.ReplicationMsg;
import org.opends.server.types.Attribute;
import org.opends.server.types.Attributes;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.Modification;
import org.opends.server.types.Operation;
import org.opends.server.types.OperationType;
import org.opends.server.types.ResultCode;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.opends.server.TestCaseUtils.*;
/**
* Stress test for the synchronization code using the ReplicationBroker API.
*/
public class StressTest extends ReplicationTestCase
{
private static final String REPLICATION_STRESS_TEST =
"Replication Stress Test";
private BrokerReader reader = null;
/**
* A "person" entry
*/
protected Entry personEntry;
private int replServerPort;
/**
* Stress test from LDAP server to client using the ReplicationBroker API.
*/
@Test(enabled=false, groups="slow")
public void fromServertoBroker() throws Exception
{
logError(Message.raw(Category.SYNC, Severity.NOTICE,
"Starting replication StressTest : fromServertoBroker"));
final DN baseDn = DN.decode("ou=People," + TEST_ROOT_DN_STRING);
final int TOTAL_MESSAGES = 1000;
ReplicationBroker broker =
openReplicationSession(baseDn, 18, 100, replServerPort, 5000, true);
Monitor monitor = new Monitor();
DirectoryServer.registerMonitorProvider(monitor);
try {
/*
* Test that operations done on this server are sent to the
* replicationServer and forwarded to our replicationServer broker session.
*/
// Create an Entry (add operation) that will be later used in the test.
Entry tmp = personEntry.duplicate(false);
AddOperationBasis addOp = new AddOperationBasis(connection,
InternalClientConnection.nextOperationID(), InternalClientConnection
.nextMessageID(), null, tmp.getDN(),
tmp.getObjectClasses(), tmp.getUserAttributes(),
tmp.getOperationalAttributes());
addOp.run();
assertTrue(DirectoryServer.entryExists(personEntry.getDN()),
"The Add Entry operation failed");
if (ResultCode.SUCCESS == addOp.getResultCode())
{
// Check if the client has received the msg
ReplicationMsg msg = broker.receive();
assertTrue(msg instanceof AddMsg,
"The received replication message is not an ADD msg");
AddMsg addMsg = (AddMsg) msg;
Operation receivedOp = addMsg.createOperation(connection);
assertTrue(OperationType.ADD.compareTo(receivedOp.getOperationType()) == 0,
"The received replication message is not an ADD msg");
assertEquals(DN.decode(addMsg.getDn()),personEntry.getDN(),
"The received ADD replication message is not for the excepted DN");
}
reader = new BrokerReader(broker);
reader.start();
int count = TOTAL_MESSAGES;
// Create a number of writer thread that will loop modifying the entry
List<Thread> writerThreadList = new LinkedList<Thread>();
for (int n = 0; n < 1; n++)
{
BrokerWriter writer = new BrokerWriter(count);
writerThreadList.add(writer);
}
for (Thread thread : writerThreadList)
{
thread.start();
}
// wait for all the threads to finish.
for (Thread thread : writerThreadList)
{
thread.join();
}
int rcvCount = reader.getCount();
if (rcvCount != TOTAL_MESSAGES)
{
fail("some messages were lost : expected : " +TOTAL_MESSAGES +
" received : " + rcvCount);
}
}
finally {
DirectoryServer.deregisterMonitorProvider(monitor);
broker.stop();
}
}
/**
* Set up the environment for performing the tests in this Class.
*
* @throws Exception
* If the environment could not be set up.
*/
@BeforeClass
public void setUp() throws Exception
{
super.setUp();
// This test suite depends on having the schema available.
// Create an internal connection
connection = InternalClientConnection.getRootConnection();
// Create necessary backend top level entry
String topEntry = "dn: ou=People," + TEST_ROOT_DN_STRING + "\n"
+ "objectClass: top\n"
+ "objectClass: organizationalUnit\n"
+ "entryUUID: 11111111-1111-1111-1111-111111111111\n";
TestCaseUtils.addEntry(topEntry);
// find a free port for the replicationServer
ServerSocket socket = TestCaseUtils.bindFreePort();
replServerPort = socket.getLocalPort();
socket.close();
// Change log
String replServerLdif =
"dn: cn=Replication Server, " + SYNCHRO_PLUGIN_DN + "\n"
+ "objectClass: top\n"
+ "objectClass: ds-cfg-replication-server\n"
+ "cn: Replication Server\n"
+ "ds-cfg-replication-port: " + replServerPort + "\n"
+ "ds-cfg-replication-db-directory: StressTest\n"
+ "ds-cfg-replication-server-id: 106\n";
replServerEntry = TestCaseUtils.entryFromLdifString(replServerLdif);
// suffix synchronized
String testName = "stressTest";
String synchroServerLdif =
"dn: cn=" + testName + ", cn=domains, " + SYNCHRO_PLUGIN_DN + "\n"
+ "objectClass: top\n"
+ "objectClass: ds-cfg-replication-domain\n"
+ "cn: " + testName + "\n"
+ "ds-cfg-base-dn: ou=People," + TEST_ROOT_DN_STRING + "\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," + TEST_ROOT_DN_STRING + "\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();
}
private class BrokerWriter extends Thread
{
int count;
/**
* Creates a new Stress Test Reader
* @param count
*/
public BrokerWriter(int count)
{
this.count = count;
}
/**
* {@inheritDoc}
*/
@Override
public void run()
{
while (count>0)
{
count--;
// must generate the mods for every operation because they are modified
// by processModify.
List<Modification> mods = generatemods("telephonenumber", "01 02 45");
ModifyOperation modOp =
connection.processModify(personEntry.getDN(), mods);
assertEquals(modOp.getResultCode(), ResultCode.SUCCESS);
}
}
}
/**
* Continuously reads messages from a replicationServer broker until there is nothing
* left. Count the number of received messages.
*/
private class BrokerReader extends Thread
{
private ReplicationBroker broker;
private int count = 0;
private Boolean finished = false;
/**
* Creates a new Stress Test Reader
* @param broker
*/
public BrokerReader(ReplicationBroker broker)
{
this.broker = broker;
}
/**
* {@inheritDoc}
*/
@Override
public void run()
{
// loop receiving messages until either we get a timeout
// because there is nothing left or an error condition happens.
try
{
while (true)
{
ReplicationMsg msg = broker.receive();
if (msg == null)
break;
count ++;
}
} catch (Exception e)
{}
finally
{
synchronized (this)
{
finished = true;
this.notify();
}
}
}
/**
* wait until the thread has finished its job then return the number of
* received messages.
*/
public int getCount()
{
synchronized (this)
{
int i = 20;
while ((finished != true) && (i-- >0))
{
try
{
this.wait(6000);
} catch (InterruptedException e)
{
return -1;
}
}
return count;
}
}
public int getCurrentCount()
{
return count;
}
}
private class Monitor extends MonitorProvider<MonitorProviderCfg>
{
@Override
public List<Attribute> getMonitorData()
{
Attribute attr;
if (reader == null)
attr = Attributes.create("received-messages", "not yet started");
else
attr = Attributes.create("received-messages", String
.valueOf(reader.getCurrentCount()));
List<Attribute> list = new LinkedList<Attribute>();
list.add(attr);
attr = Attributes.create("base-dn", "ou=People," + TEST_ROOT_DN_STRING);
list.add(attr);
return list;
}
@Override
public String getMonitorInstanceName()
{
return REPLICATION_STRESS_TEST;
}
@Override
public void initializeMonitorProvider(MonitorProviderCfg configuration)
throws ConfigException, InitializationException
{
// nothing to do
}
}
}