cache.java

来自「一个不错的cache」· Java 代码 · 共 884 行 · 第 1/3 页

JAVA
884
字号
/* * Copyright (c) 2002-2003 by OpenSymphony * All rights reserved. */package com.opensymphony.oscache.base;import com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache;import com.opensymphony.oscache.base.algorithm.LRUCache;import com.opensymphony.oscache.base.algorithm.UnlimitedCache;import com.opensymphony.oscache.base.events.*;import com.opensymphony.oscache.base.persistence.PersistenceListener;import com.opensymphony.oscache.util.FastCronParser;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import java.io.Serializable;import java.text.ParseException;import java.util.*;import javax.swing.event.EventListenerList;/** * Provides an interface to the cache itself. Creating an instance of this class * will create a cache that behaves according to its construction parameters. * The public API provides methods to manage objects in the cache and configure * any cache event listeners. * * @version        $Revision: 1.13.2.1 $ * @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="&#109;a&#105;&#108;&#116;&#111;:chris&#64;swebtec.&#99;&#111;&#109;">Chris Miller</a> */public class Cache implements Serializable {    /**     * An event that origininated from within another event.     */    public static final String NESTED_EVENT = "NESTED";    private static transient final Log log = LogFactory.getLog(Cache.class);    /**     * A list of all registered event listeners for this cache.     */    protected EventListenerList listenerList = new EventListenerList();    /**     * The actual cache map. This is where the cached objects are held.     */    private AbstractConcurrentReadCache cacheMap = null;    /**     * Date of last complete cache flush.     */    private Date flushDateTime = null;    /**     * A set that holds keys of cache entries that are currently being built.     * The cache checks against this map when a stale entry is requested.     * If the requested key is in here, we know the entry is currently being     * built by another thread and hence we can either block and wait or serve     * the stale entry (depending on whether cache blocking is enabled or not).     * <p>     * We need to isolate these here since the actual CacheEntry     * objects may not normally be held in memory at all (eg, if no     * memory cache is configured).     */    private Map updateStates = new HashMap();    /**     * Indicates whether the cache blocks requests until new content has     * been generated or just serves stale content instead.     */    private boolean blocking = false;    /**     * Create a new Cache     *     * @param useMemoryCaching Specify if the memory caching is going to be used     * @param unlimitedDiskCache Specify if the disk caching is unlimited     * @param overflowPersistence Specify if the persistent cache is used in overflow only mode     */    public Cache(boolean useMemoryCaching, boolean unlimitedDiskCache, boolean overflowPersistence) {        this(useMemoryCaching, unlimitedDiskCache, overflowPersistence, false, null, 0);    }    /**     * Create a new Cache.     *     * If a valid algorithm class is specified, it will be used for this cache.     * Otherwise if a capacity is specified, it will use LRUCache.     * If no algorithm or capacity is specified UnlimitedCache is used.     *     * @see com.opensymphony.oscache.base.algorithm.LRUCache     * @see com.opensymphony.oscache.base.algorithm.UnlimitedCache     * @param useMemoryCaching Specify if the memory caching is going to be used     * @param unlimitedDiskCache Specify if the disk caching is unlimited     * @param overflowPersistence Specify if the persistent cache is used in overflow only mode     * @param blocking This parameter takes effect when a cache entry has     * just expired and several simultaneous requests try to retrieve it. While     * one request is rebuilding the content, the other requests will either     * block and wait for the new content (<code>blocking == true</code>) or     * instead receive a copy of the stale content so they don't have to wait     * (<code>blocking == false</code>). the default is <code>false</code>,     * which provides better performance but at the expense of slightly stale     * data being served.     * @param algorithmClass The class implementing the desired algorithm     * @param capacity The capacity     */    public Cache(boolean useMemoryCaching, boolean unlimitedDiskCache, boolean overflowPersistence, boolean blocking, String algorithmClass, int capacity) {        // Instantiate the algo class if valid        if (((algorithmClass != null) && (algorithmClass.length() > 0)) && (capacity > 0)) {            try {                cacheMap = (AbstractConcurrentReadCache) Class.forName(algorithmClass).newInstance();                cacheMap.setMaxEntries(capacity);            } catch (Exception e) {                log.error("Invalid class name for cache algorithm class. " + e.toString());            }        }        if (cacheMap == null) {            // If we have a capacity, use LRU cache otherwise use unlimited Cache            if (capacity > 0) {                cacheMap = new LRUCache(capacity);            } else {                cacheMap = new UnlimitedCache();            }        }        cacheMap.setUnlimitedDiskCache(unlimitedDiskCache);        cacheMap.setOverflowPersistence(overflowPersistence);        cacheMap.setMemoryCaching(useMemoryCaching);        this.blocking = blocking;    }    /**     * Allows the capacity of the cache to be altered dynamically. Note that     * some cache implementations may choose to ignore this setting (eg the     * {@link UnlimitedCache} ignores this call).     *     * @param capacity the maximum number of items to hold in the cache.     */    public void setCapacity(int capacity) {        cacheMap.setMaxEntries(capacity);    }    /**     * Checks if the cache was flushed more recently than the CacheEntry provided.     * Used to determine whether to refresh the particular CacheEntry.     *     * @param cacheEntry The cache entry which we're seeing whether to refresh     * @return Whether or not the cache has been flushed more recently than this cache entry was updated.     */    public boolean isFlushed(CacheEntry cacheEntry) {        if (flushDateTime != null) {            long lastUpdate = cacheEntry.getLastUpdate();            return (flushDateTime.getTime() >= lastUpdate);        } else {            return false;        }    }    /**     * Retrieve an object from the cache specifying its key.     *     * @param key             Key of the object in the cache.     *     * @return The object from cache     *     * @throws NeedsRefreshException Thrown when the object either     * doesn't exist, or exists but is stale. When this exception occurs,     * the CacheEntry corresponding to the supplied key will be locked     * and other threads requesting this entry will potentially be blocked     * until the caller repopulates the cache. If the caller choses not     * to repopulate the cache, they <em>must</em> instead call     * {@link #cancelUpdate(String)}.     */    public Object getFromCache(String key) throws NeedsRefreshException {        return getFromCache(key, CacheEntry.INDEFINITE_EXPIRY, null);    }    /**     * Retrieve an object from the cache specifying its key.     *     * @param key             Key of the object in the cache.     * @param refreshPeriod   How long before the object needs refresh. To     * allow the object to stay in the cache indefinitely, supply a value     * of {@link CacheEntry#INDEFINITE_EXPIRY}.     *     * @return The object from cache     *     * @throws NeedsRefreshException Thrown when the object either     * doesn't exist, or exists but is stale. When this exception occurs,     * the CacheEntry corresponding to the supplied key will be locked     * and other threads requesting this entry will potentially be blocked     * until the caller repopulates the cache. If the caller choses not     * to repopulate the cache, they <em>must</em> instead call     * {@link #cancelUpdate(String)}.     */    public Object getFromCache(String key, int refreshPeriod) throws NeedsRefreshException {        return getFromCache(key, refreshPeriod, null);    }    /**     * Retrieve an object from the cache specifying its key.     *     * @param key             Key of the object in the cache.     * @param refreshPeriod   How long before the object needs refresh. To     * allow the object to stay in the cache indefinitely, supply a value     * of {@link CacheEntry#INDEFINITE_EXPIRY}.     * @param cronExpiry      A cron expression that specifies fixed date(s)     *                        and/or time(s) that this cache entry should     *                        expire on.     *     * @return The object from cache     *     * @throws NeedsRefreshException Thrown when the object either     * doesn't exist, or exists but is stale. When this exception occurs,     * the CacheEntry corresponding to the supplied key will be locked     * and other threads requesting this entry will potentially be blocked     * until the caller repopulates the cache. If the caller choses not     * to repopulate the cache, they <em>must</em> instead call     * {@link #cancelUpdate(String)}.     */    public Object getFromCache(String key, int refreshPeriod, String cronExpiry) throws NeedsRefreshException {        CacheEntry cacheEntry = this.getCacheEntry(key, null, null);        Object content = cacheEntry.getContent();        CacheMapAccessEventType accessEventType = CacheMapAccessEventType.HIT;        boolean reload = false;        // Check if this entry has expired or has not yet been added to the cache. If        // so, we need to decide whether to block, serve stale content or throw a        // NeedsRefreshException        if (this.isStale(cacheEntry, refreshPeriod, cronExpiry)) {            EntryUpdateState updateState = getUpdateState(key);            synchronized (updateState) {                if (updateState.isAwaitingUpdate() || updateState.isCancelled()) {                    // No one else is currently updating this entry - grab ownership                    updateState.startUpdate();                    if (cacheEntry.isNew()) {                        accessEventType = CacheMapAccessEventType.MISS;                    } else {                        accessEventType = CacheMapAccessEventType.STALE_HIT;                    }                } else if (updateState.isUpdating()) {                    // Another thread is already updating the cache. We block if this                    // is a new entry, or blocking mode is enabled. Either putInCache()                    // or cancelUpdate() can cause this thread to resume.                    if (cacheEntry.isNew() || blocking) {                        do {                            try {                                updateState.wait();                            } catch (InterruptedException e) {                            }                        } while (updateState.isUpdating());                        if (updateState.isCancelled()) {                            // The updating thread cancelled the update, let this one have a go.                            updateState.startUpdate();                            // We put the updateState object back into the updateStates map so                            // any remaining threads waiting on this cache entry will be notified                            // once this thread has done its thing (either updated the cache or                            // cancelled the update). Without this code they'll get left hanging...                            synchronized (updateStates) {                                updateStates.put(key, updateState);                            }                            if (cacheEntry.isNew()) {                                accessEventType = CacheMapAccessEventType.MISS;                            } else {                                accessEventType = CacheMapAccessEventType.STALE_HIT;                            }                        } else if (updateState.isComplete()) {                            reload = true;                        } else {                            log.error("Invalid update state for cache entry " + key);                        }                    }                } else {                    reload = true;                }            }        }        // If reload is true then another thread must have successfully rebuilt the cache entry        if (reload) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?