Search code examples
cache-controldropwizard

Set cache control headers of static assets in Dropwizard


What's the best way to set the cache control headers of static assets in a Dropwizard service?

Some Googling showed up an AssetsBundle constructor:

AssetsBundle(String resourcePath, com.google.common.cache.CacheBuilderSpec cacheBuilderSpec, String uriPath)

However on further investigation, it looks like the package com.yammer.dropwizard.bundles hasn't been part of Dropwizard since version 5.1.

Perhaps I'm missing something obvious, but is there a preferred way to handle this?


Solution

  • Building on the answer from Tim Barclay, I created a filter which sets Cache-Control and Expires one year into the future, if the resource requested is a file with extension js, css, png, jpg, gif or svg. Otherwise the cache is disabled.

    Hope it can be helpful for someone!

    protected void setCacheHeaders(Environment environment, String urlPattern, int seconds) {
        FilterRegistration.Dynamic filter = environment.servlets().addFilter(
                "cacheControlFilter",
                new Filter() {
                    @Override
                    public void init(FilterConfig filterConfig) throws ServletException {
    
                    }
    
                    @Override
                    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
                        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
                        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
    
                        String[] cacheFileTypes = {"js","css","png","jpg","gif","svg"};
                        String filetypeRequested = FilenameUtils.getExtension(httpServletRequest.getRequestURL().toString());
    
                        if (httpServletRequest.getMethod() == "GET" && seconds > 0 && Arrays.asList(cacheFileTypes).contains(filetypeRequested)) {
                            httpServletResponse.setHeader("Cache-Control", "public, max-age=" + seconds);
                            Calendar c = Calendar.getInstance();
                            c.setTime(new Date());
                            c.add(Calendar.SECOND, seconds);
                            SimpleDateFormat format = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss zzz", Locale.US);
                            format.setTimeZone(TimeZone.getTimeZone("GMT"));
                            httpServletResponse.setHeader("Expires", format.format(c.getTime()));
                        } else {
                            httpServletResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
                            httpServletResponse.setHeader("Expires", "0");
                            httpServletResponse.setHeader("Pragma", "no-cache");
                        }
    
                        filterChain.doFilter(servletRequest, servletResponse);
    
                    }
    
                    @Override
                    public void destroy() {
    
                    }
                }
        );
        filter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, urlPattern);
    }
    

    PS: I couldn't get the accepted answer's way to set the Expires-header to work:

    resp.setHeader("Expires", new Date().getTime()+500000 + "");
    

    Mine is terribly bloated in comparison, but it works:

    Calendar c = Calendar.getInstance();
    c.setTime(new Date());
    c.add(Calendar.SECOND, seconds);
    SimpleDateFormat format = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss zzz", Locale.US);
    format.setTimeZone(TimeZone.getTimeZone("GMT"));
    httpServletResponse.setHeader("Expires", format.format(c.getTime()));