KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > opensymphony > oscache > web > filter > CacheHttpServletResponseWrapper


1 /*
2  * Copyright (c) 2002-2003 by OpenSymphony
3  * All rights reserved.
4  */

5 package com.opensymphony.oscache.web.filter;
6
7 import org.apache.commons.logging.Log;
8 import org.apache.commons.logging.LogFactory;
9
10 import java.io.IOException JavaDoc;
11 import java.io.OutputStreamWriter JavaDoc;
12 import java.io.PrintWriter JavaDoc;
13
14 import java.util.Locale JavaDoc;
15
16 import javax.servlet.ServletOutputStream JavaDoc;
17 import javax.servlet.http.HttpServletResponse JavaDoc;
18 import javax.servlet.http.HttpServletResponseWrapper JavaDoc;
19
20 /**
21  * CacheServletResponse is a serialized representation of a response
22  *
23  * @author <a HREF="mailto:sergek@lokitech.com">Serge Knystautas</a>
24  * @version $Revision: 1.7 $
25  */

26 public class CacheHttpServletResponseWrapper extends HttpServletResponseWrapper JavaDoc {
27     private final Log log = LogFactory.getLog(this.getClass());
28
29     /**
30      * We cache the printWriter so we can maintain a single instance
31      * of it no matter how many times it is requested.
32      */

33     private PrintWriter JavaDoc cachedWriter = null;
34     private ResponseContent result = null;
35     private SplitServletOutputStream cacheOut = null;
36     private boolean fragment = false;
37     private int status = SC_OK;
38     private long expires = CacheFilter.EXPIRES_ON;
39     private long lastModified = CacheFilter.LAST_MODIFIED_INITIAL;
40     private long cacheControl = -60;
41
42     /**
43      * Constructor
44      *
45      * @param response The servlet response
46      */

47     public CacheHttpServletResponseWrapper(HttpServletResponse JavaDoc response) {
48         this(response, false, Long.MAX_VALUE, CacheFilter.EXPIRES_ON, CacheFilter.LAST_MODIFIED_INITIAL, -60);
49     }
50
51     /**
52      * Constructor
53      *
54      * @param response The servlet response
55      * @param fragment true if the repsonse indicates that it is a fragement of a page
56      * @param time the refresh time in millis
57      * @param lastModified defines if last modified header will be send, @see CacheFilter
58      * @param expires defines if expires header will be send, @see CacheFilter
59      * @param cacheControl defines if cache control header will be send, @see CacheFilter
60      */

61     public CacheHttpServletResponseWrapper(HttpServletResponse JavaDoc response, boolean fragment, long time, long lastModified, long expires, long cacheControl) {
62         super(response);
63         result = new ResponseContent();
64         this.fragment = fragment;
65         this.expires = expires;
66         this.lastModified = lastModified;
67         this.cacheControl = cacheControl;
68         
69         // only set inital values for last modified and expires, when a complete page is cached
70
if (!fragment) {
71             // setting a default last modified value based on object creation and remove the millis
72
if (lastModified == CacheFilter.LAST_MODIFIED_INITIAL) {
73                 long current = System.currentTimeMillis();
74                 current = current - (current % 1000);
75                 result.setLastModified(current);
76                 super.setDateHeader(CacheFilter.HEADER_LAST_MODIFIED, result.getLastModified());
77             }
78             // setting the expires value
79
if (expires == CacheFilter.EXPIRES_TIME) {
80                 result.setExpires(result.getLastModified() + time);
81                 super.setDateHeader(CacheFilter.HEADER_EXPIRES, result.getExpires());
82             }
83             // setting the cache control with max-age
84
if (cacheControl == CacheFilter.MAX_AGE_TIME) {
85                 // set the count down
86
long maxAge = System.currentTimeMillis();
87                 maxAge = maxAge - (maxAge % 1000) + time;
88                 result.setMaxAge(maxAge);
89                 super.addHeader(CacheFilter.HEADER_CACHE_CONTROL, "max-age=" + time / 1000);
90             } else if (cacheControl != CacheFilter.MAX_AGE_NO_INIT) {
91                 result.setMaxAge(cacheControl);
92                 super.addHeader(CacheFilter.HEADER_CACHE_CONTROL, "max-age=" + (-cacheControl));
93             }
94         }
95     }
96
97     /**
98      * Get a response content
99      *
100      * @return The content
101      */

102     public ResponseContent getContent() {
103         //Create the byte array
104
result.commit();
105
106         //Return the result from this response
107
return result;
108     }
109
110     /**
111      * Set the content type
112      *
113      * @param value The content type
114      */

115     public void setContentType(String JavaDoc value) {
116         if (log.isDebugEnabled()) {
117             log.debug("ContentType: " + value);
118         }
119
120         super.setContentType(value);
121         result.setContentType(value);
122     }
123
124     /**
125      * Set the date of a header
126      *
127      * @param name The header name
128      * @param value The date
129      */

130     public void setDateHeader(String JavaDoc name, long value) {
131         if (log.isDebugEnabled()) {
132             log.debug("dateheader: " + name + ": " + value);
133         }
134
135         // only set the last modified value, if a complete page is cached
136
if ((lastModified != CacheFilter.LAST_MODIFIED_OFF) && (CacheFilter.HEADER_LAST_MODIFIED.equalsIgnoreCase(name))) {
137             if (!fragment) {
138                 result.setLastModified(value);
139             } // TODO should we return now by fragments to avoid putting the header to the response?
140
}
141
142         // implement RFC 2616 14.21 Expires (without max-age)
143
if ((expires != CacheFilter.EXPIRES_OFF) && (CacheFilter.HEADER_EXPIRES.equalsIgnoreCase(name))) {
144             if (!fragment) {
145                 result.setExpires(value);
146             } // TODO should we return now by fragments to avoid putting the header to the response?
147
}
148
149         super.setDateHeader(name, value);
150     }
151
152     /**
153      * Add the date of a header
154      *
155      * @param name The header name
156      * @param value The date
157      */

158     public void addDateHeader(String JavaDoc name, long value) {
159         if (log.isDebugEnabled()) {
160             log.debug("dateheader: " + name + ": " + value);
161         }
162
163         // only set the last modified value, if a complete page is cached
164
if ((lastModified != CacheFilter.LAST_MODIFIED_OFF) && (CacheFilter.HEADER_LAST_MODIFIED.equalsIgnoreCase(name))) {
165             if (!fragment) {
166                 result.setLastModified(value);
167             } // TODO should we return now by fragments to avoid putting the header to the response?
168
}
169
170         // implement RFC 2616 14.21 Expires (without max-age)
171
if ((expires != CacheFilter.EXPIRES_OFF) && (CacheFilter.HEADER_EXPIRES.equalsIgnoreCase(name))) {
172             if (!fragment) {
173                 result.setExpires(value);
174             } // TODO should we return now by fragments to avoid putting the header to the response?
175
}
176
177         super.addDateHeader(name, value);
178     }
179
180     /**
181      * Set a header field
182      *
183      * @param name The header name
184      * @param value The header value
185      */

186     public void setHeader(String JavaDoc name, String JavaDoc value) {
187         if (log.isDebugEnabled()) {
188             log.debug("header: " + name + ": " + value);
189         }
190
191         if (CacheFilter.HEADER_CONTENT_TYPE.equalsIgnoreCase(name)) {
192             result.setContentType(value);
193         }
194
195         if (CacheFilter.HEADER_CONTENT_ENCODING.equalsIgnoreCase(name)) {
196             result.setContentEncoding(value);
197         }
198
199         super.setHeader(name, value);
200     }
201
202     /**
203      * Add a header field
204      *
205      * @param name The header name
206      * @param value The header value
207      */

208     public void addHeader(String JavaDoc name, String JavaDoc value) {
209         if (log.isDebugEnabled()) {
210             log.debug("header: " + name + ": " + value);
211         }
212
213         if (CacheFilter.HEADER_CONTENT_TYPE.equalsIgnoreCase(name)) {
214             result.setContentType(value);
215         }
216
217         if (CacheFilter.HEADER_CONTENT_ENCODING.equalsIgnoreCase(name)) {
218             result.setContentEncoding(value);
219         }
220
221         super.addHeader(name, value);
222     }
223
224     /**
225      * Set the int value of the header
226      *
227      * @param name The header name
228      * @param value The int value
229      */

230     public void setIntHeader(String JavaDoc name, int value) {
231         if (log.isDebugEnabled()) {
232             log.debug("intheader: " + name + ": " + value);
233         }
234
235         super.setIntHeader(name, value);
236     }
237
238     /**
239      * We override this so we can catch the response status. Only
240      * responses with a status of 200 (<code>SC_OK</code>) will
241      * be cached.
242      */

243     public void setStatus(int status) {
244         super.setStatus(status);
245         this.status = status;
246     }
247
248     /**
249      * We override this so we can catch the response status. Only
250      * responses with a status of 200 (<code>SC_OK</code>) will
251      * be cached.
252      */

253     public void sendError(int status, String JavaDoc string) throws IOException JavaDoc {
254         super.sendError(status, string);
255         this.status = status;
256     }
257
258     /**
259      * We override this so we can catch the response status. Only
260      * responses with a status of 200 (<code>SC_OK</code>) will
261      * be cached.
262      */

263     public void sendError(int status) throws IOException JavaDoc {
264         super.sendError(status);
265         this.status = status;
266     }
267
268     /**
269      * We override this so we can catch the response status. Only
270      * responses with a status of 200 (<code>SC_OK</code>) will
271      * be cached.
272      */

273     public void setStatus(int status, String JavaDoc string) {
274         super.setStatus(status, string);
275         this.status = status;
276     }
277
278     /**
279      * We override this so we can catch the response status. Only
280      * responses with a status of 200 (<code>SC_OK</code>) will
281      * be cached.
282      */

283     public void sendRedirect(String JavaDoc location) throws IOException JavaDoc {
284         this.status = SC_MOVED_TEMPORARILY;
285         super.sendRedirect(location);
286     }
287
288     /**
289      * Retrieves the captured HttpResponse status.
290      */

291     public int getStatus() {
292         return status;
293     }
294
295     /**
296      * Set the locale
297      *
298      * @param value The locale
299      */

300     public void setLocale(Locale JavaDoc value) {
301         super.setLocale(value);
302         result.setLocale(value);
303     }
304
305     /**
306      * Get an output stream
307      *
308      * @throws IOException
309      */

310     public ServletOutputStream JavaDoc getOutputStream() throws IOException JavaDoc {
311         // Pass this faked servlet output stream that captures what is sent
312
if (cacheOut == null) {
313             cacheOut = new SplitServletOutputStream(result.getOutputStream(), super.getOutputStream());
314         }
315
316         return cacheOut;
317     }
318
319     /**
320      * Get a print writer
321      *
322      * @throws IOException
323      */

324     public PrintWriter JavaDoc getWriter() throws IOException JavaDoc {
325         if (cachedWriter == null) {
326             String JavaDoc encoding = getCharacterEncoding();
327             if (encoding != null) {
328                 cachedWriter = new PrintWriter JavaDoc(new OutputStreamWriter JavaDoc(getOutputStream(), encoding));
329             } else { // using the default character encoding
330
cachedWriter = new PrintWriter JavaDoc(new OutputStreamWriter JavaDoc(getOutputStream()));
331             }
332         }
333
334         return cachedWriter;
335     }
336
337     public void flushBuffer() throws IOException JavaDoc {
338         super.flushBuffer();
339
340         if (cacheOut != null) {
341             cacheOut.flush();
342         }
343
344         if (cachedWriter != null) {
345             cachedWriter.flush();
346         }
347     }
348
349     /**
350      * @see javax.servlet.ServletResponseWrapper#isCommitted()
351      */

352     public boolean isCommitted() {
353         return super.isCommitted() || (result.getOutputStream() == null);
354     }
355
356     /**
357      * @see javax.servlet.ServletResponseWrapper#reset()
358      */

359     public void reset() {
360         if (!isCommitted()) {
361             super.reset();
362             cachedWriter = null;
363             result = new ResponseContent();
364             cacheOut = null;
365             fragment = false;
366             status = SC_OK;
367             expires = CacheFilter.EXPIRES_ON;
368             lastModified = CacheFilter.LAST_MODIFIED_INITIAL;
369             cacheControl = -60;
370         } else {
371             throw new IllegalStateException JavaDoc("Can't reset CacheHttpServletResponseWrapper, because it's already committed!");
372         }
373     }
374
375     /**
376      * @see javax.servlet.ServletResponseWrapper#resetBuffer()
377      */

378     public void resetBuffer() {
379         if (!isCommitted()) {
380             super.resetBuffer();
381             cachedWriter = null;
382             result = new ResponseContent();
383             cacheOut = null;
384             fragment = false;
385             // The resetBuffer method clears content in the buffer if the
386
// response is not committed without clearing the headers and status code.
387
} else {
388             throw new IllegalStateException JavaDoc("Can't reset buffer CacheHttpServletResponseWrapper, because it's already committed!");
389         }
390     }
391 }
392
Popular Tags