From 7938c7ece07363df886d2a3be2cd55870fffbfe4 Mon Sep 17 00:00:00 2001
From: fdorson <fdorson@localhost>
Date: Tue, 06 Jan 2009 14:05:02 +0000
Subject: [PATCH] With these changes the openDS servers are able to load data coming from a Sun Java System Directory Server Enterprise Edition. 

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/common/ServerStateTest.java |   92 +++++++++++++++
 opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java                   |  200 +++++++++++++++++++++++++++++++++
 opends/resource/schema/06-compat.ldif                                                               |   30 +++++
 opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java                   |   34 +++++
 4 files changed, 350 insertions(+), 6 deletions(-)

diff --git a/opends/resource/schema/06-compat.ldif b/opends/resource/schema/06-compat.ldif
new file mode 100644
index 0000000..c2b5430
--- /dev/null
+++ b/opends/resource/schema/06-compat.ldif
@@ -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' )
+
diff --git a/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java b/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
index 076d5d6..c6629eb 100644
--- a/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
+++ b/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();
   }
 
   /**
diff --git a/opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java b/opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java
index 22f526c..4862099 100644
--- a/opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java
+++ b/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);
   }
-}
\ No newline at end of file
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/common/ServerStateTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/common/ServerStateTest.java
index 6a4e9b6..5eb38fb 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/common/ServerStateTest.java
+++ b/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");
+
+  }
+
 }

--
Gitblit v1.10.0