Skip to content

Commit d3f5162

Browse files
committed
Support for Vary HTTP header in CacheFilter responses
1 parent ac2da17 commit d3f5162

File tree

8 files changed

+107
-53
lines changed

8 files changed

+107
-53
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Java EE Cache Filter
22

3+
## 2.3.0
4+
5+
* Add support for `Vary` HTTP header in `CacheFilter` responses (#2).
6+
* Update and improve Javadocs.
7+
38
## 2.2.0
49

510
* Use a `HttpServletResponseWrapper` to remove HTTP/1.0 `Pragma` header.
@@ -8,7 +13,7 @@
813

914
## 2.1.0
1015

11-
* Move from Google Project Hosting (https://code.google.com/p/cache-filter/) to GitHub (https://github.com/samaxes/javaee-cache-filter).
16+
* Move from [Google Project Hosting](https://code.google.com/p/cache-filter/) to [GitHub](https://github.com/samaxes/javaee-cache-filter).
1217
* Update compiler to Java 6.
1318
* Replace Servlet API 2.5 dependency with Java EE 6 Web API.
1419

pom.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
<groupId>com.samaxes.filter</groupId>
1212
<artifactId>cachefilter</artifactId>
13-
<version>2.2.1-SNAPSHOT</version>
13+
<version>2.3.0-SNAPSHOT</version>
1414
<packaging>jar</packaging>
1515

1616
<name>Java EE Cache Filter</name>
@@ -97,6 +97,9 @@
9797
<manifest>
9898
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
9999
</manifest>
100+
<manifestEntries>
101+
<Built-By>Samuel Santos</Built-By>
102+
</manifestEntries>
100103
<addMavenDescriptor>false</addMavenDescriptor>
101104
</archive>
102105
</configuration>

src/main/java/com/samaxes/filter/CacheFilter.java

Lines changed: 61 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -35,26 +35,29 @@
3535

3636
/**
3737
* <p>
38-
* Filter responsible for browser caching.
38+
* Filter allowing to enable browser caching.
3939
* </p>
4040
* <table border="1">
4141
* <tr>
4242
* <th>Option</th>
4343
* <th>Required</th>
4444
* <th>Default</th>
45+
* <th>Since</th>
4546
* <th>Description</th>
4647
* </tr>
4748
* <tr>
48-
* <td>static</td>
49-
* <td>No</td>
50-
* <td>false</td>
51-
* <td>Defines whether a component is static or not. Conditional requests <sup>(1)</sup> are not required for static
52-
* components.</td>
49+
* <td>{@code expiration}</td>
50+
* <td>Yes</td>
51+
* <td>--</td>
52+
* <td>2.2.0</td>
53+
* <td>Cache directive to set an expiration time, in seconds, relative to the current date. Used for both
54+
* {@code Cache-Control} and {@code Expires} HTTP headers.</td>
5355
* </tr>
5456
* <tr>
55-
* <td>private</td>
57+
* <td>{@code private}</td>
5658
* <td>No</td>
57-
* <td>false</td>
59+
* <td>{@code false}</td>
60+
* <td>2.0.0</td>
5861
* <td>Cache directive to control where the response may be cached.
5962
* <ul>
6063
* <li><code>false</code> indicates that the response MAY be cached by any cache.</li>
@@ -64,31 +67,51 @@
6467
* </td>
6568
* </tr>
6669
* <tr>
67-
* <td>expirationTime</td>
68-
* <td>Yes</td>
69-
* <td>-</td>
70-
* <td>Cache directive to set an expiration time, in seconds, relative to the current date.</td>
70+
* <td>{@code must-revalidate}</td>
71+
* <td>No</td>
72+
* <td>{@code false}</td>
73+
* <td>2.2.0</td>
74+
* <td>Cache directive to define whether conditional requests<sup>(1)</sup> are required or not for stale responses.
75+
* When the {@code must-revalidate} directive is present in a response received by a cache, that cache MUST NOT use the
76+
* entry after it becomes stale to respond to a subsequent request without first revalidating it with the origin server.
77+
* </td>
78+
* </tr>
79+
* <tr>
80+
* <td>{@code vary}</td>
81+
* <td>No</td>
82+
* <td>--</td>
83+
* <td>2.3.0</td>
84+
* <td>Cache directive to instructs proxies to cache different versions of the same resource based on specific
85+
* request-header fields. Some possible {@code Vary} header values include:
86+
* <ul>
87+
* <li><code>Accept-Encoding</code> instructs proxies to cache two versions of a compressible resource: one compressed,
88+
* and one uncompressed.</li>
89+
* <li><code>Accept-Language</code> instructs proxies to cache different versions of a resource based on the language of
90+
* a given request.</li>
91+
* <li><code>Accept</code> instructs proxies to cache different versions of a resource based on the response format of a
92+
* given request (e.g. {@code Accept: application/xml} or {@code Accept: application/json}).</li>
93+
* </ul>
94+
* <strong>Warn:</strong> if your server does vary responses but does not indicate so via the {@code Vary} header it may
95+
* result in cache corruption: <a href="http://www.subbu.org/blog/2007/12/vary-header-for-restful-applications">Vary
96+
* Header for RESTful Applications</a>.</td>
7197
* </tr>
7298
* </table>
7399
* <p>
74100
* <sup>(1)</sup> If a component is already in the browser's cache and is being re-requested, the browser will pass the
75-
* Last-Modified date in the request header. This is called a conditional GET request and if the component has not been
76-
* modified, the server will return a 304 Not Modified response.
101+
* {@code Last-Modified} date in the request header. This is called a <em>conditional GET request</em> and if the
102+
* component has not been modified, the server will return a {@code 304 Not Modified} response.
77103
* </p>
104+
* <h2>Sample configuration:</h2>
78105
* <p>
79-
* Example configuration:
106+
* Declare the filter in your web descriptor file {@code web.xml}:
80107
* </p>
81108
*
82109
* <pre>
83110
* &lt;filter&gt;
84111
* &lt;filter-name&gt;imagesCache&lt;/filter-name&gt;
85112
* &lt;filter-class&gt;com.samaxes.filter.CacheFilter&lt;/filter-class&gt;
86113
* &lt;init-param&gt;
87-
* &lt;param-name&gt;static&lt;/param-name&gt;
88-
* &lt;param-value&gt;true&lt;/param-value&gt;
89-
* &lt;/init-param&gt;
90-
* &lt;init-param&gt;
91-
* &lt;param-name&gt;expirationTime&lt;/param-name&gt;
114+
* &lt;param-name&gt;expiration&lt;/param-name&gt;
92115
* &lt;param-value&gt;2592000&lt;/param-value&gt;
93116
* &lt;/init-param&gt;
94117
* &lt;/filter&gt;
@@ -97,21 +120,25 @@
97120
* &lt;filter-name&gt;cssCache&lt;/filter-name&gt;
98121
* &lt;filter-class&gt;com.samaxes.filter.CacheFilter&lt;/filter-class&gt;
99122
* &lt;init-param&gt;
100-
* &lt;param-name&gt;expirationTime&lt;/param-name&gt;
123+
* &lt;param-name&gt;expiration&lt;/param-name&gt;
101124
* &lt;param-value&gt;604800&lt;/param-value&gt;
102125
* &lt;/init-param&gt;
126+
* &lt;init-param&gt;
127+
* &lt;param-name&gt;vary&lt;/param-name&gt;
128+
* &lt;param-value&gt;Accept-Encoding&lt;/param-value&gt;
129+
* &lt;/init-param&gt;
103130
* &lt;/filter&gt;
104131
*
105132
* &lt;filter&gt;
106133
* &lt;filter-name&gt;jsCache&lt;/filter-name&gt;
107134
* &lt;filter-class&gt;com.samaxes.filter.CacheFilter&lt;/filter-class&gt;
108135
* &lt;init-param&gt;
109-
* &lt;param-name&gt;private&lt;/param-name&gt;
110-
* &lt;param-value&gt;true&lt;/param-value&gt;
136+
* &lt;param-name&gt;expiration&lt;/param-name&gt;
137+
* &lt;param-value&gt;216000&lt;/param-value&gt;
111138
* &lt;/init-param&gt;
112139
* &lt;init-param&gt;
113-
* &lt;param-name&gt;expirationTime&lt;/param-name&gt;
114-
* &lt;param-value&gt;216000&lt;/param-value&gt;
140+
* &lt;param-name&gt;private&lt;/param-name&gt;
141+
* &lt;param-value&gt;true&lt;/param-value&gt;
115142
* &lt;/init-param&gt;
116143
* &lt;/filter&gt;
117144
* </pre>
@@ -138,7 +165,7 @@
138165
*
139166
* @author Samuel Santos
140167
* @author John Yeary
141-
* @version 2.2.0
168+
* @version 2.3.0
142169
*/
143170
public class CacheFilter implements Filter {
144171

@@ -148,23 +175,13 @@ public class CacheFilter implements Filter {
148175

149176
private boolean mustRevalidate;
150177

178+
private String vary;
179+
151180
/**
152181
* {@inheritDoc}
153182
*/
154183
@Override
155184
public void init(FilterConfig filterConfig) throws ServletException {
156-
if (filterConfig.getInitParameter("expirationTime") != null) {
157-
throw new ServletException(new StringBuilder(
158-
"The initialization parameter expirationTime has been replaced with ")
159-
.append(CacheConfigParameter.EXPIRATION.getName()).append(" for the filter ")
160-
.append(filterConfig.getFilterName()).append(".").toString());
161-
}
162-
if (filterConfig.getInitParameter("static") != null) {
163-
throw new ServletException(new StringBuilder("The initialization parameter static has been replaced with ")
164-
.append(CacheConfigParameter.MUST_REVALIDATE.getName()).append(" for the filter ")
165-
.append(filterConfig.getFilterName()).append(".").toString());
166-
}
167-
168185
try {
169186
expiration = Long.valueOf(filterConfig.getInitParameter(CacheConfigParameter.EXPIRATION.getName()));
170187
} catch (NumberFormatException e) {
@@ -176,6 +193,7 @@ public void init(FilterConfig filterConfig) throws ServletException {
176193
cacheability = Boolean.valueOf(filterConfig.getInitParameter(CacheConfigParameter.PRIVATE.getName())) ? Cacheability.PRIVATE
177194
: Cacheability.PUBLIC;
178195
mustRevalidate = Boolean.valueOf(filterConfig.getInitParameter(CacheConfigParameter.MUST_REVALIDATE.getName()));
196+
vary = filterConfig.getInitParameter(CacheConfigParameter.VARY.getName());
179197
}
180198

181199
/**
@@ -198,6 +216,11 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
198216
httpServletResponse.setDateHeader(HTTPCacheHeader.EXPIRES.getName(), System.currentTimeMillis() + expiration
199217
* 1000L);
200218

219+
// Set Vary field
220+
if (vary != null && !vary.isEmpty()) {
221+
httpServletResponse.setHeader(HTTPCacheHeader.VARY.getName(), vary);
222+
}
223+
201224
/*
202225
* By default, some servers (e.g. Tomcat) will set headers on any SSL content to deny caching. Omitting the
203226
* Pragma header takes care of user-agents implementing HTTP/1.0.

src/main/java/com/samaxes/filter/NoCacheFilter.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@
3232

3333
/**
3434
* <p>
35-
* Completely disable browser caching.
35+
* Filter allowing to completely disable browser caching.
3636
* </p>
37+
* <h2>Sample configuration:</h2>
3738
* <p>
38-
* Example configuration:
39+
* Declare the filter in your web descriptor file {@code web.xml}:
3940
* </p>
4041
*
4142
* <pre>
@@ -49,17 +50,24 @@
4950
* </p>
5051
*
5152
* <pre>
53+
* &lt;!-- Using Servlet mapping --&gt;
5254
* &lt;filter-mapping&gt;
5355
* &lt;filter-name&gt;noCache&lt;/filter-name&gt;
5456
* &lt;servlet-name&gt;MyServlet&lt;/servlet-name&gt;
5557
* &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
5658
* &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt;
5759
* &lt;/filter-mapping&gt;
60+
*
61+
* &lt;!-- Or URL mapping --&gt;
62+
* &lt;filter-mapping&gt;
63+
* &lt;filter-name&gt;noCache&lt;/filter-name&gt;
64+
* &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
65+
* &lt;/filter-mapping&gt;
5866
* </pre>
5967
*
6068
* @author Samuel Santos
6169
* @author John Yeary
62-
* @version 2.2.0
70+
* @version 2.3.0
6371
*/
6472
public class NoCacheFilter implements Filter {
6573

src/main/java/com/samaxes/filter/NoETagFilter.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,15 @@
3333

3434
/**
3535
* <p>
36-
* Filter responsible for disabling ETag header from the HTTP response.
36+
* Filter allowing to disable {@code ETag} header from a HTTP response.
3737
* <p>
38+
* <h2>Sample configuration</h2>
3839
* <p>
39-
* Example configuration:
40+
* <strong>Note:</strong> This configuration describes how to disable HTTP {@code ETag} header set by the
41+
* <strong>DefaultServlet</strong> in Tomcat.
42+
* </p>
43+
* <p>
44+
* Declare the filter in your web descriptor file {@code web.xml}:
4045
* </p>
4146
*
4247
* <pre>
@@ -46,19 +51,19 @@
4651
* &lt;/filter&gt;
4752
* </pre>
4853
* <p>
49-
* Map the filter to Tomcat&#x27;s {@code DefaultServlet}:
54+
* Map the filter to Tomcat&#x27;s <strong>DefaultServlet</strong>:
5055
* </p>
5156
*
5257
* <pre>
5358
* &lt;filter-mapping&gt;
5459
* &lt;filter-name&gt;noEtag&lt;/filter-name&gt;
55-
* &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
60+
* &lt;url-pattern&gt;default&lt;/url-pattern&gt;
5661
* &lt;/filter-mapping&gt;
5762
* </pre>
5863
*
5964
* @author Samuel Santos
6065
* @author John Yeary
61-
* @version 2.2.0
66+
* @version 2.3.0
6267
*/
6368
public class NoETagFilter implements Filter {
6469

src/main/java/com/samaxes/filter/util/CacheConfigParameter.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*
2424
* @author Samuel Santos
2525
* @author John Yeary
26-
* @version 2.2.0
26+
* @version 2.3.0
2727
*/
2828
public enum CacheConfigParameter {
2929
/**
@@ -37,7 +37,12 @@ public enum CacheConfigParameter {
3737
/**
3838
* Cache directive to define whether conditional requests are required or not for stale responses.
3939
*/
40-
MUST_REVALIDATE("must-revalidate");
40+
MUST_REVALIDATE("must-revalidate"),
41+
/**
42+
* Cache directive to instructs proxies to cache different versions of the same resource based on specific
43+
* request-header fields.
44+
*/
45+
VARY("vary");
4146

4247
private final String name;
4348

src/main/java/com/samaxes/filter/util/Cacheability.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*
2424
* @author Samuel Santos
2525
* @author John Yeary
26-
* @version 2.2.0
26+
* @version 2.3.0
2727
*/
2828
public enum Cacheability {
2929
/**

src/main/java/com/samaxes/filter/util/HTTPCacheHeader.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*
2424
* @author Samuel Santos
2525
* @author John Yeary
26-
* @version 2.2.0
26+
* @version 2.3.0
2727
*/
2828
public enum HTTPCacheHeader {
2929
/**
@@ -43,7 +43,12 @@ public enum HTTPCacheHeader {
4343
/**
4444
* The ETag response-header field provides the current value of the entity tag for the requested variant.
4545
*/
46-
ETAG("ETag");
46+
ETAG("ETag"),
47+
/**
48+
* The Vary field value indicates the set of request-header fields that fully determines, while the response is
49+
* fresh, whether a cache is permitted to use the response to reply to a subsequent request without revalidation.
50+
*/
51+
VARY("Vary");
4752

4853
private final String name;
4954

0 commit comments

Comments
 (0)