| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. The ASF licenses this file to You |
| * under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. For additional information regarding |
| * copyright in this work, please see the NOTICE file in the top level |
| * directory of this distribution. |
| */ |
| |
| package org.apache.roller.ui.rendering.util; |
| |
| import java.io.UnsupportedEncodingException; |
| import java.net.URLEncoder; |
| import java.util.Date; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.roller.config.RollerConfig; |
| import org.apache.roller.config.RollerRuntimeConfig; |
| import org.apache.roller.pojos.BookmarkData; |
| import org.apache.roller.pojos.CommentData; |
| import org.apache.roller.pojos.FolderData; |
| import org.apache.roller.pojos.RefererData; |
| import org.apache.roller.pojos.UserData; |
| import org.apache.roller.pojos.WeblogCategoryData; |
| import org.apache.roller.pojos.WeblogEntryData; |
| import org.apache.roller.pojos.WeblogTemplate; |
| import org.apache.roller.pojos.WebsiteData; |
| import org.apache.roller.util.Utilities; |
| import org.apache.roller.util.cache.Cache; |
| import org.apache.roller.util.cache.CacheHandler; |
| import org.apache.roller.util.cache.CacheManager; |
| import org.apache.roller.util.cache.ExpiringCacheEntry; |
| |
| |
| /** |
| * Cache for site-wide weblog content. |
| */ |
| public class SiteWideCache implements CacheHandler { |
| |
| private static Log log = LogFactory.getLog(SiteWideCache.class); |
| |
| // a unique identifier for this cache, this is used as the prefix for |
| // roller config properties that apply to this cache |
| public static final String CACHE_ID = "cache.sitewide"; |
| |
| // keep cached content |
| private boolean cacheEnabled = true; |
| private Cache contentCache = null; |
| |
| // keep a cached version of last expired time |
| private ExpiringCacheEntry lastUpdateTime = null; |
| private long timeout = 15 * 60 * 1000; |
| |
| // reference to our singleton instance |
| private static SiteWideCache singletonInstance = new SiteWideCache(); |
| |
| |
| private SiteWideCache() { |
| |
| cacheEnabled = RollerConfig.getBooleanProperty(CACHE_ID+".enabled"); |
| |
| Map cacheProps = new HashMap(); |
| cacheProps.put("id", CACHE_ID); |
| Enumeration allProps = RollerConfig.keys(); |
| String prop = null; |
| while(allProps.hasMoreElements()) { |
| prop = (String) allProps.nextElement(); |
| |
| // we are only interested in props for this cache |
| if(prop.startsWith(CACHE_ID+".")) { |
| cacheProps.put(prop.substring(CACHE_ID.length()+1), |
| RollerConfig.getProperty(prop)); |
| } |
| } |
| |
| log.info(cacheProps); |
| |
| if(cacheEnabled) { |
| contentCache = CacheManager.constructCache(this, cacheProps); |
| } else { |
| log.warn("Caching has been DISABLED"); |
| } |
| } |
| |
| |
| public static SiteWideCache getInstance() { |
| return singletonInstance; |
| } |
| |
| |
| public Object get(String key) { |
| |
| if(!cacheEnabled) |
| return null; |
| |
| Object entry = contentCache.get(key); |
| |
| if(entry == null) { |
| log.debug("MISS "+key); |
| } else { |
| log.debug("HIT "+key); |
| } |
| |
| return entry; |
| } |
| |
| |
| public void put(String key, Object value) { |
| |
| if(!cacheEnabled) |
| return; |
| |
| contentCache.put(key, value); |
| log.debug("PUT "+key); |
| } |
| |
| |
| public void remove(String key) { |
| |
| if(!cacheEnabled) |
| return; |
| |
| contentCache.remove(key); |
| log.debug("REMOVE "+key); |
| } |
| |
| |
| public void clear() { |
| |
| if(!cacheEnabled) |
| return; |
| |
| contentCache.clear(); |
| this.lastUpdateTime = null; |
| log.debug("CLEAR"); |
| } |
| |
| |
| public Date getLastModified() { |
| |
| Date lastModified = null; |
| |
| // first try our cached version |
| if(this.lastUpdateTime != null) { |
| lastModified = (Date) this.lastUpdateTime.getValue(); |
| } |
| |
| // still null, we need to get a fresh value |
| if(lastModified == null) { |
| lastModified = new Date(); |
| this.lastUpdateTime = new ExpiringCacheEntry(lastModified, this.timeout); |
| } |
| |
| return lastModified; |
| } |
| |
| |
| /** |
| * Generate a cache key from a parsed weblog page request. |
| * This generates a key of the form ... |
| * |
| * <handle>/<ctx>[/anchor][/language][/user] |
| * or |
| * <handle>/<ctx>[/weblogPage][/date][/category][/language][/user] |
| * |
| * |
| * examples ... |
| * |
| * foo/en |
| * foo/entry_anchor |
| * foo/20051110/en |
| * foo/MyCategory/en/user=myname |
| * |
| */ |
| public String generateKey(WeblogPageRequest pageRequest) { |
| |
| StringBuffer key = new StringBuffer(); |
| |
| key.append(this.CACHE_ID).append(":"); |
| key.append("page/"); |
| key.append(pageRequest.getWeblogHandle()); |
| |
| if(pageRequest.getWeblogAnchor() != null) { |
| String anchor = null; |
| try { |
| // may contain spaces or other bad chars |
| anchor = URLEncoder.encode(pageRequest.getWeblogAnchor(), "UTF-8"); |
| } catch(UnsupportedEncodingException ex) { |
| // ignored |
| } |
| |
| key.append("/entry/").append(anchor); |
| } else { |
| |
| if(pageRequest.getWeblogPageName() != null) { |
| key.append("/page/").append(pageRequest.getWeblogPageName()); |
| } |
| |
| if(pageRequest.getWeblogDate() != null) { |
| key.append("/").append(pageRequest.getWeblogDate()); |
| } |
| |
| if(pageRequest.getWeblogCategoryName() != null) { |
| String cat = null; |
| try { |
| // may contain spaces or other bad chars |
| cat = URLEncoder.encode(pageRequest.getWeblogCategoryName(), "UTF-8"); |
| } catch(UnsupportedEncodingException ex) { |
| // ignored |
| } |
| |
| key.append("/").append(cat); |
| } |
| } |
| |
| if(pageRequest.getLocale() != null) { |
| key.append("/").append(pageRequest.getLocale()); |
| } |
| |
| // add page number when applicable |
| if(pageRequest.getWeblogAnchor() == null) { |
| key.append("/page=").append(pageRequest.getPageNum()); |
| } |
| |
| // add login state |
| if(pageRequest.getAuthenticUser() != null) { |
| key.append("/user=").append(pageRequest.getAuthenticUser()); |
| } |
| |
| // we allow for arbitrary query params for custom pages |
| if(pageRequest.getCustomParams().size() > 0) { |
| String queryString = paramsToString(pageRequest.getCustomParams()); |
| |
| key.append("/qp=").append(queryString); |
| } |
| |
| return key.toString(); |
| } |
| |
| |
| /** |
| * Generate a cache key from a parsed weblog feed request. |
| * This generates a key of the form ... |
| * |
| * <handle>/<type>/<format>/[/category][/language][/excerpts] |
| * |
| * examples ... |
| * |
| * foo/entries/rss/en |
| * foo/comments/rss/MyCategory/en |
| * foo/entries/atom/en/excerpts |
| * |
| */ |
| public String generateKey(WeblogFeedRequest feedRequest) { |
| |
| StringBuffer key = new StringBuffer(); |
| |
| key.append(this.CACHE_ID).append(":"); |
| key.append("feed/"); |
| key.append(feedRequest.getWeblogHandle()); |
| |
| key.append("/").append(feedRequest.getType()); |
| key.append("/").append(feedRequest.getFormat()); |
| |
| if(feedRequest.getWeblogCategoryName() != null) { |
| String cat = feedRequest.getWeblogCategoryName(); |
| try { |
| cat = URLEncoder.encode(cat, "UTF-8"); |
| } catch (UnsupportedEncodingException ex) { |
| // should never happen, utf-8 is always supported |
| } |
| |
| key.append("/").append(cat); |
| } |
| |
| if(feedRequest.getLocale() != null) { |
| key.append("/").append(feedRequest.getLocale()); |
| } |
| |
| if(feedRequest.isExcerpts()) { |
| key.append("/excerpts"); |
| } |
| |
| return key.toString(); |
| } |
| |
| |
| /** |
| * A weblog entry has changed. |
| */ |
| public void invalidate(WeblogEntryData entry) { |
| |
| if(!cacheEnabled) |
| return; |
| |
| this.contentCache.clear(); |
| this.lastUpdateTime = null; |
| } |
| |
| |
| /** |
| * A weblog has changed. |
| */ |
| public void invalidate(WebsiteData website) { |
| |
| if(!cacheEnabled) |
| return; |
| |
| this.contentCache.clear(); |
| this.lastUpdateTime = null; |
| } |
| |
| |
| /** |
| * A bookmark has changed. |
| */ |
| public void invalidate(BookmarkData bookmark) { |
| if(RollerRuntimeConfig.isSiteWideWeblog(bookmark.getWebsite().getHandle())) { |
| invalidate(bookmark.getWebsite()); |
| } |
| } |
| |
| |
| /** |
| * A folder has changed. |
| */ |
| public void invalidate(FolderData folder) { |
| if(RollerRuntimeConfig.isSiteWideWeblog(folder.getWebsite().getHandle())) { |
| invalidate(folder.getWebsite()); |
| } |
| } |
| |
| |
| /** |
| * A comment has changed. |
| */ |
| public void invalidate(CommentData comment) { |
| if(RollerRuntimeConfig.isSiteWideWeblog(comment.getWeblogEntry().getWebsite().getHandle())) { |
| invalidate(comment.getWeblogEntry().getWebsite()); |
| } |
| } |
| |
| |
| /** |
| * A referer has changed. |
| */ |
| public void invalidate(RefererData referer) { |
| // ignored |
| } |
| |
| |
| /** |
| * A user profile has changed. |
| */ |
| public void invalidate(UserData user) { |
| // ignored |
| } |
| |
| |
| /** |
| * A category has changed. |
| */ |
| public void invalidate(WeblogCategoryData category) { |
| if(RollerRuntimeConfig.isSiteWideWeblog(category.getWebsite().getHandle())) { |
| invalidate(category.getWebsite()); |
| } |
| } |
| |
| |
| /** |
| * A weblog template has changed. |
| */ |
| public void invalidate(WeblogTemplate template) { |
| if(RollerRuntimeConfig.isSiteWideWeblog(template.getWebsite().getHandle())) { |
| invalidate(template.getWebsite()); |
| } |
| } |
| |
| |
| private String paramsToString(Map map) { |
| |
| if(map == null) { |
| return null; |
| } |
| |
| StringBuffer string = new StringBuffer(); |
| |
| String key = null; |
| String[] value = null; |
| Iterator keys = map.keySet().iterator(); |
| while(keys.hasNext()) { |
| key = (String) keys.next(); |
| value = (String[]) map.get(key); |
| |
| if(value != null) { |
| string.append(",").append(key).append("=").append(value[0]); |
| } |
| } |
| |
| return Utilities.toBase64(string.toString().substring(1).getBytes()); |
| } |
| |
| } |