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

matthew_swift
23.26.2007 2861a1aad527dae9aea800386d58fc7416233ae5
opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
@@ -28,48 +28,49 @@
import static org.opends.server.loggers.Access.logConnect;
import static org.opends.server.loggers.Error.logError;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.messages.MessageHandler.getMessage;
import static org.opends.server.messages.ProtocolMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.LDAPConnectionHandlerCfg;
import org.opends.server.api.AlertGenerator;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.api.ConnectionHandler;
import org.opends.server.api.ConnectionSecurityProvider;
import org.opends.server.api.KeyManagerProvider;
import org.opends.server.api.ServerShutdownListener;
import org.opends.server.api.TrustManagerProvider;
import org.opends.server.api.plugin.PostConnectPluginResult;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.PluginConfigManager;
import org.opends.server.config.BooleanConfigAttribute;
import org.opends.server.config.ConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.DNConfigAttribute;
import org.opends.server.config.IntegerConfigAttribute;
import org.opends.server.config.IntegerWithUnitConfigAttribute;
import org.opends.server.config.MultiChoiceConfigAttribute;
import org.opends.server.config.StringConfigAttribute;
import org.opends.server.extensions.NullConnectionSecurityProvider;
import org.opends.server.extensions.TLSConnectionSecurityProvider;
import org.opends.server.types.AddressMask;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.HostPort;
@@ -77,176 +78,71 @@
import org.opends.server.types.ResultCode;
import org.opends.server.types.SSLClientAuthPolicy;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.Access.*;
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 static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.ProtocolMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
/**
 * This class defines a connection handler that will be used for communicating
 * with clients over LDAP.  It is actually implemented in two parts:  as a
 * connection handler and one or more request handlers.  The connection handler
 * is responsible for accepting new connections and registering each of them
 * with a request handler.  The request handlers then are responsible for
 * reading requests from the clients and parsing them as operations.  A single
 * request handler may be used, but having multiple handlers might provide
 * This class defines a connection handler that will be used for
 * communicating with clients over LDAP. It is actually implemented in
 * two parts: as a connection handler and one or more request
 * handlers. The connection handler is responsible for accepting new
 * connections and registering each of them with a request handler.
 * The request handlers then are responsible for reading requests from
 * the clients and parsing them as operations. A single request
 * handler may be used, but having multiple handlers might provide
 * better performance in a multi-CPU system.
 */
public class LDAPConnectionHandler
       extends ConnectionHandler
       implements ConfigurableComponent, AlertGenerator
{
public final class LDAPConnectionHandler extends
    ConnectionHandler<LDAPConnectionHandlerCfg> implements
    ConfigurationChangeListener<LDAPConnectionHandlerCfg>,
    ServerShutdownListener, AlertGenerator {
  /**
   * The fully-qualified name of this class.
   */
  private static final String CLASS_NAME =
       "org.opends.server.protocols.ldap.LDAPConnectionHandler";
    "org.opends.server.protocols.ldap.LDAPConnectionHandler";
  // The current configuration state.
  private LDAPConnectionHandlerCfg currentConfig;
  /**
   * The hash map that holds the units that may be provided in conjunction with
   * the maximum request size.
   */
  private static final HashMap<String,Double> SIZE_UNITS =
       new HashMap<String,Double>();
  static
  {
    SIZE_UNITS.put(SIZE_UNIT_BYTES_ABBR, 1D);
    SIZE_UNITS.put(SIZE_UNIT_BYTES_FULL, 1D);
    SIZE_UNITS.put(SIZE_UNIT_KILOBYTES_ABBR, 1000D);
    SIZE_UNITS.put(SIZE_UNIT_KILOBYTES_FULL, 1000D);
    SIZE_UNITS.put(SIZE_UNIT_MEGABYTES_ABBR, 1000000D);
    SIZE_UNITS.put(SIZE_UNIT_MEGABYTES_FULL, 1000000D);
    SIZE_UNITS.put(SIZE_UNIT_GIGABYTES_ABBR, 1000000000D);
    SIZE_UNITS.put(SIZE_UNIT_GIGABYTES_FULL, 1000000000D);
    SIZE_UNITS.put(SIZE_UNIT_KIBIBYTES_ABBR, 1024D);
    SIZE_UNITS.put(SIZE_UNIT_KIBIBYTES_FULL, 1024D);
    SIZE_UNITS.put(SIZE_UNIT_MEBIBYTES_ABBR, (double) (1024 * 1024));
    SIZE_UNITS.put(SIZE_UNIT_MEBIBYTES_FULL, (double) (1024 * 1024));
    SIZE_UNITS.put(SIZE_UNIT_GIBIBYTES_ABBR, (double) (1024 * 1024 * 1024));
    SIZE_UNITS.put(SIZE_UNIT_GIBIBYTES_FULL, (double) (1024 * 1024 * 1024));
  }
  /**
   * The maximum value that may be specified for the max request size
   * configuration attribute.
   */
  private static final int MAX_REQUEST_SIZE_LIMIT = 2147483647;
  // The set of clients that are explicitly allowed access to the server.
  private AddressMask[] allowedClients;
  // The set of clients that have been explicitly denied access to the server.
  private AddressMask[] deniedClients;
  // Indicates whether to allow LDAPv2 clients.
  private boolean allowLDAPv2;
  // Indicates whether to allow the reuse address socket option.
  private boolean allowReuseAddress;
  // Indicates whether to allow startTLS extended operations on this connection.
  private boolean allowStartTLS;
  // Indicates whether this connection handler is enabled.
  private boolean enabled;
  // Indicates whether usage statistics should be maintained.
  private boolean keepStats;
  // Indicates whether the server should send an LDAP notice of disconnection
  // message to a client if a connection is rejected.
  private boolean sendRejectionNotice;
  // Indicates whether the Directory Server is in the process of shutting down.
  private boolean shutdownRequested;
  // Indicates whether to use TCP keepalive messages for new connections.
  private boolean useKeepAlive;
  // Indicates whether to use SSL to communicate with the clients.
  private boolean useSSL;
  // Indicates whether to use TCP_NODELAY for new connections.
  private boolean useTCPNoDelay;
  // The connection security provider that will be used by default for new
  // client connections.
  private ConnectionSecurityProvider securityProvider;
  // The DN of the configuration entry for this connection handler.
  private DN configEntryDN;
  // The DN of the key manager provider for this connection handler.
  private DN keyManagerProviderDN;
  // The DN of the trust manager provider for this connection handler.
  private DN trustManagerProviderDN;
  /* Properties that cannot be modified dynamically */
  // The set of addresses on which to listen for new connections.
  private HashSet<InetAddress> listenAddresses;
  private Set<InetAddress> listenAddresses;
  // The backlog that will be used for the accept queue.
  private int backlog;
  // The port on which this connection handler should listen for requests.
  // The port on which this connection handler should listen for
  // requests.
  private int listenPort;
  // The maximum ASN.1 element value length that will be allowed when processing
  // requests for this connection handler.
  private int maxRequestSize;
  // The number of request handlers that should be used for this connection
  // handler.
  private int numRequestHandlers;
  // The index to the request handler that will be used for the next connection
  // accepted by the server.
  private int requestHandlerIndex;
  // The set of listeners for this connection handler.
  private LinkedList<HostPort> listeners;
  // The set of request handlers that are associated with this connection
  // handler.
  private LDAPRequestHandler[] requestHandlers;
  // The set of statistics collected for this connection handler.
  private LDAPStatistics statTracker;
  // The selector that will be used to multiplex connection acceptance across
  // multiple sockets by a single thread.
  private Selector selector;
  // The SSL client auth policy used by this connection handler.
  private SSLClientAuthPolicy sslClientAuthPolicy;
  // The unique name assigned to this connection handler.
  private String handlerName;
  // The backlog that will be used for the accept queue.
  private int backlog;
  // The protocol used by this connection handler.
  private String protocol;
  // Indicates whether to allow the reuse address socket option.
  private boolean allowReuseAddress;
  // The security mechanism used for connections accepted by this connection
  // handler.
  private String securityMechanism;
  // The number of request handlers that should be used for this
  // connection handler.
  private int numRequestHandlers;
  // The nickname of the SSL certificate that should be used if SSL is enabled.
  private String sslServerCertNickname;
  // Indicates whether the Directory Server is in the process of
  // shutting down.
  private boolean shutdownRequested;
  /* Internal LDAP connection handler state */
  // Indicates whether this connection handler is enabled.
  private boolean enabled;
  // The set of clients that are explicitly allowed access to the
  // server.
  private AddressMask[] allowedClients;
  // The set of clients that have been explicitly denied access to the
  // server.
  private AddressMask[] deniedClients;
  // The set of SSL cipher suites that should be allowed.
  private String[] enabledSSLCipherSuites;
@@ -254,1013 +150,181 @@
  // The set of SSL protocols that should be allowed.
  private String[] enabledSSLProtocols;
  // The thread being used to run this connection handler.
  private Thread connHandlerThread;
  // The index to the request handler that will be used for the next
  // connection accepted by the server.
  private int requestHandlerIndex;
  // The set of listeners for this connection handler.
  private LinkedList<HostPort> listeners;
  // The set of request handlers that are associated with this
  // connection handler.
  private LDAPRequestHandler[] requestHandlers;
  // The set of statistics collected for this connection handler.
  private LDAPStatistics statTracker;
  // The selector that will be used to multiplex connection acceptance
  // across multiple sockets by a single thread.
  private Selector selector;
  // The unique name assigned to this connection handler.
  private String handlerName;
  // The protocol used by this connection handler.
  private String protocol;
  // The connection security provider that will be used by default for
  // new client connections.
  private ConnectionSecurityProvider securityProvider;
  /**
   * Creates a new instance of this LDAP connection handler.  It must be
   * initialized before it may be used.
   * Creates a new instance of this LDAP connection handler. It must
   * be initialized before it may be used.
   */
  public LDAPConnectionHandler()
  {
  public LDAPConnectionHandler() {
    super("LDAP Connection Handler Thread");
    // No real implementation is required.  Do all the work in the
    // No real implementation is required. Do all the work in the
    // initializeConnectionHandler method.
  }
  /**
   * Initializes this connection handler based on the information in the
   * provided configuration entry.
   * Indicates whether this connection handler should allow
   * interaction with LDAPv2 clients.
   *
   * @param  configEntry  The configuration entry that contains the information
   *                      to use to initialize this connection handler.
   *
   * @throws  ConfigException  If there is a problem with the configuration for
   *                           this connection handler.
   *
   * @throws  InitializationException  If a problem occurs while attempting to
   *                                   initialize this connection handler.
   * @return <CODE>true</CODE> if LDAPv2 is allowed, or <CODE>false</CODE>
   *         if not.
   */
  public void initializeConnectionHandler(ConfigEntry configEntry)
         throws ConfigException, InitializationException
  {
    enabled = true;
  public boolean allowLDAPv2() {
    return currentConfig.isAllowLDAPV2();
  }
    configEntryDN = configEntry.getDN();
    // Determine the set of addresses on which to listen.  There can be
    // multiple addresses specified.
    int msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_LISTEN_ADDRESS;
    listenAddresses = new HashSet<InetAddress>();
    StringConfigAttribute addrStub =
         new StringConfigAttribute(ATTR_LISTEN_ADDRESS, getMessage(msgID),
                                   true, true, true);
    try
    {
      StringConfigAttribute addrAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(addrStub);
      if ((addrAttr == null) || addrAttr.activeValues().isEmpty())
      {
        // This is fine -- we'll just listen on all IPv4 addresses.
        listenAddresses.add(InetAddress.getByName("0.0.0.0"));
  /**
   * Indicates whether this connection handler should allow the use of
   * the StartTLS extended operation.
   *
   * @return <CODE>true</CODE> if StartTLS is allowed, or <CODE>false</CODE>
   *         if not.
   */
  public boolean allowStartTLS() {
    if (currentConfig.isAllowStartTLS()) {
      if (currentConfig.isUseSSL()) {
        return false;
      } else {
        return true;
      }
      else
      {
        for (String s : addrAttr.activeValues())
        {
          try
          {
            listenAddresses.add(InetAddress.getByName(s));
          }
          catch (UnknownHostException uhe)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, uhe);
            }
            msgID = MSGID_LDAP_CONNHANDLER_UNKNOWN_LISTEN_ADDRESS;
            String message = getMessage(msgID, s,
                                        stackTraceToSingleLineString(uhe));
            throw new ConfigException(msgID, message, uhe);
          }
        }
      }
    }
    catch (ConfigException ce)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, ce);
      }
      throw ce;
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_LISTEN_ADDRESS;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine the port on which to listen.  There may only be a single port
    // specified.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_LISTEN_PORT;
    IntegerConfigAttribute portStub =
         new IntegerConfigAttribute(ATTR_LISTEN_PORT, getMessage(msgID), true,
                                    false, true, true, 1, true, 65535);
    try
    {
      IntegerConfigAttribute portAttr =
           (IntegerConfigAttribute) configEntry.getConfigAttribute(portStub);
      if (portAttr == null)
      {
        msgID = MSGID_LDAP_CONNHANDLER_NO_LISTEN_PORT;
        String message = getMessage(msgID, String.valueOf(configEntryDN));
        throw new ConfigException(msgID, message);
      }
      listenPort = portAttr.activeIntValue();
    }
    catch (ConfigException ce)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, ce);
      }
      throw ce;
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_LISTEN_PORT;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine the accept backlog.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_BACKLOG;
    IntegerConfigAttribute backlogStub =
         new IntegerConfigAttribute(ATTR_ACCEPT_BACKLOG, getMessage(msgID),
                                 true, false, true, true, 1, true,
                                 Integer.MAX_VALUE);
    try
    {
      IntegerConfigAttribute backlogAttr =
           (IntegerConfigAttribute) configEntry.getConfigAttribute(backlogStub);
      if (backlogAttr == null)
      {
        // This is fine -- just use the default value.
        backlog = DEFAULT_ACCEPT_BACKLOG;
      }
      else
      {
        backlog = backlogAttr.activeIntValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_BACKLOG;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine the set of allowed clients.
    allowedClients = null;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOWED_CLIENTS;
    StringConfigAttribute allowedStub =
         new StringConfigAttribute(ATTR_ALLOWED_CLIENT, getMessage(msgID),
                                   false, true, false);
    try
    {
      StringConfigAttribute allowedAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(allowedStub);
      if (allowedAttr != null)
      {
        List<String> maskStrings = allowedAttr.activeValues();
        allowedClients = new AddressMask[maskStrings.size()];
        for (int i=0; i < allowedClients.length; i++)
        {
          try
          {
            allowedClients[i] = AddressMask.decode(maskStrings.get(i));
          }
          catch (ConfigException ce)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, ce);
            }
            msgID = MSGID_LDAP_CONNHANDLER_INVALID_ADDRESS_MASK;
            String message = getMessage(msgID, maskStrings.get(i),
                                        ATTR_ALLOWED_CLIENT,
                                        String.valueOf(configEntryDN),
                                        stackTraceToSingleLineString(ce));
            throw new ConfigException(msgID, message, ce);
          }
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_ALLOWED_CLIENTS;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine the set of denied clients.
    deniedClients = null;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_DENIED_CLIENTS;
    StringConfigAttribute deniedStub =
         new StringConfigAttribute(ATTR_DENIED_CLIENT, getMessage(msgID),
                                   false, true, false);
    try
    {
      StringConfigAttribute deniedAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(deniedStub);
      if (deniedAttr != null)
      {
        List<String> maskStrings = deniedAttr.activeValues();
        deniedClients = new AddressMask[maskStrings.size()];
        for (int i=0; i < deniedClients.length; i++)
        {
          try
          {
            deniedClients[i] = AddressMask.decode(maskStrings.get(i));
          }
          catch (ConfigException ce)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, ce);
            }
            msgID = MSGID_LDAP_CONNHANDLER_INVALID_ADDRESS_MASK;
            String message = getMessage(msgID, maskStrings.get(i),
                                        ATTR_ALLOWED_CLIENT,
                                        String.valueOf(configEntryDN),
                                        stackTraceToSingleLineString(ce));
            throw new ConfigException(msgID, message, ce);
          }
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_DENIED_CLIENTS;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine whether to allow LDAPv2 clients.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOW_LDAPV2;
    BooleanConfigAttribute allowLDAPv2Stub =
         new BooleanConfigAttribute(ATTR_ALLOW_LDAPV2, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute allowLDAPv2Attr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(allowLDAPv2Stub);
      if (allowLDAPv2Attr == null)
      {
        // This is fine -- we'll just use the default behavior, which is to
        // allow these clients.
        allowLDAPv2 = DEFAULT_ALLOW_LDAPV2;
      }
      else
      {
        allowLDAPv2 = allowLDAPv2Attr.activeValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_ALLOW_LDAPV2;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine whether to keep LDAP statistics.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_KEEP_STATS;
    BooleanConfigAttribute keepStatsStub =
         new BooleanConfigAttribute(ATTR_KEEP_LDAP_STATS, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute keepStatsAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(keepStatsStub);
      if (keepStatsAttr == null)
      {
        // This is fine -- we'll just use the default behavior, which is to
        // allow these clients.
        keepStats = DEFAULT_KEEP_LDAP_STATS;
      }
      else
      {
        keepStats = keepStatsAttr.activeValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_KEEP_STATS;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine the number of request handlers to maintain.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_NUM_REQUEST_HANDLERS;
    IntegerConfigAttribute reqHandlerStub =
         new IntegerConfigAttribute(ATTR_NUM_REQUEST_HANDLERS,
                                    getMessage(msgID), true, false, true,
                                    true, 1, false, 0);
    try
    {
      IntegerConfigAttribute reqHandlerAttr =
           (IntegerConfigAttribute)
           configEntry.getConfigAttribute(reqHandlerStub);
      if (reqHandlerAttr == null)
      {
        // This is fine -- we'll just use the default value.
        numRequestHandlers = DEFAULT_NUM_REQUEST_HANDLERS;
      }
      else
      {
        numRequestHandlers = reqHandlerAttr.activeIntValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_NUM_REQUEST_HANDLERS;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine whether to send a notice to clients on rejection.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SEND_REJECTION_NOTICE;
    BooleanConfigAttribute notifyRejectsStub =
         new BooleanConfigAttribute(ATTR_SEND_REJECTION_NOTICE,
                                    getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute notifyRejectsAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(notifyRejectsStub);
      if (notifyRejectsAttr == null)
      {
        // This is fine -- we'll just use the default value.
        sendRejectionNotice = DEFAULT_SEND_REJECTION_NOTICE;
      }
      else
      {
        sendRejectionNotice = notifyRejectsAttr.activeValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_SEND_REJECTION_NOTICE;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine whether to use TCP keepalive.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_USE_TCP_KEEPALIVE;
    BooleanConfigAttribute keepAliveStub =
         new BooleanConfigAttribute(ATTR_USE_TCP_KEEPALIVE, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute keepAliveAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(keepAliveStub);
      if (keepAliveAttr == null)
      {
        // This is fine -- we'll just use the default.
        useKeepAlive = DEFAULT_USE_TCP_KEEPALIVE;
      }
      else
      {
        useKeepAlive = keepAliveAttr.activeValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_USE_TCP_KEEPALIVE;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine whether to use TCP nodelay.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_USE_TCP_NODELAY;
    BooleanConfigAttribute noDelayStub =
         new BooleanConfigAttribute(ATTR_USE_TCP_NODELAY, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute noDelayAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(noDelayStub);
      if (noDelayAttr == null)
      {
        // This is fine -- we'll just use the default.
        useTCPNoDelay = DEFAULT_USE_TCP_NODELAY;
      }
      else
      {
        useTCPNoDelay = noDelayAttr.activeValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_USE_TCP_NODELAY;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine whether to allow reuse of address/port combinations.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOW_REUSE_ADDRESS;
    BooleanConfigAttribute reuseAddrStub =
         new BooleanConfigAttribute(ATTR_ALLOW_REUSE_ADDRESS,
                                    getMessage(msgID), true);
    try
    {
      BooleanConfigAttribute reuseAddrAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(reuseAddrStub);
      if (reuseAddrAttr == null)
      {
        // This is fine -- we'll just use the default.
        allowReuseAddress = DEFAULT_ALLOW_REUSE_ADDRESS;
      }
      else
      {
        allowReuseAddress = reuseAddrAttr.activeValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_ALLOW_REUSE_ADDRESS;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine the maximum allowed request size.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_MAX_REQUEST_SIZE;
    IntegerWithUnitConfigAttribute maxReqSizeStub =
         new IntegerWithUnitConfigAttribute(ATTR_MAX_REQUEST_SIZE,
                                            getMessage(msgID), false,
                                            SIZE_UNITS, true, 0, true,
                                            MAX_REQUEST_SIZE_LIMIT);
    try
    {
      IntegerWithUnitConfigAttribute maxReqSizeAttr =
           (IntegerWithUnitConfigAttribute)
           configEntry.getConfigAttribute(maxReqSizeStub);
      if (maxReqSizeAttr == null)
      {
        // This is fine -- we'll just use the default value.
        maxRequestSize = DEFAULT_MAX_REQUEST_SIZE;
      }
      else
      {
        maxRequestSize = (int) maxReqSizeAttr.activeCalculatedValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_MAX_REQUEST_SIZE;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine whether to use SSL.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_USE_SSL;
    BooleanConfigAttribute useSSLStub =
         new BooleanConfigAttribute(ATTR_USE_SSL, getMessage(msgID), true);
    try
    {
      BooleanConfigAttribute useSSLAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(useSSLStub);
      if (useSSLAttr == null)
      {
        // This is fine -- we'll just use the default value.
        useSSL = DEFAULT_USE_SSL;
      }
      else
      {
        useSSL = useSSLAttr.activeValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_USE_SSL;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine whether to allow the StartTLS extended operation.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOW_STARTTLS;
    BooleanConfigAttribute startTLSStub =
         new BooleanConfigAttribute(ATTR_ALLOW_STARTTLS, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute startTLSAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(startTLSStub);
      if (startTLSAttr == null)
      {
        // This is fine -- we'll just use the default.
        allowStartTLS = DEFAULT_ALLOW_STARTTLS;
      }
      else
      {
        allowStartTLS = startTLSAttr.activeValue();
      }
      // See if both SSL and startTLS are configured.  If so, we'll have to
      // disable startTLS because they can't both be used concurrently.
      if (useSSL && allowStartTLS)
      {
        msgID = MSGID_LDAP_CONNHANDLER_CANNOT_HAVE_SSL_AND_STARTTLS;
        String message = getMessage(msgID, String.valueOf(configEntryDN));
        logError(ErrorLogCategory.CONFIGURATION,
                 ErrorLogSeverity.SEVERE_WARNING, message, msgID);
        allowStartTLS = false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_ALLOW_STARTTLS;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine how to handle SSL client authentication.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SSL_CLIENT_AUTH_POLICY;
    HashSet<String> allowedValues = new HashSet<String>(3);
    allowedValues.add(toLowerCase(SSLClientAuthPolicy.DISABLED.toString()));
    allowedValues.add(toLowerCase(SSLClientAuthPolicy.OPTIONAL.toString()));
    allowedValues.add(toLowerCase(SSLClientAuthPolicy.REQUIRED.toString()));
    MultiChoiceConfigAttribute sslAuthPolicyStub =
         new MultiChoiceConfigAttribute(ATTR_SSL_CLIENT_AUTH_POLICY,
                                        getMessage(msgID), false, false, true,
                                        allowedValues);
    try
    {
      MultiChoiceConfigAttribute sslAuthPolicyAttr =
           (MultiChoiceConfigAttribute)
           configEntry.getConfigAttribute(sslAuthPolicyStub);
      if (sslAuthPolicyAttr == null)
      {
        // This is fine -- We'll just use the default.
        sslClientAuthPolicy = DEFAULT_SSL_CLIENT_AUTH_POLICY;
      }
      else
      {
        sslClientAuthPolicy = SSLClientAuthPolicy.policyForName(
                                   sslAuthPolicyAttr.activeValue());
        if (sslClientAuthPolicy == null)
        {
          msgID = MSGID_LDAP_CONNHANDLER_INVALID_SSL_CLIENT_AUTH_POLICY;
          String message = getMessage(msgID, sslAuthPolicyAttr.activeValue(),
                                      String.valueOf(configEntryDN));
          throw new ConfigException(msgID, message);
        }
      }
    }
    catch (ConfigException ce)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, ce);
      }
      throw ce;
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_SSL_CLIENT_AUTH_POLICY;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine which SSL certificate to use.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SSL_CERT_NICKNAME;
    StringConfigAttribute certNameStub =
         new StringConfigAttribute(ATTR_SSL_CERT_NICKNAME, getMessage(msgID),
                                   false, false, true);
    try
    {
      StringConfigAttribute certNameAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(certNameStub);
      if (certNameAttr == null)
      {
        // This is fine -- We'll just let the server pick one.
        sslServerCertNickname = null;
      }
      else
      {
        sslServerCertNickname = certNameAttr.activeValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_SSL_CERT_NICKNAME;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine the set of SSL protocols to allow.
    enabledSSLProtocols = null;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SSL_ENABLED_PROTOCOLS;
    StringConfigAttribute sslProtocolsStub =
         new StringConfigAttribute(ATTR_SSL_PROTOCOLS, getMessage(msgID), false,
                                   true, false);
    try
    {
      StringConfigAttribute sslProtocolsAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(sslProtocolsStub);
      if (sslProtocolsAttr != null)
      {
        enabledSSLProtocols = listToArray(sslProtocolsAttr.activeValues());
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_SSL_PROTOCOLS;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine the set of SSL cipher suites to allow.
    enabledSSLCipherSuites = null;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SSL_ENABLED_CIPHERS;
    StringConfigAttribute sslCiphersStub =
         new StringConfigAttribute(ATTR_SSL_CIPHERS, getMessage(msgID), false,
                                   true, false);
    try
    {
      StringConfigAttribute sslCiphersAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(sslCiphersStub);
      if (sslCiphersAttr != null)
      {
        enabledSSLCipherSuites = listToArray(sslCiphersAttr.activeValues());
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_SSL_CIPHERS;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine the key manager provider to use.
    keyManagerProviderDN = null;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_KEYMANAGER_DN;
    DNConfigAttribute keyManagerStub =
         new DNConfigAttribute(ATTR_KEYMANAGER_DN, getMessage(msgID), false,
                               false, false);
    try
    {
      DNConfigAttribute keyManagerAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(keyManagerStub);
      if (keyManagerAttr != null)
      {
        keyManagerProviderDN = keyManagerAttr.activeValue();
        KeyManagerProvider provider =
             DirectoryServer.getKeyManagerProvider(keyManagerProviderDN);
        if (provider == null)
        {
          msgID = MSGID_LDAP_CONNHANDLER_INVALID_KEYMANAGER_DN;
          String message = getMessage(msgID, String.valueOf(configEntryDN),
                                      String.valueOf(keyManagerProviderDN));
          throw new ConfigException(msgID, message);
        }
      }
    }
    catch (ConfigException ce)
    {
      throw ce;
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_KEYMANAGER_DN;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine the trust manager provider to use.
    trustManagerProviderDN = null;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_TRUSTMANAGER_DN;
    DNConfigAttribute trustManagerStub =
         new DNConfigAttribute(ATTR_TRUSTMANAGER_DN, getMessage(msgID), false,
                               false, false);
    try
    {
      DNConfigAttribute trustManagerAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(trustManagerStub);
      if (trustManagerAttr != null)
      {
        trustManagerProviderDN = trustManagerAttr.activeValue();
        TrustManagerProvider provider =
             DirectoryServer.getTrustManagerProvider(trustManagerProviderDN);
        if (provider == null)
        {
          msgID = MSGID_LDAP_CONNHANDLER_INVALID_TRUSTMANAGER_DN;
          String message = getMessage(msgID, String.valueOf(configEntryDN),
                                      String.valueOf(trustManagerProviderDN));
          throw new ConfigException(msgID, message);
        }
      }
    }
    catch (ConfigException ce)
    {
      throw ce;
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_TRUSTMANAGER_DN;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    if (useSSL)
    {
      TLSConnectionSecurityProvider tlsProvider =
           new TLSConnectionSecurityProvider();
      tlsProvider.initializeConnectionSecurityProvider(null);
      tlsProvider.setSSLClientAuthPolicy(sslClientAuthPolicy);
      tlsProvider.setEnabledProtocols(enabledSSLProtocols);
      tlsProvider.setEnabledCipherSuites(enabledSSLCipherSuites);
      // FIXME -- Need to do something with the requested cert nickname.
      securityProvider = tlsProvider;
    }
    else
    {
      securityProvider = new NullConnectionSecurityProvider();
      securityProvider.initializeConnectionSecurityProvider(null);
    }
    DirectoryServer.registerConfigurableComponent(this);
    try
    {
      selector = Selector.open();
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_OPEN_SELECTOR_FAILED;
      String message = getMessage(msgID, configEntryDN,
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Construct a unique name for this connection handler, and put together the
    // set of listeners.
    listeners = new LinkedList<HostPort>();
    StringBuilder nameBuffer = new StringBuilder();
    nameBuffer.append("LDAP Connection Handler");
    for (InetAddress a : listenAddresses)
    {
      listeners.add(new HostPort(a.getHostAddress(), listenPort));
      nameBuffer.append(" ");
      nameBuffer.append(a.getHostAddress());
    }
    nameBuffer.append(" port ");
    nameBuffer.append(listenPort);
    handlerName = nameBuffer.toString();
    // Set the security mechanism for this connection handler.
    if (useSSL)
    {
      securityMechanism = SECURITY_MECHANISM_SSL;
      protocol          = "LDAP+SSL";
    }
    else
    {
      securityMechanism = null;
      protocol          = "LDAP";
    }
    // Perform any additional initialization that might be required.
    connHandlerThread = null;
    statTracker = new LDAPStatistics(handlerName + " Statistics");
    // Create and start the request handlers.
    requestHandlers = new LDAPRequestHandler[numRequestHandlers];
    for (int i=0; i < numRequestHandlers; i++)
    {
      requestHandlers[i] = new LDAPRequestHandler(this, i);
    }
    for (int i=0; i < numRequestHandlers; i++)
    {
      requestHandlers[i].start();
    } else {
      return false;
    }
  }
  /**
   * Closes this connection handler so that it will no longer accept new client
   * connections.  It may or may not disconnect existing client connections
   * based on the provided flag.  Note, however, that some connection handler
   * implementations may not have any way to continue processing requests from
   * existing connections, in which case they should always be closed regardless
   * of the value of the <CODE>closeConnections</CODE> flag.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(
      LDAPConnectionHandlerCfg config) {
    // Create variables to include in the response.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<String> messages = new ArrayList<String>();
    // Note that the following properties cannot be modified:
    //
    // * listen port and addresses
    // * use ssl
    // * ssl policy
    // * ssl cert nickname
    // * accept backlog
    // * tcp reuse address
    // * num request handler
    // Start/clear the stat tracker if LDAPv2 is being enabled.
    if (currentConfig.isAllowLDAPV2() != config.isAllowLDAPV2()) {
      if (config.isAllowLDAPV2()) {
        if (statTracker == null) {
          statTracker = new LDAPStatistics(handlerName
              + " Statistics");
        } else {
          statTracker.clearStatistics();
        }
      }
    }
    // Apply the changes.
    currentConfig = config;
    enabled = config.isEnabled();
    allowedClients = config.getAllowedClients().toArray(
        new AddressMask[0]);
    deniedClients = config.getDeniedClients().toArray(
        new AddressMask[0]);
    // Get the supported SSL ciphers and protocols.
    Set<String> ciphers = config.getSSLCipherSuites();
    if (ciphers.isEmpty()) {
      enabledSSLCipherSuites = null;
    } else {
      enabledSSLCipherSuites = ciphers.toArray(new String[0]);
    }
    Set<String> protocols = config.getSSLProtocols();
    if (protocols.isEmpty()) {
      enabledSSLProtocols = null;
    } else {
      enabledSSLProtocols = protocols.toArray(new String[0]);
    }
    return new ConfigChangeResult(resultCode, adminActionRequired,
        messages);
  }
  /**
   * Closes this connection handler so that it will no longer accept
   * new client connections. It may or may not disconnect existing
   * client connections based on the provided flag. Note, however,
   * that some connection handler implementations may not have any way
   * to continue processing requests from existing connections, in
   * which case they should always be closed regardless of the value
   * of the <CODE>closeConnections</CODE> flag.
   *
   * @param  finalizeReason    The reason that this connection handler should be
   *                           finalized.
   * @param  closeConnections  Indicates whether any established client
   *                           connections associated with the connection
   *                           handler should also be closed.
   * @param finalizeReason
   *          The reason that this connection handler should be
   *          finalized.
   * @param closeConnections
   *          Indicates whether any established client connections
   *          associated with the connection handler should also be
   *          closed.
   */
  public void finalizeConnectionHandler(String finalizeReason,
                                       boolean closeConnections)
  {
      boolean closeConnections) {
    shutdownRequested = true;
    currentConfig.removeLDAPChangeListener(this);
    DirectoryServer.deregisterConfigurableComponent(this);
    try
    {
    try {
      selector.wakeup();
    }
    catch (Exception e)
    {
    } catch (Exception e) {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
    }
    if (closeConnections)
    {
      for (LDAPRequestHandler requestHandler : requestHandlers)
      {
    if (closeConnections) {
      for (LDAPRequestHandler requestHandler : requestHandlers) {
        requestHandler.processServerShutdown(finalizeReason);
      }
    }
    else
    {
      for (LDAPRequestHandler requestHandler : requestHandlers)
      {
    } else {
      for (LDAPRequestHandler requestHandler : requestHandlers) {
        requestHandler.registerShutdownListener();
      }
    }
@@ -1269,48 +333,54 @@
  /**
   * {@inheritDoc}
   */
  public String getConnectionHandlerName()
  {
    return handlerName;
  }
  /**
   * {@inheritDoc}
   */
  public String getProtocol()
  {
    return protocol;
  }
  /**
   * {@inheritDoc}
   */
  public Collection<HostPort> getListeners()
  {
    return listeners;
  }
  /**
   * Retrieves the set of active client connections that have been established
   * through this connection handler.
   * Retrieves information about the set of alerts that this generator
   * may produce. The map returned should be between the notification
   * type for a particular notification and the human-readable
   * description for that notification. This alert generator must not
   * generate any alerts with types that are not contained in this
   * list.
   *
   * @return  The set of active client connections that have been established
   *          through this connection handler.
   * @return Information about the set of alerts that this generator
   *         may produce.
   */
  public Collection<ClientConnection> getClientConnections()
  {
  public LinkedHashMap<String, String> getAlerts() {
    LinkedHashMap<String, String> alerts = new LinkedHashMap<String, String>();
    alerts
        .put(ALERT_TYPE_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES,
            ALERT_DESCRIPTION_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES);
    alerts.put(ALERT_TYPE_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR,
        ALERT_DESCRIPTION_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR);
    return alerts;
  }
  /**
   * Retrieves the fully-qualified name of the Java class for this
   * alert generator implementation.
   *
   * @return The fully-qualified name of the Java class for this alert
   *         generator implementation.
   */
  public String getClassName() {
    return CLASS_NAME;
  }
  /**
   * Retrieves the set of active client connections that have been
   * established through this connection handler.
   *
   * @return The set of active client connections that have been
   *         established through this connection handler.
   */
  public Collection<ClientConnection> getClientConnections() {
    LinkedList<ClientConnection> connectionList =
         new LinkedList<ClientConnection>();
    for (LDAPRequestHandler requestHandler : requestHandlers)
    {
      new LinkedList<ClientConnection>();
    for (LDAPRequestHandler requestHandler : requestHandlers) {
      connectionList.addAll(requestHandler.getClientConnections());
    }
@@ -1320,104 +390,492 @@
  /**
   * Retrieves the DN of the configuration entry with which this alert
   * generator is associated.
   *
   * @return The DN of the configuration entry with which this alert
   *         generator is associated.
   */
  public DN getComponentEntryDN() {
    return currentConfig.dn();
  }
  /**
   * {@inheritDoc}
   */
  public String getConnectionHandlerName() {
    return handlerName;
  }
  /**
   * Retrieves the set of enabled SSL cipher suites configured for
   * this connection handler.
   *
   * @return The set of enabled SSL cipher suites configured for this
   *         connection handler.
   */
  public String[] getEnabledSSLCipherSuites() {
    return enabledSSLCipherSuites;
  }
  /**
   * Retrieves the set of enabled SSL protocols configured for this
   * connection handler.
   *
   * @return The set of enabled SSL protocols configured for this
   *         connection handler.
   */
  public String[] getEnabledSSLProtocols() {
    return enabledSSLProtocols;
  }
  /**
   * Retrieves the DN of the key manager provider that should be used
   * for operations associated with this connection handler which need
   * access to a key manager.
   *
   * @return The DN of the key manager provider that should be used
   *         for operations associated with this connection handler
   *         which need access to a key manager, or {@code null} if no
   *         key manager provider has been configured for this
   *         connection handler.
   */
  public DN getKeyManagerProviderDN() {
    return currentConfig.getKeyManagerProviderDN();
  }
  /**
   * {@inheritDoc}
   */
  public Collection<HostPort> getListeners() {
    return listeners;
  }
  /**
   * Retrieves the port on which this connection handler is listening
   * for client connections.
   *
   * @return  The port on which this connection handler is listening
   *          for client connections.
   * @return The port on which this connection handler is listening
   *         for client connections.
   */
  public int getListenPort()
  {
  public int getListenPort() {
    return listenPort;
  }
  /**
   * Operates in a loop, accepting new connections and ensuring that requests on
   * those connections are handled properly.
   * Retrieves the maximum ASN.1 element value length that will be
   * allowed by this connection handler.
   *
   * @return The maximum ASN.1 element value length that will be
   *         allowed by this connection handler.
   */
  public void run()
  {
    connHandlerThread = Thread.currentThread();
    setName(handlerName);
  public int getMaxRequestSize() {
    return (int) ((long) currentConfig.getMaxRequestSize());
  }
  /**
   * {@inheritDoc}
   */
  public String getProtocol() {
    return protocol;
  }
  /**
   * {@inheritDoc}
   */
  public String getShutdownListenerName() {
    return handlerName;
  }
  /**
   * Retrieves the nickname of the server certificate that should be
   * used in conjunction with this LDAP connection handler.
   *
   * @return The nickname of the server certificate that should be
   *         used in conjunction with this LDAP connection handler.
   */
  public String getSSLServerCertNickname() {
    return currentConfig.getSSLCertNickname();
  }
  /**
   * Retrieves the SSL client authentication policy for this
   * connection handler.
   *
   * @return The SSL client authentication policy for this connection
   *         handler.
   */
  public SSLClientAuthPolicy getSSLClientAuthPolicy() {
    return sslClientAuthPolicy;
  }
  /**
   * Retrieves the set of statistics maintained by this connection
   * handler.
   *
   * @return The set of statistics maintained by this connection
   *         handler.
   */
  public LDAPStatistics getStatTracker() {
    return statTracker;
  }
  /**
   * Retrieves the DN of the trust manager provider that should be
   * used for operations associated with this connection handler which
   * need access to a trust manager.
   *
   * @return The DN of the trust manager provider that should be used
   *         for operations associated with this connection handler
   *         which need access to a trust manager, or {@code null} if
   *         no trust manager provider has been configured for this
   *         connection handler.
   */
  public DN getTrustManagerProviderDN() {
    return currentConfig.getTrustManagerProviderDN();
  }
  /**
   * {@inheritDoc}
   */
  public void initializeConnectionHandler(
      LDAPConnectionHandlerCfg config)
      throws ConfigException, InitializationException {
    // SSL and StartTLS are mutually exclusive.
    if (config.isAllowStartTLS() && config.isUseSSL()) {
      int msgID = MSGID_LDAP_CONNHANDLER_CANNOT_HAVE_SSL_AND_STARTTLS;
      String message = getMessage(msgID, String.valueOf(config.dn()));
      logError(ErrorLogCategory.CONFIGURATION,
          ErrorLogSeverity.SEVERE_WARNING, message, msgID);
    }
    // Validate the key manager provider DN.
    DN keyManagerProviderDN = config.getKeyManagerProviderDN();
    if (keyManagerProviderDN != null) {
      KeyManagerProvider provider = DirectoryServer
          .getKeyManagerProvider(keyManagerProviderDN);
      if (provider == null) {
        int msgID = MSGID_LDAP_CONNHANDLER_INVALID_KEYMANAGER_DN;
        String message = getMessage(msgID, String
            .valueOf(config.dn()), String
            .valueOf(keyManagerProviderDN));
        throw new ConfigException(msgID, message);
      }
    }
    // Validate the trust manager provider DN.
    DN trustManagerProviderDN = config.getTrustManagerProviderDN();
    if (trustManagerProviderDN != null) {
      TrustManagerProvider provider = DirectoryServer
          .getTrustManagerProvider(trustManagerProviderDN);
      if (provider == null) {
        int msgID = MSGID_LDAP_CONNHANDLER_INVALID_TRUSTMANAGER_DN;
        String message = getMessage(msgID, String
            .valueOf(config.dn()), String
            .valueOf(trustManagerProviderDN));
        throw new ConfigException(msgID, message);
      }
    }
    // Open the selector.
    try {
      selector = Selector.open();
    } catch (Exception e) {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = MSGID_LDAP_CONNHANDLER_OPEN_SELECTOR_FAILED;
      String message = getMessage(msgID, config.dn(),
          stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Get the SSL auth policy.
    switch (config.getSSLClientAuthPolicy()) {
    case DISABLED:
      sslClientAuthPolicy = SSLClientAuthPolicy.DISABLED;
      break;
    case REQUIRED:
      sslClientAuthPolicy = SSLClientAuthPolicy.REQUIRED;
      break;
    default:
      sslClientAuthPolicy = SSLClientAuthPolicy.OPTIONAL;
      break;
    }
    // Get the supported SSL ciphers and protocols.
    Set<String> ciphers = config.getSSLCipherSuites();
    if (ciphers.isEmpty()) {
      enabledSSLCipherSuites = null;
    } else {
      enabledSSLCipherSuites = ciphers.toArray(new String[0]);
    }
    Set<String> protocols = config.getSSLProtocols();
    if (protocols.isEmpty()) {
      enabledSSLProtocols = null;
    } else {
      enabledSSLProtocols = protocols.toArray(new String[0]);
    }
    // Initialize the security provider.
    if (config.isUseSSL()) {
      TLSConnectionSecurityProvider tlsProvider =
        new TLSConnectionSecurityProvider();
      tlsProvider.initializeConnectionSecurityProvider(null);
      tlsProvider.setSSLClientAuthPolicy(sslClientAuthPolicy);
      tlsProvider.setEnabledProtocols(enabledSSLProtocols);
      tlsProvider.setEnabledCipherSuites(enabledSSLCipherSuites);
      // FIXME -- Need to do something with the requested cert
      // nickname.
      securityProvider = tlsProvider;
    } else {
      securityProvider = new NullConnectionSecurityProvider();
      securityProvider.initializeConnectionSecurityProvider(null);
    }
    // Save this configuration for future reference.
    currentConfig = config;
    enabled = config.isEnabled();
    requestHandlerIndex = 0;
    allowedClients = config.getAllowedClients().toArray(
        new AddressMask[0]);
    deniedClients = config.getDeniedClients().toArray(
        new AddressMask[0]);
    // Save properties that cannot be dynamically modified.
    allowReuseAddress = config.isAllowTCPReuseAddress();
    backlog = config.getAcceptBacklog();
    listenAddresses = config.getListenAddresses();
    listenPort = config.getListenPort();
    numRequestHandlers = config.getNumRequestHandlers();
    // Construct a unique name for this connection handler, and put
    // together the
    // set of listeners.
    listeners = new LinkedList<HostPort>();
    StringBuilder nameBuffer = new StringBuilder();
    nameBuffer.append("LDAP Connection Handler");
    for (InetAddress a : listenAddresses) {
      listeners.add(new HostPort(a.getHostAddress(), listenPort));
      nameBuffer.append(" ");
      nameBuffer.append(a.getHostAddress());
    }
    nameBuffer.append(" port ");
    nameBuffer.append(listenPort);
    handlerName = nameBuffer.toString();
    // Set the protocol for this connection handler.
    if (config.isUseSSL()) {
      protocol = "LDAP+SSL";
    } else {
      protocol = "LDAP";
    }
    // Perform any additional initialization that might be required.
    statTracker = new LDAPStatistics(handlerName + " Statistics");
    // Create and start the request handlers.
    requestHandlers = new LDAPRequestHandler[numRequestHandlers];
    for (int i = 0; i < numRequestHandlers; i++) {
      requestHandlers[i] = new LDAPRequestHandler(this, i);
    }
    for (int i = 0; i < numRequestHandlers; i++) {
      requestHandlers[i].start();
    }
    // Register this as a change listener.
    config.addLDAPChangeListener(this);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
      LDAPConnectionHandlerCfg config,
      List<String> unacceptableReasons) {
    boolean isAcceptable = true;
    // SSL and StartTLS are mutually exclusive.
    if (config.isAllowStartTLS() && config.isUseSSL()) {
      int msgID = MSGID_LDAP_CONNHANDLER_CANNOT_HAVE_SSL_AND_STARTTLS;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(config
          .dn())));
      isAcceptable = false;
    }
    // Validate the key manager provider DN.
    DN keyManagerProviderDN = config.getKeyManagerProviderDN();
    if (keyManagerProviderDN != null) {
      KeyManagerProvider provider = DirectoryServer
          .getKeyManagerProvider(keyManagerProviderDN);
      if (provider == null) {
        int msgID = MSGID_LDAP_CONNHANDLER_INVALID_KEYMANAGER_DN;
        unacceptableReasons.add(getMessage(msgID, String
            .valueOf(config.dn()), String
            .valueOf(keyManagerProviderDN)));
        isAcceptable = false;
      }
    }
    // Validate the trust manager provider DN.
    DN trustManagerProviderDN = config.getTrustManagerProviderDN();
    if (trustManagerProviderDN != null) {
      TrustManagerProvider provider = DirectoryServer
          .getTrustManagerProvider(trustManagerProviderDN);
      if (provider == null) {
        int msgID = MSGID_LDAP_CONNHANDLER_INVALID_TRUSTMANAGER_DN;
        unacceptableReasons.add(getMessage(msgID, String
            .valueOf(config.dn()), String
            .valueOf(trustManagerProviderDN)));
        isAcceptable = false;
      }
    }
    return isAcceptable;
  }
  /**
   * Indicates whether this connection handler should maintain usage
   * statistics.
   *
   * @return <CODE>true</CODE> if this connection handler should
   *         maintain usage statistics, or <CODE>false</CODE> if
   *         not.
   */
  public boolean keepStats() {
    return currentConfig.isKeepStats();
  }
  /**
   * {@inheritDoc}
   */
  public void processServerShutdown(String reason) {
    shutdownRequested = true;
    try {
      for (LDAPRequestHandler requestHandler : requestHandlers) {
        try {
          requestHandler.processServerShutdown(reason);
        } catch (Exception e) {
        }
      }
    } catch (Exception e) {
    }
  }
  /**
   * Operates in a loop, accepting new connections and ensuring that
   * requests on those connections are handled properly.
   */
  public void run() {
    setName(handlerName);
    boolean listening = false;
    while (! shutdownRequested)
    {
      // If this connection handler is not enabled, then just sleep for a bit
      // and check again.
      if (! enabled)
      {
        if (listening)
        {
    while (!shutdownRequested) {
      // If this connection handler is not enabled, then just sleep
      // for a bit and check again.
      if (!enabled) {
        if (listening) {
          cleanUpSelector();
          listening = false;
          enabled   = false;
          logError(ErrorLogCategory.CONNECTION_HANDLING,
                   ErrorLogSeverity.NOTICE,
                   MSGID_LDAP_CONNHANDLER_STOPPED_LISTENING, handlerName);
              ErrorLogSeverity.NOTICE,
              MSGID_LDAP_CONNHANDLER_STOPPED_LISTENING, handlerName);
        }
        try
        {
        try {
          Thread.sleep(1000);
        } catch (Exception e) {}
        } catch (Exception e) {
        }
        continue;
      }
      // If we have gotten here, then we are about to start listening for the
      // first time since startup or since we were previously disabled.  Make
      // sure to start with a clean selector and then create all the listeners.
      try
      {
      // If we have gotten here, then we are about to start listening
      // for the first time since startup or since we were previously
      // disabled. Make sure to start with a clean selector and then
      // create all the listeners.
      try {
        cleanUpSelector();
        int numRegistered = 0;
        for (InetAddress a : listenAddresses)
        {
          try
          {
        for (InetAddress a : listenAddresses) {
          try {
            ServerSocketChannel channel = ServerSocketChannel.open();
            channel.socket().setReuseAddress(allowReuseAddress);
            channel.socket().bind(new InetSocketAddress(a, listenPort),
                                  backlog);
            channel.socket().bind(
                new InetSocketAddress(a, listenPort), backlog);
            channel.configureBlocking(false);
            channel.register(selector, SelectionKey.OP_ACCEPT);
            numRegistered++;
            logError(ErrorLogCategory.CONNECTION_HANDLING,
                     ErrorLogSeverity.NOTICE,
                     MSGID_LDAP_CONNHANDLER_STARTED_LISTENING, handlerName);
          }
          catch (Exception e)
          {
                ErrorLogSeverity.NOTICE,
                MSGID_LDAP_CONNHANDLER_STARTED_LISTENING, handlerName);
          } catch (Exception e) {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, e);
            }
            logError(ErrorLogCategory.CONNECTION_HANDLING,
                     ErrorLogSeverity.SEVERE_ERROR,
                     MSGID_LDAP_CONNHANDLER_CREATE_CHANNEL_FAILED,
                     configEntryDN, a.getHostAddress(), listenPort,
                     stackTraceToSingleLineString(e));
                ErrorLogSeverity.SEVERE_ERROR,
                MSGID_LDAP_CONNHANDLER_CREATE_CHANNEL_FAILED,
                currentConfig.dn(), a.getHostAddress(), listenPort,
                stackTraceToSingleLineString(e));
          }
        }
        // If none of the listeners were created successfully, then consider the
        // connection handler disabled and require administrative action before
        // trying again.
        if (numRegistered == 0)
        {
        // If none of the listeners were created successfully, then
        // consider the connection handler disabled and require
        // administrative action before trying again.
        if (numRegistered == 0) {
          logError(ErrorLogCategory.CONNECTION_HANDLING,
                   ErrorLogSeverity.FATAL_ERROR,
                   MSGID_LDAP_CONNHANDLER_NO_ACCEPTORS, configEntryDN);
              ErrorLogSeverity.FATAL_ERROR,
              MSGID_LDAP_CONNHANDLER_NO_ACCEPTORS, currentConfig.dn());
          enabled = false;
          continue;
@@ -1425,66 +883,64 @@
        listening = true;
        // Enter a loop, waiting for new connections to arrive and then
        // accepting them as they come in.
        // Enter a loop, waiting for new connections to arrive and
        // then accepting them as they come in.
        boolean lastIterationFailed = false;
        while (enabled && (! shutdownRequested))
        {
          try
          {
            if (selector.select() > 0)
            {
              Iterator<SelectionKey> iterator =
                   selector.selectedKeys().iterator();
        while (enabled && (!shutdownRequested)) {
          try {
            if (selector.select() > 0) {
              Iterator<SelectionKey> iterator = selector
                  .selectedKeys().iterator();
              while (iterator.hasNext())
              {
              while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                if (key.isAcceptable())
                {
                if (key.isAcceptable()) {
                  // Accept the new client connection.
                  ServerSocketChannel serverChannel =
                       (ServerSocketChannel) key.channel();
                  SocketChannel clientChannel = serverChannel.accept();
                  ServerSocketChannel serverChannel = (ServerSocketChannel) key
                      .channel();
                  SocketChannel clientChannel = serverChannel
                      .accept();
                  LDAPClientConnection clientConnection =
                       new LDAPClientConnection(this, clientChannel);
                  InetAddress clientAddr=clientConnection.getRemoteAddress();
                  // Check to see if the client is on the denied list.  If so,
                  // then reject it immediately.
                  if((deniedClients != null) && (deniedClients.length > 0) &&
                          AddressMask.maskListContains(clientAddr.getAddress(),
                                  clientAddr.getHostName(), deniedClients))
                  {
                      clientConnection.disconnect(
                              DisconnectReason.CONNECTION_REJECTED,
                              sendRejectionNotice,
                              MSGID_LDAP_CONNHANDLER_DENIED_CLIENT,
                              clientConnection.getClientHostPort(),
                              clientConnection.getServerHostPort());
                    new LDAPClientConnection(this, clientChannel);
                  InetAddress clientAddr = clientConnection
                      .getRemoteAddress();
                  // Check to see if the client is on the denied list.
                  // If so, then reject it immediately.
                  if ((deniedClients.length > 0)
                      && AddressMask.maskListContains(clientAddr
                          .getAddress(), clientAddr.getHostName(),
                          deniedClients)) {
                    clientConnection.disconnect(
                        DisconnectReason.CONNECTION_REJECTED,
                        currentConfig.isSendRejectionNotice(),
                        MSGID_LDAP_CONNHANDLER_DENIED_CLIENT,
                        clientConnection.getClientHostPort(),
                        clientConnection.getServerHostPort());
                      iterator.remove();
                      continue;
                    iterator.remove();
                    continue;
                  }
                  // Check to see if there is an allowed list and if there is
                  // whether the client is on that list.  If not, then reject
                  // the connection.
                  if((allowedClients != null) && (allowedClients.length > 0) &&
                        (!AddressMask.maskListContains(clientAddr.getAddress(),
                                  clientAddr.getHostName(), allowedClients)))
                  {
                      clientConnection.disconnect(
                              DisconnectReason.CONNECTION_REJECTED,
                              sendRejectionNotice,
                              MSGID_LDAP_CONNHANDLER_DISALLOWED_CLIENT,
                              clientConnection.getClientHostPort(),
                              clientConnection.getServerHostPort());
                  // Check to see if there is an allowed list and if
                  // there is whether the client is on that list. If
                  // not, then reject the connection.
                  if ((allowedClients.length > 0)
                      && (!AddressMask.maskListContains(clientAddr
                          .getAddress(), clientAddr.getHostName(),
                          allowedClients))) {
                    clientConnection.disconnect(
                        DisconnectReason.CONNECTION_REJECTED,
                        currentConfig.isSendRejectionNotice(),
                        MSGID_LDAP_CONNHANDLER_DISALLOWED_CLIENT,
                        clientConnection.getClientHostPort(),
                        clientConnection.getServerHostPort());
                      iterator.remove();
                      continue;
                    iterator.remove();
                    continue;
                  }
                  clientChannel.socket().setKeepAlive(useKeepAlive);
                  clientChannel.socket().setTcpNoDelay(useTCPNoDelay);
                  clientChannel.socket().setKeepAlive(
                      currentConfig.isUseTCPKeepAlive());
                  clientChannel.socket().setTcpNoDelay(
                      currentConfig.isUseTCPNoDelay());
                  try
                  {
@@ -1510,69 +966,61 @@
                  }
                  // Check to see if the core server rejected the connection
                  // (e.g., already too many connections established).
                  if (clientConnection.getConnectionID() < 0)
                  {
                  // Check to see if the core server rejected the
                  // connection (e.g., already too many connections
                  // established).
                  if (clientConnection.getConnectionID() < 0) {
                    // The connection will have already been closed.
                    iterator.remove();
                    continue;
                  }
                  // If we've gotten here, then we'll take the connection so
                  // invoke the post-connect plugins and register the client
                  // connection with a request handler.
                  try
                  {
                    PluginConfigManager pluginManager =
                         DirectoryServer.getPluginConfigManager();
                    PostConnectPluginResult pluginResult =
                         pluginManager.invokePostConnectPlugins(
                              clientConnection);
                    if (pluginResult.connectionTerminated())
                    {
                  // If we've gotten here, then we'll take the
                  // connection so invoke the post-connect plugins and
                  // register the client connection with a request
                  // handler.
                  try {
                    PluginConfigManager pluginManager = DirectoryServer
                        .getPluginConfigManager();
                    PostConnectPluginResult pluginResult = pluginManager
                        .invokePostConnectPlugins(clientConnection);
                    if (pluginResult.connectionTerminated()) {
                      iterator.remove();
                      continue;
                    }
                    LDAPRequestHandler requestHandler =
                         requestHandlers[requestHandlerIndex++];
                    if (requestHandlerIndex >= numRequestHandlers)
                    {
                      requestHandlers[requestHandlerIndex++];
                    if (requestHandlerIndex >= numRequestHandlers) {
                      requestHandlerIndex = 0;
                    }
                    if (requestHandler.registerClient(clientConnection))
                    {
                    if (requestHandler
                        .registerClient(clientConnection)) {
                      logConnect(clientConnection);
                    }
                    else
                    {
                    } else {
                      iterator.remove();
                      continue;
                    }
                  }
                  catch (Exception e)
                  {
                  } catch (Exception e) {
                    if (debugEnabled())
                    {
                      debugCaught(DebugLogLevel.ERROR, e);
                    }
                    int msgID =
                             MSGID_LDAP_CONNHANDLER_UNABLE_TO_REGISTER_CLIENT;
                    String message =
                                getMessage(msgID,
                                           clientConnection.getClientHostPort(),
                                           clientConnection.getServerHostPort(),
                                           stackTraceToSingleLineString(e));
                      MSGID_LDAP_CONNHANDLER_UNABLE_TO_REGISTER_CLIENT;
                    String message = getMessage(msgID,
                        clientConnection.getClientHostPort(),
                        clientConnection.getServerHostPort(),
                        stackTraceToSingleLineString(e));
                    logError(ErrorLogCategory.CONNECTION_HANDLING,
                             ErrorLogSeverity.SEVERE_ERROR, message, msgID);
                        ErrorLogSeverity.SEVERE_ERROR, message, msgID);
                    clientConnection.disconnect(DisconnectReason.SERVER_ERROR,
                                                sendRejectionNotice, message,
                                                msgID);
                    clientConnection.disconnect(
                        DisconnectReason.SERVER_ERROR, currentConfig
                            .isSendRejectionNotice(), message, msgID);
                    iterator.remove();
                    continue;
@@ -1581,89 +1029,83 @@
                iterator.remove();
              }
            }
            else
            {
              if (shutdownRequested)
              {
            } else {
              if (shutdownRequested) {
                cleanUpSelector();
                listening = false;
                enabled   = false;
                enabled = false;
                continue;
              }
            }
            lastIterationFailed = false;
          }
          catch (Exception e)
          {
          } catch (Exception e) {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, e);
            }
            logError(ErrorLogCategory.CONNECTION_HANDLING,
                     ErrorLogSeverity.SEVERE_WARNING,
                     MSGID_LDAP_CONNHANDLER_CANNOT_ACCEPT_CONNECTION,
                     configEntryDN, stackTraceToSingleLineString(e));
                ErrorLogSeverity.SEVERE_WARNING,
                MSGID_LDAP_CONNHANDLER_CANNOT_ACCEPT_CONNECTION,
                currentConfig.dn(), stackTraceToSingleLineString(e));
            if (lastIterationFailed)
            {
              // The last time through the accept loop we also encountered a
              // failure.  Rather than enter a potential infinite loop of
              // failures, disable this acceptor and log an error.
            if (lastIterationFailed) {
              // The last time through the accept loop we also
              // encountered a failure. Rather than enter a potential
              // infinite loop of failures, disable this acceptor and
              // log an error.
              int msgID = MSGID_LDAP_CONNHANDLER_CONSECUTIVE_ACCEPT_FAILURES;
              String message = getMessage(msgID, String.valueOf(configEntryDN),
                                          stackTraceToSingleLineString(e));
              String message = getMessage(msgID, String
                  .valueOf(currentConfig.dn()),
                  stackTraceToSingleLineString(e));
              logError(ErrorLogCategory.CONNECTION_HANDLING,
                       ErrorLogSeverity.FATAL_ERROR, message, msgID);
                  ErrorLogSeverity.FATAL_ERROR, message, msgID);
              DirectoryServer.sendAlertNotification(this,
                   ALERT_TYPE_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES,
                   msgID, message);
              DirectoryServer
                  .sendAlertNotification(
                      this,
                      ALERT_TYPE_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES,
                      msgID, message);
              enabled = false;
              try
              {
              try {
                cleanUpSelector();
              } catch (Exception e2) {
              }
              catch (Exception e2)
              {
              }
            }
            else
            {
            } else {
              lastIterationFailed = true;
            }
          }
        }
      }
      catch (Exception e)
      {
      } catch (Exception e) {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        // This is very bad because we failed outside the loop.  The only
        // thing we can do here is log a message, send an alert, and disable the
        // selector until an administrator can figure out what's going on.
        // This is very bad because we failed outside the loop. The
        // only thing we can do here is log a message, send an alert,
        // and disable the selector until an administrator can figure
        // out what's going on.
        int msgID = MSGID_LDAP_CONNHANDLER_UNCAUGHT_ERROR;
        String message = getMessage(msgID, String.valueOf(configEntryDN),
                                    stackTraceToSingleLineString(e));
        String message = getMessage(msgID, String
            .valueOf(currentConfig.dn()),
            stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONNECTION_HANDLING,
                 ErrorLogSeverity.SEVERE_ERROR, message, msgID);
            ErrorLogSeverity.SEVERE_ERROR, message, msgID);
        DirectoryServer.sendAlertNotification(this,
             ALERT_TYPE_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR, msgID, message);
            ALERT_TYPE_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR, msgID,
            message);
        try
        {
        try {
          cleanUpSelector();
        } catch (Exception e2) {}
        } catch (Exception e2) {
        }
        enabled = false;
      }
@@ -1673,2151 +1115,66 @@
  /**
   * Cleans up the contents of the selector, closing any server socket channels
   * that might be associated with it.  Any connections that might have been
   * established through those channels should not be impacted.
   * Appends a string representation of this connection handler to the
   * provided buffer.
   *
   * @param buffer
   *          The buffer to which the information should be appended.
   */
  private void cleanUpSelector()
  {
    try
    {
      Iterator<SelectionKey> iterator = selector.keys().iterator();
      while (iterator.hasNext())
      {
        SelectionKey key = iterator.next();
        try
        {
          key.cancel();
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
        }
        try
        {
          key.channel().close();
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
    }
  }
  /**
   * Indicates whether this connection handler should maintain usage statistics.
   *
   * @return  <CODE>true</CODE> if this connection handler should maintain usage
   *          statistics, or <CODE>false</CODE> if not.
   */
  public boolean keepStats()
  {
    return keepStats;
  }
  /**
   * Specifies whether this connection handler should maintain usage statistics.
   *
   * @param  keepStats  Specifies whether this connection handler should
   *                    maintain usage statistics.
   */
  public void setKeepStats(boolean keepStats)
  {
    this.keepStats = keepStats;
  }
  /**
   * Retrieves the set of statistics maintained by this connection handler.
   *
   * @return  The set of statistics maintained by this connection handler.
   */
  public LDAPStatistics getStatTracker()
  {
    return statTracker;
  }
  /**
   * Indicates whether this connection handler should allow interaction with
   * LDAPv2 clients.
   *
   * @return  <CODE>true</CODE> if LDAPv2 is allowed, or <CODE>false</CODE> if
   *          not.
   */
  public boolean allowLDAPv2()
  {
    return allowLDAPv2;
  }
  /**
   * Indicates whether this connection handler should allow the use of the
   * StartTLS extended operation.
   *
   * @return  <CODE>true</CODE> if StartTLS is allowed, or <CODE>false</CODE> if
   *          not.
   */
  public boolean allowStartTLS()
  {
    return allowStartTLS;
  }
  /**
   * Indicates whether this connection handler should use SSL to communicate
   * with clients.
   *
   * @return  {@code true} if this connection handler should use SSL to
   *          communicate with clients, or {@code false} if not.
   */
  public boolean useSSL()
  {
    return useSSL;
  }
  /**
   * Retrieves the SSL client authentication policy for this connection handler.
   *
   * @return  The SSL client authentication policy for this connection handler.
   */
  public SSLClientAuthPolicy getSSLClientAuthPolicy()
  {
    return sslClientAuthPolicy;
  }
  /**
   * Retrieves the set of enabled SSL protocols configured for this connection
   * handler.
   *
   * @return  The set of enabled SSL protocols configured for this connection
   *          handler.
   */
  public String[] getEnabledSSLProtocols()
  {
    return enabledSSLProtocols;
  }
  /**
   * Retrieves the set of enabled SSL cipher suites configured for this
   * connection handler.
   *
   * @return  The set of enabled SSL cipher suites configured for this
   *          connection handler.
   */
  public String[] getEnabledSSLCipherSuites()
  {
    return enabledSSLCipherSuites;
  }
  /**
   * Retrieves the DN of the key manager provider that should be used for
   * operations associated with this connection handler which need access to a
   * key manager.
   *
   * @return  The DN of the key manager provider that should be used for
   *          operations associated with this connection handler which need
   *          access to a key manager, or {@code null} if no key manager
   *          provider has been configured for this connection handler.
   */
  public DN getKeyManagerProviderDN()
  {
    return keyManagerProviderDN;
  }
  /**
   * Retrieves the DN of the trust manager provider that should be used for
   * operations associated with this connection handler which need access to a
   * trust manager.
   *
   * @return  The DN of the trust manager provider that should be used for
   *          operations associated with this connection handler which need
   *          access to a trust manager, or {@code null} if no trust manager
   *          provider has been configured for this connection handler.
   */
  public DN getTrustManagerProviderDN()
  {
    return trustManagerProviderDN;
  }
  /**
   * Retrieves the nickname of the server certificate that should be used in
   * conjunction with this LDAP connection handler.
   *
   * @return  The nickname of the server certificate that should be used in
   *          conjunction with this LDAP connection handler.
   */
  public String getSSLServerCertNickname()
  {
    return sslServerCertNickname;
  }
  /**
   * Retrieves the maximum ASN.1 element value length that will be allowed by
   * this connection handler.
   *
   * @return  The maximum ASN.1 element value length that will be allowed by
   *          this connection handler.
   */
  public int getMaxRequestSize()
  {
    return maxRequestSize;
  }
  /**
   * Retrieves the human-readable name for this shutdown listener.
   *
   * @return  The human-readable name for this shutdown listener.
   */
  public String getShutdownListenerName()
  {
    return handlerName;
  }
  /**
   * Indicates that the Directory Server has received a request to stop running
   * and that this shutdown listener should take any action necessary to prepare
   * for it.
   *
   * @param  reason  The human-readable reason for the shutdown.
   */
  public void processServerShutdown(String reason)
  {
    shutdownRequested = true;
    try
    {
      for (LDAPRequestHandler requestHandler : requestHandlers)
      {
        try
        {
          requestHandler.processServerShutdown(reason);
        } catch (Exception e) {}
      }
    } catch (Exception e) {}
  }
  /**
   * Retrieves the DN of the configuration entry with which this component is
   * associated.
   *
   * @return  The DN of the configuration entry with which this component is
   *          associated.
   */
  public DN getConfigurableComponentEntryDN()
  {
    return configEntryDN;
  }
  /**
   * Retrieves the set of configuration attributes that are associated with this
   * configurable component.
   *
   * @return  The set of configuration attributes that are associated with this
   *          configurable component.
   */
  public List<ConfigAttribute> getConfigurationAttributes()
  {
    LinkedList<ConfigAttribute> configAttrs = new LinkedList<ConfigAttribute>();
    int msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_LISTEN_ADDRESS;
    ArrayList<String> listenAddressStrings =
         new ArrayList<String>(listenAddresses.size());
    for (InetAddress a : listenAddresses)
    {
      listenAddressStrings.add(a.getHostAddress());
    }
    configAttrs.add(new StringConfigAttribute(ATTR_LISTEN_ADDRESS,
                                              getMessage(msgID), true, true,
                                              true, listenAddressStrings));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_LISTEN_PORT;
    configAttrs.add(new IntegerConfigAttribute(ATTR_LISTEN_PORT,
                                               getMessage(msgID), true, false,
                                               true, true, 1, true, 65535,
                                               listenPort));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_BACKLOG;
    configAttrs.add(new IntegerConfigAttribute(ATTR_ACCEPT_BACKLOG,
                                               getMessage(msgID), true, false,
                                               true, true, 1, true,
                                               Integer.MAX_VALUE, backlog));
    if (allowedClients == null)
    {
      msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOWED_CLIENTS;
      ArrayList<String> allowedMasks = new ArrayList<String>(0);
      configAttrs.add(new StringConfigAttribute(ATTR_ALLOWED_CLIENT,
                                                getMessage(msgID), false, true,
                                                false, allowedMasks));
    }
    else
    {
      msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOWED_CLIENTS;
      ArrayList<String> allowedMasks =
           new ArrayList<String>(allowedClients.length);
      for (AddressMask m : allowedClients)
      {
        allowedMasks.add(m.toString());
      }
      configAttrs.add(new StringConfigAttribute(ATTR_ALLOWED_CLIENT,
                                                getMessage(msgID), false, true,
                                                false, allowedMasks));
    }
    if (deniedClients == null)
    {
      msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_DENIED_CLIENTS;
      ArrayList<String> deniedMasks = new ArrayList<String>(0);
      configAttrs.add(new StringConfigAttribute(ATTR_DENIED_CLIENT,
                                                getMessage(msgID), false, true,
                                                false, deniedMasks));
    }
    else
    {
      msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_DENIED_CLIENTS;
      ArrayList<String> deniedMasks =
           new ArrayList<String>(deniedClients.length);
      for (AddressMask m : deniedClients)
      {
        deniedMasks.add(m.toString());
      }
      configAttrs.add(new StringConfigAttribute(ATTR_DENIED_CLIENT,
                                                getMessage(msgID), false, true,
                                                false, deniedMasks));
    }
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOW_LDAPV2;
    configAttrs.add(new BooleanConfigAttribute(ATTR_ALLOW_LDAPV2,
                                               getMessage(msgID), false,
                                               allowLDAPv2));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_KEEP_STATS;
    configAttrs.add(new BooleanConfigAttribute(ATTR_KEEP_LDAP_STATS,
                                               getMessage(msgID), false,
                                               keepStats));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_NUM_REQUEST_HANDLERS;
    configAttrs.add(new IntegerConfigAttribute(ATTR_NUM_REQUEST_HANDLERS,
                                               getMessage(msgID), true, false,
                                               true, true, 1, false, 0,
                                               numRequestHandlers));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SEND_REJECTION_NOTICE;
    configAttrs.add(new BooleanConfigAttribute(ATTR_SEND_REJECTION_NOTICE,
                                               getMessage(msgID), false,
                                               sendRejectionNotice));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_USE_TCP_KEEPALIVE;
    configAttrs.add(new BooleanConfigAttribute(ATTR_USE_TCP_KEEPALIVE,
                                               getMessage(msgID), false,
                                               useKeepAlive));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_USE_TCP_NODELAY;
    configAttrs.add(new BooleanConfigAttribute(ATTR_USE_TCP_NODELAY,
                                               getMessage(msgID), false,
                                               useTCPNoDelay));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOW_REUSE_ADDRESS;
    configAttrs.add(new BooleanConfigAttribute(ATTR_ALLOW_REUSE_ADDRESS,
                                               getMessage(msgID), true,
                                               allowReuseAddress));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_MAX_REQUEST_SIZE;
    configAttrs.add(new IntegerWithUnitConfigAttribute(ATTR_MAX_REQUEST_SIZE,
                                                       getMessage(msgID), false,
                                                       SIZE_UNITS, true, 0,
                                                       true,
                                                       MAX_REQUEST_SIZE_LIMIT,
                                                       maxRequestSize,
                                                       SIZE_UNIT_BYTES_FULL));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_USE_SSL;
    configAttrs.add(new BooleanConfigAttribute(ATTR_USE_SSL, getMessage(msgID),
                                               true, useSSL));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOW_STARTTLS;
    configAttrs.add(new BooleanConfigAttribute(ATTR_ALLOW_STARTTLS,
                                               getMessage(msgID), false,
                                               allowStartTLS));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SSL_CLIENT_AUTH_POLICY;
    HashSet<String> allowedValues = new HashSet<String>(3);
    allowedValues.add(toLowerCase(SSLClientAuthPolicy.DISABLED.toString()));
    allowedValues.add(toLowerCase(SSLClientAuthPolicy.OPTIONAL.toString()));
    allowedValues.add(toLowerCase(SSLClientAuthPolicy.REQUIRED.toString()));
    configAttrs.add(new MultiChoiceConfigAttribute(ATTR_SSL_CLIENT_AUTH_POLICY,
                             getMessage(msgID), false, false, true,
                             allowedValues, sslClientAuthPolicy.toString()));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SSL_CERT_NICKNAME;
    configAttrs.add(new StringConfigAttribute(ATTR_SSL_CERT_NICKNAME,
                                              getMessage(msgID), false, false,
                                              true, sslServerCertNickname));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SSL_ENABLED_PROTOCOLS;
    configAttrs.add(new StringConfigAttribute(ATTR_SSL_PROTOCOLS,
                             getMessage(msgID), false, true, false,
                             arrayToList(enabledSSLProtocols)));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SSL_ENABLED_CIPHERS;
    configAttrs.add(new StringConfigAttribute(ATTR_SSL_CIPHERS,
                             getMessage(msgID), false, true, false,
                             arrayToList(enabledSSLCipherSuites)));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_KEYMANAGER_DN;
    configAttrs.add(new DNConfigAttribute(ATTR_KEYMANAGER_DN, getMessage(msgID),
                                          false, false, false,
                                          keyManagerProviderDN));
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_TRUSTMANAGER_DN;
    configAttrs.add(new DNConfigAttribute(ATTR_TRUSTMANAGER_DN,
                                          getMessage(msgID), false, false,
                                          false, trustManagerProviderDN));
    return configAttrs;
  }
  /**
   * Indicates whether the provided configuration entry has an acceptable
   * configuration for this component.  If it does not, then detailed
   * information about the problem(s) should be added to the provided list.
   *
   * @param  configEntry          The configuration entry for which to make the
   *                              determination.
   * @param  unacceptableReasons  A list that can be used to hold messages about
   *                              why the provided entry does not have an
   *                              acceptable configuration.
   *
   * @return  <CODE>true</CODE> if the provided entry has an acceptable
   *          configuration for this component, or <CODE>false</CODE> if not.
   */
  public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
                                            List<String> unacceptableReasons)
  {
    boolean configValid = true;
    // Determine the set of addresses on which to listen.  There can be
    // multiple addresses specified.
    int msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_LISTEN_ADDRESS;
    StringConfigAttribute addrStub =
         new StringConfigAttribute(ATTR_LISTEN_ADDRESS, getMessage(msgID),
                                   true, true, false);
    try
    {
      StringConfigAttribute addrAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(addrStub);
      if ((addrAttr == null) || addrAttr.activeValues().isEmpty())
      {
        // This is fine -- we'll just listen on all IPv4 addresses.
      }
      else
      {
        for (String s : addrAttr.activeValues())
        {
          try
          {
            InetAddress.getByName(s);
          }
          catch (UnknownHostException uhe)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, uhe);
            }
            msgID = MSGID_LDAP_CONNHANDLER_UNKNOWN_LISTEN_ADDRESS;
            unacceptableReasons.add(getMessage(msgID, s,
                                         stackTraceToSingleLineString(uhe)));
            configValid = false;
          }
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_LISTEN_ADDRESS;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine the port on which to listen.  There may only be a single port
    // specified.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_LISTEN_PORT;
    IntegerConfigAttribute portStub =
         new IntegerConfigAttribute(ATTR_LISTEN_PORT, getMessage(msgID), true,
                                    false, false, true, 1, true, 65535);
    try
    {
      IntegerConfigAttribute portAttr =
           (IntegerConfigAttribute) configEntry.getConfigAttribute(portStub);
      if (portAttr == null)
      {
        msgID = MSGID_LDAP_CONNHANDLER_NO_LISTEN_PORT;
        unacceptableReasons.add(getMessage(msgID,
                                           String.valueOf(configEntryDN)));
        configValid = false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_LISTEN_PORT;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine the accept backlog to use.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_BACKLOG;
    IntegerConfigAttribute backlogStub =
         new IntegerConfigAttribute(ATTR_ACCEPT_BACKLOG, getMessage(msgID),
                                    true, false, true, true, 1, true,
                                    Integer.MAX_VALUE);
    try
    {
      IntegerConfigAttribute backlogAttr =
           (IntegerConfigAttribute) configEntry.getConfigAttribute(backlogStub);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_BACKLOG;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine the set of allowed clients.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOWED_CLIENTS;
    StringConfigAttribute allowedStub =
         new StringConfigAttribute(ATTR_ALLOWED_CLIENT, getMessage(msgID),
                                   false, true, false);
    try
    {
      StringConfigAttribute allowedAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(allowedStub);
      if (allowedAttr != null)
      {
        for (String s : allowedAttr.activeValues())
        {
          try
          {
            AddressMask.decode(s);
          }
          catch (ConfigException ce)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, ce);
            }
            msgID = MSGID_LDAP_CONNHANDLER_INVALID_ADDRESS_MASK;
            unacceptableReasons.add(getMessage(msgID, s, ATTR_ALLOWED_CLIENT,
                                         String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(ce)));
            configValid = false;
          }
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_ALLOWED_CLIENTS;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine the set of denied clients.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_DENIED_CLIENTS;
    StringConfigAttribute deniedStub =
         new StringConfigAttribute(ATTR_DENIED_CLIENT, getMessage(msgID),
                                   false, true, false);
    try
    {
      StringConfigAttribute deniedAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(deniedStub);
      if (deniedAttr != null)
      {
        for (String s : deniedAttr.activeValues())
        {
          try
          {
            AddressMask.decode(s);
          }
          catch (ConfigException ce)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, ce);
            }
            msgID = MSGID_LDAP_CONNHANDLER_INVALID_ADDRESS_MASK;
            unacceptableReasons.add(getMessage(msgID, s, ATTR_DENIED_CLIENT,
                                         String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(ce)));
            configValid = false;
          }
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_DENIED_CLIENTS;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine whether to allow LDAPv2 clients.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOW_LDAPV2;
    BooleanConfigAttribute allowLDAPv2Stub =
         new BooleanConfigAttribute(ATTR_ALLOW_LDAPV2, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute allowLDAPv2Attr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(allowLDAPv2Stub);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_ALLOW_LDAPV2;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine whether to keep LDAP statistics.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_KEEP_STATS;
    BooleanConfigAttribute keepStatsStub =
         new BooleanConfigAttribute(ATTR_KEEP_LDAP_STATS, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute keepStatsAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(keepStatsStub);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_KEEP_STATS;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine the number of request handlers to maintain.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_NUM_REQUEST_HANDLERS;
    IntegerConfigAttribute reqHandlerStub =
         new IntegerConfigAttribute(ATTR_NUM_REQUEST_HANDLERS,
                                    getMessage(msgID), true, false, true,
                                    true, 1, false, 0);
    try
    {
      IntegerConfigAttribute reqHandlerAttr =
           (IntegerConfigAttribute)
           configEntry.getConfigAttribute(reqHandlerStub);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_NUM_REQUEST_HANDLERS;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine whether to send a notice to clients on rejection.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SEND_REJECTION_NOTICE;
    BooleanConfigAttribute notifyRejectsStub =
         new BooleanConfigAttribute(ATTR_SEND_REJECTION_NOTICE,
                                    getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute notifyRejectsAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(notifyRejectsStub);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_SEND_REJECTION_NOTICE;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine whether to use TCP keepalive.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_USE_TCP_KEEPALIVE;
    BooleanConfigAttribute keepAliveStub =
         new BooleanConfigAttribute(ATTR_USE_TCP_KEEPALIVE, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute keepAliveAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(keepAliveStub);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_USE_TCP_KEEPALIVE;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine whether to use TCP nodelay.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_USE_TCP_NODELAY;
    BooleanConfigAttribute noDelayStub =
         new BooleanConfigAttribute(ATTR_USE_TCP_NODELAY, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute noDelayAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(noDelayStub);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_USE_TCP_NODELAY;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine whether to allow reuse of address/port combinations.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOW_REUSE_ADDRESS;
    BooleanConfigAttribute reuseAddrStub =
         new BooleanConfigAttribute(ATTR_ALLOW_REUSE_ADDRESS,
                                    getMessage(msgID), true);
    try
    {
      BooleanConfigAttribute reuseAddrAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(reuseAddrStub);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_ALLOW_REUSE_ADDRESS;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine the maximum allowed request size.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_MAX_REQUEST_SIZE;
    IntegerWithUnitConfigAttribute maxReqSizeStub =
         new IntegerWithUnitConfigAttribute(ATTR_MAX_REQUEST_SIZE,
                                            getMessage(msgID), false,
                                            SIZE_UNITS, true, 0, true,
                                            MAX_REQUEST_SIZE_LIMIT);
    try
    {
      IntegerWithUnitConfigAttribute maxReqSizeAttr =
           (IntegerWithUnitConfigAttribute)
           configEntry.getConfigAttribute(maxReqSizeStub);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_MAX_REQUEST_SIZE;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine whether to use SSL.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_USE_SSL;
    boolean tmpUseSSL;
    BooleanConfigAttribute useSSLStub =
         new BooleanConfigAttribute(ATTR_USE_SSL, getMessage(msgID), true);
    try
    {
      BooleanConfigAttribute useSSLAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(useSSLStub);
      if (useSSLAttr == null)
      {
        tmpUseSSL = DEFAULT_USE_SSL;
      }
      else
      {
        tmpUseSSL = useSSLAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_USE_SSL;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
      tmpUseSSL   = DEFAULT_USE_SSL;
    }
    // Determine whether to allow the StartTLS extended operation.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOW_STARTTLS;
    BooleanConfigAttribute startTLSStub =
         new BooleanConfigAttribute(ATTR_ALLOW_STARTTLS, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute startTLSAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(startTLSStub);
      boolean tmpAllowStartTLS;
      if (startTLSAttr == null)
      {
        // This is fine -- we'll just use the default.
        tmpAllowStartTLS = DEFAULT_ALLOW_STARTTLS;
      }
      else
      {
        tmpAllowStartTLS = startTLSAttr.activeValue();
      }
      // See if both SSL and startTLS are configured.  If so, we'll have to
      // disable startTLS because they can't both be used concurrently.
      if (tmpUseSSL && tmpAllowStartTLS)
      {
        msgID = MSGID_LDAP_CONNHANDLER_CANNOT_HAVE_SSL_AND_STARTTLS;
        unacceptableReasons.add(getMessage(msgID,
                                           String.valueOf(configEntryDN)));
        configValid = false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_ALLOW_STARTTLS;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine how to handle SSL client authentication.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SSL_CLIENT_AUTH_POLICY;
    HashSet<String> allowedValues = new HashSet<String>(3);
    allowedValues.add(toLowerCase(SSLClientAuthPolicy.DISABLED.toString()));
    allowedValues.add(toLowerCase(SSLClientAuthPolicy.OPTIONAL.toString()));
    allowedValues.add(toLowerCase(SSLClientAuthPolicy.REQUIRED.toString()));
    MultiChoiceConfigAttribute sslAuthPolicyStub =
         new MultiChoiceConfigAttribute(ATTR_SSL_CLIENT_AUTH_POLICY,
                                        getMessage(msgID), false, false, true,
                                        allowedValues);
    try
    {
      MultiChoiceConfigAttribute sslAuthPolicyAttr =
           (MultiChoiceConfigAttribute)
           configEntry.getConfigAttribute(sslAuthPolicyStub);
      if (sslAuthPolicyAttr == null)
      {
        // This is fine -- We'll just use the default.
      }
      else
      {
        SSLClientAuthPolicy tmpPolicy = SSLClientAuthPolicy.policyForName(
                                             sslAuthPolicyAttr.activeValue());
        if (tmpPolicy == null)
        {
          msgID = MSGID_LDAP_CONNHANDLER_INVALID_SSL_CLIENT_AUTH_POLICY;
          unacceptableReasons.add(getMessage(msgID,
                                             sslAuthPolicyAttr.activeValue(),
                                             String.valueOf(configEntryDN)));
          configValid = false;
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_SSL_CLIENT_AUTH_POLICY;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine which SSL certificate to use.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SSL_CERT_NICKNAME;
    StringConfigAttribute certNameStub =
         new StringConfigAttribute(ATTR_SSL_CERT_NICKNAME, getMessage(msgID),
                                   false, false, true);
    try
    {
      StringConfigAttribute certNameAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(certNameStub);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_SSL_CERT_NICKNAME;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine the set of SSL protocols to allow.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SSL_ENABLED_PROTOCOLS;
    StringConfigAttribute sslProtocolsStub =
         new StringConfigAttribute(ATTR_SSL_PROTOCOLS, getMessage(msgID),
                                   false, true, false);
    try
    {
      StringConfigAttribute sslProtocolsAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(sslProtocolsStub);
      // FIXME -- Is there a good way to determine the set of supported
      //          protocols to validate what the user has provided?
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_SSL_PROTOCOLS;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine the set of SSL cipher suites to allow.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SSL_ENABLED_CIPHERS;
    StringConfigAttribute sslCiphersStub =
         new StringConfigAttribute(ATTR_SSL_CIPHERS, getMessage(msgID),
                                   false, true, false);
    try
    {
      StringConfigAttribute sslCiphersAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(sslCiphersStub);
      // FIXME -- Is there a good way to determine the set of supported cipher
      //          suites to validate what the user has provided?
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_SSL_CIPHERS;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      configValid = false;
    }
    // Determine the key manager provider to use.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_KEYMANAGER_DN;
    DNConfigAttribute keyManagerStub =
         new DNConfigAttribute(ATTR_KEYMANAGER_DN, getMessage(msgID), false,
                               false, false);
    try
    {
      DNConfigAttribute keyManagerAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(keyManagerStub);
      if (keyManagerAttr != null)
      {
        DN keyManagerProviderDN = keyManagerAttr.activeValue();
        KeyManagerProvider provider =
             DirectoryServer.getKeyManagerProvider(keyManagerProviderDN);
        if (provider == null)
        {
          msgID = MSGID_LDAP_CONNHANDLER_INVALID_KEYMANAGER_DN;
          String message = getMessage(msgID, String.valueOf(configEntryDN),
                                      String.valueOf(keyManagerProviderDN));
          unacceptableReasons.add(message);
          configValid = false;
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_KEYMANAGER_DN;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      unacceptableReasons.add(message);
      configValid = false;
    }
    // Determine the trust manager provider to use.
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_TRUSTMANAGER_DN;
    DNConfigAttribute trustManagerStub =
         new DNConfigAttribute(ATTR_TRUSTMANAGER_DN, getMessage(msgID), false,
                               false, false);
    try
    {
      DNConfigAttribute trustManagerAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(trustManagerStub);
      if (trustManagerAttr != null)
      {
        DN trustManagerProviderDN = trustManagerAttr.activeValue();
        TrustManagerProvider provider =
             DirectoryServer.getTrustManagerProvider(trustManagerProviderDN);
        if (provider == null)
        {
          msgID = MSGID_LDAP_CONNHANDLER_INVALID_TRUSTMANAGER_DN;
          String message = getMessage(msgID, String.valueOf(configEntryDN),
                                      String.valueOf(trustManagerProviderDN));
          unacceptableReasons.add(message);
          configValid = false;
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_TRUSTMANAGER_DN;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      unacceptableReasons.add(message);
      configValid = false;
    }
    return configValid;
  }
  /**
   * Makes a best-effort attempt to apply the configuration contained in the
   * provided entry.  Information about the result of this processing should be
   * added to the provided message list.  Information should always be added to
   * this list if a configuration change could not be applied.  If detailed
   * results are requested, then information about the changes applied
   * successfully (and optionally about parameters that were not changed) should
   * also be included.
   *
   * @param  configEntry      The entry containing the new configuration to
   *                          apply for this component.
   * @param  detailedResults  Indicates whether detailed information about the
   *                          processing should be added to the list.
   *
   * @return  Information about the result of the configuration update.
   */
  public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
                                                  boolean detailedResults)
  {
    // Create variables to include in the response.
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
    // The set of addresses and the port on which to listen are not dynamically
    // reconfigurable, so we can skip them.
    // The backlog is not dynamically reconfigurable, so we can skip it.
    // Determine the set of allowed clients.
    HashSet<AddressMask> newAllowedClients = null;
    int msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOWED_CLIENTS;
    StringConfigAttribute allowedStub =
         new StringConfigAttribute(ATTR_ALLOWED_CLIENT, getMessage(msgID),
                                   false, true, false);
    try
    {
      StringConfigAttribute allowedAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(allowedStub);
      if (allowedAttr != null)
      {
        newAllowedClients = new HashSet<AddressMask>();
        for (String s : allowedAttr.pendingValues())
        {
          try
          {
            newAllowedClients.add(AddressMask.decode(s));
          }
          catch (ConfigException ce)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, ce);
            }
            msgID = MSGID_LDAP_CONNHANDLER_INVALID_ADDRESS_MASK;
            messages.add(getMessage(msgID, s, ATTR_ALLOWED_CLIENT,
                                    String.valueOf(configEntryDN),
                                    stackTraceToSingleLineString(ce)));
            if (resultCode == ResultCode.SUCCESS)
            {
              resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
            }
          }
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_ALLOWED_CLIENTS;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    boolean allowedClientsChanged = false;
    if (resultCode == ResultCode.SUCCESS)
    {
      if ((allowedClients == null) || (allowedClients.length == 0))
      {
        allowedClientsChanged = (! ((newAllowedClients == null) ||
                                    newAllowedClients.isEmpty()));
      }
      else if ((newAllowedClients == null) || newAllowedClients.isEmpty())
      {
        allowedClientsChanged = true;
      }
      else if (allowedClients.length != newAllowedClients.size())
      {
        allowedClientsChanged = true;
      }
      else
      {
        for (AddressMask m : allowedClients)
        {
          if (! newAllowedClients.contains(m))
          {
            allowedClientsChanged = true;
            break;
          }
        }
      }
    }
    // Determine the set of denied clients.
    HashSet<AddressMask> newDeniedClients = null;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_DENIED_CLIENTS;
    StringConfigAttribute deniedStub =
         new StringConfigAttribute(ATTR_DENIED_CLIENT, getMessage(msgID),
                                   false, true, false);
    try
    {
      StringConfigAttribute deniedAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(deniedStub);
      if (deniedAttr != null)
      {
        newDeniedClients = new HashSet<AddressMask>();
        for (String s : deniedAttr.pendingValues())
        {
          try
          {
            newDeniedClients.add(AddressMask.decode(s));
          }
          catch (ConfigException ce)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, ce);
            }
            msgID = MSGID_LDAP_CONNHANDLER_INVALID_ADDRESS_MASK;
            messages.add(getMessage(msgID, s, ATTR_DENIED_CLIENT,
                                    String.valueOf(configEntryDN),
                                    stackTraceToSingleLineString(ce)));
            if (resultCode == ResultCode.SUCCESS)
            {
              resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
            }
          }
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_DENIED_CLIENTS;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    boolean deniedClientsChanged = false;
    if (resultCode == ResultCode.SUCCESS)
    {
      if ((deniedClients == null) || (deniedClients.length == 0))
      {
        deniedClientsChanged = (! ((newDeniedClients == null) ||
                                    newDeniedClients.isEmpty()));
      }
      else if ((newDeniedClients == null) || newDeniedClients.isEmpty())
      {
        deniedClientsChanged = true;
      }
      else if (deniedClients.length != newDeniedClients.size())
      {
        deniedClientsChanged = true;
      }
      else
      {
        for (AddressMask m : deniedClients)
        {
          if (! newDeniedClients.contains(m))
          {
            deniedClientsChanged = true;
            break;
          }
        }
      }
    }
    // Determine whether to allow LDAPv2 clients.
    boolean newAllowLDAPv2;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOW_LDAPV2;
    BooleanConfigAttribute allowLDAPv2Stub =
         new BooleanConfigAttribute(ATTR_ALLOW_LDAPV2, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute allowLDAPv2Attr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(allowLDAPv2Stub);
      if (allowLDAPv2Attr == null)
      {
        newAllowLDAPv2 = DEFAULT_ALLOW_LDAPV2;
      }
      else
      {
        newAllowLDAPv2 = allowLDAPv2Attr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_ALLOW_LDAPV2;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      newAllowLDAPv2 = DEFAULT_ALLOW_LDAPV2;
    }
    // Determine whether to keep LDAP statistics.
    boolean newKeepStats;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_KEEP_STATS;
    BooleanConfigAttribute keepStatsStub =
         new BooleanConfigAttribute(ATTR_KEEP_LDAP_STATS, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute keepStatsAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(keepStatsStub);
      if (keepStatsAttr == null)
      {
        newKeepStats = DEFAULT_KEEP_LDAP_STATS;
      }
      else
      {
        newKeepStats = keepStatsAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_KEEP_STATS;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      newKeepStats = DEFAULT_KEEP_LDAP_STATS;
    }
    // The number of request handlers to maintain is not dynamically
    // reconfigurable, so we can skip it.
    // Determine whether to send a notice to clients on rejection.
    boolean newSendRejectionNotice;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SEND_REJECTION_NOTICE;
    BooleanConfigAttribute notifyRejectsStub =
         new BooleanConfigAttribute(ATTR_SEND_REJECTION_NOTICE,
                                    getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute notifyRejectsAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(notifyRejectsStub);
      if (notifyRejectsAttr == null)
      {
        newSendRejectionNotice = DEFAULT_SEND_REJECTION_NOTICE;
      }
      else
      {
        newSendRejectionNotice = notifyRejectsAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_SEND_REJECTION_NOTICE;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      newSendRejectionNotice = DEFAULT_SEND_REJECTION_NOTICE;
    }
    // Determine whether to use TCP keepalive.
    boolean newUseKeepAlive;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_USE_TCP_KEEPALIVE;
    BooleanConfigAttribute keepAliveStub =
         new BooleanConfigAttribute(ATTR_USE_TCP_KEEPALIVE, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute keepAliveAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(keepAliveStub);
      if (keepAliveAttr == null)
      {
        newUseKeepAlive = DEFAULT_USE_TCP_KEEPALIVE;
      }
      else
      {
        newUseKeepAlive = keepAliveAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_USE_TCP_KEEPALIVE;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      newUseKeepAlive = DEFAULT_USE_TCP_KEEPALIVE;
    }
    // Determine whether to use TCP nodelay.
    boolean newUseTCPNoDelay;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_USE_TCP_NODELAY;
    BooleanConfigAttribute noDelayStub =
         new BooleanConfigAttribute(ATTR_USE_TCP_NODELAY, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute noDelayAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(noDelayStub);
      if (noDelayAttr == null)
      {
        newUseTCPNoDelay = DEFAULT_USE_TCP_NODELAY;
      }
      else
      {
        newUseTCPNoDelay = noDelayAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_USE_TCP_NODELAY;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      newUseTCPNoDelay = DEFAULT_USE_TCP_NODELAY;
    }
    // The reuse address option isn't dynamically reconfigurable, so we can skip
    // it.
    // Determine the maximum allowed request size.
    int newMaxRequestSize;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_MAX_REQUEST_SIZE;
    IntegerWithUnitConfigAttribute maxReqSizeStub =
         new IntegerWithUnitConfigAttribute(ATTR_MAX_REQUEST_SIZE,
                                            getMessage(msgID), false,
                                            SIZE_UNITS, true, 0, true,
                                            MAX_REQUEST_SIZE_LIMIT);
    try
    {
      IntegerWithUnitConfigAttribute maxReqSizeAttr =
           (IntegerWithUnitConfigAttribute)
           configEntry.getConfigAttribute(maxReqSizeStub);
      if (maxReqSizeAttr == null)
      {
        newMaxRequestSize = DEFAULT_MAX_REQUEST_SIZE;
      }
      else
      {
        newMaxRequestSize = (int) maxReqSizeAttr.pendingCalculatedValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_MAX_REQUEST_SIZE;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      newMaxRequestSize = DEFAULT_MAX_REQUEST_SIZE;
    }
    // The flag specifying whether to use SSL is not dynamically reconfigurable,
    // so we can skip it.
    // Determine whether to allow the StartTLS extended operation.
    boolean newAllowStartTLS;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_ALLOW_STARTTLS;
    BooleanConfigAttribute startTLSStub =
         new BooleanConfigAttribute(ATTR_ALLOW_STARTTLS, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute startTLSAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(startTLSStub);
      if (startTLSAttr == null)
      {
        // This is fine -- we'll just use the default.
        newAllowStartTLS = DEFAULT_ALLOW_STARTTLS;
      }
      else
      {
        newAllowStartTLS = startTLSAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_ALLOW_STARTTLS;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      newAllowStartTLS = DEFAULT_ALLOW_STARTTLS;
    }
    // The SSL client authentication policy and SSL certificate nickname are not
    // dynamically reconfigurable, so we can skip them.
    // Determine the set of SSL protocols to allow.
    String[] newSSLProtocols;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SSL_ENABLED_PROTOCOLS;
    StringConfigAttribute sslProtocolsStub =
         new StringConfigAttribute(ATTR_SSL_PROTOCOLS, getMessage(msgID), false,
                                   true, false);
    try
    {
      StringConfigAttribute sslProtocolsAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(sslProtocolsStub);
      if (sslProtocolsAttr == null)
      {
        newSSLProtocols = null;
      }
      else
      {
        // FIXME -- Is there a good way to validate the provided set of values?
        newSSLProtocols = listToArray(sslProtocolsAttr.pendingValues());
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_SSL_PROTOCOLS;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      newSSLProtocols = null;
    }
    // Determine the set of SSL cipher suites to allow.
    String[] newSSLCiphers;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_SSL_ENABLED_CIPHERS;
    StringConfigAttribute sslCiphersStub =
         new StringConfigAttribute(ATTR_SSL_CIPHERS, getMessage(msgID), false,
                                   true, false);
    try
    {
      StringConfigAttribute sslCiphersAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(sslCiphersStub);
      if (sslCiphersAttr == null)
      {
        newSSLCiphers = null;
      }
      else
      {
        // FIXME -- Is there a good way to validate the provided set of values?
        newSSLCiphers = listToArray(sslCiphersAttr.pendingValues());
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_SSL_CIPHERS;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      newSSLCiphers = null;
    }
    // Determine the key manager provider to use.
    DN newKeyManagerDN = null;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_KEYMANAGER_DN;
    DNConfigAttribute keyManagerStub =
         new DNConfigAttribute(ATTR_KEYMANAGER_DN, getMessage(msgID), false,
                               false, false);
    try
    {
      DNConfigAttribute keyManagerAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(keyManagerStub);
      if (keyManagerAttr != null)
      {
        newKeyManagerDN = keyManagerAttr.activeValue();
        KeyManagerProvider provider =
             DirectoryServer.getKeyManagerProvider(newKeyManagerDN);
        if (provider == null)
        {
          if (resultCode == ResultCode.SUCCESS)
          {
            resultCode = ResultCode.CONSTRAINT_VIOLATION;
          }
          msgID = MSGID_LDAP_CONNHANDLER_INVALID_KEYMANAGER_DN;
          messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                                  String.valueOf(newKeyManagerDN)));
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = ResultCode.CONSTRAINT_VIOLATION;
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_KEYMANAGER_DN;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
    }
    // Determine the trust manager provider to use.
    DN newTrustManagerDN = null;
    msgID = MSGID_LDAP_CONNHANDLER_DESCRIPTION_TRUSTMANAGER_DN;
    DNConfigAttribute trustManagerStub =
         new DNConfigAttribute(ATTR_TRUSTMANAGER_DN, getMessage(msgID), false,
                               false, false);
    try
    {
      DNConfigAttribute trustManagerAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(trustManagerStub);
      if (trustManagerAttr != null)
      {
        newTrustManagerDN = trustManagerAttr.activeValue();
        TrustManagerProvider provider =
             DirectoryServer.getTrustManagerProvider(newTrustManagerDN);
        if (provider == null)
        {
          if (resultCode == ResultCode.SUCCESS)
          {
            resultCode = ResultCode.CONSTRAINT_VIOLATION;
          }
          msgID = MSGID_LDAP_CONNHANDLER_INVALID_TRUSTMANAGER_DN;
          messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                                  String.valueOf(newTrustManagerDN)));
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = ResultCode.CONSTRAINT_VIOLATION;
      }
      msgID = MSGID_LDAP_CONNHANDLER_CANNOT_DETERMINE_TRUSTMANAGER_DN;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
    }
    // If the provided configuration is acceptable, then apply it.
    if (resultCode == ResultCode.SUCCESS)
    {
      if (allowedClientsChanged)
      {
        AddressMask[] newAllowedArray;
        if ((newAllowedClients == null) || newAllowedClients.isEmpty())
        {
          newAllowedArray = null;
        }
        else
        {
          newAllowedArray = new AddressMask[newAllowedClients.size()];
          newAllowedClients.toArray(newAllowedArray);
        }
        allowedClients = newAllowedArray;
        if (detailedResults)
        {
          messages.add(getMessage(MSGID_LDAP_CONNHANDLER_NEW_ALLOWED_CLIENTS,
                                  String.valueOf(configEntryDN)));
        }
      }
      if (deniedClientsChanged)
      {
        AddressMask[] newDeniedArray;
        if ((newDeniedClients == null) || newDeniedClients.isEmpty())
        {
          newDeniedArray = null;
        }
        else
        {
          newDeniedArray = new AddressMask[newDeniedClients.size()];
          newDeniedClients.toArray(newDeniedArray);
        }
        deniedClients = newDeniedArray;
        if (detailedResults)
        {
          messages.add(getMessage(MSGID_LDAP_CONNHANDLER_NEW_DENIED_CLIENTS,
                                  String.valueOf(configEntryDN)));
        }
      }
      if (allowLDAPv2 != newAllowLDAPv2)
      {
        allowLDAPv2 = newAllowLDAPv2;
        if (allowLDAPv2)
        {
          if (statTracker == null)
          {
            statTracker = new LDAPStatistics(handlerName + " Statistics");
          }
          else
          {
            statTracker.clearStatistics();
          }
        }
        if (detailedResults)
        {
          messages.add(getMessage(MSGID_LDAP_CONNHANDLER_NEW_ALLOW_LDAPV2,
                                  String.valueOf(newAllowLDAPv2),
                                  String.valueOf(configEntryDN)));
        }
      }
      if (keepStats != newKeepStats)
      {
        keepStats = newKeepStats;
        if (detailedResults)
        {
          messages.add(getMessage(MSGID_LDAP_CONNHANDLER_NEW_KEEP_STATS,
                                  String.valueOf(newKeepStats),
                                  String.valueOf(configEntryDN)));
        }
      }
      if (sendRejectionNotice != newSendRejectionNotice)
      {
        sendRejectionNotice = newSendRejectionNotice;
        if (detailedResults)
        {
          messages.add(getMessage(
                            MSGID_LDAP_CONNHANDLER_NEW_SEND_REJECTION_NOTICE,
                            String.valueOf(newSendRejectionNotice),
                            String.valueOf(configEntryDN)));
        }
      }
      if (useKeepAlive != newUseKeepAlive)
      {
        useKeepAlive = newUseKeepAlive;
        if (detailedResults)
        {
          messages.add(getMessage(MSGID_LDAP_CONNHANDLER_NEW_USE_KEEPALIVE,
                                  String.valueOf(newUseKeepAlive),
                                  String.valueOf(configEntryDN)));
        }
      }
      if (useTCPNoDelay != newUseTCPNoDelay)
      {
        useTCPNoDelay = newUseTCPNoDelay;
        if (detailedResults)
        {
          messages.add(getMessage(MSGID_LDAP_CONNHANDLER_NEW_USE_TCP_NODELAY,
                                  String.valueOf(newUseTCPNoDelay),
                                  String.valueOf(configEntryDN)));
        }
      }
      if (maxRequestSize != newMaxRequestSize)
      {
        maxRequestSize = newMaxRequestSize;
        if (detailedResults)
        {
          messages.add(getMessage(MSGID_LDAP_CONNHANDLER_NEW_MAX_REQUEST_SIZE,
                                  String.valueOf(newMaxRequestSize),
                                  String.valueOf(configEntryDN)));
        }
      }
      if (allowStartTLS != newAllowStartTLS)
      {
        allowStartTLS = newAllowStartTLS;
        if (detailedResults)
        {
          messages.add(getMessage(MSGID_LDAP_CONNHANDLER_NEW_ALLOW_STARTTLS,
                                  String.valueOf(newAllowStartTLS),
                                  String.valueOf(configEntryDN)));
        }
      }
      // Update enabled SSL protocols.
      if (! Arrays.equals(enabledSSLProtocols, newSSLProtocols))
      {
        enabledSSLProtocols = newSSLProtocols;
        if (detailedResults)
        {
          messages.add(getMessage(MSGID_LDAP_CONNHANDLER_NEW_SSL_PROTOCOLS,
                                  Arrays.toString(newSSLProtocols),
                                  String.valueOf(configEntryDN)));
        }
      }
      // Update enabled SSL cipher suites.
      if (! Arrays.equals(enabledSSLCipherSuites, newSSLCiphers))
      {
        enabledSSLCipherSuites = newSSLCiphers;
        if (detailedResults)
        {
          messages.add(getMessage(MSGID_LDAP_CONNHANDLER_NEW_SSL_CIPHERS,
                                  Arrays.toString(newSSLCiphers),
                                  String.valueOf(configEntryDN)));
        }
      }
      // Update the key manager provider DN.
      if (keyManagerProviderDN == null)
      {
        if (newKeyManagerDN != null)
        {
          keyManagerProviderDN = newKeyManagerDN;
          if (detailedResults)
          {
            messages.add(getMessage(MSGID_LDAP_CONNHANDLER_NEW_KEYMANAGER_DN,
                                    String.valueOf(newKeyManagerDN),
                                    String.valueOf(configEntryDN)));
          }
        }
      }
      else if ((newKeyManagerDN == null) ||
               (! keyManagerProviderDN.equals(newKeyManagerDN)))
      {
        keyManagerProviderDN = newKeyManagerDN;
        if (detailedResults)
        {
          messages.add(getMessage(MSGID_LDAP_CONNHANDLER_NEW_KEYMANAGER_DN,
                                  String.valueOf(newKeyManagerDN),
                                  String.valueOf(configEntryDN)));
        }
      }
      // Update the trust manager provider DN.
      if (trustManagerProviderDN == null)
      {
        if (newTrustManagerDN != null)
        {
          trustManagerProviderDN = newTrustManagerDN;
          if (detailedResults)
          {
            messages.add(getMessage(MSGID_LDAP_CONNHANDLER_NEW_TRUSTMANAGER_DN,
                                    String.valueOf(newTrustManagerDN),
                                    String.valueOf(configEntryDN)));
          }
        }
      }
      else if ((newTrustManagerDN == null) ||
               (! trustManagerProviderDN.equals(newTrustManagerDN)))
      {
        trustManagerProviderDN = newTrustManagerDN;
        if (detailedResults)
        {
          messages.add(getMessage(MSGID_LDAP_CONNHANDLER_NEW_TRUSTMANAGER_DN,
                                  String.valueOf(newTrustManagerDN),
                                  String.valueOf(configEntryDN)));
        }
      }
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * Appends a string representation of this connection handler to the provided
   * buffer.
   *
   * @param  buffer  The buffer to which the information should be appended.
   */
  public void toString(StringBuilder buffer)
  {
  public void toString(StringBuilder buffer) {
    buffer.append(handlerName);
  }
  /**
   * Retrieves the DN of the configuration entry with which this alert generator
   * is associated.
   * Indicates whether this connection handler should use SSL to
   * communicate with clients.
   *
   * @return  The DN of the configuration entry with which this alert generator
   *          is associated.
   * @return {@code true} if this connection handler should use SSL to
   *         communicate with clients, or {@code false} if not.
   */
  public DN getComponentEntryDN()
  {
    return configEntryDN;
  public boolean useSSL() {
    return currentConfig.isUseSSL();
  }
  /**
   * Retrieves the fully-qualified name of the Java class for this alert
   * generator implementation.
   *
   * @return  The fully-qualified name of the Java class for this alert
   *          generator implementation.
   * Cleans up the contents of the selector, closing any server socket
   * channels that might be associated with it. Any connections that
   * might have been established through those channels should not be
   * impacted.
   */
  public String getClassName()
  {
    return CLASS_NAME;
  }
  private void cleanUpSelector() {
    try {
      Iterator<SelectionKey> iterator = selector.keys().iterator();
      while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        try {
          key.cancel();
        } catch (Exception e) {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
        }
  /**
   * Retrieves information about the set of alerts that this generator may
   * produce.  The map returned should be between the notification type for a
   * particular notification and the human-readable description for that
   * notification.  This alert generator must not generate any alerts with types
   * that are not contained in this list.
   *
   * @return  Information about the set of alerts that this generator may
   *          produce.
   */
  public LinkedHashMap<String,String> getAlerts()
  {
    LinkedHashMap<String,String> alerts = new LinkedHashMap<String,String>();
    alerts.put(ALERT_TYPE_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES,
               ALERT_DESCRIPTION_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES);
    alerts.put(ALERT_TYPE_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR,
               ALERT_DESCRIPTION_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR);
    return alerts;
        try {
          key.channel().close();
        } catch (Exception e) {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
        }
      }
    } catch (Exception e) {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
    }
  }
}