| opends/resource/schema/06-compat.ldif | ●●●●● patch | view | raw | blame | history | |
| opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java | ●●●●● patch | view | raw | blame | history | |
| opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java | ●●●●● patch | view | raw | blame | history | |
| opends/tests/unit-tests-testng/src/server/org/opends/server/replication/common/ServerStateTest.java | ●●●●● patch | view | raw | blame | history |
opends/resource/schema/06-compat.ldif
New file @@ -0,0 +1,30 @@ # 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 2009 Sun Microsystems, Inc. # # # This file contains the attribute type and objectclass definitions for use # with the Directory Server configuration. dn: cn=schema objectClass: top objectClass: ldapSubentry objectClass: subschema objectClasses: ( 2.16.840.1.113730.3.2.30 NAME 'glue' SUP top X-ORIGIN 'Sun Directory Server' ) attributeTypes: ( 2.16.840.1.113730.3.1.587 NAME 'nsds50ruv' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Sun Directory Server' ) opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Copyright 2006-2008 Sun Microsystems, Inc. * Copyright 2006-2009 Sun Microsystems, Inc. */ package org.opends.server.replication.plugin; @@ -364,6 +364,18 @@ */ state = new PersistentServerState(baseDn, serverId, getServerState()); /* Check if a ReplicaUpdateVector entry is present * if so, and no state is already initialized * translate the ruv into a serverState and * a generationId */ Long compatGenId = state.checkRUVCompat(); if (compatGenId != null) { generationId = compatGenId; saveGenerationId(generationId); } startPublishService(replicationServers, window, heartbeatInterval); /* @@ -1878,16 +1890,32 @@ /** * Do what necessary when the data have changed : load state, load * generation Id. * If there is no such information check if there is a * ReplicaUpdateVector entry and translate it into a state * and generationId. * @exception DirectoryException Thrown when an error occurs. */ */ protected void loadDataState() throws DirectoryException { Long compatGenId = null; state.clearInMemory(); state.loadState(); // Check to see if a Ruv needs to be translated compatGenId = state.checkRUVCompat(); generator.adjust(state.getMaxChangeNumber(serverId)); // Retrieves the generation ID associated with the data imported generationId = loadGenerationId(); if (compatGenId != null) { generationId = compatGenId; saveGenerationId(generationId); } else generationId = loadGenerationId(); } /** opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Copyright 2006-2008 Sun Microsystems, Inc. * Copyright 2006-2009 Sun Microsystems, Inc. */ package org.opends.server.replication.plugin; import org.opends.messages.Message; @@ -36,6 +36,7 @@ import java.util.List; import java.util.Iterator; import org.opends.server.core.DeleteOperationBasis; import org.opends.server.core.DirectoryServer; import org.opends.server.core.ModifyOperationBasis; import org.opends.server.protocols.asn1.ASN1OctetString; @@ -81,6 +82,16 @@ */ protected static final String REPLICATION_STATE = "ds-sync-state"; /** * The attribute name used to store the entryUUID. */ private static final String ENTRY_UUID = "entryUUID"; /** * The attribute name used to store the RUV elements. */ private static final String REPLICATION_RUV_ELEMENT = "nsds50ruv"; /** * create a new ServerState. * @param baseDn The baseDN for which the ServerState is created @@ -471,6 +482,191 @@ } } /** * Check if a ReplicaUpdateVector entry is present * if so, translate the ruv into a serverState and * a generationId. * @return the generationId translated from the RUV * entry, 0 if no RUV is present */ public Long checkRUVCompat() { Long genId = null; SearchResultEntry ruvEntry = null; // Search the RUV in the DB ruvEntry = searchRUVEntry(); if (ruvEntry == null) return null; // Check if the serverState is already initialized if( !isServerStateInitilized()) { // Translate the ruv to serverState // and GenerationId genId = initializeStateWithRUVEntry(ruvEntry); } // In any case, remove the RUV entry // if it exists DeleteOperationBasis del = new DeleteOperationBasis(conn, InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, new ASN1OctetString(ruvEntry.getDN().toNormalizedString())); // Run the internal operation del.setInternalOperation(true); del.setSynchronizationOperation(true); del.setDontSynchronize(true); del.run(); return genId; } /** * Initialize the serverState and the GenerationId based on a RUV * entry. * @param ruvEntry the entry to translate into a serverState. * @return the generationId translated from the RUV entry. */ private Long initializeStateWithRUVEntry(SearchResultEntry ruvEntry) { Long genId = null; String value = null; String csn = null; AttributeType ruvElementType = DirectoryServer.getAttributeType(REPLICATION_RUV_ELEMENT); if (ruvElementType == null) return null; for (Attribute attr : ruvEntry.getAttribute(ruvElementType)) { Iterator<AttributeValue> it = attr.iterator(); while (it.hasNext()) { value = it.next().toString(); // Search for the GenerationId if (value.startsWith("{replicageneration} ")) { // Get only the timestamp present in the CSN String replicaGen = value.substring(20, 28); genId = Long.parseLong(replicaGen,16); } else { // Translate the other elements into serverState if (value.startsWith("{replica ")) { String[] bits = value.split(" "); if (bits.length > 3) { csn = bits[4]; String temp = csn.substring(0, 8); Long timeStamp = Long.parseLong(temp, 16); temp = csn.substring(8, 12); Integer seqNum = Integer.parseInt(temp, 16); temp = csn.substring(12, 16); Integer replicaId = Integer.parseInt(temp, 16); // No need to take into account the subSeqNum ChangeNumber cn = new ChangeNumber(timeStamp*1000, seqNum, replicaId.shortValue()); this.update(cn); } } } } } return genId; } /** * Check if the server State is initialized by searching * the attribute type REPLICATION_STATE in the root entry. * @return true if the serverState is initialized, false * otherwise */ private boolean isServerStateInitilized() { SearchResultEntry resultEntry = searchBaseEntry(); AttributeType synchronizationStateType = DirectoryServer.getAttributeType(REPLICATION_STATE); List<Attribute> attrs = resultEntry.getAttribute(synchronizationStateType); return (attrs != null); } /** * Search the database entry that represent a serverState * using the RUV format (compatibility mode). * @return the corresponding RUV entry, null otherwise */ private SearchResultEntry searchRUVEntry() { LDAPFilter filter; SearchResultEntry ruvEntry = null; // Search the RUV entry try { filter = LDAPFilter.decode("objectclass=ldapSubEntry"); } catch (LDAPException e) { // can not happen return null; } LinkedHashSet<String> attributes = new LinkedHashSet<String>(1); attributes.add(ENTRY_UUID); attributes.add(REPLICATION_RUV_ELEMENT); InternalSearchOperation search = conn.processSearch(asn1BaseDn, SearchScope.SUBORDINATE_SUBTREE, DereferencePolicy.DEREF_ALWAYS, 0, 0, false, filter,attributes); if (((search.getResultCode() != ResultCode.SUCCESS)) && ((search.getResultCode() != ResultCode.NO_SUCH_OBJECT))) return null; if (search.getResultCode() == ResultCode.SUCCESS) { /* * Search the ldapSubEntry with the entryUUID equals * to "ffffffff-ffff-ffff-ffff-ffffffffffff" */ LinkedList<SearchResultEntry> result = search.getSearchEntries(); if (!result.isEmpty()) { for (SearchResultEntry ldapSubEntry : result) { List<Attribute> attrs = ldapSubEntry.getAttribute(ENTRY_UUID.toLowerCase()); if (attrs != null) { Iterator<AttributeValue> iav = attrs.get(0).iterator(); AttributeValue attrVal = iav.next(); if (attrVal.getStringValue(). equalsIgnoreCase("ffffffff-ffff-ffff-ffff-ffffffffffff")) { ruvEntry = ldapSubEntry; break; } } } } } return ruvEntry; } /** * Get the largest ChangeNumber seen for a given LDAP server ID. * @@ -482,4 +678,4 @@ { return state.getMaxChangeNumber(serverID); } } } opends/tests/unit-tests-testng/src/server/org/opends/server/replication/common/ServerStateTest.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Copyright 2006-2008 Sun Microsystems, Inc. * Copyright 2006-2009 Sun Microsystems, Inc. */ package org.opends.server.replication.common; @@ -30,9 +30,17 @@ import java.util.Set; import org.opends.server.TestCaseUtils; import org.opends.server.core.AddOperationBasis; import org.opends.server.core.DirectoryServer; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.replication.ReplicationTestCase; import org.opends.server.replication.common.ChangeNumber; import org.opends.server.replication.common.ServerState; import org.opends.server.replication.plugin.LDAPReplicationDomain; import org.opends.server.types.DN; import org.opends.server.types.Entry; import org.opends.server.types.ResultCode; import org.opends.server.util.TimeThread; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -102,4 +110,86 @@ assertEquals(b, generatedServerState.getBytes()) ; } /** * Ensures that the Directory Server is able to * translate a ruv entry to a sever state. * * @throws Exception If an unexpected problem occurs. */ @Test public void translateRuvEntryTest() throws Exception { Entry synchroServerEntry = null; String RuvString = "dn: nsuniqueid=ffffffff-ffffffff-ffffffff-ffffffff, o=test\n" +"objectClass: top\n" +"objectClass: extensibleobject\n" +"objectClass: ldapSubEntry\n" +"o: test\n" +"nsds50ruv: {replicageneration} 49098853000000010000\n" +"nsds50ruv: {replica 3 ldap://kawax:3389} 491d517b000000030000 " +"491d564a000000030000\n" +"nsds50ruv: {replica 1 ldap://kawax:1389} 490989e8000000010000 " +"490989e8000000010000\n" +"ds6ruv: {PRIO 3 ldap://kawax:3389}\n" +"ds6ruv: {PRIO 1 ldap://kawax:1389}\n" +"entryUUID: ffffffff-ffff-ffff-ffff-ffffffffffff\n"; Entry RuvEntry = TestCaseUtils.entryFromLdifString(RuvString); AddOperationBasis addOp = new AddOperationBasis(InternalClientConnection. getRootConnection(), InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, RuvEntry.getDN(), RuvEntry.getObjectClasses(), RuvEntry.getUserAttributes(), RuvEntry.getOperationalAttributes()); addOp.setInternalOperation(true); addOp.run(); assertTrue(addOp.getResultCode() == ResultCode.SUCCESS); // Instantiate a Replication domain // suffix synchronized String synchroServerLdif = "dn: cn=o=test, cn=domains, cn=Multimaster Synchronization, cn=Synchronization Providers,cn=config\n" + "objectClass: top\n" + "objectClass: ds-cfg-replication-domain\n" + "cn: o=test\n" + "ds-cfg-base-dn: o=test\n" + "ds-cfg-replication-server: localhost:3389\n" + "ds-cfg-server-id: 1\n" + "ds-cfg-receive-status: true\n" + "ds-cfg-window-size: 10"; // When adding the replicationDomain entry the checkRUVCompat // method is called and should translate the RuvEntry Added // into a serverState + generationId synchroServerEntry = TestCaseUtils.entryFromLdifString(synchroServerLdif); DirectoryServer.getConfigHandler().addEntry(synchroServerEntry, null); assertNotNull(DirectoryServer.getConfigEntry(synchroServerEntry.getDN()), "Unable to add the synchronized server"); LDAPReplicationDomain replDomain = LDAPReplicationDomain. retrievesReplicationDomain(DN.decode("o=test")); // Then check serverSate and GenId assertTrue(replDomain.getGenerationID() == 1225361491); ServerState state = replDomain.getServerState(); assertTrue(state.getMaxChangeNumber((short) 1). compareTo(new ChangeNumber("0000011d4d42b240000100000000")) == 0); assertTrue(state.getMaxChangeNumber((short) 3). compareTo(new ChangeNumber("0000011d9a991110000300000000")) == 0); // Remove the configuration entry DirectoryServer.getConfigHandler().deleteEntry(synchroServerEntry.getDN(), null); assertNull(DirectoryServer.getConfigEntry(synchroServerEntry.getDN()), "Unable to remove the synchronized server"); } }