From 55a07ce2479e1b4c74dec15ce4e78e3fdf50a27c Mon Sep 17 00:00:00 2001
From: mrossign <mrossign@localhost>
Date: Wed, 16 Sep 2009 08:34:51 +0000
Subject: [PATCH] Fix for #4098 Initializing a fractional domain through import-ldif when server is down does not work

---
 opends/src/server/org/opends/server/replication/plugin/FractionalLDIFImportPlugin.java |  302 +++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 263 insertions(+), 39 deletions(-)

diff --git a/opends/src/server/org/opends/server/replication/plugin/FractionalLDIFImportPlugin.java b/opends/src/server/org/opends/server/replication/plugin/FractionalLDIFImportPlugin.java
index 9afb119..1f43a02 100644
--- a/opends/src/server/org/opends/server/replication/plugin/FractionalLDIFImportPlugin.java
+++ b/opends/src/server/org/opends/server/replication/plugin/FractionalLDIFImportPlugin.java
@@ -27,6 +27,7 @@
 package org.opends.server.replication.plugin;
 
 import java.util.ArrayList;
+import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -34,24 +35,31 @@
 
 import org.opends.messages.Message;
 import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.server.ServerManagementContext;
 import org.opends.server.admin.std.server.FractionalLDIFImportPluginCfg;
 import org.opends.server.admin.std.server.PluginCfg;
+import org.opends.server.admin.std.server.ReplicationDomainCfg;
+import org.opends.server.admin.std.server.ReplicationSynchronizationProviderCfg;
+import org.opends.server.admin.std.server.RootCfg;
 import org.opends.server.api.plugin.*;
 import org.opends.server.config.ConfigException;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.replication.plugin.LDAPReplicationDomain.
   AttributeValueStringIterator;
+import org.opends.server.replication.plugin.LDAPReplicationDomain.
+  FractionalConfig;
 import org.opends.server.types.*;
 
 import static org.opends.messages.ReplicationMessages.*;
 
 /**
  * This class implements a Directory Server plugin that is used in fractional
- * replication when an online full update occurs.
+ * replication to initialize a just configured fractional domain (when an online
+ * full update occurs or offline/online ldif import).
  * The following tasks are done:
- * - check that the fractional configuration (if any) stored in the root entry
- * of the domain is compliant with the fractional configuration of the domain
- *  (if not make online update stop)
+ * - check that the fractional configuration (if any) stored in the (incoming)
+ * root entry of the domain is compliant with the fractional configuration of
+ * the domain (if not make online update stop)
  * - perform filtering according to fractional configuration of the domain
  * - flush the fractional configuration of the domain in the root entry
  *  (if no one already present)
@@ -60,6 +68,57 @@
   extends DirectoryServerPlugin<FractionalLDIFImportPluginCfg>
   implements ConfigurationChangeListener<FractionalLDIFImportPluginCfg>
 {
+  // Holds the fractional configuration and if available the replication domain
+  // matching this import session (they form the importfractional context).
+  // Domain is available if the server is online (import-ldif, online full
+  // update..) otherwise, this is an import-ldif with server off. The key is the
+  // ImportConfig object of the session which acts as a cookie for the whole
+  // session. This allows to potentially run man imports at the same time.
+  private final Hashtable<LDIFImportConfig, ImportFractionalContext>
+    importSessionContexts = new Hashtable<LDIFImportConfig,
+    ImportFractionalContext>();
+
+  /**
+   * Holds an import session fractional context.
+   */
+  private static class ImportFractionalContext
+  {
+    // Fractional configuration of the local domain (may be null if import on a
+    // not replicated domain)
+    private FractionalConfig fractionalConfig = null;
+    // The local domain object (may stay null if server is offline)
+    private LDAPReplicationDomain domain = null;
+
+    /**
+     * Constructor.
+     * @param fractionalConfig
+     * @param domain
+     */
+    public ImportFractionalContext(FractionalConfig fractionalConfig,
+      LDAPReplicationDomain domain)
+    {
+      this.fractionalConfig = fractionalConfig;
+      this.domain = domain;
+    }
+
+    /**
+     * Getter for the fractional configuration.
+     * @return the fractionalConfig
+     */
+    public FractionalConfig getFractionalConfig()
+    {
+      return fractionalConfig;
+    }
+
+    /**
+     * Getter for the domain..
+     * @return the domain
+     */
+    public LDAPReplicationDomain getDomain()
+    {
+      return domain;
+    }
+  }
 
   /**
    * Creates a new instance of this Directory Server plugin.  Every plugin must
@@ -86,6 +145,7 @@
       switch (t)
       {
         case LDIF_IMPORT:
+        case LDIF_IMPORT_END:
           // This is acceptable.
           break;
 
@@ -104,9 +164,81 @@
   @Override()
   public final void finalizePlugin()
   {
+    // Nothing to do
   }
 
   /**
+   * Attempts to retrieve the fractional configuration of the domain being
+   * imported.
+   * @param entry An imported entry of the imported domain
+   * @return The parsed fractional configuration for the domain matching the
+   * passed entry. Null if no configuration is found for the domain
+   * (not a replicated domain).
+   */
+  private static FractionalConfig getStaticReplicationDomainFractionalConfig(
+    Entry entry) throws Exception {
+
+    // Retrieve the configuration
+    ServerManagementContext context = ServerManagementContext.getInstance();
+    RootCfg root = context.getRootConfiguration();
+
+
+    ReplicationSynchronizationProviderCfg sync =
+      (ReplicationSynchronizationProviderCfg)
+      root.getSynchronizationProvider("Multimaster Synchronization");
+
+    String[] domainNames = sync.listReplicationDomains();
+    if (domainNames == null)
+    {
+      // No domain in replication
+      return null;
+    }
+
+    // Find the configuration for domain the entry is part of
+    ReplicationDomainCfg matchingReplicatedDomainCfg = null;
+    for (String domainName : domainNames)
+    {
+      // Get the domain configuration object
+      ReplicationDomainCfg replicationDomainCfg =
+      sync.getReplicationDomain(domainName);
+      // Is the entry a sub entry of the replicated domain main entry ?
+      DN replicatedDn = replicationDomainCfg.getBaseDN();
+      DN entryDn = entry.getDN();
+      if (entryDn.isDescendantOf(replicatedDn))
+      {
+        // Found the matching replicated domain configuration object
+        matchingReplicatedDomainCfg = replicationDomainCfg;
+        break;
+      }
+    }
+
+    if (matchingReplicatedDomainCfg == null)
+    {
+      // No matching replicated domain found
+      return null;
+    }
+
+    // Extract the fractional configuration from the domain configuration object
+    // and return it.
+    return FractionalConfig.toFractionalConfig(matchingReplicatedDomainCfg);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public final void doLDIFImportEnd(
+    LDIFImportConfig importConfig)
+  {
+    // Remove the cookie of this import session
+    synchronized(importSessionContexts)
+    {
+      importSessionContexts.remove(importConfig);
+    }
+  }
+
+  /**
+   * See class comment for what we achieve here...
    * {@inheritDoc}
    */
   @Override()
@@ -114,24 +246,93 @@
     LDIFImportConfig importConfig, Entry entry)
   {
     /**
-     * See class comment for what we achieve here...
+     * try to get the import fractional context for this entry. If not found,
+     * create and initialize it. The mechanism here is done to take a lock only
+     * once for the whole import session (except the necessary lock of the
+     * doLDIFImportEnd method)
      */
+    ImportFractionalContext importFractionalContext =
+      importSessionContexts.get(importConfig);
 
-    // Retrieve the replicated domain this entry belongs to
     DN entryDn = entry.getDN();
-    LDAPReplicationDomain domain = MultimasterReplication.findDomain(entryDn,
-      null);
-    if (domain == null)
+    FractionalConfig localFractionalConfig = null;
+
+    // If no context, create it
+    if (importFractionalContext == null)
+    {
+      synchronized(importSessionContexts)
+      {
+        // Insure antoher thread was not creating the context at the same time
+        // (we would create it for the second time which is useless)
+        importFractionalContext = importSessionContexts.get(importConfig);
+        if (importFractionalContext == null)
+        {
+          /*
+           * Create context
+           */
+
+          /**
+           * Retrieve the replicated domain this entry belongs to. Try to
+           * retrieve replication domain instance first. If we are in an online
+           * server, we should get it (if we are treating an entry that belongs
+           * to a replicated domain), otherwise the domain is not replicated or
+           * we are in an offline server context (import-ldif command run with
+           * offline server) and we must retrieve the fractional configuration
+           * directly from the configuration management system.
+           */
+          LDAPReplicationDomain domain =
+            MultimasterReplication.findDomain(entryDn, null);
+
+          // Get the fractional configuration extracted from the local server
+          // configuration for the currently imported domain
+          if (domain == null)
+          {
+            // Server may be offline, attempt to find fractional configuration
+            // from config sub-system
+            try
+            {
+              localFractionalConfig =
+                getStaticReplicationDomainFractionalConfig(entry);
+            } catch (Exception ex)
+            {
+              Message message = ERR_FRACTIONAL_COULD_NOT_RETRIEVE_CONFIG.get(
+                entry.toString());
+              return PluginResult.ImportLDIF.stopEntryProcessing(message);
+            }
+          } else
+          {
+            // Found a live domain, retrieve the fractional configuration from
+            // it.
+            localFractionalConfig = domain.getFractionalConfig();
+          }
+          // Create context and store it
+          importFractionalContext =
+            new ImportFractionalContext(localFractionalConfig, domain);
+          importSessionContexts.put(importConfig, importFractionalContext);
+        }
+      }
+    }
+
+    // Extract the fractional configuration from the context
+    localFractionalConfig = importFractionalContext.getFractionalConfig();
+    if (localFractionalConfig == null)
     {
       // Not part of a replicated domain : nothing to do
       return PluginResult.ImportLDIF.continueEntryProcessing();
     }
 
-    // Is the entry to treat the root entry of the domain ? If yes, analyze the
-    // fractional configuration in it and compare with local domain fractional
-    // configuration. Stop the import if some inconsistency is detcted
-    DN domainBaseDn = domain.getBaseDN();
-    if (domainBaseDn.equals(entryDn))
+    /**
+     * At this point, either the domain instance has been found and we  use its
+     * fractional configuration, or the server is offline and we use the parsed
+     * fractional configuration. We differentiate both cases testing if domain
+     * is null. We are also for sure handling an entry of a replicated suffix.
+     */
+
+    // Is the entry to handle the root entry of the domain ? If yes, analyze the
+    // fractional configuration in it and compare with local fractional
+    // configuration. Stop the import if some inconsistency is detected
+    DN replicatedDomainBaseDn = localFractionalConfig.getBaseDn();
+    if (replicatedDomainBaseDn.equals(entryDn))
     {
       /*
        * This is the root entry, try to read a fractional configuration from it
@@ -169,8 +370,9 @@
       }
 
       // Compare backend and local fractional configuration
-      boolean sameConfig = domain.isFractionalConfigConsistent(exclIt, inclIt);
-      if (domain.isFractional())
+      boolean sameConfig = LDAPReplicationDomain.
+        isFractionalConfigConsistent(localFractionalConfig, exclIt, inclIt);
+      if (localFractionalConfig.isFractional())
       {
         // Local domain is fractional
         if (sameConfig)
@@ -190,19 +392,28 @@
           }
           if (remoteDomainHasSomeConfig)
           {
-            // Local domain is fractional, remote domain has some config which
-            // is different : stop import (error will be logged when import is
-            // stopped)
-            domain.setImportErrorMessageId(
-              LDAPReplicationDomain.IMPORT_ERROR_MESSAGE_BAD_REMOTE);
-            domain.setFollowImport(false);
-            return PluginResult.ImportLDIF.continueEntryProcessing();
+            LDAPReplicationDomain domain = importFractionalContext.getDomain();
+            if (domain != null)
+            {
+              // Local domain is fractional, remote domain has some config which
+              // is different : stop import (error will be logged when import is
+              // stopped)
+              domain.setImportErrorMessageId(
+                LDAPReplicationDomain.IMPORT_ERROR_MESSAGE_BAD_REMOTE);
+              domain.setFollowImport(false);
+              return PluginResult.ImportLDIF.stopEntryProcessing(null);
+            } else
+            {
+              Message message = NOTE_ERR_LDIF_IMPORT_FRACTIONAL_BAD_DATA_SET.
+                get(replicatedDomainBaseDn.toString());
+              return PluginResult.ImportLDIF.stopEntryProcessing(message);
+            }
           } else
           {
             // Local domain is fractional but remote domain has no config :
             // flush local config into root entry and follow import with
             // filtering
-            flushFractionalConfigIntoEntry(domain, entry);
+            flushFractionalConfigIntoEntry(localFractionalConfig, entry);
           }
         }
       } else
@@ -215,20 +426,32 @@
           return PluginResult.ImportLDIF.continueEntryProcessing();
         } else
         {
-          // Local domain is not fractional but remote one is : stop import :
-          // local domain should be configured with the same config as remote
-          // one
-          domain.setImportErrorMessageId(
-              LDAPReplicationDomain.IMPORT_ERROR_MESSAGE_REMOTE_IS_FRACTIONAL);
-          domain.setFollowImport(false);
-          return PluginResult.ImportLDIF.continueEntryProcessing();
+          LDAPReplicationDomain domain = importFractionalContext.getDomain();
+          if (domain != null)
+          {
+            // Local domain is not fractional but remote one is : stop import :
+            // local domain should be configured with the same config as remote
+            // one
+            domain.setImportErrorMessageId(
+                LDAPReplicationDomain.
+                IMPORT_ERROR_MESSAGE_REMOTE_IS_FRACTIONAL);
+            domain.setFollowImport(false);
+            return PluginResult.ImportLDIF.stopEntryProcessing(null);
+          } else
+          {
+            Message message =
+              NOTE_ERR_LDIF_IMPORT_FRACTIONAL_DATA_SET_IS_FRACTIONAL.get(
+                replicatedDomainBaseDn.toString());
+            return PluginResult.ImportLDIF.stopEntryProcessing(message);
+          }
         }
       }
     }
 
     // If we get here, local domain fractional configuration is enabled.
     // Now filter for potential attributes to be removed.
-    domain.fractionalRemoveAttributesFromEntry(entry.getDN().getRDN(),
+    LDAPReplicationDomain.fractionalRemoveAttributesFromEntry(
+      localFractionalConfig, entry.getDN().getRDN(),
       entry.getObjectClasses(), entry.getUserAttributes(), true);
 
     return PluginResult.ImportLDIF.continueEntryProcessing();
@@ -240,20 +463,21 @@
    * WARNING: assumption is that no fractional attributes at all is already
    * present in the passed entry. Also assumption is that domain fractional
    * configuration is on.
-   * @param domain Domain containing the fractional configuration to use
+   * @param localFractionalConfig The local domain fractional configuration
    * @param entry The entry to modify
    */
-  private static void flushFractionalConfigIntoEntry(
-    LDAPReplicationDomain domain, Entry entry)
+  private static void flushFractionalConfigIntoEntry(FractionalConfig
+    localFractionalConfig, Entry entry)
   {
-    if (domain.isFractional()) // Paranoia check
+    if (localFractionalConfig.isFractional()) // Paranoia check
     {
       // Get the fractional configuration of the domain
-      boolean fractionalExclusive = domain.isFractionalExclusive();
+      boolean fractionalExclusive =
+        localFractionalConfig.isFractionalExclusive();
       Map<String, List<String>> fractionalSpecificClassesAttributes =
-        domain.getFractionalSpecificClassesAttributes();
+        localFractionalConfig.getFractionalSpecificClassesAttributes();
       List<String> fractionalAllClassesAttributes =
-        domain.getFractionalAllClassesAttributes();
+        localFractionalConfig.getFractionalAllClassesAttributes();
 
       // Create attribute builder for the rigth fractional mode
       String fractAttribute = null;

--
Gitblit v1.10.0