mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Jean-Noël Rouvignac
03.43.2016 0aa4dc91310ba40961f5f09009588881d708f17c
OPENDJ-3037 Use SchemaBuilder.addSchema(Entry, boolean) in SchemaConfigManager
5 files modified
817 ■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/core/SchemaConfigManager.java 503 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/tasks/AddSchemaFileTask.java 11 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/types/Schema.java 239 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/messages/org/opends/messages/config.properties 5 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/tasks/AddSchemaFileTaskTestCase.java 59 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/SchemaConfigManager.java
@@ -20,18 +20,19 @@
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Collections;
import java.util.List;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.adapter.server3x.Converters;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ModificationType;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.schema.AttributeType;
import org.forgerock.opendj.ldap.schema.CoreSchema;
import org.forgerock.opendj.ldap.schema.Syntax;
import org.forgerock.opendj.ldap.schema.SchemaBuilder;
import org.opends.server.types.Attribute;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
@@ -39,12 +40,11 @@
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.Modification;
import org.opends.server.types.Schema;
import org.opends.server.types.Schema.SchemaUpdater;
import org.opends.server.util.LDIFReader;
import org.opends.server.util.StaticUtils;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.schema.SchemaConstants.*;
import static org.opends.server.util.StaticUtils.*;
/**
@@ -295,21 +295,50 @@
   *                     to be loaded.
   * @param  schemaFile  The name of the schema file to be loaded into the
   *                     provided schema.
   *
   * @return  A list of the modifications that could be performed in order to
   *          obtain the contents of the file.
   *
   * @throws  ConfigException  If a configuration problem causes the schema
   *                           element initialization to fail.
   *
   * @throws  InitializationException  If a problem occurs while initializing
   *                                   the schema elements that is not related
   *                                   to the server configuration.
   */
  public static List<Modification> loadSchemaFile(Schema schema, String schemaFile)
  public static void loadSchemaFile(Schema schema, String schemaFile)
         throws ConfigException, InitializationException
  {
    return loadSchemaFile(schema, schemaFile, true);
    loadSchemaFile(schema, schemaFile, true);
  }
  /**
   * Loads the contents of the specified schema file into the provided schema and returns the list
   * of modifications.
   *
   * @param schema
   *          The schema in which the contents of the schema file are to be loaded.
   * @param schemaFile
   *          The name of the schema file to be loaded into the provided schema.
   * @return A list of the modifications that could be performed in order to obtain the contents of
   *         the file.
   * @throws ConfigException
   *           If a configuration problem causes the schema element initialization to fail.
   * @throws InitializationException
   *           If a problem occurs while initializing the schema elements that is not related to the
   *           server configuration.
   */
  public static List<Modification> loadSchemaFileReturnModifications(Schema schema, String schemaFile)
      throws ConfigException, InitializationException
  {
    final Entry entry = loadSchemaFile(schema, schemaFile, true);
    if (entry != null)
    {
      return createAddModifications(entry,
          CoreSchema.getLDAPSyntaxesAttributeType(),
          CoreSchema.getAttributeTypesAttributeType(),
          CoreSchema.getObjectClassesAttributeType(),
          CoreSchema.getNameFormsAttributeType(),
          CoreSchema.getDITContentRulesAttributeType(),
          CoreSchema.getDITStructureRulesAttributeType(),
          CoreSchema.getMatchingRuleUseAttributeType());
    }
    return Collections.emptyList();
  }
  /**
@@ -325,20 +354,63 @@
   *                      log an error message and return without an exception.
   *                      This should only be {@code false} when called from
   *                      {@code initializeSchemaFromFiles}.
   *
   * @return  A list of the modifications that could be performed in order to
   *          obtain the contents of the file, or {@code null} if a problem
   *          occurred and {@code failOnError} is {@code false}.
   *
   * @return the schema entry that has been read from the schema file
   * @throws  ConfigException  If a configuration problem causes the schema
   *                           element initialization to fail.
   *
   * @throws  InitializationException  If a problem occurs while initializing
   *                                   the schema elements that is not related
   *                                   to the server configuration.
   */
  private static List<Modification> loadSchemaFile(Schema schema, String schemaFile,
      boolean failOnError) throws ConfigException, InitializationException
  private static Entry loadSchemaFile(Schema schema, String schemaFile, boolean failOnError)
      throws ConfigException, InitializationException
  {
    final Entry entry = readSchemaEntryFromFile(schemaFile, failOnError);
    if (entry != null)
    {
      List<Attribute> ldapSyntaxList = entry.getAttribute(CoreSchema.getLDAPSyntaxesAttributeType());
      parseLdapSyntaxesDefinitions(schema, schemaFile, failOnError, ldapSyntaxList);
      updateSchemaWithEntry(schema, schemaFile, failOnError, entry);
    }
    return entry;
  }
  private static void updateSchemaWithEntry(Schema schema, String schemaFile, boolean failOnError, final Entry entry)
      throws ConfigException
  {
    final org.forgerock.opendj.ldap.Entry schemaEntry = Converters.from(entry);
    try
    {
      updateSchema(schema, schemaEntry, false);
    }
    catch (DirectoryException e)
    {
      if (e.getResultCode().equals(ResultCode.CONSTRAINT_VIOLATION))
      {
        // Register it with the schema. We will allow duplicates, with the
        // later definition overriding any earlier definition, but we want
        // to trap them and log a warning.
        logger.warn(WARN_CONFIG_CONFLICTING_DEFINITIONS_IN_SCHEMA_FILE, schemaFile, e.getMessageObject());
        try
        {
          updateSchema(schema, schemaEntry, true);
        }
        catch (DirectoryException e2)
        {
          // This should never happen
          logger.traceException(e2);
        }
      }
      else
      {
        reportError(failOnError, e,
            WARN_CONFIG_SCHEMA_CANNOT_PARSE_DEFINITIONS_IN_SCHEMA_FILE.get(schemaFile, e.getMessageObject()));
      }
    }
  }
  private static Entry readSchemaEntryFromFile(String schemaFile, boolean failOnError)
      throws ConfigException, InitializationException
  {
    // Create an LDIF reader to use when reading the files.
    String schemaDirPath = getSchemaDirectoryPath();
@@ -359,15 +431,12 @@
      {
        throw new ConfigException(message);
      }
      else
      {
        logger.error(message);
        return null;
      }
    }
    // Read the LDIF entry from the file and close the file.
    Entry entry;
    final Entry entry;
    try
    {
      entry = reader.readEntry(false);
@@ -376,7 +445,7 @@
      {
        // The file was empty -- skip it.
        reader.close();
        return new LinkedList<>();
        return null;
      }
    }
    catch (Exception e)
@@ -390,13 +459,10 @@
      {
        throw new InitializationException(message, e);
      }
      else
      {
        logger.error(message);
        StaticUtils.close(reader);
        return null;
      }
    }
    // If there are any more entries in the file, then print a warning message.
    try
@@ -417,145 +483,34 @@
    {
      StaticUtils.close(reader);
    }
    // Get the attributeTypes attribute from the entry.
    List<Modification> mods = new LinkedList<>();
    //parse the syntaxes first because attributes rely on these.
    List<Attribute> ldapSyntaxList = getLdapSyntaxesAttributes(schema, entry, mods);
    List<Attribute> attrList = getAttributeTypeAttributes(schema, entry, mods);
    List<Attribute> ocList = getObjectClassesAttributes(schema, entry, mods);
    List<Attribute> nfList = getNameFormsAttributes(schema, entry, mods);
    List<Attribute> dcrList = getDITContentRulesAttributes(schema, entry, mods);
    List<Attribute> dsrList = getDITStructureRulesAttributes(schema, entry, mods);
    List<Attribute> mruList = getMatchingRuleUsesAttributes(schema, entry, mods);
    // Loop on all the attribute of the schema entry to
    // find the extra attribute that should be loaded in the Schema.
    for (Attribute attribute : entry.getAttributes())
    {
      if (!isSchemaAttribute(attribute))
      {
        schema.addExtraAttribute(attribute.getAttributeDescription().getNameOrOID(), attribute);
      }
    return entry;
    }
    parseLdapSyntaxesDefinitions(schema, schemaFile, failOnError, ldapSyntaxList);
    parseAttributeTypeDefinitions(schema, schemaFile, failOnError, attrList);
    parseObjectclassDefinitions(schema, schemaFile, failOnError, ocList);
    parseNameFormDefinitions(schema, schemaFile, failOnError, nfList);
    parseDITContentRuleDefinitions(schema, schemaFile, failOnError, dcrList);
    parseDITStructureRuleDefinitions(schema, schemaFile, failOnError, dsrList);
    parseMatchingRuleUseDefinitions(schema, schemaFile, failOnError, mruList);
    return mods;
  private static void updateSchema(Schema schema, final org.forgerock.opendj.ldap.Entry schemaEntry,
      final boolean overwrite) throws DirectoryException
  {
    schema.updateSchema(new SchemaUpdater()
    {
      @Override
      public org.forgerock.opendj.ldap.schema.Schema update(SchemaBuilder builder)
      {
        return builder.addSchema(schemaEntry, overwrite).toSchema();
      }
    });
  }
  private static List<Attribute> getLdapSyntaxesAttributes(Schema schema,
      Entry entry, List<Modification> mods) throws ConfigException
  private static List<Modification> createAddModifications(Entry entry,  AttributeType... attrTypes)
  {
    Syntax syntax = schema.getSyntax(SYNTAX_LDAP_SYNTAX_OID);
    if (syntax == null)
    int nbMods = entry.getUserAttributes().size() + entry.getOperationalAttributes().size();
    List<Modification> mods = new ArrayList<>(nbMods);
    for (AttributeType attrType : attrTypes)
    {
      syntax = CoreSchema.getLDAPSyntaxDescriptionSyntax();
    }
    AttributeType ldapSyntaxAttrType = schema.getAttributeType(ATTR_LDAP_SYNTAXES, syntax);
    return createAddModifications(entry, mods, ldapSyntaxAttrType);
  }
  private static List<Attribute> getAttributeTypeAttributes(Schema schema,
      Entry entry, List<Modification> mods) throws ConfigException,
      InitializationException
  {
    Syntax syntax = schema.getSyntax(SYNTAX_ATTRIBUTE_TYPE_OID);
    if (syntax == null)
    {
      syntax = CoreSchema.getAttributeTypeDescriptionSyntax();
    }
    AttributeType attributeAttrType = schema.getAttributeType(ATTR_ATTRIBUTE_TYPES, syntax);
    return createAddModifications(entry, mods, attributeAttrType);
  }
  /** Get the objectClasses attribute from the entry. */
  private static List<Attribute> getObjectClassesAttributes(Schema schema,
      Entry entry, List<Modification> mods) throws ConfigException,
      InitializationException
  {
    Syntax syntax = schema.getSyntax(SYNTAX_OBJECTCLASS_OID);
    if (syntax == null)
    {
      syntax = CoreSchema.getObjectClassDescriptionSyntax();
    }
    AttributeType objectclassAttrType = schema.getAttributeType(ATTR_OBJECTCLASSES, syntax);
    return createAddModifications(entry, mods, objectclassAttrType);
  }
  /** Get the name forms attribute from the entry. */
  private static List<Attribute> getNameFormsAttributes(Schema schema,
      Entry entry, List<Modification> mods) throws ConfigException,
      InitializationException
  {
    Syntax syntax = schema.getSyntax(SYNTAX_NAME_FORM_OID);
    if (syntax == null)
    {
      syntax = CoreSchema.getNameFormDescriptionSyntax();
    }
    AttributeType nameFormAttrType = schema.getAttributeType(ATTR_NAME_FORMS, syntax);
    return createAddModifications(entry, mods, nameFormAttrType);
  }
  /** Get the DIT content rules attribute from the entry. */
  private static List<Attribute> getDITContentRulesAttributes(Schema schema,
      Entry entry, List<Modification> mods) throws ConfigException,
      InitializationException
  {
    Syntax syntax = schema.getSyntax(SYNTAX_DIT_CONTENT_RULE_OID);
    if (syntax == null)
    {
      syntax = CoreSchema.getDITContentRuleDescriptionSyntax();
    }
    AttributeType dcrAttrType = schema.getAttributeType(ATTR_DIT_CONTENT_RULES, syntax);
    return createAddModifications(entry, mods, dcrAttrType);
  }
  /** Get the DIT structure rules attribute from the entry. */
  private static List<Attribute> getDITStructureRulesAttributes(Schema schema,
      Entry entry, List<Modification> mods) throws ConfigException,
      InitializationException
  {
    Syntax syntax = schema.getSyntax(SYNTAX_DIT_STRUCTURE_RULE_OID);
    if (syntax == null)
    {
      syntax = CoreSchema.getDITStructureRuleDescriptionSyntax();
    }
    AttributeType dsrAttrType = schema.getAttributeType(ATTR_DIT_STRUCTURE_RULES, syntax);
    return createAddModifications(entry, mods, dsrAttrType);
  }
  /** Get the matching rule uses attribute from the entry. */
  private static List<Attribute> getMatchingRuleUsesAttributes(Schema schema,
      Entry entry, List<Modification> mods) throws ConfigException,
      InitializationException
  {
    Syntax syntax = schema.getSyntax(SYNTAX_MATCHING_RULE_USE_OID);
    if (syntax == null)
    {
      syntax = CoreSchema.getMatchingRuleUseDescriptionSyntax();
    }
    AttributeType mruAttrType = schema.getAttributeType(ATTR_MATCHING_RULE_USE, syntax);
    return createAddModifications(entry, mods, mruAttrType);
  }
  private static List<Attribute> createAddModifications(Entry entry,
      List<Modification> mods, AttributeType attrType)
  {
    List<Attribute> attributes = entry.getAttribute(attrType);
    for (Attribute a : attributes)
      for (Attribute a : entry.getAttribute(attrType))
    {
      mods.add(new Modification(ModificationType.ADD, a));
    }
    return attributes;
    }
    return mods;
  }
  /** Parse the ldapsyntaxes definitions if there are any. */
@@ -603,166 +558,6 @@
    }
  }
  /** Parse the attribute type definitions if there are any. */
  private static void parseAttributeTypeDefinitions(
      Schema schema, String schemaFile, boolean failOnError, List<Attribute> attrList)
          throws ConfigException
  {
    List<String> definitions = toStrings(attrList);
    try
    {
      schema.registerAttributeTypes(definitions, schemaFile, !failOnError);
    }
    catch (DirectoryException de)
    {
      logger.traceException(de);
      if (de.getResultCode().equals(ResultCode.CONSTRAINT_VIOLATION))
      {
        // Register it with the schema. We will allow duplicates, with the
        // later definition overriding any earlier definition, but we want
        // to trap them and log a warning.
        logger.warn(WARN_CONFIG_SCHEMA_CONFLICTING_ATTR_TYPE, schemaFile, de.getMessageObject());
        try
        {
          schema.registerAttributeTypes(definitions, schemaFile, true);
        }
        catch (DirectoryException e)
        {
          // This should never happen
          logger.traceException(e);
        }
      }
      else
      {
        LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_ATTR_TYPE.get(schemaFile, de.getMessageObject());
        reportError(failOnError, de, message);
      }
    }
  }
  /** Parse the objectclass definitions if there are any. */
  private static void parseObjectclassDefinitions(Schema schema,
      String schemaFile, boolean failOnError, List<Attribute> ocList)
      throws ConfigException
  {
    List<String> definitions = toStrings(ocList);
    try
    {
      schema.registerObjectClasses(definitions, schemaFile, !failOnError);
    }
    catch (DirectoryException de)
    {
      logger.traceException(de);
      if (de.getResultCode().equals(ResultCode.CONSTRAINT_VIOLATION))
      {
        // Register it with the schema. We will allow duplicates, with the
        // later definition overriding any earlier definition, but we want
        // to trap them and log a warning.
        logger.warn(WARN_CONFIG_SCHEMA_CONFLICTING_OC, schemaFile, de.getMessageObject());
        try
        {
          schema.registerObjectClasses(definitions, schemaFile, true);
        }
        catch (DirectoryException e)
        {
          // This should never happen
          logger.traceException(e);
        }
      }
      else
      {
        LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_OC.get(schemaFile, de.getMessageObject());
        reportError(failOnError, de, message);
      }
    }
  }
  private static List<String> toStrings(List<Attribute> attributes)
  {
    List<String> results = new ArrayList<>();
    for (Attribute attr : attributes)
    {
      for (ByteString v : attr)
      {
        results.add(v.toString());
      }
    }
    return results;
  }
  /** Parse the name form definitions if there are any. */
  private static void parseNameFormDefinitions(Schema schema,
      String schemaFile, boolean failOnError, List<Attribute> nfList)
      throws ConfigException
  {
    for (Attribute a : nfList)
    {
      for (ByteString v : a)
      {
        // Register it with the schema. We will allow duplicates, with the
        // later definition overriding any earlier definition, but we want
        // to trap them and log a warning.
        String definition = v.toString();
        try
        {
          schema.registerNameForm(definition, schemaFile, failOnError);
        }
        catch (DirectoryException de)
        {
          logger.traceException(de);
          logger.warn(WARN_CONFIG_SCHEMA_CONFLICTING_NAME_FORM, schemaFile, de.getMessageObject());
          try
          {
            schema.registerNameForm(definition, schemaFile, true);
          }
          catch (Exception e)
          {
            // This should never happen.
            logger.traceException(e);
          }
        }
      }
    }
  }
  /** Parse the DIT content rule definitions if there are any. */
  private static void parseDITContentRuleDefinitions(Schema schema,
      String schemaFile, boolean failOnError, List<Attribute> dcrList)
      throws ConfigException
  {
    for (Attribute a : dcrList)
    {
      for (ByteString v : a)
      {
        final String definition = v.toString();
        try
        {
          schema.registerDITContentRule(definition, schemaFile, failOnError);
        }
        catch (DirectoryException de)
        {
          logger.traceException(de);
          logger.warn(WARN_CONFIG_SCHEMA_CONFLICTING_DCR, schemaFile, de.getMessageObject());
          try
          {
            schema.registerDITContentRule(definition, schemaFile, true);
          }
          catch (Exception e)
          {
            // This should never happen.
            logger.traceException(e);
          }
        }
      }
    }
  }
  private static void reportError(boolean failOnError, Exception e,
      LocalizableMessage message) throws ConfigException
  {
@@ -773,80 +568,6 @@
    logger.error(message);
  }
  /** Parse the DIT structure rule definitions if there are any. */
  private static void parseDITStructureRuleDefinitions(Schema schema,
      String schemaFile, boolean failOnError, List<Attribute> dsrList)
      throws ConfigException
  {
    for (Attribute a : dsrList)
    {
      for (ByteString v : a)
      {
        // Register it with the schema. We will allow duplicates, with the
        // later definition overriding any earlier definition, but we want
        // to trap them and log a warning.
        String definition = v.toString();
        try
        {
          schema.registerDITStructureRule(definition, schemaFile, failOnError);
        }
        catch (DirectoryException de)
        {
          logger.traceException(de);
          logger.warn(WARN_CONFIG_SCHEMA_CONFLICTING_DSR, schemaFile, de.getMessageObject());
          try
          {
            schema.registerDITStructureRule(definition, schemaFile, true);
          }
          catch (Exception e)
          {
            // This should never happen.
            logger.traceException(e);
          }
        }
      }
    }
  }
  /** Parse the matching rule use definitions if there are any. */
  private static void parseMatchingRuleUseDefinitions(Schema schema,
      String schemaFile, boolean failOnError, List<Attribute> mruList)
      throws ConfigException
  {
    for (Attribute a : mruList)
    {
      for (ByteString v : a)
      {
        // Register it with the schema. We will allow duplicates, with the
        // later definition overriding any earlier definition, but we want
        // to trap them and log a warning.
        String definition = v.toString();
        try
        {
          schema.registerMatchingRuleUse(definition, schemaFile, failOnError);
        }
        catch (DirectoryException de)
        {
          logger.traceException(de);
          logger.warn(WARN_CONFIG_SCHEMA_CONFLICTING_MRU, schemaFile, de.getMessageObject());
          try
          {
            schema.registerMatchingRuleUse(definition, schemaFile, true);
          }
          catch (Exception e)
          {
            // This should never happen.
            logger.traceException(e);
          }
        }
      }
    }
  }
  /**
   * This method checks if a given attribute is an attribute that
   * is used by the definition of the schema.
opendj-server-legacy/src/main/java/org/opends/server/tasks/AddSchemaFileTask.java
@@ -62,13 +62,11 @@
  /** The list of files to be added to the server schema. */
  private TreeSet<String> filesToAdd;
  /** {@inheritDoc} */
  @Override
  public LocalizableMessage getDisplayName() {
    return INFO_TASK_ADD_SCHEMA_FILE_NAME.get();
  }
  /** {@inheritDoc} */
  @Override
  public void initializeTask()
         throws DirectoryException
@@ -87,7 +85,6 @@
      }
    }
    // Get the attribute that specifies which schema file(s) to add.
    Entry taskEntry = getTaskEntry();
    AttributeType attrType = DirectoryServer.getAttributeType(ATTR_TASK_ADDSCHEMAFILE_FILENAME);
@@ -99,7 +96,6 @@
      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
    }
    // Get the name(s) of the schema files to add and make sure they exist in
    // the schema directory.
    String schemaInstanceDirectory =
@@ -136,7 +132,6 @@
      }
    }
    // Create a new dummy schema and make sure that we can add the contents of
    // all the schema files into it.  Even though this duplicates work we'll
    // have to do later, it will be good to do it now as well so we can reject
@@ -159,9 +154,6 @@
    }
  }
  /** {@inheritDoc} */
  @Override
  protected TaskState runTask()
  {
@@ -182,7 +174,7 @@
      {
        try
        {
          List<Modification> modList = SchemaConfigManager.loadSchemaFile(schema, schemaFile);
          List<Modification> modList = SchemaConfigManager.loadSchemaFileReturnModifications(schema, schemaFile);
          for (Modification m : modList)
          {
            Attribute a = m.getAttribute();
@@ -234,4 +226,3 @@
    }
  }
}
opendj-server-legacy/src/main/java/org/opends/server/types/Schema.java
@@ -33,7 +33,6 @@
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -411,50 +410,6 @@
  }
  /**
   * Registers a list of attribute types from their provided definitions.
   * <p>
   * This method allows to do only one schema change for multiple definitions,
   * thus avoiding the cost (and the issue of stale schema references) of rebuilding a new schema for each definition.
   *
   * @param definitions
   *          The definitions of the attribute types
   * @param schemaFile
   *          The schema file where these definitions belong, can be {@code null}
   * @param overwrite
   *          Indicates whether to overwrite the attribute
   *          type if it already exists based on OID or name
   * @throws DirectoryException
   *            If an error occurs
   */
  public void registerAttributeTypes(final List<String> definitions, final String schemaFile, final boolean overwrite)
      throws DirectoryException
  {
    exclusiveLock.lock();
    try
    {
      SchemaBuilder builder = new SchemaBuilder(schemaNG);
      for (String definition : definitions)
      {
        String defWithFile = getDefinitionWithSchemaFile(definition, schemaFile);
        builder.addAttributeType(defWithFile, overwrite);
      }
      switchSchema(builder.toSchema());
    }
    catch (ConflictingSchemaElementException | UnknownSchemaElementException e)
    {
      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, e.getMessageObject(), e);
    }
    catch (LocalizedIllegalArgumentException e)
    {
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, e.getMessageObject(), e);
    }
    finally
    {
      exclusiveLock.unlock();
    }
  }
  /**
   * Registers an attribute type from its provided definition.
   *
   * @param definition
@@ -471,7 +426,26 @@
  public void registerAttributeType(final String definition, final String schemaFile, final boolean overwrite)
      throws DirectoryException
  {
    registerAttributeTypes(Arrays.asList(definition), schemaFile, overwrite);
    exclusiveLock.lock();
    try
    {
      String defWithFile = getDefinitionWithSchemaFile(definition, schemaFile);
      switchSchema(new SchemaBuilder(schemaNG)
          .addAttributeType(defWithFile, overwrite)
          .toSchema());
    }
    catch (ConflictingSchemaElementException | UnknownSchemaElementException e)
    {
      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, e.getMessageObject(), e);
    }
    catch (LocalizedIllegalArgumentException e)
    {
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, e.getMessageObject(), e);
    }
    finally
    {
      exclusiveLock.unlock();
    }
  }
  /**
@@ -743,34 +717,28 @@
  }
  /**
   * Registers a list of object classes from their provided definitions.
   * <p>
   * This method allows to do only one schema change for multiple definitions,
   * thus avoiding the cost (and the issue of stale schema references) of rebuilding a new schema for each definition.
   * Registers an object class from its provided definition.
   *
   * @param definitions
   *          The definitions of the object classes
   * @param definition
   *          The definition of the object class
   * @param schemaFile
   *          The schema file where these definitions belong, can be {@code null}
   * @param overwrite
   *          Indicates whether to overwrite the attribute
   *          type if it already exists based on OID or name
   *          The schema file where this definition belongs, may be {@code null}
   * @param overwriteExisting
   *          Indicates whether to overwrite the object class
   *          if it already exists based on OID or name
   * @throws DirectoryException
   *            If an error occurs
   */
  public void registerObjectClasses(final List<String> definitions, final String schemaFile, final boolean overwrite)
  public void registerObjectClass(String definition, String schemaFile, boolean overwriteExisting)
      throws DirectoryException
  {
    exclusiveLock.lock();
    try
    {
      SchemaBuilder builder = new SchemaBuilder(schemaNG);
      for (String definition : definitions)
      {
        String defWithFile = getDefinitionWithSchemaFile(definition, schemaFile);
        builder.addObjectClass(defWithFile, overwrite);
      }
      switchSchema(builder.toSchema());
      switchSchema(new SchemaBuilder(schemaNG)
          .addObjectClass(defWithFile, overwriteExisting)
          .toSchema());
    }
    catch (ConflictingSchemaElementException | UnknownSchemaElementException e)
    {
@@ -1249,37 +1217,6 @@
    }
  }
  /**
   * Registers the provided matching rule use definition with this schema.
   *
   * @param definition
   *          The matching rule use definition to register.
   * @param schemaFile
   *          The schema file where this definition belongs, maybe {@code null}
   * @param overwriteExisting
   *          Indicates whether to overwrite an existing mapping if there are any conflicts (i.e.,
   *          another matching rule use with the same matching rule).
   * @throws DirectoryException
   *           If a conflict is encountered and the <CODE>overwriteExisting</CODE> flag is set to
   *           {@code false}
   */
  public void registerMatchingRuleUse(String definition, String schemaFile, boolean overwriteExisting)
      throws DirectoryException
  {
    exclusiveLock.lock();
    try
    {
      String definitionWithFile = getDefinitionWithSchemaFile(definition, schemaFile);
      switchSchema(new SchemaBuilder(schemaNG)
        .addMatchingRuleUse(definitionWithFile, overwriteExisting)
        .toSchema());
    }
    finally
    {
      exclusiveLock.unlock();
    }
  }
  private String getDefinitionWithSchemaFile(String definition, String schemaFile)
  {
    return schemaFile != null ? addSchemaFileToElementDefinitionIfAbsent(definition, schemaFile) : definition;
@@ -1381,37 +1318,6 @@
  }
  /**
   * Registers the provided DIT content rule definition with this schema.
   *
   * @param definition
   *          The DIT content rule definition to register.
   * @param schemaFile
   *          The schema file where this definition belongs, maybe {@code null}
   * @param overwriteExisting
   *          Indicates whether to overwrite an existing mapping if there are any conflicts (i.e.,
   *          another DIT content rule with the same object class).
   * @throws DirectoryException
   *           If a conflict is encountered and the <CODE>overwriteExisting</CODE> flag is set to
   *           {@code false}
   */
  public void registerDITContentRule(String definition, String schemaFile, boolean overwriteExisting)
      throws DirectoryException
  {
    exclusiveLock.lock();
    try
    {
      String definitionWithFile = getDefinitionWithSchemaFile(definition, schemaFile);
      switchSchema(new SchemaBuilder(schemaNG)
        .addDITContentRule(definitionWithFile, overwriteExisting)
        .toSchema());
    }
    finally
    {
      exclusiveLock.unlock();
    }
  }
  /**
   * Deregisters the provided DIT content rule definition with this
   * schema.
   *
@@ -1525,37 +1431,6 @@
  }
  /**
   * Registers the provided DIT structure rule definition with this schema.
   *
   * @param definition
   *          The definition of the DIT structure rule to register.
   * @param schemaFile
   *          The schema file where this definition belongs, maybe {@code null}
   * @param overwriteExisting
   *          Indicates whether to overwrite an existing mapping if there are any conflicts
   *          (i.e., another DIT structure rule with the same name form).
   * @throws DirectoryException
   *           If a conflict is encountered and the {@code overwriteExisting} flag is set to
   *           {@code false}
   */
  public void registerDITStructureRule(String definition, String schemaFile, boolean overwriteExisting)
      throws DirectoryException
  {
    exclusiveLock.lock();
    try
    {
      String definitionWithFile = getDefinitionWithSchemaFile(definition, schemaFile);
      switchSchema(new SchemaBuilder(schemaNG)
          .addDITStructureRule(definitionWithFile, overwriteExisting)
          .toSchema());
    }
    finally
    {
      exclusiveLock.unlock();
    }
  }
  /**
   * Deregisters the provided DIT structure rule definition with this schema.
   *
   * @param ditStructureRule
@@ -1563,8 +1438,7 @@
   * @throws DirectoryException
   *           If an error occurs.
   */
  public void deregisterDITStructureRule(
      DITStructureRule ditStructureRule) throws DirectoryException
  public void deregisterDITStructureRule(DITStructureRule ditStructureRule) throws DirectoryException
  {
    exclusiveLock.lock();
    try
@@ -1680,36 +1554,6 @@
  }
  /**
   * Registers the provided name form definition with this schema.
   *
   * @param definition
   *          The name form definition to register.
   * @param schemaFile
   *          The schema file where this definition belongs, maybe {@code null}
   * @param overwriteExisting
   *          Indicates whether to overwrite an existing mapping if there are any conflicts
   * @throws DirectoryException
   *           If a conflict is encountered and the <CODE>overwriteExisting</CODE> flag is set to
   *           {@code false}
   */
  public void registerNameForm(String definition, String schemaFile, boolean overwriteExisting)
      throws DirectoryException
  {
    exclusiveLock.lock();
    try
    {
      String definitionWithFile = getDefinitionWithSchemaFile(definition, schemaFile);
      switchSchema(new SchemaBuilder(schemaNG)
        .addNameForm(definitionWithFile, overwriteExisting)
        .toSchema());
    }
    finally
    {
      exclusiveLock.unlock();
    }
  }
  /**
   * Deregisters the provided name form definition with this schema.
   *
   * @param  nameForm  The name form definition to deregister.
@@ -1793,25 +1637,6 @@
  }
  /**
   * Registers an object class from its provided definition.
   *
   * @param definition
   *          The definition of the object class
   * @param schemaFile
   *          The schema file where this definition belongs, may be {@code null}
   * @param overwriteExisting
   *          Indicates whether to overwrite the object class
   *          if it already exists based on OID or name
   * @throws DirectoryException
   *            If an error occurs
   */
  public void registerObjectClass(String definition, String schemaFile, boolean overwriteExisting)
      throws DirectoryException
  {
    registerObjectClasses(Collections.singletonList(definition), schemaFile, overwriteExisting);
  }
  /**
   * Creates a new {@link Schema} object that is a duplicate of this one. It elements may be added
   * and removed from the duplicate without impacting this version.
   *
opendj-server-legacy/src/messages/org/opends/messages/config.properties
@@ -270,6 +270,11 @@
WARN_CONFIG_SCHEMA_MR_CONFLICTING_MR_173=The matching rule defined in \
 configuration entry %s conflicts with another matching rule defined in the \
 server configuration: %s. This matching rule will not be used
WARN_CONFIG_CONFLICTING_DEFINITIONS_IN_SCHEMA_FILE_174=One or several \
 definitions read from schema configuration file %s conflict with other \
 definitions already read into the schema: %s. The later definitions will be used
WARN_CONFIG_SCHEMA_CANNOT_PARSE_DEFINITIONS_IN_SCHEMA_FILE_175=Unable to parse \
 the definitions from schema configuration file %s: %s
ERR_CONFIG_SCHEMA_SYNTAX_CANNOT_INITIALIZE_186=An error occurred while \
 trying to initialize an attribute syntax loaded from class %s with the \
 information in configuration entry %s: %s. This syntax will be disabled
opendj-server-legacy/src/test/java/org/opends/server/tasks/AddSchemaFileTaskTestCase.java
@@ -20,7 +20,11 @@
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.forgerock.opendj.ldap.DN;
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;
@@ -28,8 +32,8 @@
import org.opends.server.backends.SchemaTestMatchingRuleImpl;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.SchemaConfigManager;
import org.opends.server.schema.SchemaConstants;
import org.forgerock.opendj.ldap.DN;
import org.opends.server.types.DirectoryException;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -39,9 +43,11 @@
 * Tests invocation of the import and export tasks, but does not aim to
 * thoroughly test the underlying backend implementations.
 */
public class AddSchemaFileTaskTestCase
       extends TasksTestCase
@SuppressWarnings("javadoc")
public class AddSchemaFileTaskTestCase extends TasksTestCase
{
  private static List<MatchingRule> matchingRulesToRemove = new ArrayList<>();
  /**
   * Make sure that the Directory Server is running.
   *
@@ -53,17 +59,29 @@
    TestCaseUtils.startServer();
  }
  private MatchingRule getMatchingRule(String name, String oid, boolean isObsolete)
  @AfterClass
  public void deregisterMatchingRules() throws Exception
  {
    Schema schema =
        new SchemaBuilder(Schema.getCoreSchema())
    for (MatchingRule matchingRule : matchingRulesToRemove)
    {
      org.opends.server.types.Schema schema = DirectoryServer.getSchema();
      schema.deregisterMatchingRuleUse(schema.getMatchingRuleUse(matchingRule));
      schema.deregisterMatchingRule(matchingRule);
    }
  }
  private void registerNewMatchingRule(String name, String oid) throws DirectoryException
  {
    MatchingRule matchingRule = new SchemaBuilder(Schema.getCoreSchema())
          .buildMatchingRule(oid)
            .syntaxOID(SchemaConstants.SYNTAX_DIRECTORY_STRING_OID)
        .syntaxOID(CoreSchema.getDirectoryStringSyntax().getOID())
            .names(name)
            .implementation(new SchemaTestMatchingRuleImpl())
            .obsolete(isObsolete)
            .addToSchema().toSchema();
    return schema.getMatchingRule(oid);
        .addToSchema()
        .toSchema()
        .getMatchingRule(oid);
    DirectoryServer.getSchema().registerMatchingRule(matchingRule, false);
    matchingRulesToRemove.add(matchingRule);
  }
  /**
@@ -85,8 +103,7 @@
    Thread.sleep(2);
    MatchingRule matchingRule = getMatchingRule("testAddValidSchemaFileMatch", "1.3.6.1.4.1.26027.1.999.23", false);
    DirectoryServer.getSchema().registerMatchingRule(matchingRule, false);
    registerNewMatchingRule("testAddValidSchemaFileMatch", "1.3.6.1.4.1.26027.1.999.23");
    String schemaDirectory = SchemaConfigManager.getSchemaDirectoryPath();
@@ -134,8 +151,6 @@
                     beforeModifyTimestamp);
  }
  /**
   * Attempts to add multiple new files to the server schema where the files
   * exist and have valid contents.
@@ -154,13 +169,7 @@
              DirectoryServer.getSchema().getYoungestModificationTime();
    Thread.sleep(2);
    String schemaDirectory = SchemaConfigManager.getSchemaDirectoryPath();
    MatchingRule matchingRule1 =
        getMatchingRule("testAddMultipleValidSchemaFiles1Match", "1.3.6.1.4.1.26027.1.999.24", false);
    DirectoryServer.getSchema().registerMatchingRule(matchingRule1, false);
    registerNewMatchingRule("testAddMultipleValidSchemaFiles1Match", "1.3.6.1.4.1.26027.1.999.24");
    String[] fileLines1 =
    {
@@ -187,13 +196,11 @@
           "APPLIES testAddMultipleValidSchemaFiles1AT )"
    };
    String schemaDirectory = SchemaConfigManager.getSchemaDirectoryPath();
    File validFile1 = new File(schemaDirectory, "05-multiple-valid-1.ldif");
    writeLines(validFile1, fileLines1);
    MatchingRule matchingRule2 =
        getMatchingRule("testAddMultipleValidSchemaFiles2Match", "1.3.6.1.4.1.26027.1.999.25", false);
    DirectoryServer.getSchema().registerMatchingRule(matchingRule2, false);
    registerNewMatchingRule("testAddMultipleValidSchemaFiles2Match", "1.3.6.1.4.1.26027.1.999.25");
    String[] fileLines2 =
    {