Jenkov.com
Tutorials Books About
Java Web Apps

1 Java Web Application Technologies
2 Java Web App Directory Layout
3 How to Take Down aJava Web App for Maintenance Without Going Offline




How to Take Down aJava Web App for Maintenance Without Going Offline


Once in a while you need to block the users of your Java web application so you can do maintenance on the system. Maintenance could be backing up databases, files, or other systems used by your web app, or it could be running some jobs on the database, or upgrading it. It general, I mean maintenance which require exclusive access (no users using them) while being executed.

Of course you don't want to shut down your entire web server during maintenance, nor even the web application. Instead you want users trying to access your web app to see a nice "This web app is down for maintenance..." page. Otherwise your users will not know what is going on. They may start to lose confidence in your system if it appears to be down too much, seemingly uncontrolled (without explanation). A "This web app is down for maintenance..." page lets them know that the down time is controlled and expected.

The solution presented in this text to the problem described above may not be the only possible solution, but it's simple, and it works.

The Web App Maintenance Mode

To block users during maintenance you must somehow be able to switch your web application from "normal operation" into "maintenance mode". This is how normal operation mode looks:

In the "normal operation" a request from the users browser is processed by the web application, and if the request processing needs database / other system access, the database / other system is accessed.

In this mode is it often not possible to do a consistent backup of your database, file system or other system. If a user request changes data in the database, how do you know if the backup you are currently executing will be consistent? With just a single database to backup, this may be less of a problem, but what if your web app uses both a database and a file system? Imagine if the user uploads a file during backup. You risk that the record inserted into the database pointing to the file is not included in the backup, but the file is. Or the other way around. That the record is inserted into the database allright, but the file is not yet uploaded at the time the file system is backed up. In both cases you have an inconsistent backup.

This problem does not only exist during backups. Exclusive access to the system being maintained may be necessary for other types of maintenance too. For instance, if you are upgrading a backend system, it is often not possible to have users using it, if the upgrade is to execute successfully and consistently.

To get the users to stay out of the backend systems you need to switch the web app into a "maintenance mode". If users try to access the web application they see a nice "This web app is down for maintenance" page. The maintenance mode is illustrated below.

The Maintenance Mode Servlet Filter

A very simple way to intercept all incoming requests and redirect them to the "maintenance mode" page, is to add a Servlet Filter to your web application. Configure the servlet filter to have all requests to the web app passing through it, before being forwarded to the web application.

My Java web ui "framework", Butterfly Web UI, comes with such a servlet filter, should you not care to implement your own (or copy the source from this page).

Here is how the web.xml configuration looks for such a setup:

<web-app>

    <filter>
        <filter-name>MaintenanceModeFilter</filter-name>
        <filter-class>com.jenkov.seo.web.MaintenanceModeFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>MaintenanceModeFilter</filter-name>
        <url-pattern>*</url-pattern>
    </filter-mapping>

 <!-- more configuration here... -->


</web-app>

Inside the Servlet Filter you need to check whether the filter is in normal operation mode, or maintenance mode. If the filter is in normal operation mode, the request is passed on to the web application as normal. If the filter is in maintenance mode the request should be passed on to a maintenance message page instead.

Switching the Filter into Maintenance Mode and Back

To be able to switch the maintenance filter into maintenance mode or back to normal operation mode, you need a mechanism of signaling the mode switch to the maintenance servlet filter.

An easy way of doing this is to add some request parameter to the end of a request, and have the filter look out for this special request parameter. In my solution I have chosen the parameter maintenance-mode (you may argue that the name operation-mode would be more correct, but it's just a name). So, request URL's that switches mode could look like this:

Maintenance Mode
http://tutorials.jenkov.com/anyPage.html?maintenance-mode=1

Normal Operation Mode    
http://tutorials.jenkov.com/anyPage.html?maintenance-mode=0

My servlet filter implementation doesn't look at the total URL, only the request parameter with the correct name (as shown in bold in the URL example).

Waiting for all Active Requests to Complete

Once you have switched the servlet filter into maintenance mode, you will have to wait for all currently processing requests started before the filter switched mode to complete. For instance, imagine that a user sends a request that takes 1 minute to process (e.g. uploading a file). Then, before that 1 minute has passed you switch the web application into maintenance mode, to do a backup of the database and uploaded files on the disk. For your backup to be consistent, you will have to wait until the file upload in progress is completed.

There are two ways of making sure that no long-running pre-maintenance-mode requests are executing:

  1. If you know the maximum possible request processing time, you can wait that amount of time.
  2. Have the servlet filter count all active requests, and wait until that number reaches 0.

The safest method is of course to count all the active requests. This method also results in as little waiting time as possible. As soon as all active requests are done processing, you can begin your maintenance. The downside to counting the active requests is that incrementing and decrementing the request counter would have to take place inside synchronized blocks of code. Although unlikely, these synchronized blocks could turn into a bottleneck for the threads trying to pass through the filter. Whether that is the case is something you can only find out by profiling the web app, if it becomes unacceptably slow with lots of concurrent requests (a web app will always slow down the more concurrent requests it has, no matter what you do).

Adding a Password

Of course you don't want just anybody to be able to switch your web application into maintenance mode. Therefore I've added a password that needs to be passed to the filter too. This password is passed as a request parameter too. Here is how the request URL's to switch mode could look with a password:

Maintenance Mode
http://tutorials.jenkov.com/anyPage.html?maintenance-mode=1&password=secret

Normal Operation Mode
http://tutorials.jenkov.com/anyPage.html?maintenance-mode=0&password=secret

This makes it a lot harder for anyone inappropriate to switch your web application into maintenance mode. Of course, since the password is transmitted in clear text, it could be intercepted. If you want to avoid that, use HTTPS instead of HTTP, when sending the operation mode switching request.

A Maintenance GUI

If the URL encoding of the maintenance mode signal is too secretive or magical for your taste, you could set up a small maintenance form instead. Such a form could look like this:


Operation Mode:
Comment:
Password:

Notice how this form contains a field for adding extra comments explaining why the web app is down, the expected down time etc.

One issue to remember in the filter implementation is, to still allow requests to the maintenance GUI to be passed through the filter, when the filter is in maintenance mode. Otherwise you can only access the GUI when the filter is in normal operation mode. That makes it rather hard to switch the filter from maintenance mode back into normal operation mode.

The Full Solution

I've posted the full solution including the servlet filter Java code, the web.xml configuration, the maintenance message page and the maintenance GUI page, for your convenience below. As you can see, it isn't actually that much code.

The MaintenanceModeFilter Java code:

package com.jenkov.seo.web;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * Copyright Jenkov Aps
 */
public class MaintenanceModeFilter implements Filter{

  public static final int MODE_NORMAL_OPERATION = 0;

  protected int    mode              = MODE_NORMAL_OPERATION;
  protected String maintenanceUrl    = null;
  protected String maintenanceGuiUrl = null;
  protected String password          = null;
  protected String comment           = null;


  public void init(FilterConfig filterConfig) throws ServletException {
    this.maintenanceUrl    = filterConfig
                              .getInitParameter("maintenanceUrl");
    this.maintenanceGuiUrl = filterConfig
                              .getInitParameter("maintenanceGuiUrl");
    this.password          = filterConfig
                              .getInitParameter("password");
  }
    

  public void doFilter(ServletRequest request, ServletResponse response,
                       FilterChain filterChain)
    throws IOException, ServletException {

    HttpServletRequest   httpRequest  = (HttpServletRequest)  request;

    if(request.getParameter("maintenance-mode") != null){

      if(password.equals(request.getParameter("password"))){
          mode    = Integer.parseInt(
                        request.getParameter("maintenance-mode"));
          comment = request.getParameter("aComment");
          request.getRequestDispatcher(this.maintenanceGuiUrl)
                 .include(request, response);
      }
      return;
    }

    if(mode != MODE_NORMAL_OPERATION){
      if(!httpRequest.getServletPath().equals(maintenanceGuiUrl)){
        request.setAttribute("comment", comment);
        request.getRequestDispatcher(this.maintenanceUrl)
               .include(request, response);
        return;
      }
    }

    //option: increment active requests count inside a synchronized section

    filterChain.doFilter(request, response);

    //option: decrement active requests count inside a synchronized section
  }


  public void destroy() {

  }
}

The web.xml configuration

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

    <filter>
        <filter-name>MaintenanceModeFilter</filter-name>
        <filter-class>com.jenkov.seo.web.MaintenanceModeFilter</filter-class>
        <init-param>
            <param-name>maintenanceUrl</param-name>
            <param-value>/maintenance.jsp</param-value>
        </init-param>
        <init-param>
            <param-name>maintenanceGuiUrl</param-name>
            <param-value>/maintenance-gui.jsp</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>123</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>MaintenanceModeFilter</filter-name>
        <url-pattern>*</url-pattern>
    </filter-mapping>

    <!-- More configuration of your web app here -->

</web-app>

The maintenance.jsp message page

<html>

<body>
<br/><br/>
<center>
This website is down for maintenance!!
</center>
<p>
    <center>
<%
    out.flush();
    String comment = (String) request.getAttribute("comment");
    if(comment != null){
        out.write(comment);
    }
    out.flush();
%>
    </center>
</p>
</body>
</html>

The maintenance-gui.jsp GUI page

Notice that the comment field is named "aComment" and not just "comment". Apparently the field name "comment" caused the text ("Comment...") in the textarea not to show up in Firefox... weird, right...?!?

<html>

<body>
<form action="" method="post">
  <table>
    <tr><td>Maintenance Mode:</td>
      <td><select name="maintenance-mode">
         <option value="0">NORMAL OPERATION</option>
         <option value="1">DOWN FOR MAINTENANCE</option>
      </select></td>
    </tr>
    <tr> <td>Comment:</td>
      <td><textarea name="aComment" rows="5" cols="60">
               Comment...
          </textarea>
      </td>
    </tr>
    <tr><td>Password: </td>
      <td><input name="password" type="password" /></td>
    </tr>
    <tr>
      <td></td>
      <td><input type="submit" value="Set Maintenance Mode"/> </td>
    </tr>

  </table>
</form>
</body>
</html>


Connect with me: Newsletter - Get all my free tips!