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

neil_a_wilson
13.48.2007 313c7ce226afaa79e811d6fa0f5ad8b8a59f0fd5
Provide a new org.opends.server.util.EmbeddedUtils class that can be used to
simplify the process of running the server as an embedded application. There
are methods to start, stop, and restart the server, as well as to determine
whether the server is running. Also, provide a new
org.opends.server.types.DirectoryEnvironmentConfig class that can be used to
define a number of "environment" properties that provide information about the
way in which the server should run.

The TestCaseUtils.startServer method has been updated to make use of the new
EmbeddedUtils functionality.
2 files added
12 files modified
2019 ■■■■ changed files
opends/src/server/org/opends/server/api/DirectoryThread.java 9 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/config/ConfigConstants.java 9 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DirectoryServer.java 307 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/LockFileManager.java 11 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/SchemaConfigManager.java 11 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/ConfigFileHandler.java 41 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/CoreMessages.java 124 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/UtilityMessages.java 14 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/DirectoryEnvironmentConfig.java 1177 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/LockManager.java 104 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/EmbeddedUtils.java 119 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/ServerConstants.java 19 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/StaticUtils.java 10 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java 64 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/api/DirectoryThread.java
@@ -141,18 +141,11 @@
      task = null;
    }
    String forceDaemonStr =
         System.getProperty(PROPERTY_FORCE_DAEMON_THREADS);
    if (forceDaemonStr != null)
    {
      String lowerStr = toLowerCase(forceDaemonStr);
      if (lowerStr.equals("true") || lowerStr.equals("yes") ||
          lowerStr.equals("on") || lowerStr.equals("1"))
    if (DirectoryServer.getEnvironmentConfig().forceDaemonThreads())
      {
        setDaemon(true);
      }
    }
  }
opends/src/server/org/opends/server/config/ConfigConstants.java
@@ -2723,6 +2723,15 @@
  /**
   * The default name of the file that holds the configuration for the Directory
   * Server.  It should exist below the directory specified by the
   * {@code CONFIG_DIR_NAME}.
   */
  public static final String CONFIG_FILE_NAME = "config.ldif";
  /**
   * The DN of the entry that will serve as the root for the Directory Server
   * configuration.
   */
opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -262,6 +262,9 @@
  // The configuration manager that will handle the certificate mapper.
  private CertificateMapperConfigManager certificateMapperConfigManager;
  // The class used to provide the config handler implementation.
  private Class configClass;
  // The configuration handler for the Directory Server.
  private ConfigHandler configHandler;
@@ -398,6 +401,9 @@
  // The crypto manager for the Directory Server.
  private CryptoManager cryptoManager;
  // The environment configuration for the Directory Server.
  private DirectoryEnvironmentConfig environmentConfig;
  // The shutdown hook that has been registered with the server.
  private DirectoryServerShutdownHook shutdownHook;
@@ -420,6 +426,10 @@
  // The configuration manager for extended operation handlers.
  private ExtendedOperationConfigManager extendedOperationConfigManager;
  // The path to the file containing the Directory Server configuration, or the
  // information needed to bootstrap the configuration handler.
  private File configFile;
  // The group manager for the Directory Server.
  private GroupManager groupManager;
@@ -535,13 +545,6 @@
  // The set of allowed task classes.
  private Set<String> allowedTasks;
  // The fully-qualified name of the configuration handler class.
  private String configClass;
  // The path to the file containing the Directory Server configuration, or the
  // information needed to bootstrap the configuration handler.
  private String configFile;
  // The time that the server was started, formatted in UTC time.
  private String startTimeUTC;
@@ -597,11 +600,28 @@
   */
  private DirectoryServer()
  {
    this(new DirectoryEnvironmentConfig());
  }
  /**
   * Creates a new instance of the Directory Server.  This will allow only a
   * single instance of the server per JVM.
   *
   * @param  config  The environment configuration to use for the Directory
   *                 Server instance.
   */
  private DirectoryServer(DirectoryEnvironmentConfig config)
  {
    environmentConfig        = config;
    isBootstrapped        = false;
    isRunning             = false;
    shuttingDown          = false;
    lockdownMode          = false;
    serverErrorResultCode = ResultCode.OTHER;
    startupDebugLogPublisher = null;
    startupErrorLogPublisher = null;
    operatingSystem = OperatingSystem.forName(System.getProperty("os.name"));
  }
@@ -627,20 +647,72 @@
   * reference to it.  This should only be used in the context of an in-core
   * restart after the existing server has been shut down.
   *
   * @param  config  The environment configuration for the Directory Server.
   *
   * @return  The new instance of the Directory Server that is associated with
   *          this JVM.
   */
  private static DirectoryServer getNewInstance()
  private static DirectoryServer
                      getNewInstance(DirectoryEnvironmentConfig config)
  {
    synchronized (directoryServer)
    {
      return directoryServer = new DirectoryServer();
      return directoryServer = new DirectoryServer(config);
    }
  }
  /**
   * Retrieves the environment configuration for the Directory Server.
   *
   * @return  The environment configuration for the Directory Server.
   */
  public static DirectoryEnvironmentConfig getEnvironmentConfig()
  {
    return directoryServer.environmentConfig;
  }
  /**
   * Sets the environment configuration for the Directory Server.  This method
   * may only be invoked when the server is not running.
   *
   * @param  config  The environment configuration for the Directory Server.
   *
   * @throws  InitializationException  If the Directory Server is currently
   *                                   running.
   */
  private void setEnvironmentConfig(DirectoryEnvironmentConfig config)
          throws InitializationException
  {
    if (isRunning)
    {
      int    msgID   = MSGID_CANNOT_SET_ENVIRONMENT_CONFIG_WHILE_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    environmentConfig = config;
  }
  /**
   * Indicates whether the Directory Server is currently running.
   *
   * @return  {@code true} if the server is currently running, or {@code false}
   *          if not.
   */
  public static boolean isRunning()
  {
    return directoryServer.isRunning;
  }
  /**
   * Bootstraps the appropriate Directory Server structures that may be needed
   * by client-side tools.  This is not intended for use in running the server
   * itself.
@@ -788,16 +860,24 @@
    // Install default debug and error loggers for use until enough of the
    // configuration has been read to allow the real loggers to be installed.
    removeAllAccessLogPublishers();
    for (AccessLogPublisher p : environmentConfig.getAccessLoggers())
    {
      addAccessLogPublisher(p);
    }
    startupErrorLogPublisher =
        TextErrorLogPublisher.getStartupTextErrorPublisher(
            new TextWriter.STDOUT());
    addErrorLogPublisher(startupErrorLogPublisher);
    removeAllErrorLogPublishers();
    for (ErrorLogPublisher p : environmentConfig.getErrorLoggers())
    {
      addErrorLogPublisher(p);
    }
    startupDebugLogPublisher =
        TextDebugLogPublisher.getStartupTextDebugPublisher(
            new TextWriter.STDOUT());
    addDebugLogPublisher(startupDebugLogPublisher);
    removeAllDebugLogPublishers();
    for (DebugLogPublisher p : environmentConfig.getDebugLoggers())
    {
      addDebugLogPublisher(p);
    }
    // Create the MBean server that we will use for JMX interaction.
    initializeJMX();
@@ -891,8 +971,45 @@
  public void initializeConfiguration(String configClass, String configFile)
         throws InitializationException
  {
    this.configClass = configClass;
    this.configFile  = configFile;
    Class cfgClass;
    try
    {
      cfgClass = Class.forName(configClass);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      int    msgID   = MSGID_CANNOT_LOAD_CONFIG_HANDLER_CLASS;
      String message = getMessage(msgID, configClass,
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    File cfgFile = new File(configFile);
    environmentConfig.setConfigClass(cfgClass);
    environmentConfig.setConfigFile(cfgFile);
    initializeConfiguration();
  }
  /**
   * Instantiates the configuration handler and loads the Directory Server
   * configuration.
   *
   * @throws  InitializationException  If a problem occurs while trying to
   *                                   initialize the config handler.
   */
  public void initializeConfiguration()
         throws InitializationException
  {
    this.configClass = environmentConfig.getConfigClass();
    this.configFile  = environmentConfig.getConfigFile();
    // Make sure that administration framework definition classes are loaded.
@@ -904,23 +1021,7 @@
    // Load and instantiate the configuration handler class.
    Class handlerClass;
    try
    {
      handlerClass = Class.forName(configClass);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      int    msgID   = MSGID_CANNOT_LOAD_CONFIG_HANDLER_CLASS;
      String message = getMessage(msgID, configClass, e);
      throw new InitializationException(msgID, message, e);
    }
    Class handlerClass = configClass;
    try
    {
      configHandler = (ConfigHandler) handlerClass.newInstance();
@@ -941,7 +1042,8 @@
    // Perform the handler-specific initialization.
    try
    {
      configHandler.initializeConfigHandler(configFile, false);
      configHandler.initializeConfigHandler(configFile.getAbsolutePath(),
                                            false);
    }
    catch (InitializationException ie)
    {
@@ -976,7 +1078,7 @@
   */
  public static String getConfigFile()
  {
    return directoryServer.configFile;
    return directoryServer.configFile.getAbsolutePath();
  }
@@ -1054,11 +1156,8 @@
      // Determine whether or not we should start the connection handlers.
      String disableProperty =
                  System.getProperty(PROPERTY_DISABLE_CONNECTION_HANDLERS);
      boolean startConnectionHandlers =
                   ((disableProperty == null) ||
                    (! disableProperty.equalsIgnoreCase("true")));
           (! environmentConfig.disableConnectionHandlers());
      // Initialize all the schema elements.
@@ -1225,8 +1324,15 @@
      sendAlertNotification(this, ALERT_TYPE_SERVER_STARTED, msgID, message);
      if (startupDebugLogPublisher != null)
      {
      removeDebugLogPublisher(startupDebugLogPublisher);
      }
      if (startupErrorLogPublisher != null)
      {
      removeErrorLogPublisher(startupErrorLogPublisher);
      }
      // If a server.starting file exists, then remove it.
@@ -2176,7 +2282,7 @@
    try
    {
      configHandler.initializeConfigHandler(configFile, true);
      configHandler.initializeConfigHandler(configFile.getAbsolutePath(), true);
    }
    catch (InitializationException ie)
    {
@@ -2860,24 +2966,16 @@
  {
    if (directoryServer.configHandler == null)
    {
      String serverRoot = System.getProperty(PROPERTY_SERVER_ROOT);
      if (serverRoot == null)
      {
        serverRoot = System.getenv(ENV_VAR_INSTANCE_ROOT);
      }
      File serverRoot = directoryServer.environmentConfig.getServerRoot();
      if (serverRoot != null)
      {
        return serverRoot;
        return serverRoot.getAbsolutePath();
      }
      else
      {
        // We don't know where the server root is, so we'll have to assume it's
        // the current working directory.
        return System.getProperty("user.dir");
      }
    }
    else
    {
      return directoryServer.configHandler.getServerRoot();
@@ -8283,10 +8381,29 @@
   */
  public static void restart(String className, String reason)
  {
    restart(className, reason, directoryServer.environmentConfig);
  }
  /**
   * Causes the Directory Server to perform an in-core restart.  This will
   * cause virtually all components of the Directory Server to shut down, and
   * once that has completed it will be restarted.
   *
   * @param  className  The fully-qualified name of the Java class that
   *                    initiated the shutdown.
   * @param  reason     The human-readable reason that the directory server is
   *                    shutting down.
   * @param  config     The environment configuration to use for the server.
   */
  public static void restart(String className, String reason,
                             DirectoryEnvironmentConfig config)
  {
    try
    {
      shutDown(className, reason);
      reinitialize();
      reinitialize(config);
      directoryServer.startServer();
    }
    catch (Exception e)
@@ -8300,23 +8417,52 @@
    }
  }
  /**
   * Reinitializes the server following a shutdown, preparing it for
   * a call to <code>startServer</code>.
   * Reinitializes the server following a shutdown, preparing it for a call to
   * {@code startServer}.
   *
   * @return  The new Directory Server instance created during the
   *          reinitialization process.
   *
   * @throws  InitializationException  If a problem occurs while trying to
   *                                   initialize the config handler or
   *                                   bootstrap that server.
   */
  public static void reinitialize() throws InitializationException
  public static DirectoryServer reinitialize()
         throws InitializationException
  {
    String configClass = directoryServer.configClass;
    String configFile  = directoryServer.configFile;
    getNewInstance();
    directoryServer.bootstrapServer();
    directoryServer.initializeConfiguration(configClass, configFile);
    return reinitialize(directoryServer.environmentConfig);
  }
  /**
   * Reinitializes the server following a shutdown, preparing it for a call to
   * {@code startServer}.
   *
   * @param  config  The environment configuration for the Directory Server.
   *
   * @return  The new Directory Server instance created during the
   *          reinitialization process.
   *
   * @throws  InitializationException  If a problem occurs while trying to
   *                                   initialize the config handler or
   *                                   bootstrap that server.
   */
  public static DirectoryServer reinitialize(DirectoryEnvironmentConfig config)
         throws InitializationException
  {
    getNewInstance(config);
    LockManager.reinitializeLockTable();
    directoryServer.bootstrapServer();
    directoryServer.initializeConfiguration();
    return directoryServer;
  }
  /**
   * Retrieves the maximum number of concurrent client connections that may be
   * established.
@@ -9217,10 +9363,45 @@
    }
    // Create an environment configuration for the server and populate a number
    // of appropriate properties.
    TextErrorLogPublisher startupErrorLogPublisher = null;
    TextDebugLogPublisher startupDebugLogPublisher = null;
    DirectoryEnvironmentConfig environmentConfig =
         new DirectoryEnvironmentConfig();
    try
    {
      environmentConfig.setProperty(PROPERTY_CONFIG_CLASS,
                                    configClass.getValue());
      environmentConfig.setProperty(PROPERTY_CONFIG_FILE,
                                    configFile.getValue());
      startupErrorLogPublisher =
          TextErrorLogPublisher.getStartupTextErrorPublisher(
              new TextWriter.STDOUT());
      environmentConfig.addErrorLogger(startupErrorLogPublisher);
      startupDebugLogPublisher =
          TextDebugLogPublisher.getStartupTextDebugPublisher(
              new TextWriter.STDOUT());
      environmentConfig.addDebugLogger(startupDebugLogPublisher);
    }
    catch (Exception e)
    {
      // This shouldn't happen.  For the methods we are using, the exception is
      // just a guard against making changes with the server running.
    }
    // Bootstrap and start the Directory Server.
    DirectoryServer directoryServer = DirectoryServer.getInstance();
    try
    {
      directoryServer.startupErrorLogPublisher = startupErrorLogPublisher;
      directoryServer.startupDebugLogPublisher = startupDebugLogPublisher;
      directoryServer.setEnvironmentConfig(environmentConfig);
      directoryServer.bootstrapServer();
      directoryServer.initializeConfiguration(configClass.getValue(),
                                              configFile.getValue());
opends/src/server/org/opends/server/core/LockFileManager.java
@@ -574,14 +574,9 @@
   */
  public static String getLockDirectoryPath()
  {
    String lockDirectory = System.getProperty(PROPERTY_LOCK_DIRECTORY);
    if ((lockDirectory == null) || (lockDirectory.length() == 0))
    {
      lockDirectory = DirectoryServer.getServerRoot() + File.separator +
                      LOCKS_DIRECTORY;
    }
    return lockDirectory;
    File lockDirectory =
              DirectoryServer.getEnvironmentConfig().getLockDirectory();
    return lockDirectory.getAbsolutePath();
  }
opends/src/server/org/opends/server/core/SchemaConfigManager.java
@@ -117,14 +117,9 @@
   */
  public static String getSchemaDirectoryPath()
  {
    String schemaDirPath = System.getProperty(PROPERTY_SCHEMA_DIRECTORY);
    if ((schemaDirPath == null) || (schemaDirPath.length() == 0))
    {
      schemaDirPath = DirectoryServer.getServerRoot() + File.separator +
                      PATH_SCHEMA_DIR;
    }
    return schemaDirPath;
    File schemaDir =
              DirectoryServer.getEnvironmentConfig().getSchemaDirectory();
    return schemaDir.getAbsolutePath();
  }
opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
@@ -655,38 +655,11 @@
    }
    // Determine the appropriate server root for the Directory Server.  First,
    // do this by looking for a Java property.  If that isn't specified, then
    // look for an environment variable, and if all else fails then try to
    // figure it out from the location of the configuration file.
    String rootDirStr = System.getProperty(PROPERTY_SERVER_ROOT);
    if (rootDirStr == null)
    {
      rootDirStr = System.getenv(ENV_VAR_INSTANCE_ROOT);
    }
    if (rootDirStr != null)
    {
      try
      {
        File serverRootFile = new File(rootDirStr);
        serverRoot = serverRootFile.getAbsolutePath();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        int    msgID   = MSGID_CONFIG_CANNOT_DETERMINE_SERVER_ROOT;
        String message = getMessage(msgID, ENV_VAR_INSTANCE_ROOT);
        throw new InitializationException(msgID, message);
      }
    }
    if (serverRoot == null)
    // Determine the appropriate server root.  If it's not defined in the
    // environment config, then try to figure it out from the location of the
    // configuration file.
    File rootFile = DirectoryServer.getEnvironmentConfig().getServerRoot();
    if (rootFile == null)
    {
      try
      {
@@ -725,6 +698,10 @@
        throw new InitializationException(msgID, message);
      }
    }
    else
    {
      serverRoot = rootFile.getAbsolutePath();
    }
    // Register with the Directory Server as an alert generator.
opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -6380,6 +6380,97 @@
       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 639;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * change the directory environment configuration while the server is running.
   * This does not take any arguments.
   */
  public static final int MSGID_DIRCFG_SERVER_ALREADY_RUNNING =
       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 640;
  /**
   * The message ID for the message that will be used if an invalid server root
   * directory is specified.  This takes a single argument, which is the invalid
   * server root directory.
   */
  public static final int MSGID_DIRCFG_INVALID_SERVER_ROOT =
       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 641;
  /**
   * The message ID for the message that will be used if an invalid
   * configuration file is specified.  This takes a single argument, which is
   * the invalid configuration file.
   */
  public static final int MSGID_DIRCFG_INVALID_CONFIG_FILE =
       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 642;
  /**
   * The message ID for the message that will be used if an invalid config
   * handler class is specified.  This takes a single argument, which is the
   * fully-qualified class name.
   */
  public static final int MSGID_DIRCFG_INVALID_CONFIG_CLASS =
       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 643;
  /**
   * The message ID for the message that will be used if an invalid schema
   * directory is specified.  This takes a single argument, which is the invalid
   * schema directory.
   */
  public static final int MSGID_DIRCFG_INVALID_SCHEMA_DIRECTORY =
       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 644;
  /**
   * The message ID for the message that will be used if an invalid lock
   * directory is specified.  This takes a single argument, which is the invalid
   * lock directory.
   */
  public static final int MSGID_DIRCFG_INVALID_LOCK_DIRECTORY =
       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 645;
  /**
   * The message ID for the message that will be used if an invalid concurrency
   * level is specified.  This takes a single argument, which is the invalid
   * concurrency level.
   */
  public static final int MSGID_DIRCFG_INVALID_CONCURRENCY_LEVEL =
       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 646;
  /**
   * The message ID for the message that will be used if an invalid lock table
   * size is specified.  This takes a single argument, which is the invalid lock
   * table size.
   */
  public static final int MSGID_DIRCFG_INVALID_LOCK_TABLE_SIZE =
       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 647;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * alter the server environment configuration while the server is running.
   * This does not take any arguments.
   */
  public static final int MSGID_CANNOT_SET_ENVIRONMENT_CONFIG_WHILE_RUNNING =
       CATEGORY_MASK_CORE | SEVERITY_MASK_FATAL_ERROR | 648;
  /**
   * Associates a set of generic messages with the message IDs defined
   * in this class.
@@ -6608,6 +6699,10 @@
                    "threads in the Directory Server was reduced");
    registerMessage(MSGID_CANNOT_SET_ENVIRONMENT_CONFIG_WHILE_RUNNING,
                    "The Directory Server is currently running.  The " +
                    "environment configuration may not be altered while the " +
                    "server is online");
    registerMessage(MSGID_CANNOT_BOOTSTRAP_WHILE_RUNNING,
                    "The Directory Server is currently running.  The " +
                    "configuration may not be bootstrapped while the server " +
@@ -8696,6 +8791,35 @@
                    "Unable to register network group %s with the Directory " +
                    "Server because another network group with the same " +
                    "network group ID is already registered");
    registerMessage(MSGID_DIRCFG_SERVER_ALREADY_RUNNING,
                    "The Directory Server is currently running.  Environment " +
                    "configuration changes are not allowed with the server " +
                    "running");
    registerMessage(MSGID_DIRCFG_INVALID_SERVER_ROOT,
                    "The specified server root directory '%s' is invalid.  " +
                    "The specified path must exist and must be a directory");
    registerMessage(MSGID_DIRCFG_INVALID_CONFIG_FILE,
                    "The specified config file path '%s' is invalid.  " +
                    "The specified path must exist and must be a file");
    registerMessage(MSGID_DIRCFG_INVALID_CONFIG_CLASS,
                    "The specified config handler class '%s' is invalid.  " +
                    "The specified class must be a subclass of the " +
                    "org.opends.server.api.ConfigHandler superclass");
    registerMessage(MSGID_DIRCFG_INVALID_SCHEMA_DIRECTORY,
                    "The specified schema configuration directory '%s' is " +
                    "invalid.  The specified path must exist and must be a " +
                    "directory");
    registerMessage(MSGID_DIRCFG_INVALID_LOCK_DIRECTORY,
                    "The specified lock directory '%s' is invalid.  The " +
                    "specified path must exist and must be a directory");
    registerMessage(MSGID_DIRCFG_INVALID_CONCURRENCY_LEVEL,
                    "The specified lock table concurrency level %d is " +
                    "invalid.  It must be an integer value greater than zero");
    registerMessage(MSGID_DIRCFG_INVALID_LOCK_TABLE_SIZE,
                    "The specified initial lock table size %d is invalid.  " +
                    "It must be an integer value greater than zero");
  }
}
opends/src/server/org/opends/server/messages/UtilityMessages.java
@@ -1753,6 +1753,15 @@
  /**
   * The message ID for the message that will be used if an attempt is made to
   * start the Directory Server if it is already running.  It does not take any
   * arguments.
   */
  public static final int MSGID_EMBEDUTILS_SERVER_ALREADY_RUNNING =
    CATEGORY_MASK_UTIL | SEVERITY_MASK_SEVERE_ERROR | 167;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -2333,6 +2342,11 @@
    registerMessage(MSGID_EXPCHECK_TRUSTMGR_SERVER_CERT_NOT_YET_VALID,
                    "Refusing to trust server or issuer certificate '%s' " +
                    "because it is not valid until %s");
    registerMessage(MSGID_EMBEDUTILS_SERVER_ALREADY_RUNNING,
                    "The Directory Server cannot be started because it is " +
                    "already running");
  }
}
opends/src/server/org/opends/server/types/DirectoryEnvironmentConfig.java
New file
@@ -0,0 +1,1177 @@
/*
 * 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.types;
import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.opends.server.api.AccessLogPublisher;
import org.opends.server.api.ConfigHandler;
import org.opends.server.api.DebugLogPublisher;
import org.opends.server.api.ErrorLogPublisher;
import org.opends.server.core.DirectoryServer;
import org.opends.server.extensions.ConfigFileHandler;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.messages.CoreMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
/**
 * This class provides a set of properties that may control various
 * aspects of the server environment.  Note that these properties may
 * only be altered before the Directory Server is started.  Any
 * attempt to change an environment configuration property while the
 * server is running will be rejected.
 */
public final class DirectoryEnvironmentConfig
{
  // The set of access loggers that should be put in place before the
  // server is started.
  private final ArrayList<AccessLogPublisher> accessLoggers;
  // The set of debug loggers that should be put in place before the
  // server is started.
  private final ArrayList<DebugLogPublisher> debugLoggers;
  // The set of error loggers that should be put in place before the
  // server is started.
  private final ArrayList<ErrorLogPublisher> errorLoggers;
  // The set of properties for the environment config.
  private final HashMap<String,String> configProperties;
  /**
   * Creates a new directory environment configuration initialized
   * from the system properties defined in the JVM.
   */
  public DirectoryEnvironmentConfig()
  {
    this(System.getProperties());
  }
  /**
   * Creates a new directory environment configuration initialized
   * with a copy of the provided set of properties.
   *
   * @param  properties  The properties to use when initializing this
   *                     environment configuration, or {@code null}
   *                     to use an empty set of properties.
   */
  public DirectoryEnvironmentConfig(Properties properties)
  {
    configProperties = new HashMap<String,String>();
    if (properties != null)
    {
      Enumeration propertyNames = properties.propertyNames();
      while (propertyNames.hasMoreElements())
      {
        Object o = propertyNames.nextElement();
        configProperties.put(String.valueOf(o),
                             String.valueOf(properties.get(o)));
      }
    }
    accessLoggers = new ArrayList<AccessLogPublisher>();
    debugLoggers  = new ArrayList<DebugLogPublisher>();
    errorLoggers  = new ArrayList<ErrorLogPublisher>();
  }
  /**
   * Creates a new directory environment configuration initialized
   * with a copy of the provided set of properties.
   *
   * @param  properties  The properties to use when initializing this
   *                     environment configuration, or {@code null}
   *                     to use an empty set of properties.
   */
  public DirectoryEnvironmentConfig(Map<String,String> properties)
  {
    if (properties == null)
    {
      configProperties = new HashMap<String,String>();
    }
    else
    {
      configProperties = new HashMap<String,String>(properties);
    }
    accessLoggers = new ArrayList<AccessLogPublisher>();
    debugLoggers  = new ArrayList<DebugLogPublisher>();
    errorLoggers  = new ArrayList<ErrorLogPublisher>();
  }
  /**
   * Retrieves the property with the specified name.  The check will
   * first be made in the local config properties, but if no value is
   * found then the JVM system properties will be checked.
   *
   * @param  name  The name of the property to retrieve.
   *
   * @return  The property with the specified name, or {@code null} if
   *          no such property is defined.
   */
  public String getProperty(String name)
  {
    String value = configProperties.get(name);
    if (value == null)
    {
      value = System.getProperty(name);
    }
    return value;
  }
  /**
   * Specifies a property with the given name and value.  If a
   * property is already defined with the given name, then its value
   * will be replaced with the provided value, or the property will be
   * removed if the given value is {@code null}.
   *
   * @param  name   The name of the property to set.
   * @param  value  The value of the property to set, or {@code null}
   *                if the property is to be removed.
   *
   * @return  The previous value held for the property, or
   *          {@code null} if it was not previously set.
   *
   * @throws  InitializationException  If the Directory Server is
   *                                   already running.
   */
  public String setProperty(String name, String value)
         throws InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_DIRCFG_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    if (value == null)
    {
      return configProperties.remove(name);
    }
    else
    {
      return configProperties.put(name, value);
    }
  }
  /**
   * Retrieves the directory that should be considered the server
   * root.  The determination will first be based on the properties
   * defined in this config object.  If no value is found there, then
   * the JVM system properties will be checked, followed by an
   * environment variable.
   *
   * @return  The directory that should be considered the server root,
   *          or {@code null} if it is not defined.
   */
  public File getServerRoot()
  {
    String serverRootPath = getProperty(PROPERTY_SERVER_ROOT);
    if (serverRootPath == null)
    {
      serverRootPath = System.getenv(ENV_VAR_INSTANCE_ROOT);
    }
    if (serverRootPath == null)
    {
      return null;
    }
    else
    {
      return new File(serverRootPath);
    }
  }
  /**
   * Specifies the directory that should be considered the server
   * root.  Any relative path used in the server should be considered
   * relative to the server root.
   *
   * @param  serverRoot  The directory that should be considered the
   *                     server root.
   *
   * @return  The previous server root, or {@code null} if there was
   *          none.
   *
   * @throws  InitializationException  If the Directory Server is
   *                                   already running or there is a
   *                                   problem with the provided
   *                                   server root.
   */
  public File setServerRoot(File serverRoot)
         throws InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_DIRCFG_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    if ((! serverRoot.exists()) || (! serverRoot.isDirectory()))
    {
      int    msgID   = MSGID_DIRCFG_INVALID_SERVER_ROOT;
      String message = getMessage(msgID,
                                  serverRoot.getAbsolutePath());
      throw new InitializationException(msgID, message);
    }
    String serverRootPath;
    try
    {
      serverRootPath = serverRoot.getCanonicalPath();
    }
    catch (Exception e)
    {
      serverRootPath = serverRoot.getAbsolutePath();
    }
    String oldRootPath = setProperty(PROPERTY_SERVER_ROOT,
                                     serverRootPath);
    if (oldRootPath == null)
    {
      return null;
    }
    else
    {
      return new File(oldRootPath);
    }
  }
  /**
   * Retrieves the configuration file that should be used to
   * initialize the Directory Server config handler.  If no default
   * configuration file is specified, then the server will attempt to
   * use "config/config.ldif" below the server root if it exists.
   *
   * @return  The configuration file that should be used to initialize
   *          the Directory Server config handler, or {@code null} if
   *          no configuration file is defined.
   */
  public File getConfigFile()
  {
    String configFilePath = getProperty(PROPERTY_CONFIG_FILE);
    if (configFilePath == null)
    {
      File serverRoot = getServerRoot();
      if (serverRoot != null)
      {
        File configDir = new File(serverRoot, CONFIG_DIR_NAME);
        File configFile = new File(configDir, CONFIG_FILE_NAME);
        if (configFile.exists())
        {
          return configFile;
        }
      }
      return null;
    }
    else
    {
      return new File(configFilePath);
    }
  }
  /**
   * Specifies the configuration file that should be used to
   * initialize the Directory Server config handler.
   *
   * @param  configFile  The configuration file that should be used to
   *                     initialize the Directory Server config
   *                     handler.
   *
   * @return  The previously-defined configuration file, or
   *          {@code null} if none was defined.
   *
   * @throws  InitializationException  If the Directory Server is
   *                                   already running or there is a
   *                                   problem with the provided
   *                                   configuration file.
   */
  public File setConfigFile(File configFile)
         throws InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_DIRCFG_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    if ((! configFile.exists()) || (! configFile.isFile()))
    {
      int    msgID   = MSGID_DIRCFG_INVALID_CONFIG_FILE;
      String message = getMessage(msgID,
                                  configFile.getAbsolutePath());
      throw new InitializationException(msgID, message);
    }
    String configFilePath;
    try
    {
      configFilePath = configFile.getCanonicalPath();
    }
    catch (Exception e)
    {
      configFilePath = configFile.getAbsolutePath();
    }
    String oldConfigFilePath = setProperty(PROPERTY_CONFIG_FILE,
                                           configFilePath);
    if (oldConfigFilePath == null)
    {
      return null;
    }
    else
    {
      return new File(oldConfigFilePath);
    }
  }
  /**
   * Retrieves the class that provides the Directory Server
   * configuration handler implementation.  If no config handler class
   * is defined, or if a problem occurs while attempting to determine
   * the config handler class, then a default class of
   * org.opends.server.extensions.ConfigFileHandler will be returned.
   *
   * @return  The class that provides the Directory Server
   *          configuration handler implementation.
   */
  public Class getConfigClass()
  {
    String className = getProperty(PROPERTY_CONFIG_CLASS);
    if (className == null)
    {
      return ConfigFileHandler.class;
    }
    else
    {
      try
      {
        return Class.forName(className);
      }
      catch (Exception e)
      {
        return ConfigFileHandler.class;
      }
    }
  }
  /**
   * Specifies the class that provides the Directory Server
   * configuration handler implementation.  The class must be a
   * subclass of the org.opends.server.api.ConfigHandler superclass.
   *
   * @param  configClass  The class that proviedes the Directory
   *                      Server configuration handler implementation.
   *
   * @return  The class that was previously configured to provide the
   *          Directory Server configuration handler implementation,
   *          or {@code null} if none was defined.
   *
   * @throws  InitializationException  If the Directory Server is
   *                                   already running or there is a
   *                                   problem with the provided
   *                                   config handler class.
   */
  public Class setConfigClass(Class configClass)
         throws InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_DIRCFG_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    if (! (ConfigHandler.class.isAssignableFrom(configClass)))
    {
      int    msgID   = MSGID_DIRCFG_INVALID_CONFIG_CLASS;
      String message = getMessage(msgID, configClass.getName());
      throw new InitializationException(msgID, message);
    }
    String oldClassName = setProperty(PROPERTY_CONFIG_CLASS,
                                      configClass.getName());
    if (oldClassName == null)
    {
      return null;
    }
    else
    {
      try
      {
        return Class.forName(oldClassName);
      }
      catch (Exception e)
      {
        return null;
      }
    }
  }
  /**
   * Retrieves the directory that contains the server schema
   * configuration files.  If no value is defined, but a default
   * directory of "config/schema" exists below the server root, then
   * that will be returned.
   *
   * @return  The directory that contains the server schema
   *          configuration files, or {@code null} if none is defined.
   */
  public File getSchemaDirectory()
  {
    String schemaDirectoryPath =
         getProperty(PROPERTY_SCHEMA_DIRECTORY);
    if (schemaDirectoryPath == null)
    {
      File serverRoot = getServerRoot();
      if (serverRoot != null)
      {
        File schemaDir = new File(serverRoot.getAbsolutePath() +
                                  File.separator + PATH_SCHEMA_DIR);
        if (schemaDir.exists() && schemaDir.isDirectory())
        {
          return schemaDir;
        }
      }
      return null;
    }
    else
    {
      return new File(schemaDirectoryPath);
    }
  }
  /**
   * Specifies the directory that should contain the server schema
   * configuration files.  It must exist and must be a directory.
   *
   * @param  schemaDirectory  The directory that should contain the
   *                          server schema configuration files.
   *
   * @return  The previously-defined schema configuration directory,
   *          or {@code null} if none was defined.
   *
   * @throws  InitializationException  If the Directory Server is
   *                                   already running or there is a
   *                                   problem with the provided
   *                                   schema directory.
   */
  public File setSchemaDirectory(File schemaDirectory)
         throws InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_DIRCFG_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    if ((! schemaDirectory.exists()) ||
        (! schemaDirectory.isDirectory()))
    {
      int    msgID   = MSGID_DIRCFG_INVALID_SCHEMA_DIRECTORY;
      String message = getMessage(msgID,
                                  schemaDirectory.getAbsolutePath());
      throw new InitializationException(msgID, message);
    }
    String schemaDirectoryPath;
    try
    {
      schemaDirectoryPath = schemaDirectory.getCanonicalPath();
    }
    catch (Exception e)
    {
      schemaDirectoryPath = schemaDirectory.getAbsolutePath();
    }
    String oldSchemaDir = setProperty(PROPERTY_SCHEMA_DIRECTORY,
                                     schemaDirectoryPath);
    if (oldSchemaDir == null)
    {
      return null;
    }
    else
    {
      return new File(oldSchemaDir);
    }
  }
  /**
   * Retrieves the directory that should be used to hold the server
   * lock files.  If no value is defined, then the server will attempt
   * to use a default directory of "locks" below the server root.
   *
   * @return  The directory that should be used to hold the server
   *          lock files, or {@code null} if it cannot be determined.
   */
  public File getLockDirectory()
  {
    String lockFilePath = getProperty(PROPERTY_LOCK_DIRECTORY);
    if (lockFilePath == null)
    {
      File serverRoot = getServerRoot();
      if (serverRoot == null)
      {
        return null;
      }
      else
      {
        return new File(serverRoot, LOCKS_DIRECTORY);
      }
    }
    else
    {
      return new File(lockFilePath);
    }
  }
  /**
   * Specifies the directory that should be used to hold the server
   * lock files.  If the specified path already exists, then it must
   * be a directory and its contents must be writable by the server.
   * If it does not exist, then its parent directory must exist and
   * the server should have permission to create a new subdirectory in
   * it.
   *
   * @param  lockDirectory  The directory that should be used to hold
   *                        the server lock files.
   *
   * @return  The previously-defined lock directory, or {@code null}
   *          if none was defined.
   *
   * @throws  InitializationException  If the Directory Server is
   *                                   already running or there is a
   *                                   problem with the provided lock
   *                                   directory.
   */
  public File setLockDirectory(File lockDirectory)
         throws InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_DIRCFG_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    if (lockDirectory.exists())
    {
      if (! lockDirectory.isDirectory())
      {
        int    msgID   = MSGID_DIRCFG_INVALID_LOCK_DIRECTORY;
        String message = getMessage(msgID,
                                    lockDirectory.getAbsolutePath());
        throw new InitializationException(msgID, message);
      }
    }
    else
    {
      File parentFile = lockDirectory.getParentFile();
      if (! (parentFile.exists() && parentFile.isDirectory()))
      {
        int    msgID   = MSGID_DIRCFG_INVALID_LOCK_DIRECTORY;
        String message = getMessage(msgID,
                                    lockDirectory.getAbsolutePath());
        throw new InitializationException(msgID, message);
      }
    }
    String lockDirectoryPath;
    try
    {
      lockDirectoryPath = lockDirectory.getCanonicalPath();
    }
    catch (Exception e)
    {
      lockDirectoryPath = lockDirectory.getAbsolutePath();
    }
    String oldLockDir = setProperty(PROPERTY_LOCK_DIRECTORY,
                                    lockDirectoryPath);
    if (oldLockDir == null)
    {
      return null;
    }
    else
    {
      return new File(oldLockDir);
    }
  }
  /**
   * Indicates whether the Directory Server startup process should
   * skip the connection handler creation and initialization phases.
   *
   * @return  {@code true} if the Directory Server should not start
   *          its connection handlers, or {@code false} if the
   *          connection handlers should be enabled.
   */
  public boolean disableConnectionHandlers()
  {
    String disableStr =
         getProperty(PROPERTY_DISABLE_CONNECTION_HANDLERS);
    if (disableStr == null)
    {
      return false;
    }
    return disableStr.equalsIgnoreCase("true");
  }
  /**
   * Specifies whether the Directory Server startup process should
   * skip the connection handler creation and initialization phases.
   *
   * @param  disableConnectionHandlers  Indicates whether the
   *                                    Directory Server should skip
   *                                    the connection handler
   *                                    creation and initialization
   *                                    phases.
   *
   * @return  The previous setting for this configuration option.  If
   *          no previous value was specified, then {@code false} will
   *          be returned.
   *
   * @throws  InitializationException  If the Directory Server is
   *                                   already running.
   */
  public boolean setDisableConnectionHandlers(
                      boolean disableConnectionHandlers)
         throws InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_DIRCFG_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    String oldDisableStr =
         setProperty(PROPERTY_DISABLE_CONNECTION_HANDLERS,
                     String.valueOf(disableConnectionHandlers));
    if (oldDisableStr == null)
    {
      return false;
    }
    else
    {
      return oldDisableStr.equalsIgnoreCase("true");
    }
  }
  /**
   * Indicates whether all threads created by the Directory Server
   * should be created as daemon threads.
   *
   * @return  {@code true} if all threads created by the Directory
   *          Server should be created as daemon threads, or
   *          {@code false} if not.
   */
  public boolean forceDaemonThreads()
  {
    String forceDaemonStr =
         getProperty(PROPERTY_FORCE_DAEMON_THREADS);
    if (forceDaemonStr == null)
    {
      return false;
    }
    else
    {
      return forceDaemonStr.equalsIgnoreCase("true");
    }
  }
  /**
   * Specifies whether all threads created by the Directory Server
   * should be created as daemon threads.
   *
   * @param  forceDaemonThreads  Indicates whether all threads created
   *                             by the Directory Server should be
   *                             created as daemon threads.
   *
   * @return  The previous setting for this configuration option.  If
   *          no previous value was specified, then {@code false} will
   *          be returned.
   *
   * @throws  InitializationException  If the Directory Server is
   *                                   already running.
   */
  public boolean setForceDaemonThreads(boolean forceDaemonThreads)
         throws InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_DIRCFG_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    String oldForceDaemonStr =
         setProperty(PROPERTY_FORCE_DAEMON_THREADS,
                     String.valueOf(forceDaemonThreads));
    if (oldForceDaemonStr == null)
    {
      return false;
    }
    else
    {
      return oldForceDaemonStr.equalsIgnoreCase("true");
    }
  }
  /**
   * Indicates whether the Directory Server should be allowed to use
   * the {@code Runtime.exec()} method to be able to launch external
   * commands on the underlying system.
   *
   * @return  {@code true} if the Directory Server should be allowed
   *          to use {@code Runtime.exec()}, or {@code false} if not.
   */
  public boolean disableExec()
  {
    String disableStr = getProperty(PROPERTY_DISABLE_EXEC);
    if (disableStr == null)
    {
      return false;
    }
    else
    {
      return disableStr.equalsIgnoreCase("true");
    }
  }
  /**
   * Specifies whether the Directory Server should be allowed to use
   * the {@code Runtime.exec()} method to be able to launch external
   * commands on the underlying system.
   *
   * @param  disableExec  Indicates whether the Directory Server
   *                      should be allowed to launch external
   *                      commands on the underlying system.
   *
   * @return  The previous setting for this configuration option.  If
   *          no previous value was specified, then {@code false} will
   *          be returned.
   *
   * @throws  InitializationException  If the Directory Server is
   *                                   already running.
   */
  public boolean setDisableExec(boolean disableExec)
         throws InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_DIRCFG_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    String oldDisableStr = setProperty(PROPERTY_DISABLE_EXEC,
                     String.valueOf(disableExec));
    if (oldDisableStr == null)
    {
      return false;
    }
    else
    {
      return oldDisableStr.equalsIgnoreCase("true");
    }
  }
  /**
   * Retrieves the concurrency level for the Directory Server lock
   * table.
   *
   * @return  The concurrency level for the Directory Server lock
   *          table.
   */
  public int getLockManagerConcurrencyLevel()
  {
    String levelStr =
         getProperty(PROPERTY_LOCK_MANAGER_CONCURRENCY_LEVEL);
    if (levelStr == null)
    {
      return LockManager.DEFAULT_CONCURRENCY_LEVEL;
    }
    int concurrencyLevel = -1;
    try
    {
      concurrencyLevel = Integer.parseInt(levelStr);
    }
    catch (Exception e)
    {
      return LockManager.DEFAULT_CONCURRENCY_LEVEL;
    }
    if (concurrencyLevel <= 0)
    {
      return LockManager.DEFAULT_CONCURRENCY_LEVEL;
    }
    else
    {
      return concurrencyLevel;
    }
  }
  /**
   * Specifies the concurrency level for the Directory Server lock
   * table.  This should be set to the maximum number of threads that
   * could attempt to interact with the lock table at any given time.
   *
   * @param  concurrencyLevel  The concurrency level for the Directory
   *                           Server lock manager.
   *
   * @return  The previously-configured concurrency level.  If there
   *          was no previously-configured value, then the default
   *          concurrency level will be returned.
   *
   * @throws  InitializationException  If the Directory Server is
   *                                   already running or there is a
   *                                   problem with the provided
   *                                   concurrency level value.
   */
  public int setLockManagerConcurrencyLevel(int concurrencyLevel)
         throws InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_DIRCFG_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    if (concurrencyLevel <= 0)
    {
        int    msgID   = MSGID_DIRCFG_INVALID_CONCURRENCY_LEVEL;
        String message = getMessage(msgID, concurrencyLevel);
        throw new InitializationException(msgID, message);
    }
    String concurrencyStr =
         setProperty(PROPERTY_LOCK_MANAGER_CONCURRENCY_LEVEL,
                     String.valueOf(concurrencyLevel));
    if (concurrencyStr == null)
    {
      return LockManager.DEFAULT_CONCURRENCY_LEVEL;
    }
    else
    {
      try
      {
        return Integer.parseInt(concurrencyStr);
      }
      catch (Exception e)
      {
        return LockManager.DEFAULT_CONCURRENCY_LEVEL;
      }
    }
  }
  /**
   * Retrieves the initial table size for the server lock table.  This
   * can be used to ensure that the lock table has the appropriate
   * size for the expected number of locks that will be held at any
   * given time.
   *
   * @return  The initial table size for the server lock table.
   */
  public int getLockManagerTableSize()
  {
    String sizeStr = getProperty(PROPERTY_LOCK_MANAGER_TABLE_SIZE);
    if (sizeStr == null)
    {
      return LockManager.DEFAULT_INITIAL_TABLE_SIZE;
    }
    else
    {
      try
      {
        return Integer.parseInt(sizeStr);
      }
      catch (Exception e)
      {
        return LockManager.DEFAULT_INITIAL_TABLE_SIZE;
      }
    }
  }
  /**
   * Specifies the initial table size for the server lock table.  This
   * can be used to ensure taht the lock table has the appropriate
   * size for the expected number of locks that will be held at any
   * given time.
   *
   * @param  lockTableSize  The initial table size for the server lock
   *                        table.
   *
   * @return  The previously-configured initial lock table size.  If
   *          there was no previously-configured value, then the
   *          default initial table size will be returned.
   *
   * @throws  InitializationException  If the Directory Server is
   *                                   already running or there is a
   *                                   problem with the provided
   *                                   initial table size.
   */
  public int setLockManagerTableSize(int lockTableSize)
         throws InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_DIRCFG_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    if (lockTableSize <= 0)
    {
        int    msgID   = MSGID_DIRCFG_INVALID_LOCK_TABLE_SIZE;
        String message = getMessage(msgID, lockTableSize);
        throw new InitializationException(msgID, message);
    }
    String concurrencyStr =
         setProperty(PROPERTY_LOCK_MANAGER_TABLE_SIZE,
                     String.valueOf(lockTableSize));
    if (concurrencyStr == null)
    {
      return LockManager.DEFAULT_CONCURRENCY_LEVEL;
    }
    else
    {
      try
      {
        return Integer.parseInt(concurrencyStr);
      }
      catch (Exception e)
      {
        return LockManager.DEFAULT_CONCURRENCY_LEVEL;
      }
    }
  }
  /**
   * Retrieves the list of access loggers that should be enabled in
   * the server during the startup process.  Note that these loggers
   * will not be automatically disabled when startup is complete, so
   * if they are no longer needed then they should be manually removed
   * from the server using the
   * {@code AccessLogger.removeAccessLogPublisher} method.
   *
   * @return  The list of access loggers that should be enabled in the
   *          server during the startup process.
   */
  public List<AccessLogPublisher> getAccessLoggers()
  {
    return accessLoggers;
  }
  /**
   * Adds the provided access logger to the set of loggers that should
   * be enabled in the server during the startup process.
   *
   * @param  accessLogger  The access logger that should be added to
   *                       the set of loggers enabled in the server
   *                       during the startup process.
   *
   * @throws  InitializationException  If the Directory Server is
   *                                   already running.
   */
  public void addAccessLogger(AccessLogPublisher accessLogger)
         throws InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_DIRCFG_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    accessLoggers.add(accessLogger);
  }
  /**
   * Retrieves the list of error loggers that should be enabled in
   * the server during the startup process.  Note that these loggers
   * will not be automatically disabled when startup is complete, so
   * if they are no longer needed then they should be manually removed
   * from the server using the
   * {@code ErrorLogger.removeErrorLogPublisher} method.
   *
   * @return  The list of error loggers that should be enabled in the
   *          server during the startup process.
   */
  public List<ErrorLogPublisher> getErrorLoggers()
  {
    return errorLoggers;
  }
  /**
   * Adds the provided error logger to the set of loggers that should
   * be enabled in the server during the startup process.
   *
   * @param  errorLogger  The error logger that should be added to the
   *                      set of loggers enabled in the server during
   *                      the startup process.
   *
   * @throws  InitializationException  If the Directory Server is
   *                                   already running.
   */
  public void addErrorLogger(ErrorLogPublisher errorLogger)
         throws InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_DIRCFG_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    errorLoggers.add(errorLogger);
  }
  /**
   * Retrieves the list of debug loggers that should be enabled in
   * the server during the startup process.  Note that these loggers
   * will not be automatically disabled when startup is complete, so
   * if they are no longer needed then they should be manually removed
   * from the server using the
   * {@code DebugLogger.removeDebugLogPublisher} method.
   *
   * @return  The list of debug loggers that should be enabled in the
   *          server during the startup process.
   */
  public List<DebugLogPublisher> getDebugLoggers()
  {
    return debugLoggers;
  }
  /**
   * Adds the provided debug logger to the set of loggers that should
   * be enabled in the server during the startup process.
   *
   * @param  debugLogger  The debug logger that should be added to
   *                      the set of loggers enabled in the server
   *                      during the startup process.
   *
   * @throws  InitializationException  If the Directory Server is
   *                                   already running.
   */
  public void addDebugLogger(DebugLogPublisher debugLogger)
         throws InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_DIRCFG_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    debugLoggers.add(debugLogger);
  }
}
opends/src/server/org/opends/server/types/LockManager.java
@@ -33,8 +33,10 @@
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static org.opends.server.loggers.debug.DebugLogger.*;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
@@ -88,49 +90,69 @@
  // Initialize all of the lock variables.
  // Initialize the lock table.
  static
  {
    // Determine the concurrency level to use.  We'll let it be
    // specified by a system property, but if it isn't specified then
    // we'll set the default value based on the number of CPUs in the
    // system.
    int concurrencyLevel = -1;
    String propertyStr =
         System.getProperty(PROPERTY_LOCK_MANAGER_CONCURRENCY_LEVEL);
    if (propertyStr != null)
    {
      try
      {
        concurrencyLevel = Integer.parseInt(propertyStr);
      } catch (Exception e) {}
    }
    if (concurrencyLevel <= 0)
    {
      concurrencyLevel =
           Math.max(DEFAULT_CONCURRENCY_LEVEL,
                    (2*Runtime.getRuntime().availableProcessors()));
    }
    // Set the lock table size either to a user-defined value from a
    // property or a hard-coded default.
    int lockTableSize = DEFAULT_INITIAL_TABLE_SIZE;
    propertyStr =
         System.getProperty(PROPERTY_LOCK_MANAGER_TABLE_SIZE);
    if (propertyStr != null)
    {
      try
      {
        lockTableSize = Integer.parseInt(propertyStr);
      } catch (Exception e) {}
    }
    // Create an empty table for holding the entry locks.
    DirectoryEnvironmentConfig environmentConfig =
         DirectoryServer.getEnvironmentConfig();
    lockTable = new ConcurrentHashMap<DN,ReentrantReadWriteLock>(
         lockTableSize, DEFAULT_LOAD_FACTOR, concurrencyLevel);
         environmentConfig.getLockManagerTableSize(),
         DEFAULT_LOAD_FACTOR,
         environmentConfig.getLockManagerConcurrencyLevel());
  }
  /**
   * Recreates the lock table.  This should be called only in the
   * case that the Directory Server is in the process of an in-core
   * restart because it will destroy the existing lock table.
   */
  public synchronized static void reinitializeLockTable()
  {
    ConcurrentHashMap<DN,ReentrantReadWriteLock> oldTable = lockTable;
    DirectoryEnvironmentConfig environmentConfig =
         DirectoryServer.getEnvironmentConfig();
    lockTable = new ConcurrentHashMap<DN,ReentrantReadWriteLock>(
         environmentConfig.getLockManagerTableSize(),
         DEFAULT_LOAD_FACTOR,
         environmentConfig.getLockManagerConcurrencyLevel());
    if  (! oldTable.isEmpty())
    {
      for (DN dn : oldTable.keySet())
      {
        try
        {
          ReentrantReadWriteLock lock = oldTable.get(dn);
          if (lock.isWriteLocked())
          {
            TRACER.debugWarning("Found stale write lock on " +
                                dn.toString());
          }
          else if (lock.getReadLockCount() > 0)
          {
            TRACER.debugWarning("Found stale read lock on " +
                                dn.toString());
          }
          else
          {
            TRACER.debugWarning("Found stale unheld lock on " +
                                dn.toString());
          }
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            TRACER.debugCaught(DebugLogLevel.ERROR, e);
          }
        }
      }
      oldTable.clear();
    }
  }
opends/src/server/org/opends/server/util/EmbeddedUtils.java
New file
@@ -0,0 +1,119 @@
/*
 * 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.util;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.DirectoryEnvironmentConfig;
import org.opends.server.types.InitializationException;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.UtilityMessages.*;
import static org.opends.server.util.ServerConstants.*;
/**
 * This class provides a number of utility methods for using OpenDS in an
 * embedded manner (i.e., running within the same JVM as another application and
 * controlled by that application).
 */
public final class EmbeddedUtils
{
  /**
   * Indicates whether the Directory Server is currently running.
   *
   * @return  {@code true} if the server is currently running, or {@code false}
   *          if not.
   */
  public static boolean isRunning()
  {
    return DirectoryServer.isRunning();
  }
  /**
   * Attempts to start the Directory Server.
   *
   * @param  config  The environment configuration to use for the server.
   *
   * @throws  ConfigException  If a configuration problem is detected during
   *                           the server initialization or startup process.
   *
   * @throws  InitializationException  If the Directory Server is already
   *                                   running, or if an error occurs during
   *                                   server initialization or startup.
   */
  public static void startServer(DirectoryEnvironmentConfig config)
         throws ConfigException, InitializationException
  {
    if (DirectoryServer.isRunning())
    {
      int    msgID   = MSGID_EMBEDUTILS_SERVER_ALREADY_RUNNING;
      String message = getMessage(msgID);
      throw new InitializationException(msgID, message);
    }
    DirectoryServer directoryServer = DirectoryServer.reinitialize(config);
    directoryServer.startServer();
  }
  /**
   * Attempts to stop the Directory Server.
   *
   * @param  className  The name of the class that initiated the shutdown.
   * @param  reason     A message explaining the reason for the shutdown.
   */
  public static void stopServer(String className, String reason)
  {
    DirectoryServer.shutDown(className, reason);
  }
  /**
   * Attempts to restart the Directory Server.  This will perform an in-core
   * restart in which the existing server instance will be shut down, a new
   * instance will be created, and it will be reinitialized and restarted.
   *
   * @param  className  The name of the class that initiated the restart.
   * @param  reason     A message explaining the reason for the retart.
   * @param  config     The environment configuration to use for the new server
   *                    instance.
   */
  public static void restartServer(String className, String reason,
                                   DirectoryEnvironmentConfig config)
  {
    DirectoryServer.restart(className, reason, config);
  }
}
opends/src/server/org/opends/server/util/ServerConstants.java
@@ -2431,6 +2431,25 @@
  /**
   * The name of the system property that can be used to specify the
   * fully-qualified name of theclass that provides the Director Server config
   * handler implementation.
   */
  public static final String PROPERTY_CONFIG_CLASS =
       "org.opends.server.ConfigClass";
  /**
   * The name of the system property that can be used to specify the path to the
   * configuration file that should be used to initialize the config handler.
   */
  public static final String PROPERTY_CONFIG_FILE =
       "org.opends.server.ConfigFile";
  /**
   * The name of the system property that can be used to disable any connection
   * handler that may be enabled in the server configuration.  This may be used
   * to start the server in a mode where it will not accept any external
opends/src/server/org/opends/server/util/StaticUtils.java
@@ -2264,15 +2264,7 @@
   */
  public static boolean mayUseExec()
  {
    String s = System.getProperty(PROPERTY_DISABLE_EXEC);
    if (s == null)
    {
      return true;
    }
    s = toLowerCase(s);
    return (s.equals("false") || s.equals("off") || s.equals("no") ||
            s.equals("0"));
    return (! DirectoryServer.getEnvironmentConfig().disableExec());
  }
opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
@@ -69,12 +69,14 @@
import org.opends.server.protocols.ldap.BindResponseProtocolOp;
import org.opends.server.tools.LDAPModify;
import org.opends.server.tools.dsconfig.DSConfig;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryEnvironmentConfig;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.FilePermission;
import org.opends.server.types.InitializationException;
import org.opends.server.types.OperatingSystem;
import org.opends.server.types.ResultCode;
import org.opends.server.util.EmbeddedUtils;
import static org.testng.Assert.*;
@@ -304,53 +306,27 @@
    serverJmxSocket.close();
    serverLdapsSocket.close();
    // Actually start the server and set a variable that will prevent us from
    // needing to do it again.
    System.setProperty(PROPERTY_SERVER_ROOT, testRoot.getAbsolutePath());
    System.setProperty(PROPERTY_FORCE_DAEMON_THREADS, "true");
    String configClass = ConfigFileHandler.class.getName();
    String configFile  = testConfigDir.getAbsolutePath() + File.separator +
                         "config.ldif";
    // Create a configuration for the server.
    DirectoryEnvironmentConfig config = new DirectoryEnvironmentConfig();
    config.setServerRoot(testRoot);
    config.setForceDaemonThreads(true);
    config.setConfigClass(ConfigFileHandler.class);
    config.setConfigFile(new File(testConfigDir, "config.ldif"));
    DirectoryServer directoryServer = DirectoryServer.getInstance();
    directoryServer.bootstrapServer();
    directoryServer.initializeConfiguration(configClass, configFile);
    String debugTarget = System.getProperty("org.opends.test.debug.target");
    if(debugTarget != null)
    {
      System.setProperty("org.opends.server.debug.enabled", "true");
      System.setProperty("org.opends.server.debug.target.1", debugTarget);
    }
    try
    {
    TextDebugLogPublisher startupDebugPublisher =
        TextDebugLogPublisher.getStartupTextDebugPublisher(
            TestListener.DEBUG_TEXT_WRITER);
    DebugLogger.removeAllDebugLogPublishers();
    DebugLogger.addDebugLogPublisher(startupDebugPublisher);
    TextErrorLogPublisher startupErrorPublisher =
        TextErrorLogPublisher.getStartupTextErrorPublisher(
            TestListener.ERROR_TEXT_WRITER);
    ErrorLogger.removeAllErrorLogPublishers();
    ErrorLogger.addErrorLogPublisher(startupErrorPublisher);
    TextAccessLogPublisher startupAccessPublisher =
    config.addAccessLogger(
        TextAccessLogPublisher.getStartupTextAccessPublisher(
            TestListener.ACCESS_TEXT_WRITER, false);
    AccessLogger.removeAllAccessLogPublishers();
    AccessLogger.addAccessLogPublisher(startupAccessPublisher);
    }
    catch(Exception e)
    {
      System.out.println("Error installing test log publishers: " +
          e.toString());
    }
              TestListener.ACCESS_TEXT_WRITER, false));
    directoryServer.startServer();
    config.addErrorLogger(
         TextErrorLogPublisher.getStartupTextErrorPublisher(
              TestListener.ERROR_TEXT_WRITER));
    config.addDebugLogger(
         TextDebugLogPublisher.getStartupTextDebugPublisher(
              TestListener.DEBUG_TEXT_WRITER));
    EmbeddedUtils.startServer(config);
    assertTrue(InvocationCounterPlugin.startupCalled());