springwro4j

How to have WRO answer with a http 304 not modified?


We are serving javascript resources (and others) via wro in our webapp. On the PROD environment, the browser gets (for example) the app.js angular webapp's content with an 'expires' headers one year in the future.

Meaning that for subsequent requests the browser takes it from cache without a request to the server. If we deploy a new version of the webapp, the browser does not get the new version, as it takes it from the local cache.

The goal is to configure wro or/and spring so that the headers will be correctly set to have the browser perform the request each time, and the server return a 304 not modified. So we would have the clients automatically "updated" uppon new deployment. Did someone already achieve this?

We use Spring's Java Configuration:

@Configuration
public class Wro4jConfiguration {   

@Value("${app.webapp.web.minimize}")
private String minimize;

@Value("${app.webapp.web.disableCache}")
private String disableCache;

@Autowired
private Environment env;

@Bean(name = "wroFilter")
public WroFilter wroFilter() {
    ConfigurableWroFilter filter = new ConfigurableWroFilter();
    filter.setWroManagerFactory(new Wro4jManagerFactory());
    filter.setWroConfigurationFactory(createProperties());
    return filter;
}

private PropertyWroConfigurationFactory createProperties() {
    Properties props = new Properties();
    props.setProperty("jmxEnabled", "false");
    props.setProperty("debug", String.valueOf(!env.acceptsProfiles(EnvConstants.PROD)));
    props.setProperty("gzipResources", "false");
    props.setProperty("ignoreMissingResources", "true");
    props.setProperty("minimizeEnabled", minimize);
    props.setProperty("resourceWatcherUpdatePeriod", "0");
    props.setProperty("modelUpdatePeriod", "0");
    props.setProperty("cacheGzippedContent", "false");
    // let's see if server-side cache is disabled (DEV only)
    if (Boolean.valueOf(disableCache)) {
        props.setProperty("resourceWatcherUpdatePeriod", "1");
        props.setProperty("modelUpdatePeriod", "5");
    }
    return new PropertyWroConfigurationFactory(props);
}
}

Solution

  • Based on Alex's information and documentation reference, I ended up overriding WroFilter.setResponseHeaders to put appropriate expire values. This is working fine. Wro already takes care of setting ETag, Date and others, so I only overwrite the expiration delay and date.

    @Configuration
    public class Wro4jConfiguration {
    
        @Value("${app.webapp.web.browserCache.maxAgeInHours}")
        private String maxAgeInHours;
    
        @Bean(name = "wroFilter")
        public WroFilter wroFilter() {
            ConfigurableWroFilter filter = createFilter();
    
            filter.setWroManagerFactory(new Wro4jManagerFactory());
            filter.setWroConfigurationFactory(createProperties());
    
            return filter;
        }
    
        private ConfigurableWroFilter createFilter() {
            return new ConfigurableWroFilter() {
    
                private final int BROWSER_CACHE_HOURS = Integer.parseInt(maxAgeInHours);
                private final int BROWSER_CACHE_SECONDS = BROWSER_CACHE_HOURS * 60 * 60;
    
                @Override
                protected void setResponseHeaders(final HttpServletResponse response){
                    super.setResponseHeaders(response);
    
                    if (!getConfiguration().isDebug()) {
                        ZonedDateTime cacheExpires = ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("GMT")).plusHours(BROWSER_CACHE_HOURS);
                        String cacheExpiresStr = cacheExpires.format(DateTimeFormatter.RFC_1123_DATE_TIME);
    
                        response.setHeader(HttpHeader.EXPIRES.toString(), cacheExpiresStr);
                        response.setHeader(HttpHeader.CACHE_CONTROL.toString(), "public,  max-age=" + BROWSER_CACHE_SECONDS);
                    }
                }
            };
        }
    
        // Other config methods
    
    }