KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > appserv > web > cache > filter > CachingFilter


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 package com.sun.appserv.web.cache.filter;
25
26 import java.io.IOException JavaDoc;
27
28 import java.text.MessageFormat JavaDoc;
29
30 import java.util.ArrayList JavaDoc;
31 import java.util.Iterator JavaDoc;
32
33 import java.util.logging.Logger JavaDoc;
34 import java.util.logging.Level JavaDoc;
35 import java.util.ResourceBundle JavaDoc;
36
37 import javax.servlet.ServletContext JavaDoc;
38 import javax.servlet.ServletRequest JavaDoc;
39 import javax.servlet.ServletResponse JavaDoc;
40 import javax.servlet.ServletException JavaDoc;
41 import javax.servlet.ServletOutputStream JavaDoc;
42 import javax.servlet.Filter JavaDoc;
43 import javax.servlet.FilterConfig JavaDoc;
44 import javax.servlet.FilterChain JavaDoc;
45
46 import javax.servlet.http.HttpServletRequest JavaDoc;
47 import javax.servlet.http.HttpServletResponse JavaDoc;
48 import javax.servlet.http.HttpServletResponseWrapper JavaDoc;
49 import javax.servlet.http.Cookie JavaDoc;
50
51 import com.sun.enterprise.web.logging.pwc.LogDomains;
52
53 import com.sun.appserv.web.cache.CacheHelper;
54 import com.sun.appserv.web.cache.DefaultCacheHelper;
55 import com.sun.appserv.web.cache.CacheManager;
56 import com.sun.appserv.web.cache.CacheManagerListener;
57
58 import com.sun.appserv.util.cache.Cache;
59
60 public class CachingFilter implements Filter JavaDoc, CacheManagerListener {
61
62     // this servlet filter name
63
String JavaDoc filterName;
64     String JavaDoc servletName;
65     String JavaDoc urlPattern;
66     
67     // associated cache and the helper
68
CacheManager manager;
69     CacheHelper helper;
70     Cache cache;
71
72     boolean isEnabled = false;
73
74     private static Logger JavaDoc _logger = null;
75     private static boolean _isTraceEnabled = false;
76
77     /**
78      * Called by the web container to indicate to a filter that it is being
79      * placed into service. The servlet container calls the init method exactly
80      * once after instantiating the filter. The init method must complete
81      * successfully before the filter is asked to do any filtering work.
82      * @param filterConfig filter config
83      * @throws ServletException
84      */

85     public void init(FilterConfig JavaDoc filterConfig) throws ServletException JavaDoc {
86
87         filterName = filterConfig.getFilterName();
88         servletName = filterConfig.getInitParameter("servletName");
89         urlPattern = filterConfig.getInitParameter("URLPattern");
90
91         ServletContext JavaDoc context = filterConfig.getServletContext();
92         manager = (CacheManager)
93                   context.getAttribute(CacheManager.CACHE_MANAGER_ATTR_NAME);
94
95         if (manager != null && manager.isEnabled()) {
96             this.cache = manager.getDefaultCache();
97             this.helper = manager.getCacheHelperByFilterName(filterName);
98
99             // add filter as a listener so caching can be disabled at runtime.
100
manager.addCacheManagerListener(this);
101             isEnabled = true;
102         }
103
104         if (_logger == null) {
105             _logger = LogDomains.getLogger(LogDomains.PWC_LOGGER);
106             _isTraceEnabled = _logger.isLoggable(Level.FINE);
107         }
108
109         if (_isTraceEnabled) {
110             _logger.fine("CachingFilter " + filterName + " ready; isEnabled = " + isEnabled + " manager = " + manager);
111         }
112     }
113     
114     /**
115      * The <code>doFilter</code> method of the Filter is called by the container
116      * each time a request/response pair is passed through the chain due
117      * to a client request for a resource at the end of the chain. The
118      * FilterChain passed in to this method allows the Filter to pass on the
119      * request and response to the next entity in the chain.<p>
120      * @param srequest the runtime request
121      * @param sresponse the runtime response
122      * @param chain the filter chain to in the request processing
123      * @throws IOException, ServletException
124      *
125      * - First check if this HTTP method permits caching (using helper)
126      * if not, call the downstream filter and return.
127      * - Otherwise, get the key based on the request (using helper).
128      * - Check if we have a response entry in the cache already.
129      * - If there is entry and is valid, write out the response from that entry.
130      * - create a CachingResponse and CachingOutputStream wrappers and call
131      * the downstream filter
132      */

133     public void doFilter (ServletRequest JavaDoc srequest, ServletResponse JavaDoc sresponse,
134                             FilterChain JavaDoc chain )
135                             throws IOException JavaDoc, ServletException JavaDoc {
136         String JavaDoc key;
137         HttpServletRequest JavaDoc request = (HttpServletRequest JavaDoc)srequest;
138         HttpServletResponse JavaDoc response = (HttpServletResponse JavaDoc)sresponse;
139
140         request.setAttribute(DefaultCacheHelper.ATTR_CACHING_FILTER_NAME,
141                                                             filterName);
142         request.setAttribute(CacheHelper.ATTR_CACHE_MAPPED_SERVLET_NAME,
143                                         servletName);
144         request.setAttribute(CacheHelper.ATTR_CACHE_MAPPED_URL_PATTERN,
145                                         urlPattern);
146
147         if (isEnabled && helper.isCacheable((HttpServletRequest JavaDoc)request) &&
148                 (key = helper.getCacheKey(request)) != null) {
149
150             // have the key index for reuse
151
int index = cache.getIndex(key);
152
153             if (_isTraceEnabled) {
154                 _logger.fine("CachingFilter " + request.getServletPath() +
155                                 " request is cacheable; key " + key + " index = " + index);
156             }
157
158             HttpCacheEntry entry = null;
159             boolean entryReady = false, waitForRefresh = true;
160             
161             // if refresh is not needed then check the cache first
162
if (!helper.isRefreshNeeded(request)) {
163                 do {
164                     // lookup cache
165
entry = (HttpCacheEntry) cache.get(key);
166
167                     if (entry != null && entry.isValid()) {
168                         // see if there is cached entry and is valid
169
entryReady = true;
170                         break;
171                     }
172                     else {
173                         /**
174                          * a cache entry needs to be generated or refreshed.
175                          * if there are more than one thread tries to fill/refresh
176                          * same cache entry, then all but the first thread will block.
177                          */

178                         waitForRefresh = cache.waitRefresh(index);
179                     }
180                 } while (waitForRefresh);
181             } else {
182                 if (_isTraceEnabled) {
183                     _logger.fine("CachingFilter " + request.getServletPath() +
184                                 " request needs a refresh; key " + key);
185                 }
186             }
187
188             // do we have a valid response?
189
if (entryReady) {
190                 if (_isTraceEnabled) {
191                     _logger.fine("CachingFilter " + request.getServletPath() +
192                                 " serving response from the cache " + key);
193                 }
194                 sendCachedResponse(entry, response);
195             } else {
196                 // call the target servlet
197

198                 HttpCacheEntry oldEntry;
199
200                 // setup the response wrapper (and the output stream)
201
CachingResponseWrapper wrapper =
202                                 new CachingResponseWrapper(response);
203
204                 // call the target resource
205
try {
206                     chain.doFilter(srequest, (ServletResponse JavaDoc)wrapper);
207                 } catch (ServletException JavaDoc se) {
208                     wrapper.clear();
209                     throw se;
210                 } catch (IOException JavaDoc ioe) {
211                     wrapper.clear();
212                     throw ioe;
213                 }
214
215                 // see if the there weren't any errors
216
if (!wrapper.isError()) {
217                     // create/refresh the cached response entry
218

219                     // compute the timeout
220
int timeout = helper.getTimeout(request);
221
222                     // previous entry gets replaced
223
entry = wrapper.cacheResponse();
224
225                     if (timeout == CacheHelper.TIMEOUT_VALUE_NOT_SET) {
226                         // extracts this from the Expires: date header
227
Long JavaDoc lval = wrapper.getExpiresDateHeader();
228
229                         if (lval == null) {
230                             timeout = manager.getDefaultTimeout();
231                             entry.computeExpireTime(timeout);
232                         } else {
233                             long expireTime = lval.longValue();
234
235                             // set the time this entry would expires
236
entry.setExpireTime(expireTime);
237                         }
238                     } else {
239                         entry.computeExpireTime(timeout);
240                     }
241
242                     oldEntry = (HttpCacheEntry)cache.put(key, entry,
243                                                          entry.getSize());
244                     cache.notifyRefresh(index);
245
246                     // transmit the response body content
247
writeBody(entry, response);
248                 } else {
249                     /** either there was an error or response from this
250                      * resource is not cacheable anymore; so, remove the
251                      * old entry from the cache.
252                      */

253                     oldEntry = (HttpCacheEntry)cache.remove(key);
254                 }
255
256                 // clear the wrapper (XXX: cache these??)
257
wrapper.clear();
258             }
259
260             /** clear the old entry?
261              * may lead to NPEs with one thread that just replaced an entry
262              * which might have just obtained before the new entry is put
263              * in cache. Must implement some sort of ref count or leave it
264              * to garbage collector
265              * if (oldEntry != null)
266              * oldEntry.clear();
267              */

268         } else {
269             if (_isTraceEnabled) {
270                 _logger.fine("CachingFilter " + request.getServletPath() +
271                                 " pass thru; isEnabled = " + isEnabled);
272             }
273             request.removeAttribute(DefaultCacheHelper.ATTR_CACHING_FILTER_NAME);
274             request.removeAttribute(CacheHelper.ATTR_CACHE_MAPPED_SERVLET_NAME);
275             request.removeAttribute(CacheHelper.ATTR_CACHE_MAPPED_URL_PATTERN);
276
277             // pass thru
278
chain.doFilter(srequest, sresponse);
279         }
280     }
281
282     /**
283      * called by doFilter to send out the cached response
284      * @param entry cached response entry
285      * @param response response object to write out the response
286      * @throws IOException and ServletException.
287      */

288     private void sendCachedResponse(HttpCacheEntry entry,
289                                     HttpServletResponse JavaDoc response)
290                                     throws IOException JavaDoc {
291         // status code/message
292
if (entry.statusCode != HttpCacheEntry.VALUE_NOT_SET) {
293             response.setStatus(entry.statusCode);
294         }
295
296         // set the outbound response headers
297
for (Iterator JavaDoc iter = entry.responseHeaders.keySet().iterator();
298                                                          iter.hasNext(); ) {
299             String JavaDoc name = (String JavaDoc)iter.next();
300             ArrayList JavaDoc values = (ArrayList JavaDoc)entry.responseHeaders.get(name);
301
302             for (int i = 0; i < values.size(); i++) {
303                 response.addHeader(name, (String JavaDoc)values.get(i));
304             }
305         }
306
307         // date headers
308
for (Iterator JavaDoc iter = entry.dateHeaders.keySet().iterator();
309                                                     iter.hasNext(); ) {
310             String JavaDoc name = (String JavaDoc)iter.next();
311             ArrayList JavaDoc values = (ArrayList JavaDoc)entry.dateHeaders.get(name);
312
313             for (int i = 0; i < values.size(); i++) {
314                 response.addDateHeader(name, ((Long JavaDoc)values.get(i)).longValue());
315             }
316         }
317
318         // cookies
319
for (int i = 0; i < entry.cookies.size(); i++) {
320             response.addCookie((Cookie JavaDoc)entry.cookies.get(i));
321         }
322
323         // content type, length and locale
324
if (entry.contentLength != HttpCacheEntry.VALUE_NOT_SET) {
325             response.setContentLength(entry.contentLength);
326         }
327         if (entry.contentType != null) {
328             response.setContentType(entry.contentType);
329         }
330         if (entry.locale != null) {
331             response.setLocale(entry.locale);
332         }
333
334         // the response body
335
writeBody(entry, response);
336     }
337
338     /**
339      * called by doFilter/sendCachedResponse to write the body content
340      * @param entry cached response entry
341      * @param response response object to write out the response
342      * @throws IOException and ServletException.
343      */

344     private void writeBody(HttpCacheEntry entry,
345                            HttpServletResponse JavaDoc response)
346                            throws IOException JavaDoc {
347         ServletOutputStream JavaDoc out = response.getOutputStream();
348         out.write(entry.bytes);
349     }
350
351     /**
352      * cache manager listener method
353      */

354     public void cacheManagerEnabled() {
355         if (_isTraceEnabled)
356             _logger.fine("CachingFilter " + filterName + " received cacheManager enabled event.");
357
358         this.isEnabled = true;
359     }
360
361     /**
362      * cache manager listener method
363      */

364     public void cacheManagerDisabled() {
365         if (_isTraceEnabled)
366             _logger.fine("CachingFilter " + filterName + " received cacheManager disabled event.");
367
368         this.isEnabled = false;
369     }
370
371     /**
372      * Called by the web container to indicate to a filter that it is being
373      * taken out of service. This method is only called once all threads
374      * within the filter's doFilter method have exited or after a timeout
375      * period has passed.
376      * After the web container calls this method, it will not call the doFilter
377      * method again on this instance of the filter.
378      */

379     public void destroy() {
380     }
381 }
382
Popular Tags