0001: /*
0002: * All content copyright (c) 2003-2007 Terracotta, Inc., except as may otherwise be noted in a separate copyright
0003: * notice. All rights reserved.
0004: */
0005: package net.sf.ehcache;
0006:
0007: import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
0008: import net.sf.ehcache.config.CacheConfiguration;
0009: import net.sf.ehcache.event.CacheEventListener;
0010: import net.sf.ehcache.event.RegisteredEventListeners;
0011: import net.sf.ehcache.store.DiskStore;
0012: import net.sf.ehcache.store.MemoryStore;
0013: import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
0014: import net.sf.ehcache.store.TimeExpiryMemoryStore;
0015:
0016: import org.apache.commons.logging.Log;
0017: import org.apache.commons.logging.LogFactory;
0018: import org.terracotta.modules.ehcache.commons_1_0.util.Util;
0019:
0020: import com.tc.object.bytecode.ManagerUtil;
0021: import com.tc.properties.TCProperties;
0022: import com.tc.util.Assert;
0023:
0024: import java.io.Serializable;
0025: import java.net.InetAddress;
0026: import java.net.UnknownHostException;
0027: import java.rmi.server.UID;
0028: import java.util.ArrayList;
0029: import java.util.Arrays;
0030: import java.util.Iterator;
0031: import java.util.List;
0032: import java.util.Set;
0033:
0034: /**
0035: * This is a modification of the Ehcache net.sf.ehcache.Cache.java class, which is under the Apache open source license.
0036: * In this implementation, DiskStore is not supported, and thus, most synchronized are removed since the memory store
0037: * methods are being synchronized.
0038: */
0039: public class CacheTC implements Ehcache {
0040: /**
0041: * A reserved word for cache names. It denotes a default configuration which is applied to caches created without
0042: * configuration.
0043: */
0044: public static final String DEFAULT_CACHE_NAME = "default";
0045:
0046: /**
0047: * System Property based method of disabling ehcache. If disabled no elements will be added to a cache. <p/> Set the
0048: * property "net.sf.ehcache.disabled=true" to disable ehcache. <p/> This can easily be done using
0049: * <code>java -Dnet.sf.ehcache.disabled=true</code> in the command line.
0050: */
0051: public static final String NET_SF_EHCACHE_DISABLED = "net.sf.ehcache.disabled";
0052:
0053: {
0054: String value = System.getProperty(NET_SF_EHCACHE_DISABLED);
0055: if (value != null) {
0056: disabled = value.equalsIgnoreCase("true");
0057: }
0058: }
0059:
0060: /**
0061: * The default interval between runs of the expiry thread.
0062: */
0063: public static final long DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS = 120;
0064:
0065: private static final Log LOG = LogFactory.getLog(Cache.class
0066: .getName());
0067:
0068: private static final MemoryStoreEvictionPolicy DSO_MEMORY_STORE_EVICTION_POLICY;
0069: private static final MemoryStoreEvictionPolicy DEFAULT_MEMORY_STORE_EVICTION_POLICY;
0070:
0071: private static InetAddress localhost;
0072:
0073: private int readLockLevel = Util.getEhcacheReadLockLevel();
0074: private int writeLockLevel = Util.getEhcacheWriteLockLevel();
0075:
0076: static {
0077: DSO_MEMORY_STORE_EVICTION_POLICY = MemoryStoreEvictionPolicy
0078: .fromString("DSO");
0079: DEFAULT_MEMORY_STORE_EVICTION_POLICY = DSO_MEMORY_STORE_EVICTION_POLICY;
0080:
0081: try {
0082: localhost = InetAddress.getLocalHost();
0083: } catch (UnknownHostException e) {
0084: LOG.error(
0085: "Unable to set localhost. This prevents creation of a GUID. Cause was: "
0086: + e.getMessage(), e);
0087: }
0088: }
0089:
0090: private boolean disabled;
0091:
0092: private String name;
0093:
0094: private Status status;
0095:
0096: private final int maxElementsInMemory;
0097:
0098: private MemoryStoreEvictionPolicy memoryStoreEvictionPolicy;
0099:
0100: private int statisticsAccuracy = Statistics.STATISTICS_ACCURACY_BEST_EFFORT;
0101:
0102: /**
0103: * The interval in seconds between runs of the disk expiry thread. 2 minutes is the default. This is not the same
0104: * thing as time to live or time to idle. When the thread runs it checks these things. So this value is how often we
0105: * check for expiry.
0106: */
0107: private final long diskExpiryThreadIntervalSeconds;
0108:
0109: /**
0110: * The maximum time between creation time and when an element expires. Is only used if the element is not eternal.
0111: */
0112: private final long timeToLiveSeconds;
0113:
0114: /**
0115: * The maximum amount of time between {@link #get(Object)}s before an element expires.
0116: */
0117: private final long timeToIdleSeconds;
0118:
0119: /**
0120: * The {@link MemoryStore} of this {@link CacheTC}. All caches have a memory store.
0121: */
0122: private TimeExpiryMemoryStore memoryStore;
0123: private final Object[] locks;
0124:
0125: private RegisteredEventListeners registeredEventListeners;
0126:
0127: private final String guid = new StringBuffer().append(localhost)
0128: .append("-").append(new UID()).toString();
0129:
0130: private CacheManager cacheManager;
0131:
0132: private BootstrapCacheLoader bootstrapCacheLoader;
0133:
0134: private CacheConfiguration configuration;
0135:
0136: public static CacheTC convert(Ehcache cache) {
0137: CacheTC tcCache = new CacheTC(cache.getName(), cache
0138: .getMaxElementsInMemory(), cache
0139: .getMemoryStoreEvictionPolicy(), false, null, false,
0140: cache.getTimeToLiveSeconds(), cache
0141: .getTimeToIdleSeconds(), false, cache
0142: .getDiskExpiryThreadIntervalSeconds(), cache
0143: .getCacheEventNotificationService(), cache
0144: .getBootstrapCacheLoader(), cache
0145: .getMaxElementsOnDisk());
0146: return tcCache;
0147: }
0148:
0149: /**
0150: * 1.0 Constructor. <p/> The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these. <p/> A
0151: * client can specify their own settings here and pass the {@link CacheTC} object into {@link CacheManager#addCache}
0152: * to specify parameters other than the defaults. <p/> Only the CacheManager can initialise them. <p/> This
0153: * constructor creates disk stores, if specified, that do not persist between restarts. <p/> The default expiry thread
0154: * interval of 120 seconds is used. This is the interval between runs of the expiry thread, where it checks the disk
0155: * store for expired elements. It is not the the timeToLiveSeconds.
0156: *
0157: * @param name the name of the cache
0158: * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
0159: * @param overflowToDisk whether to use the disk store; always false
0160: * @param eternal whether the elements in the cache are eternal, i.e. never expire; always false
0161: * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
0162: * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
0163: * @since 1.0
0164: */
0165: public CacheTC(String name, int maxElementsInMemory,
0166: boolean overflowToDisk, boolean eternal,
0167: long timeToLiveSeconds, long timeToIdleSeconds) {
0168: // overflowToDisk and diskPersistent are always false
0169: // eternal is always false
0170: this (name, maxElementsInMemory,
0171: DEFAULT_MEMORY_STORE_EVICTION_POLICY, false, null,
0172: false, timeToLiveSeconds, timeToIdleSeconds, false,
0173: DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS, null, null);
0174: }
0175:
0176: /**
0177: * 1.1 Constructor. <p/> The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these. <p/> A
0178: * client can specify their own settings here and pass the {@link CacheTC} object into {@link CacheManager#addCache}
0179: * to specify parameters other than the defaults. <p/> Only the CacheManager can initialise them.
0180: *
0181: * @param name the name of the cache
0182: * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
0183: * @param overflowToDisk whether to use the disk store; always false
0184: * @param eternal whether the elements in the cache are eternal, i.e. never expire; always false
0185: * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
0186: * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
0187: * @param diskPersistent whether to persist the cache to disk between JVM restarts; always false
0188: * @param diskExpiryThreadIntervalSeconds how often to run the disk store expiry thread. A large number of 120 seconds
0189: * plus is recommended
0190: * @since 1.1
0191: */
0192: public CacheTC(String name, int maxElementsInMemory,
0193: boolean overflowToDisk, boolean eternal,
0194: long timeToLiveSeconds, long timeToIdleSeconds,
0195: boolean diskPersistent, long diskExpiryThreadIntervalSeconds) {
0196: // overflowToDisk and diskPersistent are always false
0197: // eternal is always false
0198: this (name, maxElementsInMemory,
0199: DEFAULT_MEMORY_STORE_EVICTION_POLICY, false, null,
0200: false, timeToLiveSeconds, timeToIdleSeconds, false,
0201: diskExpiryThreadIntervalSeconds, null, null);
0202: LOG
0203: .warn("An API change between ehcache-1.1 and ehcache-1.2 results in the persistence path being set to java.io.tmp"
0204: + " when the ehcache-1.1 constructor is used. Please change to the 1.2 constructor");
0205: }
0206:
0207: /**
0208: * 1.2 Constructor <p/> The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these. <p/> A
0209: * client can specify their own settings here and pass the {@link CacheTC} object into {@link CacheManager#addCache}
0210: * to specify parameters other than the defaults. <p/> Only the CacheManager can initialise them.
0211: *
0212: * @param name the name of the cache
0213: * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
0214: * @param memoryStoreEvictionPolicy always uses DSO.
0215: * @param overflowToDisk whether to use the disk store; always false
0216: * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
0217: * @param eternal whether the elements in the cache are eternal, i.e. never expire; always false
0218: * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
0219: * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
0220: * @param diskPersistent whether to persist the cache to disk between JVM restarts; always false
0221: * @param diskExpiryThreadIntervalSeconds how often to run the disk store expiry thread. A large number of 120 seconds
0222: * plus is recommended
0223: * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered
0224: * listeners will be created.
0225: * @since 1.2
0226: */
0227: public CacheTC(String name, int maxElementsInMemory,
0228: MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
0229: boolean overflowToDisk, String diskStorePath,
0230: boolean eternal, long timeToLiveSeconds,
0231: long timeToIdleSeconds, boolean diskPersistent,
0232: long diskExpiryThreadIntervalSeconds,
0233: RegisteredEventListeners registeredEventListeners) {
0234: // overflowToDisk and diskPersistent are always false
0235: // eternal is always false
0236: this (name, maxElementsInMemory,
0237: DSO_MEMORY_STORE_EVICTION_POLICY, false, diskStorePath,
0238: false, timeToLiveSeconds, timeToIdleSeconds, false,
0239: diskExpiryThreadIntervalSeconds,
0240: registeredEventListeners, null);
0241: }
0242:
0243: /**
0244: * 1.2.1 Constructor <p/> The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these. <p/> A
0245: * client can specify their own settings here and pass the {@link CacheTC} object into {@link CacheManager#addCache}
0246: * to specify parameters other than the defaults. <p/> Only the CacheManager can initialise them.
0247: *
0248: * @param name the name of the cache
0249: * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
0250: * @param memoryStoreEvictionPolicy always uses DSO.
0251: * @param overflowToDisk whether to use the disk store; always false
0252: * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
0253: * @param eternal whether the elements in the cache are eternal, i.e. never expire; always false
0254: * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
0255: * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
0256: * @param diskPersistent whether to persist the cache to disk between JVM restarts; always false
0257: * @param diskExpiryThreadIntervalSeconds how often to run the disk store expiry thread. A large number of 120 seconds
0258: * plus is recommended
0259: * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered
0260: * listeners will be created.
0261: * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised.
0262: * Null if none is required.
0263: * @since 1.2.1
0264: */
0265: public CacheTC(String name, int maxElementsInMemory,
0266: MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
0267: boolean overflowToDisk, String diskStorePath,
0268: boolean eternal, long timeToLiveSeconds,
0269: long timeToIdleSeconds, boolean diskPersistent,
0270: long diskExpiryThreadIntervalSeconds,
0271: RegisteredEventListeners registeredEventListeners,
0272: BootstrapCacheLoader bootstrapCacheLoader) {
0273: // overflowToDisk and diskPersistent are always false
0274: // eternal is always false
0275: this (name, maxElementsInMemory,
0276: DSO_MEMORY_STORE_EVICTION_POLICY, false, diskStorePath,
0277: false, timeToLiveSeconds, timeToIdleSeconds, false,
0278: diskExpiryThreadIntervalSeconds,
0279: registeredEventListeners, bootstrapCacheLoader, 0);
0280: }
0281:
0282: /**
0283: * 1.2.4 Constructor <p/> The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these. <p/> A
0284: * client can specify their own settings here and pass the {@link CacheTC} object into {@link CacheManager#addCache}
0285: * to specify parameters other than the defaults. <p/> Only the CacheManager can initialise them.
0286: *
0287: * @param name the name of the cache
0288: * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
0289: * @param memoryStoreEvictionPolicy always uses DSO.
0290: * @param overflowToDisk whether to use the disk store; always false
0291: * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
0292: * @param eternal whether the elements in the cache are eternal, i.e. never expire; always false
0293: * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
0294: * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
0295: * @param diskPersistent whether to persist the cache to disk between JVM restarts; always false
0296: * @param diskExpiryThreadIntervalSeconds how often to run the disk store expiry thread. A large number of 120 seconds
0297: * plus is recommended
0298: * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered
0299: * listeners will be created.
0300: * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised.
0301: * Null if none is required.
0302: * @since 1.2.4
0303: */
0304: public CacheTC(String name, int maxElementsInMemory,
0305: MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
0306: boolean overflowToDisk, String diskStorePath,
0307: boolean eternal, long timeToLiveSeconds,
0308: long timeToIdleSeconds, boolean diskPersistent,
0309: long diskExpiryThreadIntervalSeconds,
0310: RegisteredEventListeners registeredEventListeners,
0311: BootstrapCacheLoader bootstrapCacheLoader,
0312: int maxElementsOnDisk) {
0313: // overflowToDisk and diskPersistent are always false
0314: changeStatus(Status.STATUS_UNINITIALISED);
0315:
0316: configuration = new CacheConfiguration();
0317: configuration.setName(name);
0318: configuration.setMaxElementsInMemory(maxElementsInMemory);
0319: configuration
0320: .setMemoryStoreEvictionPolicyFromObject(memoryStoreEvictionPolicy);
0321: configuration.setOverflowToDisk(overflowToDisk);
0322: configuration.setEternal(eternal);
0323: configuration.setTimeToLiveSeconds(timeToLiveSeconds);
0324: configuration.setTimeToIdleSeconds(timeToIdleSeconds);
0325: configuration.setDiskPersistent(diskPersistent);
0326: configuration.setMaxElementsOnDisk(maxElementsOnDisk);
0327:
0328: setName(name);
0329: this .maxElementsInMemory = maxElementsInMemory;
0330: this .memoryStoreEvictionPolicy = DSO_MEMORY_STORE_EVICTION_POLICY;
0331: this .timeToLiveSeconds = timeToLiveSeconds;
0332: this .timeToIdleSeconds = timeToIdleSeconds;
0333:
0334: if (registeredEventListeners == null) {
0335: this .registeredEventListeners = new RegisteredEventListeners(
0336: this );
0337: } else {
0338: this .registeredEventListeners = registeredEventListeners;
0339: }
0340:
0341: // Set this to a safe value.
0342: if (diskExpiryThreadIntervalSeconds == 0) {
0343: this .diskExpiryThreadIntervalSeconds = DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS;
0344: } else {
0345: this .diskExpiryThreadIntervalSeconds = diskExpiryThreadIntervalSeconds;
0346: }
0347:
0348: // For backward compatibility with 1.1 and earlier
0349: if (memoryStoreEvictionPolicy == null) {
0350: this .memoryStoreEvictionPolicy = DEFAULT_MEMORY_STORE_EVICTION_POLICY;
0351: }
0352:
0353: this .bootstrapCacheLoader = bootstrapCacheLoader;
0354:
0355: TCProperties ehcacheProperies = ManagerUtil.getTCProperties()
0356: .getPropertiesFor("ehcache");
0357: int concurrency = ehcacheProperies.getInt("concurrency");
0358: if (concurrency <= 1) {
0359: concurrency = 1;
0360: }
0361: this .locks = new Object[concurrency];
0362: for (int i = 0; i < concurrency; i++) {
0363: this .locks[i] = new Object();
0364: }
0365: }
0366:
0367: // diskSpoolBufferSizeMB is ignored
0368: public CacheTC(String name, int maxElementsInMemory,
0369: MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
0370: boolean overflowToDisk, String diskStorePath,
0371: boolean eternal, long timeToLiveSeconds,
0372: long timeToIdleSeconds, boolean diskPersistent,
0373: long diskExpiryThreadIntervalSeconds,
0374: RegisteredEventListeners registeredEventListeners,
0375: BootstrapCacheLoader bootstrapCacheLoader,
0376: int maxElementsOnDisk, int diskSpoolBufferSizeMB) {
0377: this (name, maxElementsInMemory,
0378: DSO_MEMORY_STORE_EVICTION_POLICY, false, diskStorePath,
0379: false, timeToLiveSeconds, timeToIdleSeconds, false,
0380: diskExpiryThreadIntervalSeconds,
0381: registeredEventListeners, bootstrapCacheLoader, 0);
0382: }
0383:
0384: /**
0385: * Newly created caches do not have a {@link net.sf.ehcache.store.MemoryStore} or a
0386: * {@link net.sf.ehcache.store.DiskStore}. <p/> This method creates those and makes the cache ready to accept
0387: * elements
0388: */
0389: public void initialise() {
0390: synchronized (this ) {
0391: if (!status.equals(Status.STATUS_UNINITIALISED)) {
0392: throw new IllegalStateException(
0393: "Cannot initialise the "
0394: + name
0395: + " cache because its status is not STATUS_UNINITIALISED");
0396: }
0397:
0398: if (maxElementsInMemory == 0) {
0399: if (LOG.isWarnEnabled()) {
0400: LOG
0401: .warn("Cache: "
0402: + name
0403: + " has a maxElementsInMemory of 0. It is strongly recommended to "
0404: + "have a maximumSize of at least 1. Performance is halved by not using a MemoryStore.");
0405: }
0406: }
0407:
0408: MemoryStore ms = MemoryStore.create(this , null);
0409: Assert.post(ms instanceof TimeExpiryMemoryStore);
0410:
0411: memoryStore = (TimeExpiryMemoryStore) ms;
0412: changeStatus(Status.STATUS_ALIVE);
0413: }
0414:
0415: if (LOG.isDebugEnabled()) {
0416: LOG.debug("Initialised cache: " + name);
0417: }
0418:
0419: if (disabled) {
0420: if (LOG.isWarnEnabled()) {
0421: LOG
0422: .warn("Cache: "
0423: + name
0424: + " is disabled because the "
0425: + NET_SF_EHCACHE_DISABLED
0426: + " property was set to true. No elements will be added to the cache.");
0427: }
0428: }
0429: }
0430:
0431: /**
0432: * Bootstrap command. This must be called after the Cache is intialised, during CacheManager initialisation. If loads
0433: * are synchronous, they will complete before the CacheManager initialise completes, otherwise they will happen in the
0434: * background.
0435: */
0436: public void bootstrap() {
0437: if (!disabled && bootstrapCacheLoader != null) {
0438: bootstrapCacheLoader.load(this );
0439: }
0440:
0441: }
0442:
0443: private void changeStatus(Status status) {
0444: this .status = status;
0445: }
0446:
0447: /**
0448: * Put an element in the cache. <p/> Resets the access statistics on the element, which would be the case if it has
0449: * previously been gotten from a cache, and is now being put back. <p/> Also notifies the CacheEventListener that:
0450: * <ul>
0451: * <li>the element was put, but only if the Element was actually put.
0452: * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired if it
0453: * was requested
0454: * </ul>
0455: * Synchronization is handled within the method.
0456: *
0457: * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
0458: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0459: * @throws IllegalArgumentException if the element is null
0460: */
0461: public final void put(Element element)
0462: throws IllegalArgumentException, IllegalStateException,
0463: CacheException {
0464: put(element, false);
0465: }
0466:
0467: /**
0468: * Put an element in the cache. <p/> Resets the access statistics on the element, which would be the case if it has
0469: * previously been gotten from a cache, and is now being put back. <p/> Also notifies the CacheEventListener that:
0470: * <ul>
0471: * <li>the element was put, but only if the Element was actually put.
0472: * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired if it
0473: * was requested
0474: * </ul>
0475: * Synchronization is handled within the method.
0476: *
0477: * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
0478: * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in
0479: * which case this put should not initiate a further notification to doNotNotifyCacheReplicators cache peers
0480: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0481: * @throws IllegalArgumentException if the element is null
0482: */
0483: public final void put(Element element,
0484: boolean doNotNotifyCacheReplicators)
0485: throws IllegalArgumentException, IllegalStateException,
0486: CacheException {
0487: checkStatus();
0488:
0489: if (disabled) {
0490: return;
0491: }
0492:
0493: if (element == null) {
0494: throw new IllegalArgumentException("Element cannot be null");
0495: }
0496:
0497: Object key = element.getObjectKey();
0498: Object lock = getLockObject(key);
0499: ManagerUtil.monitorEnter(lock, writeLockLevel);
0500: try {
0501: element.resetAccessStatistics();
0502: boolean elementExists;
0503: elementExists = isElementInMemoryUnlock(key)
0504: || isElementOnDisk(key);
0505: if (elementExists) {
0506: element.updateUpdateStatistics();
0507: }
0508: applyDefaultsToElementWithoutLifespanSet(element);
0509:
0510: memoryStore.putData(element);
0511: if (elementExists) {
0512: registeredEventListeners.notifyElementUpdated(element,
0513: doNotNotifyCacheReplicators);
0514: } else {
0515: registeredEventListeners.notifyElementPut(element,
0516: doNotNotifyCacheReplicators);
0517: }
0518: } finally {
0519: ManagerUtil.monitorExit(lock);
0520: }
0521:
0522: }
0523:
0524: private void applyDefaultsToElementWithoutLifespanSet(
0525: Element element) {
0526: if (!element.isLifespanSet()) {
0527: // Setting with Cache defaults
0528: // Eternal is always false
0529: element.setTimeToLive((int) timeToLiveSeconds);
0530: element.setTimeToIdle((int) timeToIdleSeconds);
0531: element.setEternal(false);
0532: }
0533: }
0534:
0535: /**
0536: * Put an element in the cache, without updating statistics, or updating listeners. This is meant to be used in
0537: * conjunction with {@link #getQuiet}. Synchronization is handled within the method.
0538: *
0539: * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
0540: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0541: * @throws IllegalArgumentException if the element is null
0542: */
0543: public final void putQuiet(Element element)
0544: throws IllegalArgumentException, IllegalStateException,
0545: CacheException {
0546: checkStatus();
0547:
0548: if (disabled) {
0549: return;
0550: }
0551:
0552: if (element == null) {
0553: throw new IllegalArgumentException("Element cannot be null");
0554: }
0555:
0556: Object key = element.getObjectKey();
0557: Object lock = getLockObject(key);
0558: ManagerUtil.monitorEnter(lock, writeLockLevel);
0559: try {
0560: applyDefaultsToElementWithoutLifespanSet(element);
0561:
0562: memoryStore.putData(element);
0563: } finally {
0564: ManagerUtil.monitorExit(lock);
0565: }
0566: }
0567:
0568: /**
0569: * Gets an element from the cache. Updates Element Statistics <p/> Note that the Element's lastAccessTime is always
0570: * the time of this get. Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
0571: * <p/> Synchronization is handled within the method.
0572: *
0573: * @param key a serializable value
0574: * @return the element, or null, if it does not exist.
0575: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0576: * @see #isExpired
0577: */
0578: public final Element get(Serializable key)
0579: throws IllegalStateException, CacheException {
0580: return get((Object) key);
0581: }
0582:
0583: /**
0584: * Gets an element from the cache. Updates Element Statistics <p/> Note that the Element's lastAccessTime is always
0585: * the time of this get. Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
0586: * <p/> Synchronization is handled within the method.
0587: *
0588: * @param key an Object value
0589: * @return the element, or null, if it does not exist.
0590: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0591: * @see #isExpired
0592: * @since 1.2
0593: */
0594: public final Element get(Object key) throws IllegalStateException,
0595: CacheException {
0596: checkStatus();
0597: Element element = searchInMemoryStore(key, true);
0598: return element;
0599: }
0600:
0601: /**
0602: * Gets an element from the cache, without updating Element statistics. Cache statistics are still updated. <p/>
0603: * Synchronization is handled within the method.
0604: *
0605: * @param key a serializable value
0606: * @return the element, or null, if it does not exist.
0607: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0608: * @see #isExpired
0609: */
0610: public final Element getQuiet(Serializable key)
0611: throws IllegalStateException, CacheException {
0612: return getQuiet((Object) key);
0613: }
0614:
0615: /**
0616: * Gets an element from the cache, without updating Element statistics. Cache statistics are not updated. <p/>
0617: * Synchronization is handled within the method.
0618: *
0619: * @param key a serializable value
0620: * @return the element, or null, if it does not exist.
0621: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0622: * @see #isExpired
0623: * @since 1.2
0624: */
0625: public final Element getQuiet(Object key)
0626: throws IllegalStateException, CacheException {
0627: return get(key);
0628: }
0629:
0630: /**
0631: * Returns a list of all element keys in the cache, whether or not they are expired. <p/> The returned keys are unique
0632: * and can be considered a set. <p/> The List returned is not live. It is a copy. <p/> The time taken is O(n). On a
0633: * single cpu 1.8Ghz P4, approximately 8ms is required for each 1000 entries.
0634: *
0635: * @return a list of {@link Object} keys
0636: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0637: */
0638: public final List getKeys() throws IllegalStateException,
0639: CacheException {
0640: checkStatus();
0641: /*
0642: * An element with the same key can exist in both the memory store and the disk store at the same time. Because the
0643: * memory store is always searched first these duplicates do not cause problems when getting elements/ This method
0644: * removes these duplicates before returning the list of keys
0645: */
0646: List allKeyList = new ArrayList();
0647: Object[] keyArrays = null;
0648: readLockAll();
0649: try {
0650: keyArrays = memoryStore.getKeyArray();
0651: } finally {
0652: unlockAll();
0653: }
0654: if (keyArrays == null) {
0655: return allKeyList;
0656: }
0657:
0658: List keyList = Arrays.asList(keyArrays);
0659: allKeyList.addAll(keyList);
0660: return allKeyList;
0661: }
0662:
0663: /**
0664: * Returns a list of all element keys in the cache. Only keys of non-expired elements are returned. <p/> The returned
0665: * keys are unique and can be considered a set. <p/> The List returned is not live. It is a copy. <p/> The time taken
0666: * is O(n), where n is the number of elements in the cache. On a 1.8Ghz P4, the time taken is approximately 200ms per
0667: * 1000 entries. This method is not syncrhonized, because it relies on a non-live list returned from
0668: * {@link #getKeys()} , which is synchronised, and which takes 8ms per 1000 entries. This way cache liveness is
0669: * preserved, even if this method is very slow to return. <p/> Consider whether your usage requires checking for
0670: * expired keys. Because this method takes so long, depending on cache settings, the list could be quite out of date
0671: * by the time you get it.
0672: *
0673: * @return a list of {@link Object} keys
0674: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0675: */
0676: public final List getKeysWithExpiryCheck()
0677: throws IllegalStateException, CacheException {
0678: List allKeyList = getKeys();
0679: // remove keys of expired elements
0680: ArrayList nonExpiredKeys = new ArrayList(allKeyList.size());
0681: int allKeyListSize = allKeyList.size();
0682: for (int i = 0; i < allKeyListSize; i++) {
0683: Object key = allKeyList.get(i);
0684: Element element = getQuiet(key);
0685: if (element != null) {
0686: nonExpiredKeys.add(key);
0687: }
0688: }
0689: nonExpiredKeys.trimToSize();
0690: return nonExpiredKeys;
0691: }
0692:
0693: /**
0694: * Returns a list of all elements in the cache, whether or not they are expired. <p/> The returned keys are not unique
0695: * and may contain duplicates. If the cache is only using the memory store, the list will be unique. If the disk store
0696: * is being used as well, it will likely contain duplicates, because of the internal store design. <p/> The List
0697: * returned is not live. It is a copy. <p/> The time taken is O(log n). On a single cpu 1.8Ghz P4, approximately 6ms
0698: * is required for 1000 entries and 36 for 50000. <p/> This is the fastest getKeys method
0699: *
0700: * @return a list of {@link Object} keys
0701: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0702: */
0703: public final List getKeysNoDuplicateCheck()
0704: throws IllegalStateException {
0705: // since overflowToDisk is always false, this is the same
0706: // as getKeys().
0707: return getKeys();
0708: }
0709:
0710: private Element searchInMemoryStore(Object key,
0711: boolean updateStatistics) {
0712: Element element;
0713: Object lock = getLockObject(key);
0714: ManagerUtil.monitorEnter(lock, readLockLevel);
0715: try {
0716: if (updateStatistics) {
0717: element = memoryStore.get(key);
0718: } else {
0719: element = memoryStore.getQuiet(key);
0720: }
0721: } finally {
0722: ManagerUtil.monitorExit(lock);
0723: }
0724: return element;
0725: }
0726:
0727: /**
0728: * Removes an {@link Element} from the Cache. This also removes it from any stores it may be in. <p/> Also notifies
0729: * the CacheEventListener after the element was removed, but only if an Element with the key actually existed. <p/>
0730: * Synchronization is handled within the method.
0731: *
0732: * @param key the element key to operate on
0733: * @return true if the element was removed, false if it was not found in the cache
0734: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0735: */
0736: public final boolean remove(Serializable key)
0737: throws IllegalStateException {
0738: return remove((Object) key);
0739: }
0740:
0741: /**
0742: * Removes an {@link Element} from the Cache. This also removes it from any stores it may be in. <p/> Also notifies
0743: * the CacheEventListener after the element was removed, but only if an Element with the key actually existed. <p/>
0744: * Synchronization is handled within the method.
0745: *
0746: * @param key the element key to operate on
0747: * @return true if the element was removed, false if it was not found in the cache
0748: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0749: * @since 1.2
0750: */
0751: public final boolean remove(Object key)
0752: throws IllegalStateException {
0753: return remove(key, false);
0754: }
0755:
0756: /**
0757: * Removes an {@link Element} from the Cache. This also removes it from any stores it may be in. <p/> Also notifies
0758: * the CacheEventListener after the element was removed, but only if an Element with the key actually existed. <p/>
0759: * Synchronization is handled within the method.
0760: *
0761: * @param key the element key to operate on
0762: * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in
0763: * which case this put should not initiate a further notification to doNotNotifyCacheReplicators cache peers
0764: * @return true if the element was removed, false if it was not found in the cache
0765: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0766: * @noinspection SameParameterValue
0767: */
0768: public final boolean remove(Serializable key,
0769: boolean doNotNotifyCacheReplicators)
0770: throws IllegalStateException {
0771: return remove((Object) key, doNotNotifyCacheReplicators);
0772: }
0773:
0774: /**
0775: * Removes an {@link Element} from the Cache. This also removes it from any stores it may be in. <p/> Also notifies
0776: * the CacheEventListener after the element was removed, but only if an Element with the key actually existed. <p/>
0777: * Synchronization is handled within the method.
0778: *
0779: * @param key the element key to operate on
0780: * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in
0781: * which case this put should not initiate a further notification to doNotNotifyCacheReplicators cache peers
0782: * @return true if the element was removed, false if it was not found in the cache
0783: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0784: */
0785: public final boolean remove(Object key,
0786: boolean doNotNotifyCacheReplicators)
0787: throws IllegalStateException {
0788: return remove(key, false, true, doNotNotifyCacheReplicators);
0789: }
0790:
0791: /**
0792: * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any stores it may
0793: * be in. <p/> Synchronization is handled within the method.
0794: *
0795: * @param key the element key to operate on
0796: * @return true if the element was removed, false if it was not found in the cache
0797: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0798: */
0799: public final boolean removeQuiet(Serializable key)
0800: throws IllegalStateException {
0801: return remove(key, false, false, false);
0802: }
0803:
0804: /**
0805: * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any stores it may
0806: * be in. <p/> Synchronization is handled within the method.
0807: *
0808: * @param key the element key to operate on
0809: * @return true if the element was removed, false if it was not found in the cache
0810: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0811: * @since 1.2
0812: */
0813: public final boolean removeQuiet(Object key)
0814: throws IllegalStateException {
0815: return remove(key, false, false, false);
0816: }
0817:
0818: /**
0819: * Removes or expires an {@link Element} from the Cache after an attempt to get it determined that it should be
0820: * expired. This also removes it from any stores it may be in. <p/> Also notifies the CacheEventListener after the
0821: * element has expired, but only if an Element with the key actually existed. <p/> Synchronization is handled within
0822: * the method. <p/> If a remove was called, listeners are notified, regardless of whether the element existed or not.
0823: * This allows distributed cache listeners to remove elements from a cluster regardless of whether they existed
0824: * locally.
0825: *
0826: * @param key the element key to operate on
0827: * @param expiry if the reason this method is being called is to expire the element
0828: * @param notifyListeners whether to notify listeners
0829: * @param doNotNotifyCacheReplicators whether not to notify cache replicators
0830: * @return true if the element was removed, false if it was not found in the cache
0831: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0832: */
0833: private boolean remove(Object key, boolean expiry,
0834: boolean notifyListeners, boolean doNotNotifyCacheReplicators)
0835: throws IllegalStateException {
0836: checkStatus();
0837: boolean removed = false;
0838: Element elementFromMemoryStore;
0839: Object lock = getLockObject(key);
0840: ManagerUtil.monitorEnter(lock, writeLockLevel);
0841: try {
0842: elementFromMemoryStore = memoryStore.remove(key);
0843: } finally {
0844: ManagerUtil.monitorExit(lock);
0845: }
0846:
0847: boolean removeNotified = false;
0848:
0849: if (elementFromMemoryStore != null) {
0850: if (notifyListeners) {
0851: if (expiry) {
0852: registeredEventListeners.notifyElementExpiry(
0853: elementFromMemoryStore,
0854: doNotNotifyCacheReplicators);
0855: } else {
0856: removeNotified = true;
0857: registeredEventListeners.notifyElementRemoved(
0858: elementFromMemoryStore,
0859: doNotNotifyCacheReplicators);
0860: }
0861: }
0862: removed = true;
0863: }
0864: // If we are trying to remove an element which does not exist locally,
0865: // we should still notify so that
0866: // cluster invalidations work.
0867: if (!expiry && !removeNotified) {
0868: Element syntheticElement = new Element(key, null);
0869: registeredEventListeners.notifyElementRemoved(
0870: syntheticElement, doNotNotifyCacheReplicators);
0871: }
0872:
0873: return removed;
0874: }
0875:
0876: /**
0877: * Removes all cached items. Synchronization is handled within the method.
0878: *
0879: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0880: */
0881: public void removeAll() throws IllegalStateException,
0882: CacheException {
0883: removeAll(false);
0884: }
0885:
0886: /**
0887: * Removes all cached items. Synchronization is handled within the method.
0888: *
0889: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0890: */
0891: public void removeAll(boolean doNotNotifyCacheReplicators)
0892: throws IllegalStateException, CacheException {
0893: checkStatus();
0894: writeLockAll();
0895: try {
0896: memoryStore.removeAll();
0897: } finally {
0898: unlockAll();
0899: }
0900: registeredEventListeners
0901: .notifyRemoveAll(doNotNotifyCacheReplicators);
0902: }
0903:
0904: /**
0905: * Flushes all cache items from memory to auxilliary caches and close the auxilliary caches. <p/> Should be invoked
0906: * only by CacheManager.
0907: *
0908: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0909: */
0910: public void dispose() throws IllegalStateException {
0911: writeLockAll();
0912: try {
0913: synchronized (this ) {
0914: checkStatus();
0915: memoryStore.stopTimeMonitoring();
0916: memoryStore.dispose();
0917: registeredEventListeners.dispose();
0918:
0919: changeStatus(Status.STATUS_SHUTDOWN);
0920: }
0921: } finally {
0922: unlockAll();
0923: memoryStore = null;
0924: }
0925: }
0926:
0927: /**
0928: * Flushes all cache items from memory to the disk store, and from the DiskStore to disk.
0929: *
0930: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0931: */
0932: public final void flush() throws IllegalStateException,
0933: CacheException {
0934: writeLockAll();
0935: try {
0936: checkStatus();
0937: memoryStore.flush();
0938: } finally {
0939: unlockAll();
0940: }
0941: }
0942:
0943: /**
0944: * Gets the size of the cache. This is a subtle concept. See below. <p/> The size is the number of {@link Element}s
0945: * in the {@link MemoryStore} plus the number of {@link Element}s in the {@link DiskStore}. <p/> This number is the
0946: * actual number of elements, including expired elements that have not been removed. <p/> Expired elements are removed
0947: * from the the memory store when getting an expired element, or when attempting to spool an expired element to disk.
0948: * <p/> Expired elements are removed from the disk store when getting an expired element, or when the expiry thread
0949: * runs, which is once every five minutes. <p/> To get an exact size, which would exclude expired elements, use
0950: * {@link #getKeysWithExpiryCheck()}.size(), although see that method for the approximate time that would take. <p/>
0951: * To get a very fast result, use {@link #getKeysNoDuplicateCheck()}.size(). If the disk store is being used, there
0952: * will be some duplicates.
0953: *
0954: * @return The size value
0955: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0956: */
0957: public final int getSize() throws IllegalStateException,
0958: CacheException {
0959: checkStatus();
0960: /*
0961: * The memory store and the disk store can simultaneously contain elements with the same key Cache size is the size
0962: * of the union of the two key sets.
0963: */
0964: return getKeys().size();
0965: }
0966:
0967: /**
0968: * Gets the size of the memory store for this cache. This method relies on calculating Serialized sizes. If the
0969: * Element values are not Serializable they will show as zero. <p/> Warning: This method can be very expensive to run.
0970: * Allow approximately 1 second per 1MB of entries. Running this method could create liveness problems because the
0971: * object lock is held for a long period <p/>
0972: *
0973: * @return the approximate size of the memory store in bytes
0974: * @throws IllegalStateException
0975: */
0976: public final long calculateInMemorySize()
0977: throws IllegalStateException, CacheException {
0978: checkStatus();
0979: readLockAll();
0980: try {
0981: return memoryStore.getSizeInBytes();
0982: } finally {
0983: unlockAll();
0984: }
0985: }
0986:
0987: /**
0988: * Returns the number of elements in the memory store.
0989: *
0990: * @return the number of elements in the memory store
0991: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0992: */
0993: public final long getMemoryStoreSize() throws IllegalStateException {
0994: checkStatus();
0995: return memoryStore.getSize();
0996: }
0997:
0998: /**
0999: * Returns the number of elements in the disk store.
1000: *
1001: * @return the number of elements in the disk store.
1002: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1003: */
1004: public final int getDiskStoreSize() throws IllegalStateException {
1005: checkStatus();
1006: return 0;
1007: }
1008:
1009: /**
1010: * Gets the status attribute of the Cache.
1011: *
1012: * @return The status value from the Status enum class
1013: */
1014: public final Status getStatus() {
1015: return status;
1016: }
1017:
1018: private void checkStatus() throws IllegalStateException {
1019: if (!status.equals(Status.STATUS_ALIVE)) {
1020: throw new IllegalStateException("The " + name
1021: + " Cache is not alive.");
1022: }
1023: }
1024:
1025: /**
1026: * The number of times a requested item was found in the cache. <p/> The internal representation of this statistic has
1027: * been changed to a long to cater for real world situations when an int is not sufficient to hold it. If the
1028: * statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value will be returned. Use
1029: * {@link net.sf.ehcache.Statistics} which contains this statistic. <p/>
1030: *
1031: * @return the number of times a requested item was found in the cache
1032: * @deprecated Use {@link net.sf.ehcache.Statistics}
1033: */
1034: public final int getHitCount() {
1035: return memoryStore.getHitCount();
1036: }
1037:
1038: /**
1039: * Number of times a requested item was found in the Memory Store. <p/> The internal representation of this statistic
1040: * has been changed to a long to cater for real world situations when an int is not sufficient to hold it. If the
1041: * statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value will be returned. Use
1042: * {@link net.sf.ehcache.Statistics} which contains this statistic. <p/>
1043: *
1044: * @return Number of times a requested item was found in the Memory Store.
1045: * @deprecated Use {@link net.sf.ehcache.Statistics}
1046: */
1047: public final int getMemoryStoreHitCount() {
1048: return memoryStore.getHitCount();
1049: }
1050:
1051: /**
1052: * Number of times a requested item was found in the Disk Store. <p/> The internal representation of this statistic
1053: * has been changed to a long to cater for real world situations when an int is not sufficient to hold it. If the
1054: * statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value will be returned. Use
1055: * {@link net.sf.ehcache.Statistics} which contains this statistic. <p/>
1056: *
1057: * @deprecated Use {@link net.sf.ehcache.Statistics}
1058: */
1059: public final int getDiskStoreHitCount() {
1060: return 0;
1061: }
1062:
1063: /**
1064: * Number of times a requested element was not found in the cache. This may be because it expired, in which case this
1065: * will also be recorded in {@link #getMissCountExpired}, or because it was simply not there. <p/> The internal
1066: * representation of this statistic has been changed to a long to cater for real world situations when an int is not
1067: * sufficient to hold it. If the statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value will be
1068: * returned. Use {@link net.sf.ehcache.Statistics} which contains this statistic. <p/>
1069: *
1070: * @deprecated Use {@link net.sf.ehcache.Statistics}
1071: */
1072: public final int getMissCountNotFound() {
1073: return memoryStore.getMissCountNotFound();
1074: }
1075:
1076: /**
1077: * Number of times a requested element was found but was expired. <p/> The internal representation of this statistic
1078: * has been changed to a long to cater for real world situations when an int is not sufficient to hold it. If the
1079: * statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value will be returned. Use
1080: * {@link net.sf.ehcache.Statistics} which contains this statistic. <p/>
1081: *
1082: * @deprecated Use {@link net.sf.ehcache.Statistics}
1083: */
1084: public final int getMissCountExpired() {
1085: return memoryStore.getMissCountExpired();
1086: }
1087:
1088: /**
1089: * Gets the cache name.
1090: */
1091: public final String getName() {
1092: return name;
1093: }
1094:
1095: /**
1096: * Sets the cache name which will name.
1097: *
1098: * @param name the name of the cache. Should not be null. Should also not contain any '/' characters, as these
1099: * interfere with distribution
1100: * @throws IllegalArgumentException if an illegal name is used.
1101: */
1102: public final void setName(String name)
1103: throws IllegalArgumentException {
1104: if (!status.equals(Status.STATUS_UNINITIALISED)) {
1105: throw new IllegalStateException(
1106: "Only unitialised caches can have their names set.");
1107: }
1108: if (name == null) {
1109: throw new IllegalArgumentException(
1110: "Cache name cannot be null.");
1111: }
1112: if (name.indexOf('/') != -1) {
1113: throw new IllegalArgumentException(
1114: "Cache name cannot contain '/' characters.");
1115: }
1116: this .name = name;
1117: }
1118:
1119: /**
1120: * Gets timeToIdleSeconds.
1121: */
1122: public final long getTimeToIdleSeconds() {
1123: return timeToIdleSeconds;
1124: }
1125:
1126: /**
1127: * Gets timeToLiveSeconds.
1128: */
1129: public final long getTimeToLiveSeconds() {
1130: return timeToLiveSeconds;
1131: }
1132:
1133: /**
1134: * Are elements eternal.
1135: */
1136: public final boolean isEternal() {
1137: return false;
1138: }
1139:
1140: /**
1141: * Does the overflow go to disk.
1142: */
1143: public final boolean isOverflowToDisk() {
1144: return false;
1145: }
1146:
1147: /**
1148: * Gets the maximum number of elements to hold in memory.
1149: */
1150: public final int getMaxElementsInMemory() {
1151: return maxElementsInMemory;
1152: }
1153:
1154: /**
1155: * Gets the maximum number of elements to hold on Disk
1156: */
1157: public int getMaxElementsOnDisk() {
1158: return 0;
1159: }
1160:
1161: /**
1162: * The policy used to evict elements from the {@link net.sf.ehcache.store.MemoryStore}. This can be one of:
1163: * <ol>
1164: * <li>LRU - least recently used
1165: * <li>LFU - least frequently used
1166: * <li>FIFO - first in first out, the oldest element by creation time
1167: * </ol>
1168: * The default value is LRU
1169: *
1170: * @since 1.2
1171: */
1172: public final MemoryStoreEvictionPolicy getMemoryStoreEvictionPolicy() {
1173: return DSO_MEMORY_STORE_EVICTION_POLICY;
1174: }
1175:
1176: /**
1177: * Returns a {@link String} representation of {@link CacheTC}.
1178: */
1179: public final String toString() {
1180: StringBuffer dump = new StringBuffer();
1181:
1182: dump.append("[ ").append(" name = ").append(name).append(
1183: " status = ").append(status).append(" eternal = ")
1184: .append(false).append(" overflowToDisk = ").append(
1185: false).append(" maxElementsInMemory = ")
1186: .append(maxElementsInMemory).append(
1187: " maxElementsOnDisk = ").append(0).append(
1188: " memoryStoreEvictionPolicy = ").append(
1189: memoryStoreEvictionPolicy).append(
1190: " timeToLiveSeconds = ").append(
1191: timeToLiveSeconds).append(
1192: " timeToIdleSeconds = ").append(
1193: timeToIdleSeconds).append(" diskPersistent = ")
1194: .append(false).append(
1195: " diskExpiryThreadIntervalSeconds = ").append(
1196: diskExpiryThreadIntervalSeconds).append(
1197: registeredEventListeners)
1198: .append(" hitCount = ").append(getHitCount()).append(
1199: " memoryStoreHitCount = ").append(
1200: getMemoryStoreHitCount()).append(
1201: " missCountNotFound = ").append(
1202: getMissCountNotFound()).append(
1203: " missCountExpired = ").append(
1204: getMissCountExpired()).append(" ]");
1205:
1206: return dump.toString();
1207: }
1208:
1209: /**
1210: * Checks whether this cache element has expired. <p/> The element is expired if:
1211: * <ol>
1212: * <li> the idle time is non-zero and has elapsed, unless the cache is eternal; or
1213: * <li> the time to live is non-zero and has elapsed, unless the cache is eternal; or
1214: * <li> the value of the element is null.
1215: * </ol>
1216: *
1217: * @return true if it has expired
1218: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1219: * @throws NullPointerException if the element is null
1220: */
1221: public final boolean isExpired(Element element)
1222: throws IllegalStateException, NullPointerException {
1223: checkStatus();
1224: Object key = element.getObjectKey();
1225: Object lock = getLockObject(key);
1226: ManagerUtil.monitorEnter(lock, readLockLevel);
1227: try {
1228: return memoryStore.isExpired(element.getObjectKey());
1229: } finally {
1230: ManagerUtil.monitorExit(lock);
1231: }
1232: }
1233:
1234: /**
1235: * Clones a cache. This is only legal if the cache has not been initialized. At that point only primitives have been
1236: * set and no {@link net.sf.ehcache.store.LruMemoryStore} or {@link net.sf.ehcache.store.DiskStore} has been created.
1237: * <p/> A new, empty, RegisteredEventListeners is created on clone. <p/>
1238: *
1239: * @return an object of type {@link CacheTC}
1240: * @throws CloneNotSupportedException
1241: */
1242: public final Object clone() throws CloneNotSupportedException {
1243: if (memoryStore != null) {
1244: throw new CloneNotSupportedException(
1245: "Cannot clone an initialized cache.");
1246: }
1247: CacheTC copy = (CacheTC) super .clone();
1248: RegisteredEventListeners registeredEventListenersFromCopy = copy
1249: .getCacheEventNotificationService();
1250: if (registeredEventListenersFromCopy == null
1251: || registeredEventListenersFromCopy
1252: .getCacheEventListeners().size() == 0) {
1253: copy.registeredEventListeners = new RegisteredEventListeners(
1254: copy);
1255: } else {
1256: copy.registeredEventListeners = new RegisteredEventListeners(
1257: copy);
1258: Set cacheEventListeners = registeredEventListeners
1259: .getCacheEventListeners();
1260: for (Iterator iterator = cacheEventListeners.iterator(); iterator
1261: .hasNext();) {
1262: CacheEventListener cacheEventListener = (CacheEventListener) iterator
1263: .next();
1264: CacheEventListener cacheEventListenerClone = (CacheEventListener) cacheEventListener
1265: .clone();
1266: copy.registeredEventListeners
1267: .registerListener(cacheEventListenerClone);
1268: }
1269: }
1270:
1271: if (bootstrapCacheLoader != null) {
1272: BootstrapCacheLoader bootstrapCacheLoaderClone = (BootstrapCacheLoader) bootstrapCacheLoader
1273: .clone();
1274: copy.setBootstrapCacheLoader(bootstrapCacheLoaderClone);
1275: }
1276:
1277: return copy;
1278: }
1279:
1280: /**
1281: * Gets the internal MemoryStore.
1282: *
1283: * @return the MemoryStore referenced by this cache
1284: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1285: */
1286: final MemoryStore getMemoryStore() throws IllegalStateException {
1287: checkStatus();
1288: return memoryStore;
1289: }
1290:
1291: /**
1292: * @return true if the cache overflows to disk and the disk is persistent between restarts
1293: */
1294: public final boolean isDiskPersistent() {
1295: return false;
1296: }
1297:
1298: /**
1299: * @return the interval between runs of the expiry thread, where it checks the disk store for expired elements. It is
1300: * not the the timeToLiveSeconds.
1301: */
1302: public final long getDiskExpiryThreadIntervalSeconds() {
1303: return diskExpiryThreadIntervalSeconds;
1304: }
1305:
1306: /**
1307: * Use this to access the service in order to register and unregister listeners
1308: *
1309: * @return the RegisteredEventListeners instance for this cache.
1310: */
1311: public final RegisteredEventListeners getCacheEventNotificationService() {
1312: return registeredEventListeners;
1313: }
1314:
1315: /**
1316: * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
1317: *
1318: * @return true if an element matching the key is found in memory
1319: */
1320: public final boolean isElementInMemory(Serializable key) {
1321: return isElementInMemory((Object) key);
1322: }
1323:
1324: /**
1325: * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
1326: *
1327: * @return true if an element matching the key is found in memory
1328: * @since 1.2
1329: */
1330: public final boolean isElementInMemory(Object key) {
1331: Object lock = getLockObject(key);
1332: ManagerUtil.monitorEnter(lock, readLockLevel);
1333: try {
1334: return isElementInMemoryUnlock(key);
1335: } finally {
1336: ManagerUtil.monitorExit(lock);
1337: }
1338: }
1339:
1340: private boolean isElementInMemoryUnlock(Object key) {
1341: return memoryStore.containsKey(key);
1342: }
1343:
1344: /**
1345: * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
1346: *
1347: * @return true if an element matching the key is found in the diskStore
1348: */
1349: public final boolean isElementOnDisk(Serializable key) {
1350: return false;
1351: }
1352:
1353: /**
1354: * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
1355: *
1356: * @return true if an element matching the key is found in the diskStore
1357: * @since 1.2
1358: */
1359: public final boolean isElementOnDisk(Object key) {
1360: return false;
1361: }
1362:
1363: /**
1364: * The GUID for this cache instance can be used to determine whether two cache instance references are pointing to the
1365: * same cache.
1366: *
1367: * @return the globally unique identifier for this cache instance. This is guaranteed to be unique.
1368: * @since 1.2
1369: */
1370: public final String getGuid() {
1371: return guid;
1372: }
1373:
1374: /**
1375: * Gets the CacheManager managing this cache. For a newly created cache this will be null until it has been added to a
1376: * CacheManager.
1377: *
1378: * @return the manager or null if there is none
1379: */
1380: public final CacheManager getCacheManager() {
1381: return cacheManager;
1382: }
1383:
1384: /**
1385: * Resets statistics counters back to 0.
1386: *
1387: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1388: */
1389: public synchronized void clearStatistics()
1390: throws IllegalStateException {
1391: checkStatus();
1392:
1393: writeLockAll();
1394: try {
1395: memoryStore.clearStatistics();
1396: } finally {
1397: unlockAll();
1398: }
1399: }
1400:
1401: /**
1402: * Accurately measuring statistics can be expensive. Returns the current accuracy setting.
1403: *
1404: * @return one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT},
1405: * {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
1406: */
1407: public int getStatisticsAccuracy() {
1408: return statisticsAccuracy;
1409: }
1410:
1411: /**
1412: * Sets the statistics accuracy.
1413: *
1414: * @param statisticsAccuracy one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT},
1415: * {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
1416: */
1417: public void setStatisticsAccuracy(int statisticsAccuracy) {
1418: this .statisticsAccuracy = statisticsAccuracy;
1419: }
1420:
1421: /**
1422: * Causes all elements stored in the Cache to be synchronously checked for expiry, and if expired, evicted.
1423: */
1424: public void evictExpiredElements() {
1425: writeLockAll();
1426: try {
1427: memoryStore.evictExpiredElements();
1428: } finally {
1429: unlockAll();
1430: }
1431: }
1432:
1433: /**
1434: * An inexpensive check to see if the key exists in the cache. <p/> This method is not synchronized. It is possible
1435: * that an element may exist in the cache aned be removed before the check gets to it, or vice versa.
1436: *
1437: * @param key the key to check.
1438: * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the
1439: * Element.
1440: */
1441: public boolean isKeyInCache(Object key) {
1442: return isElementInMemory(key) || isElementOnDisk(key);
1443: }
1444:
1445: /**
1446: * An extremely expensive check to see if the value exists in the cache. This implementation is O(n). Ehcache is not
1447: * designed for efficient access in this manner. <p/> This method is not synchronized. It is possible that an element
1448: * may exist in the cache aned be removed before the check gets to it, or vice versa. Because it is slow to execute
1449: * the probability of that this will have happened.
1450: *
1451: * @param value to check for
1452: * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the
1453: * Element.
1454: */
1455: public boolean isValueInCache(Object value) {
1456: boolean isSerializable = value instanceof Serializable;
1457: List keys;
1458: if (isSerializable) {
1459: keys = getKeys();
1460: } else {
1461: keys = Arrays.asList(memoryStore.getKeyArray());
1462: }
1463:
1464: for (int i = 0; i < keys.size(); i++) {
1465: Object key = keys.get(i);
1466: Element element = get(key);
1467: if (element != null) {
1468: Object elementValue = element.getValue();
1469: if (elementValue == null) {
1470: if (value == null) {
1471: return true;
1472: }
1473: } else {
1474: if (elementValue.equals(value)) {
1475: return true;
1476: }
1477: }
1478: }
1479: }
1480: return false;
1481: }
1482:
1483: /**
1484: * {@inheritDoc} <p/> Note, the {@link #getSize} method will have the same value as the size reported by Statistics
1485: * for the statistics accuracy of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}.
1486: */
1487: public Statistics getStatistics() throws IllegalStateException {
1488: int size = 0;
1489: if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_BEST_EFFORT) {
1490: size = getSize();
1491: } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_GUARANTEED) {
1492: size = getKeysWithExpiryCheck().size();
1493: } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_NONE) {
1494: size = getKeysNoDuplicateCheck().size();
1495: }
1496: return new Statistics(this , statisticsAccuracy, getHitCount(),
1497: 0, getMemoryStoreHitCount(), getMissCountExpired()
1498: + getMissCountNotFound(), size);
1499: }
1500:
1501: /**
1502: * For use by CacheManager.
1503: *
1504: * @param cacheManager the CacheManager for this cache to use.
1505: */
1506: public void setCacheManager(CacheManager cacheManager) {
1507: this .cacheManager = cacheManager;
1508: }
1509:
1510: /**
1511: * Accessor for the BootstrapCacheLoader associated with this cache. For testing purposes.
1512: */
1513: public BootstrapCacheLoader getBootstrapCacheLoader() {
1514: return bootstrapCacheLoader;
1515: }
1516:
1517: /**
1518: * Sets the bootstrap cache loader.
1519: *
1520: * @param bootstrapCacheLoader the loader to be used
1521: * @throws CacheException if this method is called after the cache is initialized
1522: */
1523: public void setBootstrapCacheLoader(
1524: BootstrapCacheLoader bootstrapCacheLoader)
1525: throws CacheException {
1526: if (!status.equals(Status.STATUS_UNINITIALISED)) {
1527: throw new CacheException(
1528: "A bootstrap cache loader can only be set before the cache is initialized. "
1529: + name);
1530: }
1531: this .bootstrapCacheLoader = bootstrapCacheLoader;
1532: }
1533:
1534: /**
1535: * DiskStore paths can conflict between CacheManager instances. This method allows the path to be changed.
1536: *
1537: * @param diskStorePath the new path to be used.
1538: * @throws CacheException if this method is called after the cache is initialized
1539: */
1540: public void setDiskStorePath(String diskStorePath)
1541: throws CacheException {
1542: // do nothing
1543: }
1544:
1545: /**
1546: * An equals method which follows the contract of {@link Object#equals(Object)}
1547: *
1548: * @param object the reference object with which to compare.
1549: * @return <code>true</code> if this object is the same as the obj argument; <code>false</code> otherwise. Same
1550: * for a Cache means, the same GUID
1551: * @see #hashCode()
1552: * @see java.util.Hashtable
1553: */
1554: public boolean equals(Object object) {
1555: if (object == null) {
1556: return false;
1557: }
1558: if (!(object instanceof Ehcache)) {
1559: return false;
1560: }
1561: Ehcache other = (Ehcache) object;
1562: return guid.equals(other.getGuid());
1563: }
1564:
1565: /**
1566: * Returns a hash code value for the object. This method is supported for the benefit of hashtables such as those
1567: * provided by <code>java.util.Hashtable</code>. <p/> The general contract of <code>hashCode</code> is:
1568: * <ul>
1569: * <li>Whenever it is invoked on the same object more than once during an execution of a Java application, the
1570: * <tt>hashCode</tt> method must consistently return the same integer, provided no information used in
1571: * <tt>equals</tt> comparisons on the object is modified. This integer need not remain consistent from one execution
1572: * of an application to another execution of the same application.
1573: * <li>If two objects are equal according to the <tt>equals(Object)</tt> method, then calling the
1574: * <code>hashCode</code> method on each of the two objects must produce the same integer result.
1575: * <li>It is <em>not</em> required that if two objects are unequal according to the {@link Object#equals(Object)}
1576: * method, then calling the <tt>hashCode</tt> method on each of the two objects must produce distinct integer
1577: * results. However, the programmer should be aware that producing distinct integer results for unequal objects may
1578: * improve the performance of hashtables.
1579: * </ul>
1580: * <p/> As much as is reasonably practical, the hashCode method defined by class <tt>Object</tt> does return
1581: * distinct integers for distinct objects. (This is typically implemented by converting the internal address of the
1582: * object into an integer, but this implementation technique is not required by the Java<font size="-2"><sup>TM</sup></font>
1583: * programming language.) <p/> This implementation use the GUID of the cache.
1584: *
1585: * @return a hash code value for this object.
1586: * @see Object#equals(Object)
1587: * @see java.util.Hashtable
1588: */
1589: public int hashCode() {
1590: return guid.hashCode();
1591: }
1592:
1593: public CacheConfiguration getCacheConfiguration() {
1594: return configuration;
1595: }
1596:
1597: private int getStoreIndex(Object key) {
1598: return Util.hash(key, locks.length);
1599: }
1600:
1601: private Object getLockObject(Object key) {
1602: return this .locks[getStoreIndex(key)];
1603: }
1604:
1605: private void readLockAll() {
1606: for (int i = 0; i < this .locks.length; i++) {
1607: ManagerUtil.monitorEnter(locks[i], readLockLevel);
1608: }
1609: }
1610:
1611: private void writeLockAll() {
1612: for (int i = 0; i < this .locks.length; i++) {
1613: ManagerUtil.monitorEnter(locks[i], writeLockLevel);
1614: }
1615: }
1616:
1617: private void unlockAll() {
1618: for (int i = 0; i < this.locks.length; i++) {
1619: ManagerUtil.monitorExit(locks[i]);
1620: }
1621: }
1622: }
|