ReplicationTestCase.java revision 28215a00c6a6c49ab982f51dee97d501ce954ad3
/*
* 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 legal-notices/CDDLv1_0.txt
* 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 legal-notices/CDDLv1_0.txt.
* 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-2015 ForgeRock AS
*/
/** An abstract class that all Replication unit test should extend. */
@SuppressWarnings("javadoc")
public abstract class ReplicationTestCase extends DirectoryServerTestCase
{
/** The tracer object for the debug logger. */
/**
* This is the generation id matching the memory test backend with its initial
* root entry o=test created. This matches the backend obtained calling:
* TestCaseUtils.initializeTestBackend(true). (using the default
* TestCaseUtils.TEST_ROOT_DN_STRING suffix)
*/
protected static final long TEST_DN_WITH_ROOT_ENTRY_GENID = 5055L;
/** Generation id for a fully empty domain. */
/** The internal connection used for operation. */
protected InternalClientConnection connection;
/** Created entries that will be deleted on class cleanup. */
/** Created config entries that will be deleted on class cleanup. */
/** Replicated suffix (replication domain). */
protected Entry synchroServerEntry;
protected Entry replServerEntry;
public static ReplicationDBImplementation replicationDbImplementation = ReplicationDBImplementation.valueOf(
/** Replication monitor stats. */
private String monitorAttr;
private long lastCount;
/**
* Call the paranoiaCheck at test cleanup or not.
* <p>
* Must not been touched except if sub class has its own clean up code, for
* instance:
*
* <pre>
* @AfterClass
* public void classCleanUp() throws Exception
* {
* callParanoiaCheck = false;
* super.classCleanUp();
*
* // Clear my own stuff that I have setup (in my own setup() method for instance)
* // This removes the replication changes backend
* myReplServerInstantiatedWithConstructor.remove();
*
* // Now call paramoiaCheck myself
* paranoiaCheck();
* }
*
* </pre>
*/
protected boolean callParanoiaCheck = true;
/** The replication plugin entry. */
protected static final String SYNCHRO_PLUGIN_DN =
"cn=Multimaster Synchronization, cn=Synchronization Providers,cn=config";
/** Set up the environment for performing the tests in this suite. */
{
// This test suite depends on having the schema available.
// Initialize the test backend (TestCaseUtils.TEST_ROOT_DN_STRING)
// (in case previous (non replication?) tests were run before...)
// Create an internal connection
callParanoiaCheck = true;
}
/**
* Retrieves the domain associated to the baseDN, and the value of the generationId
* of this domain. If the domain does not exist, returns the default hard-coded\
* value of the generationId corresponding to test backend with its default
* initial o=test root root entry.
*
* @param baseDN The baseDN for which we want the generationId
* @return The value of the generationId.
*/
{
try
{
return replDomain.getGenerationID();
}
catch(Exception e) {
logger.traceException(e);
// This is the value of the generationId computed by the server when the
// test suffix (o=test) has only the root entry created.
return TEST_DN_WITH_ROOT_ENTRY_GENID;
}
}
/**
* Open a replicationServer session to the local ReplicationServer.
* The generation is read from the replicationDomain object. If it
* does not exist, take the 'empty backend' generationID.
*/
{
}
/** Open a replicationServer session to the local ReplicationServer providing the generationId. */
long generationId) throws Exception
{
}
{
return broker;
}
{
return fakeCfg;
}
{
// give some time to the broker to connect to the replicationServer.
if (timeout != 0)
{
}
}
/**
* Check connection of the provided ds to the replication server. Waits for connection to be ok up
* to secTimeout seconds before failing.
*/
protected void checkConnection(int secTimeout, final ReplicationBroker rb, int rsPort) throws Exception
{
.toTimer();
{
{
if (rb.isConnected())
{
return null;
}
return null;
}
});
}
{
{
}
}
/** Suppress all the config entries created by the tests in this class. */
protected void cleanConfigEntries() throws Exception
{
{
}
}
/** Suppress all the real entries created by the tests in this class. */
protected void cleanRealEntries() throws Exception
{
{
}
}
/**
* Clean up the environment. return null;
*
* @throws Exception If the environment could not be set up.
*/
public void classCleanUp() throws Exception
{
// Clear the test backend (TestCaseUtils.TEST_ROOT_DN_STRING)
// (in case our test created some entries in it)
if (callParanoiaCheck)
{
}
}
/**
* After having run, each replication test should not leave any of the following:
* - config entry for replication server
* - config entry for a replication domain
* - replication domain object
* - config entry for a replication changes backend
* - replication changes backend object
* This method checks for existence of anything of that type.
*/
protected void paranoiaCheck() throws Exception
{
// Check for config entries for replication server
assertNoConfigEntriesWithFilter("(objectclass=ds-cfg-replication-server)",
"Found unexpected replication server config left");
// Be sure that no replication server instance is left
// Check for config entries for replication domain
assertNoConfigEntriesWithFilter("(objectclass=ds-cfg-replication-domain)",
"Found unexpected replication domain config left");
// Check for left domain object
assertEquals(MultimasterReplication.getNumberOfDomains(), 0, "Some replication domain objects left");
}
{
{
return;
}
{
}
else
{
}
}
/** Cleanup databases of the currently instantiated replication servers in the VM. */
protected void cleanUpReplicationServersDB() throws Exception
{
{
}
}
/** Remove trailing directories and databases of the currently instantiated replication servers. */
protected void removeReplicationServerDB() throws Exception
{
// avoid ConcurrentModificationException
}
{
}
throws Exception
{
{
{
}
}
}
{
{
return;
}
{
{
}
}
}
/**
* Performs a search on the config backend with the specified filter.
* Fails if a config entry is found.
* @param filter The filter to apply for the search
* @param errorMsg The error message to display if a config entry is found
*/
throws Exception
{
// Search for matching entries in config backend
InternalSearchOperation op = connection.processSearch(newSearchRequest("cn=config", WHOLE_SUBTREE, filter));
// Check that no entries have been found
{
}
}
/** Configure the replication for this test. */
{
}
throws Exception
{
}
{
if (configEntry != null)
{
}
}
/**
* Get the value of the specified attribute for a given replication
* domain from the monitor entry.
* @return The monitor value
* @throws Exception If an error occurs.
*/
{
.toTimer();
{
{
connection.processSearch(newSearchRequest("cn=replication,cn=monitor", WHOLE_SUBTREE, monitorFilter));
Assertions.assertThat(op.getSearchEntries()).as("Could not read monitoring information").isNotEmpty();
}
});
}
protected void checkEntryHasAttributeValue(final DN dn, final String attrTypeStr, final String valueString,
{
}
protected void checkEntryHasNoSuchAttributeValue(final DN dn, final String attrTypeStr, final String valueString,
{
}
protected boolean checkEntryHasAttribute(final DN dn, final String attrTypeStr, final String valueString,
{
checkEntryHasAttribute(dn, attrTypeStr, valueString, timeout / 1000, expectedAttributeValueFound, null);
return expectedAttributeValueFound;
}
private void checkEntryHasAttribute(final DN dn, final String attrTypeStr, final String valueString,
int timeoutInSecs, final boolean expectedAttributeValueFound, final String foundMsg) throws Exception
{
.toTimer();
{
{
return null;
}
});
}
/**
* Retrieves an entry from the local Directory Server.
* @throws Exception When the entry cannot be locked.
*/
{
.toTimer();
{
{
return null;
}
});
{
}
return null;
}
/** Update the monitor count for the specified monitor attribute. */
{
monitorAttr = attr;
}
/**
* Get the delta between the current / last monitor counts.
* @return The delta between the current and last monitor count.
*/
protected long getMonitorDelta() throws Exception
{
return delta;
}
/**
* Generate a new modification replace with the given information.
*
* @param attrName The attribute to replace.
* @param attrValue The new value for the attribute
*
* @return The modification replace.
*/
{
}
/** Utility method to create, run a task and check its result. */
{
// Wait until the task completes.
.toTimer();
{
{
return resultEntry;
}
});
// Check that the task state is as expected.
"The task completed in an unexpected state");
}
/**
* Create a new replication session security object that can be used in unit tests.
*
* @return A new replication session security object.
* @throws ConfigException If an error occurs.
*/
{
}
{
}
/**
* Add a task to the configuration of the current running DS.
* @param taskEntry The task to add.
* @param expectedResult The expected result code for the ADD.
* @param errorMessage The expected error message when the expected
* result code is not SUCCESS
*/
{
// Change config of DS to launch the total update task
"Result of ADD operation of the task is: "
{
}
else
{
}
// Entry will be removed at the end of the test
}
{
.toTimer();
{
{
return resultEntry;
}
});
// Check that the task contains some log messages.
{
"No log messages were written to the task entry on a failed task");
}
if (!logMessages.isEmpty())
{
if (expectedMessage != null)
{
}
}
{
// We usually wait the running state after adding the task
// and if the task is fast enough then it may be already done
// and we can go on.
}
else
{
+ " Expected task state:" + expectedTaskState);
}
}
{
}
/** Add to the current DB the entries necessary to the test. */
{
{
{
}
else
{
}
}
}
/**
* Get the entryUUID for a given DN.
*
* @throws Exception if the entry does not exist or does not have
* an entryUUID.
*/
{
.toTimer();
{
{
return found;
}
});
}
/** Utility method : removes a domain deleting the passed config entry */
{
{
{
}
}
}
/**
* Wait for the arrival of a specific message type on the provided session
* before going in timeout and failing.
* @param session Session from which we should receive the message.
* @param msgType Class of the message we are waiting for.
* @return The expected message if it comes in time or fails (assertion).
*/
protected static <T extends ReplicationMsg> T waitForSpecificMsg(Session session, Class<T> msgType) throws Exception
{
}
/**
* Wait for the arrival of a specific message type on the provided broker
* before going in timeout and failing.
* @param broker Broker from which we should receive the message.
* @param msgType Class of the message we are waiting for.
* @return The expected message if it comes in time or fails (assertion).
*/
protected static <T extends ReplicationMsg> T waitForSpecificMsg(ReplicationBroker broker, Class<T> msgType)
throws Exception
{
}
protected static ReplicationMsg waitForSpecificMsgs(Session session, Class<?>... msgTypes) throws Exception
{
}
protected static ReplicationMsg waitForSpecificMsgs(ReplicationBroker broker, Class<?>... msgTypes) throws Exception
{
}
private static ReplicationMsg waitForSpecificMsgs(Session session, ReplicationBroker broker, Class<?>... msgTypes)
throws Exception
{
assertTrue(session != null || broker != null, "One of Session or ReplicationBroker parameter must not be null");
assertTrue(session == null || broker == null, "Only one of Session or ReplicationBroker parameter must not be null");
boolean timedOut = false;
while (!timedOut)
{
{
}
{
}
{
// Ok, got it, let's return the expected message
return replMsg;
}
}
// Timeout
+ " Also received the following messages during wait time: " + msgs);
return null;
}
/**
* Performs an internal search, waiting for at most 3 seconds for expected result code and expected
* number of entries.
*/
protected InternalSearchOperation waitForSearchResult(final String dn, final SearchScope scope, final String filter,
{
.toTimer();
{
{
return searchOp;
}
});
}
{
}
}