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

neil_a_wilson
12.36.2007 679051a2a5aea0a95298083f1840735ea40bc71c
opends/build.xml
@@ -1148,6 +1148,7 @@
      <jvmarg  value="-Demma.coverage.out.file=${coverage.data.dir}/unit.emma" />
      <jvmarg value="-Demma.coverage.out.merge=false" />
      <jvmarg value="-Dorg.opends.server.BuildRoot=${basedir}" />
      <jvmarg value="-Dorg.opends.server.RunningUnitTests=true" />
      <jvmarg value="-Dorg.opends.test.suppressOutput=${org.opends.test.suppressOutput}" />
      <jvmarg value="-Dorg.opends.test.debug.target=${org.opends.test.debug.target}" />
      <jvmarg value="-Xms${MEM}" />
opends/src/server/org/opends/server/api/SynchronizationProvider.java
@@ -28,6 +28,8 @@
import java.util.List;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.AddOperation;
@@ -36,6 +38,7 @@
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.InitializationException;
import org.opends.server.types.Modification;
import org.opends.server.types.SynchronizationProviderResult;
@@ -368,5 +371,24 @@
  public abstract void doPostOperation(
                            ModifyDNOperation modifyDNOperation)
         throws DirectoryException;
  /**
   * Performs any processing that may be required whenever the server
   * schema has been updated.  This may be invoked for schema
   * modifications made with the server online, and it may also be
   * called if the server detects that there were any scheam changes
   * made with the server offline (e.g., by directly editing the
   * schema configuration files).
   * <BR><BR>
   * At the time this method is called, the schema changes will have
   * already been applied to the server.  As such, this method must
   * make a best effort attempt to process the associated schema
   * changes, and is not allowed to throw any exceptions.
   *
   * @param  modifications  The set of modifications that have been
   *                        made to the server schema.
   */
  public abstract void processSchemaChange(List<Modification>
                                                modifications);
}
opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -40,6 +40,7 @@
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@@ -109,7 +110,6 @@
import org.opends.server.types.RestoreConfig;
import org.opends.server.types.ResultCode;
import org.opends.server.types.Schema;
import org.opends.server.types.SchemaFileElement;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchScope;
import org.opends.server.util.DynamicConstants;
@@ -239,8 +239,6 @@
  {
    super();
    // Perform all initialization in initializeBackend.
  }
@@ -420,6 +418,116 @@
    supportedFeatures = new HashSet<String>(0);
    // Identify any differences that may exist between the concatenated schema
    // file from the last online modification and the current schema files.  If
    // there are any differences, then they should be from making changes to the
    // schema files with the server offline.
    try
    {
      // First, generate lists of elements from the current schema.
      LinkedHashSet<String> newATs  = new LinkedHashSet<String>();
      LinkedHashSet<String> newOCs  = new LinkedHashSet<String>();
      LinkedHashSet<String> newNFs  = new LinkedHashSet<String>();
      LinkedHashSet<String> newDCRs = new LinkedHashSet<String>();
      LinkedHashSet<String> newDSRs = new LinkedHashSet<String>();
      LinkedHashSet<String> newMRUs = new LinkedHashSet<String>();
      Schema.genConcatenatedSchema(newATs, newOCs, newNFs, newDCRs, newDSRs,
                                   newMRUs);
      // Next, generate lists of elements from the previous concatenated schema.
      // If there isn't a previous concatenated schema, then use the base
      // schema for the current revision.
      String concatFilePath;
      File configFile       = new File(DirectoryServer.getConfigFile());
      File configDirectory  = configFile.getParentFile();
      File upgradeDirectory = new File(configDirectory, "upgrade");
      File concatFile       = new File(upgradeDirectory,
                                       SCHEMA_CONCAT_FILE_NAME);
      if (concatFile.exists())
      {
        concatFilePath = concatFile.getAbsolutePath();
      }
      else
      {
        concatFile = new File(upgradeDirectory,
                              SCHEMA_BASE_FILE_NAME_WITHOUT_REVISION +
                              DynamicConstants.REVISION_NUMBER);
        if (concatFile.exists())
        {
          concatFilePath = concatFile.getAbsolutePath();
        }
        else
        {
          String runningUnitTestsStr =
               System.getProperty(PROPERTY_RUNNING_UNIT_TESTS);
          if ((runningUnitTestsStr != null) &&
              runningUnitTestsStr.equalsIgnoreCase("true"))
          {
            Schema.writeConcatenatedSchema();
            concatFile = new File(upgradeDirectory, SCHEMA_CONCAT_FILE_NAME);
            concatFilePath = concatFile.getAbsolutePath();
          }
          else
          {
            msgID = MSGID_SCHEMA_CANNOT_FIND_CONCAT_FILE;
            String message = getMessage(msgID,
                                        upgradeDirectory.getAbsolutePath(),
                                        SCHEMA_CONCAT_FILE_NAME,
                                        concatFile.getName());
            throw new InitializationException(msgID, message);
          }
        }
      }
      LinkedHashSet<String> oldATs  = new LinkedHashSet<String>();
      LinkedHashSet<String> oldOCs  = new LinkedHashSet<String>();
      LinkedHashSet<String> oldNFs  = new LinkedHashSet<String>();
      LinkedHashSet<String> oldDCRs = new LinkedHashSet<String>();
      LinkedHashSet<String> oldDSRs = new LinkedHashSet<String>();
      LinkedHashSet<String> oldMRUs = new LinkedHashSet<String>();
      Schema.readConcatenatedSchema(concatFilePath, oldATs, oldOCs, oldNFs,
                                    oldDCRs, oldDSRs, oldMRUs);
      // Create a list of modifications and add any differences between the old
      // and new schema into them.
      LinkedList<Modification> mods = new LinkedList<Modification>();
      Schema.compareConcatenatedSchema(oldATs, newATs, attributeTypesType,
                                       mods);
      Schema.compareConcatenatedSchema(oldOCs, newOCs, objectClassesType, mods);
      Schema.compareConcatenatedSchema(oldNFs, newNFs, nameFormsType, mods);
      Schema.compareConcatenatedSchema(oldDCRs, newDCRs, ditContentRulesType,
                                       mods);
      Schema.compareConcatenatedSchema(oldDSRs, newDSRs, ditStructureRulesType,
                                       mods);
      Schema.compareConcatenatedSchema(oldMRUs, newMRUs, matchingRuleUsesType,
                                       mods);
      if (! mods.isEmpty())
      {
        DirectoryServer.setOfflineSchemaChanges(mods);
        // Write a new concatenated schema file with the most recent information
        // so we don't re-find these same changes on the next startup.
        Schema.writeConcatenatedSchema();
      }
    }
    catch (InitializationException ie)
    {
      throw ie;
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SCHEMA_ERROR_DETERMINING_SCHEMA_CHANGES;
      String message = getMessage(msgID, stackTraceToSingleLineString(e));
      logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR, message,
               msgID);
    }
    // Register with the Directory Server as a configurable component.
    DirectoryServer.registerConfigurableComponent(this);
  }
@@ -961,13 +1069,6 @@
    }
    // At present, we only allow the addition of new attribute types,
    // object classes, name forms, DIT content rules, DIT structure rules, and
    // matching rule uses.  We will not support removing or replacing existing
    // elements, nor will we allow modification of any other attributes.  Make
    // sure that the included modify operation is acceptable within these
    // constraints.
    ArrayList<Modification> mods =
         new ArrayList<Modification>(modifyOperation.getModifications());
    if (mods.isEmpty())
@@ -978,18 +1079,19 @@
    Schema newSchema = DirectoryServer.getSchema().duplicate();
    TreeSet<String> modifiedSchemaFiles = new TreeSet<String>();
    LinkedHashSet<SchemaFileElement> dependentElements =
         new LinkedHashSet<SchemaFileElement>();
    int pos = -1;
    for (Modification m : mods)
    Iterator<Modification> iterator = mods.iterator();
    while (iterator.hasNext())
    {
      Modification m = iterator.next();
      pos++;
      if (m.isInternal())
      {
        // We don't need to do anything for internal modifications (e.g., like
        // those that set modifiersName and modifyTimestamp).
        iterator.remove();
        continue;
      }
@@ -1441,6 +1543,13 @@
      cleanUpTempSchemaFiles(tempSchemaFiles);
    }
    // Create a single file with all of the concatenated schema information
    // that we can use on startup to detect whether the schema files have been
    // edited with the server offline.
    Schema.writeConcatenatedSchema();
    DN authzDN = modifyOperation.getAuthorizationDN();
    if (authzDN == null)
    {
opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -35,6 +35,7 @@
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@@ -141,6 +142,7 @@
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.InitializationException;
import org.opends.server.types.MatchingRuleUse;
import org.opends.server.types.Modification;
import org.opends.server.types.NameForm;
import org.opends.server.types.ObjectClass;
import org.opends.server.types.ObjectClassType;
@@ -435,6 +437,10 @@
  // The set of connections that are currently established.
  private LinkedHashSet<ClientConnection> establishedConnections;
  // The set of schema changes made by editing the schema configuration files
  // with the server offline.
  private List<Modification> offlineSchemaChanges;
  // The logger configuration manager for the Directory Server.
  private LoggerConfigManager loggerConfigManager;
@@ -670,6 +676,7 @@
    directoryServer.saslMechanismHandlers =
         new ConcurrentHashMap<String,SASLMechanismHandler>();
    directoryServer.authenticatedUsers = new AuthenticatedUsers();
    directoryServer.offlineSchemaChanges = new LinkedList<Modification>();
  }
@@ -2585,6 +2592,42 @@
  /**
   * Retrieves a list of modifications detailing any schema changes that may
   * have been made with the server offline (e.g., by directly editing the
   * schema configuration files).  Note that this information will not be
   * available until the server backends (and in particular, the schema backend)
   * have been initialized.
   *
   * @return  A list of modifications detailing any schema changes that may have
   *          been made with the server offline, or an empty list if no offline
   *          schema changes have been detected.
   */
  public static List<Modification> getOfflineSchemaChanges()
  {
    return directoryServer.offlineSchemaChanges;
  }
  /**
   * Specifies a list of modifications detailing any schema changes that may
   * have been made with the server offline.
   *
   * @param  offlineSchemaChanges  A list of modifications detailing any schema
   *                               changes that may have been made with the
   *                               server offline.  It must not be {@code null}.
   */
  public static void setOfflineSchemaChanges(List<Modification>
                                                  offlineSchemaChanges)
  {
    ensureNotNull(offlineSchemaChanges);
    directoryServer.offlineSchemaChanges = offlineSchemaChanges;
  }
  /**
   * Retrieves the set of matching rules registered with the Directory Server.
   * The mapping will be between the lowercase name or OID for each matching
   * rule and the matching rule implementation.  The same matching rule instance
opends/src/server/org/opends/server/core/SchemaConfigManager.java
@@ -31,6 +31,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.opends.server.api.AttributeSyntax;
@@ -63,6 +64,8 @@
import org.opends.server.types.InitializationException;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.MatchingRuleUse;
import org.opends.server.types.Modification;
import org.opends.server.types.ModificationType;
import org.opends.server.types.NameForm;
import org.opends.server.types.ObjectClass;
import org.opends.server.types.ResultCode;
@@ -94,8 +97,6 @@
public class SchemaConfigManager
       implements ConfigChangeListener, ConfigAddListener, ConfigDeleteListener
{
  // The schema that has been parsed from the server configuration.
  private Schema schema;
@@ -771,6 +772,9 @@
   * @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.
   *
@@ -778,10 +782,11 @@
   *                                   the schema elements that is not related
   *                                   to the server configuration.
   */
  public static void loadSchemaFile(Schema schema, String schemaFile)
  public static List<Modification> loadSchemaFile(Schema schema,
                                                  String schemaFile)
         throws ConfigException, InitializationException
  {
    loadSchemaFile(schema, schemaFile, true);
    return loadSchemaFile(schema, schemaFile, true);
  }
@@ -800,6 +805,10 @@
   *                      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}.
   *
   * @throws  ConfigException  If a configuration problem causes the schema
   *                           element initialization to fail.
   *
@@ -807,8 +816,9 @@
   *                                   the schema elements that is not related
   *                                   to the server configuration.
   */
  private static void loadSchemaFile(Schema schema, String schemaFile,
                                     boolean failOnError)
  private static List<Modification> loadSchemaFile(Schema schema,
                                                   String schemaFile,
                                                   boolean failOnError)
         throws ConfigException, InitializationException
  {
    // Create an LDIF reader to use when reading the files.
@@ -838,7 +848,7 @@
      {
        logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        return;
        return null;
      }
    }
@@ -852,7 +862,7 @@
      if (entry == null)
      {
        // The file was empty -- skip it.
        return;
        return new LinkedList<Modification>();
      }
    }
    catch (Exception e)
@@ -874,7 +884,7 @@
      {
        logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        return;
        return null;
      }
    }
@@ -892,6 +902,7 @@
    // Get the attributeTypes attribute from the entry.
    LinkedList<Modification> mods = new LinkedList<Modification>();
    AttributeTypeSyntax attrTypeSyntax;
    try
    {
@@ -924,6 +935,13 @@
    }
    List<Attribute> attrList = entry.getAttribute(attributeAttrType);
    if ((attrList != null) && (! attrList.isEmpty()))
    {
      for (Attribute a : attrList)
      {
        mods.add(new Modification(ModificationType.ADD, a.duplicate()));
      }
    }
    // Get the objectClasses attribute from the entry.
@@ -958,6 +976,13 @@
    }
    List<Attribute> ocList = entry.getAttribute(objectclassAttrType);
    if ((ocList != null) && (! ocList.isEmpty()))
    {
      for (Attribute a : ocList)
      {
        mods.add(new Modification(ModificationType.ADD, a.duplicate()));
      }
    }
    // Get the name forms attribute from the entry.
@@ -991,6 +1016,13 @@
    }
    List<Attribute> nfList = entry.getAttribute(nameFormAttrType);
    if ((nfList != null) && (! nfList.isEmpty()))
    {
      for (Attribute a : nfList)
      {
        mods.add(new Modification(ModificationType.ADD, a.duplicate()));
      }
    }
    // Get the DIT content rules attribute from the entry.
@@ -1026,6 +1058,13 @@
    }
    List<Attribute> dcrList = entry.getAttribute(dcrAttrType);
    if ((dcrList != null) && (! dcrList.isEmpty()))
    {
      for (Attribute a : dcrList)
      {
        mods.add(new Modification(ModificationType.ADD, a.duplicate()));
      }
    }
    // Get the DIT structure rules attribute from the entry.
@@ -1061,6 +1100,13 @@
    }
    List<Attribute> dsrList = entry.getAttribute(dsrAttrType);
    if ((dsrList != null) && (! dsrList.isEmpty()))
    {
      for (Attribute a : dsrList)
      {
        mods.add(new Modification(ModificationType.ADD, a.duplicate()));
      }
    }
    // Get the matching rule uses attribute from the entry.
@@ -1096,6 +1142,14 @@
    }
    List<Attribute> mruList = entry.getAttribute(mruAttrType);
    if ((mruList != null) && (! mruList.isEmpty()))
    {
      for (Attribute a : mruList)
      {
        mods.add(new Modification(ModificationType.ADD, a.duplicate()));
      }
    }
    AttributeType synchronizationStateType =
      schema.getAttributeType(ATTR_SYNCHRONIZATION_STATE_LC);
@@ -1124,6 +1178,8 @@
          {
            attrType = attrTypeSyntax.decodeAttributeType(v.getValue(),
                                                          schema, false);
            attrType.setExtraProperty(SCHEMA_PROPERTY_FILENAME, (String) null);
            attrType.setSchemaFile(schemaFile);
          }
          catch (DirectoryException de)
          {
@@ -1221,6 +1277,8 @@
          try
          {
            oc = ocSyntax.decodeObjectClass(v.getValue(), schema, false);
            oc.setExtraProperty(SCHEMA_PROPERTY_FILENAME, (String) null);
            oc.setSchemaFile(schemaFile);
          }
          catch (DirectoryException de)
          {
@@ -1702,6 +1760,9 @@
        }
      }
    }
    return mods;
  }
opends/src/server/org/opends/server/messages/BackendMessages.java
@@ -3181,6 +3181,41 @@
  /**
   * The message ID for the message that will be used if the schema backend is
   * unable to find a file containing the concatenated schema definitions.  This
   * takes three arguments, which are the path to the directory in which the
   * file should have been found, the name of the most recent concatenated
   * schema file, and the name of the base concatenated schema file shipped with
   * the server.
   */
  public static final int MSGID_SCHEMA_CANNOT_FIND_CONCAT_FILE =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 294;
  /**
   * The message ID for the message that will be used if an error occurs while
   * attempting determine whether there were any changes made to the server
   * schema while the server was offline.  This takes a single argument, which
   * is a string representation of the exception that was caught.
   */
  public static final int MSGID_SCHEMA_ERROR_DETERMINING_SCHEMA_CHANGES =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 295;
  /**
   * The message ID for the message that will be used if an error occurs while
   * trying to write a file containing the concatenated server schema.  This
   * takes two arguments, which are the path to the file being written and a
   * string representation of the exception that was caught.
   */
  public static final int MSGID_SCHEMA_CANNOT_WRITE_CONCAT_SCHEMA_FILE =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 296;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -3401,6 +3436,22 @@
                    " attribute of configuration entry %s:  %s.  The default " +
                    "behavior, which is to treat the attribute types as " +
                    "defined in the server schema, will be used.");
    registerMessage(MSGID_SCHEMA_CANNOT_FIND_CONCAT_FILE,
                    "Unable to find a file containing concatenated schema " +
                    "element definitions in order to determine if any schema " +
                    "changes were made with the server offline.  The " +
                    "file was expected in the %s directory and should have " +
                    "been named either %s or %s.");
    registerMessage(MSGID_SCHEMA_ERROR_DETERMINING_SCHEMA_CHANGES,
                    "An error occurred while attempting to determine whether " +
                    "any schema changes had been made by directly editing " +
                    "the schema files with the server offline:  %s.");
    registerMessage(MSGID_SCHEMA_CANNOT_WRITE_CONCAT_SCHEMA_FILE,
                    "An error occurred while attempting to write file %s " +
                    "containing a concatenated list of all server schema " +
                    "elements:  %s.  The server may not be able to " +
                    "accurately identify any schema changes made with the " +
                    "server offline.");
    registerMessage(MSGID_SCHEMA_ADD_NOT_SUPPORTED,
                    "Unwilling to add entry \"%s\" because add operations " +
                    "are not supported in the schema backend.");
opends/src/server/org/opends/server/messages/TaskMessages.java
@@ -202,6 +202,18 @@
  /**
   * The message ID for the shutdown message that will be used if an error
   * occurs while trying to notify a synchronization provider about the new
   * schema elements added to the server.  This takes two arguments, which are
   * the name of the synchronization provider class and a string representation
   * of the exception that was caught.
   */
  public static final int MSGID_TASK_ADDSCHEMAFILE_CANNOT_NOTIFY_SYNC_PROVIDER =
       CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 17;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -245,6 +257,10 @@
    registerMessage(MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE,
                    "An error occurred while attempting to load the contents " +
                    "of schema file %s into the server schema:  %s.");
    registerMessage(MSGID_TASK_ADDSCHEMAFILE_CANNOT_NOTIFY_SYNC_PROVIDER,
                    "An error occurred while attempting to notify a " +
                    "synchronization provider of type %s about the schema " +
                    "changes made by the add schema file task:  %s.");
    registerMessage(MSGID_TASK_ADDSCHEMAFILE_CANNOT_LOCK_SCHEMA,
                    "Unable to add one or more files to the server schema " +
                    "because the server was unable to obtain a write lock on " +
opends/src/server/org/opends/server/synchronization/plugin/MultimasterSynchronization.java
@@ -120,6 +120,17 @@
        createNewSynchronizationDomain(domainEntry);
      }
    }
    /*
     * If any schema changes were made with the server offline, then handle them
     * now.
     */
    List<Modification> offlineSchemaChanges =
         DirectoryServer.getOfflineSchemaChanges();
    if ((offlineSchemaChanges != null) && (! offlineSchemaChanges.isEmpty()))
    {
      processSchemaChange(offlineSchemaChanges);
    }
  }
  /**
@@ -548,7 +559,7 @@
   *                                      applied to the schema.
   *
   */
  public static void schemaChangeNotification(List<Modification> modifications)
  public void processSchemaChange(List<Modification> modifications)
  {
    SynchronizationDomain domain =
      findDomain(DirectoryServer.getSchemaDN(), null);
opends/src/server/org/opends/server/tasks/AddSchemaFileTask.java
@@ -29,11 +29,14 @@
import java.io.File;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;
import java.util.concurrent.locks.Lock;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.SynchronizationProvider;
import org.opends.server.backends.task.Task;
import org.opends.server.backends.task.TaskState;
import org.opends.server.config.ConfigException;
@@ -43,6 +46,7 @@
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
@@ -50,13 +54,16 @@
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LockManager;
import org.opends.server.types.Modification;
import org.opends.server.types.Privilege;
import org.opends.server.types.ResultCode;
import org.opends.server.types.Schema;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.TaskMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
@@ -68,9 +75,6 @@
public class AddSchemaFileTask
       extends Task
{
  // The list of files to be added to the server schema.
  TreeSet<String> filesToAdd;
@@ -139,6 +143,11 @@
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
          int    msgID   = MSGID_TASK_ADDSCHEMAFILE_ERROR_CHECKING_FOR_FILE;
          String message = getMessage(msgID, filename, schemaDirectory,
                                      stackTraceToSingleLineString(e));
@@ -163,6 +172,11 @@
      }
      catch (ConfigException ce)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, ce);
        }
        int    msgID   = MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE;
        String message = getMessage(msgID, String.valueOf(schemaFile),
                                    ce.getMessage());
@@ -171,6 +185,11 @@
      }
      catch (InitializationException ie)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, ie);
        }
        int    msgID   = MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE;
        String message = getMessage(msgID, String.valueOf(schemaFile),
                                    ie.getMessage());
@@ -207,15 +226,53 @@
    try
    {
      LinkedList<Modification> mods = new LinkedList<Modification>();
      Schema schema = DirectoryServer.getSchema().duplicate();
      for (String schemaFile : filesToAdd)
      {
        try
        {
          SchemaConfigManager.loadSchemaFile(schema, schemaFile);
          List<Modification> modList =
               SchemaConfigManager.loadSchemaFile(schema, schemaFile);
          for (Modification m : modList)
          {
            Attribute a = m.getAttribute();
            LinkedHashSet<AttributeValue> valuesWithFileElement =
                 new LinkedHashSet<AttributeValue>();
            for (AttributeValue v : a.getValues())
            {
              String s = v.getStringValue();
              if (s.indexOf(SCHEMA_PROPERTY_FILENAME) < 0)
              {
                if (s.endsWith(" )"))
                {
                 s = s.substring(0, s.length()-1) + SCHEMA_PROPERTY_FILENAME +
                     " '" + schemaFile + "' )";
                }
                else if (s.endsWith(")"))
                {
                 s = s.substring(0, s.length()-1) + " " +
                     SCHEMA_PROPERTY_FILENAME + " '" + schemaFile + "' )";
                }
              }
              valuesWithFileElement.add(new AttributeValue(a.getAttributeType(),
                                                           s));
            }
            Attribute attrWithFile = new Attribute(a.getAttributeType(),
                                                   a.getName(),
                                                   valuesWithFileElement);
            mods.add(new Modification(m.getModificationType(), attrWithFile));
          }
        }
        catch (ConfigException ce)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, ce);
          }
          int    msgID   = MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE;
          String message = getMessage(msgID, String.valueOf(schemaFile),
                                      ce.getMessage());
@@ -225,6 +282,11 @@
        }
        catch (InitializationException ie)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, ie);
          }
          int    msgID   = MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE;
          String message = getMessage(msgID, String.valueOf(schemaFile),
                                      ie.getMessage());
@@ -234,6 +296,33 @@
        }
      }
      if (! mods.isEmpty())
      {
        for (SynchronizationProvider provider :
             DirectoryServer.getSynchronizationProviders())
        {
          try
          {
            provider.processSchemaChange(mods);
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, e);
            }
            int msgID = MSGID_TASK_ADDSCHEMAFILE_CANNOT_NOTIFY_SYNC_PROVIDER;
            String message = getMessage(msgID, provider.getClass().getName(),
                                        stackTraceToSingleLineString(e));
            logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR,
                     message, msgID);
          }
        }
        Schema.writeConcatenatedSchema();
      }
      schema.setYoungestModificationTime(System.currentTimeMillis());
      DirectoryServer.setSchema(schema);
      return TaskState.COMPLETED_SUCCESSFULLY;
opends/src/server/org/opends/server/types/AttributeType.java
@@ -40,10 +40,7 @@
import org.opends.server.core.DirectoryServer;
import org.opends.server.schema.AttributeTypeSyntax;
import static
    org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static
    org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.messages.CoreMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
@@ -70,9 +67,6 @@
       extends CommonSchemaElements
       implements SchemaFileElement
{
  // The approximate matching rule for this attribute type.
  private final ApproximateMatchingRule approximateMatchingRule;
@@ -267,7 +261,6 @@
    ensureNotNull(definition, oid);
    this.definition   = definition;
    this.superiorType = superiorType;
    this.isCollective = isCollective;
    this.isNoUserModification = isNoUserModification;
@@ -275,6 +268,36 @@
    mayHaveSubordinateTypes = false;
    int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME);
    if (schemaFilePos > 0)
    {
      String defStr;
      try
      {
        int firstQuotePos = definition.indexOf('\'', schemaFilePos);
        int secondQuotePos = definition.indexOf('\'',
                                                firstQuotePos+1);
        defStr = definition.substring(0, schemaFilePos).trim() + " " +
                 definition.substring(secondQuotePos+1).trim();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        defStr = definition;
      }
      this.definition = defStr;
    }
    else
    {
      this.definition = definition;
    }
    if (syntax == null)
    {
      if (superiorType != null)
opends/src/server/org/opends/server/types/DITContentRule.java
@@ -38,6 +38,7 @@
import org.opends.server.schema.DITContentRuleSyntax;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.messages.CoreMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
@@ -55,9 +56,6 @@
public final class DITContentRule
       implements SchemaFileElement
{
  // Indicates whether this content rule is declared "obsolete".
  private final boolean isObsolete;
@@ -133,11 +131,40 @@
  {
    ensureNotNull(definition, structuralClass);
    this.definition      = definition;
    this.structuralClass = structuralClass;
    this.description     = description;
    this.isObsolete      = isObsolete;
    int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME);
    if (schemaFilePos > 0)
    {
      String defStr;
      try
      {
        int firstQuotePos = definition.indexOf('\'', schemaFilePos);
        int secondQuotePos = definition.indexOf('\'',
                                                firstQuotePos+1);
        defStr = definition.substring(0, schemaFilePos).trim() + " " +
                 definition.substring(secondQuotePos+1).trim();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        defStr = definition;
      }
      this.definition = defStr;
    }
    else
    {
      this.definition = definition;
    }
    if ((names == null) || names.isEmpty())
    {
      this.names = new LinkedHashMap<String,String>(0);
opends/src/server/org/opends/server/types/DITStructureRule.java
@@ -38,6 +38,7 @@
import org.opends.server.schema.DITStructureRuleSyntax;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.messages.CoreMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
@@ -53,9 +54,6 @@
public final class DITStructureRule
       implements SchemaFileElement
{
  // Indicates whether this DIT structure rule is declared "obsolete".
  private final boolean isObsolete;
@@ -113,12 +111,41 @@
  {
    ensureNotNull(definition);
    this.definition  = definition;
    this.ruleID      = ruleID;
    this.description = description;
    this.isObsolete  = isObsolete;
    this.nameForm    = nameForm;
    int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME);
    if (schemaFilePos > 0)
    {
      String defStr;
      try
      {
        int firstQuotePos = definition.indexOf('\'', schemaFilePos);
        int secondQuotePos = definition.indexOf('\'',
                                                firstQuotePos+1);
        defStr = definition.substring(0, schemaFilePos).trim() + " " +
                 definition.substring(secondQuotePos+1).trim();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        defStr = definition;
      }
      this.definition = defStr;
    }
    else
    {
      this.definition = definition;
    }
    if ((names == null) || names.isEmpty())
    {
      this.names = new LinkedHashMap<String,String>(0);
opends/src/server/org/opends/server/types/MatchingRuleUse.java
@@ -39,6 +39,7 @@
import org.opends.server.api.MatchingRule;
import org.opends.server.schema.MatchingRuleUseSyntax;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.util.Validator.*;
@@ -54,9 +55,6 @@
public final class MatchingRuleUse
       implements SchemaFileElement
{
  // Indicates whether this matching rule use is declared "obsolete".
  private final boolean isObsolete;
@@ -113,11 +111,40 @@
  {
    ensureNotNull(definition, matchingRule);
    this.definition   = definition;
    this.matchingRule = matchingRule;
    this.description  = description;
    this.isObsolete   = isObsolete;
    int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME);
    if (schemaFilePos > 0)
    {
      String defStr;
      try
      {
        int firstQuotePos = definition.indexOf('\'', schemaFilePos);
        int secondQuotePos = definition.indexOf('\'',
                                                firstQuotePos+1);
        defStr = definition.substring(0, schemaFilePos).trim() + " " +
                 definition.substring(secondQuotePos+1).trim();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        defStr = definition;
      }
      this.definition = defStr;
    }
    else
    {
      this.definition = definition;
    }
    if ((names == null) || names.isEmpty())
    {
      this.names = new LinkedHashMap<String,String>(0);
opends/src/server/org/opends/server/types/NameForm.java
@@ -38,6 +38,7 @@
import org.opends.server.schema.NameFormSyntax;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.util.Validator.*;
@@ -53,9 +54,6 @@
public final class NameForm
       implements SchemaFileElement
{
  // Indicates whether this name form is declared "obsolete".
  private final boolean isObsolete;
@@ -119,12 +117,41 @@
  {
    ensureNotNull(definition, oid, structuralClass);
    this.definition      = definition;
    this.oid             = oid;
    this.description     = description;
    this.isObsolete      = isObsolete;
    this.structuralClass = structuralClass;
    int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME);
    if (schemaFilePos > 0)
    {
      String defStr;
      try
      {
        int firstQuotePos = definition.indexOf('\'', schemaFilePos);
        int secondQuotePos = definition.indexOf('\'',
                                                firstQuotePos+1);
        defStr = definition.substring(0, schemaFilePos).trim() + " " +
                 definition.substring(secondQuotePos+1).trim();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        defStr = definition;
      }
      this.definition = defStr;
    }
    else
    {
      this.definition = definition;
    }
    if ((names == null) || names.isEmpty())
    {
      this.names = new LinkedHashMap<String,String>(0);
opends/src/server/org/opends/server/types/ObjectClass.java
@@ -39,6 +39,7 @@
import org.opends.server.schema.ObjectClassSyntax;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.Validator.*;
@@ -63,7 +64,6 @@
       extends CommonSchemaElements
       implements SchemaFileElement
{
  // The set of optional attribute types for this objectclass.
  private final Set<AttributeType> optionalAttributes;
@@ -150,9 +150,38 @@
    ensureNotNull(definition, oid);
    this.definition    = definition;
    this.superiorClass = superiorClass;
    int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME);
    if (schemaFilePos > 0)
    {
      String defStr;
      try
      {
        int firstQuotePos = definition.indexOf('\'', schemaFilePos);
        int secondQuotePos = definition.indexOf('\'',
                                                firstQuotePos+1);
        defStr = definition.substring(0, schemaFilePos).trim() + " " +
                 definition.substring(secondQuotePos+1).trim();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        defStr = definition;
      }
      this.definition = defStr;
    }
    else
    {
      this.definition = definition;
    }
    // Set flag indicating whether or not this object class allows any
    // attributes.
    if (hasName(OC_EXTENSIBLE_OBJECT_LC)
opends/src/server/org/opends/server/types/Schema.java
@@ -28,10 +28,17 @@
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import org.opends.server.api.ApproximateMatchingRule;
@@ -40,10 +47,17 @@
import org.opends.server.api.MatchingRule;
import org.opends.server.api.OrderingMatchingRule;
import org.opends.server.api.SubstringMatchingRule;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.SchemaConfigManager;
import org.opends.server.protocols.asn1.ASN1OctetString;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.loggers.Error.*;
import static org.opends.server.messages.BackendMessages.*;
import static org.opends.server.messages.CoreMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
@@ -2822,5 +2836,434 @@
  {
    synchronizationState = values;
  }
  /**
   * Writes a single file containing all schema element definitions,
   * which can be used on startup to determine whether the schema
   * files were edited with the server offline.
   */
  public static void writeConcatenatedSchema()
  {
    String concatFilePath = null;
    try
    {
      LinkedHashSet<String> attributeTypes =
           new LinkedHashSet<String>();
      LinkedHashSet<String> objectClasses =
           new LinkedHashSet<String>();
      LinkedHashSet<String> nameForms = new LinkedHashSet<String>();
      LinkedHashSet<String> ditContentRules =
           new LinkedHashSet<String>();
      LinkedHashSet<String> ditStructureRules =
           new LinkedHashSet<String>();
      LinkedHashSet<String> matchingRuleUses =
           new LinkedHashSet<String>();
      genConcatenatedSchema(attributeTypes, objectClasses, nameForms,
                            ditContentRules, ditStructureRules,
                            matchingRuleUses);
      File configFile = new File(DirectoryServer.getConfigFile());
      File configDirectory  = configFile.getParentFile();
      File upgradeDirectory = new File(configDirectory, "upgrade");
      File concatFile       = new File(upgradeDirectory,
                                       SCHEMA_CONCAT_FILE_NAME);
      concatFilePath = concatFile.getAbsolutePath();
      File tempFile = new File(concatFilePath + ".tmp");
      BufferedWriter writer =
           new BufferedWriter(new FileWriter(tempFile, false));
      writer.write("dn: " + DirectoryServer.getSchemaDN().toString());
      writer.newLine();
      writer.write("objectClass: top");
      writer.newLine();
      writer.write("objectClass: ldapSubentry");
      writer.newLine();
      writer.write("objectClass: subschema");
      writer.newLine();
      for (String line : attributeTypes)
      {
        writer.write(ATTR_ATTRIBUTE_TYPES);
        writer.write(": ");
        writer.write(line);
        writer.newLine();
      }
      for (String line : objectClasses)
      {
        writer.write(ATTR_OBJECTCLASSES);
        writer.write(": ");
        writer.write(line);
        writer.newLine();
      }
      for (String line : nameForms)
      {
        writer.write(ATTR_NAME_FORMS);
        writer.write(": ");
        writer.write(line);
        writer.newLine();
      }
      for (String line : ditContentRules)
      {
        writer.write(ATTR_DIT_CONTENT_RULES);
        writer.write(": ");
        writer.write(line);
        writer.newLine();
      }
      for (String line : ditStructureRules)
      {
        writer.write(ATTR_DIT_STRUCTURE_RULES);
        writer.write(": ");
        writer.write(line);
        writer.newLine();
      }
      for (String line : matchingRuleUses)
      {
        writer.write(ATTR_MATCHING_RULE_USE);
        writer.write(": ");
        writer.write(line);
        writer.newLine();
      }
      writer.close();
      if (concatFile.exists())
      {
        concatFile.delete();
      }
      tempFile.renameTo(concatFile);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      // This is definitely not ideal, but it's not the end of the
      // world.  The worst that should happen is that the schema
      // changes could potentially be sent to the other servers again
      // when this server is restarted, which shouldn't hurt anything.
      // Still, we should log a warning message.
      logError(ErrorLogCategory.SCHEMA,
               ErrorLogSeverity.SEVERE_WARNING,
               MSGID_SCHEMA_CANNOT_WRITE_CONCAT_SCHEMA_FILE,
               String.valueOf(concatFilePath),
               stackTraceToSingleLineString(e));
    }
  }
  /**
   * Reads the files contained in the schema directory and generates a
   * concatenated view of their contents in the provided sets.
   *
   * @param  attributeTypes     The set into which to place the
   *                            attribute types read from the schema
   *                            files.
   * @param  objectClasses      The set into which to place the object
   *                            classes read from the schema files.
   * @param  nameForms          The set into which to place the name
   *                            forms read from the schema files.
   * @param  ditContentRules    The set into which to place the DIT
   *                            content rules read from the schema
   *                            files.
   * @param  ditStructureRules  The set into which to place the DIT
   *                            structure rules read from the schema
   *                            files.
   * @param  matchingRuleUses   The set into which to place the
   *                            matching rule uses read from the
   *                            schema files.
   *
   * @throws  IOException  If a problem occurs while reading the
   *                       schema file elements.
   */
  public static void genConcatenatedSchema(
                          LinkedHashSet<String> attributeTypes,
                          LinkedHashSet<String> objectClasses,
                          LinkedHashSet<String> nameForms,
                          LinkedHashSet<String> ditContentRules,
                          LinkedHashSet<String> ditStructureRules,
                          LinkedHashSet<String> matchingRuleUses)
          throws IOException
  {
    // Get a sorted list of the files in the schema directory.
    String schemaDirectory =
                SchemaConfigManager.getSchemaDirectoryPath();
    TreeSet<String> schemaFileNames = new TreeSet<String>();
    for (File f : new File(schemaDirectory).listFiles())
    {
      if (f.isFile())
      {
        schemaFileNames.add(f.getName());
      }
    }
    // Open each of the files in order and read the elements that they
    // contain, appending them to the appropriate lists.
    for (String name : schemaFileNames)
    {
      // Read the contents of the file into a list with one schema
      // element per list element.
      LinkedList<StringBuilder> lines =
           new LinkedList<StringBuilder>();
      BufferedReader reader =
           new BufferedReader(new FileReader(
                    new File(schemaDirectory, name)));
      while (true)
      {
        String line = reader.readLine();
        if (line == null)
        {
          break;
        }
        else if (line.startsWith("#") || (line.length() == 0))
        {
          continue;
        }
        else if (line.startsWith(" "))
        {
          lines.getLast().append(line.substring(1));
        }
        else
        {
          lines.add(new StringBuilder(line));
        }
      }
      reader.close();
      // Iterate through each line in the list.  Find the colon and
      // get the attribute name at the beginning.  If it's someting
      // that we don't recognize, then skip it.  Otherwise, add the
      // X-SCHEMA-FILE extension and add it to the appropriate schema
      // element list.
      for (StringBuilder buffer : lines)
      {
        // Get the line and add the X-SCHEMA-FILE extension to the end
        // of it.  All of them should end with " )" but some might
        // have the parenthesis crammed up against the last character
        // so deal with that as well.
        String line = buffer.toString().trim();
        if (line.endsWith(" )"))
        {
         line = line.substring(0, line.length()-1) +
                SCHEMA_PROPERTY_FILENAME + " '" + name + "' )";
        }
        else if (line.endsWith(")"))
        {
         line = line.substring(0, line.length()-1) + " " +
                SCHEMA_PROPERTY_FILENAME + " '" + name + "' )";
        }
        else
        {
          continue;
        }
        String value;
        String lowerLine = toLowerCase(line);
        if (lowerLine.startsWith(ATTR_ATTRIBUTE_TYPES_LC))
        {
          value = line.substring(
                       ATTR_ATTRIBUTE_TYPES.length()+1).trim();
          attributeTypes.add(value);
        }
        else if (lowerLine.startsWith(ATTR_OBJECTCLASSES_LC))
        {
          value = line.substring(
                       ATTR_OBJECTCLASSES.length()+1).trim();
          objectClasses.add(value);
        }
        else if (lowerLine.startsWith(ATTR_NAME_FORMS_LC))
        {
          value = line.substring(ATTR_NAME_FORMS.length()+1).trim();
          nameForms.add(value);
        }
        else if (lowerLine.startsWith(ATTR_DIT_CONTENT_RULES_LC))
        {
          value = line.substring(
                     ATTR_DIT_CONTENT_RULES.length()+1).trim();
          ditContentRules.add(value);
        }
        else if (lowerLine.startsWith(ATTR_DIT_STRUCTURE_RULES_LC))
        {
          value = line.substring(
                     ATTR_DIT_STRUCTURE_RULES.length()+1).trim();
          ditStructureRules.add(value);
        }
        else if (lowerLine.startsWith(ATTR_MATCHING_RULE_USE_LC))
        {
          value = line.substring(
                     ATTR_MATCHING_RULE_USE.length()+1).trim();
          matchingRuleUses.add(value);
        }
      }
    }
  }
  /**
   * Reads data from the specified concatenated schema file into the
   * provided sets.
   *
   * @param  concatSchemaFile   The path to the concatenated schema
   *                            file to be read.
   * @param  attributeTypes     The set into which to place the
   *                            attribute types read from the
   *                            concatenated schema file.
   * @param  objectClasses      The set into which to place the object
   *                            classes read from the concatenated
   *                            schema file.
   * @param  nameForms          The set into which to place the name
   *                            forms read from the concatenated
   *                            schema file.
   * @param  ditContentRules    The set into which to place the DIT
   *                            content rules read from the
   *                            concatenated schema file.
   * @param  ditStructureRules  The set into which to place the DIT
   *                            structure rules read from the
   *                            concatenated schema file.
   * @param  matchingRuleUses   The set into which to place the
   *                            matching rule uses read from the
   *                            concatenated schema file.
   *
   * @throws  IOException  If a problem occurs while reading the
   *                       schema file elements.
   */
  public static void readConcatenatedSchema(String concatSchemaFile,
                          LinkedHashSet<String> attributeTypes,
                          LinkedHashSet<String> objectClasses,
                          LinkedHashSet<String> nameForms,
                          LinkedHashSet<String> ditContentRules,
                          LinkedHashSet<String> ditStructureRules,
                          LinkedHashSet<String> matchingRuleUses)
          throws IOException
  {
    BufferedReader reader =
         new BufferedReader(new FileReader(concatSchemaFile));
    while (true)
    {
      String line = reader.readLine();
      if (line == null)
      {
        break;
      }
      String value;
      String lowerLine = toLowerCase(line);
      if (lowerLine.startsWith(ATTR_ATTRIBUTE_TYPES_LC))
      {
        value =
             line.substring(ATTR_ATTRIBUTE_TYPES.length()+1).trim();
        attributeTypes.add(value);
      }
      else if (lowerLine.startsWith(ATTR_OBJECTCLASSES_LC))
      {
        value = line.substring(ATTR_OBJECTCLASSES.length()+1).trim();
        objectClasses.add(value);
      }
      else if (lowerLine.startsWith(ATTR_NAME_FORMS_LC))
      {
        value = line.substring(ATTR_NAME_FORMS.length()+1).trim();
        nameForms.add(value);
      }
      else if (lowerLine.startsWith(ATTR_DIT_CONTENT_RULES_LC))
      {
        value = line.substring(
                     ATTR_DIT_CONTENT_RULES.length()+1).trim();
        ditContentRules.add(value);
      }
      else if (lowerLine.startsWith(ATTR_DIT_STRUCTURE_RULES_LC))
      {
        value = line.substring(
                     ATTR_DIT_STRUCTURE_RULES.length()+1).trim();
        ditStructureRules.add(value);
      }
      else if (lowerLine.startsWith(ATTR_MATCHING_RULE_USE_LC))
      {
        value = line.substring(
                     ATTR_MATCHING_RULE_USE.length()+1).trim();
        matchingRuleUses.add(value);
      }
    }
    reader.close();
  }
  /**
   * Compares the provided sets of schema element definitions and
   * writes any differences found into the given list of
   * modifications.
   *
   * @param  oldElements  The set of elements of the specified type
   *                      read from the previous concatenated schema
   *                      files.
   * @param  newElements  The set of elements of the specified type
   *                      read from the server's current schema.
   * @param  elementType  The attribute type associated with the
   *                      schema element being compared.
   * @param  mods         The list of modifications into which any
   *                      identified differences should be written.
   */
  public static void compareConcatenatedSchema(
                          LinkedHashSet<String> oldElements,
                          LinkedHashSet<String> newElements,
                          AttributeType elementType,
                          LinkedList<Modification> mods)
  {
    AttributeType attributeTypesType =
         DirectoryServer.getAttributeType(ATTR_ATTRIBUTE_TYPES_LC,
                                          true);
    LinkedHashSet<AttributeValue> values =
         new LinkedHashSet<AttributeValue>();
    for (String s : oldElements)
    {
      if (! newElements.contains(s))
      {
        values.add(new AttributeValue(attributeTypesType, s));
      }
    }
    if (! values.isEmpty())
    {
      mods.add(new Modification(ModificationType.DELETE,
                        new Attribute(elementType,
                                      elementType.getNameOrOID(),
                                      values)));
    }
    values.clear();
    for (String s : newElements)
    {
      if (! oldElements.contains(s))
      {
        values.add(new AttributeValue(attributeTypesType, s));
      }
    }
    if (! values.isEmpty())
    {
      mods.add(new Modification(ModificationType.ADD,
                        new Attribute(elementType,
                                      elementType.getNameOrOID(),
                                      values)));
    }
  }
}
opends/src/server/org/opends/server/util/ServerConstants.java
@@ -2232,6 +2232,15 @@
  /**
   * The name of the system property that can be used to determine whether the
   * Directory Server is starting up for the purpose of running the unit tests.
   */
  public static final String PROPERTY_RUNNING_UNIT_TESTS =
       "org.opends.server.RunningUnitTests";
  /**
   * The name of the system property that can be used to specify the path to the
   * directory in which the schema configuration files may be found.  If this is
   * not set, then the server wiill use a directory named "schema" below the
@@ -2281,5 +2290,25 @@
   * The column at which to wrap long lines of output in the command-line tools.
   */
  public static final int MAX_LINE_WIDTH = 79;
  /**
   * The name that should be used for the file to which the latest complete
   * schema data should be concatenated.
   */
  public static final String SCHEMA_CONCAT_FILE_NAME = "schema.ldif.current";
  /**
   * The name that should be used for the concatenated schema file generated at
   * build time with the base schema for the Subversion revision on which the
   * current build is based.  The value of
   * {@code DynamicConstants.REVISION_NUMBER} must be appended to this value in
   * order to get the full name.
   */
  public static final String SCHEMA_BASE_FILE_NAME_WITHOUT_REVISION  =
       "schema.ldif.";
}
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SchemaSynchronizationTest.java
@@ -38,6 +38,7 @@
import java.util.List;
import org.opends.server.TestCaseUtils;
import org.opends.server.api.SynchronizationProvider;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.Operation;
@@ -276,7 +277,11 @@
    Modification mod = new Modification(ModificationType.ADD, attr);
    mods.add(mod);
    MultimasterSynchronization.schemaChangeNotification(mods);
    for (SynchronizationProvider provider :
         DirectoryServer.getSynchronizationProviders())
    {
      provider.processSchemaChange(mods);
    }
    // receive the message on the broker side.
    SynchronizationMessage msg = broker.receive();