KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > roller > presentation > pagecache > rollercache > LRUCacheHandler2


1 /*
2  * Created on Jun 15, 2004
3  */

4 package org.roller.presentation.pagecache.rollercache;
5 import java.io.IOException JavaDoc;
6 import java.util.Iterator JavaDoc;
7 import java.util.Locale JavaDoc;
8 import java.util.Map JavaDoc;
9 import java.util.Set JavaDoc;
10 import java.util.TreeMap JavaDoc;
11
12 import javax.servlet.FilterChain JavaDoc;
13 import javax.servlet.FilterConfig JavaDoc;
14 import javax.servlet.ServletException JavaDoc;
15 import javax.servlet.ServletRequest JavaDoc;
16 import javax.servlet.ServletResponse JavaDoc;
17 import javax.servlet.http.HttpServletRequest JavaDoc;
18 import javax.servlet.http.HttpServletResponse JavaDoc;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.roller.config.RollerConfig;
23 import org.roller.pojos.UserData;
24 import org.roller.presentation.LanguageUtil;
25 import org.roller.presentation.pagecache.FilterHandler;
26 import org.roller.util.LRUCache2;
27
28 /**
29  * Page cache implementation that uses a simple LRUCache. Can be configured
30  * using filter configuration parameters:
31  * <ul>
32  * <li><b>size</b>: number of pages to keep in cache. Once cache reaches
33  * this size, each new cache entry will push out the LRU cache entry.</li>
34  * <li><b>timeout</b>: interval to timeout pages in milliseconds.</li>
35  * </ul>
36  * @author David M Johnson
37  */

38 public class LRUCacheHandler2 implements FilterHandler
39 {
40     private static Log mLogger =
41         LogFactory.getFactory().getInstance(LRUCacheHandler2.class);
42
43     private LRUCache2 mPageCache = null;
44     private String JavaDoc mName = null;
45     
46     // Statistics
47
private int misses = 0;
48     private int hits = 0;
49     
50     private final static String JavaDoc FILE_SEPARATOR = "/";
51     private final static char FILE_SEPARATOR_CHAR = FILE_SEPARATOR.charAt(0);
52     private final static short AVERAGE_KEY_LENGTH = 30;
53     private static final String JavaDoc m_strBase64Chars =
54         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
55
56     public LRUCacheHandler2(FilterConfig JavaDoc config)
57     {
58         mName = config.getFilterName();
59         mLogger.info("Initializing for: " + mName);
60         
61         String JavaDoc cacheSize = RollerConfig.getProperty("cache.filter.page.size");
62         String JavaDoc cacheTimeout = RollerConfig.getProperty("cache.filter.page.timeout");
63         
64         int size = 200;
65         try
66         {
67             size = Integer.parseInt(cacheSize);
68         }
69         catch (Exception JavaDoc e)
70         {
71             mLogger.warn(config.getFilterName()
72                 + "Can't read cache size parameter, using default...");
73         }
74         mLogger.info(mName + " size=" + size);
75
76         long timeout = 30000;
77         try
78         {
79             timeout = Long.parseLong(cacheTimeout);
80         }
81         catch (Exception JavaDoc e)
82         {
83             mLogger.warn(config.getFilterName()
84                 + "Can't read timeout parameter, using default.");
85         }
86         mLogger.info(mName + " timeout=" + timeout + "seconds");
87         mPageCache = new LRUCache2(size, timeout*1000);
88     }
89     
90     /**
91      * @see org.roller.presentation.pagecache.FilterHandler#destroy()
92      */

93     public void destroy()
94     {
95     }
96
97     /**
98      * @see org.roller.presentation.pagecache.FilterHandler#doFilter(
99      * javax.servlet.ServletRequest, javax.servlet.ServletResponse,
100      * javax.servlet.FilterChain)
101      */

102     public void doFilter(ServletRequest JavaDoc req, ServletResponse JavaDoc res,
103                     FilterChain JavaDoc chain) throws ServletException JavaDoc, IOException JavaDoc
104     {
105         HttpServletRequest JavaDoc request = (HttpServletRequest JavaDoc) req;
106         HttpServletResponse JavaDoc response = (HttpServletResponse JavaDoc) res;
107
108         // Get locale, needed for creating cache key.
109
// Unfortunately, getViewLocal works by parsing the request URL
110
// which we would like to avoid where possible.
111
Locale JavaDoc locale = null;
112         try
113         {
114             locale = LanguageUtil.getViewLocale(request);
115         }
116         catch (Exception JavaDoc e)
117         {
118             // An error parsjng the request is considered to be a 404
119
mLogger.debug("Unable determine view local from request");
120             response.sendError(HttpServletResponse.SC_NOT_FOUND);
121             return;
122         }
123         
124         // generate language-sensitive cache-key
125
String JavaDoc generatedKey = null;
126         if (locale != null)
127         {
128             generatedKey = generateEntryKey(null,
129                       request, 1, locale.getLanguage());
130         }
131         else
132         {
133             generatedKey = generateEntryKey(null,
134                       request, 1, null);
135         }
136         
137         // Add authenticated user name, if there is one, to cache key
138
java.security.Principal JavaDoc prince = request.getUserPrincipal();
139         StringBuffer JavaDoc keyb = new StringBuffer JavaDoc();
140         keyb.append(generatedKey);
141         if (prince != null)
142         {
143             keyb.append("_");
144             keyb.append(prince);
145         }
146         String JavaDoc key = keyb.toString();
147                 
148         ResponseContent respContent = (ResponseContent)getFromCache(key);
149         if (respContent == null)
150         {
151             try
152             {
153                 CacheHttpServletResponseWrapper cacheResponse =
154                     new CacheHttpServletResponseWrapper(response);
155                 
156                 chain.doFilter(request, cacheResponse);
157                 cacheResponse.flushBuffer();
158
159                 // Store as the cache content the result of the response
160
// if no exception was noted by content generator.
161
if (request.getAttribute("DisplayException") == null)
162                 {
163                     ResponseContent rc = cacheResponse.getContent();
164                     putToCache(key, rc);
165                 }
166                 else
167                 {
168                     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
169                     sb.append("Display exception, cache, key=");
170                     sb.append(key);
171                     mLogger.error(sb.toString());
172                 }
173             }
174             catch (java.net.SocketException JavaDoc se)
175             {
176                 // ignore socket exceptions
177
}
178             catch (Exception JavaDoc e)
179             {
180                 // something unexpected and bad happened
181
StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
182                 sb.append("Error rendering page, key=");
183                 sb.append(key);
184                 mLogger.error(sb.toString());
185                 mLogger.debug(e);
186             }
187         }
188         else
189         {
190             try
191             {
192                 respContent.writeTo(response);
193             }
194             catch (java.net.SocketException JavaDoc se)
195             {
196                 // ignore socket exceptions
197
}
198             catch (Exception JavaDoc e)
199             {
200                 if (mLogger.isDebugEnabled())
201                 {
202                     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
203                     sb.append("Probably a client abort exception, key=");
204                     sb.append(key);
205                     mLogger.error(sb.toString());
206                 }
207             }
208             
209         }
210     }
211
212     /**
213      * Purge entire cache.
214      */

215     public synchronized void flushCache(HttpServletRequest JavaDoc req)
216     {
217         mPageCache.purge();
218     }
219
220     /**
221      * Purge user's entries from cache.
222      */

223     public synchronized void removeFromCache(HttpServletRequest JavaDoc req, UserData user)
224     {
225         // TODO: can we make this a little more precise, perhaps via regex?
226
String JavaDoc rssString = "/rss/" + user.getUserName(); // user's pages
227
String JavaDoc pageString = "/page/" + user.getUserName(); // user's RSS feeds
228
String JavaDoc mainRssString = "/rss_"; // main RSS feed
229
String JavaDoc mainPageString = "/main.do"; // main page
230
String JavaDoc planetPageString = "/planet.do"; // planet page
231

232         int beforeSize = mPageCache.size();
233         mPageCache.purge(new String JavaDoc[]
234         {
235             rssString,
236             pageString,
237             mainRssString,
238             mainPageString,
239             planetPageString
240         });
241         int afterSize = mPageCache.size();
242         
243         if (mLogger.isDebugEnabled())
244         {
245             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
246             sb.append("Purged, count=");
247             sb.append(beforeSize - afterSize);
248             sb.append(", user=");
249             sb.append(user.getUserName());
250             mLogger.debug(sb.toString());
251         }
252     }
253     
254     /**
255      * Get from cache. Synchronized because "In access-ordered linked hash
256      * maps, merely querying the map with get is a structural modification"
257      */

258     public synchronized Object JavaDoc getFromCache(String JavaDoc key)
259     {
260         Object JavaDoc entry = mPageCache.get(key);
261         
262         if (entry != null && mLogger.isDebugEnabled())
263         {
264             hits++;
265         }
266         return entry;
267     }
268
269     public synchronized void putToCache(String JavaDoc key, Object JavaDoc entry)
270     {
271         mPageCache.put(key, entry);
272         if (mLogger.isDebugEnabled())
273         {
274             misses++;
275             
276             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
277             sb.append("Missed, cache size=");
278             sb.append(mPageCache.size());
279             sb.append(", hits=");
280             sb.append(hits);
281             sb.append(", misses=");
282             sb.append(misses);
283             sb.append(", key=");
284             sb.append(key);
285             mLogger.debug(sb.toString());
286         }
287     }
288
289     public String JavaDoc generateEntryKey(String JavaDoc key,
290                        HttpServletRequest JavaDoc request, int scope, String JavaDoc language)
291     {
292         StringBuffer JavaDoc cBuffer = new StringBuffer JavaDoc(AVERAGE_KEY_LENGTH);
293         // Append the language if available
294
if (language != null)
295         {
296             cBuffer.append(FILE_SEPARATOR).append(language);
297         }
298         
299         //cBuffer.append(FILE_SEPARATOR).append(request.getServerName());
300

301         if (key != null)
302         {
303             cBuffer.append(FILE_SEPARATOR).append(key);
304         }
305         else
306         {
307             String JavaDoc generatedKey = request.getRequestURI();
308             if (generatedKey.charAt(0) != FILE_SEPARATOR_CHAR)
309             {
310                 cBuffer.append(FILE_SEPARATOR_CHAR);
311             }
312             cBuffer.append(generatedKey);
313             cBuffer.append("_").append(request.getMethod()).append("_");
314             generatedKey = getSortedQueryString(request);
315             if (generatedKey != null)
316             {
317                 try
318                 {
319                     java.security.MessageDigest JavaDoc digest =
320                         java.security.MessageDigest.getInstance("MD5");
321                     byte[] b = digest.digest(generatedKey.getBytes());
322                     cBuffer.append("_");
323                     // Base64 encoding allows for unwanted slash characters.
324
cBuffer.append(toBase64(b).replace('/', '_'));
325                 }
326                 catch (Exception JavaDoc e)
327                 {
328                     // Ignore query string
329
}
330             }
331         }
332         return cBuffer.toString();
333     }
334
335     protected String JavaDoc getSortedQueryString(HttpServletRequest JavaDoc request)
336     {
337         Map JavaDoc paramMap = request.getParameterMap();
338         if (paramMap.isEmpty())
339         {
340             return null;
341         }
342         Set JavaDoc paramSet = new TreeMap JavaDoc(paramMap).entrySet();
343         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
344         boolean first = true;
345         for (Iterator JavaDoc it = paramSet.iterator(); it.hasNext();)
346         {
347             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
348             String JavaDoc[] values = (String JavaDoc[]) entry.getValue();
349             for (int i = 0; i < values.length; i++)
350             {
351                 String JavaDoc key = (String JavaDoc) entry.getKey();
352                 if ((key.length() != 10) || !"jsessionid".equals(key))
353                 {
354                     if (first)
355                     {
356                         first = false;
357                     }
358                     else
359                     {
360                         buf.append('&');
361                     }
362                     buf.append(key).append('=').append(values[i]);
363                 }
364             }
365         }
366         // We get a 0 length buffer if the only parameter was a jsessionid
367
if (buf.length() == 0)
368         {
369             return null;
370         }
371         else
372         {
373             return buf.toString();
374         }
375     }
376
377     /**
378      * Convert a byte array into a Base64 string (as used in mime formats)
379      */

380     private static String JavaDoc toBase64(byte[] aValue)
381     {
382         int byte1;
383         int byte2;
384         int byte3;
385         int iByteLen = aValue.length;
386         StringBuffer JavaDoc tt = new StringBuffer JavaDoc();
387         for (int i = 0; i < iByteLen; i += 3)
388         {
389             boolean bByte2 = (i + 1) < iByteLen;
390             boolean bByte3 = (i + 2) < iByteLen;
391             byte1 = aValue[i] & 0xFF;
392             byte2 = (bByte2) ? (aValue[i + 1] & 0xFF) : 0;
393             byte3 = (bByte3) ? (aValue[i + 2] & 0xFF) : 0;
394             tt.append(m_strBase64Chars.charAt(byte1 / 4));
395             tt.append(m_strBase64Chars.charAt((byte2 / 16)
396                             + ((byte1 & 0x3) * 16)));
397             tt.append(((bByte2) ? m_strBase64Chars.charAt((byte3 / 64)
398                             + ((byte2 & 0xF) * 4)) : '='));
399             tt.append(((bByte3) ? m_strBase64Chars.charAt(byte3 & 0x3F) : '='));
400         }
401         return tt.toString();
402     }
403 }
404
Popular Tags