From 90809948a377234acbeed1005733cea9b55208e0 Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Wed, 22 May 2013 10:33:52 +0000
Subject: [PATCH] OPENDJ-918 (CR-1723) - HTTP Connection Handler - Server cannot start with horked http-config.json

---
 opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java |  229 ++++++++++++++++++++++++++++++++++----------------------
 1 files changed, 138 insertions(+), 91 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java
index b6afd3b..73e412c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java
@@ -30,6 +30,7 @@
 import static org.opends.messages.ProtocolMessages.*;
 import static org.opends.server.loggers.ErrorLogger.*;
 import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.*;
 
 import java.io.File;
@@ -142,6 +143,9 @@
    */
   private volatile boolean shutdownRequested;
 
+  /** Indicates whether this connection handler is enabled. */
+  private boolean enabled;
+
   /** The set of listeners for this connection handler. */
   private List<HostPort> listeners = new LinkedList<HostPort>();
 
@@ -181,9 +185,7 @@
    */
   private final Object waitListen = new Object();
 
-  /**
-   * The friendly name of this connection handler.
-   */
+  /** The friendly name of this connection handler. */
   private String friendlyName;
 
   /**
@@ -279,6 +281,7 @@
 
     this.initConfig = config;
     this.currentConfig = config;
+    this.enabled = this.currentConfig.isEnabled();
 
     return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired,
         messages);
@@ -373,8 +376,12 @@
   @Override
   public LinkedHashMap<String, String> getAlerts()
   {
-    // TODO Auto-generated method stub
-    return null;
+    LinkedHashMap<String, String> alerts = new LinkedHashMap<String, String>();
+
+    alerts.put(ALERT_TYPE_HTTP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES,
+        ALERT_DESCRIPTION_HTTP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES);
+
+    return alerts;
   }
 
   /** {@inheritDoc} */
@@ -535,6 +542,7 @@
 
     this.initConfig = config;
     this.currentConfig = config;
+    this.enabled = this.currentConfig.isEnabled();
   }
 
   private String getHandlerName(HTTPConnectionHandlerCfg config)
@@ -558,8 +566,7 @@
   {
     HTTPConnectionHandlerCfg config = (HTTPConnectionHandlerCfg) configuration;
 
-    if ((currentConfig == null)
-        || (!currentConfig.isEnabled() && config.isEnabled()))
+    if ((currentConfig == null) || (!this.enabled && config.isEnabled()))
     {
       // Attempt to bind to the listen port on all configured addresses to
       // verify whether the connection handler will be able to start.
@@ -713,11 +720,12 @@
   {
     setName(handlerName);
 
+    boolean lastIterationFailed = false;
     while (!shutdownRequested)
     {
       // If this connection handler is not enabled, then just sleep
       // for a bit and check again.
-      if (!currentConfig.isEnabled())
+      if (!this.enabled)
       {
         if (isListening())
         {
@@ -735,17 +743,65 @@
         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. Start the embedded HTTP server
-      startHttpServer();
+      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
+        startHttpServer();
+        lastIterationFailed = false;
+      }
+      catch (Exception e)
+      {
+        // clean up the messed up HTTP server
+        cleanUpHttpServer();
+
+        // error + alert about the horked config
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+
+        logError(ERR_CONNHANDLER_CANNOT_ACCEPT_CONNECTION.get(friendlyName,
+            String.valueOf(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.
+          Message message =
+              ERR_CONNHANDLER_CONSECUTIVE_ACCEPT_FAILURES.get(friendlyName,
+                  String.valueOf(currentConfig.dn()),
+                  stackTraceToSingleLineString(e));
+          logError(message);
+
+          DirectoryServer.sendAlertNotification(this,
+              ALERT_TYPE_HTTP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES, message);
+
+          this.enabled = false;
+        }
+        else
+        {
+          lastIterationFailed = true;
+        }
+      }
     }
 
     // Initiate shutdown
     stopHttpServer();
   }
 
-  private void startHttpServer()
+  private void startHttpServer() throws Exception
   {
     // silence Grizzly's own logging
     Logger.getLogger("org.glassfish.grizzly").setLevel(Level.OFF);
@@ -768,84 +824,67 @@
       setHttpStatsProbe();
     }
 
-    try
+    for (NetworkListener listener : this.httpServer.getListeners())
     {
-      for (NetworkListener listener : this.httpServer.getListeners())
+      TCPNIOTransport transport = listener.getTransport();
+      transport.setReuseAddress(currentConfig.isAllowTCPReuseAddress());
+      transport.setKeepAlive(currentConfig.isUseTCPKeepAlive());
+      transport.setTcpNoDelay(currentConfig.isUseTCPNoDelay());
+      transport.setWriteTimeout(currentConfig.getMaxBlockedWriteTimeLimit(),
+          TimeUnit.MILLISECONDS);
+
+      int bufferSize = (int) currentConfig.getBufferSize();
+      transport.setReadBufferSize(bufferSize);
+      transport.setWriteBufferSize(bufferSize);
+      transport.setIOStrategy(SameThreadIOStrategy.getInstance());
+      final int numRequestHandlers =
+          getNumRequestHandlers(currentConfig.getNumRequestHandlers(),
+              friendlyName);
+      transport.setSelectorRunnersCount(numRequestHandlers);
+      transport.setServerConnectionBackLog(currentConfig.getAcceptBacklog());
+
+      if (sslContext != null)
       {
-        TCPNIOTransport transport = listener.getTransport();
-        transport.setReuseAddress(currentConfig.isAllowTCPReuseAddress());
-        transport.setKeepAlive(currentConfig.isUseTCPKeepAlive());
-        transport.setTcpNoDelay(currentConfig.isUseTCPNoDelay());
-        transport.setWriteTimeout(currentConfig.getMaxBlockedWriteTimeLimit(),
-            TimeUnit.MILLISECONDS);
-
-        int bufferSize = (int) currentConfig.getBufferSize();
-        transport.setReadBufferSize(bufferSize);
-        transport.setWriteBufferSize(bufferSize);
-        transport.setIOStrategy(SameThreadIOStrategy.getInstance());
-        final int numRequestHandlers = getNumRequestHandlers(
-                currentConfig.getNumRequestHandlers(), friendlyName);
-        transport.setSelectorRunnersCount(numRequestHandlers);
-        transport.setServerConnectionBackLog(currentConfig.getAcceptBacklog());
-
-        if (sslContext != null)
-        {
-          listener.setSecure(true);
-          listener.setSSLEngineConfig(new SSLEngineConfigurator(sslContext));
-        }
-      }
-
-      final String servletName = "OpenDJ Rest2LDAP servlet";
-      final String urlPattern = "/*";
-      final WebappContext ctx = new WebappContext(servletName);
-
-      final JsonValue configuration =
-          parseJsonConfiguration(getFileForPath(this.currentConfig
-              .getConfigFile()));
-
-      final HTTPAuthenticationConfig authenticationConfig =
-          getAuthenticationConfig(configuration);
-
-      javax.servlet.Filter filter =
-          new CollectClientConnectionsFilter(this, authenticationConfig);
-      FilterRegistration filterReg =
-          ctx.addFilter("collectClientConnections", filter);
-      // TODO JNR this is not working
-      // filterReg.addMappingForServletNames(EnumSet.allOf(
-      // DispatcherType.class), servletName);
-      filterReg.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),
-          true, urlPattern);
-
-      ConnectionFactory connFactory = getConnectionFactory(configuration);
-
-      final ServletRegistration reg =
-          ctx.addServlet(servletName, new HttpServlet(connFactory,
-          // Used for hooking our HTTPClientConnection in Rest2LDAP
-              Rest2LDAPContextFactory.getHttpServletContextFactory()));
-      reg.addMapping(urlPattern);
-
-      ctx.deploy(this.httpServer);
-
-      TRACER.debugInfo("Starting HTTP server...");
-      this.httpServer.start();
-      TRACER.debugInfo("HTTP server started");
-      logError(NOTE_CONNHANDLER_STARTED_LISTENING.get(handlerName));
-
-      // 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();
+        listener.setSecure(true);
+        listener.setSSLEngineConfig(new SSLEngineConfigurator(sslContext));
       }
     }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-    }
+
+    final String servletName = "OpenDJ Rest2LDAP servlet";
+    final String urlPattern = "/*";
+    final WebappContext ctx = new WebappContext(servletName);
+
+    final JsonValue configuration =
+        parseJsonConfiguration(getFileForPath(this.currentConfig
+            .getConfigFile()));
+
+    final HTTPAuthenticationConfig authenticationConfig =
+        getAuthenticationConfig(configuration);
+
+    javax.servlet.Filter filter =
+        new CollectClientConnectionsFilter(this, authenticationConfig);
+    FilterRegistration filterReg =
+        ctx.addFilter("collectClientConnections", filter);
+    // TODO JNR this is not working
+    // filterReg.addMappingForServletNames(EnumSet.allOf(
+    // DispatcherType.class), servletName);
+    filterReg.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),
+        true, urlPattern);
+
+    ConnectionFactory connFactory = getConnectionFactory(configuration);
+
+    final ServletRegistration reg =
+        ctx.addServlet(servletName, new HttpServlet(connFactory,
+        // Used for hooking our HTTPClientConnection in Rest2LDAP
+            Rest2LDAPContextFactory.getHttpServletContextFactory()));
+    reg.addMapping(urlPattern);
+
+    ctx.deploy(this.httpServer);
+
+    TRACER.debugInfo("Starting HTTP server...");
+    this.httpServer.start();
+    TRACER.debugInfo("HTTP server started");
+    logError(NOTE_CONNHANDLER_STARTED_LISTENING.get(handlerName));
   }
 
   private void setHttpStatsProbe()
@@ -926,12 +965,20 @@
 
   private void stopHttpServer()
   {
-    TRACER.debugInfo("Stopping HTTP server...");
-    this.httpServer.stop();
+    if (this.httpServer != null)
+    {
+      TRACER.debugInfo("Stopping HTTP server...");
+      this.httpServer.stop();
+      cleanUpHttpServer();
+      TRACER.debugInfo("HTTP server stopped");
+      logError(NOTE_CONNHANDLER_STOPPED_LISTENING.get(handlerName));
+    }
+  }
+
+  private void cleanUpHttpServer()
+  {
     this.httpServer = null;
     this.httpProbe = null;
-    TRACER.debugInfo("HTTP server stopped");
-    logError(NOTE_CONNHANDLER_STOPPED_LISTENING.get(handlerName));
   }
 
   /** {@inheritDoc} */

--
Gitblit v1.10.0