From de025522059a4caaf1f5793c635e33648b68a84c Mon Sep 17 00:00:00 2001
From: Jean-Noël Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Thu, 28 Jan 2016 08:29:22 +0000
Subject: [PATCH] OPENDJ-2638 (PR-201) Collation matching rule should be configurable via config.ldif

---
 opendj-server-legacy/src/main/java/org/opends/server/schema/CollationMatchingRuleFactory.java |  208 +++++++++++++++++++++++++++++++++------------------
 1 files changed, 134 insertions(+), 74 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/schema/CollationMatchingRuleFactory.java b/opendj-server-legacy/src/main/java/org/opends/server/schema/CollationMatchingRuleFactory.java
index 1a6d452..faa7dfd 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/schema/CollationMatchingRuleFactory.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/schema/CollationMatchingRuleFactory.java
@@ -32,6 +32,8 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -41,15 +43,18 @@
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.config.server.ConfigChangeResult;
 import org.forgerock.opendj.config.server.ConfigException;
+import org.forgerock.opendj.ldap.schema.ConflictingSchemaElementException;
 import org.forgerock.opendj.ldap.schema.CoreSchema;
 import org.forgerock.opendj.ldap.schema.MatchingRule;
 import org.forgerock.opendj.ldap.schema.Schema;
+import org.forgerock.opendj.ldap.schema.SchemaBuilder;
 import org.opends.server.admin.server.ConfigurationChangeListener;
 import org.opends.server.admin.std.server.CollationMatchingRuleCfg;
 import org.opends.server.api.MatchingRuleFactory;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.InitializationException;
+import org.opends.server.types.Schema.SchemaUpdater;
 import org.opends.server.util.CollectionUtils;
 
 /**
@@ -82,24 +87,57 @@
     return Collections.unmodifiableCollection(matchingRules.values());
   }
 
-  /**
-   * Adds a new mapping of OID and MatchingRule.
-   *
-   * @param oid
-   *          OID of the matching rule
-   * @param matchingRule
-   *          instance of a MatchingRule.
-   */
-  private void addMatchingRule(String oid, MatchingRule matchingRule)
-  {
-    matchingRules.put(oid, matchingRule);
-  }
-
   @Override
   public void initializeMatchingRule(CollationMatchingRuleCfg configuration)
       throws ConfigException, InitializationException
   {
-    final Schema coreSchema = CoreSchema.getInstance();
+    // The core schema contains all supported collation matching rules so read it for initialization.
+    // The server's schemaNG may have different things configured slightly differently
+    org.opends.server.types.Schema schema = DirectoryServer.getSchema();
+    Schema coreSchema = CoreSchema.getInstance();
+
+    // on startup, the SDK already has existing matching rules
+    // remove them all before letting the server set them all up
+    // according to what this factory decides must be setup
+    final Set<MatchingRule> defaultMatchingRules = getCollationMatchingRules(coreSchema.getMatchingRules());
+    unregisterMatchingRules(schema, defaultMatchingRules);
+    matchingRules.putAll(collectConfiguredMatchingRules(configuration, coreSchema));
+
+    // Save this configuration.
+    currentConfig = configuration;
+
+    // Register for change events.
+    currentConfig.addCollationChangeListener(this);
+  }
+
+  private void unregisterMatchingRules(org.opends.server.types.Schema schema,
+      final Collection<MatchingRule> matchingRules) throws ConfigException
+  {
+    try
+    {
+      schema.updateSchema(new SchemaUpdater()
+      {
+        @Override
+        public Schema update(SchemaBuilder builder)
+        {
+          for (final MatchingRule rule : matchingRules)
+          {
+            builder.removeMatchingRule(rule.getNameOrOID());
+          }
+          return builder.toSchema();
+        }
+      });
+    }
+    catch (DirectoryException e)
+    {
+      throw new ConfigException(e.getMessageObject(), e);
+    }
+  }
+
+  private Map<String, MatchingRule> collectConfiguredMatchingRules(CollationMatchingRuleCfg configuration,
+      Schema coreSchema)
+  {
+    final Map<String, MatchingRule> results = new HashMap<>();
     for (String collation : configuration.getCollation())
     {
       CollationMapper mapper = new CollationMapper(collation);
@@ -111,40 +149,39 @@
         logger.error(WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_FORMAT, collation);
         continue;
       }
-
       Locale locale = getLocale(languageTag);
-      if (locale != null)
-      {
-        try
-        {
-          final int[] numericSuffixes = { 1, 2, 3, 4, 5, 6 };
-          for (int suffix : numericSuffixes)
-          {
-            final String oid =  nOID + "." + suffix;
-            addMatchingRule(oid, coreSchema.getMatchingRule(oid));
-          }
-          // the default (equality) matching rule
-          addMatchingRule(nOID, coreSchema.getMatchingRule(nOID));
-        }
-        catch (Exception e)
-        {
-          logger.error(LocalizableMessage.raw("Error when adding a collation matching rule with oid %s, tag %s: %s",
-              nOID, languageTag, e.getMessage()));
-        }
-      }
-      else
+      if (locale == null)
       {
         // This locale is not supported by JVM.
-        logger.error(WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_LOCALE,
-                collation, configuration.dn().toString(), languageTag);
+        logger.error(WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_LOCALE, collation, configuration.dn(), languageTag);
+        continue;
+      }
+
+      final int[] numericSuffixes = { 1, 2, 3, 4, 5, 6 };
+      for (int suffix : numericSuffixes)
+      {
+        final String oid = nOID + "." + suffix;
+        final MatchingRule matchingRule = coreSchema.getMatchingRule(oid);
+        results.put(oid, matchingRule);
+      }
+      // the default (equality) matching rule
+      final MatchingRule defaultEqualityMatchingRule = coreSchema.getMatchingRule(nOID);
+      results.put(nOID, defaultEqualityMatchingRule);
+    }
+    return results;
+  }
+
+  private Set<MatchingRule> getCollationMatchingRules(Collection<MatchingRule> matchingRules)
+  {
+    final Set<MatchingRule> results = new HashSet<>();
+    for (MatchingRule matchingRule : matchingRules)
+    {
+      if (matchingRule.getOID().startsWith("1.3.6.1.4.1.42.2.27.9.4."))
+      {
+        results.add(matchingRule);
       }
     }
-
-    // Save this configuration.
-    currentConfig = configuration;
-
-    // Register for change events.
-    currentConfig.addCollationChangeListener(this);
+    return results;
   }
 
   @Override
@@ -155,9 +192,9 @@
   }
 
   @Override
-  public ConfigChangeResult applyConfigurationChange(
-      CollationMatchingRuleCfg configuration)
+  public ConfigChangeResult applyConfigurationChange(final CollationMatchingRuleCfg configuration)
   {
+    // validation has already been performed in isConfigurationChangeAcceptable()
     final ConfigChangeResult ccr = new ConfigChangeResult();
 
     if (!configuration.isEnabled()
@@ -171,39 +208,64 @@
       return ccr;
     }
 
-    // Since we have come here it means that this Factory is enabled and
-    // there is a change in the CollationMatchingRuleFactory's configuration.
-    // Deregister all the Matching Rule corresponding to this factory.
-    for (MatchingRule rule : getMatchingRules())
-    {
-      DirectoryServer.deregisterMatchingRule(rule);
-    }
+    // Since we have come here it means that this Factory is enabled
+    // and there is a change in the CollationMatchingRuleFactory's configuration.
+    final org.opends.server.types.Schema serverSchema = DirectoryServer.getSchema();
+    final Collection<MatchingRule> existingCollationRules = getCollationMatchingRules(serverSchema.getMatchingRules());
 
     matchingRules.clear();
+    final Map<String, MatchingRule> configuredMatchingRules =
+        collectConfiguredMatchingRules(configuration, CoreSchema.getInstance());
+    matchingRules.putAll(configuredMatchingRules);
 
-    final Schema schema = DirectoryServer.getSchema().getSchemaNG();
-    for (String collation : configuration.getCollation())
+    for (Iterator<MatchingRule> it = existingCollationRules.iterator(); it.hasNext();)
     {
-      // validation has already been performed in isConfigurationChangeAcceptable()
-      CollationMapper mapper = new CollationMapper(collation);
-      String nOID = mapper.getNumericOID();
-      addMatchingRule(nOID, schema.getMatchingRule(nOID));
-    }
-
-    try
-    {
-      for (MatchingRule matchingRule : getMatchingRules())
+      String oid = it.next().getOID();
+      if (configuredMatchingRules.remove(oid) != null)
       {
-        DirectoryServer.registerMatchingRule(matchingRule, false);
+        // no change
+        it.remove();
       }
     }
-    catch (DirectoryException de)
+    try
     {
-      LocalizableMessage message =
-          WARN_CONFIG_SCHEMA_MR_CONFLICTING_MR.get(configuration.dn(), de.getMessageObject());
-      ccr.setAdminActionRequired(true);
-      ccr.addMessage(message);
+      serverSchema.updateSchema(new SchemaUpdater()
+      {
+        @Override
+        public Schema update(SchemaBuilder builder)
+        {
+          Collection<MatchingRule> defaultMatchingRules = CoreSchema.getInstance().getMatchingRules();
+          for (MatchingRule rule : defaultMatchingRules)
+          {
+            if (configuredMatchingRules.containsKey(rule.getOID()))
+            {
+              try
+              {
+                // added
+                builder.buildMatchingRule(rule).addToSchema();
+              }
+              catch (ConflictingSchemaElementException e)
+              {
+                ccr.setAdminActionRequired(true);
+                ccr.addMessage(WARN_CONFIG_SCHEMA_MR_CONFLICTING_MR.get(configuration.dn(), e.getMessageObject()));
+              }
+            }
+          }
+          for (MatchingRule ruleToRemove : existingCollationRules)
+          {
+            // removed
+            builder.removeMatchingRule(ruleToRemove.getOID());
+          }
+          return builder.toSchema();
+        }
+      });
     }
+    catch (DirectoryException e)
+    {
+      ccr.setResultCode(e.getResultCode());
+      ccr.addMessage(e.getMessageObject());
+    }
+
     currentConfig = configuration;
     return ccr;
   }
@@ -234,18 +296,16 @@
       if (nOID == null || languageTag == null)
       {
         configAcceptable = false;
-        LocalizableMessage msg = WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_FORMAT.get(collation);
-        unacceptableReasons.add(msg);
+        unacceptableReasons.add(WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_FORMAT.get(collation));
         continue;
       }
 
       Locale locale = getLocale(languageTag);
       if (locale == null)
       {
-        LocalizableMessage msg = WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_LOCALE.get(
-                collation, configuration.dn(), languageTag);
-        unacceptableReasons.add(msg);
         configAcceptable = false;
+        unacceptableReasons.add(WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_LOCALE.get(
+                collation, configuration.dn(), languageTag));
         continue;
       }
     }

--
Gitblit v1.10.0