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

coulbeck
21.23.2007 19fb5731ffbe883fe8375ee9b785984c8ccf1880
opends/src/server/org/opends/server/core/BackendConfigManager.java
@@ -31,42 +31,38 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.opends.server.api.Backend;
import org.opends.server.api.BackendInitializationListener;
import org.opends.server.api.ConfigAddListener;
import org.opends.server.api.ConfigChangeListener;
import org.opends.server.api.ConfigDeleteListener;
import org.opends.server.api.ConfigHandler;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.config.BooleanConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.DNConfigAttribute;
import org.opends.server.config.MultiChoiceConfigAttribute;
import org.opends.server.config.StringConfigAttribute;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.WritabilityMode;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigConstants;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import org.opends.server.types.DebugLogLevel;
import static org.opends.server.loggers.Error.*;
import org.opends.server.types.*;
import static org.opends.server.loggers.Error.logError;
import static org.opends.server.messages.ConfigMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import static org.opends.server.util.StaticUtils.*;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.server.ServerManagementContext;
import org.opends.server.admin.server.ServerManagedObject;
import org.opends.server.admin.server.ConfigExceptionFactory;
import org.opends.server.admin.server.ServerManagedObjectDecodingException;
import org.opends.server.admin.std.server.BackendCfg;
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.admin.std.meta.BackendCfgDefn;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.DefinitionDecodingException;
/**
@@ -76,8 +72,10 @@
 * started, and then will manage any changes to them while the server is
 * running.
 */
public class BackendConfigManager
       implements ConfigChangeListener, ConfigAddListener, ConfigDeleteListener
public class BackendConfigManager implements
     ConfigurationChangeListener<BackendCfg>,
     ConfigurationAddListener<BackendCfg>,
     ConfigurationDeleteListener<BackendCfg>
{
@@ -86,9 +84,6 @@
  // backend implementations.
  private ConcurrentHashMap<DN,Backend> registeredBackends;
  // The DN of the associated configuration entry.
  private DN configEntryDN;
  /**
@@ -118,13 +113,21 @@
    registeredBackends = new ConcurrentHashMap<DN,Backend>();
    // Create an internal server management context and retrieve
    // the root configuration.
    ServerManagementContext context = ServerManagementContext.getInstance();
    RootCfg root = context.getRootConfiguration();
    // Register add and delete listeners.
    root.addBackendAddListener(this);
    root.addBackendDeleteListener(this);
    // Get the configuration entry that is at the root of all the backends in
    // the server.
    ConfigEntry backendRoot;
    try
    {
      configEntryDN = DN.decode(DN_BACKEND_BASE);
      DN configEntryDN = DN.decode(ConfigConstants.DN_BACKEND_BASE);
      backendRoot   = DirectoryServer.getConfigEntry(configEntryDN);
    }
    catch (Exception e)
@@ -137,6 +140,7 @@
      int    msgID   = MSGID_CONFIG_BACKEND_CANNOT_GET_CONFIG_BASE;
      String message = getMessage(msgID, stackTraceToSingleLineString(e));
      throw new ConfigException(msgID, message, e);
    }
@@ -151,701 +155,313 @@
    }
    // Register as an add and delete listener for the base entry so that we can
    // be notified if new backends are added or existing backends are removed.
    backendRoot.registerAddListener(this);
    backendRoot.registerDeleteListener(this);
    // Iterate through the set of immediate children below the backend config
    // root.
    for (ConfigEntry backendEntry : backendRoot.getChildren().values())
    // Initialize existing backends.
    for (String name : root.listBackends())
    {
      DN backendDN = backendEntry.getDN();
      // Get the handler's configuration.
      // This will decode and validate its properties.
      BackendCfg backendCfg = root.getBackend(name);
      DN backendDN = backendCfg.dn();
      String backendID = backendCfg.getBackendId();
      // Register as a change listener for this backend entry so that we will
      // be notified of any changes that may be made to it.
      backendEntry.registerChangeListener(this);
      // Register as a change listener for this backend so that we can be
      // notified when it is disabled or enabled.
      backendCfg.addChangeListener(this);
      // Check to see if this entry appears to contain a backend configuration.
      // If not, log a warning and skip it.
      try
      // Ignore this handler if it is disabled.
      if (backendCfg.isBackendEnabled())
      {
        SearchFilter backendFilter =
             SearchFilter.createFilterFromString("(objectClass=" + OC_BACKEND +
                                                 ")");
        if (! backendFilter.matchesEntry(backendEntry.getEntry()))
        // If there is already a backend registered with the specified ID,
        // then log an error and skip it.
        if (DirectoryServer.hasBackend(backendCfg.getBackendId()))
        {
          int msgID = MSGID_CONFIG_BACKEND_ENTRY_DOES_NOT_HAVE_BACKEND_CONFIG;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
          continue;
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        int msgID = MSGID_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY;
        String message = getMessage(msgID, String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
      }
      // See if the entry contains an attribute that indicates whether the
      // backend should be enabled.  If it does not, or if it is not set to
      // "true", then skip it.
      int msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_ENABLED;
      BooleanConfigAttribute enabledStub =
           new BooleanConfigAttribute(ATTR_BACKEND_ENABLED, getMessage(msgID),
                                      false);
      try
      {
        BooleanConfigAttribute enabledAttr =
             (BooleanConfigAttribute)
             backendEntry.getConfigAttribute(enabledStub);
        if (enabledAttr == null)
        {
          // The attribute is not present, so this backend will be disabled.
          // Log a message and continue.
          msgID = MSGID_CONFIG_BACKEND_NO_ENABLED_ATTR;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
          continue;
        }
        else if (! enabledAttr.activeValue())
        {
          // The backend is explicitly disabled.  Log a mild warning and
          // continue.
          msgID = MSGID_CONFIG_BACKEND_DISABLED;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.INFORMATIONAL, message, msgID);
          continue;
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE;
        String message = getMessage(msgID, String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
      }
      // See if the entry contains an attribute that specifies the backend ID.
      // If it does not, then log an error and skip it.
      msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BACKEND_ID;
      String backendID = null;
      StringConfigAttribute idStub =
           new StringConfigAttribute(ATTR_BACKEND_ID, getMessage(msgID),
                                     true, false, true);
      try
      {
        StringConfigAttribute idAttr =
             (StringConfigAttribute) backendEntry.getConfigAttribute(idStub);
        if (idAttr == null)
        {
          msgID = MSGID_CONFIG_BACKEND_NO_BACKEND_ID;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
          continue;
        }
        else
        {
          backendID = idAttr.activeValue();
          // If there is already a backend registered with the specified ID,
          // then log an error and skip it.
          if (DirectoryServer.hasBackend(backendID))
          {
            msgID = MSGID_CONFIG_BACKEND_DUPLICATE_BACKEND_ID;
            String message = getMessage(msgID, backendID,
                                        String.valueOf(backendDN));
            logError(ErrorLogCategory.CONFIGURATION,
                     ErrorLogSeverity.SEVERE_WARNING, message, msgID);
            continue;
          }
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BACKEND_ID;
        String message = getMessage(msgID, String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
      }
      // Get the writability mode for this backend.  It must be provided.
      LinkedHashSet<String> writabilityModes = new LinkedHashSet<String>(3);
      writabilityModes.add(WritabilityMode.ENABLED.toString());
      writabilityModes.add(WritabilityMode.DISABLED.toString());
      writabilityModes.add(WritabilityMode.INTERNAL_ONLY.toString());
      msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_WRITABILITY;
      WritabilityMode writabilityMode = null;
      MultiChoiceConfigAttribute writabilityStub =
           new MultiChoiceConfigAttribute(ATTR_BACKEND_WRITABILITY_MODE,
                                          getMessage(msgID), true, false, false,
                                          writabilityModes);
      try
      {
        MultiChoiceConfigAttribute writabilityAttr =
             (MultiChoiceConfigAttribute)
             backendEntry.getConfigAttribute(writabilityStub);
        if (writabilityAttr == null)
        {
          msgID = MSGID_CONFIG_BACKEND_NO_WRITABILITY_MODE;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_ERROR, message, msgID);
          continue;
        }
        else
        {
          writabilityMode =
               WritabilityMode.modeForName(writabilityAttr.activeValue());
          if (writabilityMode == null)
          {
            msgID = MSGID_CONFIG_BACKEND_INVALID_WRITABILITY_MODE;
            String message =
                 getMessage(msgID, String.valueOf(backendDN),
                            String.valueOf(writabilityAttr.activeValue()));
            logError(ErrorLogCategory.CONFIGURATION,
                     ErrorLogSeverity.SEVERE_ERROR, message, msgID);
            continue;
          }
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_WRITABILITY;
        String message = getMessage(msgID, String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
      }
      // See if the entry contains an attribute that specifies the base DNs for
      // the backend.  If it does not, then log an error and skip it.
      msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS;
      DN[] baseDNs = null;
      DNConfigAttribute baseDNStub =
           new DNConfigAttribute(ATTR_BACKEND_BASE_DN, getMessage(msgID),
                                 true, true, true);
      try
      {
        DNConfigAttribute baseDNAttr =
             (DNConfigAttribute) backendEntry.getConfigAttribute(baseDNStub);
        if (baseDNAttr == null)
        {
          msgID = MSGID_CONFIG_BACKEND_NO_BASE_DNS;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_ERROR, message, msgID);
          continue;
        }
        else
        {
          List<DN> dnList = baseDNAttr.activeValues();
          baseDNs = new DN[dnList.size()];
          dnList.toArray(baseDNs);
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BASE_DNS;
        String message = getMessage(msgID, String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
      }
      // See if the entry contains an attribute that specifies the class name
      // for the backend implementation.  If it does, then load it and make sure
      // that it's a valid backend implementation.  There is no such attribute,
      // the specified class cannot be loaded, or it does not contain a valid
      // backend implementation, then log an error and skip it.
      String className;
      msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_CLASS;
      StringConfigAttribute classStub =
           new StringConfigAttribute(ATTR_BACKEND_CLASS, getMessage(msgID),
                                     true, false, true);
      try
      {
        StringConfigAttribute classAttr =
             (StringConfigAttribute) backendEntry.getConfigAttribute(classStub);
        if (classAttr == null)
        {
          msgID = MSGID_CONFIG_BACKEND_NO_CLASS_ATTR;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_ERROR, message, msgID);
          continue;
        }
        else
        {
          className = classAttr.activeValue();
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_GET_CLASS;
        String message = getMessage(msgID, String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
      }
      Backend backend;
      try
      {
        Class backendClass = DirectoryServer.loadClass(className);
        backend = (Backend) backendClass.newInstance();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
        String message = getMessage(msgID, String.valueOf(className),
                                    String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
      }
      // If this backend is a configuration manager, then we don't want to do
      // any more with it because the configuration will have already been
      // started.
      if (backend instanceof ConfigHandler)
      {
        continue;
      }
      // Set the backend ID and writability mode for this backend.
      backend.setBackendID(backendID);
      backend.setWritabilityMode(writabilityMode);
      // Acquire a shared lock on this backend.  This will prevent operations
      // like LDIF import or restore from occurring while the backend is active.
      try
      {
        String lockFile = LockFileManager.getBackendLockFileName(backend);
        StringBuilder failureReason = new StringBuilder();
        if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
        {
          msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
          int msgID = MSGID_CONFIG_BACKEND_DUPLICATE_BACKEND_ID;
          String message = getMessage(msgID, backendID,
                                      String.valueOf(failureReason));
                                      String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
          // FIXME -- Do we need to send an admin alert?
          continue;
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        // See if the entry contains an attribute that specifies the class name
        // for the backend implementation.  If it does, then load it and make
        // sure that it's a valid backend implementation.  There is no such
        // attribute, the specified class cannot be loaded, or it does not
        // contain a valid backend implementation, then log an error and skip
        // it.
        String className = backendCfg.getBackendClass();
        Class backendClass;
        Backend backend;
        try
        {
          debugCaught(DebugLogLevel.ERROR, e);
          backendClass = DirectoryServer.loadClass(className);
          backend = (Backend) backendClass.newInstance();
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
          int msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
          String message = getMessage(msgID, String.valueOf(className),
                                      String.valueOf(backendDN),
                                      stackTraceToSingleLineString(e));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_ERROR,
                   message, msgID);
          continue;
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
        String message = getMessage(msgID, backendID,
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION,
                 ErrorLogSeverity.SEVERE_WARNING, message, msgID);
        // FIXME -- Do we need to send an admin alert?
        continue;
      }
      // Perform the necessary initialization for the backend entry.
      try
      {
        backend.initializeBackend(backendEntry, baseDNs);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        // If this backend is a configuration manager, then we don't want to do
        // any more with it because the configuration will have already been
        // started.
        if (backend instanceof ConfigHandler)
        {
          debugCaught(DebugLogLevel.ERROR, e);
          continue;
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_INITIALIZE;
        String message = getMessage(msgID, String.valueOf(className),
                                    String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        // See if the entry contains an attribute that specifies the writability
        // mode.
        WritabilityMode writabilityMode = WritabilityMode.ENABLED;
        BackendCfgDefn.BackendWritabilityMode bwm =
             backendCfg.getBackendWritabilityMode();
        switch (bwm)
        {
          case DISABLED:
            writabilityMode = WritabilityMode.DISABLED;
            break;
          case ENABLED:
            writabilityMode = WritabilityMode.ENABLED;
            break;
          case INTERNAL_ONLY:
            writabilityMode = WritabilityMode.INTERNAL_ONLY;
            break;
        }
        // Set the backend ID and writability mode for this backend.
        backend.setBackendID(backendID);
        backend.setWritabilityMode(writabilityMode);
        // Acquire a shared lock on this backend.  This will prevent operations
        // like LDIF import or restore from occurring while the backend is
        // active.
        try
        {
          String lockFile = LockFileManager.getBackendLockFileName(backend);
          StringBuilder failureReason = new StringBuilder();
          if (! LockFileManager.releaseLock(lockFile, failureReason))
          if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
          {
            int msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
            String message = getMessage(msgID, backendID,
                                        String.valueOf(failureReason));
            logError(ErrorLogCategory.CONFIGURATION,
                     ErrorLogSeverity.SEVERE_WARNING, message, msgID);
            // FIXME -- Do we need to send an admin alert?
            continue;
          }
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
          int msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
          String message = getMessage(msgID, backendID,
                                      stackTraceToSingleLineString(e));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
          // FIXME -- Do we need to send an admin alert?
          continue;
        }
        // Perform the necessary initialization for the backend entry.
        DN[] baseDNs = new DN[backendCfg.getBackendBaseDN().size()];
        baseDNs = backendCfg.getBackendBaseDN().toArray(baseDNs);
        try
        {
          backend.initializeBackend(
               DirectoryServer.getConfigEntry(backendCfg.dn()), baseDNs);
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
          int msgID = MSGID_CONFIG_BACKEND_CANNOT_INITIALIZE;
          String message = getMessage(msgID, String.valueOf(className),
                                      String.valueOf(backendDN),
                                      stackTraceToSingleLineString(e));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_ERROR,
                   message, msgID);
          try
          {
            String lockFile = LockFileManager.getBackendLockFileName(backend);
            StringBuilder failureReason = new StringBuilder();
            if (! LockFileManager.releaseLock(lockFile, failureReason))
            {
              msgID = MSGID_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK;
              message = getMessage(msgID, backendID,
                                   String.valueOf(failureReason));
              logError(ErrorLogCategory.CONFIGURATION,
                       ErrorLogSeverity.SEVERE_WARNING, message, msgID);
              // FIXME -- Do we need to send an admin alert?
            }
          }
          catch (Exception e2)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, e2);
            }
            msgID = MSGID_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK;
            message = getMessage(msgID, backendID,
                                 String.valueOf(failureReason));
                                 stackTraceToSingleLineString(e2));
            logError(ErrorLogCategory.CONFIGURATION,
                     ErrorLogSeverity.SEVERE_WARNING, message, msgID);
            // FIXME -- Do we need to send an admin alert?
          }
          continue;
        }
        catch (Exception e2)
        // Notify any backend initialization listeners.
        for (BackendInitializationListener listener :
             DirectoryServer.getBackendInitializationListeners())
        {
          listener.performBackendInitializationProcessing(backend);
        }
        // Register the backend with the server.
        try
        {
          DirectoryServer.registerBackend(backend);
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e2);
            debugCaught(DebugLogLevel.ERROR, e);
          }
          msgID = MSGID_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK;
          message = getMessage(msgID, backendID,
                               stackTraceToSingleLineString(e2));
          int msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
          String message = getMessage(msgID, backendID,
                                      stackTraceToSingleLineString(e));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
                   ErrorLogSeverity.SEVERE_ERROR,
                   message, msgID);
          // FIXME -- Do we need to send an admin alert?
        }
        continue;
        // Put this backend in the hash so that we will be able to find it if it
        // is altered.
        registeredBackends.put(backendDN, backend);
      }
      // Notify any backend initialization listeners.
      for (BackendInitializationListener listener :
           DirectoryServer.getBackendInitializationListeners())
      else
      {
        listener.performBackendInitializationProcessing(backend);
        // The backend is explicitly disabled.  Log a mild warning and
        // continue.
        int msgID = MSGID_CONFIG_BACKEND_DISABLED;
        String message = getMessage(msgID, String.valueOf(backendDN));
        logError(ErrorLogCategory.CONFIGURATION,
                 ErrorLogSeverity.INFORMATIONAL, message, msgID);
      }
      // Register the backend with the server.
      try
      {
        DirectoryServer.registerBackend(backend);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
        String message = getMessage(msgID, backendID,
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        // FIXME -- Do we need to send an admin alert?
      }
      // Put this backend in the hash so that we will be able to find it if it
      // is altered.
      registeredBackends.put(backendDN, backend);
    }
  }
  /**
   * Indicates whether the configuration entry that will result from a proposed
   * modification is acceptable to this change listener.
   *
   * @param  configEntry         The configuration entry that will result from
   *                             the requested update.
   * @param  unacceptableReason  A buffer to which this method can append a
   *                             human-readable message explaining why the
   *                             proposed change is not acceptable.
   *
   * @return  <CODE>true</CODE> if the proposed entry contains an acceptable
   *          configuration, or <CODE>false</CODE> if it does not.
   * {@inheritDoc}
   */
  public boolean configChangeIsAcceptable(ConfigEntry configEntry,
                                          StringBuilder unacceptableReason)
  public boolean isConfigurationChangeAcceptable(
       BackendCfg configEntry,
       List<String> unacceptableReason)
  {
    DN backendDN = configEntry.getDN();
    DN backendDN = configEntry.dn();
    // Check to see if this entry appears to contain a backend configuration.
    // If not, log a warning and skip it.
    try
    Set<DN> baseDNs = configEntry.getBackendBaseDN();
    // See if the backend is registered with the server.  If it is, then
    // see what's changed and whether those changes are acceptable.
    Backend backend = registeredBackends.get(backendDN);
    if (backend != null)
    {
      SearchFilter backendFilter =
           SearchFilter.createFilterFromString("(objectClass=" + OC_BACKEND +
                                               ")");
      if (! backendFilter.matchesEntry(configEntry.getEntry()))
      LinkedHashSet<DN> removedDNs = new LinkedHashSet<DN>();
      for (DN dn : backend.getBaseDNs())
      {
        int msgID = MSGID_CONFIG_BACKEND_ENTRY_DOES_NOT_HAVE_BACKEND_CONFIG;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
        removedDNs.add(dn);
      }
      int msgID = MSGID_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // See if the entry contains an attribute that indicates whether the
    // backend should be enabled.  If it does not, or if it is not set to
    // "true", then skip it.
    int msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_ENABLED;
    BooleanConfigAttribute enabledStub =
         new BooleanConfigAttribute(ATTR_BACKEND_ENABLED, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute enabledAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(enabledStub);
      if (enabledAttr == null)
      LinkedHashSet<DN> addedDNs = new LinkedHashSet<DN>();
      for (DN dn : baseDNs)
      {
        // The attribute is not present, so this backend will be disabled.
        msgID = MSGID_CONFIG_BACKEND_NO_ENABLED_ATTR;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
        addedDNs.add(dn);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // See if the entry contains an attribute that specifies the backend ID.  If
    // it does not, then reject it.
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BACKEND_ID;
    StringConfigAttribute idStub =
         new StringConfigAttribute(ATTR_BACKEND_ID, getMessage(msgID), true,
                                   false, true);
    try
    {
      StringConfigAttribute idAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(idStub);
      if (idAttr == null)
      Iterator<DN> iterator = removedDNs.iterator();
      while (iterator.hasNext())
      {
        // The attribute is not present.  We will not allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_BACKEND_ID;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BACKEND_ID;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // See if the entry contains an attribute that specifies the writability
    // mode.  If it does not, then reject it.
    LinkedHashSet<String> writabilityModes = new LinkedHashSet<String>(3);
    writabilityModes.add(WritabilityMode.ENABLED.toString());
    writabilityModes.add(WritabilityMode.DISABLED.toString());
    writabilityModes.add(WritabilityMode.INTERNAL_ONLY.toString());
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_WRITABILITY;
    MultiChoiceConfigAttribute writabilityStub =
         new MultiChoiceConfigAttribute(ATTR_BACKEND_WRITABILITY_MODE,
                                        getMessage(msgID), true, false, false,
                                        writabilityModes);
    try
    {
      MultiChoiceConfigAttribute writabilityAttr =
           (MultiChoiceConfigAttribute)
           configEntry.getConfigAttribute(writabilityStub);
      if (writabilityAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_WRITABILITY_MODE;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_WRITABILITY;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // See if the entry contains an attribute that specifies the set of base DNs
    // for the backend.  If it does not, then skip it.
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS;
    DNConfigAttribute baseDNStub =
         new DNConfigAttribute(ATTR_BACKEND_BASE_DN, getMessage(msgID), true,
                               true, true);
    try
    {
      DNConfigAttribute baseDNAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(baseDNStub);
      if (baseDNAttr == null)
      {
        // The attribute is not present.  We will not allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_BASE_DNS;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
      // See if the backend is registered with the server.  If it is, then
      // see what's changed and whether those changes are acceptable.
      Backend backend = registeredBackends.get(configEntryDN);
      if (backend != null)
      {
        LinkedHashSet<DN> removedDNs = new LinkedHashSet<DN>();
        for (DN dn : backend.getBaseDNs())
        DN dn = iterator.next();
        if (addedDNs.remove(dn))
        {
          removedDNs.add(dn);
        }
        LinkedHashSet<DN> addedDNs = new LinkedHashSet<DN>();
        for (DN dn : baseDNAttr.pendingValues())
        {
          addedDNs.add(dn);
        }
        Iterator<DN> iterator = removedDNs.iterator();
        while (iterator.hasNext())
        {
          DN dn = iterator.next();
          if (addedDNs.remove(dn))
          {
            iterator.remove();
          }
        }
        for (DN dn : addedDNs)
        {
          try
          {
            DirectoryServer.registerBaseDN(dn, backend, false, true);
          }
          catch (DirectoryException de)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, de);
            }
            unacceptableReason.append(de.getMessage());
            return false;
          }
        }
        for (DN dn : removedDNs)
        {
          try
          {
            DirectoryServer.deregisterBaseDN(dn, true);
          }
          catch (DirectoryException de)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, de);
            }
            unacceptableReason.append(de.getMessage());
            return false;
          }
          iterator.remove();
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      for (DN dn : addedDNs)
      {
        debugCaught(DebugLogLevel.ERROR, e);
        try
        {
          DirectoryServer.registerBaseDN(dn, backend, false, true);
        }
        catch (DirectoryException de)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, de);
          }
          unacceptableReason.add(de.getMessage());
          return false;
        }
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BASE_DNS;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                stackTraceToSingleLineString(e)));
      return false;
      for (DN dn : removedDNs)
      {
        try
        {
          DirectoryServer.deregisterBaseDN(dn, true);
        }
        catch (DirectoryException de)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, de);
          }
          unacceptableReason.add(de.getMessage());
          return false;
        }
      }
    }
@@ -854,47 +470,15 @@
    // that it's a valid backend implementation.  There is no such attribute,
    // the specified class cannot be loaded, or it does not contain a valid
    // backend implementation, then log an error and skip it.
    String className;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_CLASS;
    StringConfigAttribute classStub =
         new StringConfigAttribute(ATTR_BACKEND_CLASS, getMessage(msgID),
                                   true, false, true);
    try
    {
      StringConfigAttribute classAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(classStub);
      if (classAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_CLASS_ATTR;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
      else
      {
        className = classAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_GET_CLASS;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    String className = configEntry.getBackendClass();
    try
    {
      Class backendClass = DirectoryServer.loadClass(className);
      if (! Backend.class.isAssignableFrom(backendClass))
      {
        msgID = MSGID_CONFIG_BACKEND_CLASS_NOT_BACKEND;
        unacceptableReason.append(getMessage(msgID, String.valueOf(className),
                                             String.valueOf(backendDN)));
        int msgID = MSGID_CONFIG_BACKEND_CLASS_NOT_BACKEND;
        unacceptableReason.add(getMessage(msgID, String.valueOf(className),
                                          String.valueOf(backendDN)));
        return false;
      }
    }
@@ -905,10 +489,10 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
      unacceptableReason.append(getMessage(msgID, String.valueOf(className),
                                           String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      int msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
      unacceptableReason.add(getMessage(msgID, String.valueOf(className),
                                        String.valueOf(backendDN),
                                        stackTraceToSingleLineString(e)));
      return false;
    }
@@ -920,79 +504,24 @@
  }
  /**
   * Attempts to apply a new configuration to this Directory Server component
   * based on the provided changed entry.
   *
   * @param  configEntry  The configuration entry that containing the updated
   *                      configuration for this component.
   *
   * @return  Information about the result of processing the configuration
   *          change.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry)
  public ConfigChangeResult applyConfigurationChange(BackendCfg cfg)
  {
    DN                backendDN           = configEntry.getDN();
    DN                backendDN           = cfg.dn();
    Backend           backend             = registeredBackends.get(backendDN);
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
    // Check to see if this entry appears to contain a backend configuration.
    // If not, log a warning and skip it.
    try
    {
      SearchFilter backendFilter =
           SearchFilter.createFilterFromString("(objectClass=" + OC_BACKEND +
                                               ")");
      if (! backendFilter.matchesEntry(configEntry.getEntry()))
      {
        int msgID = MSGID_CONFIG_BACKEND_ENTRY_DOES_NOT_HAVE_BACKEND_CONFIG;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = MSGID_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    // See if the entry contains an attribute that indicates whether the
    // backend should be enabled.
    boolean needToEnable = false;
    int msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_ENABLED;
    BooleanConfigAttribute enabledStub =
         new BooleanConfigAttribute(ATTR_BACKEND_ENABLED, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute enabledAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(enabledStub);
      if (enabledAttr == null)
      {
        // The attribute is not present.  We won't allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_ENABLED_ATTR;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else if (enabledAttr.pendingValue())
      if (cfg.isBackendEnabled())
      {
        // The backend is marked as enabled.  See if that is already true.
        if (backend == null)
@@ -1029,7 +558,7 @@
            StringBuilder failureReason = new StringBuilder();
            if (! LockFileManager.releaseLock(lockFile, failureReason))
            {
              msgID = MSGID_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK;
              int msgID = MSGID_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK;
              String message = getMessage(msgID, backend.getBackendID(),
                                          String.valueOf(failureReason));
              logError(ErrorLogCategory.CONFIGURATION,
@@ -1044,7 +573,7 @@
              debugCaught(DebugLogLevel.ERROR, e2);
            }
            msgID = MSGID_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK;
            int msgID = MSGID_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK;
            String message = getMessage(msgID, backend.getBackendID(),
                                        stackTraceToSingleLineString(e2));
            logError(ErrorLogCategory.CONFIGURATION,
@@ -1068,7 +597,7 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE;
      int msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
@@ -1078,141 +607,32 @@
    // See if the entry contains an attribute that specifies the backend ID for
    // the backend.
    String backendID = null;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BACKEND_ID;
    StringConfigAttribute idStub =
         new StringConfigAttribute(ATTR_BACKEND_ID, getMessage(msgID), true,
                                   false, true);
    try
    {
      StringConfigAttribute idAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(idStub);
      if (idAttr == null)
      {
        // The attribute is not present.  We won't allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_BACKEND_ID;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        backendID = idAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BACKEND_ID;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    String backendID = cfg.getBackendId();
    // See if the entry contains an attribute that specifies the writability
    // mode.
    LinkedHashSet<String> writabilityModes = new LinkedHashSet<String>(3);
    writabilityModes.add(WritabilityMode.ENABLED.toString());
    writabilityModes.add(WritabilityMode.DISABLED.toString());
    writabilityModes.add(WritabilityMode.INTERNAL_ONLY.toString());
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_WRITABILITY;
    WritabilityMode writabilityMode = null;
    MultiChoiceConfigAttribute writabilityStub =
         new MultiChoiceConfigAttribute(ATTR_BACKEND_WRITABILITY_MODE,
                                        getMessage(msgID), true, false, false,
                                        writabilityModes);
    try
    WritabilityMode writabilityMode = WritabilityMode.ENABLED;
    BackendCfgDefn.BackendWritabilityMode bwm =
         cfg.getBackendWritabilityMode();
    switch (bwm)
    {
      MultiChoiceConfigAttribute writabilityAttr =
           (MultiChoiceConfigAttribute)
           configEntry.getConfigAttribute(writabilityStub);
      if (writabilityStub == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_WRITABILITY_MODE;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        writabilityMode =
             WritabilityMode.modeForName(writabilityAttr.activeValue());
        if (writabilityMode == null)
        {
          msgID = MSGID_CONFIG_BACKEND_INVALID_WRITABILITY_MODE;
          messages.add(getMessage(msgID, String.valueOf(backendDN),
                            String.valueOf(writabilityAttr.activeValue())));
          resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
          return new ConfigChangeResult(resultCode, adminActionRequired,
                                        messages);
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_WRITABILITY;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
      case DISABLED:
        writabilityMode = WritabilityMode.DISABLED;
        break;
      case ENABLED:
        writabilityMode = WritabilityMode.ENABLED;
        break;
      case INTERNAL_ONLY:
        writabilityMode = WritabilityMode.INTERNAL_ONLY;
        break;
    }
    // See if the entry contains an attribute that specifies the base DNs for
    // the backend.
    DN[] baseDNs = null;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS;
    DNConfigAttribute baseDNStub =
         new DNConfigAttribute(ATTR_BACKEND_BASE_DN, getMessage(msgID), true,
                               true, true);
    try
    {
      DNConfigAttribute baseDNAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(baseDNStub);
      if (baseDNAttr == null)
      {
        // The attribute is not present.  We won't allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_BASE_DNS;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        List<DN> baseList = baseDNAttr.pendingValues();
        baseDNs = new DN[baseList.size()];
        baseList.toArray(baseDNs);
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BASE_DNS;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    Set<DN> baseList = cfg.getBackendBaseDN();
    DN[] baseDNs = new DN[baseList.size()];
    baseList.toArray(baseDNs);
    // See if the entry contains an attribute that specifies the class name
@@ -1220,42 +640,7 @@
    // that it's a valid backend implementation.  There is no such attribute,
    // the specified class cannot be loaded, or it does not contain a valid
    // backend implementation, then log an error and skip it.
    String className;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_CLASS;
    StringConfigAttribute classStub =
         new StringConfigAttribute(ATTR_BACKEND_CLASS, getMessage(msgID),
                                   true, false, true);
    try
    {
      StringConfigAttribute classAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(classStub);
      if (classAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_CLASS_ATTR;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        className = classAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_GET_CLASS;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired,
                                    messages);
    }
    String className = cfg.getBackendClass();
    // See if this backend is currently active and if so if the name of the
@@ -1274,7 +659,7 @@
            // It appears to be a valid backend class.  We'll return that the
            // change is successful, but indicate that some administrative
            // action is required.
            msgID = MSGID_CONFIG_BACKEND_ACTION_REQUIRED_TO_CHANGE_CLASS;
            int msgID = MSGID_CONFIG_BACKEND_ACTION_REQUIRED_TO_CHANGE_CLASS;
            messages.add(getMessage(msgID, String.valueOf(backendDN),
                                    backend.getClass().getName(), className));
            adminActionRequired = true;
@@ -1284,7 +669,7 @@
          else
          {
            // It is not a valid backend class.  This is an error.
            msgID = MSGID_CONFIG_BACKEND_CLASS_NOT_BACKEND;
            int msgID = MSGID_CONFIG_BACKEND_CLASS_NOT_BACKEND;
            messages.add(getMessage(msgID, String.valueOf(className),
                                    String.valueOf(backendDN)));
            resultCode = ResultCode.CONSTRAINT_VIOLATION;
@@ -1299,7 +684,7 @@
            debugCaught(DebugLogLevel.ERROR, e);
          }
          msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
          int msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
          messages.add(getMessage(msgID, String.valueOf(className),
                                  String.valueOf(backendDN),
                                  stackTraceToSingleLineString(e)));
@@ -1315,15 +700,16 @@
    // backend.  Try to do so.
    if (needToEnable)
    {
      Class backendClass;
      try
      {
        Class backendClass = DirectoryServer.loadClass(className);
        backendClass = DirectoryServer.loadClass(className);
        backend = (Backend) backendClass.newInstance();
      }
      catch (Exception e)
      {
        // It is not a valid backend class.  This is an error.
        msgID = MSGID_CONFIG_BACKEND_CLASS_NOT_BACKEND;
        int msgID = MSGID_CONFIG_BACKEND_CLASS_NOT_BACKEND;
        messages.add(getMessage(msgID, String.valueOf(className),
                                String.valueOf(backendDN)));
        resultCode = ResultCode.CONSTRAINT_VIOLATION;
@@ -1345,7 +731,7 @@
        StringBuilder failureReason = new StringBuilder();
        if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
        {
          msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
          int msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
          String message = getMessage(msgID, backendID,
                                      String.valueOf(failureReason));
          logError(ErrorLogCategory.CONFIGURATION,
@@ -1366,7 +752,7 @@
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
        int msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
        String message = getMessage(msgID, backendID,
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION,
@@ -1383,7 +769,8 @@
      try
      {
        backend.initializeBackend(configEntry, baseDNs);
        backend.initializeBackend(
             DirectoryServer.getConfigEntry(cfg.dn()), baseDNs);
      }
      catch (Exception e)
      {
@@ -1392,7 +779,7 @@
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_INITIALIZE;
        int msgID = MSGID_CONFIG_BACKEND_CANNOT_INITIALIZE;
        messages.add(getMessage(msgID, String.valueOf(className),
                                String.valueOf(backendDN),
                                stackTraceToSingleLineString(e)));
@@ -1452,7 +839,7 @@
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
        int msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
        String message = getMessage(msgID, backendID,
                                    stackTraceToSingleLineString(e));
@@ -1486,203 +873,33 @@
  }
  /**
   * Indicates whether the configuration entry that will result from a proposed
   * add is acceptable to this add listener.
   *
   * @param  configEntry         The configuration entry that will result from
   *                             the requested add.
   * @param  unacceptableReason  A buffer to which this method can append a
   *                             human-readable message explaining why the
   *                             proposed entry is not acceptable.
   *
   * @return  <CODE>true</CODE> if the proposed entry contains an acceptable
   *          configuration, or <CODE>false</CODE> if it does not.
   * {@inheritDoc}
   */
  public boolean configAddIsAcceptable(ConfigEntry configEntry,
                                       StringBuilder unacceptableReason)
  public boolean isConfigurationAddAcceptable(
       BackendCfg configEntry,
       List<String> unacceptableReason)
  {
    DN backendDN = configEntry.getDN();
    // Check to see if this entry appears to contain a backend configuration.
    // If not then fail.
    try
    {
      SearchFilter backendFilter =
           SearchFilter.createFilterFromString("(objectClass=" + OC_BACKEND +
                                               ")");
      if (! backendFilter.matchesEntry(configEntry.getEntry()))
      {
        int msgID = MSGID_CONFIG_BACKEND_ENTRY_DOES_NOT_HAVE_BACKEND_CONFIG;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = MSGID_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // See if the entry contains an attribute that indicates whether the
    // backend should be enabled.  If it does not, or if it is not set to
    // "true", then skip it.
    int msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_ENABLED;
    BooleanConfigAttribute enabledStub =
         new BooleanConfigAttribute(ATTR_BACKEND_ENABLED, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute enabledAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(enabledStub);
      if (enabledAttr == null)
      {
        // The attribute is not present.  We will not allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_ENABLED_ATTR;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    DN backendDN = configEntry.dn();
    // See if the entry contains an attribute that specifies the backend ID.  If
    // it does not, then skip it.
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BACKEND_ID;
    StringConfigAttribute idStub =
         new StringConfigAttribute(ATTR_BACKEND_ID, getMessage(msgID), true,
                                   false, true);
    try
    String backendID = configEntry.getBackendId();
    if (DirectoryServer.hasBackend(backendID))
    {
      StringConfigAttribute idAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(idStub);
      if (idAttr == null)
      {
        // The attribute is not present.  We will not allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_BACKEND_ID;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
      else
      {
        String backendID = idAttr.activeValue();
        if (DirectoryServer.hasBackend(backendID))
        {
          msgID = MSGID_CONFIG_BACKEND_DUPLICATE_BACKEND_ID;
          unacceptableReason.append(getMessage(msgID,
                                               String.valueOf(backendDN)));
          return false;
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BACKEND_ID;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // See if the entry contains an attribute that specifies the writability
    // mode.  If it does not, then reject it.
    LinkedHashSet<String> writabilityModes = new LinkedHashSet<String>(3);
    writabilityModes.add(WritabilityMode.ENABLED.toString());
    writabilityModes.add(WritabilityMode.DISABLED.toString());
    writabilityModes.add(WritabilityMode.INTERNAL_ONLY.toString());
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_WRITABILITY;
    MultiChoiceConfigAttribute writabilityStub =
         new MultiChoiceConfigAttribute(ATTR_BACKEND_WRITABILITY_MODE,
                                        getMessage(msgID), true, false, false,
                                        writabilityModes);
    try
    {
      MultiChoiceConfigAttribute writabilityAttr =
           (MultiChoiceConfigAttribute)
           configEntry.getConfigAttribute(writabilityStub);
      if (writabilityAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_WRITABILITY_MODE;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_WRITABILITY;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      int msgID = MSGID_CONFIG_BACKEND_DUPLICATE_BACKEND_ID;
      unacceptableReason.add(getMessage(msgID,
                                        String.valueOf(backendDN)));
      return false;
    }
    // See if the entry contains an attribute that specifies the set of base DNs
    // for the backend.  If it does not, then skip it.
    List<DN> baseDNs = null;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS;
    DNConfigAttribute baseDNStub =
         new DNConfigAttribute(ATTR_BACKEND_BASE_DN, getMessage(msgID), true,
                               true, true);
    try
    {
      DNConfigAttribute baseDNAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(baseDNStub);
      if (baseDNAttr == null)
      {
        // The attribute is not present.  We will not allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_BASE_DNS;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
      else
      {
        baseDNs = baseDNAttr.pendingValues();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BASE_DNS;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                stackTraceToSingleLineString(e)));
      return false;
    }
    Set<DN> baseList = configEntry.getBackendBaseDN();
    DN[] baseDNs = new DN[baseList.size()];
    baseList.toArray(baseDNs);
    // See if the entry contains an attribute that specifies the class name
@@ -1690,38 +907,7 @@
    // that it's a valid backend implementation.  There is no such attribute,
    // the specified class cannot be loaded, or it does not contain a valid
    // backend implementation, then log an error and skip it.
    String className;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_CLASS;
    StringConfigAttribute classStub =
         new StringConfigAttribute(ATTR_BACKEND_CLASS, getMessage(msgID),
                                   true, false, true);
    try
    {
      StringConfigAttribute classAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(classStub);
      if (classAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_CLASS_ATTR;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
      else
      {
        className = classAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_GET_CLASS;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    String className = configEntry.getBackendClass();
    Backend backend;
    try
@@ -1736,42 +922,42 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
      unacceptableReason.append(getMessage(msgID, String.valueOf(className),
                                           String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      int msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
      unacceptableReason.add(getMessage(msgID, String.valueOf(className),
                                        String.valueOf(backendDN),
                                        stackTraceToSingleLineString(e)));
      return false;
    }
    // If the backend is a configurable component, then make sure that its
    // configuration is valid.
    if (backend instanceof ConfigurableComponent)
    {
      ConfigurableComponent cc = (ConfigurableComponent) backend;
      LinkedList<String> errorMessages = new LinkedList<String>();
      if (! cc.hasAcceptableConfiguration(configEntry, errorMessages))
      {
        if (errorMessages.isEmpty())
        {
          msgID = MSGID_CONFIG_BACKEND_UNACCEPTABLE_CONFIG;
          unacceptableReason.append(getMessage(msgID,
                                               String.valueOf(configEntryDN)));
        }
        else
        {
          Iterator<String> iterator = errorMessages.iterator();
          unacceptableReason.append(iterator.next());
          while (iterator.hasNext())
          {
            unacceptableReason.append("  ");
            unacceptableReason.append(iterator.next());
          }
        }
        return false;
      }
    }
//    if (backend instanceof ConfigurableComponent)
//    {
//      ConfigurableComponent cc = (ConfigurableComponent) backend;
//      LinkedList<String> errorMessages = new LinkedList<String>();
//      if (! cc.hasAcceptableConfiguration(configEntry, errorMessages))
//      {
//        if (errorMessages.isEmpty())
//        {
//          int msgID = MSGID_CONFIG_BACKEND_UNACCEPTABLE_CONFIG;
//          unacceptableReason.add(getMessage(msgID,
//                                            String.valueOf(configEntryDN)));
//        }
//        else
//        {
//          Iterator<String> iterator = errorMessages.iterator();
//          unacceptableReason.add(iterator.next());
//          while (iterator.hasNext())
//          {
//            unacceptableReason.add("  ");
//            unacceptableReason.add(iterator.next());
//          }
//        }
//
//        return false;
//      }
//    }
    // Make sure that all of the base DNs are acceptable for use in the server.
@@ -1783,12 +969,12 @@
      }
      catch (DirectoryException de)
      {
        unacceptableReason.append(de.getMessage());
        unacceptableReason.add(de.getMessage());
        return false;
      }
      catch (Exception e)
      {
        unacceptableReason.append(stackTraceToSingleLineString(e));
        unacceptableReason.add(stackTraceToSingleLineString(e));
        return false;
      }
    }
@@ -1803,17 +989,11 @@
  /**
   * Attempts to apply a new configuration based on the provided added entry.
   *
   * @param  configEntry  The new configuration entry that contains the
   *                      configuration to apply.
   *
   * @return  Information about the result of processing the configuration
   *          change.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry)
  public ConfigChangeResult applyConfigurationAdd(BackendCfg cfg)
  {
    DN                backendDN           = configEntry.getDN();
    DN                backendDN           = cfg.dn();
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
@@ -1821,241 +1001,66 @@
    // Register as a change listener for this backend entry so that we will
    // be notified of any changes that may be made to it.
    configEntry.registerChangeListener(this);
    // Check to see if this entry appears to contain a backend configuration.
    // If not then fail.
    try
    {
      SearchFilter backendFilter =
           SearchFilter.createFilterFromString("(objectClass=" + OC_BACKEND +
                                               ")");
      if (! backendFilter.matchesEntry(configEntry.getEntry()))
      {
        int msgID = MSGID_CONFIG_BACKEND_ENTRY_DOES_NOT_HAVE_BACKEND_CONFIG;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = MSGID_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    cfg.addChangeListener(this);
    // See if the entry contains an attribute that indicates whether the
    // backend should be enabled.  If it does not, or if it is not set to
    // "true", then skip it.
    int msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_ENABLED;
    BooleanConfigAttribute enabledStub =
         new BooleanConfigAttribute(ATTR_BACKEND_ENABLED, getMessage(msgID),
                                    false);
    try
    if (!cfg.isBackendEnabled())
    {
      BooleanConfigAttribute enabledAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(enabledStub);
      if (enabledAttr == null)
      {
        // The attribute is not present, so this backend will be disabled.  We
        // will log a message to indicate that it won't be enabled and return.
        msgID = MSGID_CONFIG_BACKEND_NO_ENABLED_ATTR;
        String message = getMessage(msgID, String.valueOf(backendDN));
        logError(ErrorLogCategory.CONFIGURATION,
                 ErrorLogSeverity.SEVERE_WARNING, message, msgID);
        messages.add(message);
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else if (! enabledAttr.activeValue())
      {
        // The backend is explicitly disabled.  We will log a message to
        // indicate that it won't be enabled and return.
        msgID = MSGID_CONFIG_BACKEND_DISABLED;
        String message = getMessage(msgID, String.valueOf(backendDN));
        logError(ErrorLogCategory.CONFIGURATION,
                 ErrorLogSeverity.INFORMATIONAL, message, msgID);
        messages.add(message);
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      // The backend is explicitly disabled.  We will log a message to
      // indicate that it won't be enabled and return.
      int msgID = MSGID_CONFIG_BACKEND_DISABLED;
      String message = getMessage(msgID, String.valueOf(backendDN));
      logError(ErrorLogCategory.CONFIGURATION,
               ErrorLogSeverity.INFORMATIONAL, message, msgID);
      messages.add(message);
      return new ConfigChangeResult(resultCode, adminActionRequired,
                                    messages);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    // See if the entry contains an attribute that specifies the backend ID.  If
    // it does not, then skip it.
    String backendID;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BACKEND_ID;
    StringConfigAttribute idStub =
         new StringConfigAttribute(ATTR_BACKEND_ID, getMessage(msgID), true,
                                   false, true);
    try
    String backendID = cfg.getBackendId();
    if (DirectoryServer.hasBackend(backendID))
    {
      StringConfigAttribute idAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(idStub);
      if (idAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_BACKEND_ID;
        String message = getMessage(msgID, String.valueOf(backendDN));
        logError(ErrorLogCategory.CONFIGURATION,
                 ErrorLogSeverity.SEVERE_WARNING, message, msgID);
        messages.add(message);
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        backendID = idAttr.pendingValue();
        if (DirectoryServer.hasBackend(backendID))
        {
          msgID = MSGID_CONFIG_BACKEND_DUPLICATE_BACKEND_ID;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
          messages.add(message);
          return new ConfigChangeResult(resultCode, adminActionRequired,
                                        messages);
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BACKEND_ID;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
      int msgID = MSGID_CONFIG_BACKEND_DUPLICATE_BACKEND_ID;
      String message = getMessage(msgID, String.valueOf(backendDN));
      logError(ErrorLogCategory.CONFIGURATION,
               ErrorLogSeverity.SEVERE_WARNING, message, msgID);
      messages.add(message);
      return new ConfigChangeResult(resultCode, adminActionRequired,
                                    messages);
    }
    // See if the entry contains an attribute that specifies the writability
    // mode.
    LinkedHashSet<String> writabilityModes = new LinkedHashSet<String>(3);
    writabilityModes.add(WritabilityMode.ENABLED.toString());
    writabilityModes.add(WritabilityMode.DISABLED.toString());
    writabilityModes.add(WritabilityMode.INTERNAL_ONLY.toString());
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_WRITABILITY;
    WritabilityMode writabilityMode = null;
    MultiChoiceConfigAttribute writabilityStub =
         new MultiChoiceConfigAttribute(ATTR_BACKEND_WRITABILITY_MODE,
                                        getMessage(msgID), true, false, false,
                                        writabilityModes);
    try
    WritabilityMode writabilityMode = WritabilityMode.ENABLED;
    BackendCfgDefn.BackendWritabilityMode bwm =
         cfg.getBackendWritabilityMode();
    switch (bwm)
    {
      MultiChoiceConfigAttribute writabilityAttr =
           (MultiChoiceConfigAttribute)
           configEntry.getConfigAttribute(writabilityStub);
      if (writabilityAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_WRITABILITY_MODE;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        writabilityMode =
             WritabilityMode.modeForName(writabilityAttr.activeValue());
        if (writabilityMode == null)
        {
          msgID = MSGID_CONFIG_BACKEND_INVALID_WRITABILITY_MODE;
          messages.add(getMessage(msgID, String.valueOf(backendDN),
                            String.valueOf(writabilityAttr.activeValue())));
          resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
          return new ConfigChangeResult(resultCode, adminActionRequired,
                                        messages);
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_WRITABILITY;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
      case DISABLED:
        writabilityMode = WritabilityMode.DISABLED;
        break;
      case ENABLED:
        writabilityMode = WritabilityMode.ENABLED;
        break;
      case INTERNAL_ONLY:
        writabilityMode = WritabilityMode.INTERNAL_ONLY;
        break;
    }
    // See if the entry contains an attribute that specifies the base DNs for
    // the entry.  If it does not, then skip it.
    DN[] baseDNs = null;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS;
    DNConfigAttribute baseDNStub =
         new DNConfigAttribute(ATTR_BACKEND_BASE_DN, getMessage(msgID),
                               true, true, true);
    try
    {
      DNConfigAttribute baseDNAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(baseDNStub);
      if (baseDNAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_BASE_DNS;
        String message = getMessage(msgID, String.valueOf(backendDN));
        logError(ErrorLogCategory.CONFIGURATION,
                 ErrorLogSeverity.SEVERE_WARNING, message, msgID);
        messages.add(message);
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        List<DN> dnList = baseDNAttr.pendingValues();
        baseDNs = new DN[dnList.size()];
        dnList.toArray(baseDNs);
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BASE_DNS;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    Set<DN> dnList = cfg.getBackendBaseDN();
    DN[] baseDNs = new DN[dnList.size()];
    dnList.toArray(baseDNs);
    // See if the entry contains an attribute that specifies the class name
@@ -2063,46 +1068,13 @@
    // that it's a valid backend implementation.  There is no such attribute,
    // the specified class cannot be loaded, or it does not contain a valid
    // backend implementation, then log an error and skip it.
    String className;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_CLASS;
    StringConfigAttribute classStub =
         new StringConfigAttribute(ATTR_BACKEND_CLASS, getMessage(msgID),
                                   true, false, true);
    try
    {
      StringConfigAttribute classAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(classStub);
      if (classAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_CLASS_ATTR;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        className = classAttr.activeValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_GET_CLASS;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    String className = cfg.getBackendClass();
    Class backendClass;
    Backend backend;
    try
    {
      Class backendClass = DirectoryServer.loadClass(className);
      backendClass = DirectoryServer.loadClass(className);
      backend = (Backend) backendClass.newInstance();
    }
    catch (Exception e)
@@ -2112,7 +1084,7 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
      int msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
      messages.add(getMessage(msgID, String.valueOf(className),
                              String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
@@ -2135,7 +1107,7 @@
      StringBuilder failureReason = new StringBuilder();
      if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
      {
        msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
        int msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
        String message = getMessage(msgID, backendID,
                                    String.valueOf(failureReason));
        logError(ErrorLogCategory.CONFIGURATION,
@@ -2156,7 +1128,7 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
      int msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
      String message = getMessage(msgID, backendID,
                                  stackTraceToSingleLineString(e));
      logError(ErrorLogCategory.CONFIGURATION,
@@ -2174,7 +1146,8 @@
    // Perform the necessary initialization for the backend entry.
    try
    {
      backend.initializeBackend(configEntry, baseDNs);
      backend.initializeBackend(
           DirectoryServer.getConfigEntry(cfg.dn()), baseDNs);
    }
    catch (Exception e)
    {
@@ -2183,7 +1156,7 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_INITIALIZE;
      int msgID = MSGID_CONFIG_BACKEND_CANNOT_INITIALIZE;
      messages.add(getMessage(msgID, String.valueOf(className),
                              String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
@@ -2243,7 +1216,7 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
      int msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
      String message = getMessage(msgID, backendID,
                                  stackTraceToSingleLineString(e));
@@ -2263,24 +1236,14 @@
  }
  /**
   * Indicates whether it is acceptable to remove the provided configuration
   * entry.
   *
   * @param  configEntry         The configuration entry that will be removed
   *                             from the configuration.
   * @param  unacceptableReason  A buffer to which this method can append a
   *                             human-readable message explaining why the
   *                             proposed delete is not acceptable.
   *
   * @return  <CODE>true</CODE> if the proposed entry may be removed from the
   *          configuration, or <CODE>false</CODE> if not.
   * {@inheritDoc}
   */
  public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
                                          StringBuilder unacceptableReason)
  public boolean isConfigurationDeleteAcceptable(
       BackendCfg configEntry,
       List<String> unacceptableReason)
  {
    DN backendDN = configEntry.getDN();
    DN backendDN = configEntry.dn();
    // See if this backend config manager has a backend registered with the
@@ -2304,24 +1267,18 @@
    else
    {
      int msgID = MSGID_CONFIG_BACKEND_CANNOT_REMOVE_BACKEND_WITH_SUBORDINATES;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
      unacceptableReason.add(getMessage(msgID, String.valueOf(backendDN)));
      return false;
    }
  }
  /**
   * Attempts to apply a new configuration based on the provided deleted entry.
   *
   * @param  configEntry  The new configuration entry that has been deleted.
   *
   * @return  Information about the result of processing the configuration
   *          change.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry)
  public ConfigChangeResult applyConfigurationDelete(BackendCfg configEntry)
  {
    DN                backendDN           = configEntry.getDN();
    DN                backendDN           = configEntry.dn();
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
@@ -2363,7 +1320,7 @@
      }
      DirectoryServer.deregisterBackend(backend);
      configEntry.deregisterChangeListener(this);
      configEntry.removeChangeListener(this);
      // Remove the shared lock for this backend.
      try
@@ -2406,5 +1363,54 @@
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
  }
  /**
   * Gets the configuration corresponding to a config entry.
   *
   * @param <S>
   *          The type of server configuration.
   * @param definition
   *          The required definition of the required managed object.
   * @param configEntry
   *          A configuration entry.
   * @return Returns the server-side configuration.
   * @throws ConfigException
   *           If the entry could not be decoded.
   */
  public static <S extends Configuration> S getConfiguration(
      AbstractManagedObjectDefinition<?, S> definition, ConfigEntry configEntry)
      throws ConfigException {
    try {
      ServerManagedObject<? extends S> mo = ServerManagedObject
          .decode(ManagedObjectPath.emptyPath(), definition,
              configEntry);
      return mo.getConfiguration();
    } catch (DefinitionDecodingException e) {
      throw ConfigExceptionFactory.getInstance()
          .createDecodingExceptionAdaptor(configEntry.getDN(), e);
    } catch (ServerManagedObjectDecodingException e) {
      throw ConfigExceptionFactory.getInstance()
          .createDecodingExceptionAdaptor(e);
    }
  }
  /**
   * Gets the backend configuration corresponding to a backend config entry.
   *
   * @param configEntry A backend config entry.
   * @return Returns the backend configuration.
   * @throws ConfigException If the config entry could not be decoded.
   */
  public static BackendCfg getBackendCfg(ConfigEntry configEntry)
      throws ConfigException
  {
    return getConfiguration(BackendCfgDefn.getInstance(), configEntry);
  }
}