UpdateOperationTest.java revision 2342
/*
* 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
* 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
*
*
* Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
/**
* Test synchronization of update operations on the directory server and through
* the replication server broker interface.
*/
public class UpdateOperationTest extends ReplicationTestCase
{
/**
* An entry with a entryUUID
*/
private Entry personWithUUIDEntry;
private Entry personWithSecondUniqueID;
private String user1entrysecondUUID;
private String user1entryUUID;
/**
* A "person" entry
*/
protected Entry personEntry;
private int replServerPort;
private String domain1uid;
private String domain2uid;
private String domain3uid;
/**
* Set up the environment for performing the tests in this Class.
*
* @throws Exception
* If the environment could not be set up.
*/
{
// This test suite depends on having the schema available.
// Create an internal connection
// Create backend top level entries
+ "objectClass: domain\n";
+ "objectClass: organizationalUnit\n"
+ "entryUUID: 11111111-1111-1111-1111-111111111111\n";
{
}
// top level synchro provider
// Multimaster Synchro plugin
synchroPluginStringDN = "cn=Multimaster Synchronization, "
// find a free port for the replicationServer
// replication server
+ "objectClass: top\n"
+ "objectClass: ds-cfg-replication-server-config\n"
+ "cn: Replication Server\n"
+ "ds-cfg-replication-server-id: 1\n";
// suffix synchronized
+ "objectClass: top\n"
+ "objectClass: ds-cfg-replication-domain-config\n"
+ "cn: example\n"
+ "ds-cfg-synchronization-dn: ou=People,dc=example,dc=com\n"
+ "ds-cfg-directory-server-id: 1\n" + "ds-cfg-receive-status: true\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";
/*
* The 2 entries defined in the following code are used for the naming
* conflict resolution test (called namingConflicts)
* They must have the same DN but different entryUUID.
*/
user1entryUUID = "33333333-3333-3333-3333-333333333333";
user1entrysecondUUID = "22222222-2222-2222-2222-222222222222";
user1dn = "uid=user1,ou=People,dc=example,dc=com";
+ "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"
+ "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"
domain1dn = "dc=domain1,ou=People,dc=example,dc=com";
domain2dn = "dc=domain2,dc=domain1,ou=People,dc=example,dc=com";
domain3dn = "dc=domain3,dc=domain1,ou=People,dc=example,dc=com";
+ "objectClass:domain\n"
+ "dc:domain1");
+ "objectClass:domain\n"
+ "dc:domain2");
+ "objectClass:domain\n"
+ "dc:domain3");
}
/**
* Add an entry in the datatbase
*
*/
{
addOp.setInternalOperation(true);
}
/**
* Delete an entry in the datatbase
*
*/
{
}
/**
* Tests whether the synchronization provider receive status can be disabled
* then re-enabled.
* @throws Exception
*/
public void toggleReceiveStatus() throws Exception
{
"Starting synchronization test : toggleReceiveStatus"));
/*
* Open a session to the replicationServer using the broker API.
* This must use a different serverId to that of the directory server.
*/
/*
* Create a Change number generator to generate new changenumbers
* when we need to send operation messages to the replicationServer.
*/
// Disable the directory server receive status.
// Create and publish an update message to add an entry.
// Check that the entry has not been created in the directory server.
"The replication message was replayed while the server " +
"receive status was disabled");
// Enable the directory server receive status.
// Create and publish another update message to add an entry.
// Check that the entry has been created in the directory server.
"The replication message was not replayed after the server " +
"receive status was enabled");
// Delete the entries to clean the database.
// Check that the delete operation has been applied.
"The DELETE replication message was not replayed");
}
/**
* Tests whether the synchronization provider fails over when it loses
* the heartbeat from the replication server.
*/
public void lostHeartbeatFailover() throws Exception
{
"Starting replication test : lostHeartbeatFailover"));
/*
* Open a session to the replicationServer using the broker API.
* This must use a different serverId to that of the directory server.
*/
/*
* Create a Change number generator to generate new changenumbers
* when we need to send operation messages to the replicationServer.
*/
// Create and publish an update message to add an entry.
// Check that the entry has been created in the directory server.
"The ADD replication message was not replayed");
// Send a first modify operation message.
// Check that the modify has been replayed.
"telephonenumber", "01 02 45", 10000, true);
if (!found)
{
fail("The first modification was not replayed.");
}
// Simulate loss of heartbeats.
HeartbeatThread.setHeartbeatsDisabled(false);
// Send a second modify operation message.
// Check that the modify has been replayed.
"description", "Description was changed",
10000, true);
if (!found)
{
fail("The second modification was not replayed.");
}
// Delete the entries to clean the database.
// Check that the delete operation has been applied.
"The DELETE replication message was not replayed");
}
/**
* Tests the modify conflict resolution code.
* In this test, the local server acts both as an LDAP server and
* a replicationServer that are inter-connected.
*
* The test creates an other session to the replicationServer using
* directly the ReplicationBroker API.
* It then uses this session to simulate conflicts and therefore
* test the modify conflict resolution code.
*/
public void modifyConflicts()
throws Exception
{
final AttributeType attrType =
final AttributeType entryuuidType =
/*
* Open a session to the replicationServer using the broker API.
* This must use a different serverId to that of the directory server.
*/
// Add the first test entry.
"displayname: Test1",
"objectClass: top",
"objectClass: person",
"objectClass: organizationalPerson",
"objectClass: inetOrgPerson",
"cn: test1",
"sn: test"
);
// Read the entry back to get its UUID.
// A change on a first server.
// A change on a second server.
// Simulate the ordering t2:replace:B followed by t1:add:A that
// Replay a replace of a value B at time t2 on a second server.
// Replay an add of a value A at time t1 on a first server.
// Read the entry to see how the conflict was resolved.
// the value should be the last (time t2) value added
// Simulate the ordering t2:delete:displayname followed by
// t1:replace:displayname
// A change on a first server.
// A change on a second server.
// Simulate the ordering t2:delete:displayname followed by t1:replace:A
// Replay an delete of attribute displayname at time t2 on a second server.
// Replay a replace of a value A at time t1 on a first server.
// Read the entry to see how the conflict was resolved.
// there should not be a value (delete at time t1)
}
/**
* Tests the naming conflict resolution code.
* In this test, the local server act both as an LDAP server and
* a replicationServer that are inter-connected.
*
* The test creates an other session to the replicationServer using
* directly the ReplicationBroker API.
* It then uses this session to siomulate conflicts and therefore
* test the naming conflict resolution code.
*/
public void namingConflicts() throws Exception
{
"Starting replication test : namingConflicts"));
/*
* Open a session to the replicationServer using the ReplicationServer broker API.
* This must use a serverId different from the LDAP server ID
*/
/*
* Create a Change number generator to generate new changenumbers
* when we need to send operations messages to the replicationServer.
*/
/*
* Test that the conflict resolution code is able to find entries
* that have been renamed by an other master.
* To simulate this, create an entry with a given UUID and a given DN
* then send a modify operation using another DN but the same UUID.
* Finally check that the modify operation has been applied.
*/
// create the entry with a given DN
// Check that the entry has been created in the local DS.
"The send ADD replication message was not applied");
// send a modify operation with the correct unique ID but another DN
// check that the modify has been applied as if the entry had been renamed.
"telephonenumber", "01 02 45", 10000, true);
if (found == false)
fail("The modification has not been correctly replayed.");
// check that there was no administrative alert generated
// because the conflict has been automatically resolved.
"An alert was incorrectly generated when resolving conflicts");
/*
* Test that the conflict resolution code is able to detect
* that and entry have been renamed and that a new entry has
* been created with the same DN but another entry UUID
* To simulate this, create and entry with a given UUID and a given DN
* then send a modify operation using the same DN but another UUID.
* Finally check that the modify operation has not been applied to the
* entry with the given DN.
*/
// create the entry with a given DN and unique ID
// Check that the entry has been created in the local DS.
"The ADD replication message was not applied");
// send a modify operation with a wrong unique ID but the same DN
// check that the modify has not been applied
"telephonenumber", "02 01 03 05", 10000, false);
if (found == true)
fail("The modification has been replayed while it should not.");
// Check that there was no administrative alert generated
// because the conflict has been automatically resolved.
"An alert was incorrectly generated when resolving conflicts");
/*
* Test that the conflict resolution code is able to find entries
* that have been renamed by an other master.
* To simulate this, send a delete operation using another DN but
* the same UUID has the entry that has been used in the tests above.
* Finally check that the delete operation has been applied.
*/
// send a delete operation with a wrong dn but the unique ID of the entry
// used above
new DeleteMsg("cn=anotherdn,ou=People,dc=example,dc=com",
// check that the delete operation has been applied
"The DELETE replication message was not replayed");
// Check that there was no administrative alert generated
// because the conflict has been automatically resolved.
"An alert was incorrectly generated when resolving conflicts");
/*
* Test that two adds with the same DN but a different unique ID result
* cause a conflict and result in the second entry to be renamed.
*/
// create an entry with a given DN and unique ID
// Check that the entry has been created in the local DS.
"The ADD replication message was not applied");
// create an entry with the same DN and another unique ID
// Check that the entry has been renamed and created in the local DS.
10000, true);
"The ADD replication message was not applied");
// Check that there was an administrative alert generated
// because the conflict has not been automatically resolved.
"An alert was not generated when resolving conflicts");
// delete the entries to clean the database.
delMsg =
delMsg =
// check that the delete operation has been applied
"The DELETE replication message was not replayed");
/*
* Check that and added entry is correctly added below it's
* parent entry when this parent entry has been renamed.
*
* Simulate this by trying to add an entry below a DN that does not
* exist but with a parent ID that exist.
*/
"uid=new person,o=nothere,o=below,ou=People,dc=example,dc=com",
// Check that the entry has been renamed and created in the local DS.
"The ADD replication message was not applied");
// Check that there was no administrative alert generated
// because the conflict has been automatically resolved.
"An alert was incorrectly generated when resolving conflicts");
/*
* Check that when replaying delete the naming conflict code
* verify that the unique ID op the replayed operation is
* the same as the unique ID of the entry with the given DN
*
* To achieve this send a delete operation with a correct DN
* but a wrong unique ID.
*/
delMsg =
new DeleteMsg("uid=new person,ou=People,dc=example,dc=com",
// check that the delete operation has not been applied
"The DELETE replication message was replayed when it should not");
// Check that there was no administrative alert generated
// because the conflict has been automatically resolved.
"An alert was incorrectly generated when resolving conflicts");
/*
* Check that when replaying modify dn operations, the conflict
* resolution code is able to find the new DN of the parent entry
* if it has been renamed on another master.
*
* To simulate this try to rename an entry below an entry that does
* not exist but giving the unique ID of an existing entry.
*/
user1entryUUID, baseUUID, false,
"uid=wrong, ou=people,dc=example,dc=com",
"uid=newrdn");
// check that the operation has been correctly relayed
"The modify dn was not or badly replayed");
// Check that there was no administrative alert generated
// because the conflict has been automatically resolved.
"An alert was incorrectly generated when resolving conflicts");
/*
* same test but by giving a bad entry DN
*/
modDnMsg = new ModifyDNMsg(
// check that the operation has been correctly relayed
"The modify dn was not or badly replayed");
// Check that there was no administrative alert generated
// because the conflict has been automatically resolved.
"An alert was incorrectly generated when resolving conflicts");
/*
* Check that conflicting entries are renamed when a
* modifyDN is done with the same DN as an entry added on another server.
*/
// add a second entry
// check that the second entry has been added
// check that the add operation has been applied
// try to rename the first entry
user1entrysecondUUID, baseUUID, false,
// check that the second entry has been renamed
"ou=People,dc=example,dc=com"), 10000, true);
// Check that there was no administrative alert generated
// because the conflict has been automatically resolved.
"An alert was not generated when resolving conflicts");
// delete the entries to clean the database
delMsg =
new DeleteMsg("uid=reallynewrdn,ou=People,dc=example,dc=com",
// check that the delete operation has been applied
"The DELETE replication message was not replayed");
delMsg =
",ou=People,dc=example,dc=com",
",ou=People,dc=example,dc=com"), 10000, false);
// check that the delete operation has been applied
"The DELETE replication message was not replayed");
/*
* When replaying add operations it is possible that the parent entry has
* been renamed before and that another entry have taken the former dn of
* the parent entry. In such case the replication replay code should
* detect that the parent has been renamed and should add the entry below
* the new dn of the parent (thus changing the original dn with which the
* entry had been created)
*
* Steps
* - create parent entry 1 with baseDn1
* - create Add Msg for user1 with parent entry 1 UUID
* - MODDN parent entry 1 to baseDn2 in the LDAP server
* - add new parent entry 2 with baseDn1
* - publish msg
* - check that the Dn has been changed to baseDn2 in the msg received
*/
// - create parent entry 1 with baseDn1
+ "objectClass: organizationalUnit\n"
+ "entryUUID: 55555555-5555-5555-5555-555555555555\n";
{
addOp.setInternalOperation(true);
}
"Entry not added: ou=baseDn1,"+baseDn);
// - create Add Msg for user1 with parent entry 1 UUID
"uid=new person,ou=baseDn1,"+baseDn,
// - MODDN parent entry 1 to baseDn2 in the LDAP server
.nextMessageID(), null,
baseDn);
// - add new parent entry 2 with baseDn1
+ "objectClass: organizationalUnit\n"
+ "entryUUID: 66666666-6666-6666-6666-666666666666\n";
addOp.setInternalOperation(true);
// - publish msg
// - check that the Dn has been changed to baseDn2
"The ADD replication message was applied under ou=baseDn1,"+baseDn);
"The ADD replication message was NOT applied under ou=baseDn2,"+baseDn);
// Check that there was no administrative alert generated
// because the conflict has been automatically resolved.
"An alert was incorrectly generated when resolving conflicts");
//
// Check that when a delete is conflicting with Add of some entries
// below the deleted entries, the child entry that have been added
// before the deleted is replayed gets renamed correctly.
//
// add domain1 entry with 2 childs domain2 and domain3
// delete domain1
// check that the domain1 has correctly been deleted
"The DELETE replication message was not replayed");
// check that domain2 and domain3 have been renamed
"The conflicting entries were not created");
"The conflicting entries were not created");
// check that the 2 conflicting entries have been correctly marked
// check that unresolved conflict count has been incremented
// Check that an administrative alert was generated
// because the conflict has not been automatically resolved.
"An alert was incorrectly generated when resolving conflicts");
// delete the resulting entries for the next test
//
// Check that when an entry is added on one master below an entry
// that is currently deleted on another master, the replay of the
// add on the second master cause the added entry to be renamed
//
// check that conflict entry was created
"The conflicting entries were not created");
// check that the entry have been correctly marked as conflicting.
// check that unresolved conflict count has been incremented
// Check that when an entry is deleted on a first master and
// renamed on a second master and the rename is replayed last
// this is correctly detected as a resolved conflict.
// To simulate this simply try a modifyDN on a non existent uid.
modDnMsg = new ModifyDNMsg(
"33343333-3533-3633-3373-333333833333", baseUUID, false,
"uid=wrong, ou=people,dc=example,dc=com",
"uid=newrdn");
// unfortunately it is difficult to check that the operation
// did not do anything.
// The only thing we can check is that resolved naminf conflict counter
// has correctly been incremented.
// Check that there was no administrative alert generated
// because the conflict has been automatically resolved.
"An alert was incorrectly generated when resolving conflicts");
}
/**
* Check that the given entry does contain the attribute that mark the
* entry as conflicting.
*
* @param entry The entry that needs to be asserted.
*
* @return A boolean indicating if the entry is correctly marked.
*/
{
return false;
else
return true;
}
public Object[][] getAssuredFlag()
{
return new Object[][] { { false }, {true} };
}
/**
* Tests done using directly the ReplicationBroker interface.
*/
{
"Starting replication test : updateOperations " + assured));
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)
"The Add Entry operation failed");
{
// Check if the client has received the msg
"The received replication message is not an ADD msg");
"The received replication message is not an ADD msg");
"The received ADD replication message is not for the excepted DN");
}
// Modify the entry
modOp.setInternalOperation(true);
// See if the client has received the msg
"The received replication message is not a MODIFY msg");
"The received MODIFY replication message is not for the excepted DN");
// Modify the entry DN
.decode("ou=People,dc=example,dc=com"));
"The MOD_DN operation didn't create the new person entry");
"The MOD_DN operation didn't delete the old person entry");
// See if the client has received the msg
"The received replication message is not a MODIFY DN msg");
"The received MODIFY_DN message is not for the excepted DN");
// Delete the entry
.decode("uid= new person,ou=People,dc=example,dc=com"));
"Unable to delete the new person Entry");
// See if the client has received the msg
"The received replication message is not a MODIFY DN msg");
"The received DELETE message is not for the excepted DN");
/*
* Now check that when we send message to the ReplicationServer
* and that they are received and correctly replayed by the server.
*
* Start by testing the Add message reception
*/
if (assured)
addMsg.setAssured();
/*
* Check that the entry has been created in the local DS.
*/
/*
* Test the reception of Modify Msg
*/
if (assured)
modMsg.setAssured();
"telephonenumber", "01 02 45", 10000, true);
if (found == false)
fail("The modification has not been correctly replayed.");
// Test that replication is able to add attribute that do
// not exist in the schema.
if (assured)
modMsg.setAssured();
if (found == false)
fail("The modification has not been correctly replayed.");
/*
* Test the Reception of Modify Dn Msg
*/
true, null, "uid= new person");
if (assured)
"The modify DN replication message was not applied");
/*
* Test the Reception of Delete Msg
*/
if (assured)
delMsg.setAssured();
"The DELETE replication message was not replayed");
}
finally
{
}
}
/**
* Get the entryUUID for a given DN.
*
* @throws Exception if the entry does not exist or does not have
* an entryUUID.
*/
{
int count = 10;
if (count<1)
count=1;
{
for (int i=0; i < 3; i++)
{
{
break;
}
}
{
}
try
{
{
{
break;
}
}
}
finally
{
}
count --;
}
return found;
}
/**
* Test case for
* [Issue 635] NullPointerException when trying to access non existing entry.
*/
public void deleteNoSuchObject() throws Exception
{
"Starting replication test : deleteNoSuchObject"));
dn);
}
/**
* Test case for
* [Issue 798] break infinite loop when problems with naming resolution
* conflict.
*/
public void infiniteReplayLoop() throws Exception
{
"Starting replication test : infiniteReplayLoop"));
try
{
// Create a test entry.
+ "objectClass: top\n" + "objectClass: person\n"
+ "objectClass: organizationalPerson\n"
+ "objectClass: inetOrgPerson\n" + "uid: user.2\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";
// Get the UUID of the test entry.
// Register a short circuit that will fake a no-such-object result code
// on a delete. This will cause a replication replay loop.
"PreParse", 32);
try
{
// Publish a delete message for this test entry.
uuid);
// Wait for the operation to be replayed.
{
}
}
finally
{
"PreParse");
}
// If the replication replay loop was detected and broken then the
// counter will still be updated even though the replay was unsuccessful.
{
fail("Operation was not replayed");
}
}
finally
{
}
}
/**
* Enable or disable the receive status of a synchronization provider.
*
* @param syncConfigDN The DN of the synchronization provider configuration
* entry.
* @param enable Specifies whether the receive status should be enabled
* or disabled.
*/
{
if (enable)
{
}
else
{
}
new ASN1OctetString(syncConfigDN);
{
throw new RuntimeException("Cannot set receive status");
}
}
}