package onlinefrontlines.taglib; import onlinefrontlines.Constants; import onlinefrontlines.web.WebAction; import java.util.HashMap; import java.util.HashSet; import java.io.IOException; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import net.sf.ehcache.*; import net.sf.ehcache.constructs.blocking.*; /** * Tag to cache jsp generated data * * @author jorrit * * Copyright (C) 2009-2013 Jorrit Rouwe * * This file is part of Online Frontlines. * * Online Frontlines is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Online Frontlines is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Online Frontlines. If not, see <http://www.gnu.org/licenses/>. */ public class CacheTag extends BodyTagSupport { public static final long serialVersionUID = 0; /** * Singleton cache */ private static BlockingCache cache; /** * Mapping of groups to keys */ private static HashMap<String, HashSet<String>> groups = new HashMap<String, HashSet<String>>(); /** * Init singleton cache */ public static void initCache() { cache = new BlockingCache(CacheManager.getInstance().getCache("CacheTag")); } /** * Shutdown singleton cache */ public static void shutdownCache() { cache = null; } /** * Remove everything from the cache */ public static void purgeAll() { cache.removeAll(); } /** * Remove an element from the tag cache * * @param key key attribute used on tag * @param keyExp keyExp attribute used on tag * @param userId user id associated with the tag (or 0 if none) */ public static void purgeElement(String key, String keyExp, int userId) { String k = key; if (keyExp != null && !keyExp.isEmpty()) k += "_" + keyExp; if (userId != 0) k += "_" + userId; cache.remove(k); } /** * Remove everything that belongs to a group from the cache * * @param group group attribute used on tag * @param keyExp keyExp attribute used on tag * @param userId user id associated with the tag (or 0 if none) */ public static void purgeGroup(String group, String keyExp, int userId) { synchronized (groups) { HashSet<String> keySet = groups.get(group); if (keySet != null) for (String key : keySet) { purgeElement(key, keyExp, userId); } } } /** * Cache key */ private String key; public void setKey(String key) { this.key = key; } /** * Cache variable key expression */ private String keyExp; public void setKeyExp(String keyExp) { this.keyExp = keyExp; } /** * If the cache should be user specific */ private boolean cachePerUser = false; public void setCachePerUser(boolean cachePerUser) { this.cachePerUser = cachePerUser; } /** * How long this entry is valid (in seconds) */ private int timeToLiveSeconds = 120; public void setTimeToLiveSeconds(int timeToLiveSeconds) { this.timeToLiveSeconds = timeToLiveSeconds; } /** * Group this tag belongs to (for purging elements from the cache) */ private String group = null; public void setGroup(String group) { this.group = group; } /** * Calculated key */ private String calculatedKey; private void calculateKey() { // Start with specified key calculatedKey = key; // Add expression if (keyExp != null && !keyExp.isEmpty()) calculatedKey += "_" + keyExp; // Make user specific with user ID if requested if (cachePerUser) { WebAction action = (WebAction)pageContext.getRequest().getAttribute(Constants.CURRENT_ACTION); if (action.user != null) calculatedKey += "_" + action.user.id; } } public int doStartTag() throws JspTagException { // Calculate key calculateKey(); // Get element Element element; try { element = cache.get(calculatedKey); } catch (LockTimeoutException e) { throw new JspTagException(e); } if (element != null) { // Serve cached version try { pageContext.getOut().write((String)element.getObjectValue()); } catch (IOException e) { throw new JspTagException(e); } return SKIP_BODY; } else { // Eval body return EVAL_BODY_BUFFERED; } } public int doAfterBody() throws JspTagException { // Get content String content = getBodyContent().getString(); // Store in cache Element element = new Element(calculatedKey, content); element.setTimeToLive(timeToLiveSeconds); cache.put(element); // Store group if (group != null) { synchronized (groups) { HashSet<String> set = groups.get(group); if (set != null) { // Add to existing set set.add(key); } else { // Create new set set = new HashSet<String>(); set.add(key); groups.put(group, set); } } } // Serve newly cached version try { getPreviousOut().write(content); } catch (IOException e) { throw new JspTagException(e); } return SKIP_BODY; } }