net.sf.ehcache.Cache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache-core Show documentation
Show all versions of ehcache-core Show documentation
Internal ehcache-core module. This artifact is not meant to be used directly
/**
* Copyright Terracotta, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.ehcache;
import net.sf.ehcache.CacheOperationOutcomes.GetAllOutcome;
import net.sf.ehcache.CacheOperationOutcomes.GetOutcome;
import net.sf.ehcache.CacheOperationOutcomes.PutAllOutcome;
import net.sf.ehcache.CacheOperationOutcomes.PutOutcome;
import net.sf.ehcache.CacheOperationOutcomes.RemoveAllOutcome;
import net.sf.ehcache.CacheOperationOutcomes.RemoveOutcome;
import net.sf.ehcache.CacheOperationOutcomes.PutIfAbsentOutcome;
import net.sf.ehcache.CacheOperationOutcomes.RemoveElementOutcome;
import net.sf.ehcache.CacheOperationOutcomes.SearchOutcome;
import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
import net.sf.ehcache.bootstrap.BootstrapCacheLoaderFactory;
import net.sf.ehcache.cluster.CacheCluster;
import net.sf.ehcache.cluster.ClusterScheme;
import net.sf.ehcache.cluster.ClusterSchemeNotAvailableException;
import net.sf.ehcache.concurrent.CacheLockProvider;
import net.sf.ehcache.concurrent.LockType;
import net.sf.ehcache.concurrent.StripedReadWriteLockSync;
import net.sf.ehcache.concurrent.Sync;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.CacheWriterConfiguration;
import net.sf.ehcache.config.DiskStoreConfiguration;
import net.sf.ehcache.config.InvalidConfigurationException;
import net.sf.ehcache.config.NonstopConfiguration;
import net.sf.ehcache.config.PersistenceConfiguration;
import net.sf.ehcache.config.PersistenceConfiguration.Strategy;
import net.sf.ehcache.config.PinningConfiguration;
import net.sf.ehcache.config.SearchAttribute;
import net.sf.ehcache.config.TerracottaConfiguration;
import net.sf.ehcache.config.TerracottaConfiguration.Consistency;
import net.sf.ehcache.config.AbstractCacheConfigurationListener;
import net.sf.ehcache.constructs.nonstop.concurrency.LockOperationTimedOutNonstopException;
import net.sf.ehcache.event.CacheEventListener;
import net.sf.ehcache.event.CacheEventListenerFactory;
import net.sf.ehcache.event.RegisteredEventListeners;
import net.sf.ehcache.exceptionhandler.CacheExceptionHandler;
import net.sf.ehcache.extension.CacheExtension;
import net.sf.ehcache.extension.CacheExtensionFactory;
import net.sf.ehcache.loader.CacheLoader;
import net.sf.ehcache.loader.CacheLoaderFactory;
import net.sf.ehcache.pool.Pool;
import net.sf.ehcache.pool.PoolEvictor;
import net.sf.ehcache.pool.SizeOfEngine;
import net.sf.ehcache.pool.impl.BoundedPool;
import net.sf.ehcache.pool.impl.FromLargestCachePoolEvictor;
import net.sf.ehcache.pool.impl.UnboundedPool;
import net.sf.ehcache.search.Attribute;
import net.sf.ehcache.search.Query;
import net.sf.ehcache.search.Results;
import net.sf.ehcache.search.SearchException;
import net.sf.ehcache.search.aggregator.AggregatorInstance;
import net.sf.ehcache.search.attribute.AttributeExtractor;
import net.sf.ehcache.search.attribute.DynamicAttributesExtractor;
import net.sf.ehcache.search.attribute.UnknownAttributeException;
import net.sf.ehcache.search.expression.BaseCriteria;
import net.sf.ehcache.statistics.StatisticsGateway;
import net.sf.ehcache.store.CopyingCacheStore;
import net.sf.ehcache.store.ElementValueComparator;
import net.sf.ehcache.store.LegacyStoreWrapper;
import net.sf.ehcache.store.LruMemoryStore;
import net.sf.ehcache.store.MemoryStore;
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
import net.sf.ehcache.store.Policy;
import net.sf.ehcache.store.Store;
import net.sf.ehcache.store.StoreListener;
import net.sf.ehcache.store.StoreQuery;
import net.sf.ehcache.store.StoreQuery.Ordering;
import net.sf.ehcache.store.TerracottaStore;
import net.sf.ehcache.store.TerracottaTransactionalCopyingCacheStore;
import net.sf.ehcache.store.TxCopyingCacheStore;
import net.sf.ehcache.store.compound.ReadWriteSerializationCopyStrategy;
import net.sf.ehcache.store.disk.DiskStore;
import net.sf.ehcache.store.disk.StoreUpdateException;
import net.sf.ehcache.terracotta.InternalEhcache;
import net.sf.ehcache.terracotta.TerracottaNotRunningException;
import net.sf.ehcache.transaction.AbstractTransactionStore;
import net.sf.ehcache.transaction.SoftLockManager;
import net.sf.ehcache.transaction.TransactionIDFactory;
import net.sf.ehcache.transaction.local.JtaLocalTransactionStore;
import net.sf.ehcache.transaction.local.LocalTransactionStore;
import net.sf.ehcache.transaction.manager.TransactionManagerLookup;
import net.sf.ehcache.transaction.xa.XATransactionStore;
import net.sf.ehcache.util.ClassLoaderUtil;
import net.sf.ehcache.util.NamedThreadFactory;
import net.sf.ehcache.util.PropertyUtil;
import net.sf.ehcache.util.TimeUtil;
import net.sf.ehcache.util.VmUtils;
import net.sf.ehcache.writer.CacheWriter;
import net.sf.ehcache.writer.CacheWriterFactory;
import net.sf.ehcache.writer.CacheWriterManager;
import net.sf.ehcache.writer.CacheWriterManagerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terracotta.context.annotations.ContextAttribute;
import org.terracotta.statistics.StatisticsManager;
import org.terracotta.statistics.observer.OperationObserver;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import static net.sf.ehcache.statistics.StatisticBuilder.operation;
/**
* Cache is the central class in ehcache. Caches have {@link Element}s and are managed
* by the {@link CacheManager}. The Cache performs logical actions. It delegates physical
* implementations to its {@link net.sf.ehcache.store.Store}s.
*
* A reference to a Cache can be obtained through the {@link CacheManager}. A Cache thus obtained
* is guaranteed to have status {@link Status#STATUS_ALIVE}. This status is checked for any method which
* throws {@link IllegalStateException} and the same thrown if it is not alive. This would normally
* happen if a call is made after {@link CacheManager#shutdown} is invoked.
*
* Cache is threadsafe.
*
* Statistics on cache usage are collected and made available through the {@link #getStatistics()} methods.
*
* Various decorators are available for Cache, such as BlockingCache, SelfPopulatingCache and the dynamic proxy
* ExceptionHandlingDynamicCacheProxy. See each class for details.
*
* @author Greg Luck
* @author Geert Bevin
* @version $Id: Cache.java 10567 2017-02-10 17:41:41Z ljacomet $
*/
public class Cache implements InternalEhcache, StoreListener {
/**
* A reserved word for cache names. It denotes a default configuration
* which is applied to caches created without configuration.
*/
public static final String DEFAULT_CACHE_NAME = "default";
/**
* System Property based method of disabling ehcache. If disabled no elements will be added to a cache.
*
* Set the property "net.sf.ehcache.disabled=true" to disable ehcache.
*
* This can easily be done using java -Dnet.sf.ehcache.disabled=true
in the command line.
*/
public static final String NET_SF_EHCACHE_DISABLED = "net.sf.ehcache.disabled";
/**
* System Property based method of selecting the LruMemoryStore in use up to ehcache 1.5. This is provided
* for ease of migration.
*
* Set the property "net.sf.ehcache.use.classic.lru=true" to use the older LruMemoryStore implementation
* when LRU is selected as the eviction policy.
*
* This can easily be done using java -Dnet.sf.ehcache.use.classic.lru=true
in the command line.
*/
public static final String NET_SF_EHCACHE_USE_CLASSIC_LRU = "net.sf.ehcache.use.classic.lru";
/**
* The default interval between runs of the expiry thread.
* @see CacheConfiguration#DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS CacheConfiguration#DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS for a preferred way of setting
*/
public static final long DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS = CacheConfiguration.DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS;
private static final Logger LOG = LoggerFactory.getLogger(Cache.class.getName());
private static InetAddress localhost;
/**
* The amount of time to wait if a store gets backed up
*/
private static final int BACK_OFF_TIME_MILLIS = 50;
private static final int EXECUTOR_KEEP_ALIVE_TIME = 60000;
private static final int EXECUTOR_MAXIMUM_POOL_SIZE = Math.min(10, Runtime.getRuntime().availableProcessors());
private static final int EXECUTOR_CORE_POOL_SIZE = 1;
private static final String EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY_PROP = "ehcache.clusteredStore.maxConcurrency";
private static final int DEFAULT_EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY = 4096;
static {
try {
localhost = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
LOG.error("Unable to set localhost. This prevents creation of a GUID. Cause was: " + e.getMessage(), e);
} catch (java.lang.NoClassDefFoundError e) {
LOG.debug("InetAddress is being blocked by your runtime environment. e.g. Google App Engine." +
" Ehcache will work as a local cache.");
}
}
private volatile boolean disabled = Boolean.getBoolean(NET_SF_EHCACHE_DISABLED);
private final boolean useClassicLru = Boolean.getBoolean(NET_SF_EHCACHE_USE_CLASSIC_LRU);
private final CacheStatus cacheStatus = new CacheStatus();
private final CacheConfiguration configuration;
/**
* The {@link import net.sf.ehcache.store.Store} of this {@link Cache}.
*/
private volatile Store compoundStore;
private volatile CacheLockProvider lockProvider;
private volatile RegisteredEventListeners registeredEventListeners;
private final List registeredCacheExtensions = new CopyOnWriteArrayList();;
private final String guid = createGuid();
private volatile CacheManager cacheManager;
private volatile BootstrapCacheLoader bootstrapCacheLoader;
private volatile CacheExceptionHandler cacheExceptionHandler;
private final List registeredCacheLoaders = new CopyOnWriteArrayList();
private volatile CacheWriterManager cacheWriterManager;
private final AtomicBoolean cacheWriterManagerInitFlag = new AtomicBoolean(false);
private final ReentrantLock cacheWriterManagerInitLock = new ReentrantLock();
private volatile CacheWriter registeredCacheWriter;
private final OperationObserver getObserver = operation(GetOutcome.class).named("get").of(this).tag("cache").build();
private final OperationObserver putObserver = operation(PutOutcome.class).named("put").of(this).tag("cache").build();
private final OperationObserver removeObserver = operation(RemoveOutcome.class).named("remove").of(this).tag("cache").build();
private final OperationObserver getAllObserver = operation(GetAllOutcome.class).named("getAll").of(this)
.tag("cache", "bulk").build();
private final OperationObserver putAllObserver = operation(PutAllOutcome.class).named("putAll").of(this)
.tag("cache", "bulk").build();
private final OperationObserver removeAllObserver = operation(RemoveAllOutcome.class).named("removeAll").of(this)
.tag("cache", "bulk").build();
private final OperationObserver searchObserver = operation(SearchOutcome.class).named("search").of(this).tag("cache").build();
private final OperationObserver replace1Observer = operation(CacheOperationOutcomes.ReplaceOneArgOutcome.class).named("replace1").of(this)
.tag("cache").build();
private final OperationObserver replace2Observer = operation(CacheOperationOutcomes.ReplaceTwoArgOutcome.class).named("replace2").of(this)
.tag("cache").build();
private final OperationObserver putIfAbsentObserver = operation(PutIfAbsentOutcome.class).named("putIfAbsent").of(this)
.tag("cache").build();
private final OperationObserver removeElementObserver = operation(RemoveElementOutcome.class).named("removeElement").of(this)
.tag("cache").build();
/**
* A ThreadPoolExecutor which uses a thread pool to schedule loads in the order in which they are requested.
*
* Each cache can have its own executor service, if required. The keep alive time is 60 seconds, after which,
* if the thread is not required it will be stopped and collected, as core threads are allowed to time out.
*
* The executorService is only used for cache loading, and is created lazily on demand to avoid unnecessary resource
* usage.
*
* Use {@link #getExecutorService()} to ensure that it is initialised.
*/
private volatile ExecutorService executorService;
private volatile TransactionManagerLookup transactionManagerLookup;
private volatile boolean allowDisable = true;
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
private volatile ElementValueComparator elementValueComparator;
private StatisticsGateway statistics;
private CacheClusterStateStatisticsListener clusterStateListener = null;
private AbstractCacheConfigurationListener configListener;
/**
* 2.0 and higher Constructor
*
* The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
*
* A client can specify their own settings here and pass the {@link Cache} object
* into {@link CacheManager#addCache} to specify parameters other than the defaults.
*
* Only the CacheManager can initialise them.
*
* @param cacheConfiguration the configuration that should be used to create the cache with
*/
public Cache(CacheConfiguration cacheConfiguration) {
this(cacheConfiguration, null, (BootstrapCacheLoader) null);
}
/**
* 2.0 and higher Constructor
*
* The {@link net.sf.ehcache.config.ConfigurationFactory}
* and clients can create these.
*
* A client can specify their own settings here and pass the {@link Cache}
* object into {@link CacheManager#addCache} to specify parameters other
* than the defaults.
*
* Only the CacheManager can initialise them.
*
* @param cacheConfiguration the configuration that should be used to create the cache with
* @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
* @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
*/
public Cache(CacheConfiguration cacheConfiguration,
RegisteredEventListeners registeredEventListeners,
BootstrapCacheLoader bootstrapCacheLoader) {
final ClassLoader loader = cacheConfiguration.getClassLoader();
cacheStatus.changeState(Status.STATUS_UNINITIALISED);
this.configuration = cacheConfiguration.clone();
configuration.validateCompleteConfiguration();
if (registeredEventListeners == null) {
this.registeredEventListeners = new RegisteredEventListeners(this);
} else {
this.registeredEventListeners = registeredEventListeners;
}
RegisteredEventListeners listeners = getCacheEventNotificationService();
registerCacheListeners(configuration, listeners, loader);
registerCacheExtensions(configuration, this, loader);
if (null == bootstrapCacheLoader) {
this.bootstrapCacheLoader = createBootstrapCacheLoader(configuration.getBootstrapCacheLoaderFactoryConfiguration(), loader);
} else {
this.bootstrapCacheLoader = bootstrapCacheLoader;
}
registerCacheLoaders(configuration, this, loader);
registerCacheWriter(configuration, this, loader);
}
/**
* 1.0 Constructor.
*
* The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
*
* A client can specify their own settings here and pass the {@link Cache} object
* into {@link CacheManager#addCache} to specify parameters other than the defaults.
*
* Only the CacheManager can initialise them.
*
* This constructor creates disk stores, if specified, that do not persist between restarts.
*
* The default expiry thread interval of 120 seconds is used. This is the interval between runs
* of the expiry thread, where it checks the disk store for expired elements. It is not the
* the timeToLiveSeconds.
*
* @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
* @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
* @param overflowToDisk whether to use the disk store
* @param eternal whether the elements in the cache are eternal, i.e. never expire
* @param timeToLiveSeconds the default amount of time to live for an element from its creation date
* @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
* @since 1.0
* @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
* for full construction support of version 2.0 and higher features.
*/
public Cache(String name, int maxElementsInMemory, boolean overflowToDisk,
boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds) {
this(new CacheConfiguration(name, maxElementsInMemory)
.overflowToDisk(overflowToDisk)
.eternal(eternal)
.timeToLiveSeconds(timeToLiveSeconds)
.timeToIdleSeconds(timeToIdleSeconds));
}
/**
* 1.1 Constructor.
*
* The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
*
* A client can specify their own settings here and pass the {@link Cache} object
* into {@link CacheManager#addCache} to specify parameters other than the defaults.
*
* Only the CacheManager can initialise them.
*
* @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
* @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
* @param overflowToDisk whether to use the disk store
* @param eternal whether the elements in the cache are eternal, i.e. never expire
* @param timeToLiveSeconds the default amount of time to live for an element from its creation date
* @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
* @param diskPersistent whether to persist the cache to disk between JVM restarts
* @param diskExpiryThreadIntervalSeconds
* how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
* @since 1.1
* @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
* for full construction support of version 2.0 and higher features.
*/
public Cache(String name,
int maxElementsInMemory,
boolean overflowToDisk,
boolean eternal,
long timeToLiveSeconds,
long timeToIdleSeconds,
boolean diskPersistent,
long diskExpiryThreadIntervalSeconds) {
this(new CacheConfiguration(name, maxElementsInMemory)
.overflowToDisk(overflowToDisk)
.eternal(eternal)
.timeToLiveSeconds(timeToLiveSeconds)
.timeToIdleSeconds(timeToIdleSeconds)
.diskPersistent(diskPersistent)
.diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds));
LOG.warn("An API change between ehcache-1.1 and ehcache-1.2 results in the persistence path being set to " +
DiskStoreConfiguration.getDefaultPath() + " when the ehcache-1.1 constructor is used. " +
"Please change to the 1.2 constructor.");
}
/**
* 1.2 Constructor
*
* The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
*
* A client can specify their own settings here and pass the {@link Cache} object
* into {@link CacheManager#addCache} to specify parameters other than the defaults.
*
* Only the CacheManager can initialise them.
*
* @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
* @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
* @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
* @param overflowToDisk whether to use the disk store
* @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
* @param eternal whether the elements in the cache are eternal, i.e. never expire
* @param timeToLiveSeconds the default amount of time to live for an element from its creation date
* @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
* @param diskPersistent whether to persist the cache to disk between JVM restarts
* @param diskExpiryThreadIntervalSeconds
* how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
* @param registeredEventListeners a notification service. Optionally null, in which case a new
* one with no registered listeners will be created.
* @since 1.2
* @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
* for full construction support of version 2.0 and higher features.
*/
public Cache(String name,
int maxElementsInMemory,
MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
boolean overflowToDisk,
String diskStorePath,
boolean eternal,
long timeToLiveSeconds,
long timeToIdleSeconds,
boolean diskPersistent,
long diskExpiryThreadIntervalSeconds,
RegisteredEventListeners registeredEventListeners) {
this(new CacheConfiguration(name, maxElementsInMemory)
.memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
.overflowToDisk(overflowToDisk)
.eternal(eternal)
.timeToLiveSeconds(timeToLiveSeconds)
.timeToIdleSeconds(timeToIdleSeconds)
.diskPersistent(diskPersistent)
.diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds),
registeredEventListeners,
null);
}
/**
* 1.2.1 Constructor
*
* The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
*
* A client can specify their own settings here and pass the {@link Cache} object
* into {@link CacheManager#addCache} to specify parameters other than the defaults.
*
* Only the CacheManager can initialise them.
*
* @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
* @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
* @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
* @param overflowToDisk whether to use the disk store
* @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
* @param eternal whether the elements in the cache are eternal, i.e. never expire
* @param timeToLiveSeconds the default amount of time to live for an element from its creation date
* @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
* @param diskPersistent whether to persist the cache to disk between JVM restarts
* @param diskExpiryThreadIntervalSeconds
* how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
* @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
* @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
* @since 1.2.1
* @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
* for full construction support of version 2.0 and higher features.
*/
public Cache(String name,
int maxElementsInMemory,
MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
boolean overflowToDisk,
String diskStorePath,
boolean eternal,
long timeToLiveSeconds,
long timeToIdleSeconds,
boolean diskPersistent,
long diskExpiryThreadIntervalSeconds,
RegisteredEventListeners registeredEventListeners,
BootstrapCacheLoader bootstrapCacheLoader) {
this(new CacheConfiguration(name, maxElementsInMemory)
.memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
.overflowToDisk(overflowToDisk)
.eternal(eternal)
.timeToLiveSeconds(timeToLiveSeconds)
.timeToIdleSeconds(timeToIdleSeconds)
.diskPersistent(diskPersistent)
.diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds),
registeredEventListeners,
bootstrapCacheLoader);
}
/**
* 1.2.4 Constructor
*
* The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
*
* A client can specify their own settings here and pass the {@link Cache} object
* into {@link CacheManager#addCache} to specify parameters other than the defaults.
*
* Only the CacheManager can initialise them.
*
* @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
* @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
* @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
* @param overflowToDisk whether to use the disk store
* @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
* @param eternal whether the elements in the cache are eternal, i.e. never expire
* @param timeToLiveSeconds the default amount of time to live for an element from its creation date
* @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
* @param diskPersistent whether to persist the cache to disk between JVM restarts
* @param diskExpiryThreadIntervalSeconds
* how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
* @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
* @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
* @param maxElementsOnDisk the maximum number of Elements to allow on the disk. 0 means unlimited.
* @since 1.2.4
* @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
* for full construction support of version 2.0 and higher features.
*/
public Cache(String name,
int maxElementsInMemory,
MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
boolean overflowToDisk,
String diskStorePath,
boolean eternal,
long timeToLiveSeconds,
long timeToIdleSeconds,
boolean diskPersistent,
long diskExpiryThreadIntervalSeconds,
RegisteredEventListeners registeredEventListeners,
BootstrapCacheLoader bootstrapCacheLoader,
int maxElementsOnDisk) {
this(new CacheConfiguration(name, maxElementsInMemory)
.memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
.overflowToDisk(overflowToDisk)
.eternal(eternal)
.timeToLiveSeconds(timeToLiveSeconds)
.timeToIdleSeconds(timeToIdleSeconds)
.diskPersistent(diskPersistent)
.diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
.maxElementsOnDisk(maxElementsOnDisk),
registeredEventListeners,
bootstrapCacheLoader);
}
/**
* 1.3 Constructor
*
* The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
*
* A client can specify their own settings here and pass the {@link Cache} object
* into {@link CacheManager#addCache} to specify parameters other than the defaults.
*
* Only the CacheManager can initialise them.
*
* @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
* @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
* @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
* @param overflowToDisk whether to use the disk store
* @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
* @param eternal whether the elements in the cache are eternal, i.e. never expire
* @param timeToLiveSeconds the default amount of time to live for an element from its creation date
* @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
* @param diskPersistent whether to persist the cache to disk between JVM restarts
* @param diskExpiryThreadIntervalSeconds
* how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
* @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
* @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
* @param maxElementsOnDisk the maximum number of Elements to allow on the disk. 0 means unlimited.
* @param diskSpoolBufferSizeMB the amount of memory to allocate the write buffer for puts to the DiskStore.
* @since 1.3
* @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
* for full construction support of version 2.0 and higher features.
*/
public Cache(String name,
int maxElementsInMemory,
MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
boolean overflowToDisk,
String diskStorePath,
boolean eternal,
long timeToLiveSeconds,
long timeToIdleSeconds,
boolean diskPersistent,
long diskExpiryThreadIntervalSeconds,
RegisteredEventListeners registeredEventListeners,
BootstrapCacheLoader bootstrapCacheLoader,
int maxElementsOnDisk,
int diskSpoolBufferSizeMB) {
this(new CacheConfiguration(name, maxElementsInMemory)
.memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
.overflowToDisk(overflowToDisk)
.eternal(eternal)
.timeToLiveSeconds(timeToLiveSeconds)
.timeToIdleSeconds(timeToIdleSeconds)
.diskPersistent(diskPersistent)
.diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
.maxElementsOnDisk(maxElementsOnDisk)
.diskSpoolBufferSizeMB(diskSpoolBufferSizeMB),
registeredEventListeners,
bootstrapCacheLoader);
}
/**
* 1.6.0 Constructor
*
* The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
*
* A client can specify their own settings here and pass the {@link Cache} object
* into {@link CacheManager#addCache} to specify parameters other than the defaults.
*
* Only the CacheManager can initialise them.
*
* @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
* @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
* @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
* @param overflowToDisk whether to use the disk store
* @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
* @param eternal whether the elements in the cache are eternal, i.e. never expire
* @param timeToLiveSeconds the default amount of time to live for an element from its creation date
* @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
* @param diskPersistent whether to persist the cache to disk between JVM restarts
* @param diskExpiryThreadIntervalSeconds
* how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
* @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
* @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
* @param maxElementsOnDisk the maximum number of Elements to allow on the disk. 0 means unlimited.
* @param diskSpoolBufferSizeMB the amount of memory to allocate the write buffer for puts to the DiskStore.
* @param clearOnFlush whether the in-memory storage should be cleared when {@link #flush flush()} is called on the cache
* @since 1.6.0
* @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
* for full construction support of version 2.0 and higher features.
*/
public Cache(String name,
int maxElementsInMemory,
MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
boolean overflowToDisk,
String diskStorePath,
boolean eternal,
long timeToLiveSeconds,
long timeToIdleSeconds,
boolean diskPersistent,
long diskExpiryThreadIntervalSeconds,
RegisteredEventListeners registeredEventListeners,
BootstrapCacheLoader bootstrapCacheLoader,
int maxElementsOnDisk,
int diskSpoolBufferSizeMB,
boolean clearOnFlush) {
this(new CacheConfiguration(name, maxElementsInMemory)
.memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
.overflowToDisk(overflowToDisk)
.eternal(eternal)
.timeToLiveSeconds(timeToLiveSeconds)
.timeToIdleSeconds(timeToIdleSeconds)
.diskPersistent(diskPersistent)
.diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
.maxElementsOnDisk(maxElementsOnDisk)
.diskSpoolBufferSizeMB(diskSpoolBufferSizeMB)
.clearOnFlush(clearOnFlush),
registeredEventListeners,
bootstrapCacheLoader);
}
/**
* 1.7.0 Constructor
*
* The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
*
* A client can specify their own settings here and pass the {@link Cache} object
* into {@link CacheManager#addCache} to specify parameters other than the defaults.
*
* Only the CacheManager can initialise them.
*
* @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
* @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
* @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
* @param overflowToDisk whether to use the disk store
* @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
* @param eternal whether the elements in the cache are eternal, i.e. never expire
* @param timeToLiveSeconds the default amount of time to live for an element from its creation date
* @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
* @param diskPersistent whether to persist the cache to disk between JVM restarts
* @param diskExpiryThreadIntervalSeconds
* how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
* @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
* @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
* @param maxElementsOnDisk the maximum number of Elements to allow on the disk. 0 means unlimited.
* @param diskSpoolBufferSizeMB the amount of memory to allocate the write buffer for puts to the DiskStore.
* @param clearOnFlush whether the in-memory storage should be cleared when {@link #flush flush()} is called on the cache
* @param isTerracottaClustered whether to cluster this cache with Terracotta
* @param terracottaCoherentReads whether this cache should use coherent reads (usually should be true) unless optimizing for read-only
* @since 1.7.0
* @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
* for full construction support of version 2.0 and higher features.
*/
public Cache(String name, int maxElementsInMemory, MemoryStoreEvictionPolicy memoryStoreEvictionPolicy, boolean overflowToDisk,
String diskStorePath, boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds, boolean diskPersistent,
long diskExpiryThreadIntervalSeconds, RegisteredEventListeners registeredEventListeners,
BootstrapCacheLoader bootstrapCacheLoader, int maxElementsOnDisk, int diskSpoolBufferSizeMB, boolean clearOnFlush,
boolean isTerracottaClustered, boolean terracottaCoherentReads) {
this(new CacheConfiguration(name, maxElementsInMemory)
.memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
.overflowToDisk(overflowToDisk)
.eternal(eternal)
.timeToLiveSeconds(timeToLiveSeconds)
.timeToIdleSeconds(timeToIdleSeconds)
.diskPersistent(diskPersistent)
.diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
.maxElementsOnDisk(maxElementsOnDisk)
.diskSpoolBufferSizeMB(diskSpoolBufferSizeMB)
.clearOnFlush(clearOnFlush)
.terracotta(new TerracottaConfiguration()
.clustered(isTerracottaClustered)
.coherentReads(terracottaCoherentReads)),
registeredEventListeners,
bootstrapCacheLoader);
}
/**
* Test only
*/
Cache(CacheConfiguration config, Store compoundStore, RegisteredEventListeners listeners) {
this.configuration = config;
this.compoundStore = compoundStore;
this.registeredEventListeners = listeners;
cacheStatus.changeState(Status.STATUS_ALIVE);
}
/**
* Constructor for cloning.
* @param original
* @throws CloneNotSupportedException
*/
private Cache(Cache original) throws CloneNotSupportedException {
if (original.compoundStore != null) {
throw new CloneNotSupportedException("Cannot clone an initialized cache.");
}
final ClassLoader loader = original.configuration.getClassLoader();
// create new copies of the statistics
configuration = original.configuration.clone();
cacheStatus.changeState(Status.STATUS_UNINITIALISED);
configuration.getCopyStrategyConfiguration().setCopyStrategyInstance(null);
//XXX - should this be here?
elementValueComparator = configuration.getElementValueComparatorConfiguration().createElementComparatorInstance(configuration, loader);
for (PropertyChangeListener propertyChangeListener : original.propertyChangeSupport.getPropertyChangeListeners()) {
addPropertyChangeListener(propertyChangeListener);
}
RegisteredEventListeners registeredEventListenersFromOriginal = original.getCacheEventNotificationService();
if (registeredEventListenersFromOriginal == null || registeredEventListenersFromOriginal.getCacheEventListeners().size() == 0) {
registeredEventListeners = new RegisteredEventListeners(this);
} else {
registeredEventListeners = new RegisteredEventListeners(this);
Set cacheEventListeners = original.registeredEventListeners.getCacheEventListeners();
for (Object cacheEventListener1 : cacheEventListeners) {
CacheEventListener cacheEventListener = (CacheEventListener) cacheEventListener1;
CacheEventListener cacheEventListenerClone = (CacheEventListener) cacheEventListener.clone();
registeredEventListeners.registerListener(cacheEventListenerClone);
}
}
for (CacheExtension registeredCacheExtension : original.registeredCacheExtensions) {
registerCacheExtension(registeredCacheExtension.clone(this));
}
for (CacheLoader registeredCacheLoader : original.registeredCacheLoaders) {
registerCacheLoader(registeredCacheLoader.clone(this));
}
if (original.registeredCacheWriter != null) {
registerCacheWriter(registeredCacheWriter.clone(this));
}
if (original.bootstrapCacheLoader != null) {
BootstrapCacheLoader bootstrapCacheLoaderClone = (BootstrapCacheLoader) original.bootstrapCacheLoader.clone();
this.setBootstrapCacheLoader(bootstrapCacheLoaderClone);
}
}
/**
* A factory method to create a RegisteredEventListeners
* @param loader
*/
private static void registerCacheListeners(CacheConfiguration cacheConfiguration,
RegisteredEventListeners registeredEventListeners, ClassLoader loader) {
List cacheEventListenerConfigurations = cacheConfiguration.getCacheEventListenerConfigurations();
for (Object cacheEventListenerConfiguration : cacheEventListenerConfigurations) {
CacheConfiguration.CacheEventListenerFactoryConfiguration factoryConfiguration =
(CacheConfiguration.CacheEventListenerFactoryConfiguration) cacheEventListenerConfiguration;
CacheEventListener cacheEventListener = createCacheEventListener(factoryConfiguration, loader);
registeredEventListeners.registerListener(cacheEventListener, factoryConfiguration.getListenFor());
}
}
/**
* A factory method to register cache extensions
*
* @param cacheConfiguration the cache configuration
* @param cache the cache
* @param loader
*/
private static void registerCacheExtensions(CacheConfiguration cacheConfiguration, Ehcache cache, ClassLoader loader) {
List cacheExtensionConfigurations = cacheConfiguration.getCacheExtensionConfigurations();
for (Object cacheExtensionConfiguration : cacheExtensionConfigurations) {
CacheConfiguration.CacheExtensionFactoryConfiguration factoryConfiguration =
(CacheConfiguration.CacheExtensionFactoryConfiguration) cacheExtensionConfiguration;
CacheExtension cacheExtension = createCacheExtension(factoryConfiguration, cache, loader);
cache.registerCacheExtension(cacheExtension);
}
}
/**
* A factory method to register cache Loaders
*
* @param cacheConfiguration the cache configuration
* @param cache the cache
* @param loader
*/
private static void registerCacheLoaders(CacheConfiguration cacheConfiguration, Ehcache cache, ClassLoader loader) {
List cacheLoaderConfigurations = cacheConfiguration.getCacheLoaderConfigurations();
for (Object cacheLoaderConfiguration : cacheLoaderConfigurations) {
CacheConfiguration.CacheLoaderFactoryConfiguration factoryConfiguration =
(CacheConfiguration.CacheLoaderFactoryConfiguration) cacheLoaderConfiguration;
CacheLoader cacheLoader = createCacheLoader(factoryConfiguration, cache, loader);
cache.registerCacheLoader(cacheLoader);
}
}
/**
* A factory method to register cache writers
*
* @param cacheConfiguration the cache configuration
* @param cache the cache
* @param loader
*/
private static void registerCacheWriter(CacheConfiguration cacheConfiguration, Ehcache cache, ClassLoader loader) {
CacheWriterConfiguration config = cacheConfiguration.getCacheWriterConfiguration();
if (config != null) {
CacheWriter cacheWriter = createCacheWriter(config, cache, loader);
cache.registerCacheWriter(cacheWriter);
}
}
/**
* Tries to load the class specified otherwise defaults to null.
*
* @param factoryConfiguration
* @param loader
*/
private static CacheEventListener createCacheEventListener(
CacheConfiguration.CacheEventListenerFactoryConfiguration factoryConfiguration, ClassLoader loader) {
String className = null;
CacheEventListener cacheEventListener = null;
if (factoryConfiguration != null) {
className = factoryConfiguration.getFullyQualifiedClassPath();
}
if (className == null) {
LOG.debug("CacheEventListener factory not configured. Skipping...");
} else {
CacheEventListenerFactory factory = (CacheEventListenerFactory)
ClassLoaderUtil.createNewInstance(loader, className);
Properties properties =
PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
factoryConfiguration.getPropertySeparator());
cacheEventListener =
factory.createCacheEventListener(properties);
}
return cacheEventListener;
}
/**
* Tries to load the class specified otherwise defaults to null.
*
* @param factoryConfiguration
* @param loader
*/
private static CacheExtension createCacheExtension(
CacheConfiguration.CacheExtensionFactoryConfiguration factoryConfiguration, Ehcache cache, ClassLoader loader) {
String className = null;
CacheExtension cacheExtension = null;
if (factoryConfiguration != null) {
className = factoryConfiguration.getFullyQualifiedClassPath();
}
if (className == null) {
LOG.debug("CacheExtension factory not configured. Skipping...");
} else {
CacheExtensionFactory factory = (CacheExtensionFactory) ClassLoaderUtil.createNewInstance(loader, className);
Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
factoryConfiguration.getPropertySeparator());
cacheExtension = factory.createCacheExtension(cache, properties);
}
return cacheExtension;
}
/**
* Tries to load the class specified otherwise defaults to null.
*
* @param factoryConfiguration
* @param loader
*/
private static CacheLoader createCacheLoader(
CacheConfiguration.CacheLoaderFactoryConfiguration factoryConfiguration, Ehcache cache, ClassLoader loader) {
String className = null;
CacheLoader cacheLoader = null;
if (factoryConfiguration != null) {
className = factoryConfiguration.getFullyQualifiedClassPath();
}
if (className == null) {
LOG.debug("CacheLoader factory not configured. Skipping...");
} else {
CacheLoaderFactory factory = (CacheLoaderFactory) ClassLoaderUtil.createNewInstance(loader, className);
Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
factoryConfiguration.getPropertySeparator());
cacheLoader = factory.createCacheLoader(cache, properties);
}
return cacheLoader;
}
/**
* Tries to load the class specified otherwise defaults to null.
*
* @param config
* @param loader
*/
private static CacheWriter createCacheWriter(CacheWriterConfiguration config, Ehcache cache, ClassLoader loader) {
String className = null;
CacheWriter cacheWriter = null;
CacheWriterConfiguration.CacheWriterFactoryConfiguration factoryConfiguration = config.getCacheWriterFactoryConfiguration();
if (factoryConfiguration != null) {
className = factoryConfiguration.getFullyQualifiedClassPath();
}
if (null == className) {
LOG.debug("CacheWriter factory not configured. Skipping...");
} else {
CacheWriterFactory factory = (CacheWriterFactory) ClassLoaderUtil.createNewInstance(loader, className);
Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
factoryConfiguration.getPropertySeparator());
if (null == properties) {
properties = new Properties();
}
cacheWriter = factory.createCacheWriter(cache, properties);
}
return cacheWriter;
}
/**
* Tries to load a BootstrapCacheLoader from the class specified.
* @param loader
*
* @return If there is none returns null.
*/
private static final BootstrapCacheLoader createBootstrapCacheLoader(
CacheConfiguration.BootstrapCacheLoaderFactoryConfiguration factoryConfiguration, ClassLoader loader) throws CacheException {
String className = null;
BootstrapCacheLoader bootstrapCacheLoader = null;
if (factoryConfiguration != null) {
className = factoryConfiguration.getFullyQualifiedClassPath();
}
if (className == null || className.length() == 0) {
LOG.debug("No BootstrapCacheLoaderFactory class specified. Skipping...");
} else {
BootstrapCacheLoaderFactory factory = (BootstrapCacheLoaderFactory)
ClassLoaderUtil.createNewInstance(loader, className);
Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
factoryConfiguration.getPropertySeparator());
return factory.createBootstrapCacheLoader(properties);
}
return bootstrapCacheLoader;
}
/**
* Get the TransactionManagerLookup implementation used to lookup the TransactionManager.
* This is generally only set for XA transactional caches
* @return The {@link net.sf.ehcache.transaction.manager.TransactionManagerLookup} instance
*/
public TransactionManagerLookup getTransactionManagerLookup() {
return transactionManagerLookup;
}
/**
* Sets the TransactionManagerLookup that needs to be used for this cache to lookup the TransactionManager
* This needs to be set before {@link Cache#initialise()} is called
* @param lookup The {@link net.sf.ehcache.transaction.manager.TransactionManagerLookup} instance
*/
public void setTransactionManagerLookup(TransactionManagerLookup lookup) {
TransactionManagerLookup oldValue = getTransactionManagerLookup();
this.transactionManagerLookup = lookup;
firePropertyChange("TransactionManagerLookup", oldValue, lookup);
}
/**
* Newly created caches do not have a {@link net.sf.ehcache.store.Store}.
*
* This method creates the store and makes the cache ready to accept elements
*/
public void initialise() {
synchronized (this) {
final ClassLoader loader = getCacheConfiguration().getClassLoader();
// verify that the cache and cache manager use the same classloader reference
if (loader != cacheManager.getConfiguration().getClassLoader()) {
// XXX: Is there a better way to relax this check for shadow caches?
if (!getName().startsWith(CacheManager.LOCAL_CACHE_NAME_PREFIX)) {
throw new CacheException("This cache (" + getName() + ") is configurated with a different classloader reference than its containing cache manager");
}
}
if (!cacheStatus.canInitialize()) {
throw new IllegalStateException("Cannot initialise the " + configuration.getName()
+ " cache because its status is not STATUS_UNINITIALISED");
}
// on-heap pool configuration
final Pool onHeapPool;
if (configuration.getMaxBytesLocalHeap() > 0) {
PoolEvictor evictor = new FromLargestCachePoolEvictor();
SizeOfEngine sizeOfEngine = cacheManager.createSizeOfEngine(this);
onHeapPool = new BoundedPool(configuration.getMaxBytesLocalHeap(), evictor, sizeOfEngine);
} else if (getCacheManager() != null && getCacheManager().getConfiguration().isMaxBytesLocalHeapSet()) {
onHeapPool = getCacheManager().getOnHeapPool();
} else {
onHeapPool = new UnboundedPool();
}
// on-disk pool configuration
final Pool onDiskPool;
if (configuration.getMaxBytesLocalDisk() > 0) {
PoolEvictor evictor = new FromLargestCachePoolEvictor();
onDiskPool = new BoundedPool(configuration.getMaxBytesLocalDisk(), evictor, null);
} else if (getCacheManager() != null && getCacheManager().getConfiguration().isMaxBytesLocalDiskSet()) {
onDiskPool = getCacheManager().getOnDiskPool();
} else {
onDiskPool = new UnboundedPool();
}
/*We don't have to worry about the old value as when we are called the CacheConfiguration should
have validated and resized the Cachemanager Pool as CacheConfiguration adds itself as first listener.
so we just handle heap and disk pools resizing.*/
this.configListener = new AbstractCacheConfigurationListener() {
@Override
public void maxBytesLocalHeapChanged(long oldValue, long newValue) {
onHeapPool.setMaxSize(newValue);
}
@Override
public void maxBytesLocalDiskChanged(long oldValue, long newValue) {
onDiskPool.setMaxSize(newValue);
}
};
this.configuration.addConfigurationListener(configListener);
Store store;
if (isTerracottaClustered()) {
checkClusteredConfig();
int maxConcurrency = Integer.getInteger(EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY_PROP,
DEFAULT_EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY);
if (getCacheConfiguration().getTerracottaConfiguration().getConcurrency() > maxConcurrency) {
throw new InvalidConfigurationException("Maximum supported concurrency for Terracotta clustered caches is "
+ maxConcurrency + ". Please reconfigure cache '" + getName() + "' with concurrency value <= " + maxConcurrency
+ " or use system property '" + EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY_PROP + "' to override the default");
}
elementValueComparator = configuration.getElementValueComparatorConfiguration().createElementComparatorInstance(configuration, loader);
Callable callable = new Callable() {
@Override
public TerracottaStore call() throws Exception {
cacheManager.getClusteredInstanceFactory().linkClusteredCacheManager(cacheManager.getName(), cacheManager.getConfiguration());
Store tempStore = null;
try {
tempStore = cacheManager.createTerracottaStore(Cache.this);
} catch (IllegalArgumentException e) {
handleExceptionInTerracottaStoreCreation(e);
}
if (!(tempStore instanceof TerracottaStore)) {
throw new CacheException(
"CacheManager should create instances of TerracottaStore for Terracotta Clustered caches instead of - "
+ (tempStore == null ? "null" : tempStore.getClass().getName()));
}
CacheConfiguration.TransactionalMode clusteredTransactionalMode = ((TerracottaStore) tempStore)
.getTransactionalMode();
if (clusteredTransactionalMode != null
&& !clusteredTransactionalMode.equals(getCacheConfiguration().getTransactionalMode())) {
throw new InvalidConfigurationException("Transactional mode cannot be changed on clustered caches. "
+ "Please reconfigure cache '" + getName() + "' with transactionalMode = " + clusteredTransactionalMode);
}
TerracottaStore terracottaStore = makeClusteredTransactionalIfNeeded((TerracottaStore) tempStore, elementValueComparator, loader);
if (isSearchable()) {
Map extractors = new HashMap();
for (SearchAttribute sa : configuration.getSearchAttributes().values()) {
extractors.put(sa.getName(), sa.constructExtractor(loader));
}
terracottaStore.setAttributeExtractors(extractors);
}
return terracottaStore;
}
};
NonstopConfiguration nonstopConfig = getCacheConfiguration().getTerracottaConfiguration().getNonstopConfiguration();
// freeze the config whether nonstop is enabled or not
if (nonstopConfig != null) {
nonstopConfig.freezeConfig();
}
store = cacheManager.getClusteredInstanceFactory().createNonStopStore(callable, this);
clusterStateListener = new CacheClusterStateStatisticsListener(this);
getCacheCluster().addTopologyListener(clusterStateListener);
} else {
FeaturesManager featuresManager = cacheManager.getFeaturesManager();
if (featuresManager == null) {
if (configuration.isOverflowToOffHeap()) {
throw new CacheException("Cache " + configuration.getName()
+ " cannot be configured because the enterprise features manager could not be found. "
+ "You must use an enterprise version of Ehcache to successfully enable overflowToOffHeap.");
}
PersistenceConfiguration persistence = configuration.getPersistenceConfiguration();
if (persistence != null && Strategy.LOCALRESTARTABLE.equals(persistence.getStrategy())) {
throw new CacheException("Cache " + configuration.getName()
+ " cannot be configured because the enterprise features manager could not be found. "
+ "You must use an enterprise version of Ehcache to successfully enable enterprise persistence.");
}
if (useClassicLru && configuration.getMemoryStoreEvictionPolicy().equals(MemoryStoreEvictionPolicy.LRU)) {
Store disk = createDiskStore();
store = new LegacyStoreWrapper(new LruMemoryStore(this, disk), disk, registeredEventListeners, configuration);
} else {
if (configuration.isOverflowToDisk()) {
store = DiskStore.createCacheStore(this, onHeapPool, onDiskPool);
} else {
store = MemoryStore.create(this, onHeapPool);
}
}
} else {
store = featuresManager.createStore(this, onHeapPool, onDiskPool);
}
store = handleTransactionalAndCopy(store, loader);
}
this.compoundStore = store;
if (!isTerracottaClustered() && isSearchable()) {
Map extractors = new HashMap();
for (SearchAttribute sa : configuration.getSearchAttributes().values()) {
extractors.put(sa.getName(), sa.constructExtractor(loader));
}
compoundStore.setAttributeExtractors(extractors);
}
this.cacheWriterManager = configuration.getCacheWriterConfiguration().getWriteMode().createWriterManager(this, compoundStore);
StatisticsManager.associate(this).withChild(cacheWriterManager);
cacheStatus.changeState(Status.STATUS_ALIVE);
initialiseRegisteredCacheWriter();
initialiseCacheWriterManager(false);
initialiseRegisteredCacheExtensions();
initialiseRegisteredCacheLoaders();
Object context = compoundStore.getInternalContext();
if (context instanceof CacheLockProvider) {
lockProvider = (CacheLockProvider) context;
} else {
this.lockProvider = new StripedReadWriteLockSync(StripedReadWriteLockSync.DEFAULT_NUMBER_OF_MUTEXES);
}
StatisticsManager.associate(this).withChild(compoundStore);
statistics = new StatisticsGateway(this, cacheManager.getStatisticsExecutor());
}
if (!isTerracottaClustered()) {
compoundStore.addStoreListener(this);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Initialised cache: " + configuration.getName());
}
if (disabled) {
LOG.warn("Cache: " + configuration.getName() + " is disabled because the " + NET_SF_EHCACHE_DISABLED
+ " property was set to true. No elements will be added to the cache.");
}
}
private Store handleTransactionalAndCopy(Store store, ClassLoader loader) {
Store wrappedStore;
if (configuration.getTransactionalMode().isTransactional()) {
elementValueComparator = TxCopyingCacheStore.wrap(
configuration.getElementValueComparatorConfiguration().createElementComparatorInstance(configuration, loader), configuration);
wrappedStore = TxCopyingCacheStore.wrapTxStore(makeTransactional(store), configuration);
} else {
elementValueComparator = CopyingCacheStore.wrapIfCopy(
configuration.getElementValueComparatorConfiguration().createElementComparatorInstance(configuration, loader), configuration);
wrappedStore = CopyingCacheStore.wrapIfCopy(store, configuration);
}
return wrappedStore;
}
private void handleExceptionInTerracottaStoreCreation(IllegalArgumentException e) {
if (e.getMessage().contains("copyOnReadEnabled")) {
throw new InvalidConfigurationException("Conflict in configuration for clustered cache " + getName() + " . " +
"Source is either copyOnRead or transactional mode setting.");
} else {
throw new InvalidConfigurationException("Conflict in configuration for clustered cache " + getName() + " : " + e.getMessage());
}
}
private void checkClusteredConfig() {
final Consistency consistency = getCacheConfiguration().getTerracottaConfiguration().getConsistency();
final boolean coherent = getCacheConfiguration().getTerracottaConfiguration().isCoherent();
if (getCacheConfiguration().getTerracottaConfiguration().isSynchronousWrites() && consistency == Consistency.EVENTUAL) {
throw new InvalidConfigurationException(
"Terracotta clustered caches with eventual consistency and synchronous writes are not supported yet."
+ " You can fix this by either making the cache in 'strong' consistency mode "
+ "() or turning off synchronous writes.");
}
if (getCacheConfiguration().getTransactionalMode().isTransactional() && consistency == Consistency.EVENTUAL) {
throw new InvalidConfigurationException("Consistency should be " + Consistency.STRONG
+ " when cache is configured with transactions enabled. "
+ "You can fix this by either making the cache in 'strong' consistency mode "
+ "() or turning off transactions.");
}
if (getCacheConfiguration().getTransactionalMode().isTransactional()
&& !getCacheConfiguration().getTransactionalMode().equals(CacheConfiguration.TransactionalMode.XA_STRICT)
&& getCacheConfiguration().getTerracottaConfiguration().isNonstopEnabled()) {
LOG.warn("Cache: " + configuration.getName() + " configured both NonStop and transactional non xa_strict."
+ " NonStop features won't work for this cache!");
}
if ((coherent && consistency == Consistency.EVENTUAL) || (!coherent && consistency == Consistency.STRONG)) {
throw new InvalidConfigurationException("Coherent and consistency attribute values are conflicting. "
+ "Please remove the coherent attribute as its deprecated.");
}
}
private AbstractTransactionStore makeTransactional(final Store store) {
AbstractTransactionStore wrappedStore;
if (configuration.isXaStrictTransactional()) {
if (transactionManagerLookup.getTransactionManager() == null) {
throw new CacheException("You've configured cache " + cacheManager.getName() + "." + configuration.getName()
+ " to be transactional, but no TransactionManager could be found!");
}
// set xa enabled
if (configuration.isTerracottaClustered()) {
configuration.getTerracottaConfiguration().setCacheXA(true);
}
SoftLockManager softLockManager = cacheManager.createSoftLockManager(this);
TransactionIDFactory transactionIDFactory = cacheManager.getOrCreateTransactionIDFactory();
wrappedStore = new XATransactionStore(transactionManagerLookup, softLockManager,
transactionIDFactory, this, store, elementValueComparator);
} else if (configuration.isXaTransactional()) {
SoftLockManager softLockManager = cacheManager.createSoftLockManager(this);
LocalTransactionStore localTransactionStore = new LocalTransactionStore(getCacheManager().getTransactionController(),
getCacheManager().getOrCreateTransactionIDFactory(), softLockManager, this, store, elementValueComparator);
wrappedStore = new JtaLocalTransactionStore(localTransactionStore, transactionManagerLookup,
cacheManager.getTransactionController());
} else if (configuration.isLocalTransactional()) {
SoftLockManager softLockManager = cacheManager.createSoftLockManager(this);
wrappedStore = new LocalTransactionStore(getCacheManager().getTransactionController(), getCacheManager()
.getOrCreateTransactionIDFactory(), softLockManager, this, store, elementValueComparator);
} else {
throw new IllegalStateException("Method should called only with a transactional configuration");
}
return wrappedStore;
}
private TerracottaStore makeClusteredTransactionalIfNeeded(final TerracottaStore store, final ElementValueComparator comparator, ClassLoader loader) {
TerracottaStore wrappedStore;
if (configuration.getTransactionalMode().isTransactional()) {
if (configuration.isXaStrictTransactional()) {
if (transactionManagerLookup.getTransactionManager() == null) {
throw new CacheException("You've configured cache " + cacheManager.getName() + "." + configuration.getName()
+ " to be transactional, but no TransactionManager could be found!");
}
// set xa enabled
if (configuration.isTerracottaClustered()) {
configuration.getTerracottaConfiguration().setCacheXA(true);
}
SoftLockManager softLockManager = cacheManager.createSoftLockManager(this);
TransactionIDFactory transactionIDFactory = cacheManager.getOrCreateTransactionIDFactory();
wrappedStore = new XATransactionStore(transactionManagerLookup, softLockManager, transactionIDFactory, this, store, comparator);
} else if (configuration.isXaTransactional()) {
SoftLockManager softLockManager = cacheManager.createSoftLockManager(this);
LocalTransactionStore localTransactionStore = new LocalTransactionStore(getCacheManager().getTransactionController(),
getCacheManager().getOrCreateTransactionIDFactory(), softLockManager, this, store, comparator);
wrappedStore = new JtaLocalTransactionStore(localTransactionStore, transactionManagerLookup,
cacheManager.getTransactionController());
} else if (configuration.isLocalTransactional()) {
SoftLockManager softLockManager = cacheManager.createSoftLockManager(this);
wrappedStore = new LocalTransactionStore(getCacheManager().getTransactionController(), getCacheManager()
.getOrCreateTransactionIDFactory(), softLockManager, this, store, comparator);
} else {
throw new IllegalStateException("Should not get there");
}
wrappedStore = new TerracottaTransactionalCopyingCacheStore(wrappedStore, new ReadWriteSerializationCopyStrategy(), loader);
} else {
wrappedStore = store;
}
return wrappedStore;
}
private CacheCluster getCacheCluster() {
CacheCluster cacheCluster;
try {
cacheCluster = getCacheManager().getCluster(ClusterScheme.TERRACOTTA);
} catch (ClusterSchemeNotAvailableException e) {
LOG.info("Terracotta ClusterScheme is not available, using ClusterScheme.NONE");
cacheCluster = getCacheManager().getCluster(ClusterScheme.NONE);
}
return cacheCluster;
}
/**
* The CacheWriterManager's initialisation can be deferred until an actual CacheWriter has been registered.
*
* This allows users to register a cache through XML in the cache manager and still specify the CacheWriter manually through Java code, possibly referencing local resources.
*
* @param imperative indicates whether it's imperative for the cache writer manager to be initialised before operations can continue
* @throws CacheException when the CacheWriterManager couldn't be initialised but it was imperative to do so
*/
private void initialiseCacheWriterManager(boolean imperative) throws CacheException {
if (!cacheWriterManagerInitFlag.get()) {
cacheWriterManagerInitLock.lock();
try {
if (!cacheWriterManagerInitFlag.get()) {
if (cacheWriterManager != null && registeredCacheWriter != null) {
cacheWriterManager.init(this);
cacheWriterManagerInitFlag.set(true);
} else if (imperative) {
throw new CacheException("Cache: " + configuration.getName() + " was being used with cache writer " +
"features, but it wasn't properly registered beforehand.");
}
}
} finally {
cacheWriterManagerInitLock.unlock();
}
}
}
/**
* {@inheritDoc}
*/
public CacheWriterManager getWriterManager() {
return cacheWriterManager;
}
/**
* Creates a disk store when either:
*
* - overflowToDisk is enabled
*
- diskPersistent is enabled
*
*
* @return the disk store
*/
protected DiskStore createDiskStore() {
if (isDiskStore()) {
return DiskStore.create(this);
} else {
return null;
}
}
/**
* Whether this cache uses a disk store
*
* @return true if the cache either overflows to disk or uses a local-classic persistence strategy.
*/
protected boolean isDiskStore() {
return configuration.isOverflowToDisk();
}
/**
* Indicates whether this cache is clustered by Terracotta
*
* @return {@code true} when the cache is clustered by Terracotta; or {@code false} otherwise
*/
public boolean isTerracottaClustered() {
return configuration.isTerracottaClustered();
}
/**
* Bootstrap command. This must be called after the Cache is initialised, during
* CacheManager initialisation. If loads are synchronous, they will complete before the CacheManager
* initialise completes, otherwise they will happen in the background.
*/
public void bootstrap() {
if (!disabled && bootstrapCacheLoader != null) {
bootstrapCacheLoader.load(this);
}
}
/**
* Put an element in the cache.
*
* Resets the access statistics on the element, which would be the case if it has previously been
* gotten from a cache, and is now being put back.
*
* Also notifies the CacheEventListener that:
*
* - the element was put, but only if the Element was actually put.
*
- if the element exists in the cache, that an update has occurred, even if the element would be expired
* if it was requested
*
*
* Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
* This exception should be caught in those circumstances.
*
* @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
* null
or the key is null
, it is ignored as a NOOP.
* @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
* @throws CacheException
*/
public final void put(Element element) throws IllegalArgumentException, IllegalStateException,
CacheException {
put(element, false);
}
/**
* {@inheritDoc}
*/
public void putAll(Collection elements) throws IllegalArgumentException, IllegalStateException, CacheException {
putAll(elements, false);
}
/**
* Put an element in the cache.
*
* Resets the access statistics on the element, which would be the case if it has previously been
* gotten from a cache, and is now being put back.
*
* Also notifies the CacheEventListener that:
*
* - the element was put, but only if the Element was actually put.
*
- if the element exists in the cache, that an update has occurred, even if the element would be expired
* if it was requested
*
* Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
* This exception should be caught in those circumstances.
*
* @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
* null
or the key is null
, it is ignored as a NOOP.
* @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
* further notification to doNotNotifyCacheReplicators cache peers
* @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
* @throws IllegalArgumentException if the element is null
*/
public final void put(Element element, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
IllegalStateException, CacheException {
putInternal(element, doNotNotifyCacheReplicators, false);
}
/**
* {@inheritDoc}
*/
private void putAll(Collection elements, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
IllegalStateException, CacheException {
putAllInternal(elements, doNotNotifyCacheReplicators);
}
/**
* {@inheritDoc}
*/
public void putWithWriter(Element element) throws IllegalArgumentException, IllegalStateException, CacheException {
putInternal(element, false, true);
}
private void putInternal(Element element, boolean doNotNotifyCacheReplicators, boolean useCacheWriter) {
putObserver.begin();
if (useCacheWriter) {
initialiseCacheWriterManager(true);
}
checkStatus();
if (disabled) {
putObserver.end(PutOutcome.IGNORED);
return;
}
if (element == null) {
if (doNotNotifyCacheReplicators) {
LOG.debug("Element from replicated put is null. This happens because the element is a SoftReference" +
" and it has been collected. Increase heap memory on the JVM or set -Xms to be the same as " +
"-Xmx to avoid this problem.");
}
putObserver.end(PutOutcome.IGNORED);
return;
}
if (element.getObjectKey() == null) {
putObserver.end(PutOutcome.IGNORED);
return;
}
element.resetAccessStatistics();
applyDefaultsToElementWithoutLifespanSet(element);
backOffIfDiskSpoolFull();
element.updateUpdateStatistics();
boolean elementExists = false;
if (useCacheWriter) {
boolean notifyListeners = true;
try {
elementExists = !compoundStore.putWithWriter(element, cacheWriterManager);
} catch (StoreUpdateException e) {
elementExists = e.isUpdate();
notifyListeners = configuration.getCacheWriterConfiguration().getNotifyListenersOnException();
RuntimeException cause = e.getCause();
if (cause instanceof CacheWriterManagerException) {
throw ((CacheWriterManagerException)cause).getCause();
}
throw cause;
} finally {
if (notifyListeners) {
notifyPutInternalListeners(element, doNotNotifyCacheReplicators, elementExists);
}
}
} else {
elementExists = !compoundStore.put(element);
notifyPutInternalListeners(element, doNotNotifyCacheReplicators, elementExists);
}
putObserver.end(elementExists ? PutOutcome.UPDATED : PutOutcome.ADDED);
}
private void putAllInternal(Collection elements, boolean doNotNotifyCacheReplicators) {
putAllObserver.begin();
checkStatus();
if (disabled || elements.isEmpty()) {
putAllObserver.end(PutAllOutcome.IGNORED);
return;
}
backOffIfDiskSpoolFull();
compoundStore.putAll(elements);
for (Element element : elements) {
element.resetAccessStatistics();
applyDefaultsToElementWithoutLifespanSet(element);
notifyPutInternalListeners(element, doNotNotifyCacheReplicators, false);
}
putAllObserver.end(PutAllOutcome.COMPLETED);
}
private void notifyPutInternalListeners(Element element, boolean doNotNotifyCacheReplicators, boolean elementExists) {
if (elementExists) {
registeredEventListeners.notifyElementUpdated(element, doNotNotifyCacheReplicators);
} else {
registeredEventListeners.notifyElementPut(element, doNotNotifyCacheReplicators);
}
}
/**
* wait outside of synchronized block so as not to block readers
* If the disk store spool is full wait a short time to give it a chance to
* catch up.
* todo maybe provide a warning if this is continually happening or monitor via JMX
*/
private void backOffIfDiskSpoolFull() {
if (compoundStore.bufferFull()) {
// back off to avoid OutOfMemoryError
try {
Thread.sleep(BACK_OFF_TIME_MILLIS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
private void applyDefaultsToElementWithoutLifespanSet(Element element) {
if (!element.isLifespanSet()) {
element.setLifespanDefaults(TimeUtil.convertTimeToInt(configuration.getTimeToIdleSeconds()),
TimeUtil.convertTimeToInt(configuration.getTimeToLiveSeconds()),
configuration.isEternal());
}
}
/**
* Put an element in the cache, without updating statistics, or updating listeners. This is meant to be used
* in conjunction with {@link #getQuiet}.
* Synchronization is handled within the method.
*
* Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
* This exception should be caught in those circumstances.
*
*
* @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
* null
or the key is null
, it is ignored as a NOOP.
* @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
* @throws IllegalArgumentException if the element is null
*/
public final void putQuiet(Element element) throws IllegalArgumentException, IllegalStateException,
CacheException {
checkStatus();
if (disabled) {
return;
}
if (element == null || element.getObjectKey() == null) {
//nulls are ignored
return;
}
applyDefaultsToElementWithoutLifespanSet(element);
compoundStore.put(element);
}
/**
* Gets an element from the cache. Updates Element Statistics
*
* Note that the Element's lastAccessTime is always the time of this get.
* Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
*
* Synchronization is handled within the method.
*
* @param key a serializable value. Null keys are not stored so get(null) always returns null
* @return the element, or null, if it does not exist.
* @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
* @see #isExpired
*/
public final Element get(Serializable key) throws IllegalStateException, CacheException {
return get((Object) key);
}
/**
* Gets an element from the cache. Updates Element Statistics
*
* Note that the Element's lastAccessTime is always the time of this get.
* Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
*
* Synchronization is handled within the method.
*
* @param key an Object value
* @return the element, or null, if it does not exist.
* @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
* @see #isExpired
* @since 1.2
*/
public final Element get(Object key) throws IllegalStateException, CacheException {
getObserver.begin();
checkStatus();
if (disabled) {
getObserver.end(GetOutcome.MISS_NOT_FOUND);
return null;
}
Element element = compoundStore.get(key);
if (element == null) {
getObserver.end(GetOutcome.MISS_NOT_FOUND);
return null;
} else if (isExpired(element)) {
tryRemoveImmediately(key, true);
getObserver.end(GetOutcome.MISS_EXPIRED);
return null;
} else if (!skipUpdateAccessStatistics(element)) {
element.updateAccessStatistics();
}
getObserver.end(GetOutcome.HIT);
return element;
}
/**
* {@inheritDoc}
*/
public Map