Bryan Turner avatar Bryan Turner committed 0ab14c4

JOHNSON-8 Add JohnsonHttpRequestHandlerServlet.

- Updated all dependencies to the most current versions
- Implemented Johnson-aware HttpRequestHandlerServlet
- Bypasses initialisation during load if Spring initialisation has
been bypassed or failed
- Lazily inits if possible when trying to service a request and, if
initialisation fails, redirects to the configured Johnson page

Comments (0)

Files changed (3)

     <parent>
         <groupId>com.atlassian.pom</groupId>
         <artifactId>atlassian-public-pom</artifactId>
-        <version>29.4</version>
+        <version>29.7</version>
     </parent>
     
     <groupId>com.atlassian.johnson</groupId>
     </modules>
 
     <properties>
+        <encoding>UTF-8</encoding>
         <jdkLevel>1.6</jdkLevel>
         <requireJavaVersion>1.6.0</requireJavaVersion>
 
-        <slf4j.libversion>1.6.4</slf4j.libversion>
-        <spring.libversion>3.1.1.RELEASE</spring.libversion>
+        <slf4j.libversion>1.6.6</slf4j.libversion>
+        <spring.libversion>3.1.2.RELEASE</spring.libversion>
     </properties>
 
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>2.5</version>
+                    <configuration>
+                        <encoding>${encoding}</encoding>
+                        <source>${jdkLevel}</source>
+                        <target>${jdkLevel}</target>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
     <dependencyManagement>
         <dependencies>
             <dependency>
             <dependency>
                 <groupId>ch.qos.logback</groupId>
                 <artifactId>logback-classic</artifactId>
-                <version>1.0.0</version>
+                <version>1.0.7</version>
             </dependency>
             <dependency>
                 <groupId>com.google.guava</groupId>
             <dependency>
                 <groupId>org.easymock</groupId>
                 <artifactId>easymock</artifactId>
-                <version>3.0</version>
+                <version>3.1</version>
             </dependency>
             <dependency>
                 <groupId>org.slf4j</groupId>
             </dependency>
         </dependencies>
     </dependencyManagement>
-    
+
     <distributionManagement>
         <site>
             <id>atlassian-documentation</id>

spring/src/main/java/com/atlassian/johnson/spring/web/SpringEventType.java

         String value = config.getInitParameter(ADD_EVENT_ON_BYPASS_PARAM);
         if (value == null)
         {
-            //If parameter was found at the servlet level, look at the context level.
+            //If no parameter was found at the servlet level, look at the context level.
             return addEventOnBypass(config.getServletContext());
         }
         //If any value is found at the servlet level, be it true or false, that value always overrides any value set

spring/src/main/java/com/atlassian/johnson/spring/web/context/support/JohnsonHttpRequestHandlerServlet.java

+package com.atlassian.johnson.spring.web.context.support;
+
+import com.atlassian.johnson.Johnson;
+import com.atlassian.johnson.config.JohnsonConfig;
+import com.atlassian.johnson.event.Event;
+import com.atlassian.johnson.spring.web.context.JohnsonContextLoaderListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.support.HttpRequestHandlerServlet;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @since 2.1
+ */
+public class JohnsonHttpRequestHandlerServlet extends HttpRequestHandlerServlet
+{
+    private static final Logger LOG = LoggerFactory.getLogger(JohnsonHttpRequestHandlerServlet.class);
+
+    private final Object lock = new Object();
+
+    private volatile boolean uninitialised = true;
+
+    @Override
+    public void init() throws ServletException
+    {
+        //During startup, we'd like to initialise if possible because doing so here doesn't require locking. However,
+        //if the WebApplicationContext was bypassed, attempting to initialise here will fail and may make the entire
+        //server inaccessible. That would prevent accessing the Johnson page, so if the WebApplicationContext is not
+        //available we bypass initialisation here.
+        maybeInit();
+    }
+
+    @Override
+    protected void service(HttpServletRequest request, HttpServletResponse response)
+            throws ServletException, IOException
+    {
+        //When a request comes in, we're required to be initialised. If we're not, the only possible reason is that
+        //the application is Johnsoned. That suggests we will not be able to initialise here, but we still try, on
+        //the assumption that this URL was allowed through the Johnson filter and may, therefore, work. If so, the
+        //request will be processed normally.
+        if (uninitialised)
+        {
+            synchronized (lock)
+            {
+                if (uninitialised)
+                {
+                    if (!maybeInit())
+                    {
+                        //If we can't initialise at this point, redirect to the Johnson error page. The filter should
+                        //not have allowed access to this URL.
+                        ServletContext servletContext = getServletContext();
+                        JohnsonConfig config = Johnson.getConfig(servletContext);
+
+                        LOG.warn("HttpRequestHandlerServlet [{}] cannot be initialised to service an incoming " +
+                                "request; redirecting to {}", getServletName(), config.getErrorPath());
+                        response.sendRedirect(servletContext.getContextPath() + config.getErrorPath());
+
+                        return;
+                    }
+                }
+            }
+        }
+
+        super.service(request, response);
+    }
+
+    private boolean maybeInit() throws ServletException
+    {
+        ServletContext servletContext = getServletContext();
+
+        Object attribute = servletContext.getAttribute(JohnsonContextLoaderListener.BYPASSED_ATTRIBUTE);
+        //First, check to see if the WebApplicationContext was bypassed. If it was, it's possible, based on
+        //configuration, that no event was added. However, we must bypass handler initialisation as well,
+        //because no parent context will be available.
+        if (Boolean.TRUE == attribute) //Fully bypassed, without even trying to start
+        {
+            LOG.error("Bypassing HttpRequestHandlerServlet [{}] initialisation; Spring initialisation was bypassed",
+                    getServletName());
+            return false;
+        }
+        //If WebApplicationContext initialisation wasn't bypassed, check to see if it failed. Handler initialisation
+        //is guaranteed to fail if the primary WebApplicationContext failed, so we'll want to bypass it.
+        if (attribute instanceof Event)
+        {
+            Event event = (Event) attribute;
+
+            LOG.error("Bypassing HttpRequestHandlerServlet [{}] initialisation; Spring initialisation failed: {}",
+                    getServletName(), event.getDesc());
+            return false;
+        }
+
+        //If we make it here, the Spring WebApplicationContext should have started successfully. That means it's safe
+        //to try and start this handler.
+        try
+        {
+            super.init();
+
+            //No longer uninitialised, so future calls through this servlet should be serviced normally. Setting this
+            //optimises the locking out of the service(...) method.
+            uninitialised = false;
+        }
+        catch (Exception e)
+        {
+            LOG.error("HttpRequestHandlerServlet [" + getServletName() + "] could not be started", e);
+
+            return false;
+        }
+
+        return true;
+    }
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.