opends/resource/schema/03-changelog.ldif
@@ -89,6 +89,12 @@ SINGLE-VALUE USAGE directoryOperation X-ORIGIN 'OpenDS Directory Server' ) attributeTypes: ( 1.3.6.1.4.1.26027.1.1.604 NAME 'changeInitiatorsName' DESC 'The initiator user of the change' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE USAGE directoryOperation X-ORIGIN 'OpenDS Directory Server' ) objectClasses: ( 2.16.840.1.113730.3.2.1 NAME 'changeLogEntry' SUP top STRUCTURAL MUST ( changeNumber $ targetDN $ changeType $ changeTime ) opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
@@ -4537,6 +4537,20 @@ newattrs.add(a); } ((DeleteMsg)msg).setEclIncludes(newattrs); // For delete only, add the modifiersName since they are required in the // ECL entry but are not part of other parts of the message AttributeType atype = DirectoryServer.getAttributeType("modifiersname"); List<Attribute> attrs = entry.getAttribute(atype); if (attrs != null) { for (Attribute a : attrs) { ((DeleteMsg)msg).setInitiatorsName(a.iterator().next().toString()); break; } } } else if (op instanceof PostOperationModifyOperation) { opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java
@@ -43,6 +43,8 @@ */ public class DeleteMsg extends LDAPUpdateMsg { String initiatorsName; /** * Creates a new delete message. * @@ -137,13 +139,28 @@ byte[] byteEntryAttrLen = String.valueOf(encodedEclIncludes.length).getBytes("UTF-8"); bodyLength += byteEntryAttrLen.length + 1; bodyLength += encodedEclIncludes.length + 1; byte[] byteInitiatorsName = null; if (initiatorsName != null) { byteInitiatorsName = initiatorsName.getBytes("UTF-8"); bodyLength += byteInitiatorsName.length + 1; } else { bodyLength++; } /* encode the header in a byte[] large enough to also contain the mods */ byte [] encodedMsg = encodeHeader(MSG_TYPE_DELETE, bodyLength, ProtocolVersion.REPLICATION_PROTOCOL_V4); int pos = encodedMsg.length - bodyLength; if (byteInitiatorsName != null) pos = addByteArray(byteInitiatorsName, encodedMsg, pos); else encodedMsg[pos++] = 0; pos = addByteArray(byteEntryAttrLen, encodedMsg, pos); pos = addByteArray(encodedEclIncludes, encodedMsg, pos); return encodedMsg; @@ -158,6 +175,17 @@ { // Read ecl attr len int length = getNextLength(in, pos); if (length != 0) { initiatorsName = new String(in, pos, length, "UTF-8"); pos += length + 1; } else { initiatorsName = null; pos += 1; } length = getNextLength(in, pos); int eclAttrLen = Integer.valueOf(new String(in, pos, length,"UTF-8")); pos += length + 1; @@ -216,4 +244,23 @@ return encodedEclIncludes.length + headerSize(); } /** * Set the initiator's name of this change. * * @param iname the initiator's name. */ public void setInitiatorsName(String iname) { initiatorsName = iname; } /** * Get the initiator's name of this change. * @return the initiator's name. */ public String getInitiatorsName() { return initiatorsName; } } opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java
@@ -62,6 +62,7 @@ import org.opends.server.core.AccessControlConfigManager; import org.opends.server.core.AddOperation; import org.opends.server.core.DirectoryServer; import org.opends.server.core.ModifyDNOperation; import org.opends.server.core.ModifyOperation; import org.opends.server.core.PersistentSearch; import org.opends.server.core.PluginConfigManager; @@ -733,7 +734,7 @@ null, // real time current entry eclAttributes, // entry attributes eclmsg.getDraftChangeNumber(), "add"); "add", null); } else if (msg instanceof ModifyMsg) @@ -761,7 +762,7 @@ null, // real time current entry eclAttributes, // entry attributes eclmsg.getDraftChangeNumber(), "modify"); "modify",null); } catch(Exception e) @@ -774,21 +775,28 @@ } else if (msg instanceof ModifyDNMsg) { try { InternalClientConnection conn = InternalClientConnection.getRootConnection(); ModifyDNMsg modDNMsg = (ModifyDNMsg)msg; ArrayList<RawAttribute> eclAttributes = modDNMsg.getEclIncludes(); ModifyDNOperation modifyDNOperation = (ModifyDNOperation)modDNMsg.createOperation(conn); String LDIFchanges = modToLDIF(modifyDNOperation.getModifications()); clEntry = createChangelogEntry( eclmsg.getServiceId(), eclmsg.getCookie().toString(), DN.decode(modDNMsg.getDn()), modDNMsg.getChangeNumber(), null, LDIFchanges, modDNMsg.getUniqueId(), null, // real time current entry eclAttributes, // entry attributes eclmsg.getDraftChangeNumber(), "modrdn"); "modrdn", null); Attribute a = Attributes.create("newrdn", modDNMsg.getNewRDN()); clEntry.addAttribute(a, null); @@ -803,6 +811,14 @@ Attribute c = Attributes.create("deleteoldrdn", String.valueOf(modDNMsg.deleteOldRdn())); clEntry.addAttribute(c, null); } catch(Exception e) { // Exceptions raised by createOperation for example throw new DirectoryException(ResultCode.OTHER, Message.raw(Category.SYNC, Severity.NOTICE, " Server fails to create entry: "),e); } } else if (msg instanceof DeleteMsg) @@ -821,7 +837,7 @@ null, eclAttributes, // entry attributes eclmsg.getDraftChangeNumber(), "delete"); "delete", delMsg.getInitiatorsName()); } return clEntry; } @@ -861,6 +877,7 @@ * @param histEntryAttributes TODO:ECL Adress hist entry attributes * @param draftChangenumber The provided draft change number (integer) * @param changetype The provided change type (add, ...) * @param delInitiatorsName The provided del initiatiors name * @return The created ECL entry. * @throws DirectoryException * When any error occurs. @@ -875,12 +892,14 @@ Entry entry, List<RawAttribute> histEntryAttributes, int draftChangenumber, String changetype) String changetype, String delInitiatorsName) throws DirectoryException { AttributeType attributeType; String dnString = ""; String pattern; if (draftChangenumber == 0) { // Draft uncompat mode @@ -1007,6 +1026,8 @@ if (clearLDIFchanges != null) { if (changetype.equals("add")) { if((attributeType = DirectoryServer.getAttributeType("changes")) == null) attributeType = @@ -1020,6 +1041,85 @@ operationalAttrs.put(attributeType, attrList); else uAttrs.put(attributeType, attrList); pattern = "creatorsName: "; int att_cr = clearLDIFchanges.indexOf(pattern); if (att_cr>0) { int start_val_cr = clearLDIFchanges.indexOf(':', att_cr); int end_val_cr = clearLDIFchanges.indexOf(EOL, att_cr); String creatorsName = clearLDIFchanges.substring(start_val_cr+2, end_val_cr); if((attributeType = DirectoryServer.getAttributeType("changeInitiatorsName")) == null) attributeType = DirectoryServer.getDefaultAttributeType("changeInitiatorsName"); a = Attributes.create(attributeType, creatorsName); attrList = new ArrayList<Attribute>(1); attrList.add(a); if(attributeType.isOperational()) operationalAttrs.put(attributeType, attrList); else uAttrs.put(attributeType, attrList); } } else if (changetype.equals("modify")||changetype.equals("modrdn")) { if (changetype.equals("modify")) { if((attributeType = DirectoryServer.getAttributeType("changes")) == null) attributeType = DirectoryServer.getDefaultAttributeType("changes"); a = Attributes.create(attributeType, clearLDIFchanges + "\n"); // force base64 attrList = new ArrayList<Attribute>(1); attrList.add(a); if(attributeType.isOperational()) operationalAttrs.put(attributeType, attrList); else uAttrs.put(attributeType, attrList); } pattern = "modifiersName: "; int att_cr = clearLDIFchanges.indexOf(pattern); if (att_cr>0) { int start_val_cr = att_cr + pattern.length(); int end_val_cr = clearLDIFchanges.indexOf(EOL, att_cr); String modifiersName = clearLDIFchanges.substring(start_val_cr, end_val_cr); if((attributeType = DirectoryServer.getAttributeType("changeInitiatorsName")) == null) attributeType = DirectoryServer.getDefaultAttributeType("changeInitiatorsName"); a = Attributes.create(attributeType, modifiersName); attrList = new ArrayList<Attribute>(1); attrList.add(a); if(attributeType.isOperational()) operationalAttrs.put(attributeType, attrList); else uAttrs.put(attributeType, attrList); } } } if (changetype.equals("delete") && (delInitiatorsName!=null)) { if((attributeType = DirectoryServer.getAttributeType("changeInitiatorsName")) == null) attributeType = DirectoryServer.getDefaultAttributeType("changeInitiatorsName"); a = Attributes.create(attributeType, delInitiatorsName); attrList = new ArrayList<Attribute>(1); attrList.add(a); if(attributeType.isOperational()) operationalAttrs.put(attributeType, attrList); else uAttrs.put(attributeType, attrList); } if (targetUUID != null) @@ -1072,7 +1172,8 @@ { try { String eclName = "target" + ra.getAttributeType().toLowerCase(); String attrName = ra.getAttributeType().toLowerCase(); String eclName = "target" + attrName; AttributeBuilder builder = new AttributeBuilder( DirectoryServer.getDefaultAttributeType(eclName)); AttributeType at = builder.getAttributeType(); @@ -1250,6 +1351,11 @@ */ public static String modToLDIF(List<Modification> mods) { if (mods==null) { // test case only return null; } StringBuilder modTypeLine = new StringBuilder(); Iterator<Modification> iterator = mods.iterator(); while (iterator.hasNext()) opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java
@@ -27,6 +27,7 @@ package org.opends.server.replication; import static org.opends.server.TestCaseUtils.TEST_ROOT_DN_STRING; import static org.opends.server.loggers.ErrorLogger.logError; import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; import static org.opends.server.loggers.debug.DebugLogger.getTracer; import static org.opends.server.replication.protocol.OperationContext.SYNCHROCONTEXT; @@ -54,6 +55,9 @@ import java.util.SortedSet; import java.util.TreeSet; import org.opends.messages.Category; import org.opends.messages.Message; import org.opends.messages.Severity; import org.opends.server.TestCaseUtils; import org.opends.server.api.Backend; import org.opends.server.api.ConnectionHandler; @@ -226,6 +230,7 @@ // Write additional changes and read ECL from a provided draft change number ts = ECLCompatWriteReadAllOps(5);replicationServer.clearDb(); // ECLIncludeAttributes();replicationServer.clearDb(); } @Test(enabled=true, groups="slow", dependsOnMethods = { "ECLReplicationServerTest"}) @@ -2428,10 +2433,10 @@ { if (debugEnabled()) { // logError(Message.raw(Category.SYNC, Severity.NOTICE, // "** TEST " + tn + " ** " + s)); TRACER.debugInfo("** TEST " + tn + " ** " + s); } // logError(Message.raw(Category.SYNC, Severity.NOTICE, // "** TEST " + tn + " ** " + s)); } /** @@ -3434,14 +3439,25 @@ { String tn = "ECLIncludeAttributes"; debugInfo(tn, "Starting test\n\n"); Backend backend2 = null; Backend backend3 = null; ReplicationBroker server01 = null; DeleteOperationBasis delOp =null; LDAPReplicationDomain domain2 = null; LDAPReplicationDomain domain3 = null; LDAPReplicationDomain domain21 = null; SynchronizationProvider replicationPlugin2 = null; SynchronizationProvider replicationPlugin3 = 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 Backend backend2 = initializeTestBackend(false, backend2 = initializeTestBackend(false, TEST_ROOT_DN_STRING2, TEST_BACKEND_ID2); DN baseDn2 = DN.decode(TEST_ROOT_DN_STRING2); baseDn2 = DN.decode(TEST_ROOT_DN_STRING2); SortedSet<String> replServers = new TreeSet<String>(); replServers.add("localhost:"+replicationServerPort); DomainFakeCfg domainConf = @@ -3449,20 +3465,20 @@ SortedSet<String> includeAttributes = new TreeSet<String>(); includeAttributes.add("sn"); domainConf.setEclIncludes(includeAttributes); LDAPReplicationDomain domain2 = MultimasterReplication.createNewDomain(domainConf); SynchronizationProvider replicationPlugin2 = new MultimasterReplication(); domain2 = MultimasterReplication.createNewDomain(domainConf); replicationPlugin2 = new MultimasterReplication(); replicationPlugin2.completeSynchronizationProvider(); Backend backend3 = initializeTestBackend(false, backend3 = initializeTestBackend(false, TEST_ROOT_DN_STRING3, TEST_BACKEND_ID3); DN baseDn3 = DN.decode(TEST_ROOT_DN_STRING3); baseDn3 = DN.decode(TEST_ROOT_DN_STRING3); domainConf = new DomainFakeCfg(baseDn3, 1703, replServers); includeAttributes = new TreeSet<String>(); includeAttributes.add("objectclass"); domainConf.setEclIncludes(includeAttributes); LDAPReplicationDomain domain3 = MultimasterReplication.createNewDomain(domainConf); SynchronizationProvider replicationPlugin3 = new MultimasterReplication(); domain3 = MultimasterReplication.createNewDomain(domainConf); replicationPlugin3 = new MultimasterReplication(); replicationPlugin3.completeSynchronizationProvider(); domainConf = @@ -3470,11 +3486,11 @@ includeAttributes = new TreeSet<String>(); includeAttributes.add("cn"); domainConf.setEclIncludes(includeAttributes); LDAPReplicationDomain domain21 = MultimasterReplication.createNewDomain(domainConf); domain21 = MultimasterReplication.createNewDomain(domainConf); Set<String> attrList = new HashSet<String>(); attrList.add(new String("cn")); ReplicationBroker server01 = openReplicationSession( server01 = openReplicationSession( DN.decode(TEST_ROOT_DN_STRING2), 1206, 100, replicationServerPort, 1000, true, -1 , domain21); @@ -3550,7 +3566,7 @@ waitOpResult(modDNOp, ResultCode.SUCCESS); // DeleteOperationBasis delOp = new DeleteOperationBasis(connection, delOp = new DeleteOperationBasis(connection, InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, DN.decode("cn=Robert Hue2," + TEST_ROOT_DN_STRING3)); @@ -3611,10 +3627,40 @@ checkValue(resultEntry,"targetsn","jensen"); checkValue(resultEntry,"targetcn","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("Ending "+tn+" test with exception:\n" + stackTraceToSingleLineString(e)); } finally { try { server01.stop(); // 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 (domain2 != null) @@ -3628,16 +3674,8 @@ if (replicationPlugin3 != null) DirectoryServer.deregisterSynchronizationProvider(replicationPlugin3); removeTestBackend2(backend3); } catch(Exception e) { fail("Ending "+tn+" test with exception:\n" + stackTraceToSingleLineString(e)); } finally { catch(Exception e) {} } debugInfo(tn, "Ending test with success"); } opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
@@ -350,11 +350,12 @@ { msg.setEclIncludes(entryAttrList); } msg.setInitiatorsName("johnny h"); DeleteMsg generatedMsg = (DeleteMsg) ReplicationMsg.generateMsg( msg.getBytes(), ProtocolVersion.getCurrentVersion()); assertEquals(msg.toString(), generatedMsg.toString()); assertEquals(msg.getInitiatorsName(), generatedMsg.getInitiatorsName()); assertEquals(msg.getChangeNumber(), generatedMsg.getChangeNumber());