From 3211d8caa762e2242a6c2669e1b91bd97025f33b Mon Sep 17 00:00:00 2001
From: gbellato <gbellato@localhost>
Date: Wed, 22 Aug 2007 06:57:34 +0000
Subject: [PATCH] Fix for 1602 : removing root entries of domain cause replay of many changes

---
 opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java |  177 +++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 142 insertions(+), 35 deletions(-)

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 127d625..34f508e 100644
--- a/opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java
+++ b/opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java
@@ -51,10 +51,12 @@
 import org.opends.server.types.Control;
 import org.opends.server.types.DN;
 import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.DirectoryException;
 import org.opends.server.types.LDAPException;
 import org.opends.server.types.ModificationType;
 import org.opends.server.types.RawModification;
 import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
 import org.opends.server.types.SearchResultEntry;
 import org.opends.server.types.SearchScope;
 
@@ -118,18 +120,54 @@
    */
   public void loadState()
   {
+    SearchResultEntry stateEntry = null;
+
+    // try to load the state from the base entry.
+    stateEntry = searchBaseEntry();
+
+    if (stateEntry == null)
+    {
+      // The base entry does not exist yet
+      // in the database or was deleted. Try to read the ServerState
+      // from the configuration instead.
+      stateEntry = searchConfigEntry();
+    }
+
+    if (stateEntry != null)
+    {
+      updateStateFromEntry(stateEntry);
+    }
+
     /*
-     * Read the serverState from the database,
-     * If not there create empty entry
+     * TODO : The ServerState is saved to the database periodically,
+     * therefore in case of crash it is possible that is does not contain
+     * the latest changes that have been processed and saved to the
+     * database.
+     * In order to make sure that we don't loose them, search all the entries
+     * that have been updated after this entry.
+     * This is done by using the HistoricalCsnOrderingMatchingRule
+     * and an ordering index for historical attribute
      */
+
+  }
+
+  /**
+   * Run a search operation to find the base entry
+   * of the replication domain for which this ServerState was created.
+   *
+   * @return Thebasen Entry or null if no entry was found;
+   */
+  private SearchResultEntry searchBaseEntry()
+  {
     LDAPFilter filter;
+
     try
     {
       filter = LDAPFilter.decode("objectclass=*");
     } catch (LDAPException e)
     {
       // can not happen
-      return;
+      return null;
     }
 
     /*
@@ -149,45 +187,90 @@
           get(search.getResultCode().getResultCodeName(), search.toString(),
               search.getErrorMessage(), baseDn.toString());
       logError(message);
+      return null;
     }
 
-    SearchResultEntry resultEntry = null;
+    SearchResultEntry stateEntry = null;
     if (search.getResultCode() == ResultCode.SUCCESS)
     {
       /*
        * Read the serverState from the REPLICATION_STATE attribute
        */
       LinkedList<SearchResultEntry> result = search.getSearchEntries();
-      resultEntry = result.getFirst();
-      if (resultEntry != null)
+      if (!result.isEmpty())
       {
-        AttributeType synchronizationStateType =
-          DirectoryServer.getAttributeType(REPLICATION_STATE);
-        List<Attribute> attrs =
-          resultEntry.getAttribute(synchronizationStateType);
-        if (attrs != null)
+        stateEntry = result.getFirst();
+      }
+    }
+    return stateEntry;
+  }
+
+  /**
+   * Run a search operation to find the entry with the configuration
+   * of the replication domain for which this ServerState was created.
+   *
+   * @return The configuration Entry or null if no entry was found;
+   */
+  private SearchResultEntry searchConfigEntry()
+  {
+    try
+    {
+      SearchFilter filter =
+        SearchFilter.createFilterFromString(
+            "(&(objectclass=ds-cfg-replication-domain-config)"
+            +"(ds-cfg-replication-dn="+baseDn+"))");
+
+      LinkedHashSet<String> attributes = new LinkedHashSet<String>(1);
+      attributes.add(REPLICATION_STATE);
+      InternalSearchOperation op =
+          conn.processSearch(DN.decode("cn=config"),
+          SearchScope.SUBORDINATE_SUBTREE,
+          DereferencePolicy.NEVER_DEREF_ALIASES,
+          1, 0, false, filter, attributes);
+
+      if (op.getResultCode() == ResultCode.SUCCESS)
+      {
+        /*
+         * Read the serverState from the REPLICATION_STATE attribute
+         */
+        LinkedList<SearchResultEntry> resultEntries =
+          op.getSearchEntries();
+        if (!resultEntries.isEmpty())
         {
-          Attribute attr = attrs.get(0);
-          LinkedHashSet<AttributeValue> values = attr.getValues();
-          for (AttributeValue value : values)
-          {
-            ChangeNumber changeNumber =
-              new ChangeNumber(value.getStringValue());
-            update(changeNumber);
-          }
+          SearchResultEntry resultEntry = resultEntries.getFirst();
+          return resultEntry;
         }
       }
+      return null;
+    } catch (DirectoryException e)
+    {
+      // can not happen
+      return null;
+    }
+  }
 
-      /*
-       * TODO : The ServerState is saved to the database periodically,
-       * therefore in case of crash it is possible that is does not contain
-       * the latest changes that have been processed and saved to the
-       * database.
-       * In order to make sure that we don't loose them, search all the entries
-       * that have been updated after this entry.
-       * This is done by using the HistoricalCsnOrderingMatchingRule
-       * and an ordering index for historical attribute
-       */
+  /**
+   * Update this ServerState from the provided entry.
+   *
+   * @param resultEntry The entry that should be used to update this
+   *                    ServerState.
+   */
+  private void updateStateFromEntry(SearchResultEntry resultEntry)
+  {
+    AttributeType synchronizationStateType =
+      DirectoryServer.getAttributeType(REPLICATION_STATE);
+    List<Attribute> attrs =
+      resultEntry.getAttribute(synchronizationStateType);
+    if (attrs != null)
+    {
+      Attribute attr = attrs.get(0);
+      LinkedHashSet<AttributeValue> values = attr.getValues();
+      for (AttributeValue value : values)
+      {
+        ChangeNumber changeNumber =
+          new ChangeNumber(value.getStringValue());
+        update(changeNumber);
+      }
     }
   }
 
@@ -202,6 +285,32 @@
     /*
      * Generate a modify operation on the Server State baseD Entry.
      */
+    ResultCode result = runUpdateStateEntry(baseDn);
+
+    if (result == ResultCode.NO_SUCH_OBJECT)
+    {
+      // The base entry does not exist yet in the database or
+      // has been deleted, save the state to the config entry instead.
+      SearchResultEntry configEntry = searchConfigEntry();
+      if (configEntry != null)
+      {
+        DN configDN = configEntry.getDN();
+        result = runUpdateStateEntry(configDN);
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Run a modify operation to update the entry whose DN is given as
+   * a parameter with the serverState information.
+   *
+   * @param serverStateEntryDN The DN of the entry to be updated.
+   *
+   * @return A ResultCode indicating if the operation was successful.
+   */
+  private ResultCode runUpdateStateEntry(DN serverStateEntryDN)
+  {
     ArrayList<ASN1OctetString> values = this.toASN1ArrayList();
 
     if (values.size() == 0)
@@ -216,16 +325,14 @@
     ModifyOperationBasis op =
       new ModifyOperationBasis(conn, InternalClientConnection.nextOperationID(),
           InternalClientConnection.nextMessageID(),
-          new ArrayList<Control>(0), asn1BaseDn,
+          new ArrayList<Control>(0),
+          new ASN1OctetString(serverStateEntryDN.toString()),
           mods);
     op.setInternalOperation(true);
     op.setSynchronizationOperation(true);
     op.setDontSynchronize(true);
     op.run();
-
-    ResultCode result = op.getResultCode();
-    if ((result != ResultCode.SUCCESS) &&
-        ((result != ResultCode.NO_SUCH_OBJECT)))
+    if (op.getResultCode() != ResultCode.SUCCESS)
     {
       Message message = DEBUG_ERROR_UPDATING_RUV.get(
               op.getResultCode().getResultCodeName().toString(),
@@ -234,6 +341,6 @@
               baseDn.toString());
       logError(message);
     }
-    return result;
+    return op.getResultCode();
   }
 }

--
Gitblit v1.10.0