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

neil_a_wilson
30.11.2007 f386801829fe0295a232ad8af4785ba60d2be044
Add a new task that may be used to cause the server to load the contents of a
new file into the schema without the need to restart.

OpenDS Issue Number: 367
2 files added
5 files modified
1068 ■■■■■ changed files
opendj-sdk/opends/resource/schema/02-config.ldif 6 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java 37 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java 6 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/core/SchemaConfigManager.java 216 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/messages/TaskMessages.java 83 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/tasks/AddSchemaFileTask.java 238 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/AddSchemaFileTaskTestCase.java 482 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/resource/schema/02-config.ldif
@@ -1028,6 +1028,9 @@
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.301
  NAME 'ds-cfg-reject-unauthenticated-requests' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
  SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.302
  NAME 'ds-task-schema-file-name' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
  MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -1409,4 +1412,7 @@
  MAY ( ds-connectionhandler-connection $ ds-connectionhandler-listener $
  ds-connectionhandler-num-connections $ ds-connectionhandler-protocol )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.80 NAME 'ds-task-add-schema-file'
  SUP ds-task MUST ds-task-schema-file-name
  X-ORIGIN 'OpenDS Directory Server' )
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -1693,5 +1693,42 @@
    return scheduledTaskParentDN;
  }
  /**
   * Retrieves the scheduled task for the entry with the provided DN.
   *
   * @param  taskEntryDN  The DN of the entry for the task to retrieve.
   *
   * @return  The requested task, or {@code null} if there is no task with the
   *          specified entry DN.
   */
  public Task getScheduledTask(DN taskEntryDN)
  {
    assert debugEnter(CLASS_NAME, "getScheduledTask",
                      String.valueOf(taskEntryDN));
    return taskScheduler.getScheduledTask(taskEntryDN);
  }
  /**
   * Retrieves the recurring task for the entry with the provided DN.
   *
   * @param  taskEntryDN  The DN of the entry for the recurring task to
   *                      retrieve.
   *
   * @return  The requested recurring task, or {@code null} if there is no task
   *          with the specified entry DN.
   */
  public RecurringTask getRecurringTask(DN taskEntryDN)
  {
    assert debugEnter(CLASS_NAME, "getScheduledTask",
                      String.valueOf(taskEntryDN));
    return taskScheduler.getRecurringTask(taskEntryDN);
  }
}
opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
@@ -3730,5 +3730,11 @@
  /**
   * The name of the attribute in the add schema file task definition that
   * specifies the name of the schema file to be added.
   */
  public static final String ATTR_TASK_ADDSCHEMAFILE_FILENAME =
       NAME_PREFIX_TASK + "schema-file-name";
}
opendj-sdk/opends/src/server/org/opends/server/core/SchemaConfigManager.java
@@ -623,19 +623,20 @@
  /**
   * Initializes all the attribute type and objectclass definitions by reading
   * the server schema files.  These files will be located in a single directory
   * and will be processed in alphabetic order.  However, to make the order
   * Initializes all the attribute type, object class, name form, DIT content
   * rule, DIT structure rule, and matching rule use definitions by reading the
   * server schema files.  These files will be located in a single directory and
   * will be processed in lexicographic order.  However, to make the order
   * easier to understand, they may be prefixed with a two digit number (with a
   * leading zero if necessary) so that they will be read in numeric order.
   * This should only be called at Directory Server startup.
   *
   * @throws  ConfigException  If a configuration problem causes the attribute
   *                           type or objectclass initialization to fail.
   * @throws  ConfigException  If a configuration problem causes the schema
   *                           element initialization to fail.
   *
   * @throws  InitializationException  If a problem occurs while initializing
   *                                   the attribute types/objectclasses that is
   *                                   not related to the server configuration.
   *                                   the schema elements that is not related
   *                                   to the server configuration.
   */
  public void initializeSchemaFromFiles()
         throws ConfigException, InitializationException
@@ -673,7 +674,7 @@
      {
        if (f.isFile())
        {
          fileList.add(f.getAbsolutePath());
          fileList.add(f.getName());
        }
        long modificationTime = f.lastModified();
@@ -732,11 +733,74 @@
    // from that entry and parse them to initialize the server schema.
    for (String schemaFile : fileNames)
    {
      loadSchemaFile(schema, schemaFile, false);
    }
  }
  /**
   * Loads the contents of the specified schema file into the provided schema.
   *
   * @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.
   *
   * @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 void loadSchemaFile(Schema schema, String schemaFile)
         throws ConfigException, InitializationException
  {
    assert debugEnter(CLASS_NAME, "loadSchemaFile", String.valueOf(schema),
                      String.valueOf(schemaFile));
    loadSchemaFile(schema, schemaFile, true);
  }
  /**
   * Loads the contents of the specified schema file into the provided schema.
   *
   * @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.
   * @param  failOnError  If {@code true}, indicates that this method should
   *                      throw an exception if certain kinds of errors occur.
   *                      If {@code false}, indicates that this method should
   *                      log an error message and return without an exception.
   *                      This should only be {@code false} when called from
   *                      {@code initializeSchemaFromFiles}.
   *
   * @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 void loadSchemaFile(Schema schema, String schemaFile,
                                     boolean failOnError)
         throws ConfigException, InitializationException
  {
    assert debugEnter(CLASS_NAME, "loadSchemaFile", String.valueOf(schema),
                      String.valueOf(schemaFile), String.valueOf(failOnError));
      // Create an LDIF reader to use when reading the files.
    String schemaDirPath = getSchemaDirectoryPath();
      LDIFReader reader;
      try
      {
        reader = new LDIFReader(new LDIFImportConfig(schemaFile));
      File f = new File(schemaDirPath, schemaFile);
      reader = new LDIFReader(new LDIFImportConfig(f.getAbsolutePath()));
      }
      catch (Exception e)
      {
@@ -745,9 +809,17 @@
        int    msgID   = MSGID_CONFIG_SCHEMA_CANNOT_OPEN_FILE;
        String message = getMessage(msgID, schemaFile, schemaDirPath,
                                    stackTraceToSingleLineString(e));
      if (failOnError)
      {
        throw new ConfigException(msgID, message);
      }
      else
      {
        logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
        return;
      }
      }
@@ -760,7 +832,7 @@
        if (entry == null)
        {
          // The file was empty -- skip it.
          continue;
        return;
        }
      }
      catch (Exception e)
@@ -770,9 +842,17 @@
        int    msgID   = MSGID_CONFIG_SCHEMA_CANNOT_READ_LDIF_ENTRY;
        String message = getMessage(msgID, schemaFile, schemaDirPath,
                                    stackTraceToSingleLineString(e));
      if (failOnError)
      {
        throw new InitializationException(msgID, message, e);
      }
      else
      {
        logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
        return;
      }
      }
      try
@@ -996,10 +1076,18 @@
              int    msgID   = MSGID_CONFIG_SCHEMA_CANNOT_PARSE_ATTR_TYPE;
              String message = getMessage(msgID, schemaFile,
                                          de.getErrorMessage());
            if (failOnError)
            {
              throw new ConfigException(msgID, message, de);
            }
            else
            {
              logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING,
                       message, msgID);
              continue;
            }
          }
            catch (Exception e)
            {
              assert debugException(CLASS_NAME, "initializeSchemaFromFiles", e);
@@ -1008,17 +1096,24 @@
              String message = getMessage(msgID, schemaFile,
                                          v.getStringValue() + ":  " +
                                               stackTraceToSingleLineString(e));
            if (failOnError)
            {
              throw new ConfigException(msgID, message, e);
            }
            else
            {
              logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING,
                       message, msgID);
              continue;
            }
          }
            // 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.
            try
            {
              schema.registerAttributeType(attrType, false);
            schema.registerAttributeType(attrType, failOnError);
            }
            catch (DirectoryException de)
            {
@@ -1028,6 +1123,7 @@
              int    msgID   = MSGID_CONFIG_SCHEMA_CONFLICTING_ATTR_TYPE;
              String message = getMessage(msgID, schemaFile,
                                          de.getErrorMessage());
              logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING,
                       message, msgID);
@@ -1038,8 +1134,7 @@
              catch (Exception e)
              {
                // This should never happen.
                assert debugException(CLASS_NAME, "initializeSchemaFromFiles",
                                      e);
              assert debugException(CLASS_NAME, "initializeSchemaFromFiles", e);
              }
            }
          }
@@ -1068,10 +1163,18 @@
              int    msgID   = MSGID_CONFIG_SCHEMA_CANNOT_PARSE_OC;
              String message = getMessage(msgID, schemaFile,
                                          de.getErrorMessage());
            if (failOnError)
            {
              throw new ConfigException(msgID, message, de);
            }
            else
            {
              logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING,
                       message, msgID);
              continue;
            }
          }
            catch (Exception e)
            {
              assert debugException(CLASS_NAME, "initializeSchemaFromFiles", e);
@@ -1080,17 +1183,25 @@
              String message = getMessage(msgID, schemaFile,
                                          v.getStringValue() + ":  " +
                                               stackTraceToSingleLineString(e));
            if (failOnError)
            {
              throw new ConfigException(msgID, message, e);
            }
            else
            {
              logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING,
                       message, msgID);
              continue;
            }
          }
            // 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.
            try
            {
              schema.registerObjectClass(oc, false);
            schema.registerObjectClass(oc, failOnError);
            }
            catch (DirectoryException de)
            {
@@ -1142,10 +1253,18 @@
              int    msgID   = MSGID_CONFIG_SCHEMA_CANNOT_PARSE_NAME_FORM;
              String message = getMessage(msgID, schemaFile,
                                          de.getErrorMessage());
            if (failOnError)
            {
              throw new ConfigException(msgID, message, de);
            }
            else
            {
              logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING,
                       message, msgID);
              continue;
            }
          }
            catch (Exception e)
            {
              assert debugException(CLASS_NAME, "initializeSchemaFromFiles", e);
@@ -1154,17 +1273,25 @@
              String message = getMessage(msgID, schemaFile,
                                          v.getStringValue() + ":  " +
                                               stackTraceToSingleLineString(e));
            if (failOnError)
            {
              throw new ConfigException(msgID, message, e);
            }
            else
            {
              logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING,
                       message, msgID);
              continue;
            }
          }
            // 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.
            try
            {
              schema.registerNameForm(nf, false);
            schema.registerNameForm(nf, failOnError);
            }
            catch (DirectoryException de)
            {
@@ -1216,10 +1343,18 @@
              int    msgID   = MSGID_CONFIG_SCHEMA_CANNOT_PARSE_DCR;
              String message = getMessage(msgID, schemaFile,
                                          de.getErrorMessage());
            if (failOnError)
            {
              throw new ConfigException(msgID, message, de);
            }
            else
            {
              logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING,
                       message, msgID);
              continue;
            }
          }
            catch (Exception e)
            {
              assert debugException(CLASS_NAME, "initializeSchemaFromFiles", e);
@@ -1228,17 +1363,25 @@
              String message = getMessage(msgID, schemaFile,
                                          v.getStringValue() + ":  " +
                                               stackTraceToSingleLineString(e));
            if (failOnError)
            {
              throw new ConfigException(msgID, message, e);
            }
            else
            {
              logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING,
                       message, msgID);
              continue;
            }
          }
            // 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.
            try
            {
              schema.registerDITContentRule(dcr, false);
            schema.registerDITContentRule(dcr, failOnError);
            }
            catch (DirectoryException de)
            {
@@ -1291,10 +1434,18 @@
              int    msgID   = MSGID_CONFIG_SCHEMA_CANNOT_PARSE_DSR;
              String message = getMessage(msgID, schemaFile,
                                          de.getErrorMessage());
            if (failOnError)
            {
              throw new ConfigException(msgID, message, de);
            }
            else
            {
              logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING,
                       message, msgID);
              continue;
            }
          }
            catch (Exception e)
            {
              assert debugException(CLASS_NAME, "initializeSchemaFromFiles", e);
@@ -1303,17 +1454,25 @@
              String message = getMessage(msgID, schemaFile,
                                          v.getStringValue() + ":  " +
                                               stackTraceToSingleLineString(e));
            if (failOnError)
            {
              throw new ConfigException(msgID, message, e);
            }
            else
            {
              logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING,
                       message, msgID);
              continue;
            }
          }
            // 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.
            try
            {
              schema.registerDITStructureRule(dsr, false);
            schema.registerDITStructureRule(dsr, failOnError);
            }
            catch (DirectoryException de)
            {
@@ -1366,10 +1525,18 @@
              int    msgID   = MSGID_CONFIG_SCHEMA_CANNOT_PARSE_MRU;
              String message = getMessage(msgID, schemaFile,
                                          de.getErrorMessage());
            if (failOnError)
            {
              throw new ConfigException(msgID, message, de);
            }
            else
            {
              logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING,
                       message, msgID);
              continue;
            }
          }
            catch (Exception e)
            {
              assert debugException(CLASS_NAME, "initializeSchemaFromFiles", e);
@@ -1378,17 +1545,25 @@
              String message = getMessage(msgID, schemaFile,
                                          v.getStringValue() + ":  " +
                                               stackTraceToSingleLineString(e));
            if (failOnError)
            {
              throw new ConfigException(msgID, message, e);
            }
            else
            {
              logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING,
                       message, msgID);
              continue;
            }
          }
            // 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.
            try
            {
              schema.registerMatchingRuleUse(mru, false);
            schema.registerMatchingRuleUse(mru, failOnError);
            }
            catch (DirectoryException de)
            {
@@ -1416,7 +1591,6 @@
        }
      }
    }
  }
opendj-sdk/opends/src/server/org/opends/server/messages/TaskMessages.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.messages;
@@ -62,7 +62,7 @@
   * own shutdown message.
   */
  public static final int MSGID_TASK_SHUTDOWN_DEFAULT_MESSAGE =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_INFORMATIONAL | 3;
       CATEGORY_MASK_TASK | SEVERITY_MASK_INFORMATIONAL | 3;
@@ -72,7 +72,62 @@
   * shutdown message.
   */
  public static final int MSGID_TASK_SHUTDOWN_CUSTOM_MESSAGE =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_INFORMATIONAL | 4;
       CATEGORY_MASK_TASK | SEVERITY_MASK_INFORMATIONAL | 4;
  /**
   * The message ID for the shutdown message that will be used if no schema file
   * names were provided.  This takes two arguments, which are the name of the
   * attribute and the DN of the entry in which the file names should have been
   * given.
   */
  public static final int MSGID_TASK_ADDSCHEMAFILE_NO_FILENAME =
       CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 5;
  /**
   * The message ID for the shutdown message that will be used if a specified
   * schema file does not exist in the schema directory.  This takes two
   * arguments, which are the name of the schema file and the path to the schema
   * directory.
   */
  public static final int MSGID_TASK_ADDSCHEMAFILE_NO_SUCH_FILE =
       CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 6;
  /**
   * The message ID for the shutdown message that will be used if an error
   * occurs while attempting to check for the existence of a schema file in the
   * schema directory.  This takes three arguments, which are the name of the
   * file, the path to the schema directory, and a string representation of the
   * exception that was caught.
   */
  public static final int MSGID_TASK_ADDSCHEMAFILE_ERROR_CHECKING_FOR_FILE =
       CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 7;
  /**
   * The message ID for the shutdown message that will be used if an error
   * occurs while trying to read and load the contents of a schema file into the
   * server schema.  This takes two arguments, which are the name of the schema
   * file and a message explaining the problem that occurred.
   */
  public static final int MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE =
       CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 8;
  /**
   * The message ID for the shutdown message that will be used if the server is
   * unable to obtain a write lock on the server schema.  This takes a single
   * argument, which is the DN of the schema entry.
   */
  public static final int MSGID_TASK_ADDSCHEMAFILE_CANNOT_LOCK_SCHEMA =
       CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 9;
@@ -94,5 +149,27 @@
    registerMessage(MSGID_TASK_SHUTDOWN_CUSTOM_MESSAGE,
                    "The Directory Server shutdown process has been " +
                    "initiated by task %s:  %s");
    registerMessage(MSGID_TASK_ADDSCHEMAFILE_NO_FILENAME,
                    "Unable to add one or more files to the server schema " +
                    "because no schema file names were provided in " +
                    "attribute %s of task entry %s.");
    registerMessage(MSGID_TASK_ADDSCHEMAFILE_NO_SUCH_FILE,
                    "Unable to add one or more files to the server schema " +
                    "because the specified schema file %s does not exist in " +
                    "schema directory %s.");
    registerMessage(MSGID_TASK_ADDSCHEMAFILE_ERROR_CHECKING_FOR_FILE,
                    "Unable to add one or more files to the server schema " +
                    "because an error occurred while attempting to determine " +
                    "whether file %s exists in schema directory %s:  %s.");
    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_LOCK_SCHEMA,
                    "Unable to add one or more files to the server schema " +
                    "because the server was unable to obtain a write lock on " +
                    "the schema entry %s after multiple attempts.");
  }
}
opendj-sdk/opends/src/server/org/opends/server/tasks/AddSchemaFileTask.java
New file
@@ -0,0 +1,238 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tasks;
import java.io.File;
import java.util.TreeSet;
import java.util.List;
import java.util.concurrent.locks.Lock;
import org.opends.server.backends.task.Task;
import org.opends.server.backends.task.TaskState;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.SchemaConfigManager;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LockManager;
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.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.TaskMessages.*;
import static org.opends.server.util.StaticUtils.*;
/**
 * This class provides an implementation of a Directory Server task that can be
 * used to add the contents of a new schema file into the server schema.
 */
public class AddSchemaFileTask
       extends Task
{
  /**
   * The fully-qualified name of this class for debugging purposes.
   */
  private static final String CLASS_NAME =
       "org.opends.server.tasks.AddSchemaFileTask";
  // The list of files to be added to the server schema.
  TreeSet<String> filesToAdd;
  /**
   * {@inheritDoc}
   */
  @Override
  public void initializeTask()
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "initializeTask");
    // Get the attribute that specifies which schema file(s) to add.
    Entry taskEntry = getTaskEntry();
    AttributeType attrType = DirectoryServer.getAttributeType(
                                  ATTR_TASK_ADDSCHEMAFILE_FILENAME, true);
    List<Attribute> attrList = taskEntry.getAttribute(attrType);
    if ((attrList == null) || attrList.isEmpty())
    {
      int    msgID   = MSGID_TASK_ADDSCHEMAFILE_NO_FILENAME;
      String message = getMessage(msgID, ATTR_TASK_ADDSCHEMAFILE_FILENAME,
                                  String.valueOf(taskEntry.getDN()));
      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
                                   msgID);
    }
    // Get the name(s) of the schema files to add and make sure they exist in
    // the schema directory.
    String schemaDirectory = SchemaConfigManager.getSchemaDirectoryPath();
    filesToAdd = new TreeSet<String>();
    for (Attribute a : attrList)
    {
      for (AttributeValue v  : a.getValues())
      {
        String filename = v.getStringValue();
        filesToAdd.add(filename);
        try
        {
          File schemaFile = new File(schemaDirectory, filename);
          if ((! schemaFile.exists()) ||
              (! schemaFile.getParent().equals(schemaDirectory)))
          {
            int    msgID   = MSGID_TASK_ADDSCHEMAFILE_NO_SUCH_FILE;
            String message = getMessage(msgID, filename, schemaDirectory);
            throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
                                         message, msgID);
          }
        }
        catch (Exception e)
        {
          int    msgID   = MSGID_TASK_ADDSCHEMAFILE_ERROR_CHECKING_FOR_FILE;
          String message = getMessage(msgID, filename, schemaDirectory,
                                      stackTraceToSingleLineString(e));
          throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
                                       message, msgID, e);
        }
      }
    }
    // 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
    // the entry immediately which will fail the attempt by the client to add it
    // to the server, rather than having to check its status after the fact.
    Schema schema = DirectoryServer.getSchema().duplicate();
    for (String schemaFile : filesToAdd)
    {
      try
      {
        SchemaConfigManager.loadSchemaFile(schema, schemaFile);
      }
      catch (ConfigException ce)
      {
        int    msgID   = MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE;
        String message = getMessage(msgID, String.valueOf(schemaFile),
                                    ce.getMessage());
        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                     message, msgID, ce);
      }
      catch (InitializationException ie)
      {
        int    msgID   = MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE;
        String message = getMessage(msgID, String.valueOf(schemaFile),
                                    ie.getMessage());
        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                     message, msgID, ie);
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  protected TaskState runTask()
  {
    assert debugEnter(CLASS_NAME, "runTask");
    // Obtain a write lock on the server schema so that we can be sure nothing
    // else tries to write to it at the same time.
    DN schemaDN = DirectoryServer.getSchemaDN();
    Lock schemaLock = LockManager.lockWrite(schemaDN);
    for (int i=0; ((schemaLock == null) && (i < 3)); i++)
    {
      schemaLock = LockManager.lockWrite(schemaDN);
    }
    if (schemaLock == null)
    {
      int    msgID   = MSGID_TASK_ADDSCHEMAFILE_CANNOT_LOCK_SCHEMA;
      String message = getMessage(msgID, String.valueOf(schemaDN));
      logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR, message,
               msgID);
      return TaskState.STOPPED_BY_ERROR;
    }
    try
    {
      Schema schema = DirectoryServer.getSchema().duplicate();
      for (String schemaFile : filesToAdd)
      {
        try
        {
          SchemaConfigManager.loadSchemaFile(schema, schemaFile);
        }
        catch (ConfigException ce)
        {
          int    msgID   = MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE;
          String message = getMessage(msgID, String.valueOf(schemaFile),
                                      ce.getMessage());
          logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR,
                   message, msgID);
          return TaskState.STOPPED_BY_ERROR;
        }
        catch (InitializationException ie)
        {
          int    msgID   = MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE;
          String message = getMessage(msgID, String.valueOf(schemaFile),
                                      ie.getMessage());
          logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR,
                   message, msgID);
          return TaskState.STOPPED_BY_ERROR;
        }
      }
      DirectoryServer.setSchema(schema);
      return TaskState.COMPLETED_SUCCESSFULLY;
    }
    finally
    {
      LockManager.unlock(schemaDN, schemaLock);
    }
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/AddSchemaFileTaskTestCase.java
New file
@@ -0,0 +1,482 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tasks;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeClass;
import org.opends.server.TestCaseUtils;
import org.opends.server.backends.SchemaTestMatchingRule;
import org.opends.server.backends.task.Task;
import org.opends.server.backends.task.TaskBackend;
import org.opends.server.backends.task.TaskState;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.SchemaConfigManager;
import org.opends.server.tools.LDAPModify;
import org.opends.server.types.DN;
import static org.testng.Assert.*;
/**
 * Tests invocation of the import and export tasks, but does not aim to
 * thoroughly test the underlying backend implementations.
 */
public class AddSchemaFileTaskTestCase
       extends TasksTestCase
{
  /**
   * Make sure that the Directory Server is running.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @BeforeClass()
  public void startServer()
         throws Exception
  {
    TestCaseUtils.startServer();
  }
  /**
   * Attempts to add a new file to the server schema where the file exists and
   * has valid contents.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddValidSchemaFile()
         throws Exception
  {
    SchemaTestMatchingRule matchingRule =
         new SchemaTestMatchingRule("testAddValidSchemaFileMatch",
                                    "1.3.6.1.4.1.26027.1.999.23");
    DirectoryServer.registerMatchingRule(matchingRule, false);
    String schemaDirectory = SchemaConfigManager.getSchemaDirectoryPath();
    String[] fileLines =
    {
      "dn: cn=schema",
      "objectClass: top",
      "objectClass: ldapSubentry",
      "objectClass: subschema",
      "attributeTypes: ( testaddvalidschemafileat-oid " +
           "NAME 'testAddValidSchemaFileAT' )",
      "objectClasses: ( testaddvalidschemafileoc-oid " +
           "NAME 'testAddValidSchemaFileOC' STRUCTURAL " +
           "MUST testAddValidSchemaFileAT )",
      "nameForms: ( testaddvalidschemafilenf-oid " +
           "NAME 'testAddValidSchemaFileNF' OC testAddValidSchemaFileOC " +
           "MUST testAddValidSchemaFileAT )",
      "dITContentRules: ( testaddvalidschemafileoc-oid " +
           "NAME 'testAddValidSchemaFileDCR' MAY description )",
      "dITStructureRules: ( 999016 NAME 'testAddValidSchemaFileDSR' " +
           "FORM testAddValidSchemaFileNF )",
      "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.23 " +
           "NAME 'testAddValidSchemaFileMRU' APPLIES testAddValidSchemaFileAT )"
    };
    File validFile = new File(schemaDirectory, "05-single-valid.ldif");
    BufferedWriter writer = new BufferedWriter(new FileWriter(validFile));
    for (String line : fileLines)
    {
      writer.write(line);
      writer.newLine();
    }
    writer.close();
    String taskDNStr =
         "ds-task-id=add-single-valid-file,cn=Scheduled Tasks,cn=Tasks";
    String path = TestCaseUtils.createTempFile(
         "dn: " + taskDNStr,
         "changetype: add",
         "objectClass: top",
         "objectClass: ds-task",
         "objectClass: ds-task-add-schema-file",
         "ds-task-id: add-single-valid-file",
         "ds-task-class-name: org.opends.server.tasks.AddSchemaFileTask",
         "ds-task-schema-file-name: 05-single-valid.ldif");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    Task task = getCompletedTask(DN.decode(taskDNStr));
    assertEquals(task.getTaskState(), TaskState.COMPLETED_SUCCESSFULLY);
  }
  /**
   * Attempts to add multiple new files to the server schema where the files
   * exist and have valid contents.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddMultipleValidSchemaFiles()
         throws Exception
  {
    String schemaDirectory = SchemaConfigManager.getSchemaDirectoryPath();
    SchemaTestMatchingRule matchingRule1 =
         new SchemaTestMatchingRule("testAddMultipleValidSchemaFiles1Match",
                                    "1.3.6.1.4.1.26027.1.999.24");
    DirectoryServer.registerMatchingRule(matchingRule1, false);
    String[] fileLines1 =
    {
      "dn: cn=schema",
      "objectClass: top",
      "objectClass: ldapSubentry",
      "objectClass: subschema",
      "attributeTypes: ( testaddmultiplevalidschemafiles1at-oid " +
           "NAME 'testAddMultipleValidSchemaFiles1AT' )",
      "objectClasses: ( testaddmultiplevalidschemafiles1oc-oid " +
           "NAME 'testAddMultipleValidSchemaFiles1OC' STRUCTURAL " +
           "MUST testAddMultipleValidSchemaFiles1AT )",
      "nameForms: ( testaddmultiplevalidschemafiles1nf-oid " +
           "NAME 'testAddMultipleValidSchemaFiles1NF' " +
           "OC testAddMultipleValidSchemaFiles1OC " +
           "MUST testAddMultipleValidSchemaFiles1AT )",
      "dITContentRules: ( testaddmultiplevalidschemafiles1oc-oid " +
           "NAME 'testAddMultipleValidSchemaFiles1DCR' MAY description )",
      "dITStructureRules: ( 999017 " +
           "NAME 'testAddMultipleValidSchemaFiles1DSR' " +
           "FORM testAddMultipleValidSchemaFiles1NF )",
      "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.24 " +
           "NAME 'testAddMultipleValidSchemaFiles1MRU' " +
           "APPLIES testAddMultipleValidSchemaFiles1AT )"
    };
    File validFile1 = new File(schemaDirectory, "05-multiple-valid-1.ldif");
    BufferedWriter writer1 = new BufferedWriter(new FileWriter(validFile1));
    for (String line : fileLines1)
    {
      writer1.write(line);
      writer1.newLine();
    }
    writer1.close();
    SchemaTestMatchingRule matchingRule2 =
         new SchemaTestMatchingRule("testAddMultipleValidSchemaFiles2Match",
                                    "1.3.6.1.4.1.26027.1.999.25");
    DirectoryServer.registerMatchingRule(matchingRule2, false);
    String[] fileLines2 =
    {
      "dn: cn=schema",
      "objectClass: top",
      "objectClass: ldapSubentry",
      "objectClass: subschema",
      "attributeTypes: ( testaddmultiplevalidschemafiles2at-oid " +
           "NAME 'testAddMultipleValidSchemaFiles2AT' )",
      "objectClasses: ( testaddmultiplevalidschemafiles2oc-oid " +
           "NAME 'testAddMultipleValidSchemaFiles2OC' STRUCTURAL " +
           "MUST testAddMultipleValidSchemaFiles2AT )",
      "nameForms: ( testaddmultiplevalidschemafiles2nf-oid " +
           "NAME 'testAddMultipleValidSchemaFiles2NF' " +
           "OC testAddMultipleValidSchemaFiles2OC " +
           "MUST testAddMultipleValidSchemaFiles2AT )",
      "dITContentRules: ( testaddmultiplevalidschemafiles2oc-oid " +
           "NAME 'testAddMultipleValidSchemaFiles2DCR' MAY description )",
      "dITStructureRules: ( 999018 " +
           "NAME 'testAddMultipleValidSchemaFiles2DSR' " +
           "FORM testAddMultipleValidSchemaFiles2NF )",
      "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.25 " +
           "NAME 'testAddMultipleValidSchemaFiles2MRU' " +
           "APPLIES testAddMultipleValidSchemaFiles2AT )"
    };
    File validFile2 = new File(schemaDirectory, "05-multiple-valid-2.ldif");
    BufferedWriter writer2 = new BufferedWriter(new FileWriter(validFile2));
    for (String line : fileLines2)
    {
      writer2.write(line);
      writer2.newLine();
    }
    writer2.close();
    String taskDNStr =
         "ds-task-id=add-multiple-valid-files,cn=Scheduled Tasks,cn=Tasks";
    String path = TestCaseUtils.createTempFile(
         "dn: " + taskDNStr,
         "changetype: add",
         "objectClass: top",
         "objectClass: ds-task",
         "objectClass: ds-task-add-schema-file",
         "ds-task-id: add-multiple-valid-files",
         "ds-task-class-name: org.opends.server.tasks.AddSchemaFileTask",
         "ds-task-schema-file-name: 05-multiple-valid-1.ldif",
         "ds-task-schema-file-name: 05-multiple-valid-2.ldif");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    Task task = getCompletedTask(DN.decode(taskDNStr));
    assertEquals(task.getTaskState(), TaskState.COMPLETED_SUCCESSFULLY);
  }
  /**
   * Attempts to add a new file to the server schema in which the task entry
   * does not specify the name of the file to add.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddMissingSchemaFileNames()
         throws Exception
  {
    String taskDNStr =
         "ds-task-id=add-missing-file-names,cn=Scheduled Tasks,cn=Tasks";
    String path = TestCaseUtils.createTempFile(
         "dn: " + taskDNStr,
         "changetype: add",
         "objectClass: top",
         "objectClass: ds-task",
         "ds-task-id: add-missing-file-names",
         "ds-task-class-name: org.opends.server.tasks.AddSchemaFileTask");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Attempts to add a new file to the server schema in which the file does not
   * exist.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddMissingSchemaFile()
         throws Exception
  {
    String taskDNStr =
         "ds-task-id=add-missing-file,cn=Scheduled Tasks,cn=Tasks";
    String path = TestCaseUtils.createTempFile(
         "dn: " + taskDNStr,
         "changetype: add",
         "objectClass: top",
         "objectClass: ds-task",
         "objectClass: ds-task-add-schema-file",
         "ds-task-id: add-missing-file",
         "ds-task-class-name: org.opends.server.tasks.AddSchemaFileTask",
         "ds-task-schema-file-name: 05-missing.ldif");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Attempts to add a new file to the server schema in which the file exists
   * and is empty.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddEmptySchemaFile()
         throws Exception
  {
    String schemaDirectory = SchemaConfigManager.getSchemaDirectoryPath();
    File emptyFile = new File(schemaDirectory, "05-empty.ldif");
    emptyFile.createNewFile();
    String taskDNStr = "ds-task-id=add-empty-file,cn=Scheduled Tasks,cn=Tasks";
    String path = TestCaseUtils.createTempFile(
         "dn: " + taskDNStr,
         "changetype: add",
         "objectClass: top",
         "objectClass: ds-task",
         "objectClass: ds-task-add-schema-file",
         "ds-task-id: add-empty-file",
         "ds-task-class-name: org.opends.server.tasks.AddSchemaFileTask",
         "ds-task-schema-file-name: 05-empty.ldif");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    Task task = getCompletedTask(DN.decode(taskDNStr));
    assertEquals(task.getTaskState(), TaskState.COMPLETED_SUCCESSFULLY);
  }
  /**
   * Attempts to add a new file to the server schema in which the file exists
   * but does not contain a valid schema definition.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddInvalidSchemaFile()
         throws Exception
  {
    String schemaDirectory = SchemaConfigManager.getSchemaDirectoryPath();
    File invalidFile = new File(schemaDirectory, "05-invalid.ldif");
    BufferedWriter writer = new BufferedWriter(new FileWriter(invalidFile));
    writer.write("invalid");
    writer.close();
    String taskDNStr =
         "ds-task-id=add-invalid-file,cn=Scheduled Tasks,cn=Tasks";
    String path = TestCaseUtils.createTempFile(
         "dn: " + taskDNStr,
         "changetype: add",
         "objectClass: top",
         "objectClass: ds-task",
         "objectClass: ds-task-add-schema-file",
         "ds-task-id: add-invalid-file",
         "ds-task-class-name: org.opends.server.tasks.AddSchemaFileTask",
         "ds-task-schema-file-name: 05-invalid.ldif");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    invalidFile.delete();
  }
  /**
   * Retrieves the specified task from the server, waiting for it to finish all
   * the running its going to do before returning.
   *
   * @param  taskEntryDN  The DN of the entry for the task to retrieve.
   *
   * @return  The requested task entry.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  private Task getCompletedTask(DN taskEntryDN)
          throws Exception
  {
    TaskBackend taskBackend =
         (TaskBackend) DirectoryServer.getBackend(DN.decode("cn=tasks"));
    Task task = taskBackend.getScheduledTask(taskEntryDN);
    if (task == null)
    {
      long stopWaitingTime = System.currentTimeMillis() + 10000L;
      while ((task == null) && (System.currentTimeMillis() < stopWaitingTime))
      {
        Thread.sleep(10);
        task = taskBackend.getScheduledTask(taskEntryDN);
      }
    }
    if (task == null)
    {
      throw new AssertionError("There is no such task " +
                               taskEntryDN.toString());
    }
    if (! TaskState.isDone(task.getTaskState()))
    {
      long stopWaitingTime = System.currentTimeMillis() + 20000L;
      while ((! TaskState.isDone(task.getTaskState())) &&
             (System.currentTimeMillis() < stopWaitingTime))
      {
        Thread.sleep(10);
      }
    }
    if (! TaskState.isDone(task.getTaskState()))
    {
      throw new AssertionError("Task " + taskEntryDN.toString() +
                               " did not complete in a timely manner.");
    }
    return task;
  }
}