Categories
jakarta-ee java jsp servlets

Servlet for serving static content

150

I deploy a webapp on two different containers (Tomcat and Jetty), but their default servlets for serving the static content have a different way of handling the URL structure I want to use (details).

I am therefore looking to include a small servlet in the webapp to serve its own static content (images, CSS, etc.). The servlet should have the following properties:

  • No external dependencies
  • Simple and reliable
  • Support for If-Modified-Since header (i.e. custom getLastModified method)
  • (Optional) support for gzip encoding, etags,…

Is such a servlet available somewhere? The closest I can find is example 4-10 from the servlet book.

Update: The URL structure I want to use – in case you are wondering – is simply:

    <servlet-mapping>
            <servlet-name>main</servlet-name>
            <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>/static/*</url-pattern>
    </servlet-mapping>

So all requests should be passed to the main servlet, unless they are for the static path. The problem is that Tomcat’s default servlet does not take the ServletPath into account (so it looks for the static files in the main folder), while Jetty does (so it looks in the static folder).

5

  • Could you elaborate on the “URL structure” you want to use? Rolling your own, based on the linked example 4-10, seems like a trivial effort. I’ve done it myself plenty of times…

    Sep 25, 2008 at 9:12

  • I edited my question to elaborate the URL structure. And yes, I ended up rolling my own servlet. See my answer below.

    Sep 25, 2008 at 12:17

  • 1

    Why don’t you use the webserver for static content?

    – Stephen

    Oct 2, 2008 at 19:10

  • 4

    @Stephen: because there is not always an Apache in front of the Tomcat/Jetty. And to avoid the hassle of a separate configuration. But you are right, I could consider that option.

    Oct 3, 2008 at 13:32

  • I just can’t understand, why you didn’t use mapping like this <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> to serve static content

    Nov 5, 2011 at 9:40


20

I ended up rolling my own StaticServlet. It supports If-Modified-Since, gzip encoding and it should be able to serve static files from war-files as well. It is not very difficult code, but it is not entirely trivial either.

The code is available: StaticServlet.java. Feel free to comment.

Update: Khurram asks about the ServletUtils class which is referenced in StaticServlet. It is simply a class with auxiliary methods that I used for my project. The only method you need is coalesce (which is identical to the SQL function COALESCE). This is the code:

public static <T> T coalesce(T...ts) {
    for(T t: ts)
        if(t != null)
            return t;
    return null;
}

5

  • 2

    Don’t name your inner class Error. That might cause confusion as you can mistake it for java.lang.Error Also, is your web.xml the same ?

    – Leonel

    Sep 25, 2008 at 17:10

  • Thanks for the Error warning. web.xml is the same, with “default” replaced by the name of the StaticServlet.

    Sep 25, 2008 at 17:25

  • 1

    As for the coalesce method, it can be replaced (inside the Servlet class) by commons-lang StringUtils.defaultString(String, String)

    Jul 25, 2011 at 6:53

  • The transferStreams() method can also be replaced with Files.copy(is ,os);

    Aug 12, 2014 at 6:25

  • Why is this approach so popular? Why are people reimplementing static file servers like this? There’s so many security holes just waiting to be discovered, and so many features of real static file servers that aren’t implemented.

    Mar 23, 2020 at 16:28

46

There is no need for completely custom implementation of the default servlet in this case, you can use this simple servlet to wrap request to the container’s implementation:


package com.example;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class DefaultWrapperServlet extends HttpServlet
{   
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        RequestDispatcher rd = getServletContext().getNamedDispatcher("default");

        HttpServletRequest wrapped = new HttpServletRequestWrapper(req) {
            public String getServletPath() { return ""; }
        };

        rd.forward(wrapped, resp);
    }
}

2

30

I’ve had good results with FileServlet, as it supports pretty much all of HTTP (etags, chunking, etc.).

3

  • Thanks! hours of failed attempts and bad answers, and this solved my problem

    Aug 15, 2012 at 7:38

  • 4

    Though in order to serve content from a folder outside the app (I use it to server a folder from the disk, say C:\resources) I modified the this row: this.basePath = getServletContext().getRealPath(getInitParameter(“basePath”)); And replaced it with: this.basePath = getInitParameter(“basePath”);

    Aug 15, 2012 at 7:39


  • 1

    An updated version is available at showcase.omnifaces.org/servlets/FileServlet

    – koppor

    Nov 23, 2016 at 9:19