Search code examples
struts2cache-control

In Struts2 HOWTO set cache headers for static content NOT placed in the standard /struts/, /template/ directories


I have a Struts2 (version 2.2.6) web application where I want to set the Expires response header for static resources that are not present in the traditional static-related folders for struts2. E.g. they are placed under /images/, /css/, /js/ at the root. I want all the resources to have a custom Expires response header.

How to do this in Struts2?


Solution

  • So based on the hint provided by @Dave I have come up with a following approach.

    In web.xml setup following filter & it's mapping.

    <filter>
        <filter-name>StaticContentCacheHeaderFilter</filter-name>
        <filter-class>management.filters.StaticContentCacheHeaderFilter</filter-class>
    </filter>
    
    <filter-mapping>
        <filter-name>StaticContentCacheHeaderFilter</filter-name>
        <url-pattern>/images/*</url-pattern>
    </filter-mapping>
    

    And the StaticContentCacheHeader which implements Filter has the following method -

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request = (HttpServletRequest) servletRequest;
    
        long ifModifiedSince = 0;
        try {
            ifModifiedSince = request.getDateHeader("If-Modified-Since");
        } catch (Exception e) {
            LOG.warning("Invalid If-Modified-Since header value: '"
                    + request.getHeader("If-Modified-Since") + "', ignoring");
        }
    
        long now = DateTime.now().getMillis();
    
        long lastModifiedMillis = now;
    
        DateTime dateTime = new DateTime();
    
        //1 month seems to be the minimum recommended period for static resources acc to          
        //https://developers.google.com/speed/docs/best-practices/caching#LeverageBrowserCaching
        long expires = dateTime.plusMonths(1).getMillis();
    
        if (ifModifiedSince > 0 && ifModifiedSince <= lastModifiedMillis) {
            // not modified, content is not sent - only basic
            // headers and status SC_NOT_MODIFIED
            response.setDateHeader("Expires", expires);
            response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            return;
        }
    
        // set heading information for caching static content
        response.setDateHeader("Date", now);
        response.setDateHeader("Expires", expires);
        response.setDateHeader("Retry-After", expires);
        response.setHeader("Cache-Control", "public");
        response.setDateHeader("Last-Modified", lastModifiedMillis);
    
        filterChain.doFilter(servletRequest, servletResponse);
    }
    

    This will set the cache headers to expire in one month from now.

    Let me know if there is a better way to deal with this.