diff --git a/spring-mvc-cache-control/src/main/java/net/rossillo/spring/web/mvc/CacheControl.java b/spring-mvc-cache-control/src/main/java/net/rossillo/spring/web/mvc/CacheControl.java index b0a91c6..ec34ea1 100644 --- a/spring-mvc-cache-control/src/main/java/net/rossillo/spring/web/mvc/CacheControl.java +++ b/spring-mvc-cache-control/src/main/java/net/rossillo/spring/web/mvc/CacheControl.java @@ -36,4 +36,9 @@ */ int sharedMaxAge() default -1; + /** + * Sometimes caching need vary headers. E.g. 'Accept'. + */ + String[] vary() default {}; + } diff --git a/spring-mvc-cache-control/src/main/java/net/rossillo/spring/web/mvc/CacheControlHandlerInterceptor.java b/spring-mvc-cache-control/src/main/java/net/rossillo/spring/web/mvc/CacheControlHandlerInterceptor.java index 5b8be21..1aee7a4 100644 --- a/spring-mvc-cache-control/src/main/java/net/rossillo/spring/web/mvc/CacheControlHandlerInterceptor.java +++ b/spring-mvc-cache-control/src/main/java/net/rossillo/spring/web/mvc/CacheControlHandlerInterceptor.java @@ -14,7 +14,7 @@ /** * Provides a cache control handler interceptor to assign cache-control * headers to HTTP responses. - * + * * @author Scott Rossillo * */ @@ -22,46 +22,51 @@ public class CacheControlHandlerInterceptor extends HandlerInterceptorAdapter im private static final String HEADER_EXPIRES = "Expires"; private static final String HEADER_CACHE_CONTROL = "Cache-Control"; - + private static final String HEADER_VARY = "Vary"; + private boolean useExpiresHeader = true; - + /** * Creates a new cache control handler interceptor. */ public CacheControlHandlerInterceptor() { super(); } - + /** * Assigns a CacheControl header to the given response. - * + * * @param request the HttpServletRequest * @param response the HttpServletResponse * @param handler the handler for the given request */ protected final void assignCacheControlHeader( final HttpServletRequest request, - final HttpServletResponse response, + final HttpServletResponse response, final Object handler) { - + final CacheControl cacheControl = this.getCacheControl(request, response, handler); final String cacheControlHeader = this.createCacheControlHeader(cacheControl); - + if (cacheControlHeader != null) { response.setHeader(HEADER_CACHE_CONTROL, cacheControlHeader); if (useExpiresHeader) { response.setDateHeader(HEADER_EXPIRES, createExpiresHeader(cacheControl)); } + String[] vary = cacheControl.vary(); + for (String v : vary) { + response.addHeader(HEADER_VARY, v); + } } } - + /** * Returns cache control header value from the given {@link CacheControl} * annotation. - * + * * @param cacheControl the CacheControl annotation from which to * create the returned cache control header value - * + * * @return the cache control header value */ protected final String createCacheControlHeader(final CacheControl cacheControl) { @@ -96,77 +101,77 @@ protected final String createCacheControlHeader(final CacheControl cacheControl) return (builder.length() > 0 ? builder.toString() : null); } - + /** - * Returns an expires header value generated from the given + * Returns an expires header value generated from the given * {@link CacheControl} annotation. - * + * * @param cacheControl the CacheControl annotation from which to * create the returned expires header value - * + * * @return the expires header value */ protected final long createExpiresHeader(final CacheControl cacheControl) { - + final Calendar expires = new GregorianCalendar(TimeZone.getTimeZone("GMT")); - + if (cacheControl.maxAge() >= 0) { expires.add(Calendar.SECOND, cacheControl.maxAge()); } - + return expires.getTime().getTime(); } - + /** * Returns the {@link CacheControl} annotation specified for the * given request, response and handler. - * + * * @param request the current HttpServletRequest * @param response the current HttpServletResponse * @param handler the current request handler - * + * * @return the CacheControl annotation specified by * the given handler if present; null otherwise */ protected final CacheControl getCacheControl( final HttpServletRequest request, - final HttpServletResponse response, + final HttpServletResponse response, final Object handler) { - + if (handler == null || !(handler instanceof HandlerMethod)) { return null; } - + final HandlerMethod handlerMethod = (HandlerMethod) handler; CacheControl cacheControl = handlerMethod.getMethodAnnotation(CacheControl.class); - + if (cacheControl == null) { return handlerMethod.getBeanType().getAnnotation(CacheControl.class); } - + return cacheControl; } - + @Override public final boolean preHandle( final HttpServletRequest request, - final HttpServletResponse response, + final HttpServletResponse response, final Object handler) throws Exception { - + this.assignCacheControlHeader(request, response, handler); - + return super.preHandle(request, response, handler); } /** * True to set an expires header when a {@link CacheControl} annotation is present * on a handler; false otherwise. Defaults to true. - * - * @param useExpiresHeader true to set an expires header when a + * + * @param useExpiresHeader true to set an expires header when a * CacheControl annotation is present on a handler; false otherwise */ public final void setUseExpiresHeader(final boolean useExpiresHeader) { this.useExpiresHeader = useExpiresHeader; } - + } diff --git a/spring-mvc-cache-control/src/test/java/net/rossillo/spring/web/mvc/CacheControlAnnotatedTestController.java b/spring-mvc-cache-control/src/test/java/net/rossillo/spring/web/mvc/CacheControlAnnotatedTestController.java index c129961..c3a54b7 100644 --- a/spring-mvc-cache-control/src/test/java/net/rossillo/spring/web/mvc/CacheControlAnnotatedTestController.java +++ b/spring-mvc-cache-control/src/test/java/net/rossillo/spring/web/mvc/CacheControlAnnotatedTestController.java @@ -25,6 +25,11 @@ public String handlePubliclyCachedPageAndProxyRevalidatedRequest() { public String handlePrivatelyCachedPageRequest() { return null; } + + @CacheControl(policy = CachePolicy.PRIVATE, maxAge = 360, vary = "Accept") + public String handlePrivatelyCachedPageRequestWithVary() { + return null; + } public String handleWithDefaultPolicy() { return null; diff --git a/spring-mvc-cache-control/src/test/java/net/rossillo/spring/web/mvc/CacheControlHandlerInterceptorTest.java b/spring-mvc-cache-control/src/test/java/net/rossillo/spring/web/mvc/CacheControlHandlerInterceptorTest.java index 0d86863..c9d3e88 100644 --- a/spring-mvc-cache-control/src/test/java/net/rossillo/spring/web/mvc/CacheControlHandlerInterceptorTest.java +++ b/spring-mvc-cache-control/src/test/java/net/rossillo/spring/web/mvc/CacheControlHandlerInterceptorTest.java @@ -97,7 +97,25 @@ public void testCacheControlPrivate() throws Exception { assertTrue(response.getHeader("Cache-Control").contains("private")); assertFalse(response.getHeader("Cache-Control").contains("public")); } - + + @Test + public void testCacheControlPrivateWithVary() throws Exception { + + final HandlerMethod handler = new HandlerMethod( + controller, + controller.getClass().getMethod("handlePrivatelyCachedPageRequestWithVary")); + + interceptor.preHandle(request, response, handler); + + System.err.println("Vary: " + response.getHeader("Vary")); + + assertNotNull(response.getHeader("Cache-Control")); + assertTrue(response.getHeader("Cache-Control").contains("private")); + assertFalse(response.getHeader("Cache-Control").contains("public")); + assertTrue(response.getHeader("Vary").contains("Accept")); + + } + @Test public void testExpires() throws Exception {