From 464f6dd52a1eeeef0f7b38d4dc1840501818a36f Mon Sep 17 00:00:00 2001
From: Yannick Lecaillez <yannick.lecaillez@forgerock.com>
Date: Mon, 07 Nov 2016 13:59:40 +0000
Subject: [PATCH] OPENDJ-3179: Migrate LDAP Connection Handler to SDK Grizzly transport

---
 opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java | 1742 ++++++++++++++++++++++++++-------------------------------
 1 files changed, 788 insertions(+), 954 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java b/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java
index 5e71d6c..753c8da 100644
--- a/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java
+++ b/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java
@@ -92,1025 +92,859 @@
 import com.forgerock.reactive.Stream;
 
 /**
- * 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.
+ * 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 final class LDAPConnectionHandler2 extends
-    ConnectionHandler<LDAPConnectionHandlerCfg> implements
-    ConfigurationChangeListener<LDAPConnectionHandlerCfg>,
-    ServerShutdownListener, AlertGenerator
-{
-  /** Task run periodically by the connection finalizer. */
-  private final class ConnectionFinalizerRunnable implements Runnable
-  {
+public final class LDAPConnectionHandler2 extends ConnectionHandler<LDAPConnectionHandlerCfg> implements
+        ConfigurationChangeListener<LDAPConnectionHandlerCfg>, ServerShutdownListener, AlertGenerator {
+    /** Task run periodically by the connection finalizer. */
+    private final class ConnectionFinalizerRunnable implements Runnable {
+        @Override
+        public void run() {
+            if (!connectionFinalizerActiveJobQueue.isEmpty()) {
+                for (Runnable r : connectionFinalizerActiveJobQueue) {
+                    r.run();
+                }
+                connectionFinalizerActiveJobQueue.clear();
+            }
+
+            // Switch the lists.
+            synchronized (connectionFinalizerLock) {
+                List<Runnable> tmp = connectionFinalizerActiveJobQueue;
+                connectionFinalizerActiveJobQueue = connectionFinalizerPendingJobQueue;
+                connectionFinalizerPendingJobQueue = tmp;
+            }
+        }
+    }
+
+    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
+
+    /** Default friendly name for the LDAP connection handler. */
+    private static final String DEFAULT_FRIENDLY_NAME = "LDAP Connection Handler";
+
+    /** SSL instance name used in context creation. */
+    private static final String SSL_CONTEXT_INSTANCE_NAME = "TLS";
+
+    private GrizzlyLDAPListener listener;
+
+    /** The current configuration state. */
+    private LDAPConnectionHandlerCfg currentConfig;
+
+    /* Properties that cannot be modified dynamically */
+
+    /** The set of addresses on which to listen for new connections. */
+    private Set<InetSocketAddress> listenAddresses;
+
+    /** The SSL client auth policy used by this connection handler. */
+    private SSLClientAuthPolicy sslClientAuthPolicy;
+
+    /** The backlog that will be used for the accept queue. */
+    private int backlog;
+
+    /** Indicates whether to allow the reuse address socket option. */
+    private boolean allowReuseAddress;
+
+    /** Indicates whether the Directory Server is in the process of shutting down. */
+    private volatile 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 Collection<AddressMask> allowedClients;
+
+    /** The set of clients that have been explicitly denied access to the server. */
+    private Collection<AddressMask> deniedClients;
+
+    /** The set of listeners for this connection handler. */
+    private List<HostPort> listeners;
+
+    /** The set of statistics collected for this connection handler. */
+    private LDAPStatistics statTracker;
+
+    /** The client connection monitor provider associated with this connection handler. */
+    private ClientConnectionMonitorProvider connMonitor;
+
+    /** The unique name assigned to this connection handler. */
+    private String handlerName;
+
+    /** The protocol used by this connection handler. */
+    private String protocol;
+
+    /** Queueing strategy. */
+    private final QueueingStrategy queueingStrategy;
+
+    /**
+     * The condition variable that will be used by the start method to wait for the socket port to be opened and ready
+     * to process requests before returning.
+     */
+    private final Object waitListen = new Object();
+
+    /** The friendly name of this connection handler. */
+    private String friendlyName;
+
+    /**
+     * SSL context.
+     *
+     * @see LDAPConnectionHandler2#sslEngine
+     */
+    private SSLContext sslContext;
+
+    /** The SSL engine is used for obtaining default SSL parameters. */
+    private SSLEngine sslEngine;
+
+    /**
+     * Connection finalizer thread.
+     * <p>
+     * This thread is defers closing clients for approximately 100ms. This gives the client a chance to close the
+     * connection themselves before the server thus avoiding leaving the server side in the TIME WAIT state.
+     */
+    private final Object connectionFinalizerLock = new Object();
+    private ScheduledExecutorService connectionFinalizer;
+    private List<Runnable> connectionFinalizerActiveJobQueue;
+    private List<Runnable> connectionFinalizerPendingJobQueue;
+
+    private final List<ClientConnection> connectionList = Collections
+            .synchronizedList(new ArrayList<ClientConnection>());
+
+    /**
+     * Creates a new instance of this LDAP connection handler. It must be initialized before it may be used.
+     */
+    public LDAPConnectionHandler2() {
+        this(new WorkQueueStrategy(), null); // Use name from configuration.
+    }
+
+    /**
+     * Creates a new instance of this LDAP connection handler, using a queueing strategy. It must be initialized before
+     * it may be used.
+     *
+     * @param strategy
+     *            Request handling strategy.
+     * @param friendlyName
+     *            The name of of this connection handler, or {@code null} if the name should be taken from the
+     *            configuration.
+     */
+    public LDAPConnectionHandler2(QueueingStrategy strategy, String friendlyName) {
+        super(friendlyName != null ? friendlyName : DEFAULT_FRIENDLY_NAME + " Thread");
+
+        this.friendlyName = friendlyName;
+        this.queueingStrategy = strategy;
+    }
+
+    /**
+     * 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 currentConfig.isAllowLDAPV2();
+    }
+
+    /**
+     * 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 currentConfig.isAllowStartTLS() && !currentConfig.isUseSSL();
+    }
+
     @Override
-    public void run()
-    {
-      if (!connectionFinalizerActiveJobQueue.isEmpty())
-      {
-        for (Runnable r : connectionFinalizerActiveJobQueue)
-        {
-          r.run();
+    public ConfigChangeResult applyConfigurationChange(LDAPConnectionHandlerCfg config) {
+        final ConfigChangeResult ccr = new ConfigChangeResult();
+
+        // 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
+
+        // Clear the stat tracker if LDAPv2 is being enabled.
+        if (currentConfig.isAllowLDAPV2() != config.isAllowLDAPV2() && config.isAllowLDAPV2()) {
+            statTracker.clearStatistics();
         }
-        connectionFinalizerActiveJobQueue.clear();
-      }
 
-      // Switch the lists.
-      synchronized (connectionFinalizerLock)
-      {
-        List<Runnable> tmp = connectionFinalizerActiveJobQueue;
-        connectionFinalizerActiveJobQueue = connectionFinalizerPendingJobQueue;
-        connectionFinalizerPendingJobQueue = tmp;
-      }
-    }
-  }
-  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
+        // Apply the changes.
+        currentConfig = config;
+        enabled = config.isEnabled();
+        allowedClients = config.getAllowedClient();
+        deniedClients = config.getDeniedClient();
 
-  /** Default friendly name for the LDAP connection handler. */
-  private static final String DEFAULT_FRIENDLY_NAME = "LDAP Connection Handler";
+        // Reconfigure SSL if needed.
+        try {
+            configureSSL(config);
+        } catch (DirectoryException e) {
+            logger.traceException(e);
+            ccr.setResultCode(e.getResultCode());
+            ccr.addMessage(e.getMessageObject());
+            return ccr;
+        }
 
-  /** SSL instance name used in context creation. */
-  private static final String SSL_CONTEXT_INSTANCE_NAME = "TLS";
+        if (config.isAllowLDAPV2()) {
+            DirectoryServer.registerSupportedLDAPVersion(2, this);
+        } else {
+            DirectoryServer.deregisterSupportedLDAPVersion(2, this);
+        }
 
-  private GrizzlyLDAPListener listener;
-
-  /** The current configuration state. */
-  private LDAPConnectionHandlerCfg currentConfig;
-
-  /* Properties that cannot be modified dynamically */
-
-  /** The set of addresses on which to listen for new connections. */
-  private Set<InetSocketAddress> listenAddresses;
-
-  /** The SSL client auth policy used by this connection handler. */
-  private SSLClientAuthPolicy sslClientAuthPolicy;
-
-  /** The backlog that will be used for the accept queue. */
-  private int backlog;
-
-  /** Indicates whether to allow the reuse address socket option. */
-  private boolean allowReuseAddress;
-
-  /** Indicates whether the Directory Server is in the process of shutting down. */
-  private volatile 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 Collection<AddressMask> allowedClients;
-
-  /** The set of clients that have been explicitly denied access to the server. */
-  private Collection<AddressMask> deniedClients;
-
-  /** The set of listeners for this connection handler. */
-  private List<HostPort> listeners;
-
-  /** The set of statistics collected for this connection handler. */
-  private LDAPStatistics statTracker;
-
-  /** The client connection monitor provider associated with this connection handler. */
-  private ClientConnectionMonitorProvider connMonitor;
-
-  /** The unique name assigned to this connection handler. */
-  private String handlerName;
-
-  /** The protocol used by this connection handler. */
-  private String protocol;
-
-  /** Queueing strategy. */
-  private final QueueingStrategy queueingStrategy;
-
-  /**
-   * The condition variable that will be used by the start method to wait for
-   * the socket port to be opened and ready to process requests before
-   * returning.
-   */
-  private final Object waitListen = new Object();
-
-  /** The friendly name of this connection handler. */
-  private String friendlyName;
-
-  /**
-   * SSL context.
-   *
-   * @see LDAPConnectionHandler2#sslEngine
-   */
-  private SSLContext sslContext;
-
-  /** The SSL engine is used for obtaining default SSL parameters. */
-  private SSLEngine sslEngine;
-
-  /**
-   * Connection finalizer thread.
-   * <p>
-   * This thread is defers closing clients for approximately 100ms. This gives
-   * the client a chance to close the connection themselves before the server
-   * thus avoiding leaving the server side in the TIME WAIT state.
-   */
-  private final Object connectionFinalizerLock = new Object();
-  private ScheduledExecutorService connectionFinalizer;
-  private List<Runnable> connectionFinalizerActiveJobQueue;
-  private List<Runnable> connectionFinalizerPendingJobQueue;
-
-  private final List<ClientConnection> connectionList = Collections.synchronizedList(new ArrayList<ClientConnection>());
-
-  /**
-   * Creates a new instance of this LDAP connection handler. It must be
-   * initialized before it may be used.
-   */
-  public LDAPConnectionHandler2()
-  {
-    this(new WorkQueueStrategy(), null); // Use name from configuration.
-  }
-
-  /**
-   * Creates a new instance of this LDAP connection handler, using a queueing
-   * strategy. It must be initialized before it may be used.
-   *
-   * @param strategy
-   *          Request handling strategy.
-   * @param friendlyName
-   *          The name of of this connection handler, or {@code null} if the
-   *          name should be taken from the configuration.
-   */
-  public LDAPConnectionHandler2(QueueingStrategy strategy, String friendlyName)
-  {
-    super(friendlyName != null ? friendlyName : DEFAULT_FRIENDLY_NAME + " Thread");
-
-    this.friendlyName = friendlyName;
-    this.queueingStrategy = strategy;
-  }
-
-  /**
-   * 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 currentConfig.isAllowLDAPV2();
-  }
-
-  /**
-   * 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 currentConfig.isAllowStartTLS() && !currentConfig.isUseSSL();
-  }
-
-  @Override
-  public ConfigChangeResult applyConfigurationChange(
-      LDAPConnectionHandlerCfg config)
-  {
-    final ConfigChangeResult ccr = new ConfigChangeResult();
-
-    // 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
-
-    // Clear the stat tracker if LDAPv2 is being enabled.
-    if (currentConfig.isAllowLDAPV2() != config.isAllowLDAPV2()
-        && config.isAllowLDAPV2())
-    {
-      statTracker.clearStatistics();
+        return ccr;
     }
 
-    // Apply the changes.
-    currentConfig = config;
-    enabled = config.isEnabled();
-    allowedClients = config.getAllowedClient();
-    deniedClients = config.getDeniedClient();
-
-    // Reconfigure SSL if needed.
-    try
-    {
-      configureSSL(config);
-    }
-    catch (DirectoryException e)
-    {
-      logger.traceException(e);
-      ccr.setResultCode(e.getResultCode());
-      ccr.addMessage(e.getMessageObject());
-      return ccr;
+    private void configureSSL(LDAPConnectionHandlerCfg config) throws DirectoryException {
+        protocol = config.isUseSSL() ? "LDAPS" : "LDAP";
+        if (config.isUseSSL() || config.isAllowStartTLS()) {
+            sslContext = createSSLContext(config);
+            sslEngine = createSSLEngine(config, sslContext);
+        } else {
+            sslContext = null;
+            sslEngine = null;
+        }
     }
 
-    if (config.isAllowLDAPV2())
-    {
-      DirectoryServer.registerSupportedLDAPVersion(2, this);
-    }
-    else
-    {
-      DirectoryServer.deregisterSupportedLDAPVersion(2, this);
+    @Override
+    public void finalizeConnectionHandler(LocalizableMessage finalizeReason) {
+        shutdownRequested = true;
+        currentConfig.removeLDAPChangeListener(this);
+
+        if (connMonitor != null) {
+            DirectoryServer.deregisterMonitorProvider(connMonitor);
+        }
+
+        if (statTracker != null) {
+            DirectoryServer.deregisterMonitorProvider(statTracker);
+        }
+
+        DirectoryServer.deregisterSupportedLDAPVersion(2, this);
+        DirectoryServer.deregisterSupportedLDAPVersion(3, this);
+
+        // Shutdown the connection finalizer and ensure that any pending
+        // unclosed connections are closed.
+        synchronized (connectionFinalizerLock) {
+            connectionFinalizer.shutdown();
+            connectionFinalizer = null;
+
+            Runnable r = new ConnectionFinalizerRunnable();
+            r.run(); // Flush active queue.
+            r.run(); // Flush pending queue.
+        }
     }
 
-    return ccr;
-  }
+    /**
+     * 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.
+     */
+    @Override
+    public Map<String, String> getAlerts() {
+        Map<String, String> alerts = new LinkedHashMap<>();
 
-  private void configureSSL(LDAPConnectionHandlerCfg config)
-      throws DirectoryException
-  {
-    protocol = config.isUseSSL() ? "LDAPS" : "LDAP";
-    if (config.isUseSSL() || config.isAllowStartTLS())
-    {
-      sslContext = createSSLContext(config);
-      sslEngine = createSSLEngine(config, sslContext);
-    }
-    else
-    {
-      sslContext = null;
-      sslEngine = null;
-    }
-  }
+        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);
 
-  @Override
-  public void finalizeConnectionHandler(LocalizableMessage finalizeReason)
-  {
-    shutdownRequested = true;
-    currentConfig.removeLDAPChangeListener(this);
-
-    if (connMonitor != null)
-    {
-      DirectoryServer.deregisterMonitorProvider(connMonitor);
+        return alerts;
     }
 
-    if (statTracker != null)
-    {
-      DirectoryServer.deregisterMonitorProvider(statTracker);
+    /**
+     * 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.
+     */
+    @Override
+    public String getClassName() {
+        return LDAPConnectionHandler2.class.getName();
     }
 
-    DirectoryServer.deregisterSupportedLDAPVersion(2, this);
-    DirectoryServer.deregisterSupportedLDAPVersion(3, this);
-
-    // Shutdown the connection finalizer and ensure that any pending
-    // unclosed connections are closed.
-    synchronized (connectionFinalizerLock)
-    {
-      connectionFinalizer.shutdown();
-      connectionFinalizer = null;
-
-      Runnable r = new ConnectionFinalizerRunnable();
-      r.run(); // Flush active queue.
-      r.run(); // Flush pending queue.
-    }
-  }
-
-  /**
-   * 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.
-   */
-  @Override
-  public Map<String, String> getAlerts()
-  {
-    Map<String, String> alerts = new LinkedHashMap<>();
-
-    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.
-   */
-  @Override
-  public String getClassName()
-  {
-    return LDAPConnectionHandler2.class.getName();
-  }
-
-  /**
-   * 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.
-   */
-  @Override
-  public Collection<ClientConnection> getClientConnections()
-  {
-    return connectionList;
-  }
-
-  /**
-   * 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.
-   */
-  @Override
-  public DN getComponentEntryDN()
-  {
-    return currentConfig.dn();
-  }
-
-  @Override
-  public String getConnectionHandlerName()
-  {
-    return handlerName;
-  }
-
-  @Override
-  public Collection<String> getEnabledSSLCipherSuites()
-  {
-    final SSLEngine engine = sslEngine;
-    if (engine != null)
-    {
-      return Arrays.asList(engine.getEnabledCipherSuites());
-    }
-    return super.getEnabledSSLCipherSuites();
-  }
-
-  @Override
-  public Collection<String> getEnabledSSLProtocols()
-  {
-    final SSLEngine engine = sslEngine;
-    if (engine != null)
-    {
-      return Arrays.asList(engine.getEnabledProtocols());
-    }
-    return super.getEnabledSSLProtocols();
-  }
-
-  @Override
-  public Collection<HostPort> getListeners()
-  {
-    return listeners;
-  }
-
-  /**
-   * Retrieves the maximum length of time in milliseconds that attempts to write
-   * to LDAP client connections should be allowed to block.
-   *
-   * @return The maximum length of time in milliseconds that attempts to write
-   *         to LDAP client connections should be allowed to block, or zero if
-   *         there should not be any limit imposed.
-   */
-  public long getMaxBlockedWriteTimeLimit()
-  {
-    return currentConfig.getMaxBlockedWriteTimeLimit();
-  }
-
-  /**
-   * 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 (int) currentConfig.getMaxRequestSize();
-  }
-
-  /**
-   * Retrieves the size in bytes of the LDAP response message write buffer
-   * defined for this connection handler.
-   *
-   * @return The size in bytes of the LDAP response message write buffer.
-   */
-  public int getBufferSize()
-  {
-    return (int) currentConfig.getBufferSize();
-  }
-
-  @Override
-  public String getProtocol()
-  {
-    return protocol;
-  }
-
-  @Override
-  public String getShutdownListenerName()
-  {
-    return handlerName;
-  }
-
-  /**
-   * 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;
-  }
-
-  @Override
-  public void initializeConnectionHandler(ServerContext serverContext, LDAPConnectionHandlerCfg config)
-      throws ConfigException, InitializationException
-  {
-    if (friendlyName == null)
-    {
-      friendlyName = config.dn().rdn().getFirstAVA().getAttributeValue().toString();
+    /**
+     * 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.
+     */
+    @Override
+    public Collection<ClientConnection> getClientConnections() {
+        return connectionList;
     }
 
-    // Save this configuration for future reference.
-    currentConfig = config;
-    enabled = config.isEnabled();
-    allowedClients = config.getAllowedClient();
-    deniedClients = config.getDeniedClient();
-
-    // Configure SSL if needed.
-    try
-    {
-      // This call may disable the connector if wrong SSL settings
-      configureSSL(config);
-    }
-    catch (DirectoryException e)
-    {
-      logger.traceException(e);
-      throw new InitializationException(e.getMessageObject());
+    /**
+     * 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.
+     */
+    @Override
+    public DN getComponentEntryDN() {
+        return currentConfig.dn();
     }
 
-    // Save properties that cannot be dynamically modified.
-    allowReuseAddress = config.isAllowTCPReuseAddress();
-    backlog = config.getAcceptBacklog();
-    listenAddresses = new HashSet<>();
-    for(InetAddress addr :config.getListenAddress()) {
-      listenAddresses.add(new InetSocketAddress(addr, config.getListenPort()));
+    @Override
+    public String getConnectionHandlerName() {
+        return handlerName;
     }
 
-    // Construct a unique name for this connection handler, and put
-    // together the set of listeners.
-    listeners = new LinkedList<>();
-    StringBuilder nameBuffer = new StringBuilder();
-    nameBuffer.append(friendlyName);
-    for (InetSocketAddress a : listenAddresses)
-    {
-      listeners.add(new HostPort(a.getHostName(), a.getPort()));
-      nameBuffer.append(" ");
-      nameBuffer.append(a.getHostName());
-    }
-    nameBuffer.append(" port ");
-    nameBuffer.append(config.getListenPort());
-    handlerName = nameBuffer.toString();
-
-    // Attempt to bind to the listen port on all configured addresses to
-    // verify whether the connection handler will be able to start.
-    LocalizableMessage errorMessage =
-        checkAnyListenAddressInUse(config.getListenAddress(), config.getListenPort(),
-            allowReuseAddress, config.dn());
-    if (errorMessage != null)
-    {
-      logger.error(errorMessage);
-      throw new InitializationException(errorMessage);
+    @Override
+    public Collection<String> getEnabledSSLCipherSuites() {
+        final SSLEngine engine = sslEngine;
+        if (engine != null) {
+            return Arrays.asList(engine.getEnabledCipherSuites());
+        }
+        return super.getEnabledSSLCipherSuites();
     }
 
-    // Create a system property to store the LDAP(S) port the server is
-    // listening to. This information can be displayed with jinfo.
-    System.setProperty(protocol + "_port", String.valueOf(config.getListenPort()));
-
-    // Create and start a connection finalizer thread for this
-    // connection handler.
-    connectionFinalizer = Executors
-        .newSingleThreadScheduledExecutor(new DirectoryThread.Factory(
-            "LDAP Connection Finalizer for connection handler " + toString()));
-
-    connectionFinalizerActiveJobQueue = new ArrayList<>();
-    connectionFinalizerPendingJobQueue = new ArrayList<>();
-
-    connectionFinalizer.scheduleWithFixedDelay(
-        new ConnectionFinalizerRunnable(), 100, 100, TimeUnit.MILLISECONDS);
-
-    // Register the set of supported LDAP versions.
-    DirectoryServer.registerSupportedLDAPVersion(3, this);
-    if (config.isAllowLDAPV2())
-    {
-      DirectoryServer.registerSupportedLDAPVersion(2, this);
+    @Override
+    public Collection<String> getEnabledSSLProtocols() {
+        final SSLEngine engine = sslEngine;
+        if (engine != null) {
+            return Arrays.asList(engine.getEnabledProtocols());
+        }
+        return super.getEnabledSSLProtocols();
     }
 
-    // Create and register monitors.
-    statTracker = new LDAPStatistics(handlerName + " Statistics");
-    DirectoryServer.registerMonitorProvider(statTracker);
-
-    connMonitor = new ClientConnectionMonitorProvider(this);
-    DirectoryServer.registerMonitorProvider(connMonitor);
-
-    // Register this as a change listener.
-    config.addLDAPChangeListener(this);
-  }
-
-  @Override
-  public boolean isConfigurationAcceptable(ConnectionHandlerCfg configuration,
-      List<LocalizableMessage> unacceptableReasons)
-  {
-    LDAPConnectionHandlerCfg config = (LDAPConnectionHandlerCfg) configuration;
-
-    if (currentConfig == null
-        || (!currentConfig.isEnabled() && config.isEnabled()))
-    {
-      // Attempt to bind to the listen port on all configured addresses to
-      // verify whether the connection handler will be able to start.
-      LocalizableMessage errorMessage =
-          checkAnyListenAddressInUse(config.getListenAddress(), config
-              .getListenPort(), config.isAllowTCPReuseAddress(), config.dn());
-      if (errorMessage != null)
-      {
-        unacceptableReasons.add(errorMessage);
-        return false;
-      }
+    @Override
+    public Collection<HostPort> getListeners() {
+        return listeners;
     }
 
-    if (config.isEnabled()
+    /**
+     * Retrieves the maximum length of time in milliseconds that attempts to write to LDAP client connections should be
+     * allowed to block.
+     *
+     * @return The maximum length of time in milliseconds that attempts to write to LDAP client connections should be
+     *         allowed to block, or zero if there should not be any limit imposed.
+     */
+    public long getMaxBlockedWriteTimeLimit() {
+        return currentConfig.getMaxBlockedWriteTimeLimit();
+    }
+
+    /**
+     * 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 (int) currentConfig.getMaxRequestSize();
+    }
+
+    /**
+     * Retrieves the size in bytes of the LDAP response message write buffer defined for this connection handler.
+     *
+     * @return The size in bytes of the LDAP response message write buffer.
+     */
+    public int getBufferSize() {
+        return (int) currentConfig.getBufferSize();
+    }
+
+    @Override
+    public String getProtocol() {
+        return protocol;
+    }
+
+    @Override
+    public String getShutdownListenerName() {
+        return handlerName;
+    }
+
+    /**
+     * 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;
+    }
+
+    @Override
+    public void initializeConnectionHandler(ServerContext serverContext, LDAPConnectionHandlerCfg config)
+            throws ConfigException, InitializationException {
+        if (friendlyName == null) {
+            friendlyName = config.dn().rdn().getFirstAVA().getAttributeValue().toString();
+        }
+
+        // Save this configuration for future reference.
+        currentConfig = config;
+        enabled = config.isEnabled();
+        allowedClients = config.getAllowedClient();
+        deniedClients = config.getDeniedClient();
+
+        // Configure SSL if needed.
+        try {
+            // This call may disable the connector if wrong SSL settings
+            configureSSL(config);
+        } catch (DirectoryException e) {
+            logger.traceException(e);
+            throw new InitializationException(e.getMessageObject());
+        }
+
+        // Save properties that cannot be dynamically modified.
+        allowReuseAddress = config.isAllowTCPReuseAddress();
+        backlog = config.getAcceptBacklog();
+        listenAddresses = new HashSet<>();
+        for (InetAddress addr : config.getListenAddress()) {
+            listenAddresses.add(new InetSocketAddress(addr, config.getListenPort()));
+        }
+
+        // Construct a unique name for this connection handler, and put
+        // together the set of listeners.
+        listeners = new LinkedList<>();
+        StringBuilder nameBuffer = new StringBuilder();
+        nameBuffer.append(friendlyName);
+        for (InetSocketAddress a : listenAddresses) {
+            listeners.add(new HostPort(a.getHostName(), a.getPort()));
+            nameBuffer.append(" ");
+            nameBuffer.append(a.getHostName());
+        }
+        nameBuffer.append(" port ");
+        nameBuffer.append(config.getListenPort());
+        handlerName = nameBuffer.toString();
+
+        // Attempt to bind to the listen port on all configured addresses to
+        // verify whether the connection handler will be able to start.
+        LocalizableMessage errorMessage = checkAnyListenAddressInUse(config.getListenAddress(), config.getListenPort(),
+                allowReuseAddress, config.dn());
+        if (errorMessage != null) {
+            logger.error(errorMessage);
+            throw new InitializationException(errorMessage);
+        }
+
+        // Create a system property to store the LDAP(S) port the server is
+        // listening to. This information can be displayed with jinfo.
+        System.setProperty(protocol + "_port", String.valueOf(config.getListenPort()));
+
+        // Create and start a connection finalizer thread for this
+        // connection handler.
+        connectionFinalizer = Executors.newSingleThreadScheduledExecutor(new DirectoryThread.Factory(
+                "LDAP Connection Finalizer for connection handler " + toString()));
+
+        connectionFinalizerActiveJobQueue = new ArrayList<>();
+        connectionFinalizerPendingJobQueue = new ArrayList<>();
+
+        connectionFinalizer.scheduleWithFixedDelay(new ConnectionFinalizerRunnable(), 100, 100, TimeUnit.MILLISECONDS);
+
+        // Register the set of supported LDAP versions.
+        DirectoryServer.registerSupportedLDAPVersion(3, this);
+        if (config.isAllowLDAPV2()) {
+            DirectoryServer.registerSupportedLDAPVersion(2, this);
+        }
+
+        // Create and register monitors.
+        statTracker = new LDAPStatistics(handlerName + " Statistics");
+        DirectoryServer.registerMonitorProvider(statTracker);
+
+        connMonitor = new ClientConnectionMonitorProvider(this);
+        DirectoryServer.registerMonitorProvider(connMonitor);
+
+        // Register this as a change listener.
+        config.addLDAPChangeListener(this);
+    }
+
+    @Override
+    public boolean isConfigurationAcceptable(ConnectionHandlerCfg configuration,
+            List<LocalizableMessage> unacceptableReasons) {
+        LDAPConnectionHandlerCfg config = (LDAPConnectionHandlerCfg) configuration;
+
+        if (currentConfig == null || (!currentConfig.isEnabled() && config.isEnabled())) {
+            // Attempt to bind to the listen port on all configured addresses to
+            // verify whether the connection handler will be able to start.
+            LocalizableMessage errorMessage = checkAnyListenAddressInUse(config.getListenAddress(),
+                    config.getListenPort(), config.isAllowTCPReuseAddress(), config.dn());
+            if (errorMessage != null) {
+                unacceptableReasons.add(errorMessage);
+                return false;
+            }
+        }
+
+        if (config.isEnabled()
         // Check that the SSL configuration is valid.
-        && (config.isUseSSL() || config.isAllowStartTLS()))
-    {
-      try
-      {
-        createSSLEngine(config, createSSLContext(config));
-      }
-      catch (DirectoryException e)
-      {
-        logger.traceException(e);
+                && (config.isUseSSL() || config.isAllowStartTLS())) {
+            try {
+                createSSLEngine(config, createSSLContext(config));
+            } catch (DirectoryException e) {
+                logger.traceException(e);
 
-        unacceptableReasons.add(e.getMessageObject());
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-  /**
-   * Checks whether any listen address is in use for the given port. The check
-   * is performed by binding to each address and port.
-   *
-   * @param listenAddresses
-   *          the listen {@link InetAddress} to test
-   * @param listenPort
-   *          the listen port to test
-   * @param allowReuseAddress
-   *          whether addresses can be reused
-   * @param configEntryDN
-   *          the configuration entry DN
-   * @return an error message if at least one of the address is already in use,
-   *         null otherwise.
-   */
-  private LocalizableMessage checkAnyListenAddressInUse(
-      Collection<InetAddress> listenAddresses, int listenPort,
-      boolean allowReuseAddress, DN configEntryDN)
-  {
-    for (InetAddress a : listenAddresses)
-    {
-      try
-      {
-        if (StaticUtils.isAddressInUse(a, listenPort, allowReuseAddress))
-        {
-          throw new IOException(ERR_CONNHANDLER_ADDRESS_INUSE.get().toString());
+                unacceptableReasons.add(e.getMessageObject());
+                return false;
+            }
         }
-      }
-      catch (IOException e)
-      {
-        logger.traceException(e);
-        return ERR_CONNHANDLER_CANNOT_BIND.get("LDAP", configEntryDN, a.getHostAddress(), listenPort,
-            getExceptionMessage(e));
-      }
+
+        return true;
     }
-    return null;
-  }
 
-  @Override
-  public boolean isConfigurationChangeAcceptable(
-      LDAPConnectionHandlerCfg config, List<LocalizableMessage> unacceptableReasons)
-  {
-    return isConfigurationAcceptable(config, unacceptableReasons);
-  }
-
-  @Override
-  public void processServerShutdown(LocalizableMessage reason)
-  {
-    shutdownRequested = true;
-  }
-
-  void stopListener()
-  {
-    if (listener != null)
-    {
-      listener.close();
-      listener = null;
+    /**
+     * Checks whether any listen address is in use for the given port. The check is performed by binding to each address
+     * and port.
+     *
+     * @param listenAddresses
+     *            the listen {@link InetAddress} to test
+     * @param listenPort
+     *            the listen port to test
+     * @param allowReuseAddress
+     *            whether addresses can be reused
+     * @param configEntryDN
+     *            the configuration entry DN
+     * @return an error message if at least one of the address is already in use, null otherwise.
+     */
+    private LocalizableMessage checkAnyListenAddressInUse(Collection<InetAddress> listenAddresses, int listenPort,
+            boolean allowReuseAddress, DN configEntryDN) {
+        for (InetAddress a : listenAddresses) {
+            try {
+                if (StaticUtils.isAddressInUse(a, listenPort, allowReuseAddress)) {
+                    throw new IOException(ERR_CONNHANDLER_ADDRESS_INUSE.get().toString());
+                }
+            } catch (IOException e) {
+                logger.traceException(e);
+                return ERR_CONNHANDLER_CANNOT_BIND.get("LDAP", configEntryDN, a.getHostAddress(), listenPort,
+                        getExceptionMessage(e));
+            }
+        }
+        return null;
     }
-  }
 
-  private void startListener() throws IOException
-  {
-    listener = new GrizzlyLDAPListener(
-            listenAddresses,
-            Options.defaultOptions()
-                   .set(LDAPListener.CONNECT_MAX_BACKLOG, backlog)
-                   .set(LDAPListener.REQUEST_MAX_SIZE_IN_BYTES, (int) currentConfig.getMaxRequestSize()),
-                   new Function<LDAPClientContext,
-                                ReactiveHandler<LDAPClientContext,LdapRawMessage, Stream<Response>>,
-                                LdapException>() {
+    @Override
+    public boolean isConfigurationChangeAcceptable(LDAPConnectionHandlerCfg config,
+            List<LocalizableMessage> unacceptableReasons) {
+        return isConfigurationAcceptable(config, unacceptableReasons);
+    }
+
+    @Override
+    public void processServerShutdown(LocalizableMessage reason) {
+        shutdownRequested = true;
+    }
+
+    void stopListener() {
+        if (listener != null) {
+            listener.close();
+            listener = null;
+        }
+    }
+
+    private void startListener() throws IOException {
+        listener = new GrizzlyLDAPListener(
+                listenAddresses,
+                Options.defaultOptions().set(LDAPListener.CONNECT_MAX_BACKLOG, backlog)
+                        .set(LDAPListener.REQUEST_MAX_SIZE_IN_BYTES, (int) currentConfig.getMaxRequestSize()),
+                new Function<LDAPClientContext,
+                             ReactiveHandler<LDAPClientContext, LdapRawMessage, Stream<Response>>,
+                             LdapException>() {
                     @Override
-                    public ReactiveHandler<LDAPClientContext, LdapRawMessage, Stream<Response>>
-                        apply(LDAPClientContext clientContext) throws LdapException {
+                    public ReactiveHandler<LDAPClientContext, LdapRawMessage, Stream<Response>> apply(
+                            LDAPClientContext clientContext) throws LdapException {
                         final LDAPClientConnection2 conn = canAccept(clientContext);
                         return new ReactiveHandler<LDAPClientContext, LdapRawMessage, Stream<Response>>() {
                             @Override
-                            public Single<Stream<Response>> handle(LDAPClientContext context,
-                                    LdapRawMessage request) throws Exception {
+                            public Single<Stream<Response>> handle(LDAPClientContext context, LdapRawMessage request)
+                                    throws Exception {
                                 return conn.handle(queueingStrategy, request);
                             }
                         };
                     }
                 });
-  }
+    }
 
-  /**
-   * Operates in a loop, accepting new connections and ensuring that requests on
-   * those connections are handled properly.
-   */
-  @Override
-  public void run()
-  {
-    setName(handlerName);
-    boolean starting = true;
-    setName(handlerName);
+    /**
+     * Operates in a loop, accepting new connections and ensuring that requests on those connections are handled
+     * properly.
+     */
+    @Override
+    public void run() {
+        setName(handlerName);
+        boolean starting = true;
+        setName(handlerName);
 
-    boolean lastIterationFailed = false;
+        boolean lastIterationFailed = false;
 
-    while (!shutdownRequested)
-    {
-      // If this connection handler is not enabled, then just sleep for a bit and check again.
-      if (!this.enabled)
-      {
-        if (listener != null)
-        {
-          stopListener();
+        while (!shutdownRequested) {
+            // If this connection handler is not enabled, then just sleep for a bit and check again.
+            if (!this.enabled) {
+                if (listener != null) {
+                    stopListener();
+                }
+
+                if (starting) {
+                    // This may happen if there was an initialisation error which led to disable the connector.
+                    // The main thread is waiting for the connector to listen on its port, which will not occur yet,
+                    // so notify here to allow the server startup to complete.
+                    synchronized (waitListen) {
+                        starting = false;
+                        waitListen.notify();
+                    }
+                }
+
+                StaticUtils.sleep(1000);
+                continue;
+            }
+
+            if (listener != null) {
+                // If already listening, then sleep for a bit and check again.
+                StaticUtils.sleep(1000);
+                continue;
+            }
+
+            try {
+                // At this point, the connection Handler either started correctly or failed
+                // to start but the start process should be notified and resume its work in any cases.
+                synchronized (waitListen) {
+                    waitListen.notify();
+                }
+
+                // If we have gotten here, then we are about to start listening
+                // for the first time since startup or since we were previously disabled.
+                // Start the embedded HTTP server
+                startListener();
+                lastIterationFailed = false;
+            } catch (Exception e) {
+                // Clean up the messed up HTTP server
+                stopListener();
+
+                // Error + alert about the horked config
+                logger.traceException(e);
+                logger.error(ERR_CONNHANDLER_CANNOT_ACCEPT_CONNECTION, friendlyName, currentConfig.dn(),
+                        getExceptionMessage(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.
+                    LocalizableMessage message = ERR_CONNHANDLER_CONSECUTIVE_ACCEPT_FAILURES.get(friendlyName,
+                            currentConfig.dn(), stackTraceToSingleLineString(e));
+                    logger.error(message);
+
+                    DirectoryServer.sendAlertNotification(this,
+                            ALERT_TYPE_HTTP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES, message);
+                    this.enabled = false;
+                } else {
+                    lastIterationFailed = true;
+                }
+            }
         }
 
-        if (starting)
-        {
-          // This may happen if there was an initialisation error which led to disable the connector.
-          // The main thread is waiting for the connector to listen on its port, which will not occur yet,
-          // so notify here to allow the server startup to complete.
-          synchronized (waitListen)
-          {
-            starting = false;
-            waitListen.notify();
-          }
-        }
-
-        StaticUtils.sleep(1000);
-        continue;
-      }
-
-      if (listener != null)
-      {
-        // If already listening, then sleep for a bit and check again.
-        StaticUtils.sleep(1000);
-        continue;
-      }
-
-      try
-      {
-        // At this point, the connection Handler either started correctly or failed
-        // to start but the start process should be notified and resume its work in any cases.
-        synchronized (waitListen)
-        {
-          waitListen.notify();
-        }
-
-        // If we have gotten here, then we are about to start listening
-        // for the first time since startup or since we were previously disabled.
-        // Start the embedded HTTP server
-        startListener();
-        lastIterationFailed = false;
-      }
-      catch (Exception e)
-      {
-        // Clean up the messed up HTTP server
+        // Initiate shutdown
         stopListener();
+    }
 
-        // Error + alert about the horked config
-        logger.traceException(e);
-        logger.error(
-            ERR_CONNHANDLER_CANNOT_ACCEPT_CONNECTION, friendlyName, currentConfig.dn(), getExceptionMessage(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.
-          LocalizableMessage message = ERR_CONNHANDLER_CONSECUTIVE_ACCEPT_FAILURES.get(
-              friendlyName, currentConfig.dn(), stackTraceToSingleLineString(e));
-          logger.error(message);
-
-          DirectoryServer.sendAlertNotification(this, ALERT_TYPE_HTTP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES, message);
-          this.enabled = false;
+    private LDAPClientConnection2 canAccept(LDAPClientContext clientContext) throws LdapException {
+        // Check to see if the core server rejected the
+        // connection (e.g., already too many connections
+        // established).
+        final LDAPClientConnection2 clientConnection = new LDAPClientConnection2(this, clientContext, getProtocol(),
+                currentConfig.isKeepStats());
+        if (clientConnection.getConnectionID() < 0) {
+            clientConnection.disconnect(DisconnectReason.ADMIN_LIMIT_EXCEEDED, true,
+                    ERR_CONNHANDLER_REJECTED_BY_SERVER.get());
+            throw LdapException.newLdapException(ResultCode.ADMIN_LIMIT_EXCEEDED);
         }
-        else
-        {
-          lastIterationFailed = true;
+
+        InetAddress clientAddr = clientConnection.getRemoteAddress();
+        // Check to see if the client is on the denied list.
+        // If so, then reject it immediately.
+        if (!deniedClients.isEmpty() && AddressMask.matchesAny(deniedClients, clientAddr)) {
+            clientConnection.disconnect(
+                    DisconnectReason.CONNECTION_REJECTED,
+                    currentConfig.isSendRejectionNotice(),
+                    ERR_CONNHANDLER_DENIED_CLIENT.get(clientConnection.getClientHostPort(),
+                            clientConnection.getServerHostPort()));
+            throw LdapException.newLdapException(ResultCode.CONSTRAINT_VIOLATION);
         }
-      }
-    }
+        // 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.isEmpty() && !AddressMask.matchesAny(allowedClients, clientAddr)) {
+            clientConnection.disconnect(
+                    DisconnectReason.CONNECTION_REJECTED,
+                    currentConfig.isSendRejectionNotice(),
+                    ERR_CONNHANDLER_DISALLOWED_CLIENT.get(clientConnection.getClientHostPort(),
+                            clientConnection.getServerHostPort()));
+            throw LdapException.newLdapException(ResultCode.CONSTRAINT_VIOLATION);
+        }
 
-    // Initiate shutdown
-    stopListener();
-  }
-
-  private LDAPClientConnection2 canAccept(LDAPClientContext clientContext) throws LdapException
-  {
-    // Check to see if the core server rejected the
-    // connection (e.g., already too many connections
-    // established).
-    final LDAPClientConnection2 clientConnection =
-            new LDAPClientConnection2(this, clientContext, getProtocol(), currentConfig.isKeepStats());
-    if (clientConnection.getConnectionID() < 0)
-    {
-      clientConnection.disconnect(
-          DisconnectReason.ADMIN_LIMIT_EXCEEDED, true, ERR_CONNHANDLER_REJECTED_BY_SERVER.get());
-      throw LdapException.newLdapException(ResultCode.ADMIN_LIMIT_EXCEEDED);
-    }
-
-    InetAddress clientAddr = clientConnection.getRemoteAddress();
-    // Check to see if the client is on the denied list.
-    // If so, then reject it immediately.
-    if (!deniedClients.isEmpty()
-        && AddressMask.matchesAny(deniedClients, clientAddr))
-    {
-      clientConnection.disconnect(DisconnectReason.CONNECTION_REJECTED,
-          currentConfig.isSendRejectionNotice(), ERR_CONNHANDLER_DENIED_CLIENT
-              .get(clientConnection.getClientHostPort(), clientConnection
-                  .getServerHostPort()));
-      throw LdapException.newLdapException(ResultCode.CONSTRAINT_VIOLATION);
-    }
-    // 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.isEmpty()
-        && !AddressMask.matchesAny(allowedClients, clientAddr))
-    {
-      clientConnection.disconnect(DisconnectReason.CONNECTION_REJECTED,
-          currentConfig.isSendRejectionNotice(),
-          ERR_CONNHANDLER_DISALLOWED_CLIENT.get(clientConnection
-              .getClientHostPort(), clientConnection.getServerHostPort()));
-      throw LdapException.newLdapException(ResultCode.CONSTRAINT_VIOLATION);
-    }
-
-    // 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();
-      PluginResult.PostConnect pluginResult = pluginManager
-          .invokePostConnectPlugins(clientConnection);
-      if (!pluginResult.continueProcessing())
-      {
-        clientConnection.disconnect(pluginResult.getDisconnectReason(),
-            pluginResult.sendDisconnectNotification(),
-            pluginResult.getErrorMessage());
-        throw LdapException.newLdapException(ResultCode.CONSTRAINT_VIOLATION);
-      }
-    }
-    catch (Exception e)
-    {
-      logger.traceException(e);
-
-      LocalizableMessage message =
-          INFO_CONNHANDLER_UNABLE_TO_REGISTER_CLIENT.get(clientConnection
-              .getClientHostPort(), clientConnection.getServerHostPort(),
-              getExceptionMessage(e));
-      logger.debug(message);
-
-      clientConnection.disconnect(DisconnectReason.SERVER_ERROR,
-          currentConfig.isSendRejectionNotice(), message);
-      throw LdapException.newLdapException(ResultCode.OPERATIONS_ERROR);
-    }
-
-    if (useSSL()) {
+        // 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 {
-            clientContext.enableTLS(createSSLEngine());
-        } catch (DirectoryException e) {
-            throw LdapException.newLdapException(e.getResultCode(), e);
+            PluginConfigManager pluginManager = DirectoryServer.getPluginConfigManager();
+            PluginResult.PostConnect pluginResult = pluginManager.invokePostConnectPlugins(clientConnection);
+            if (!pluginResult.continueProcessing()) {
+                clientConnection.disconnect(pluginResult.getDisconnectReason(),
+                        pluginResult.sendDisconnectNotification(), pluginResult.getErrorMessage());
+                throw LdapException.newLdapException(ResultCode.CONSTRAINT_VIOLATION);
+            }
+        } catch (Exception e) {
+            logger.traceException(e);
+
+            LocalizableMessage message = INFO_CONNHANDLER_UNABLE_TO_REGISTER_CLIENT.get(
+                    clientConnection.getClientHostPort(), clientConnection.getServerHostPort(), getExceptionMessage(e));
+            logger.debug(message);
+
+            clientConnection.disconnect(DisconnectReason.SERVER_ERROR, currentConfig.isSendRejectionNotice(), message);
+            throw LdapException.newLdapException(ResultCode.OPERATIONS_ERROR);
+        }
+
+        if (useSSL()) {
+            try {
+                clientContext.enableTLS(createSSLEngine());
+            } catch (DirectoryException e) {
+                throw LdapException.newLdapException(e.getResultCode(), e);
+            }
+        }
+
+        return clientConnection;
+    }
+
+    /**
+     * Appends a string representation of this connection handler to the provided buffer.
+     *
+     * @param buffer
+     *            The buffer to which the information should be appended.
+     */
+    @Override
+    public void toString(StringBuilder buffer) {
+        buffer.append(handlerName);
+    }
+
+    /**
+     * 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 currentConfig.isUseSSL();
+    }
+
+    SSLEngine createSSLEngine() throws DirectoryException {
+        return createSSLEngine(currentConfig, sslContext);
+    }
+
+    private SSLEngine createSSLEngine(LDAPConnectionHandlerCfg config, SSLContext sslContext)
+            throws DirectoryException {
+        try {
+            SSLEngine sslEngine = sslContext.createSSLEngine();
+            sslEngine.setUseClientMode(false);
+
+            final Set<String> protocols = config.getSSLProtocol();
+            if (!protocols.isEmpty()) {
+                sslEngine.setEnabledProtocols(protocols.toArray(new String[0]));
+            }
+
+            final Set<String> ciphers = config.getSSLCipherSuite();
+            if (!ciphers.isEmpty()) {
+                sslEngine.setEnabledCipherSuites(ciphers.toArray(new String[0]));
+            }
+
+            switch (config.getSSLClientAuthPolicy()) {
+            case DISABLED:
+                sslEngine.setNeedClientAuth(false);
+                sslEngine.setWantClientAuth(false);
+                break;
+            case REQUIRED:
+                sslEngine.setWantClientAuth(true);
+                sslEngine.setNeedClientAuth(true);
+                break;
+            case OPTIONAL:
+            default:
+                sslEngine.setNeedClientAuth(false);
+                sslEngine.setWantClientAuth(true);
+                break;
+            }
+
+            return sslEngine;
+        } catch (Exception e) {
+            logger.traceException(e);
+            ResultCode resCode = DirectoryServer.getServerErrorResultCode();
+            LocalizableMessage message = ERR_CONNHANDLER_SSL_CANNOT_INITIALIZE.get(getExceptionMessage(e));
+            throw new DirectoryException(resCode, message, e);
         }
     }
 
-    return clientConnection;
-  }
-
-  /**
-   * Appends a string representation of this connection handler to the provided
-   * buffer.
-   *
-   * @param buffer
-   *          The buffer to which the information should be appended.
-   */
-  @Override
-  public void toString(StringBuilder buffer)
-  {
-    buffer.append(handlerName);
-  }
-
-  /**
-   * 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 currentConfig.isUseSSL();
-  }
-
-  SSLEngine createSSLEngine() throws DirectoryException {
-      return createSSLEngine(currentConfig, sslContext);
-  }
-
-  private SSLEngine createSSLEngine(LDAPConnectionHandlerCfg config, SSLContext sslContext) throws DirectoryException
-  {
-    try
-    {
-      SSLEngine sslEngine = sslContext.createSSLEngine();
-      sslEngine.setUseClientMode(false);
-
-      final Set<String> protocols = config.getSSLProtocol();
-      if (!protocols.isEmpty())
-      {
-        sslEngine.setEnabledProtocols(protocols.toArray(new String[0]));
-      }
-
-      final Set<String> ciphers = config.getSSLCipherSuite();
-      if (!ciphers.isEmpty())
-      {
-        sslEngine.setEnabledCipherSuites(ciphers.toArray(new String[0]));
-      }
-
-      switch (config.getSSLClientAuthPolicy())
-      {
-      case DISABLED:
-        sslEngine.setNeedClientAuth(false);
-        sslEngine.setWantClientAuth(false);
-        break;
-      case REQUIRED:
-        sslEngine.setWantClientAuth(true);
-        sslEngine.setNeedClientAuth(true);
-        break;
-      case OPTIONAL:
-      default:
-        sslEngine.setNeedClientAuth(false);
-        sslEngine.setWantClientAuth(true);
-        break;
-      }
-
-      return sslEngine;
-    }
-    catch (Exception e)
-    {
-      logger.traceException(e);
-      ResultCode resCode = DirectoryServer.getServerErrorResultCode();
-      LocalizableMessage message = ERR_CONNHANDLER_SSL_CANNOT_INITIALIZE
-          .get(getExceptionMessage(e));
-      throw new DirectoryException(resCode, message, e);
-    }
-  }
-
-  private void disableAndWarnIfUseSSL(LDAPConnectionHandlerCfg config)
-  {
-    if (config.isUseSSL())
-    {
-      logger.warn(INFO_DISABLE_CONNECTION, friendlyName);
-      enabled = false;
-    }
-  }
-
-  private SSLContext createSSLContext(LDAPConnectionHandlerCfg config)
-      throws DirectoryException
-  {
-    try
-    {
-      DN keyMgrDN = config.getKeyManagerProviderDN();
-      KeyManagerProvider<?> keyManagerProvider = DirectoryServer
-          .getKeyManagerProvider(keyMgrDN);
-      if (keyManagerProvider == null)
-      {
-        logger.error(ERR_NULL_KEY_PROVIDER_MANAGER, keyMgrDN, friendlyName);
-        disableAndWarnIfUseSSL(config);
-        keyManagerProvider = new NullKeyManagerProvider();
-        // The SSL connection is unusable without a key manager provider
-      }
-      else if (! keyManagerProvider.containsAtLeastOneKey())
-      {
-        logger.error(ERR_INVALID_KEYSTORE, friendlyName);
-        disableAndWarnIfUseSSL(config);
-      }
-
-      final SortedSet<String> aliases = new TreeSet<>(config.getSSLCertNickname());
-      final KeyManager[] keyManagers;
-      if (aliases.isEmpty())
-      {
-        keyManagers = keyManagerProvider.getKeyManagers();
-      }
-      else
-      {
-        final Iterator<String> it = aliases.iterator();
-        while (it.hasNext())
-        {
-          if (!keyManagerProvider.containsKeyWithAlias(it.next()))
-          {
-            logger.error(ERR_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, aliases, friendlyName);
-            it.remove();
-          }
+    private void disableAndWarnIfUseSSL(LDAPConnectionHandlerCfg config) {
+        if (config.isUseSSL()) {
+            logger.warn(INFO_DISABLE_CONNECTION, friendlyName);
+            enabled = false;
         }
+    }
 
-        if (aliases.isEmpty())
-        {
-          disableAndWarnIfUseSSL(config);
+    private SSLContext createSSLContext(LDAPConnectionHandlerCfg config) throws DirectoryException {
+        try {
+            DN keyMgrDN = config.getKeyManagerProviderDN();
+            KeyManagerProvider<?> keyManagerProvider = DirectoryServer.getKeyManagerProvider(keyMgrDN);
+            if (keyManagerProvider == null) {
+                logger.error(ERR_NULL_KEY_PROVIDER_MANAGER, keyMgrDN, friendlyName);
+                disableAndWarnIfUseSSL(config);
+                keyManagerProvider = new NullKeyManagerProvider();
+                // The SSL connection is unusable without a key manager provider
+            } else if (!keyManagerProvider.containsAtLeastOneKey()) {
+                logger.error(ERR_INVALID_KEYSTORE, friendlyName);
+                disableAndWarnIfUseSSL(config);
+            }
+
+            final SortedSet<String> aliases = new TreeSet<>(config.getSSLCertNickname());
+            final KeyManager[] keyManagers;
+            if (aliases.isEmpty()) {
+                keyManagers = keyManagerProvider.getKeyManagers();
+            } else {
+                final Iterator<String> it = aliases.iterator();
+                while (it.hasNext()) {
+                    if (!keyManagerProvider.containsKeyWithAlias(it.next())) {
+                        logger.error(ERR_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, aliases, friendlyName);
+                        it.remove();
+                    }
+                }
+
+                if (aliases.isEmpty()) {
+                    disableAndWarnIfUseSSL(config);
+                }
+                keyManagers = SelectableCertificateKeyManager.wrap(keyManagerProvider.getKeyManagers(), aliases,
+                        friendlyName);
+            }
+
+            DN trustMgrDN = config.getTrustManagerProviderDN();
+            TrustManagerProvider<?> trustManagerProvider = DirectoryServer.getTrustManagerProvider(trustMgrDN);
+            if (trustManagerProvider == null) {
+                trustManagerProvider = new NullTrustManagerProvider();
+            }
+
+            SSLContext sslContext = SSLContext.getInstance(SSL_CONTEXT_INSTANCE_NAME);
+            sslContext.init(keyManagers, trustManagerProvider.getTrustManagers(), null);
+            return sslContext;
+        } catch (Exception e) {
+            logger.traceException(e);
+            ResultCode resCode = DirectoryServer.getServerErrorResultCode();
+            LocalizableMessage message = ERR_CONNHANDLER_SSL_CANNOT_INITIALIZE.get(getExceptionMessage(e));
+            throw new DirectoryException(resCode, message, e);
         }
-        keyManagers = SelectableCertificateKeyManager.wrap(keyManagerProvider.getKeyManagers(), aliases, friendlyName);
-      }
-
-      DN trustMgrDN = config.getTrustManagerProviderDN();
-      TrustManagerProvider<?> trustManagerProvider = DirectoryServer
-          .getTrustManagerProvider(trustMgrDN);
-      if (trustManagerProvider == null)
-      {
-        trustManagerProvider = new NullTrustManagerProvider();
-      }
-
-      SSLContext sslContext = SSLContext.getInstance(SSL_CONTEXT_INSTANCE_NAME);
-      sslContext.init(keyManagers, trustManagerProvider.getTrustManagers(),
-          null);
-      return sslContext;
     }
-    catch (Exception e)
-    {
-      logger.traceException(e);
-      ResultCode resCode = DirectoryServer.getServerErrorResultCode();
-      LocalizableMessage message = ERR_CONNHANDLER_SSL_CANNOT_INITIALIZE
-          .get(getExceptionMessage(e));
-      throw new DirectoryException(resCode, message, e);
-    }
-  }
 
-  /**
-   * Enqueue a connection finalizer which will be invoked after a short delay.
-   *
-   * @param r
-   *          The connection finalizer runnable.
-   */
-  void registerConnectionFinalizer(Runnable r)
-  {
-    synchronized (connectionFinalizerLock)
-    {
-      if (connectionFinalizer != null)
-      {
-        connectionFinalizerPendingJobQueue.add(r);
-      }
-      else
-      {
-        // Already finalized - invoked immediately.
-        r.run();
-      }
+    /**
+     * Enqueue a connection finalizer which will be invoked after a short delay.
+     *
+     * @param r
+     *            The connection finalizer runnable.
+     */
+    void registerConnectionFinalizer(Runnable r) {
+        synchronized (connectionFinalizerLock) {
+            if (connectionFinalizer != null) {
+                connectionFinalizerPendingJobQueue.add(r);
+            } else {
+                // Already finalized - invoked immediately.
+                r.run();
+            }
+        }
     }
-  }
 }

--
Gitblit v1.10.0