001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. The ASF licenses this file to You
004: * under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License. For additional information regarding
015: * copyright in this work, please see the NOTICE file in the top level
016: * directory of this distribution.
017: */
018:
019: package org.apache.roller.ui.rendering.util.cache;
020:
021: import java.io.UnsupportedEncodingException;
022: import java.net.URLEncoder;
023: import java.util.Enumeration;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.Map;
027: import java.util.Set;
028: import java.util.TreeSet;
029: import org.apache.commons.logging.Log;
030: import org.apache.commons.logging.LogFactory;
031: import org.apache.roller.config.RollerConfig;
032: import org.apache.roller.ui.rendering.util.WeblogPageRequest;
033: import org.apache.roller.util.Utilities;
034: import org.apache.roller.util.cache.Cache;
035: import org.apache.roller.util.cache.CacheManager;
036: import org.apache.roller.util.cache.LazyExpiringCacheEntry;
037:
038: /**
039: * Cache for weblog page content.
040: */
041: public class WeblogPageCache {
042:
043: private static Log log = LogFactory.getLog(WeblogPageCache.class);
044:
045: // a unique identifier for this cache, this is used as the prefix for
046: // roller config properties that apply to this cache
047: public static final String CACHE_ID = "cache.weblogpage";
048:
049: // keep cached content
050: private boolean cacheEnabled = true;
051: private Cache contentCache = null;
052:
053: // reference to our singleton instance
054: private static WeblogPageCache singletonInstance = new WeblogPageCache();
055:
056: private WeblogPageCache() {
057:
058: cacheEnabled = RollerConfig.getBooleanProperty(CACHE_ID
059: + ".enabled");
060:
061: Map cacheProps = new HashMap();
062: cacheProps.put("id", CACHE_ID);
063: Enumeration allProps = RollerConfig.keys();
064: String prop = null;
065: while (allProps.hasMoreElements()) {
066: prop = (String) allProps.nextElement();
067:
068: // we are only interested in props for this cache
069: if (prop.startsWith(CACHE_ID + ".")) {
070: cacheProps.put(prop.substring(CACHE_ID.length() + 1),
071: RollerConfig.getProperty(prop));
072: }
073: }
074:
075: log.info(cacheProps);
076:
077: if (cacheEnabled) {
078: contentCache = CacheManager
079: .constructCache(null, cacheProps);
080: } else {
081: log.warn("Caching has been DISABLED");
082: }
083: }
084:
085: public static WeblogPageCache getInstance() {
086: return singletonInstance;
087: }
088:
089: public Object get(String key, long lastModified) {
090:
091: if (!cacheEnabled)
092: return null;
093:
094: Object entry = null;
095:
096: LazyExpiringCacheEntry lazyEntry = (LazyExpiringCacheEntry) this .contentCache
097: .get(key);
098: if (lazyEntry != null) {
099: entry = lazyEntry.getValue(lastModified);
100:
101: if (entry != null) {
102: log.debug("HIT " + key);
103: } else {
104: log.debug("HIT-EXPIRED " + key);
105: }
106:
107: } else {
108: log.debug("MISS " + key);
109: }
110:
111: return entry;
112: }
113:
114: public void put(String key, Object value) {
115:
116: if (!cacheEnabled)
117: return;
118:
119: contentCache.put(key, new LazyExpiringCacheEntry(value));
120: log.debug("PUT " + key);
121: }
122:
123: public void remove(String key) {
124:
125: if (!cacheEnabled)
126: return;
127:
128: contentCache.remove(key);
129: log.debug("REMOVE " + key);
130: }
131:
132: public void clear() {
133:
134: if (!cacheEnabled)
135: return;
136:
137: contentCache.clear();
138: log.debug("CLEAR");
139: }
140:
141: /**
142: * Generate a cache key from a parsed weblog page request.
143: * This generates a key of the form ...
144: *
145: * <handle>/<ctx>[/anchor][/language][/user]
146: * or
147: * <handle>/<ctx>[/weblogPage][/date][/category][/language][/user]
148: *
149: *
150: * examples ...
151: *
152: * foo/en
153: * foo/entry_anchor
154: * foo/20051110/en
155: * foo/MyCategory/en/user=myname
156: *
157: */
158: public String generateKey(WeblogPageRequest pageRequest) {
159:
160: StringBuffer key = new StringBuffer();
161:
162: key.append(this .CACHE_ID).append(":");
163: key.append(pageRequest.getWeblogHandle());
164:
165: if (pageRequest.getWeblogAnchor() != null) {
166:
167: String anchor = null;
168: try {
169: // may contain spaces or other bad chars
170: anchor = URLEncoder.encode(pageRequest
171: .getWeblogAnchor(), "UTF-8");
172: } catch (UnsupportedEncodingException ex) {
173: // ignored
174: }
175:
176: key.append("/entry/").append(anchor);
177: } else {
178:
179: if (pageRequest.getWeblogPageName() != null) {
180: key.append("/page/").append(
181: pageRequest.getWeblogPageName());
182: }
183:
184: if (pageRequest.getWeblogDate() != null) {
185: key.append("/").append(pageRequest.getWeblogDate());
186: }
187:
188: if (pageRequest.getWeblogCategoryName() != null) {
189: String cat = null;
190: try {
191: // may contain spaces or other bad chars
192: cat = URLEncoder.encode(pageRequest
193: .getWeblogCategoryName(), "UTF-8");
194: } catch (UnsupportedEncodingException ex) {
195: // ignored
196: }
197:
198: key.append("/").append(cat);
199: }
200:
201: if (pageRequest.getTags() != null
202: && pageRequest.getTags().size() > 0) {
203: Set ordered = new TreeSet(pageRequest.getTags());
204: String[] tags = (String[]) ordered
205: .toArray(new String[ordered.size()]);
206: key.append("/tags/").append(
207: Utilities.stringArrayToString(tags, "+"));
208: }
209: }
210:
211: if (pageRequest.getLocale() != null) {
212: key.append("/").append(pageRequest.getLocale());
213: }
214:
215: // add page number when applicable
216: if (pageRequest.getWeblogAnchor() == null) {
217: key.append("/page=").append(pageRequest.getPageNum());
218: }
219:
220: // add login state
221: if (pageRequest.getAuthenticUser() != null) {
222: key.append("/user=").append(pageRequest.getAuthenticUser());
223: }
224:
225: // we allow for arbitrary query params for custom pages
226: if (pageRequest.getWeblogPageName() != null
227: && pageRequest.getCustomParams().size() > 0) {
228: String queryString = paramsToString(pageRequest
229: .getCustomParams());
230:
231: key.append("/qp=").append(queryString);
232: }
233:
234: return key.toString();
235: }
236:
237: private String paramsToString(Map map) {
238:
239: if (map == null) {
240: return null;
241: }
242:
243: StringBuffer string = new StringBuffer();
244:
245: String key = null;
246: String[] value = null;
247: Iterator keys = map.keySet().iterator();
248: while (keys.hasNext()) {
249: key = (String) keys.next();
250: value = (String[]) map.get(key);
251:
252: if (value != null) {
253: string.append(",").append(key).append("=").append(
254: value[0]);
255: }
256: }
257:
258: return Utilities.toBase64(string.toString().substring(1)
259: .getBytes());
260: }
261:
262: }
|