cachetag.java
来自「oscache-2.4.1-full」· Java 代码 · 共 825 行 · 第 1/2 页
JAVA
825 行
/*
* Copyright (c) 2002-2003 by OpenSymphony
* All rights reserved.
*/
package com.opensymphony.oscache.web.tag;
import com.opensymphony.oscache.base.Cache;
import com.opensymphony.oscache.base.NeedsRefreshException;
import com.opensymphony.oscache.util.StringUtil;
import com.opensymphony.oscache.web.ServletCacheAdministrator;
import com.opensymphony.oscache.web.WebEntryRefreshPolicy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.TryCatchFinally;
/**
* CacheTag is a tag that allows for server-side caching of post-processed JSP content.<p>
*
* It also gives great programatic control over refreshing, flushing and updating the cache.<p>
*
* Usage Example:
* <pre><code>
* <%@ taglib uri="oscache" prefix="cache" %>
* <cache:cache key="mycache"
* scope="application"
* refresh="false"
* time="30">
* jsp content here... refreshed every 30 seconds
* </cache:cache>
* </code></pre>
*
* @author <a href="mailto:mike@atlassian.com">Mike Cannon-Brookes</a>
* @author <a href="mailto:tgochenour@peregrine.com">Todd Gochenour</a>
* @author <a href="mailto:fbeauregard@pyxis-tech.com">Francois Beauregard</a>
* @author <a href="mailto:abergevin@pyxis-tech.com">Alain Bergevin</a>
* @version $Revision: 331 $
*/
public class CacheTag extends BodyTagSupport implements TryCatchFinally {
/**
* Constants for time computation
*/
private final static int SECOND = 1;
private final static int MINUTE = 60 * SECOND;
private final static int HOUR = 60 * MINUTE;
private final static int DAY = 24 * HOUR;
private final static int WEEK = 7 * DAY;
private final static int MONTH = 30 * DAY;
private final static int YEAR = 365 * DAY;
/**
* The key under which the tag counter will be stored in the request
*/
private final static String CACHE_TAG_COUNTER_KEY = "__oscache_tag_counter";
/**
* Constants for refresh time
*/
final static private int ONE_MINUTE = 60;
final static private int ONE_HOUR = 60 * ONE_MINUTE;
final static private int DEFAULT_TIMEOUT = ONE_HOUR;
private static transient Log log = LogFactory.getLog(CacheTag.class);
/**
* Cache modes
*/
final static private int SILENT_MODE = 1;
/**
* A flag to indicate whether a NeedsRefreshException was thrown and
* the update needs to be cancelled
*/
boolean cancelUpdateRequired = false;
private Cache cache = null;
/**
* If no groups are specified, the cached content does not get put into any groups
*/
private List groups = null;
private ServletCacheAdministrator admin = null;
/**
* The actual key to use. This is generated based on the supplied key, scope etc.
*/
private String actualKey = null;
/**
* The content that was retrieved from cache
*/
private String content = null;
/**
* The cron expression that is used to expire cache entries at specific dates and/or times.
*/
private String cron = null;
/**
* if cache key is null, the request URI is used
*/
private String key = null;
/**
* The ISO-639 language code to distinguish different pages in application scope
*/
private String language = null;
/**
* Class used to handle the refresh policy logic
*/
private String refreshPolicyClass = null;
/**
* Parameters that will be passed to the init method of the
* refresh policy instance.
*/
private String refreshPolicyParam = null;
/**
* Whether the cache should be refreshed instantly
*/
private boolean refresh = false;
/**
* used for subtags to tell this tag that we should use the cached version
*/
private boolean useBody = true;
/**
* The cache mode. Valid values are SILENT_MODE
*/
private int mode = 0;
/**
* The cache scope to use
*/
private int scope = PageContext.APPLICATION_SCOPE;
/**
* time (in seconds) before cache should be refreshed
*/
private int time = DEFAULT_TIMEOUT;
/**
* Set the time this cache entry will be cached for. A date and/or time in
* either ISO-8601 format or a simple format can be specified. The acceptable
* syntax for the simple format can be any one of the following:
*
* <ul>
* <li>0 (seconds)
* <li>0s (seconds)
* <li>0m (minutes)
* <li>0h (hours)
* <li>0d (days)
* <li>0w (weeks)
* </ul>
*
* @param duration The duration to cache this content (using either the simple
* or the ISO-8601 format). Passing in a duration of zero will turn off the
* caching, while a negative value will result in the cached content never
* expiring (ie, the cached content will always be served as long as it is
* present).
*/
public void setDuration(String duration) {
try {
// Try Simple Date Format Duration first because it's faster
this.time = parseDuration(duration);
} catch (Exception ex) {
if (log.isDebugEnabled()) {
log.debug("Failed parsing simple duration format '" + duration + "' (" + ex.getMessage() + "). Trying ISO-8601 format...");
}
try {
// Try ISO-8601 Duration
this.time = parseISO_8601_Duration(duration);
} catch (Exception ex1) {
// An invalid duration entered, not much impact.
// The default timeout will be used
log.warn("The requested cache duration '" + duration + "' is invalid (" + ex1.getMessage() + "). Reverting to the default timeout");
this.time = DEFAULT_TIMEOUT;
}
}
}
/**
* Sets the cron expression that should be used to expire content at specific
* dates and/or times.
*/
public void setCron(String cron) {
this.cron = cron;
}
/**
* Sets the groups for this cache entry. Any existing groups will
* be replaced.
*
* @param groups A comma-delimited list of groups that the cache entry belongs to.
*/
public void setGroups(String groups) {
// FIXME: ArrayList doesn't avoid duplicates
this.groups = StringUtil.split(groups, ',');
}
/**
* Adds to the groups for this cache entry.
*
* @param group A group to which the cache entry should belong.
*/
void addGroup(String group) {
if (groups == null) {
// FIXME: ArrayList doesn't avoid duplicates
groups = new ArrayList();
}
groups.add(group);
}
/**
* Adds comma-delimited list of groups that the cache entry belongs to.
*
* @param groups A comma-delimited list of groups that the cache entry belongs to also.
*/
void addGroups(String groupsString) {
if (groups == null) {
// FIXME: ArrayList doesn't avoid duplicates
groups = new ArrayList();
}
groups.addAll(StringUtil.split(groupsString, ','));
}
/**
* Set the key for this cache entry.
*
* @param key The key for this cache entry.
*/
public void setKey(String key) {
this.key = key;
}
/**
* Set the ISO-639 language code to distinguish different pages in application scope
*
* @param language The language code for this cache entry.
*/
public void setLanguage(String language) {
this.language = language;
}
/**
* This method allows the user to programatically decide whether the cached
* content should be refreshed immediately.
*
* @param refresh Whether or not to refresh this cache entry immediately.
*/
public void setRefresh(boolean refresh) {
this.refresh = refresh;
}
/**
* Setting this to <code>true</code> prevents the cache from writing any output
* to the response, however the JSP content is still cached as normal.
* @param mode The cache mode to use.
*/
public void setMode(String mode) {
if ("silent".equalsIgnoreCase(mode)) {
this.mode = SILENT_MODE;
} else {
this.mode = 0;
}
}
/**
* Class used to handle the refresh policy logic
*/
public void setRefreshpolicyclass(String refreshPolicyClass) {
this.refreshPolicyClass = refreshPolicyClass;
}
/**
* Parameters that will be passed to the init method of the
* refresh policy instance.
*/
public void setRefreshpolicyparam(String refreshPolicyParam) {
this.refreshPolicyParam = refreshPolicyParam;
}
// ----------- setMethods ------------------------------------------------------
/**
* Set the scope of this cache.
* <p>
* @param scope The scope of this cache. Either "application" (default) or "session".
*/
public void setScope(String scope) {
if (scope.equalsIgnoreCase(ServletCacheAdministrator.SESSION_SCOPE_NAME)) {
this.scope = PageContext.SESSION_SCOPE;
} else {
this.scope = PageContext.APPLICATION_SCOPE;
}
}
/**
* Set the time this cache entry will be cached for (in seconds)
*
* @param time The time to cache this content (in seconds). Passing in
* a time of zero will turn off the caching. A negative value for the
* time will result in the cached content never expiring (ie, the cached
* content will always be served if it is present)
*/
public void setTime(int time) {
this.time = time;
}
/**
* This controls whether or not the body of the tag is evaluated or used.<p>
*
* It is most often called by the <UseCached /> tag to tell this tag to
* use the cached content.
*
* @see UseCachedTag
* @param useBody Whether or not to use the cached content.
*/
public void setUseBody(boolean useBody) {
if (log.isDebugEnabled()) {
log.debug("<cache>: Set useBody to " + useBody);
}
this.useBody = useBody;
}
/**
* After the cache body, either update the cache, serve new cached content or
* indicate an error.
*
* @throws JspTagException The standard exception thrown.
* @return The standard BodyTag return.
*/
public int doAfterBody() throws JspTagException {
String body = null;
try {
// if we have a body, and we have not been told to use the cached version
if ((bodyContent != null) && (useBody || (time == 0)) && ((body = bodyContent.getString()) != null)) {
if ((time != 0) || (refreshPolicyClass != null)) {
// Instantiate custom refresh policy if needed
WebEntryRefreshPolicy policy = null;
if (refreshPolicyClass != null) {
try {
policy = (WebEntryRefreshPolicy) Class.forName(refreshPolicyClass).newInstance();
policy.init(actualKey, refreshPolicyParam);
} catch (Exception e) {
if (log.isInfoEnabled()) {
log.info("<cache>: Problem instantiating or initializing refresh policy : " + refreshPolicyClass);
}
}
}
if (log.isDebugEnabled()) {
log.debug("<cache>: Updating cache entry with new content : " + actualKey);
}
cancelUpdateRequired = false;
if ((groups == null) || groups.isEmpty()) {
cache.putInCache(actualKey, body, policy);
} else {
String[] groupArray = new String[groups.size()];
groups.toArray(groupArray);
cache.putInCache(actualKey, body, groupArray, policy, null);
}
}
}
// otherwise if we have been told to use the cached content and we have cached content
else {
if (!useBody && (content != null)) {
if (log.isInfoEnabled()) {
log.info("<cache>: Using cached version as instructed, useBody = false : " + actualKey);
}
body = content;
}
// either the cached entry is blank and a subtag has said don't useBody, or body is null
else {
if (log.isInfoEnabled()) {
log.info("<cache>: Missing cached content : " + actualKey);
}
body = "Missing cached content";
}
}
// Only display anything if we're not running in silent mode
if (mode != SILENT_MODE) {
bodyContent.clearBody();
bodyContent.write(body);
bodyContent.writeOut(bodyContent.getEnclosingWriter());
}
} catch (java.io.IOException e) {
throw new JspTagException("IO Error: " + e.getMessage());
}
return SKIP_BODY;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?