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

neil_a_wilson
11.47.2006 c401eb8dc952c53586b0f58e1241c98eb705fde6
Add an account status notification handler config manager, which is responsible
for loading the account status notification handlers on startup, as well as
registering and deregistering them accordingly as changes are made to the
configuration while the server is running.
1 files added
4 files modified
1277 ■■■■■ changed files
opends/resource/schema/02-config.ldif 13 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/config/ConfigConstants.java 36 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/AccountStatusNotificationHandlerConfigManager.java 958 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DirectoryServer.java 11 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/ConfigMessages.java 259 ●●●●● patch | view | raw | blame | history
opends/resource/schema/02-config.ldif
@@ -959,6 +959,14 @@
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.280
  NAME 'ds-pwp-account-expiration-time' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
  SINGLE-VALUE USAGE directoryOperation X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.281
  NAME 'ds-cfg-account-status-notification-handler-class'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.282
  NAME 'ds-cfg-account-status-notification-handler-enabled'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
  MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -1312,4 +1320,9 @@
  NAME 'ds-cfg-password-modify-extended-operation-handler'
  SUP ds-cfg-extended-operation-handler MUST ds-cfg-identity-mapper-dn
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.75
  NAME 'ds-cfg-account-status-notification-handler' SUP top STRUCTURAL
  MUST ( ds-cfg-account-status-notification-handler-class $
  ds-cfg-account-status-notification-handler-enabled )
  X-ORIGIN 'OpenDS Directory Server' )
opends/src/server/org/opends/server/config/ConfigConstants.java
@@ -106,6 +106,24 @@
  /**
   * The name of the configuration attribute that holds the fully-qualified name
   * for the account status notification handler class.
   */
  public static final String ATTR_ACCT_NOTIFICATION_HANDLER_CLASS =
       NAME_PREFIX_CFG + "account-status-notification-handler-class";
  /**
   * The name of the configuration attribute that indicates whether an account
   * status notification handler is enabled.
   */
  public static final String ATTR_ACCT_NOTIFICATION_HANDLER_ENABLED =
       NAME_PREFIX_CFG + "account-status-notification-handler-enabled";
  /**
   * The name of the configuration attribute that indicates whether to
   * automatically add missing RDN attributes or to return an error response to
   * the client.
@@ -2392,6 +2410,15 @@
  /**
   * The DN of the entry that will serve as the base for all Directory Server
   * account status notification handlers.
   */
  public static final String DN_ACCT_NOTIFICATION_HANDLER_CONFIG_BASE =
       "cn=Account Status Notification Handlers," + DN_CONFIG_ROOT;
  /**
   * The DN of the entry that will serve as the base for all Directory Server
   * backends.
   */
  public static final String DN_BACKEND_BASE = "cn=Backends," + DN_CONFIG_ROOT;
@@ -2724,6 +2751,15 @@
  /**
   * The name of the objectclass that will be used for a Directory Server
   * account status notification handler.
   */
  public static final String OC_ACCT_NOTIFICATION_HANDLER =
       NAME_PREFIX_CFG + "account-status-notification-handler";
  /**
   * The name of the objectclass that will be used for a Directory Server alert
   * handler.
   */
opends/src/server/org/opends/server/core/AccountStatusNotificationHandlerConfigManager.java
New file
@@ -0,0 +1,958 @@
/*
 * 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 2006 Sun Microsystems, Inc.
 */
package org.opends.server.core;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.opends.server.api.AccountStatusNotificationHandler;
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.StringConfigAttribute;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.ResultCode;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.Debug.*;
import static org.opends.server.loggers.Error.*;
import static org.opends.server.messages.ConfigMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
/**
 * This class defines a utility that will be used to manage the set of account
 * status notification handlers defined in the Directory Server.  It will
 * initialize the handlers when the server starts, and then will manage any
 * additions, removals, or modifications to any notification handlers while the
 * server is running.
 */
public class AccountStatusNotificationHandlerConfigManager
       implements ConfigChangeListener, ConfigAddListener, ConfigDeleteListener
{
  /**
   * The fully-qualified name of this class for debugging purposes.
   */
  private static final String CLASS_NAME =
       "org.opends.server.core.AccountStatusNotificationHandlerConfigManager";
  // A mapping between the DNs of the config entries and the associated
  // notification handlers.
  private ConcurrentHashMap<DN,AccountStatusNotificationHandler>
               notificationHandlers;
  // The configuration handler for the Directory Server.
  private ConfigHandler configHandler;
  /**
   * Creates a new instance of this account status notification handler config
   * manager.
   */
  public AccountStatusNotificationHandlerConfigManager()
  {
    assert debugConstructor(CLASS_NAME);
    configHandler = DirectoryServer.getConfigHandler();
    notificationHandlers =
         new ConcurrentHashMap<DN,AccountStatusNotificationHandler>();
  }
  /**
   * Initializes all account status notification handlers currently defined in
   * the Directory Server configuration.  This should only be called at
   * Directory Server startup.
   *
   * @throws  ConfigException  If a configuration problem causes the
   *                           notification handler initialization process to
   *                           fail.
   *
   * @throws  InitializationException  If a problem occurs while initializing
   *                                   the account status notification handlers
   *                                   that is not related to the server
   *                                   configuration.
   */
  public void initializeNotificationHandlers()
         throws ConfigException, InitializationException
  {
    assert debugEnter(CLASS_NAME, "initializeNotificationHandlers");
    // First, get the configuration base entry.
    ConfigEntry baseEntry;
    try
    {
      DN handlerBase = DN.decode(DN_ACCT_NOTIFICATION_HANDLER_CONFIG_BASE);
      baseEntry = configHandler.getConfigEntry(handlerBase);
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "initializeNotificationHandlers",
                            e);
      int    msgID   = MSGID_CONFIG_ACCTNOTHANDLER_CANNOT_GET_BASE;
      String message = getMessage(msgID, String.valueOf(e));
      throw new ConfigException(msgID, message, e);
    }
    if (baseEntry == null)
    {
      // The notification handler base entry does not exist.  This is not
      // acceptable, so throw an exception.
      int    msgID   = MSGID_CONFIG_ACCTNOTHANDLER_BASE_DOES_NOT_EXIST;
      String message = getMessage(msgID);
      throw new ConfigException(msgID, message);
    }
    // Register add and delete listeners with the notification handler base
    // entry.  We don't care about modifications to it.
    baseEntry.registerAddListener(this);
    baseEntry.registerDeleteListener(this);
    // See if the base entry has any children.  If not, then we don't need to do
    // anything else.
    if (! baseEntry.hasChildren())
    {
      return;
    }
    // Iterate through the child entries and process them as account status
    // notification handler configuration entries.
    for (ConfigEntry childEntry : baseEntry.getChildren().values())
    {
      childEntry.registerChangeListener(this);
      StringBuilder unacceptableReason = new StringBuilder();
      if (! configAddIsAcceptable(childEntry, unacceptableReason))
      {
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 MSGID_CONFIG_ACCTNOTHANDLER_ENTRY_UNACCEPTABLE,
                 childEntry.getDN().toString(), unacceptableReason.toString());
        continue;
      }
      try
      {
        ConfigChangeResult result = applyConfigurationAdd(childEntry);
        if (result.getResultCode() != ResultCode.SUCCESS)
        {
          StringBuilder buffer = new StringBuilder();
          List<String> resultMessages = result.getMessages();
          if ((resultMessages == null) || (resultMessages.isEmpty()))
          {
            buffer.append(getMessage(MSGID_CONFIG_UNKNOWN_UNACCEPTABLE_REASON));
          }
          else
          {
            Iterator<String> iterator = resultMessages.iterator();
            buffer.append(iterator.next());
            while (iterator.hasNext())
            {
              buffer.append(EOL);
              buffer.append(iterator.next());
            }
          }
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_ERROR,
                   MSGID_CONFIG_ACCTNOTHANDLER_CANNOT_CREATE_HANDLER,
                   childEntry.getDN().toString(), buffer.toString());
        }
      }
      catch (Exception e)
      {
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 MSGID_CONFIG_ACCTNOTHANDLER_CANNOT_CREATE_HANDLER,
                 childEntry.getDN().toString(), String.valueOf(e));
      }
    }
  }
  /**
   * 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.
   */
  public boolean configChangeIsAcceptable(ConfigEntry configEntry,
                                          StringBuilder unacceptableReason)
  {
    assert debugEnter(CLASS_NAME, "configChangeIsAcceptable",
                      String.valueOf(configEntry), "java.lang.StringBuilder");
    // Make sure that the entry has an appropriate objectclass for an account
    // status notification handler.
    if (! configEntry.hasObjectClass(OC_ACCT_NOTIFICATION_HANDLER))
    {
      int    msgID   = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_OBJECTCLASS;
      String message = getMessage(msgID, configEntry.getDN().toString());
      unacceptableReason.append(message);
      return false;
    }
    // Make sure that the entry specifies the notification handler class name.
    StringConfigAttribute classNameAttr;
    try
    {
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_DESCRIPTION_CLASS_NAME;
      StringConfigAttribute classStub =
           new StringConfigAttribute(ATTR_ACCT_NOTIFICATION_HANDLER_CLASS,
                                     getMessage(msgID), true, false, true);
      classNameAttr = (StringConfigAttribute)
                      configEntry.getConfigAttribute(classStub);
      if (classNameAttr == null)
      {
        msgID = MSGID_CONFIG_ACCTNOTHANDLER_NO_CLASS_NAME;
        String message = getMessage(msgID, configEntry.getDN().toString());
        unacceptableReason.append(message);
        return false;
      }
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "configChangeIsAcceptable", e);
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_CLASS_NAME;
      String message = getMessage(msgID, configEntry.getDN().toString(),
                                  String.valueOf(e));
      unacceptableReason.append(message);
      return false;
    }
    Class handlerClass;
    try
    {
      // FIXME -- Should this be done with a custom class loader?
      handlerClass = Class.forName(classNameAttr.pendingValue());
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "configChangeIsAcceptable", e);
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_CLASS_NAME;
      String message = getMessage(msgID, configEntry.getDN().toString(),
                                  String.valueOf(e));
      unacceptableReason.append(message);
      return false;
    }
    try
    {
      AccountStatusNotificationHandler handler =
           (AccountStatusNotificationHandler) handlerClass.newInstance();
    }
    catch(Exception e)
    {
      assert debugException(CLASS_NAME, "configChangeIsAcceptable", e);
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_CLASS;
      String message = getMessage(msgID, handlerClass.getName(),
                                  String.valueOf(configEntry.getDN()),
                                  String.valueOf(e));
      unacceptableReason.append(message);
      return false;
    }
    // See if this account status notification handler should be enabled.
    BooleanConfigAttribute enabledAttr;
    try
    {
      BooleanConfigAttribute enabledStub =
           new BooleanConfigAttribute(ATTR_ACCT_NOTIFICATION_HANDLER_ENABLED,
                    getMessage(MSGID_CONFIG_ACCTNOTHANDLER_DESCRIPTION_ENABLED),
                               false);
      enabledAttr = (BooleanConfigAttribute)
                    configEntry.getConfigAttribute(enabledStub);
      if (enabledAttr == null)
      {
        int msgID = MSGID_CONFIG_ACCTNOTHANDLER_NO_ENABLED_ATTR;
        String message = getMessage(msgID, configEntry.getDN().toString());
        unacceptableReason.append(message);
        return false;
      }
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "configChangeIsAcceptable", e);
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_ENABLED_VALUE;
      String message = getMessage(msgID, configEntry.getDN().toString(),
                                  String.valueOf(e));
      unacceptableReason.append(message);
      return false;
    }
    // If we've gotten here then the notification handler entry appears to be
    // acceptable.
    return true;
  }
  /**
   * 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.
   */
  public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry)
  {
    assert debugEnter(CLASS_NAME, "applyConfigurationChange",
                      String.valueOf(configEntry));
    DN                configEntryDN       = configEntry.getDN();
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
    // Make sure that the entry has an appropriate objectclass for an account
    // status notification handler.
    if (! configEntry.hasObjectClass(OC_ACCT_NOTIFICATION_HANDLER))
    {
      int    msgID   = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_OBJECTCLASS;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN)));
      resultCode = ResultCode.UNWILLING_TO_PERFORM;
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    // Get the corresponding notification handler if it is active.
    AccountStatusNotificationHandler handler =
         notificationHandlers.get(configEntryDN);
    // See if this handler should be enabled or disabled.
    boolean needsEnabled = false;
    BooleanConfigAttribute enabledAttr;
    try
    {
      BooleanConfigAttribute enabledStub =
           new BooleanConfigAttribute(ATTR_ACCT_NOTIFICATION_HANDLER_ENABLED,
                    getMessage(MSGID_CONFIG_ACCTNOTHANDLER_DESCRIPTION_ENABLED),
                               false);
      enabledAttr = (BooleanConfigAttribute)
                    configEntry.getConfigAttribute(enabledStub);
      if (enabledAttr == null)
      {
        int msgID = MSGID_CONFIG_ACCTNOTHANDLER_NO_ENABLED_ATTR;
        messages.add(getMessage(msgID, String.valueOf(configEntryDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      if (enabledAttr.activeValue())
      {
        if (handler == null)
        {
          needsEnabled = true;
        }
        else
        {
          // The handler is already active, so no action is required.
        }
      }
      else
      {
        if (handler == null)
        {
          // The handler is already disabled, so no action is required and we
          // can short-circuit out of this processing.
          return new ConfigChangeResult(resultCode, adminActionRequired,
                                        messages);
        }
        else
        {
          // The handler is active, so it needs to be disabled.  Do this and
          // return that we were successful.
          notificationHandlers.remove(configEntryDN);
          handler.finalizeStatusNotificationHandler();
          DirectoryServer.deregisterAccountStatusNotificationHandler(
               configEntryDN);
          return new ConfigChangeResult(resultCode, adminActionRequired,
                                        messages);
        }
      }
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "configChangeIsAcceptable", e);
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_ENABLED_VALUE;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              String.valueOf(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    // Make sure that the entry specifies the notification handler class name.
    // If it has changed, then we will not try to dynamically apply it.
    String className;
    try
    {
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_DESCRIPTION_CLASS_NAME;
      StringConfigAttribute classStub =
           new StringConfigAttribute(ATTR_ACCT_NOTIFICATION_HANDLER_CLASS,
                                     getMessage(msgID), true, false, true);
      StringConfigAttribute classNameAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(classStub);
      if (classNameAttr == null)
      {
        msgID = MSGID_CONFIG_ACCTNOTHANDLER_NO_CLASS_NAME;
        messages.add(getMessage(msgID, String.valueOf(configEntryDN)));
        resultCode = ResultCode.OBJECTCLASS_VIOLATION;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      className = classNameAttr.pendingValue();
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "configChangeIsAcceptable", e);
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_CLASS_NAME;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              String.valueOf(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    boolean classChanged = false;
    String  oldClassName = null;
    if (handler != null)
    {
      oldClassName = handler.getClass().getName();
      classChanged = (! className.equals(oldClassName));
    }
    if (classChanged)
    {
      // This will not be applied dynamically.  Add a message to the response
      // and indicate that admin action is required.
      adminActionRequired = true;
      messages.add(getMessage(MSGID_CONFIG_ACCTNOTHANDLER_CLASS_ACTION_REQUIRED,
                              String.valueOf(oldClassName),
                              String.valueOf(className),
                              String.valueOf(configEntryDN)));
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    if (needsEnabled)
    {
      try
      {
        // FIXME -- Should this be done with a dynamic class loader?
        Class handlerClass = Class.forName(className);
        handler = (AccountStatusNotificationHandler) handlerClass.newInstance();
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "applyConfigurationChange", e);
        int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_CLASS;
        messages.add(getMessage(msgID, className,
                                String.valueOf(configEntryDN),
                                String.valueOf(e)));
        resultCode = DirectoryServer.getServerErrorResultCode();
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      try
      {
        handler.initializeStatusNotificationHandler(configEntry);
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "applyConfigurationChange", e);
        int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INITIALIZATION_FAILED;
        messages.add(getMessage(msgID, className,
                                String.valueOf(configEntryDN),
                                String.valueOf(e)));
        resultCode = DirectoryServer.getServerErrorResultCode();
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      notificationHandlers.put(configEntryDN, handler);
      DirectoryServer.registerAccountStatusNotificationHandler(configEntryDN,
                                                               handler);
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    // If we've gotten here, then there haven't been any changes to anything
    // that we care about.
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * 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.
   */
  public boolean configAddIsAcceptable(ConfigEntry configEntry,
                                       StringBuilder unacceptableReason)
  {
    assert debugEnter(CLASS_NAME, "configAddIsAcceptable",
                      String.valueOf(configEntry), "java.lang.StringBuilder");
    // Make sure that no entry already exists with the specified DN.
    DN configEntryDN = configEntry.getDN();
    if (notificationHandlers.containsKey(configEntryDN))
    {
      int    msgID   = MSGID_CONFIG_ACCTNOTHANDLER_EXISTS;
      String message = getMessage(msgID, String.valueOf(configEntryDN));
      unacceptableReason.append(message);
      return false;
    }
    // Make sure that the entry has an appropriate objectclass for an account
    // status notification handler.
    if (! configEntry.hasObjectClass(OC_ACCT_NOTIFICATION_HANDLER))
    {
      int    msgID   = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_OBJECTCLASS;
      String message = getMessage(msgID, configEntry.getDN().toString());
      unacceptableReason.append(message);
      return false;
    }
    // Make sure that the entry specifies the handler class.
    StringConfigAttribute classNameAttr;
    try
    {
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_DESCRIPTION_CLASS_NAME;
      StringConfigAttribute classStub =
           new StringConfigAttribute(ATTR_ACCT_NOTIFICATION_HANDLER_CLASS,
                                     getMessage(msgID), true, false, true);
      classNameAttr = (StringConfigAttribute)
                      configEntry.getConfigAttribute(classStub);
      if (classNameAttr == null)
      {
        msgID = MSGID_CONFIG_ACCTNOTHANDLER_NO_CLASS_NAME;
        String message = getMessage(msgID, configEntry.getDN().toString());
        unacceptableReason.append(message);
        return false;
      }
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "configAddIsAcceptable", e);
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_CLASS_NAME;
      String message = getMessage(msgID, configEntry.getDN().toString(),
                                  String.valueOf(e));
      unacceptableReason.append(message);
      return false;
    }
    Class handlerClass;
    try
    {
      // FIXME -- Should this be done with a custom class loader?
      handlerClass = Class.forName(classNameAttr.pendingValue());
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "configAddIsAcceptable", e);
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_CLASS_NAME;
      String message = getMessage(msgID, configEntry.getDN().toString(),
                                  String.valueOf(e));
      unacceptableReason.append(message);
      return false;
    }
    AccountStatusNotificationHandler handler;
    try
    {
      handler = (AccountStatusNotificationHandler) handlerClass.newInstance();
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "configAddIsAcceptable", e);
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_CLASS;
      String message = getMessage(msgID, handlerClass.getName(),
                                  String.valueOf(configEntryDN),
                                  String.valueOf(e));
      unacceptableReason.append(message);
      return false;
    }
    // If the notofication handler is a configurable component, then make sure
    // that its configuration is valid.
    if (handler instanceof ConfigurableComponent)
    {
      ConfigurableComponent cc = (ConfigurableComponent) handler;
      LinkedList<String> errorMessages = new LinkedList<String>();
      if (! cc.hasAcceptableConfiguration(configEntry, errorMessages))
      {
        if (errorMessages.isEmpty())
        {
          int msgID = MSGID_CONFIG_ACCTNOTHANDLER_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;
      }
    }
    // See if this notification handler should be enabled.
    BooleanConfigAttribute enabledAttr;
    try
    {
      BooleanConfigAttribute enabledStub =
           new BooleanConfigAttribute(ATTR_ACCT_NOTIFICATION_HANDLER_ENABLED,
                    getMessage(MSGID_CONFIG_ACCTNOTHANDLER_DESCRIPTION_ENABLED),
                               false);
      enabledAttr = (BooleanConfigAttribute)
                    configEntry.getConfigAttribute(enabledStub);
      if (enabledAttr == null)
      {
        int msgID = MSGID_CONFIG_ACCTNOTHANDLER_NO_ENABLED_ATTR;
        String message = getMessage(msgID, configEntry.getDN().toString());
        unacceptableReason.append(message);
        return false;
      }
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "configAddIsAcceptable", e);
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_ENABLED_VALUE;
      String message = getMessage(msgID, configEntry.getDN().toString(),
                                  String.valueOf(e));
      unacceptableReason.append(message);
      return false;
    }
    // If we've gotten here then the notification handler entry appears to be
    // acceptable.
    return true;
  }
  /**
   * 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.
   */
  public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry)
  {
    assert debugEnter(CLASS_NAME, "applyConfigurationAdd",
                      String.valueOf(configEntry));
    DN                configEntryDN       = configEntry.getDN();
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
    // Make sure that the entry has an appropriate objectclass for an account
    // status notification handler.
    if (! configEntry.hasObjectClass(OC_ACCT_NOTIFICATION_HANDLER))
    {
      int    msgID   = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_OBJECTCLASS;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN)));
      resultCode = ResultCode.UNWILLING_TO_PERFORM;
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    // See if this notification handler should be enabled or disabled.
    BooleanConfigAttribute enabledAttr;
    try
    {
      BooleanConfigAttribute enabledStub =
           new BooleanConfigAttribute(ATTR_ACCT_NOTIFICATION_HANDLER_ENABLED,
                    getMessage(MSGID_CONFIG_ACCTNOTHANDLER_DESCRIPTION_ENABLED),
                               false);
      enabledAttr = (BooleanConfigAttribute)
                    configEntry.getConfigAttribute(enabledStub);
      if (enabledAttr == null)
      {
        // The attribute doesn't exist, so it will be disabled by default.
        int msgID = MSGID_CONFIG_ACCTNOTHANDLER_NO_ENABLED_ATTR;
        messages.add(getMessage(msgID, String.valueOf(configEntryDN)));
        resultCode = ResultCode.SUCCESS;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else if (! enabledAttr.activeValue())
      {
        // It is explicitly configured as disabled, so we don't need to do
        // anything.
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "applyConfigurationAdd", e);
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_ENABLED_VALUE;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              String.valueOf(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    // Make sure that the entry specifies the handler class name.
    String className;
    try
    {
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_DESCRIPTION_CLASS_NAME;
      StringConfigAttribute classStub =
           new StringConfigAttribute(ATTR_ACCT_NOTIFICATION_HANDLER_CLASS,
                                     getMessage(msgID), true, false, true);
      StringConfigAttribute classNameAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(classStub);
      if (classNameAttr == null)
      {
        msgID = MSGID_CONFIG_ACCTNOTHANDLER_NO_CLASS_NAME;
        messages.add(getMessage(msgID, String.valueOf(configEntryDN)));
        resultCode = ResultCode.OBJECTCLASS_VIOLATION;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      className = classNameAttr.pendingValue();
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "applyConfigurationAdd", e);
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_CLASS_NAME;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              String.valueOf(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    // Load and initialize the notificationhandler class, and register it with
    // the Directory Server.
    AccountStatusNotificationHandler handler;
    try
    {
      // FIXME -- Should this be done with a dynamic class loader?
      Class handlerClass = Class.forName(className);
      handler = (AccountStatusNotificationHandler) handlerClass.newInstance();
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "applyConfigurationAdd", e);
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INVALID_CLASS;
      messages.add(getMessage(msgID, className, String.valueOf(configEntryDN),
                              String.valueOf(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    try
    {
      handler.initializeStatusNotificationHandler(configEntry);
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "applyConfigurationAdd", e);
      int msgID = MSGID_CONFIG_ACCTNOTHANDLER_INITIALIZATION_FAILED;
      messages.add(getMessage(msgID, className, String.valueOf(configEntryDN),
                              String.valueOf(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    notificationHandlers.put(configEntryDN, handler);
    DirectoryServer.registerAccountStatusNotificationHandler(configEntryDN,
                                                             handler);
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * 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.
   */
  public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
                                          StringBuilder unacceptableReason)
  {
    assert debugEnter(CLASS_NAME, "configDeleteIsAcceptable",
                      String.valueOf(configEntry), "java.lang.StringBuilder");
    // A delete should always be acceptable, so just return true.
    return true;
  }
  /**
   * 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.
   */
  public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry)
  {
    assert debugEnter(CLASS_NAME, "applyConfigurationDelete",
                      String.valueOf(configEntry));
    DN         configEntryDN       = configEntry.getDN();
    ResultCode resultCode          = ResultCode.SUCCESS;
    boolean    adminActionRequired = false;
    // See if the entry is registered as an account status notification handler.
    // If so, deregister it and stop the handler.
    AccountStatusNotificationHandler handler =
         notificationHandlers.remove(configEntryDN);
    if (handler != null)
    {
      DirectoryServer.deregisterAccountStatusNotificationHandler(configEntryDN);
      handler.finalizeStatusNotificationHandler();
    }
    return new ConfigChangeResult(resultCode, adminActionRequired);
  }
}
opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -182,6 +182,10 @@
  // The policy to use regarding syntax enforcement.
  private AcceptRejectWarn syntaxEnforcementPolicy;
  // The account status notification handler config manager for the server.
  private AccountStatusNotificationHandlerConfigManager
       accountStatusNotificationHandlerConfigManager;
  // The default syntax to use for binary attributes.
  private AttributeSyntax defaultBinarySyntax;
@@ -2084,6 +2088,13 @@
    passwordGeneratorConfigManager.initializePasswordGenerators();
    // Initialize the account status notification handlers.
    accountStatusNotificationHandlerConfigManager =
         new AccountStatusNotificationHandlerConfigManager();
    accountStatusNotificationHandlerConfigManager.
         initializeNotificationHandlers();
    // Initialize all the password policies.
    passwordPolicyConfigManager = new PasswordPolicyConfigManager();
    passwordPolicyConfigManager.initializePasswordPolicies();
opends/src/server/org/opends/server/messages/ConfigMessages.java
@@ -5898,6 +5898,183 @@
  /**
   * The message ID for the message that will be used if an error occurs while
   * attempting to retrieve the account status notification handler base entry
   * from the configuration.  This takes a single argument, which is a string
   * representation of the exception that was caught.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_CANNOT_GET_BASE =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_FATAL_ERROR | 545;
  /**
   * The message ID for the message that will be used if the account status
   * notification handler base entry does not exist in the Directory Server
   * configuration.  This does not take any arguments.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_BASE_DOES_NOT_EXIST =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_WARNING | 546;
  /**
   * The message ID for the message that will be used if an account status
   * notification handler configuration entry does not contain an acceptable
   * configuration.  This takes two arguments, which are the DN of the
   * configuration entry and the reason that it is not acceptable.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_ENTRY_UNACCEPTABLE =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 547;
  /**
   * The message ID for the message that will be used if an error occurs while
   * trying to create an account status notification handler from a
   * configuration entry.  This takes two arguments, which are the DN of the
   * configuration entry and a message that explains the problem that occurred.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_CANNOT_CREATE_HANDLER =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 548;
  /**
   * The message ID for the message that will be used if an entry below the
   * account status notification handler base does not contain a valid handler
   * objectclass.  This takes a single argument, which is the DN of the
   * configuration entry.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_INVALID_OBJECTCLASS =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 549;
  /**
   * The message ID for the description of the account status notification
   * handler class name configuration attribute.  This does not take any
   * arguments.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_DESCRIPTION_CLASS_NAME =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_INFORMATIONAL | 550;
  /**
   * The message ID for the message that will be used if an entry below the
   * account status notification handler base does not contain a value for the
   * class name.  This takes a single argument, which is the DN of the
   * configuration entry.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_NO_CLASS_NAME =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 551;
  /**
   * The message ID for the message that will be used if an entry below the
   * account status notification handler base contains an invalid value for the
   * class name.  This takes two arguments, which are the DN of the
   * configuration entry and a string representation of the exception that was
   * caught.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_INVALID_CLASS_NAME =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 552;
  /**
   * The message ID for the message that will be used if a configuration entry
   * defines a Directory Server account status notification handler but the
   * associated class cannot be instantiated as a notification handler.  This
   * takes three arguments, which are the handler class name, the DN of the
   * configuration entry, and a string representation of the exception that was
   * caught.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_INVALID_CLASS =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 553;
  /**
   * The message ID for the description of the account status notification
   * handler enabled configuration attribute.  This does not take any arguments.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_DESCRIPTION_ENABLED =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_INFORMATIONAL | 554;
  /**
   * The message ID for the message that will be used if an entry below the
   * account status notification handler base does not contain a value for the
   * enabled attribute.  This takes a single argument, which is the DN of the
   * configuration entry.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_NO_ENABLED_ATTR =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 555;
  /**
   * The message ID for the message that will be used if an entry below the
   * account status notification handler base has an invalid value for the
   * enabled attribute.  This takes two arguments, which are the DN of the
   * configuration entry and a string representation of the exception that was
   * caught.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_INVALID_ENABLED_VALUE =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 556;
  /**
   * The message ID for the message that will be used if the account status
   * notification handler class has changed and will require administrative
   * action to take effect.  This takes three arguments, which are the old class
   * name, the new class name, and the DN of the associated configuration entry.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_CLASS_ACTION_REQUIRED =
            CATEGORY_MASK_CONFIG | SEVERITY_MASK_INFORMATIONAL | 557;
  /**
   * The message ID for the message that will be used if an error occurs while
   * initializing a Directory Server account status notification handler.  This
   * takes three arguments, which are the class name for the handler class, the
   * DN of the configuration entry, and a string representation of the exception
   * that was caught.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_INITIALIZATION_FAILED =
            CATEGORY_MASK_CONFIG | SEVERITY_MASK_MILD_ERROR | 558;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new account status notification handler with a DN that matches the DN
   * of a handler that already exists.  This takes a single argument, which is
   * the DN of the notification handler configuration entry.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_EXISTS =
            CATEGORY_MASK_CONFIG | SEVERITY_MASK_MILD_ERROR | 559;
  /**
   * The message ID for the message that will be used if an account status
   * notification handler entry contains an unacceptable configuration but does
   * not provide any specific details about the nature of the problem.  This
   * takes a single argument, which is the DN of the configuration entry.
   */
  public static final int MSGID_CONFIG_ACCTNOTHANDLER_UNACCEPTABLE_CONFIG =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 560;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -8549,6 +8726,88 @@
    registerMessage(MSGID_CONFIG_ROOTDN_EXISTS,
                    "Unable to add root DN entry %s because another root " +
                    "user is already registered with that DN.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_CANNOT_GET_BASE,
                    "An error occurred while attempting to retrieve the " +
                    "account status notification handler base entry " +
                    DN_ACCT_NOTIFICATION_HANDLER_CONFIG_BASE +
                    " from the Directory Server configuration:  %s.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_BASE_DOES_NOT_EXIST,
                    "The account status notification handler configuration " +
                    "base " + DN_ACCT_NOTIFICATION_HANDLER_CONFIG_BASE +
                    " does not exist in the Directory Server configuration.  " +
                    "This entry must be present for the server to function " +
                    "properly.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_ENTRY_UNACCEPTABLE,
                    "Configuration entry %s does not contain a valid " +
                    "account status notification handler configuration:  " +
                    "%s.  It will be ignored.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_CANNOT_CREATE_HANDLER,
                    "An error occurred while attempting to create a " +
                    "Directory Server account status notification handler " +
                    "from the information in configuration entry %s:  %s.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_INVALID_OBJECTCLASS,
                    "Configuration entry %s does not contain the " +
                    OC_ACCT_NOTIFICATION_HANDLER + " objectclass, which is " +
                    "required for account status notification handler " +
                    "definitions.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_DESCRIPTION_CLASS_NAME,
                    "The fully-qualified name of the Java class that defines " +
                    "the Directory Server account status notification " +
                    "handler.  If this is altered while the associated " +
                    "notification handler is enabled, then that handler must " +
                    "be disabled and re-enabled for the change to take " +
                    "effect.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_NO_CLASS_NAME,
                    "Configuration entry %s does not contain a valid value " +
                    "for configuration attribute " +
                    ATTR_ACCT_NOTIFICATION_HANDLER_CLASS +
                    " which specifies the fully-qualified class name for " +
                    "the associated account status notification handler.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_INVALID_CLASS_NAME,
                    "Configuration entry %s has an invalid value for " +
                    "attribute " + ATTR_ACCT_NOTIFICATION_HANDLER_CLASS +
                    ":  %s.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_INVALID_CLASS,
                    "Class %s specified in configuration entry %s does not " +
                    "contain a valid account status notification handler " +
                    "implementation:  %s.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_DESCRIPTION_ENABLED,
                    "Indicates whether this Directory Server account status " +
                    "notification handler should be enabled.  Changes to " +
                    "this attribute will take effect immediately.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_NO_ENABLED_ATTR,
                    "Configuration entry %s does not contain a valid value " +
                    "for configuration attribute " +
                    ATTR_ACCT_NOTIFICATION_HANDLER_ENABLED +
                    " which indicates whether the account status " +
                    "notification handler should be enabled for use in the " +
                    "Directory Server.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_INVALID_ENABLED_VALUE,
                    "Configuration entry %s has an invalid value for " +
                    "attribute " + ATTR_ACCT_NOTIFICATION_HANDLER_ENABLED +
                    ":  %s.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_CLASS_ACTION_REQUIRED,
                    "The requested change in the account status notification " +
                    "handler class name from %s to %s in configuration entry " +
                    "%s cannot be dynamically applied.  This change will not " +
                    "take effect until the notification handler is disabled " +
                    "and re-enabled or the Directory Server is restarted.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_INITIALIZATION_FAILED,
                    "An error occurred while trying to initialize an " +
                    "instance of class %s as an account status notification " +
                    "handler as defined in configuration entry %s:  %s.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_EXISTS,
                    "Unable to add a new account status notification handler " +
                    "entry with DN %s because there is already a " +
                    "notification handler registered with that DN.");
    registerMessage(MSGID_CONFIG_ACCTNOTHANDLER_UNACCEPTABLE_CONFIG,
                    "The configuration for the account status notification " +
                    "handler defined in configuration entry %s was not " +
                    "acceptable according to its internal validation.  " +
                    "However, no specific information is available regarding " +
                    "the problem(s) with the entry.");
  }
}