0001: /**
0002: * Copyright 2003-2007 Luck Consulting Pty Ltd
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */package net.sf.ehcache;
0016:
0017: import edu.emory.mathcs.backport.java.util.concurrent.ExecutionException;
0018: import edu.emory.mathcs.backport.java.util.concurrent.Future;
0019: import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue;
0020: import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor;
0021: import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
0022: import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
0023: import net.sf.ehcache.config.CacheConfiguration;
0024: import net.sf.ehcache.event.CacheEventListener;
0025: import net.sf.ehcache.event.RegisteredEventListeners;
0026: import net.sf.ehcache.exceptionhandler.CacheExceptionHandler;
0027: import net.sf.ehcache.extension.CacheExtension;
0028: import net.sf.ehcache.loader.CacheLoader;
0029: import net.sf.ehcache.store.DiskStore;
0030: import net.sf.ehcache.store.MemoryStore;
0031: import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
0032: import net.sf.ehcache.store.Store;
0033: import org.apache.commons.logging.Log;
0034: import org.apache.commons.logging.LogFactory;
0035:
0036: import java.io.IOException;
0037: import java.io.Serializable;
0038: import java.net.InetAddress;
0039: import java.net.UnknownHostException;
0040: import java.rmi.server.UID;
0041: import java.util.ArrayList;
0042: import java.util.Arrays;
0043: import java.util.Collection;
0044: import java.util.Collections;
0045: import java.util.HashMap;
0046: import java.util.HashSet;
0047: import java.util.Iterator;
0048: import java.util.List;
0049: import java.util.Map;
0050: import java.util.Set;
0051:
0052: /**
0053: * Cache is the central class in ehcache. Caches have {@link Element}s and are managed
0054: * by the {@link CacheManager}. The Cache performs logical actions. It delegates physical
0055: * implementations to its {@link net.sf.ehcache.store.Store}s.
0056: * <p/>
0057: * A reference to a Cache can be obtained through the {@link CacheManager}. A Cache thus obtained
0058: * is guaranteed to have status {@link Status#STATUS_ALIVE}. This status is checked for any method which
0059: * throws {@link IllegalStateException} and the same thrown if it is not alive. This would normally
0060: * happen if a call is made after {@link CacheManager#shutdown} is invoked.
0061: * <p/>
0062: * Cache is threadsafe.
0063: * <p/>
0064: * Statistics on cache usage are collected and made available through the {@link #getStatistics()} methods.
0065: * <p/>
0066: * Various decorators are available for Cache, such as BlockingCache, SelfPopulatingCache and the dynamic proxy
0067: * ExceptionHandlingDynamicCacheProxy. See each class for details.
0068: *
0069: * @author Greg Luck
0070: * @version $Id: Cache.java 581 2008-02-20 10:09:03Z gregluck $
0071: */
0072: public class Cache implements Ehcache {
0073:
0074: /**
0075: * A reserved word for cache names. It denotes a default configuration
0076: * which is applied to caches created without configuration.
0077: */
0078: public static final String DEFAULT_CACHE_NAME = "default";
0079:
0080: /**
0081: * System Property based method of disabling ehcache. If disabled no elements will be added to a cache.
0082: * <p/>
0083: * Set the property "net.sf.ehcache.disabled=true" to disable ehcache.
0084: * <p/>
0085: * This can easily be done using <code>java -Dnet.sf.ehcache.disabled=true</code> in the command line.
0086: */
0087: public static final String NET_SF_EHCACHE_DISABLED = "net.sf.ehcache.disabled";
0088:
0089: {
0090: String value = System.getProperty(NET_SF_EHCACHE_DISABLED);
0091: if (value != null) {
0092: disabled = value.equalsIgnoreCase("true");
0093: }
0094: }
0095:
0096: /**
0097: * The default interval between runs of the expiry thread.
0098: */
0099: public static final long DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS = 120;
0100:
0101: /**
0102: * Set a buffer size for the spool of approx 30MB
0103: */
0104: private static final int DEFAULT_SPOOL_BUFFER_SIZE = 30;
0105:
0106: private static final Log LOG = LogFactory.getLog(Cache.class
0107: .getName());
0108:
0109: private static final MemoryStoreEvictionPolicy DEFAULT_MEMORY_STORE_EVICTION_POLICY = MemoryStoreEvictionPolicy.LRU;
0110:
0111: private static InetAddress localhost;
0112:
0113: /**
0114: * The amount of time to wait if a store gets backed up
0115: */
0116: private static final int BACK_OFF_TIME_MILLIS = 50;
0117:
0118: private static final int EXECUTOR_KEEP_ALIVE_TIME = 60000;
0119: private static final int EXECUTOR_MAXIMUM_POOL_SIZE = 10;
0120: private static final int EXECUTOR_CORE_POOL_SIZE = 0;
0121:
0122: static {
0123: try {
0124: localhost = InetAddress.getLocalHost();
0125: } catch (UnknownHostException e) {
0126: LOG.error(
0127: "Unable to set localhost. This prevents creation of a GUID. Cause was: "
0128: + e.getMessage(), e);
0129: }
0130: }
0131:
0132: private boolean disabled;
0133:
0134: private Store diskStore;
0135:
0136: private String diskStorePath;
0137:
0138: private Status status;
0139:
0140: private CacheConfiguration configuration;
0141:
0142: /**
0143: * Cache hit count.
0144: */
0145: private long hitCount;
0146:
0147: /**
0148: * Memory cache hit count.
0149: */
0150: private long memoryStoreHitCount;
0151:
0152: /**
0153: * DiskStore hit count.
0154: */
0155: private long diskStoreHitCount;
0156:
0157: /**
0158: * Count of misses where element was not found.
0159: */
0160: private long missCountNotFound;
0161:
0162: /**
0163: * Count of misses where element was expired.
0164: */
0165: private long missCountExpired;
0166:
0167: /**
0168: * The {@link MemoryStore} of this {@link Cache}. All caches have a memory store.
0169: */
0170: private MemoryStore memoryStore;
0171:
0172: private RegisteredEventListeners registeredEventListeners;
0173:
0174: private List registeredCacheExtensions;
0175:
0176: private String guid;
0177:
0178: private CacheManager cacheManager;
0179:
0180: private BootstrapCacheLoader bootstrapCacheLoader;
0181:
0182: private int statisticsAccuracy;
0183:
0184: private long totalGetTime;
0185:
0186: private CacheExceptionHandler cacheExceptionHandler;
0187:
0188: private CacheLoader cacheLoader;
0189:
0190: /**
0191: * A ThreadPoolExecutor which uses a thread pool to schedule loads in the order in which they are requested.
0192: * <p/>
0193: * Each cache has its own one of these, if required. Because the Core Thread Pool is zero, no threads
0194: * are used until actually needed. Threads are added to the pool up to a maximum of 10. The keep alive
0195: * time is 60 seconds, after which, if they are not required they will be stopped and collected.
0196: * <p/>
0197: * The executorService is only used for cache loading, and is created lazily on demand to avoid unnecessary resource
0198: * usage.
0199: * <p/>
0200: * Use {@link #getExecutorService()} to ensure that it is initialised.
0201: */
0202: private ThreadPoolExecutor executorService;
0203:
0204: /**
0205: * 1.0 Constructor.
0206: * <p/>
0207: * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
0208: * <p/>
0209: * A client can specify their own settings here and pass the {@link Cache} object
0210: * into {@link CacheManager#addCache} to specify parameters other than the defaults.
0211: * <p/>
0212: * Only the CacheManager can initialise them.
0213: * <p/>
0214: * This constructor creates disk stores, if specified, that do not persist between restarts.
0215: * <p/>
0216: * The default expiry thread interval of 120 seconds is used. This is the interval between runs
0217: * of the expiry thread, where it checks the disk store for expired elements. It is not the
0218: * the timeToLiveSeconds.
0219: *
0220: * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
0221: * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
0222: * @param overflowToDisk whether to use the disk store
0223: * @param eternal whether the elements in the cache are eternal, i.e. never expire
0224: * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
0225: * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
0226: * @since 1.0
0227: */
0228: public Cache(String name, int maxElementsInMemory,
0229: boolean overflowToDisk, boolean eternal,
0230: long timeToLiveSeconds, long timeToIdleSeconds) {
0231: this (name, maxElementsInMemory,
0232: DEFAULT_MEMORY_STORE_EVICTION_POLICY, overflowToDisk,
0233: null, eternal, timeToLiveSeconds, timeToIdleSeconds,
0234: false, DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS, null,
0235: null);
0236: }
0237:
0238: /**
0239: * 1.1 Constructor.
0240: * <p/>
0241: * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
0242: * <p/>
0243: * A client can specify their own settings here and pass the {@link Cache} object
0244: * into {@link CacheManager#addCache} to specify parameters other than the defaults.
0245: * <p/>
0246: * Only the CacheManager can initialise them.
0247: *
0248: * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
0249: * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
0250: * @param overflowToDisk whether to use the disk store
0251: * @param eternal whether the elements in the cache are eternal, i.e. never expire
0252: * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
0253: * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
0254: * @param diskPersistent whether to persist the cache to disk between JVM restarts
0255: * @param diskExpiryThreadIntervalSeconds
0256: * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
0257: * @since 1.1
0258: */
0259: public Cache(String name, int maxElementsInMemory,
0260: boolean overflowToDisk, boolean eternal,
0261: long timeToLiveSeconds, long timeToIdleSeconds,
0262: boolean diskPersistent, long diskExpiryThreadIntervalSeconds) {
0263: this (name, maxElementsInMemory,
0264: DEFAULT_MEMORY_STORE_EVICTION_POLICY, overflowToDisk,
0265: null, eternal, timeToLiveSeconds, timeToIdleSeconds,
0266: diskPersistent, diskExpiryThreadIntervalSeconds, null,
0267: null);
0268: LOG
0269: .warn("An API change between ehcache-1.1 and ehcache-1.2 results in the persistence path being set to java.io.tmpdir"
0270: + " when the ehcache-1.1 constructor is used. Please change to the 1.2 constructor");
0271: }
0272:
0273: /**
0274: * 1.2 Constructor
0275: * <p/>
0276: * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
0277: * <p/>
0278: * A client can specify their own settings here and pass the {@link Cache} object
0279: * into {@link CacheManager#addCache} to specify parameters other than the defaults.
0280: * <p/>
0281: * Only the CacheManager can initialise them.
0282: *
0283: * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
0284: * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
0285: * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
0286: * @param overflowToDisk whether to use the disk store
0287: * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
0288: * @param eternal whether the elements in the cache are eternal, i.e. never expire
0289: * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
0290: * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
0291: * @param diskPersistent whether to persist the cache to disk between JVM restarts
0292: * @param diskExpiryThreadIntervalSeconds
0293: * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
0294: * @param registeredEventListeners a notification service. Optionally null, in which case a new
0295: * one with no registered listeners will be created.
0296: * @since 1.2
0297: */
0298: public Cache(String name, int maxElementsInMemory,
0299: MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
0300: boolean overflowToDisk, String diskStorePath,
0301: boolean eternal, long timeToLiveSeconds,
0302: long timeToIdleSeconds, boolean diskPersistent,
0303: long diskExpiryThreadIntervalSeconds,
0304: RegisteredEventListeners registeredEventListeners) {
0305: this (name, maxElementsInMemory, memoryStoreEvictionPolicy,
0306: overflowToDisk, diskStorePath, eternal,
0307: timeToLiveSeconds, timeToIdleSeconds, diskPersistent,
0308: diskExpiryThreadIntervalSeconds,
0309: registeredEventListeners, null);
0310: }
0311:
0312: /**
0313: * 1.2.1 Constructor
0314: * <p/>
0315: * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
0316: * <p/>
0317: * A client can specify their own settings here and pass the {@link Cache} object
0318: * into {@link CacheManager#addCache} to specify parameters other than the defaults.
0319: * <p/>
0320: * Only the CacheManager can initialise them.
0321: *
0322: * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
0323: * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
0324: * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
0325: * @param overflowToDisk whether to use the disk store
0326: * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
0327: * @param eternal whether the elements in the cache are eternal, i.e. never expire
0328: * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
0329: * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
0330: * @param diskPersistent whether to persist the cache to disk between JVM restarts
0331: * @param diskExpiryThreadIntervalSeconds
0332: * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
0333: * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
0334: * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
0335: * @since 1.2.1
0336: */
0337: public Cache(String name, int maxElementsInMemory,
0338: MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
0339: boolean overflowToDisk, String diskStorePath,
0340: boolean eternal, long timeToLiveSeconds,
0341: long timeToIdleSeconds, boolean diskPersistent,
0342: long diskExpiryThreadIntervalSeconds,
0343: RegisteredEventListeners registeredEventListeners,
0344: BootstrapCacheLoader bootstrapCacheLoader) {
0345:
0346: this (name, maxElementsInMemory, memoryStoreEvictionPolicy,
0347: overflowToDisk, diskStorePath, eternal,
0348: timeToLiveSeconds, timeToIdleSeconds, diskPersistent,
0349: diskExpiryThreadIntervalSeconds,
0350: registeredEventListeners, bootstrapCacheLoader, 0);
0351: }
0352:
0353: /**
0354: * 1.2.4 Constructor
0355: * <p/>
0356: * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
0357: * <p/>
0358: * A client can specify their own settings here and pass the {@link Cache} object
0359: * into {@link CacheManager#addCache} to specify parameters other than the defaults.
0360: * <p/>
0361: * Only the CacheManager can initialise them.
0362: *
0363: * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
0364: * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
0365: * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
0366: * @param overflowToDisk whether to use the disk store
0367: * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
0368: * @param eternal whether the elements in the cache are eternal, i.e. never expire
0369: * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
0370: * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
0371: * @param diskPersistent whether to persist the cache to disk between JVM restarts
0372: * @param diskExpiryThreadIntervalSeconds
0373: * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
0374: * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
0375: * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
0376: * @since 1.2.4
0377: */
0378: public Cache(String name, int maxElementsInMemory,
0379: MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
0380: boolean overflowToDisk, String diskStorePath,
0381: boolean eternal, long timeToLiveSeconds,
0382: long timeToIdleSeconds, boolean diskPersistent,
0383: long diskExpiryThreadIntervalSeconds,
0384: RegisteredEventListeners registeredEventListeners,
0385: BootstrapCacheLoader bootstrapCacheLoader,
0386: int maxElementsOnDisk) {
0387:
0388: this (name, maxElementsInMemory, memoryStoreEvictionPolicy,
0389: overflowToDisk, diskStorePath, eternal,
0390: timeToLiveSeconds, timeToIdleSeconds, diskPersistent,
0391: diskExpiryThreadIntervalSeconds,
0392: registeredEventListeners, bootstrapCacheLoader,
0393: maxElementsOnDisk, 0);
0394:
0395: }
0396:
0397: /**
0398: * 1.2.4 Constructor
0399: * <p/>
0400: * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
0401: * <p/>
0402: * A client can specify their own settings here and pass the {@link Cache} object
0403: * into {@link CacheManager#addCache} to specify parameters other than the defaults.
0404: * <p/>
0405: * Only the CacheManager can initialise them.
0406: *
0407: * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
0408: * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
0409: * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
0410: * @param overflowToDisk whether to use the disk store
0411: * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
0412: * @param eternal whether the elements in the cache are eternal, i.e. never expire
0413: * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
0414: * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
0415: * @param diskPersistent whether to persist the cache to disk between JVM restarts
0416: * @param diskExpiryThreadIntervalSeconds
0417: * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
0418: * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
0419: * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
0420: * @param diskSpoolBufferSizeMB the amount of memory to allocate the write buffer for puts to the DiskStore.
0421: * @since 1.2.4
0422: */
0423: public Cache(String name, int maxElementsInMemory,
0424: MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
0425: boolean overflowToDisk, String diskStorePath,
0426: boolean eternal, long timeToLiveSeconds,
0427: long timeToIdleSeconds, boolean diskPersistent,
0428: long diskExpiryThreadIntervalSeconds,
0429: RegisteredEventListeners registeredEventListeners,
0430: BootstrapCacheLoader bootstrapCacheLoader,
0431: int maxElementsOnDisk, int diskSpoolBufferSizeMB) {
0432:
0433: changeStatus(Status.STATUS_UNINITIALISED);
0434:
0435: guid = createGuid();
0436:
0437: configuration = new CacheConfiguration();
0438: configuration.setName(name);
0439: configuration.setMaxElementsInMemory(maxElementsInMemory);
0440: configuration
0441: .setMemoryStoreEvictionPolicyFromObject(memoryStoreEvictionPolicy);
0442: configuration.setOverflowToDisk(overflowToDisk);
0443: configuration.setEternal(eternal);
0444: configuration.setTimeToLiveSeconds(timeToLiveSeconds);
0445: configuration.setTimeToIdleSeconds(timeToIdleSeconds);
0446: configuration.setDiskPersistent(diskPersistent);
0447: configuration.setMaxElementsOnDisk(maxElementsOnDisk);
0448:
0449: if (diskStorePath == null) {
0450: this .diskStorePath = System.getProperty("java.io.tmpdir");
0451: } else {
0452: this .diskStorePath = diskStorePath;
0453: }
0454:
0455: if (registeredEventListeners == null) {
0456: this .registeredEventListeners = new RegisteredEventListeners(
0457: this );
0458: } else {
0459: this .registeredEventListeners = registeredEventListeners;
0460: }
0461:
0462: registeredCacheExtensions = createNewCacheExtensionsList();
0463:
0464: //Set this to a safe value.
0465: if (diskExpiryThreadIntervalSeconds == 0) {
0466: configuration
0467: .setDiskExpiryThreadIntervalSeconds(DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS);
0468: } else {
0469: configuration
0470: .setDiskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds);
0471: }
0472:
0473: if (diskSpoolBufferSizeMB == 0) {
0474: configuration
0475: .setDiskSpoolBufferSizeMB(DEFAULT_SPOOL_BUFFER_SIZE);
0476: } else {
0477: configuration
0478: .setDiskSpoolBufferSizeMB(diskSpoolBufferSizeMB);
0479: }
0480:
0481: // For backward compatibility with 1.1 and earlier
0482: if (memoryStoreEvictionPolicy == null) {
0483: configuration
0484: .setMemoryStoreEvictionPolicyFromObject(DEFAULT_MEMORY_STORE_EVICTION_POLICY);
0485: }
0486:
0487: this .bootstrapCacheLoader = bootstrapCacheLoader;
0488:
0489: statisticsAccuracy = Statistics.STATISTICS_ACCURACY_BEST_EFFORT;
0490:
0491: }
0492:
0493: /**
0494: * Newly created caches do not have a {@link net.sf.ehcache.store.MemoryStore} or a {@link net.sf.ehcache.store.DiskStore}.
0495: * <p/>
0496: * This method creates those and makes the cache ready to accept elements
0497: */
0498: public void initialise() {
0499: synchronized (this ) {
0500: if (!status.equals(Status.STATUS_UNINITIALISED)) {
0501: throw new IllegalStateException(
0502: "Cannot initialise the "
0503: + configuration.getName()
0504: + " cache because its status is not STATUS_UNINITIALISED");
0505: }
0506:
0507: if (configuration.getMaxElementsInMemory() == 0) {
0508: if (LOG.isWarnEnabled()) {
0509: LOG
0510: .warn("Cache: "
0511: + configuration.getName()
0512: + " has a maxElementsInMemory of 0. It is strongly recommended to "
0513: + "have a maximumSize of at least 1. Performance is halved by not using a MemoryStore.");
0514: }
0515: }
0516:
0517: this .diskStore = createDiskStore();
0518:
0519: memoryStore = MemoryStore.create(this , diskStore);
0520: changeStatus(Status.STATUS_ALIVE);
0521: initialiseRegisteredCacheExtensions();
0522: }
0523:
0524: if (LOG.isDebugEnabled()) {
0525: LOG.debug("Initialised cache: " + configuration.getName());
0526: }
0527:
0528: if (disabled) {
0529: if (LOG.isWarnEnabled()) {
0530: LOG
0531: .warn("Cache: "
0532: + configuration.getName()
0533: + " is disabled because the "
0534: + NET_SF_EHCACHE_DISABLED
0535: + " property was set to true. No elements will be added to the cache.");
0536: }
0537: }
0538: }
0539:
0540: /**
0541: * Creates a disk store.
0542: */
0543: protected Store createDiskStore() {
0544: if (configuration.isOverflowToDisk()) {
0545: return new DiskStore(this , diskStorePath);
0546: } else {
0547: return null;
0548: }
0549: }
0550:
0551: /**
0552: * Bootstrap command. This must be called after the Cache is intialised, during
0553: * CacheManager initialisation. If loads are synchronous, they will complete before the CacheManager
0554: * initialise completes, otherwise they will happen in the background.
0555: */
0556: public void bootstrap() {
0557: if (!disabled && bootstrapCacheLoader != null) {
0558: bootstrapCacheLoader.load(this );
0559: }
0560:
0561: }
0562:
0563: private void changeStatus(Status status) {
0564: this .status = status;
0565: }
0566:
0567: /**
0568: * Put an element in the cache.
0569: * <p/>
0570: * Resets the access statistics on the element, which would be the case if it has previously been
0571: * gotten from a cache, and is now being put back.
0572: * <p/>
0573: * Also notifies the CacheEventListener that:
0574: * <ul>
0575: * <li>the element was put, but only if the Element was actually put.
0576: * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
0577: * if it was requested
0578: * </ul>
0579: * Synchronization is handled within the method.
0580: * <p/>
0581: * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
0582: * This exception should be caught in those cirucmstances.
0583: *
0584: * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
0585: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0586: * @throws IllegalArgumentException if the element is null
0587: * @throws CacheException
0588: */
0589: public final void put(Element element)
0590: throws IllegalArgumentException, IllegalStateException,
0591: CacheException {
0592: put(element, false);
0593: }
0594:
0595: /**
0596: * Put an element in the cache.
0597: * <p/>
0598: * Resets the access statistics on the element, which would be the case if it has previously been
0599: * gotten from a cache, and is now being put back.
0600: * <p/>
0601: * Also notifies the CacheEventListener that:
0602: * <ul>
0603: * <li>the element was put, but only if the Element was actually put.
0604: * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
0605: * if it was requested
0606: * </ul>
0607: * Synchronization is handled within the method.
0608: * <p/>
0609: * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
0610: * This exception should be caught in those cirucmstances.
0611: *
0612: * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
0613: * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
0614: * further notification to doNotNotifyCacheReplicators cache peers
0615: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0616: * @throws IllegalArgumentException if the element is null
0617: */
0618: public final void put(Element element,
0619: boolean doNotNotifyCacheReplicators)
0620: throws IllegalArgumentException, IllegalStateException,
0621: CacheException {
0622: checkStatus();
0623:
0624: if (disabled) {
0625: return;
0626: }
0627:
0628: if (element == null) {
0629: throw new IllegalArgumentException("Element cannot be null");
0630: }
0631:
0632: element.resetAccessStatistics();
0633: boolean elementExists;
0634: Object key = element.getObjectKey();
0635: elementExists = isElementInMemory(key) || isElementOnDisk(key);
0636: if (elementExists) {
0637: element.updateUpdateStatistics();
0638: }
0639: applyDefaultsToElementWithoutLifespanSet(element);
0640:
0641: backOffIfDiskSpoolFull();
0642:
0643: synchronized (this ) {
0644: memoryStore.put(element);
0645: }
0646:
0647: if (elementExists) {
0648: registeredEventListeners.notifyElementUpdated(element,
0649: doNotNotifyCacheReplicators);
0650: } else {
0651: registeredEventListeners.notifyElementPut(element,
0652: doNotNotifyCacheReplicators);
0653: }
0654:
0655: }
0656:
0657: /**
0658: * wait outside of synchronized block so as not to block readers
0659: * If the disk store spool is full wait a short time to give it a chance to
0660: * catch up.
0661: */
0662: private void backOffIfDiskSpoolFull() {
0663:
0664: if (diskStore != null && diskStore.backedUp()) {
0665: //back off to avoid OutOfMemoryError
0666: try {
0667: Thread.sleep(BACK_OFF_TIME_MILLIS);
0668: } catch (InterruptedException e) {
0669: //do not care if this happens
0670: }
0671: }
0672: }
0673:
0674: private void applyDefaultsToElementWithoutLifespanSet(
0675: Element element) {
0676: if (!element.isLifespanSet()) {
0677: //Setting with Cache defaults
0678: element.setTimeToLive((int) configuration
0679: .getTimeToLiveSeconds());
0680: element.setTimeToIdle((int) configuration
0681: .getTimeToIdleSeconds());
0682: element.setEternal(configuration.isEternal());
0683: }
0684: }
0685:
0686: /**
0687: * Put an element in the cache, without updating statistics, or updating listeners. This is meant to be used
0688: * in conjunction with {@link #getQuiet}.
0689: * Synchronization is handled within the method.
0690: * <p/>
0691: * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
0692: * This exception should be caught in those cirucmstances.
0693: * <p/>
0694: *
0695: * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
0696: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0697: * @throws IllegalArgumentException if the element is null
0698: */
0699: public final void putQuiet(Element element)
0700: throws IllegalArgumentException, IllegalStateException,
0701: CacheException {
0702: checkStatus();
0703:
0704: if (disabled) {
0705: return;
0706: }
0707:
0708: if (element == null) {
0709: throw new IllegalArgumentException("Element cannot be null");
0710: }
0711:
0712: applyDefaultsToElementWithoutLifespanSet(element);
0713:
0714: synchronized (this ) {
0715: memoryStore.put(element);
0716: }
0717: }
0718:
0719: /**
0720: * Gets an element from the cache. Updates Element Statistics
0721: * <p/>
0722: * Note that the Element's lastAccessTime is always the time of this get.
0723: * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
0724: * <p/>
0725: * Synchronization is handled within the method.
0726: *
0727: * @param key a serializable value
0728: * @return the element, or null, if it does not exist.
0729: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0730: * @see #isExpired
0731: */
0732: public final Element get(Serializable key)
0733: throws IllegalStateException, CacheException {
0734: return get((Object) key);
0735: }
0736:
0737: /**
0738: * Gets an element from the cache. Updates Element Statistics
0739: * <p/>
0740: * Note that the Element's lastAccessTime is always the time of this get.
0741: * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
0742: * <p/>
0743: * Synchronization is handled within the method.
0744: *
0745: * @param key an Object value
0746: * @return the element, or null, if it does not exist.
0747: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0748: * @see #isExpired
0749: * @since 1.2
0750: */
0751: public final Element get(Object key) throws IllegalStateException,
0752: CacheException {
0753: checkStatus();
0754: Element element;
0755: long start = System.currentTimeMillis();
0756:
0757: synchronized (this ) {
0758: element = searchInMemoryStore(key, true);
0759: if (element == null && configuration.isOverflowToDisk()) {
0760: element = searchInDiskStore(key, true);
0761: }
0762: if (element == null) {
0763: missCountNotFound++;
0764: if (LOG.isTraceEnabled()) {
0765: LOG
0766: .trace(configuration.getName()
0767: + " cache - Miss");
0768: }
0769: } else {
0770: hitCount++;
0771: }
0772: }
0773: long end = System.currentTimeMillis();
0774: totalGetTime += (end - start);
0775: return element;
0776: }
0777:
0778: /**
0779: * Warning: This method is related to the JSR107 specification, which is in draft. It is subject to change without notice.
0780: * <p/>
0781: * This method will return, from the cache, the object associated with
0782: * the argument "key".
0783: * <p/>
0784: * If the object is not in the cache, the associated
0785: * cache loader will be called. That is either the CacheLoader passed in, or if null, the one associated with the cache.
0786: * If both are null, no load is performed and null is returned.
0787: * <p/>
0788: * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
0789: * are synchronized.
0790: *
0791: * @param key key whose associated value is to be returned.
0792: * @param loader the override loader to use. If null, the cache's default loader will be used
0793: * @param loaderArgument an argument to pass to the CacheLoader.
0794: * @return an element if it existed or could be loaded, otherwise null
0795: * @throws CacheException
0796: */
0797: public Element getWithLoader(Object key, CacheLoader loader,
0798: Object loaderArgument) throws CacheException {
0799:
0800: Element element = get(key);
0801: if (element != null) {
0802: return element;
0803: }
0804:
0805: if (cacheLoader == null && loader == null) {
0806: return null;
0807: }
0808:
0809: try {
0810: //check again in case the last thread loaded it
0811: element = getQuiet(key);
0812: if (element != null) {
0813: return element;
0814: }
0815: Future future = asynchronousLoad(key, loader,
0816: loaderArgument);
0817: //wait for result
0818: future.get();
0819: } catch (Exception e) {
0820: throw new CacheException(
0821: "Exception on load for key " + key, e);
0822: }
0823: return getQuiet(key);
0824: }
0825:
0826: /**
0827: * Warning: This method is related to the JSR107 specification, which is in draft. It is subject to change without notice.
0828: * <p/>
0829: * The load method provides a means to "pre load" the cache. This method will, asynchronously, load the specified
0830: * object into the cache using the associated cacheloader. If the object already exists in the cache, no action is
0831: * taken. If no loader is associated with the object, no object will be loaded into the cache. If a problem is
0832: * encountered during the retrieving or loading of the object, an exception should be logged. If the "arg" argument
0833: * is set, the arg object will be passed to the CacheLoader.load method. The cache will not dereference the object.
0834: * If no "arg" value is provided a null will be passed to the load method. The storing of null values in the cache
0835: * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding
0836: * the object in the cache. In both cases a null is returned.
0837: * <p/>
0838: * The Ehcache native API provides similar functionality to loaders using the
0839: * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
0840: *
0841: * @param key key whose associated value to be loaded using the associated cacheloader if this cache doesn't contain it.
0842: * @throws CacheException
0843: */
0844: public void load(final Object key) throws CacheException {
0845: if (cacheLoader == null) {
0846: if (LOG.isDebugEnabled()) {
0847: LOG.debug("The CacheLoader is null. Returning.");
0848: }
0849: return;
0850: }
0851:
0852: boolean existsOnCall = isKeyInCache(key);
0853: if (existsOnCall) {
0854: if (LOG.isDebugEnabled()) {
0855: LOG.debug("The key " + key
0856: + " exists in the cache. Returning.");
0857: }
0858: return;
0859: }
0860:
0861: asynchronousLoad(key, null, null);
0862: }
0863:
0864: /**
0865: * Warning: This method is related to the JSR107 specification, which is in draft. It is subject to change without notice.
0866: * <p/>
0867: * The getAll method will return, from the cache, a Map of the objects associated with the Collection of keys in argument "keys".
0868: * If the objects are not in the cache, the associated cache loader will be called. If no loader is associated with an object,
0869: * a null is returned. If a problem is encountered during the retrieving or loading of the objects, an exception will be thrown.
0870: * If the "arg" argument is set, the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference
0871: * the object. If no "arg" value is provided a null will be passed to the loadAll method. The storing of null values in the cache
0872: * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding the object in
0873: * the cache. In both cases a null is returned.
0874: * <p/>
0875: * <p/>
0876: * Note. If the getAll exceeds the maximum cache size, the returned map will necessarily be less than the number specified.
0877: * <p/>
0878: * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
0879: * are synchronized.
0880: * <p/>
0881: * The constructs package provides similar functionality using the
0882: * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
0883: *
0884: * @param keys a collection of keys to be returned/loaded
0885: * @param loaderArgument an argument to pass to the CacheLoader.
0886: * @return a Map populated from the Cache. If there are no elements, an empty Map is returned.
0887: * @throws CacheException
0888: */
0889: public Map getAllWithLoader(Collection keys, Object loaderArgument)
0890: throws CacheException {
0891: if (keys == null) {
0892: return new HashMap(0);
0893: }
0894: Map map = new HashMap(keys.size());
0895:
0896: List missingKeys = new ArrayList(keys.size());
0897:
0898: if (cacheLoader != null) {
0899: Object key = null;
0900: try {
0901: map = new HashMap(keys.size());
0902:
0903: for (Iterator iterator = keys.iterator(); iterator
0904: .hasNext();) {
0905: key = iterator.next();
0906:
0907: if (isKeyInCache(key)) {
0908: Element element = get(key);
0909: if (element != null) {
0910: map.put(key, element.getObjectValue());
0911: } else {
0912: map.put(key, null);
0913: }
0914: } else {
0915: missingKeys.add(key);
0916: }
0917: }
0918:
0919: //now load everything that's missing.
0920: Future future = asynchronousLoadAll(missingKeys,
0921: loaderArgument);
0922: future.get();
0923:
0924: for (int i = 0; i < missingKeys.size(); i++) {
0925: key = missingKeys.get(i);
0926: Element element = get(key);
0927: if (element != null) {
0928: map.put(key, element.getObjectValue());
0929: } else {
0930: map.put(key, null);
0931: }
0932: }
0933:
0934: } catch (ExecutionException e) {
0935: throw new CacheException(e.getMessage() + " for key "
0936: + key, e);
0937: } catch (InterruptedException e) {
0938: throw new CacheException(e.getMessage() + " for key "
0939: + key, e);
0940: }
0941: } else {
0942: for (Iterator iterator = keys.iterator(); iterator
0943: .hasNext();) {
0944: Object key = iterator.next();
0945: Element element = get(key);
0946: if (element != null) {
0947: map.put(key, element.getObjectValue());
0948: } else {
0949: map.put(key, null);
0950: }
0951: }
0952: }
0953: return map;
0954: }
0955:
0956: /**
0957: * Warning: This method is related to the JSR107 specification, which is in draft. It is subject to change without notice.
0958: * <p/>
0959: * The loadAll method provides a means to "pre load" objects into the cache. This method will, asynchronously, load
0960: * the specified objects into the cache using the associated cache loader. If the an object already exists in the
0961: * cache, no action is taken. If no loader is associated with the object, no object will be loaded into the cache.
0962: * If a problem is encountered during the retrieving or loading of the objects, an exception (to be defined)
0963: * should be logged. The getAll method will return, from the cache, a Map of the objects associated with the
0964: * Collection of keys in argument "keys". If the objects are not in the cache, the associated cache loader will be
0965: * called. If no loader is associated with an object, a null is returned. If a problem is encountered during the
0966: * retrieving or loading of the objects, an exception (to be defined) will be thrown. If the "arg" argument is set,
0967: * the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference the object.
0968: * If no "arg" value is provided a null will be passed to the loadAll method.
0969: * <p/>
0970: * keys - collection of the keys whose associated values to be loaded into this cache by using the associated
0971: * cacheloader if this cache doesn't contain them.
0972: * <p/>
0973: * The Ehcache native API provides similar functionality to loaders using the
0974: * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
0975: */
0976: public void loadAll(final Collection keys, final Object argument)
0977: throws CacheException {
0978:
0979: if (cacheLoader == null) {
0980: if (LOG.isDebugEnabled()) {
0981: LOG.debug("The CacheLoader is null. Returning.");
0982: }
0983: return;
0984: }
0985: if (keys == null) {
0986: return;
0987: }
0988: asynchronousLoadAll(keys, argument);
0989: }
0990:
0991: /**
0992: * Gets an element from the cache, without updating Element statistics. Cache statistics are
0993: * still updated.
0994: * <p/>
0995: * Synchronization is handled within the method.
0996: *
0997: * @param key a serializable value
0998: * @return the element, or null, if it does not exist.
0999: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1000: * @see #isExpired
1001: */
1002: public final Element getQuiet(Serializable key)
1003: throws IllegalStateException, CacheException {
1004: return getQuiet((Object) key);
1005: }
1006:
1007: /**
1008: * Gets an element from the cache, without updating Element statistics. Cache statistics are
1009: * not updated.
1010: * <p/>
1011: * Synchronization is handled within the method.
1012: *
1013: * @param key a serializable value
1014: * @return the element, or null, if it does not exist.
1015: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1016: * @see #isExpired
1017: * @since 1.2
1018: */
1019: public final Element getQuiet(Object key)
1020: throws IllegalStateException, CacheException {
1021: checkStatus();
1022: Element element;
1023:
1024: synchronized (this ) {
1025: element = searchInMemoryStore(key, false);
1026: if (element == null && configuration.isOverflowToDisk()) {
1027: element = searchInDiskStore(key, false);
1028: }
1029: }
1030: return element;
1031: }
1032:
1033: /**
1034: * Returns a list of all element keys in the cache, whether or not they are expired.
1035: * <p/>
1036: * The returned keys are unique and can be considered a set.
1037: * <p/>
1038: * The List returned is not live. It is a copy.
1039: * <p/>
1040: * The time taken is O(n). On a single cpu 1.8Ghz P4, approximately 8ms is required
1041: * for each 1000 entries.
1042: *
1043: * @return a list of {@link Object} keys
1044: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1045: */
1046: public final synchronized List getKeys()
1047: throws IllegalStateException, CacheException {
1048: checkStatus();
1049: /* An element with the same key can exist in both the memory store and the
1050: disk store at the same time. Because the memory store is always searched first
1051: these duplicates do not cause problems when getting elements/
1052:
1053: This method removes these duplicates before returning the list of keys*/
1054: List allKeyList = new ArrayList();
1055: List keyList = Arrays.asList(memoryStore.getKeyArray());
1056: allKeyList.addAll(keyList);
1057: if (configuration.isOverflowToDisk()) {
1058: Set allKeys = new HashSet();
1059: //within the store keys will be unique
1060: allKeys.addAll(keyList);
1061: Object[] diskKeys = diskStore.getKeyArray();
1062: for (int i = 0; i < diskKeys.length; i++) {
1063: Object diskKey = diskKeys[i];
1064: if (allKeys.add(diskKey)) {
1065: //Unique, so add it to the list
1066: allKeyList.add(diskKey);
1067: }
1068: }
1069: }
1070: return allKeyList;
1071: }
1072:
1073: /**
1074: * Returns a list of all element keys in the cache. Only keys of non-expired
1075: * elements are returned.
1076: * <p/>
1077: * The returned keys are unique and can be considered a set.
1078: * <p/>
1079: * The List returned is not live. It is a copy.
1080: * <p/>
1081: * The time taken is O(n), where n is the number of elements in the cache. On
1082: * a 1.8Ghz P4, the time taken is approximately 200ms per 1000 entries. This method
1083: * is not syncrhonized, because it relies on a non-live list returned from {@link #getKeys()}
1084: * , which is synchronised, and which takes 8ms per 1000 entries. This way
1085: * cache liveness is preserved, even if this method is very slow to return.
1086: * <p/>
1087: * Consider whether your usage requires checking for expired keys. Because
1088: * this method takes so long, depending on cache settings, the list could be
1089: * quite out of date by the time you get it.
1090: *
1091: * @return a list of {@link Object} keys
1092: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1093: */
1094: public final List getKeysWithExpiryCheck()
1095: throws IllegalStateException, CacheException {
1096: List allKeyList = getKeys();
1097: //remove keys of expired elements
1098: ArrayList nonExpiredKeys = new ArrayList(allKeyList.size());
1099: int allKeyListSize = allKeyList.size();
1100: for (int i = 0; i < allKeyListSize; i++) {
1101: Object key = allKeyList.get(i);
1102: Element element = getQuiet(key);
1103: if (element != null) {
1104: nonExpiredKeys.add(key);
1105: }
1106: }
1107: nonExpiredKeys.trimToSize();
1108: return nonExpiredKeys;
1109: }
1110:
1111: /**
1112: * Returns a list of all elements in the cache, whether or not they are expired.
1113: * <p/>
1114: * The returned keys are not unique and may contain duplicates. If the cache is only
1115: * using the memory store, the list will be unique. If the disk store is being used
1116: * as well, it will likely contain duplicates, because of the internal store design.
1117: * <p/>
1118: * The List returned is not live. It is a copy.
1119: * <p/>
1120: * The time taken is O(log n). On a single cpu 1.8Ghz P4, approximately 6ms is required
1121: * for 1000 entries and 36 for 50000.
1122: * <p/>
1123: * This is the fastest getKeys method
1124: *
1125: * @return a list of {@link Object} keys
1126: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1127: */
1128: public final synchronized List getKeysNoDuplicateCheck()
1129: throws IllegalStateException {
1130: checkStatus();
1131: ArrayList allKeys = new ArrayList();
1132: List memoryKeySet = Arrays.asList(memoryStore.getKeyArray());
1133: allKeys.addAll(memoryKeySet);
1134: if (configuration.isOverflowToDisk()) {
1135: List diskKeySet = Arrays.asList(diskStore.getKeyArray());
1136: allKeys.addAll(diskKeySet);
1137: }
1138: return allKeys;
1139: }
1140:
1141: private Element searchInMemoryStore(Object key,
1142: boolean updateStatistics) {
1143: Element element;
1144: if (updateStatistics) {
1145: element = memoryStore.get(key);
1146: } else {
1147: element = memoryStore.getQuiet(key);
1148: }
1149: if (element != null) {
1150: if (isExpired(element)) {
1151: if (LOG.isDebugEnabled()) {
1152: LOG.debug(configuration.getName()
1153: + " Memory cache hit, but element expired");
1154: }
1155: missCountExpired++;
1156: remove(key, true, true, false);
1157: element = null;
1158: } else {
1159: memoryStoreHitCount++;
1160: }
1161: }
1162: return element;
1163: }
1164:
1165: private Element searchInDiskStore(Object key,
1166: boolean updateStatistics) {
1167: if (!(key instanceof Serializable)) {
1168: return null;
1169: }
1170: Serializable serializableKey = (Serializable) key;
1171: Element element;
1172: if (updateStatistics) {
1173: element = diskStore.get(serializableKey);
1174: } else {
1175: element = diskStore.getQuiet(serializableKey);
1176: }
1177: if (element != null) {
1178: if (isExpired(element)) {
1179: if (LOG.isDebugEnabled()) {
1180: LOG
1181: .debug(configuration.getName()
1182: + " cache - Disk Store hit, but element expired");
1183: }
1184: missCountExpired++;
1185: remove(key, true, true, false);
1186: element = null;
1187: } else {
1188: diskStoreHitCount++;
1189: //Put the item back into memory to preserve policies in the memory store and to save updated statistics
1190: memoryStore.put(element);
1191: }
1192: }
1193: return element;
1194: }
1195:
1196: /**
1197: * Removes an {@link Element} from the Cache. This also removes it from any
1198: * stores it may be in.
1199: * <p/>
1200: * Also notifies the CacheEventListener after the element was removed.
1201: * <p/>
1202: * Synchronization is handled within the method.
1203: * <p/>
1204: * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1205: * This exception should be caught in those cirucmstances.
1206: *
1207: * @param key the element key to operate on
1208: * @return true if the element was removed, false if it was not found in the cache
1209: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1210: */
1211: public final boolean remove(Serializable key)
1212: throws IllegalStateException {
1213: return remove((Object) key);
1214: }
1215:
1216: /**
1217: * Removes an {@link Element} from the Cache. This also removes it from any
1218: * stores it may be in.
1219: * <p/>
1220: * Also notifies the CacheEventListener after the element was removed, but only if an Element
1221: * with the key actually existed.
1222: * <p/>
1223: * Synchronization is handled within the method.
1224: * <p/>
1225: * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1226: * This exception should be caught in those cirucmstances.
1227: * <p/>
1228: *
1229: * @param key the element key to operate on
1230: * @return true if the element was removed, false if it was not found in the cache
1231: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1232: * @since 1.2
1233: */
1234: public final boolean remove(Object key)
1235: throws IllegalStateException {
1236: return remove(key, false);
1237: }
1238:
1239: /**
1240: * Removes an {@link Element} from the Cache. This also removes it from any
1241: * stores it may be in.
1242: * <p/>
1243: * Also notifies the CacheEventListener after the element was removed, but only if an Element
1244: * with the key actually existed.
1245: * <p/>
1246: * Synchronization is handled within the method.
1247: * <p/>
1248: * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1249: * This exception should be caught in those cirucmstances.
1250: *
1251: * @param key the element key to operate on
1252: * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1253: * further notification to doNotNotifyCacheReplicators cache peers
1254: * @return true if the element was removed, false if it was not found in the cache
1255: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1256: * @noinspection SameParameterValue
1257: */
1258: public final boolean remove(Serializable key,
1259: boolean doNotNotifyCacheReplicators)
1260: throws IllegalStateException {
1261: return remove((Object) key, doNotNotifyCacheReplicators);
1262: }
1263:
1264: /**
1265: * Removes an {@link Element} from the Cache. This also removes it from any
1266: * stores it may be in.
1267: * <p/>
1268: * Also notifies the CacheEventListener after the element was removed, but only if an Element
1269: * with the key actually existed.
1270: * <p/>
1271: * Synchronization is handled within the method.
1272: *
1273: * @param key the element key to operate on
1274: * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1275: * further notification to doNotNotifyCacheReplicators cache peers
1276: * @return true if the element was removed, false if it was not found in the cache
1277: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1278: */
1279: public final boolean remove(Object key,
1280: boolean doNotNotifyCacheReplicators)
1281: throws IllegalStateException {
1282: return remove(key, false, true, doNotNotifyCacheReplicators);
1283: }
1284:
1285: /**
1286: * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
1287: * stores it may be in.
1288: * <p/>
1289: * Synchronization is handled within the method.
1290: *
1291: * @param key the element key to operate on
1292: * @return true if the element was removed, false if it was not found in the cache
1293: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1294: */
1295: public final boolean removeQuiet(Serializable key)
1296: throws IllegalStateException {
1297: return remove(key, false, false, false);
1298: }
1299:
1300: /**
1301: * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
1302: * stores it may be in.
1303: * <p/>
1304: * Synchronization is handled within the method.
1305: * <p/>
1306: * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1307: * This exception should be caught in those cirucmstances.
1308: *
1309: * @param key the element key to operate on
1310: * @return true if the element was removed, false if it was not found in the cache
1311: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1312: * @since 1.2
1313: */
1314: public final boolean removeQuiet(Object key)
1315: throws IllegalStateException {
1316: return remove(key, false, false, false);
1317: }
1318:
1319: /**
1320: * Removes or expires an {@link Element} from the Cache after an attempt to get it determined that it should be expired.
1321: * This also removes it from any stores it may be in.
1322: * <p/>
1323: * Also notifies the CacheEventListener after the element has expired, but only if an Element
1324: * with the key actually existed.
1325: * <p/>
1326: * Synchronization is handled within the method.
1327: * <p/>
1328: * If a remove was called, listeners are notified, regardless of whether the element existed or not.
1329: * This allows distributed cache listeners to remove elements from a cluster regardless of whether they
1330: * existed locally.
1331: * <p/>
1332: * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1333: * This exception should be caught in those cirucmstances.
1334: *
1335: * @param key the element key to operate on
1336: * @param expiry if the reason this method is being called is to expire the element
1337: * @param notifyListeners whether to notify listeners
1338: * @param doNotNotifyCacheReplicators whether not to notify cache replicators
1339: * @return true if the element was removed, false if it was not found in the cache
1340: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1341: */
1342: private boolean remove(Object key, boolean expiry,
1343: boolean notifyListeners, boolean doNotNotifyCacheReplicators)
1344: throws IllegalStateException {
1345: checkStatus();
1346: boolean removed = false;
1347: Element elementFromMemoryStore;
1348: Element elementFromDiskStore;
1349: synchronized (this ) {
1350: elementFromMemoryStore = memoryStore.remove(key);
1351:
1352: //could have been removed from both places, if there are two copies in the cache
1353: elementFromDiskStore = null;
1354: if (configuration.isOverflowToDisk()) {
1355: if ((key instanceof Serializable)) {
1356: Serializable serializableKey = (Serializable) key;
1357: elementFromDiskStore = diskStore
1358: .remove(serializableKey);
1359: }
1360:
1361: }
1362: }
1363:
1364: boolean removeNotified = false;
1365:
1366: if (elementFromMemoryStore != null) {
1367: if (notifyListeners) {
1368: if (expiry) {
1369: registeredEventListeners.notifyElementExpiry(
1370: elementFromMemoryStore,
1371: doNotNotifyCacheReplicators);
1372: } else {
1373: removeNotified = true;
1374: registeredEventListeners.notifyElementRemoved(
1375: elementFromMemoryStore,
1376: doNotNotifyCacheReplicators);
1377: }
1378: }
1379: removed = true;
1380: }
1381: if (elementFromDiskStore != null) {
1382: if (expiry) {
1383: registeredEventListeners.notifyElementExpiry(
1384: elementFromDiskStore,
1385: doNotNotifyCacheReplicators);
1386: } else {
1387: removeNotified = true;
1388: registeredEventListeners.notifyElementRemoved(
1389: elementFromDiskStore,
1390: doNotNotifyCacheReplicators);
1391: }
1392: removed = true;
1393: }
1394: //If we are trying to remove an element which does not exist locally, we should still notify so that
1395: //cluster invalidations work.
1396: if (!expiry && !removeNotified) {
1397: Element syntheticElement = new Element(key, null);
1398: registeredEventListeners.notifyElementRemoved(
1399: syntheticElement, doNotNotifyCacheReplicators);
1400: }
1401:
1402: return removed;
1403: }
1404:
1405: /**
1406: * Removes all cached items.
1407: * Synchronization is handled within the method.
1408: * <p/>
1409: * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1410: * This exception should be caught in those cirucmstances.
1411: *
1412: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1413: */
1414: public void removeAll() throws IllegalStateException,
1415: CacheException {
1416: removeAll(false);
1417: }
1418:
1419: /**
1420: * Removes all cached items.
1421: * Synchronization is handled within the method.
1422: * <p/>
1423: * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1424: * This exception should be caught in those cirucmstances.
1425: *
1426: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1427: */
1428: public void removeAll(boolean doNotNotifyCacheReplicators)
1429: throws IllegalStateException, CacheException {
1430: checkStatus();
1431: synchronized (this ) {
1432: memoryStore.removeAll();
1433: if (configuration.isOverflowToDisk()) {
1434: diskStore.removeAll();
1435: }
1436: }
1437: registeredEventListeners
1438: .notifyRemoveAll(doNotNotifyCacheReplicators);
1439: }
1440:
1441: /**
1442: * Starts an orderly shutdown of the Cache. Steps are:
1443: * <ol>
1444: * <li>Completes any outstanding CacheLoader loads.
1445: * <li>Disposes any cache extensions.
1446: * <li>Disposes any cache event listeners. The listeners normally complete, so for example distributed caching operations will complete.
1447: * <li>Flushes all cache items from memory to the disk store, if any
1448: * <li>changes status to shutdown, so that any cache operations after this point throw IllegalStateException
1449: * </ol>
1450: * This method should be invoked only by CacheManager, as a cache's lifecycle is bound into that of it's cache manager.
1451: *
1452: * @throws IllegalStateException if the cache is already {@link Status#STATUS_SHUTDOWN}
1453: */
1454: public synchronized void dispose() throws IllegalStateException {
1455: checkStatusNotDisposed();
1456:
1457: if (executorService != null) {
1458: executorService.shutdown();
1459: }
1460: disposeRegisteredCacheExtensions();
1461: registeredEventListeners.dispose();
1462:
1463: if (memoryStore != null) {
1464: memoryStore.dispose();
1465: }
1466: memoryStore = null;
1467: if (configuration.isOverflowToDisk() && diskStore != null) {
1468: diskStore.dispose();
1469: diskStore = null;
1470: }
1471: changeStatus(Status.STATUS_SHUTDOWN);
1472: }
1473:
1474: private void initialiseRegisteredCacheExtensions() {
1475: for (int i = 0; i < registeredCacheExtensions.size(); i++) {
1476: CacheExtension cacheExtension = (CacheExtension) registeredCacheExtensions
1477: .get(i);
1478: cacheExtension.init();
1479: }
1480: }
1481:
1482: private void disposeRegisteredCacheExtensions() {
1483: for (int i = 0; i < registeredCacheExtensions.size(); i++) {
1484: CacheExtension cacheExtension = (CacheExtension) registeredCacheExtensions
1485: .get(i);
1486: cacheExtension.dispose();
1487: }
1488: }
1489:
1490: /**
1491: * Gets the cache configuration this cache was created with.
1492: * <p/>
1493: * Things like listeners that are added dynamically are excluded.
1494: */
1495: public CacheConfiguration getCacheConfiguration() {
1496: return configuration;
1497: }
1498:
1499: /**
1500: * Flushes all cache items from memory to the disk store, and from the DiskStore to disk.
1501: *
1502: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1503: */
1504: public final synchronized void flush()
1505: throws IllegalStateException, CacheException {
1506: checkStatus();
1507: try {
1508: memoryStore.flush();
1509: if (configuration.isOverflowToDisk()) {
1510: diskStore.flush();
1511: }
1512: } catch (IOException e) {
1513: throw new CacheException("Unable to flush cache: "
1514: + configuration.getName() + ". Initial cause was "
1515: + e.getMessage(), e);
1516: }
1517: }
1518:
1519: /**
1520: * Gets the size of the cache. This is a subtle concept. See below.
1521: * <p/>
1522: * The size is the number of {@link Element}s in the {@link MemoryStore} plus
1523: * the number of {@link Element}s in the {@link DiskStore}.
1524: * <p/>
1525: * This number is the actual number of elements, including expired elements that have
1526: * not been removed.
1527: * <p/>
1528: * Expired elements are removed from the the memory store when
1529: * getting an expired element, or when attempting to spool an expired element to
1530: * disk.
1531: * <p/>
1532: * Expired elements are removed from the disk store when getting an expired element,
1533: * or when the expiry thread runs, which is once every five minutes.
1534: * <p/>
1535: * To get an exact size, which would exclude expired elements, use {@link #getKeysWithExpiryCheck()}.size(),
1536: * although see that method for the approximate time that would take.
1537: * <p/>
1538: * To get a very fast result, use {@link #getKeysNoDuplicateCheck()}.size(). If the disk store
1539: * is being used, there will be some duplicates.
1540: *
1541: * @return The size value
1542: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1543: */
1544: public final synchronized int getSize()
1545: throws IllegalStateException, CacheException {
1546: checkStatus();
1547: /* The memory store and the disk store can simultaneously contain elements with the same key
1548: Cache size is the size of the union of the two key sets.*/
1549: return getKeys().size();
1550: }
1551:
1552: /**
1553: * Gets the size of the memory store for this cache. This method relies on calculating
1554: * Serialized sizes. If the Element values are not Serializable they will show as zero.
1555: * <p/>
1556: * Warning: This method can be very expensive to run. Allow approximately 1 second
1557: * per 1MB of entries. Running this method could create liveness problems
1558: * because the object lock is held for a long period
1559: * <p/>
1560: *
1561: * @return the approximate size of the memory store in bytes
1562: * @throws IllegalStateException
1563: */
1564: public final synchronized long calculateInMemorySize()
1565: throws IllegalStateException, CacheException {
1566: checkStatus();
1567: return memoryStore.getSizeInBytes();
1568: }
1569:
1570: /**
1571: * Returns the number of elements in the memory store.
1572: *
1573: * @return the number of elements in the memory store
1574: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1575: */
1576: public final long getMemoryStoreSize() throws IllegalStateException {
1577: checkStatus();
1578: return memoryStore.getSize();
1579: }
1580:
1581: /**
1582: * Returns the number of elements in the disk store.
1583: *
1584: * @return the number of elements in the disk store.
1585: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1586: */
1587: public final int getDiskStoreSize() throws IllegalStateException {
1588: checkStatus();
1589: if (configuration.isOverflowToDisk()) {
1590: return diskStore.getSize();
1591: } else {
1592: return 0;
1593: }
1594: }
1595:
1596: /**
1597: * Gets the status attribute of the Cache.
1598: *
1599: * @return The status value from the Status enum class
1600: */
1601: public final Status getStatus() {
1602: return status;
1603: }
1604:
1605: private void checkStatus() throws IllegalStateException {
1606: if (!status.equals(Status.STATUS_ALIVE)) {
1607: throw new IllegalStateException("The "
1608: + configuration.getName() + " Cache is not alive.");
1609: }
1610: }
1611:
1612: private void checkStatusNotDisposed() throws IllegalStateException {
1613: if (status.equals(Status.STATUS_SHUTDOWN)) {
1614: throw new IllegalStateException("The "
1615: + configuration.getName() + " Cache is disposed.");
1616: }
1617: }
1618:
1619: /**
1620: * The number of times a requested item was found in the cache.
1621: * <p/>
1622: * The internal representation of this statistic has been changed to a long
1623: * to cater for real world situations when an int is not sufficient to hold it.
1624: * If the statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value
1625: * will be returned. Use {@link net.sf.ehcache.Statistics} which contains this statistic.
1626: * <p/>
1627: *
1628: * @return the number of times a requested item was found in the cache
1629: * @deprecated Use {@link net.sf.ehcache.Statistics}
1630: */
1631: public final int getHitCount() {
1632: return (int) hitCount;
1633: }
1634:
1635: /**
1636: * Number of times a requested item was found in the Memory Store.
1637: * <p/>
1638: * The internal representation of this statistic has been changed to a long
1639: * to cater for real world situations when an int is not sufficient to hold it.
1640: * If the statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value
1641: * will be returned. Use {@link net.sf.ehcache.Statistics} which contains this statistic.
1642: * <p/>
1643: *
1644: * @return Number of times a requested item was found in the Memory Store.
1645: * @deprecated Use {@link net.sf.ehcache.Statistics}
1646: */
1647: public final int getMemoryStoreHitCount() {
1648: return (int) memoryStoreHitCount;
1649: }
1650:
1651: /**
1652: * Number of times a requested item was found in the Disk Store.
1653: * <p/>
1654: * The internal representation of this statistic has been changed to a long
1655: * to cater for real world situations when an int is not sufficient to hold it.
1656: * If the statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value
1657: * will be returned. Use {@link net.sf.ehcache.Statistics} which contains this statistic.
1658: * <p/>
1659: *
1660: * @deprecated Use {@link net.sf.ehcache.Statistics}
1661: */
1662: public final int getDiskStoreHitCount() {
1663: return (int) diskStoreHitCount;
1664: }
1665:
1666: /**
1667: * Number of times a requested element was not found in the cache. This
1668: * may be because it expired, in which case this will also be recorded in {@link #getMissCountExpired},
1669: * or because it was simply not there.
1670: * <p/>
1671: * The internal representation of this statistic has been changed to a long
1672: * to cater for real world situations when an int is not sufficient to hold it.
1673: * If the statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value
1674: * will be returned. Use {@link net.sf.ehcache.Statistics} which contains this statistic.
1675: * <p/>
1676: *
1677: * @deprecated Use {@link net.sf.ehcache.Statistics}
1678: */
1679: public final int getMissCountNotFound() {
1680: return (int) missCountNotFound;
1681: }
1682:
1683: /**
1684: * Number of times a requested element was found but was expired.
1685: * <p/>
1686: * The internal representation of this statistic has been changed to a long
1687: * to cater for real world situations when an int is not sufficient to hold it.
1688: * If the statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value
1689: * will be returned. Use {@link net.sf.ehcache.Statistics} which contains this statistic.
1690: * <p/>
1691: *
1692: * @deprecated Use {@link net.sf.ehcache.Statistics}
1693: */
1694: public final int getMissCountExpired() {
1695: return (int) missCountExpired;
1696: }
1697:
1698: /**
1699: * Gets the cache name.
1700: */
1701: public final String getName() {
1702: return configuration.getName();
1703: }
1704:
1705: /**
1706: * Sets the cache name which will name.
1707: *
1708: * @param name the name of the cache. Should not be null. Should also not contain any '/' characters, as these interfere
1709: * with distribution
1710: * @throws IllegalArgumentException if an illegal name is used.
1711: */
1712: public final void setName(String name)
1713: throws IllegalArgumentException {
1714: if (!status.equals(Status.STATUS_UNINITIALISED)) {
1715: throw new IllegalStateException(
1716: "Only unitialised caches can have their names set.");
1717: }
1718: configuration.setName(name);
1719: }
1720:
1721: /**
1722: * Gets timeToIdleSeconds.
1723: *
1724: * @deprecated Get this from the configuration
1725: */
1726: public final long getTimeToIdleSeconds() {
1727: return configuration.getTimeToIdleSeconds();
1728: }
1729:
1730: /**
1731: * Gets timeToLiveSeconds.
1732: *
1733: * @deprecated Get this from the configuration
1734: */
1735: public final long getTimeToLiveSeconds() {
1736: return configuration.getTimeToLiveSeconds();
1737: }
1738:
1739: /**
1740: * Are elements eternal.
1741: *
1742: * @deprecated Get this from the configuration
1743: */
1744: public final boolean isEternal() {
1745: return configuration.isEternal();
1746: }
1747:
1748: /**
1749: * Does the overflow go to disk.
1750: *
1751: * @deprecated Get this from the configuration
1752: */
1753: public final boolean isOverflowToDisk() {
1754: return configuration.isOverflowToDisk();
1755: }
1756:
1757: /**
1758: * Gets the maximum number of elements to hold in memory.
1759: *
1760: * @deprecated Get this from the configuration
1761: */
1762: public final int getMaxElementsInMemory() {
1763: return configuration.getMaxElementsInMemory();
1764: }
1765:
1766: /**
1767: * Gets the maximum number of elements to hold on Disk
1768: *
1769: * @deprecated Get this from the configuration
1770: */
1771: public int getMaxElementsOnDisk() {
1772: return configuration.getMaxElementsOnDisk();
1773: }
1774:
1775: /**
1776: * The policy used to evict elements from the {@link net.sf.ehcache.store.MemoryStore}.
1777: * This can be one of:
1778: * <ol>
1779: * <li>LRU - least recently used
1780: * <li>LFU - least frequently used
1781: * <li>FIFO - first in first out, the oldest element by creation time
1782: * </ol>
1783: * The default value is LRU
1784: *
1785: * @since 1.2
1786: * @deprecated Get this from the configuration
1787: */
1788: public final MemoryStoreEvictionPolicy getMemoryStoreEvictionPolicy() {
1789: return configuration.getMemoryStoreEvictionPolicy();
1790: }
1791:
1792: /**
1793: * @return true if the cache overflows to disk and the disk is persistent between restarts
1794: * @deprecated Get this from the configuration *
1795: */
1796: public final boolean isDiskPersistent() {
1797: return configuration.isDiskPersistent();
1798: }
1799:
1800: /**
1801: * @return the interval between runs
1802: * of the expiry thread, where it checks the disk store for expired elements. It is not the
1803: * the timeToLiveSeconds.
1804: * @deprecated Get this from the configuration
1805: */
1806: public final long getDiskExpiryThreadIntervalSeconds() {
1807: return configuration.getDiskExpiryThreadIntervalSeconds();
1808: }
1809:
1810: /**
1811: * Returns a {@link String} representation of {@link Cache}.
1812: */
1813: public final String toString() {
1814: StringBuffer dump = new StringBuffer();
1815:
1816: dump.append("[").append(" name = ").append(
1817: configuration.getName()).append(" status = ").append(
1818: status).append(" eternal = ").append(
1819: configuration.isEternal()).append(" overflowToDisk = ")
1820: .append(configuration.isOverflowToDisk()).append(
1821: " maxElementsInMemory = ").append(
1822: configuration.getMaxElementsInMemory()).append(
1823: " maxElementsOnDisk = ").append(
1824: configuration.getMaxElementsOnDisk()).append(
1825: " memoryStoreEvictionPolicy = ").append(
1826: configuration.getMemoryStoreEvictionPolicy())
1827: .append(" timeToLiveSeconds = ").append(
1828: configuration.getTimeToLiveSeconds()).append(
1829: " timeToIdleSeconds = ").append(
1830: configuration.getTimeToIdleSeconds()).append(
1831: " diskPersistent = ").append(
1832: configuration.isDiskPersistent()).append(
1833: " diskExpiryThreadIntervalSeconds = ").append(
1834: configuration
1835: .getDiskExpiryThreadIntervalSeconds())
1836: .append(registeredEventListeners)
1837: .append(" hitCount = ").append(hitCount).append(
1838: " memoryStoreHitCount = ").append(
1839: memoryStoreHitCount).append(
1840: " diskStoreHitCount = ").append(
1841: diskStoreHitCount).append(
1842: " missCountNotFound = ").append(
1843: missCountNotFound).append(
1844: " missCountExpired = ")
1845: .append(missCountExpired).append(" ]");
1846:
1847: return dump.toString();
1848: }
1849:
1850: /**
1851: * Checks whether this cache element has expired.
1852: * <p/>
1853: * The element is expired if:
1854: * <ol>
1855: * <li> the idle time is non-zero and has elapsed, unless the cache is eternal; or
1856: * <li> the time to live is non-zero and has elapsed, unless the cache is eternal; or
1857: * <li> the value of the element is null.
1858: * </ol>
1859: *
1860: * @return true if it has expired
1861: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1862: * @throws NullPointerException if the element is null
1863: */
1864: public final boolean isExpired(Element element)
1865: throws IllegalStateException, NullPointerException {
1866: checkStatus();
1867: synchronized (element) {
1868: return element.isExpired();
1869: }
1870: }
1871:
1872: /**
1873: * Clones a cache. This is only legal if the cache has not been
1874: * initialized. At that point only primitives have been set and no
1875: * {@link net.sf.ehcache.store.LruMemoryStore} or {@link net.sf.ehcache.store.DiskStore} has been created.
1876: * <p/>
1877: * A new, empty, RegisteredEventListeners is created on clone.
1878: * <p/>
1879: *
1880: * @return an object of type {@link Cache}
1881: * @throws CloneNotSupportedException
1882: */
1883: public final Object clone() throws CloneNotSupportedException {
1884: if (!(memoryStore == null && diskStore == null)) {
1885: throw new CloneNotSupportedException(
1886: "Cannot clone an initialized cache.");
1887: }
1888: Cache copy = (Cache) super .clone();
1889: copy.configuration = (CacheConfiguration) configuration.clone();
1890: copy.guid = createGuid();
1891:
1892: RegisteredEventListeners registeredEventListenersFromCopy = copy
1893: .getCacheEventNotificationService();
1894: if (registeredEventListenersFromCopy == null
1895: || registeredEventListenersFromCopy
1896: .getCacheEventListeners().size() == 0) {
1897: copy.registeredEventListeners = new RegisteredEventListeners(
1898: copy);
1899: } else {
1900: copy.registeredEventListeners = new RegisteredEventListeners(
1901: copy);
1902: Set cacheEventListeners = registeredEventListeners
1903: .getCacheEventListeners();
1904: for (Iterator iterator = cacheEventListeners.iterator(); iterator
1905: .hasNext();) {
1906: CacheEventListener cacheEventListener = (CacheEventListener) iterator
1907: .next();
1908: CacheEventListener cacheEventListenerClone = (CacheEventListener) cacheEventListener
1909: .clone();
1910: copy.registeredEventListeners
1911: .registerListener(cacheEventListenerClone);
1912: }
1913: }
1914:
1915: copy.registeredCacheExtensions = createNewCacheExtensionsList();
1916: for (int i = 0; i < registeredCacheExtensions.size(); i++) {
1917: CacheExtension cacheExtension = (CacheExtension) registeredCacheExtensions
1918: .get(i);
1919: copy.registerCacheExtension(cacheExtension.clone(copy));
1920: }
1921:
1922: if (bootstrapCacheLoader != null) {
1923: BootstrapCacheLoader bootstrapCacheLoaderClone = (BootstrapCacheLoader) bootstrapCacheLoader
1924: .clone();
1925: copy.setBootstrapCacheLoader(bootstrapCacheLoaderClone);
1926: }
1927:
1928: return copy;
1929: }
1930:
1931: /**
1932: * Gets the internal DiskStore.
1933: *
1934: * @return the DiskStore referenced by this cache
1935: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1936: */
1937: final Store getDiskStore() throws IllegalStateException {
1938: checkStatus();
1939: return diskStore;
1940: }
1941:
1942: /**
1943: * Gets the internal MemoryStore.
1944: *
1945: * @return the MemoryStore referenced by this cache
1946: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1947: */
1948: final MemoryStore getMemoryStore() throws IllegalStateException {
1949: checkStatus();
1950: return memoryStore;
1951: }
1952:
1953: /**
1954: * Use this to access the service in order to register and unregister listeners
1955: *
1956: * @return the RegisteredEventListeners instance for this cache.
1957: */
1958: public final RegisteredEventListeners getCacheEventNotificationService() {
1959: return registeredEventListeners;
1960: }
1961:
1962: /**
1963: * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
1964: *
1965: * @return true if an element matching the key is found in memory
1966: */
1967: public final boolean isElementInMemory(Serializable key) {
1968: return isElementInMemory((Object) key);
1969: }
1970:
1971: /**
1972: * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
1973: *
1974: * @return true if an element matching the key is found in memory
1975: * @since 1.2
1976: */
1977: public final boolean isElementInMemory(Object key) {
1978: return memoryStore.containsKey(key);
1979: }
1980:
1981: /**
1982: * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
1983: *
1984: * @return true if an element matching the key is found in the diskStore
1985: */
1986: public final boolean isElementOnDisk(Serializable key) {
1987: return isElementOnDisk((Object) key);
1988: }
1989:
1990: /**
1991: * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
1992: *
1993: * @return true if an element matching the key is found in the diskStore
1994: * @since 1.2
1995: */
1996: public final boolean isElementOnDisk(Object key) {
1997: if (!(key instanceof Serializable)) {
1998: return false;
1999: }
2000: Serializable serializableKey = (Serializable) key;
2001: if (!configuration.isOverflowToDisk()) {
2002: return false;
2003: } else {
2004: return diskStore != null
2005: && diskStore.containsKey(serializableKey);
2006: }
2007: }
2008:
2009: /**
2010: * The GUID for this cache instance can be used to determine whether two cache instance references
2011: * are pointing to the same cache.
2012: *
2013: * @return the globally unique identifier for this cache instance. This is guaranteed to be unique.
2014: * @since 1.2
2015: */
2016: public final String getGuid() {
2017: return guid;
2018: }
2019:
2020: /**
2021: * Gets the CacheManager managing this cache. For a newly created cache this will be null until
2022: * it has been added to a CacheManager.
2023: *
2024: * @return the manager or null if there is none
2025: */
2026: public final CacheManager getCacheManager() {
2027: return cacheManager;
2028: }
2029:
2030: /**
2031: * Resets statistics counters back to 0.
2032: *
2033: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2034: */
2035: public synchronized void clearStatistics()
2036: throws IllegalStateException {
2037: checkStatus();
2038: hitCount = 0;
2039: memoryStoreHitCount = 0;
2040: diskStoreHitCount = 0;
2041: missCountExpired = 0;
2042: missCountNotFound = 0;
2043: totalGetTime = 0;
2044: registeredEventListeners.clearCounters();
2045: }
2046:
2047: /**
2048: * Accurately measuring statistics can be expensive. Returns the current accuracy setting.
2049: *
2050: * @return one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
2051: */
2052: public int getStatisticsAccuracy() {
2053: return statisticsAccuracy;
2054: }
2055:
2056: /**
2057: * Sets the statistics accuracy.
2058: *
2059: * @param statisticsAccuracy one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
2060: */
2061: public void setStatisticsAccuracy(int statisticsAccuracy) {
2062: this .statisticsAccuracy = statisticsAccuracy;
2063: }
2064:
2065: /**
2066: * Causes all elements stored in the Cache to be synchronously checked for expiry, and if expired, evicted.
2067: */
2068: public void evictExpiredElements() {
2069: Object[] keys = memoryStore.getKeyArray();
2070: synchronized (this ) {
2071: for (int i = 0; i < keys.length; i++) {
2072: Object key = keys[i];
2073: searchInMemoryStore(key, false);
2074: }
2075: }
2076: //This is called regularly by the expiry thread, but call it here synchronously
2077: if (configuration.isOverflowToDisk()) {
2078: diskStore.expireElements();
2079: }
2080: }
2081:
2082: /**
2083: * An inexpensive check to see if the key exists in the cache.
2084: * <p/>
2085: * This method is not synchronized. It is possible that an element may exist in the cache aned be removed
2086: * before the check gets to it, or vice versa.
2087: *
2088: * @param key the key to check.
2089: * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
2090: */
2091: public boolean isKeyInCache(Object key) {
2092: return isElementInMemory(key) || isElementOnDisk(key);
2093: }
2094:
2095: /**
2096: * An extremely expensive check to see if the value exists in the cache. This implementation is O(n). Ehcache
2097: * is not designed for efficient access in this manner.
2098: * <p/>
2099: * This method is not synchronized. It is possible that an element may exist in the cache aned be removed
2100: * before the check gets to it, or vice versa. Because it is slow to execute the probability of that this will
2101: * have happened.
2102: *
2103: * @param value to check for
2104: * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
2105: */
2106: public boolean isValueInCache(Object value) {
2107: boolean isSerializable = value instanceof Serializable;
2108: List keys;
2109: if (isSerializable) {
2110: keys = getKeys();
2111: } else {
2112: keys = Arrays.asList(memoryStore.getKeyArray());
2113: }
2114:
2115: for (int i = 0; i < keys.size(); i++) {
2116: Object key = keys.get(i);
2117: Element element = get(key);
2118: if (element != null) {
2119: Object elementValue = element.getValue();
2120: if (elementValue == null) {
2121: if (value == null) {
2122: return true;
2123: }
2124: } else {
2125: if (elementValue.equals(value)) {
2126: return true;
2127: }
2128: }
2129: }
2130: }
2131: return false;
2132: }
2133:
2134: /**
2135: * {@inheritDoc}
2136: * <p/>
2137: * Note, the {@link #getSize} method will have the same value as the size reported by Statistics
2138: * for the statistics accuracy of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}.
2139: */
2140: public Statistics getStatistics() throws IllegalStateException {
2141: int size = 0;
2142: if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_BEST_EFFORT) {
2143: size = getSize();
2144: } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_GUARANTEED) {
2145: size = getKeysWithExpiryCheck().size();
2146: } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_NONE) {
2147: size = getKeysNoDuplicateCheck().size();
2148: }
2149: return new Statistics(this , statisticsAccuracy, hitCount,
2150: diskStoreHitCount, memoryStoreHitCount,
2151: missCountExpired + missCountNotFound, size,
2152: getAverageGetTime(), registeredEventListeners
2153: .getElementsEvictedCounter());
2154: }
2155:
2156: /**
2157: * For use by CacheManager.
2158: *
2159: * @param cacheManager the CacheManager for this cache to use.
2160: */
2161: public void setCacheManager(CacheManager cacheManager) {
2162: this .cacheManager = cacheManager;
2163: }
2164:
2165: /**
2166: * Accessor for the BootstrapCacheLoader associated with this cache. For testing purposes.
2167: */
2168: public BootstrapCacheLoader getBootstrapCacheLoader() {
2169: return bootstrapCacheLoader;
2170: }
2171:
2172: /**
2173: * Sets the bootstrap cache loader.
2174: *
2175: * @param bootstrapCacheLoader the loader to be used
2176: * @throws CacheException if this method is called after the cache is initialized
2177: */
2178: public void setBootstrapCacheLoader(
2179: BootstrapCacheLoader bootstrapCacheLoader)
2180: throws CacheException {
2181: if (!status.equals(Status.STATUS_UNINITIALISED)) {
2182: throw new CacheException(
2183: "A bootstrap cache loader can only be set before the cache is initialized. "
2184: + configuration.getName());
2185: }
2186: this .bootstrapCacheLoader = bootstrapCacheLoader;
2187: }
2188:
2189: /**
2190: * DiskStore paths can conflict between CacheManager instances. This method allows the path to be changed.
2191: *
2192: * @param diskStorePath the new path to be used.
2193: * @throws CacheException if this method is called after the cache is initialized
2194: */
2195: public void setDiskStorePath(String diskStorePath)
2196: throws CacheException {
2197: if (!status.equals(Status.STATUS_UNINITIALISED)) {
2198: throw new CacheException(
2199: "A DiskStore path can only be set before the cache is initialized. "
2200: + configuration.getName());
2201: }
2202: this .diskStorePath = diskStorePath;
2203: }
2204:
2205: /**
2206: * An equals method which follows the contract of {@link Object#equals(Object)}
2207: * <p/>
2208: * An Cache is equal to another one if it implements Ehcache and has the same GUID.
2209: *
2210: * @param object the reference object with which to compare.
2211: * @return <code>true</code> if this object is the same as the obj
2212: * argument; <code>false</code> otherwise.
2213: * @see #hashCode()
2214: * @see java.util.Hashtable
2215: */
2216: public boolean equals(Object object) {
2217: if (object == null) {
2218: return false;
2219: }
2220: if (!(object instanceof Ehcache)) {
2221: return false;
2222: }
2223: Ehcache other = (Ehcache) object;
2224: return guid.equals(other.getGuid());
2225: }
2226:
2227: /**
2228: * Returns a hash code value for the object. This method is
2229: * supported for the benefit of hashtables such as those provided by
2230: * <code>java.util.Hashtable</code>.
2231: * <p/>
2232: * The general contract of <code>hashCode</code> is:
2233: * <ul>
2234: * <li>Whenever it is invoked on the same object more than once during
2235: * an execution of a Java application, the <tt>hashCode</tt> method
2236: * must consistently return the same integer, provided no information
2237: * used in <tt>equals</tt> comparisons on the object is modified.
2238: * This integer need not remain consistent from one execution of an
2239: * application to another execution of the same application.
2240: * <li>If two objects are equal according to the <tt>equals(Object)</tt>
2241: * method, then calling the <code>hashCode</code> method on each of
2242: * the two objects must produce the same integer result.
2243: * <li>It is <em>not</em> required that if two objects are unequal
2244: * according to the {@link Object#equals(Object)}
2245: * method, then calling the <tt>hashCode</tt> method on each of the
2246: * two objects must produce distinct integer results. However, the
2247: * programmer should be aware that producing distinct integer results
2248: * for unequal objects may improve the performance of hashtables.
2249: * </ul>
2250: * <p/>
2251: * As much as is reasonably practical, the hashCode method defined by
2252: * class <tt>Object</tt> does return distinct integers for distinct
2253: * objects. (This is typically implemented by converting the internal
2254: * address of the object into an integer, but this implementation
2255: * technique is not required by the
2256: * Java<font size="-2"><sup>TM</sup></font> programming language.)
2257: * <p/>
2258: * This implementation use the GUID of the cache.
2259: *
2260: * @return a hash code value for this object.
2261: * @see Object#equals(Object)
2262: * @see java.util.Hashtable
2263: */
2264: public int hashCode() {
2265: return guid.hashCode();
2266: }
2267:
2268: /**
2269: * Create globally unique ID for this cache.
2270: */
2271: private String createGuid() {
2272: return new StringBuffer().append(localhost).append("-").append(
2273: new UID()).toString();
2274: }
2275:
2276: /**
2277: * Register a {@link CacheExtension} with the cache. It will then be tied into the cache lifecycle.
2278: * <p/>
2279: * If the CacheExtension is not initialised, initialise it.
2280: */
2281: public void registerCacheExtension(CacheExtension cacheExtension) {
2282: registeredCacheExtensions.add(cacheExtension);
2283: }
2284:
2285: /**
2286: * Unregister a {@link CacheExtension} with the cache. It will then be detached from the cache lifecycle.
2287: */
2288: public void unregisterCacheExtension(CacheExtension cacheExtension) {
2289: registeredCacheExtensions.remove(cacheExtension);
2290: }
2291:
2292: private List createNewCacheExtensionsList() {
2293: return Collections.synchronizedList(new ArrayList());
2294: }
2295:
2296: /**
2297: * The average get time in ms.
2298: */
2299: public float getAverageGetTime() {
2300: if (hitCount == 0) {
2301: return 0;
2302: } else {
2303: return (float) totalGetTime / hitCount;
2304: }
2305: }
2306:
2307: /**
2308: * Sets an ExceptionHandler on the Cache. If one is already set, it is overwritten.
2309: * <p/>
2310: * The ExceptionHandler is only used if this Cache's methods are accessed using
2311: * {@link net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy}.
2312: *
2313: * @see net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy
2314: */
2315: public void setCacheExceptionHandler(
2316: CacheExceptionHandler cacheExceptionHandler) {
2317: this .cacheExceptionHandler = cacheExceptionHandler;
2318: }
2319:
2320: /**
2321: * Gets the ExceptionHandler on this Cache, or null if there isn't one.
2322: * <p/>
2323: * The ExceptionHandler is only used if this Cache's methods are accessed using
2324: * {@link net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy}.
2325: *
2326: * @see net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy
2327: */
2328: public CacheExceptionHandler getCacheExceptionHandler() {
2329: return cacheExceptionHandler;
2330: }
2331:
2332: /**
2333: * Setter for the CacheLoader. Changing the CacheLoader takes immediate effect.
2334: *
2335: * @param cacheLoader the loader to dynamically load new cache entries
2336: */
2337: public void setCacheLoader(CacheLoader cacheLoader) {
2338: this .cacheLoader = cacheLoader;
2339: }
2340:
2341: /**
2342: * Gets the CacheLoader registered in this cache
2343: *
2344: * @return the loader, or null if there is none
2345: */
2346: public CacheLoader getCacheLoader() {
2347: return cacheLoader;
2348: }
2349:
2350: /**
2351: * Used to store a future and the key it is in respect of
2352: */
2353: class KeyedFuture {
2354:
2355: private Object key;
2356: private Future future;
2357:
2358: /**
2359: * Full constructor
2360: *
2361: * @param key
2362: * @param future
2363: */
2364: public KeyedFuture(Object key, Future future) {
2365: this .key = key;
2366: this .future = future;
2367: }
2368: }
2369:
2370: /**
2371: * Does the asynchronous loading.
2372: *
2373: * @param specificLoader a specific loader to use. If null the default loader is used.
2374: * @return a Future which can be used to monitor execution
2375: */
2376: Future asynchronousLoad(final Object key,
2377: final CacheLoader specificLoader, final Object argument) {
2378: Future future = getExecutorService().submit(new Runnable() {
2379:
2380: /**
2381: * Calls the CacheLoader and puts the result in the Cache
2382: */
2383: public void run() throws CacheException {
2384: try {
2385: //Test to see if it has turned up in the meantime
2386: boolean existsOnRun = isKeyInCache(key);
2387: if (!existsOnRun) {
2388: Object value;
2389: if (specificLoader == null) {
2390: if (cacheLoader == null) {
2391: return;
2392: }
2393: if (argument == null) {
2394: value = cacheLoader.load(key);
2395: } else {
2396: value = cacheLoader.load(key, argument);
2397: }
2398: } else {
2399: if (argument == null) {
2400: value = specificLoader.load(key);
2401: } else {
2402: value = specificLoader.load(key,
2403: argument);
2404: }
2405: }
2406: put(new Element(key, value), false);
2407: }
2408: } catch (Throwable e) {
2409: if (LOG.isDebugEnabled()) {
2410: LOG.debug(
2411: "Problem during load. Load will not be completed. Cause was "
2412: + e.getCause(), e);
2413: }
2414: throw new CacheException(e);
2415: }
2416: }
2417: });
2418: return future;
2419: }
2420:
2421: /**
2422: * Does the asynchronous loading.
2423: *
2424: * @param argument the loader argument
2425: * @return a Future which can be used to monitor execution
2426: */
2427: Future asynchronousLoadAll(final Collection keys,
2428: final Object argument) {
2429: Future future = getExecutorService().submit(new Runnable() {
2430: /**
2431: * Calls the CacheLoader and puts the result in the Cache
2432: */
2433: public void run() {
2434: try {
2435: List nonLoadedKeys = new ArrayList(keys.size());
2436: for (Iterator iterator = keys.iterator(); iterator
2437: .hasNext();) {
2438: Object key = iterator.next();
2439: if (!isKeyInCache(key)) {
2440: nonLoadedKeys.add(key);
2441: }
2442: }
2443: Map map;
2444: if (argument == null) {
2445: map = cacheLoader.loadAll(nonLoadedKeys);
2446: } else {
2447: map = cacheLoader.loadAll(nonLoadedKeys,
2448: argument);
2449: }
2450:
2451: for (Iterator iterator = map.keySet().iterator(); iterator
2452: .hasNext();) {
2453: Object key = iterator.next();
2454: put(new Element(key, map.get(key)));
2455: }
2456: } catch (Throwable e) {
2457: if (LOG.isDebugEnabled()) {
2458: LOG.debug(
2459: "Problem during load. Load will not be completed. Cause was "
2460: + e.getCause(), e);
2461: }
2462: }
2463: }
2464: });
2465: return future;
2466: }
2467:
2468: /**
2469: * @return Gets the executor service. This is not publically accessible.
2470: */
2471: ThreadPoolExecutor getExecutorService() {
2472: if (executorService == null) {
2473: synchronized (this ) {
2474: executorService = new ThreadPoolExecutor(
2475: EXECUTOR_CORE_POOL_SIZE,
2476: EXECUTOR_MAXIMUM_POOL_SIZE,
2477: EXECUTOR_KEEP_ALIVE_TIME,
2478: TimeUnit.MILLISECONDS,
2479: new LinkedBlockingQueue());
2480: }
2481: }
2482: return executorService;
2483: }
2484: }
|