Setting up a cache filter in Tomcat or JBoss for static files

Introduction

When installing TIBCO JasperReports® Server on a running Tomcat 6 server without a web server such as Apache, Tomcat 6 assumes the roles of both web server AND application server. This dual role is fine in most cases, especially when JasperReports Server is running internally behind a corporate firewall, but we should recognize that Tomcat isn't an ideal web server.

Once server loads start to go up, you should consider setting up a web server such as Apache in front of your Tomcat server, which gives you better options for serving up static content and also lightens Tomcat's load.
See http://httpd.apache.org/docs/2.2/caching.html and http://httpd.apache.org/docs/2.2/mod/mod_expires.html.

However, if you have to stay with Tomcat, you can also implement a "cache filter" mechanism, which decreases the number of times that static files get requested by a browser. The cache filter effectively intercepts a server response, and gives it an "expiration value" far into the future, which it then adds to the HTTP response header.

When the browser gets the response back with the expiration value in the distant future, it will know not to request that file again, and to simply pull it from its own browser cache. This strategy is very effective for dealing with static files such as CSS, JavaScript, static HTML and image files.

This article deals specifically with how to set up a cache on Tomcat 6. You could apply the solution we present here to Tomcat 7, or you could use the built-in cache filter that Tomcat 7 ships with. This solution also works with JBoss 7.

Download the Cache filter here: Cache Filter Jar Zip

This cache filter is released under an open source Apache License 2.0. The JAR file comes packaged up with the source files. Additional documentation may be found at the cache-filter project.

What the Cache Filter does

The Cache Filter is a JAR file containing Java CLASS files that modify the outgoing HTTP headers. Without a cache filter, you can expect to see something like the image below (I used Firebug to capture the HTTP traffic).

The fact that the "Expires" value is in the past forces the browser to request this file every single time. This isn't efficient, especially for static files such as this CSS file, because not only does this file not change, but Tomcat itself is forced to process the request. The key is to be able to change the outgoing value here so it doesn't expire, or that it expires at a date far into the future. A Tomcat server with the cache filter in place will return header values like the ones below:

In this particular case, this CSS page expires a week into the future.

Deploying the Cache Filter

To deploy the cache filter, download the JAR file, which you can find in the Introduction section of this article. Then:

  • Stop your Tomcat server
  • Copy the cachefilter.jar file to [TOMCAT_HOME]/webapps/jasperserver-pro/WEB-INF/lib
  • Modify the [TOMCAT_HOME]/webapps/jasperserver-pro/WEB-INF/web.xml file

    The purpose of modifying this file is to map the file types that the cache filter will be processing. We do this by defining the URL pattern we're looking for (or just the file extension, as in the example below).

    Add this section anywhere you want within the web.xml file.

    <filter>
        <filter-name>imagesCache</filter-name>
        <filter-class>com.samaxes.filter.CacheFilter</filter-class>
        <init-param>
            <param-name>static</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>expirationTime</param-name>
            <param-value>2592000</param-value>
        </init-param>
    </filter>
    <filter>
        <filter-name>cssCache</filter-name>
        <filter-class>com.samaxes.filter.CacheFilter</filter-class>
        <init-param>
            <param-name>expirationTime</param-name>
            <param-value>604800</param-value>
        </init-param>
    </filter>
    <filter>
        <filter-name>jsCache</filter-name>
        <filter-class>com.samaxes.filter.CacheFilter</filter-class>
        <init-param>
            <param-name>private</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>expirationTime</param-name>
            <param-value>216000</param-value>
        </init-param>
    </filter>

    This sets up the different filters, and gives them expiration values that get added to the time when the server request was made. The values are in seconds; 216000 corresponds to two and a half days, 604800 corresponds to seven full days, and 2592000 corresponds to 30 full days.

    The next item is to add the filter mapping to this same file. In the example below, we look for specific file extensions; you are not limited to the ones we added there. Add this section to another part of the same file:

    <filter-mapping>
        <filter-name>cssCache</filter-name>
        <url-pattern>*.css</url-pattern>
    </filter-mapping>
     
    <filter-mapping>
        <filter-name>jsCache</filter-name>
        <url-pattern>*.js</url-pattern>
    </filter-mapping>
     
    <filter-mapping>
        <filter-name>imagesCache</filter-name>
        <url-pattern>*.png</url-pattern>
        <url-pattern>*.gif</url-pattern>
        <url-pattern>*.jpg</url-pattern>
    </filter-mapping>
  • Restart the Tomcat server
  • Test!

Cache Control filter in 5.x

Beginning with v5.1 an internal JasperReports Server cache control filter was created. The expires time is configurable in web.xml in the StaticFilesCacheControlFilter filter and AppThemeServlet servlet, the default is 1 day (86400 seconds). The StaticFilesCacheControlFilter takes a space-delimited list of files as shown in the config snippet below. Image files such as .jpg, .png and .gif are served from the AppThemeServlet, which sets the expires time.

  <filter>
    <filter-name>StaticFilesCacheControlFilter</filter-name>
    <filter-class>com.jaspersoft.jasperserver.war.StaticFilesCacheControlFilter</filter-class>
    <init-param>
      <param-name>urlEndsWith</param-name>
      <param-value>.js .htm .html</param-value>
    </init-param>
    <init-param>
      <param-name>expiresAfterAccessInSecs</param-name>
      <param-value>86400</param-value>
    </init-param>
  </filter>
  <servlet>
    <servlet-name>AppThemeServlet</servlet-name>
    <servlet-class>com.jaspersoft.jasperserver.war.themes.ThemeResolverServlet</servlet-class>
    <init-param>
      <param-name>expiresAfterAccessInSecs</param-name>
      <param-value>86400</param-value>
    </init-param>
  </servlet> 

Cache Control filter in 6.x

In 6.x the Cache Control filters have been built into the JRS product, so no additional configuration is necessary. In 6.0 thru 6.1.1 see the StaticFilesCacheControlFilter in web.xml, in 6.2.0 thru 6.3 see resourceHTTPHeadersFilter in applicationContext-webapp.xml.

Feedback
randomness