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
This is the ehcache core module. Pair it with other modules for added functionality.
/**
* Copyright 2003-2010 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.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.SearchAttribute;
import net.sf.ehcache.config.TerracottaConfiguration;
import net.sf.ehcache.config.TerracottaConfiguration.Consistency;
import net.sf.ehcache.constructs.nonstop.NonstopActiveDelegateHolder;
import net.sf.ehcache.constructs.nonstop.NonstopExecutorService;
import net.sf.ehcache.constructs.nonstop.store.NonstopStoreImpl;
import net.sf.ehcache.constructs.nonstop.store.RejoinAwareNonstopStore;
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.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.attribute.AttributeExtractor;
import net.sf.ehcache.statistics.CacheUsageListener;
import net.sf.ehcache.statistics.LiveCacheStatistics;
import net.sf.ehcache.statistics.LiveCacheStatisticsWrapper;
import net.sf.ehcache.statistics.sampled.SampledCacheStatistics;
import net.sf.ehcache.statistics.sampled.SampledCacheStatisticsWrapper;
import net.sf.ehcache.store.DiskStore;
import net.sf.ehcache.store.ElementValueComparator;
import net.sf.ehcache.store.LegacyStoreWrapper;
import net.sf.ehcache.store.LruMemoryStore;
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.TerracottaStore;
import net.sf.ehcache.store.compound.ImmutableValueElementCopyStrategy;
import net.sf.ehcache.store.compound.ReadWriteCopyStrategy;
import net.sf.ehcache.store.compound.StoreUpdateException;
import net.sf.ehcache.store.compound.impl.DiskPersistentStore;
import net.sf.ehcache.store.compound.impl.MemoryOnlyStore;
import net.sf.ehcache.store.compound.impl.OverflowToDiskStore;
import net.sf.ehcache.terracotta.TerracottaClient;
import net.sf.ehcache.terracotta.TerracottaNotRunningException;
import net.sf.ehcache.transaction.SoftLockFactory;
import net.sf.ehcache.transaction.local.JtaLocalTransactionStore;
import net.sf.ehcache.transaction.local.LocalTransactionStore;
import net.sf.ehcache.transaction.TransactionIDFactory;
import net.sf.ehcache.transaction.manager.TransactionManagerLookup;
import net.sf.ehcache.transaction.xa.EhcacheXAResource;
import net.sf.ehcache.transaction.xa.XATransactionStore;
import net.sf.ehcache.transaction.xa.EhcacheXAResourceImpl;
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 java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
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.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.AbstractExecutorService;
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.locks.ReentrantLock;
/**
* 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 4148 2011-06-20 16:16:48Z lorban $
*/
public class Cache implements Ehcache, 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 String OFF_HEAP_STORE_CLASSNAME = "net.sf.ehcache.store.offheap.OffHeapStore";
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 volatile String diskStorePath;
private volatile CacheStatus cacheStatus = new CacheStatus();
private volatile 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 volatile List registeredCacheExtensions;
private volatile String guid;
private volatile CacheManager cacheManager;
private volatile BootstrapCacheLoader bootstrapCacheLoader;
private volatile CacheExceptionHandler cacheExceptionHandler;
private volatile List registeredCacheLoaders;
private volatile CacheWriterManager cacheWriterManager;
private final AtomicBoolean cacheWriterManagerInitFlag = new AtomicBoolean(false);
private final ReentrantLock cacheWriterManagerInitLock = new ReentrantLock();
private volatile CacheWriter registeredCacheWriter;
/**
* A ThreadPoolExecutor which uses a thread pool to schedule loads in the order in which they are requested.
*
* Each cache has its own one of these, if required. Because the Core Thread Pool is zero, no threads
* are used until actually needed. Threads are added to the pool up to a maximum of 10. The keep alive
* time is 60 seconds, after which, if they are not required they will be stopped and collected.
*
* 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 LiveCacheStatisticsWrapper liveCacheStatisticsData;
private volatile SampledCacheStatisticsWrapper sampledCacheStatistics;
private volatile TransactionManagerLookup transactionManagerLookup;
private volatile boolean allowDisable = true;
private volatile PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
private volatile ElementValueComparator elementValueComparator;
private volatile NonstopActiveDelegateHolderImpl nonstopActiveDelegateHolder;
/**
* 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, 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) {
cacheStatus.changeState(Status.STATUS_UNINITIALISED);
this.configuration = cacheConfiguration.clone();
configuration.validateCompleteConfiguration();
elementValueComparator = cacheConfiguration.getElementValueComparatorConfiguration().getElementComparatorInstance();
guid = createGuid();
this.diskStorePath = cacheConfiguration.getDiskStorePath();
if (registeredEventListeners == null) {
this.registeredEventListeners = new RegisteredEventListeners(this);
} else {
this.registeredEventListeners = registeredEventListeners;
}
registeredCacheExtensions = new CopyOnWriteArrayList();
registeredCacheLoaders = new CopyOnWriteArrayList();
//initialize statistics
liveCacheStatisticsData = new LiveCacheStatisticsWrapper(this);
sampledCacheStatistics = new SampledCacheStatisticsWrapper();
RegisteredEventListeners listeners = getCacheEventNotificationService();
registerCacheListeners(configuration, listeners);
registerCacheExtensions(configuration, this);
if (null == bootstrapCacheLoader) {
this.bootstrapCacheLoader = createBootstrapCacheLoader(configuration.getBootstrapCacheLoaderFactoryConfiguration());
} else {
this.bootstrapCacheLoader = bootstrapCacheLoader;
}
registerCacheLoaders(configuration, this);
registerCacheWriter(configuration, this);
this.nonstopActiveDelegateHolder = new NonstopActiveDelegateHolderImpl(this);
}
/**
* 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)
.diskStorePath(diskStorePath)
.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)
.diskStorePath(diskStorePath)
.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)
.diskStorePath(diskStorePath)
.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)
.diskStorePath(diskStorePath)
.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)
.diskStorePath(diskStorePath)
.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 terracottaValueMode either "SERIALIZATION" or "IDENTITY" mode, only used if isTerracottaClustered=true
* @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, String terracottaValueMode, boolean terracottaCoherentReads) {
this(new CacheConfiguration(name, maxElementsInMemory)
.memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
.overflowToDisk(overflowToDisk)
.diskStorePath(diskStorePath)
.eternal(eternal)
.timeToLiveSeconds(timeToLiveSeconds)
.timeToIdleSeconds(timeToIdleSeconds)
.diskPersistent(diskPersistent)
.diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
.maxElementsOnDisk(maxElementsOnDisk)
.diskSpoolBufferSizeMB(diskSpoolBufferSizeMB)
.clearOnFlush(clearOnFlush)
.terracotta(new TerracottaConfiguration()
.clustered(isTerracottaClustered)
.valueMode(terracottaValueMode)
.coherentReads(terracottaCoherentReads)),
registeredEventListeners,
bootstrapCacheLoader);
}
/**
* A factory method to create a RegisteredEventListeners
*/
private static void registerCacheListeners(CacheConfiguration cacheConfiguration,
RegisteredEventListeners registeredEventListeners) {
List cacheEventListenerConfigurations = cacheConfiguration.getCacheEventListenerConfigurations();
for (Object cacheEventListenerConfiguration : cacheEventListenerConfigurations) {
CacheConfiguration.CacheEventListenerFactoryConfiguration factoryConfiguration =
(CacheConfiguration.CacheEventListenerFactoryConfiguration) cacheEventListenerConfiguration;
CacheEventListener cacheEventListener = createCacheEventListener(factoryConfiguration);
registeredEventListeners.registerListener(cacheEventListener, factoryConfiguration.getListenFor());
}
}
/**
* A factory method to register cache extensions
*
* @param cacheConfiguration the cache configuration
* @param cache the cache
*/
private static void registerCacheExtensions(CacheConfiguration cacheConfiguration, Ehcache cache) {
List cacheExtensionConfigurations = cacheConfiguration.getCacheExtensionConfigurations();
for (Object cacheExtensionConfiguration : cacheExtensionConfigurations) {
CacheConfiguration.CacheExtensionFactoryConfiguration factoryConfiguration =
(CacheConfiguration.CacheExtensionFactoryConfiguration) cacheExtensionConfiguration;
CacheExtension cacheExtension = createCacheExtension(factoryConfiguration, cache);
cache.registerCacheExtension(cacheExtension);
}
}
/**
* A factory method to register cache Loaders
*
* @param cacheConfiguration the cache configuration
* @param cache the cache
*/
private static void registerCacheLoaders(CacheConfiguration cacheConfiguration, Ehcache cache) {
List cacheLoaderConfigurations = cacheConfiguration.getCacheLoaderConfigurations();
for (Object cacheLoaderConfiguration : cacheLoaderConfigurations) {
CacheConfiguration.CacheLoaderFactoryConfiguration factoryConfiguration =
(CacheConfiguration.CacheLoaderFactoryConfiguration) cacheLoaderConfiguration;
CacheLoader cacheLoader = createCacheLoader(factoryConfiguration, cache);
cache.registerCacheLoader(cacheLoader);
}
}
/**
* A factory method to register cache writers
*
* @param cacheConfiguration the cache configuration
* @param cache the cache
*/
private static void registerCacheWriter(CacheConfiguration cacheConfiguration, Ehcache cache) {
CacheWriterConfiguration config = cacheConfiguration.getCacheWriterConfiguration();
if (config != null) {
CacheWriter cacheWriter = createCacheWriter(config, cache);
cache.registerCacheWriter(cacheWriter);
}
}
/**
* Tries to load the class specified otherwise defaults to null.
*
* @param factoryConfiguration
*/
private static CacheEventListener createCacheEventListener(
CacheConfiguration.CacheEventListenerFactoryConfiguration factoryConfiguration) {
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(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
*/
private static CacheExtension createCacheExtension(
CacheConfiguration.CacheExtensionFactoryConfiguration factoryConfiguration, Ehcache cache) {
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(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
*/
private static CacheLoader createCacheLoader(
CacheConfiguration.CacheLoaderFactoryConfiguration factoryConfiguration, Ehcache cache) {
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(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
*/
private static CacheWriter createCacheWriter(CacheWriterConfiguration config, Ehcache cache) {
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(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.
*
* @return If there is none returns null.
*/
private static final BootstrapCacheLoader createBootstrapCacheLoader(
CacheConfiguration.BootstrapCacheLoaderFactoryConfiguration factoryConfiguration) 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(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) {
if (!cacheStatus.canInitialize()) {
throw new IllegalStateException("Cannot initialise the " + configuration.getName()
+ " cache because its status is not STATUS_UNINITIALISED");
}
if (configuration.getMaxElementsInMemory() == 0) {
LOG.warn("Cache: " + configuration.getName() +
" has a maxElementsInMemory of 0. " +
"In Ehcache 2.0 this has been changed to mean a store" +
" with no capacity limit. Set it to 1 if you want" +
" no elements cached in memory");
}
ReadWriteCopyStrategy copyStrategy = null;
if (configuration.getTransactionalMode().isTransactional()) {
configuration.getCopyStrategyConfiguration().setCopyStrategyInstance(null);
copyStrategy = configuration.getCopyStrategyConfiguration().getCopyStrategyInstance();
configuration.getCopyStrategyConfiguration().setCopyStrategyInstance(new ImmutableValueElementCopyStrategy());
}
if (configuration.getTransactionalMode().isTransactional()
&& configuration.isTerracottaClustered()
&& configuration.getTerracottaConfiguration().getValueMode() != TerracottaConfiguration.ValueMode.SERIALIZATION) {
throw new CacheException("To be transactional, a Terracotta clustered cache needs to be in Serialization value mode");
}
final Store store;
if (isTerracottaClustered()) {
final Consistency consistency = getCacheConfiguration().getTerracottaConfiguration().getConsistency();
final boolean coherent = getCacheConfiguration().getTerracottaConfiguration().isCoherent();
if (getCacheConfiguration().isOverflowToOffHeap()) {
throw new InvalidConfigurationException(
"Terracotta clustered caches with local overflow to offheap are not supported yet."
+ " You can fix this by disabling overflow to offheap for clustered caches.");
}
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.");
}
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");
}
if (getCacheConfiguration().getMaxElementsOnDisk() == 0) {
LOG.warn("Performance may degrade and server disks could run out of space!\nThe distributed cache {} does not have " +
"maxElementsOnDisk set. Failing to set maxElementsOnDisk could mean no eviction of its elements from the " +
"Terracotta Server Array disk store. To avoid this, set maxElementsOnDisk to a non-zero value.", getName());
}
if (!getCacheConfiguration().getTerracottaConfiguration().isStorageStrategySet()) {
getCacheConfiguration().getTerracottaConfiguration().storageStrategy(
TerracottaClient.getTerracottaDefaultStrategyForCurrentRuntime(getCacheConfiguration()));
}
Store tempStore = cacheManager.createTerracottaStore(this);
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()));
}
TerracottaStore terracottaStore = (TerracottaStore) makeXaStrictTransactionalIfNeeded(tempStore, copyStrategy);
NonstopConfiguration nonstopConfig = getCacheConfiguration().getTerracottaConfiguration().getNonstopConfiguration();
// freeze the config whether nonstop is enabled or not
if (nonstopConfig != null) {
nonstopConfig.freezeConfig();
}
if (getCacheConfiguration().getTerracottaConfiguration().isNonstopEnabled()) {
nonstopActiveDelegateHolder.terracottaStoreInitialized(terracottaStore);
store = nonstopActiveDelegateHolder.getNonstopStore();
} else {
store = terracottaStore;
}
} else if (getCacheConfiguration().isOverflowToOffHeap()) {
try {
Class storeClass = ClassLoaderUtil.loadClass(OFF_HEAP_STORE_CLASSNAME);
try {
store = makeXaStrictTransactionalIfNeeded((Store) storeClass.getMethod("create", Ehcache.class, String.class)
.invoke(null, this, diskStorePath), copyStrategy);
} catch (NoSuchMethodException e) {
throw new CacheException("Cache: " + configuration.getName() + " cannot find static factory"
+ " method create(Ehcache, String)" + " in store class " + OFF_HEAP_STORE_CLASSNAME, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof CacheException) {
throw (CacheException) cause;
} else {
throw new CacheException("Cache: " + configuration.getName() + " cannot instantiate store "
+ OFF_HEAP_STORE_CLASSNAME, cause);
}
} catch (IllegalAccessException e) {
throw new CacheException("Cache: " + configuration.getName() + " cannot instantiate store "
+ OFF_HEAP_STORE_CLASSNAME, e);
}
} catch (ClassNotFoundException e) {
throw new CacheException("Cache " + configuration.getName()
+ " cannot be configured because the off-heap store class could not be found. "
+ "You must use an enterprise version of Ehcache to successfully enable overflowToOffHeap.");
}
} else {
if (useClassicLru && configuration.getMemoryStoreEvictionPolicy().equals(MemoryStoreEvictionPolicy.LRU)) {
Store disk = createDiskStore();
store = makeXaStrictTransactionalIfNeeded(new LegacyStoreWrapper(
new LruMemoryStore(this, disk), disk, registeredEventListeners, configuration), copyStrategy);
} else {
if (configuration.isDiskPersistent()) {
store = makeXaStrictTransactionalIfNeeded(DiskPersistentStore.create(this, diskStorePath), copyStrategy);
} else if (configuration.isOverflowToDisk()) {
store = makeXaStrictTransactionalIfNeeded(OverflowToDiskStore.create(this, diskStorePath), copyStrategy);
} else {
store = makeXaStrictTransactionalIfNeeded(MemoryOnlyStore.create(this, diskStorePath), copyStrategy);
}
}
}
/* note: this isn't part of makeXaStrictTransactionalIfNeeded() as only xa_strict supports NonStop, meaning that only
* that transactional store can be wrapped by NonStopStore. Other TX modes have to wrap the NonStop store due to their
* lack of NonStop support (ie: lack of transaction context suspension/resuming).
*/
if (configuration.isXaTransactional()) {
SoftLockFactory softLockFactory = cacheManager.createSoftLockFactory(this);
LocalTransactionStore localTransactionStore = new LocalTransactionStore(getCacheManager().getTransactionController(),
softLockFactory, this, store, copyStrategy);
this.compoundStore = new JtaLocalTransactionStore(localTransactionStore, transactionManagerLookup,
cacheManager.getTransactionController());
} else if (configuration.isLocalTransactional()) {
SoftLockFactory softLockFactory = cacheManager.createSoftLockFactory(this);
this.compoundStore = new LocalTransactionStore(getCacheManager().getTransactionController(), softLockFactory, this, store,
copyStrategy);
} else {
this.compoundStore = store;
}
Map extractors = new HashMap();
for (SearchAttribute sa : configuration.getSearchAttributes().values()) {
extractors.put(sa.getName(), sa.constructExtractor());
}
compoundStore.setAttributeExtractors(extractors);
this.cacheWriterManager = configuration.getCacheWriterConfiguration().getWriteMode().createWriterManager(this);
initialiseCacheWriterManager(false);
cacheStatus.changeState(Status.STATUS_ALIVE);
initialiseRegisteredCacheExtensions();
initialiseRegisteredCacheLoaders();
initialiseRegisteredCacheWriter();
// initialize live statistics
// register to get notifications of
// put/update/removeInternal/expiry/eviction
getCacheEventNotificationService().registerListener(liveCacheStatisticsData);
// set up default values
liveCacheStatisticsData.setStatisticsAccuracy(Statistics.STATISTICS_ACCURACY_BEST_EFFORT);
liveCacheStatisticsData.setStatisticsEnabled(configuration.getStatistics());
// register the sampled cache statistics
this.registerCacheUsageListener(sampledCacheStatistics);
if (isTerracottaClustered()) {
// create this to be sure that it's present on each node to receive clustered events,
// even if this node is not sending out its events
cacheManager.createTerracottaEventReplicator(this);
}
Object context = compoundStore.getInternalContext();
if (context instanceof CacheLockProvider) {
lockProvider = (CacheLockProvider) context;
} else {
this.lockProvider = new StripedReadWriteLockSync(StripedReadWriteLockSync.DEFAULT_NUMBER_OF_MUTEXES);
}
}
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.");
}
}
/*
* Note: this method could be used for xa and local tx modes as well if they supported NonStop
*/
private Store makeXaStrictTransactionalIfNeeded(Store clusteredStore, ReadWriteCopyStrategy copyStrategy) {
Store 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);
}
SoftLockFactory softLockFactory = cacheManager.createSoftLockFactory(this);
TransactionIDFactory transactionIDFactory = cacheManager.createTransactionIDFactory();
// this xaresource is for initial registration and recovery
EhcacheXAResource xaResource = new EhcacheXAResourceImpl(this, clusteredStore, transactionManagerLookup,
softLockFactory, transactionIDFactory);
transactionManagerLookup.register(xaResource);
wrappedStore = new XATransactionStore(transactionManagerLookup, softLockFactory, transactionIDFactory, this, clusteredStore,
copyStrategy);
} else {
wrappedStore = clusteredStore;
}
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, diskStorePath);
} else {
return null;
}
}
/**
* Whether this cache uses a disk store
*
* @return true if the cache either overflows to disk or is disk persistent
*/
protected boolean isDiskStore() {
return configuration.isOverflowToDisk() || configuration.isDiskPersistent();
}
/**
* 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);
}
/**
* 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}
*/
public void putWithWriter(Element element) throws IllegalArgumentException, IllegalStateException, CacheException {
putInternal(element, false, true);
}
private void putInternal(Element element, boolean doNotNotifyCacheReplicators, boolean useCacheWriter) {
if (useCacheWriter) {
initialiseCacheWriterManager(true);
}
checkStatus();
if (disabled) {
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.");
}
//nulls are ignored
return;
}
if (element.getObjectKey() == null) {
//nulls are ignored
return;
}
element.resetAccessStatistics();
applyDefaultsToElementWithoutLifespanSet(element);
backOffIfDiskSpoolFull();
if (useCacheWriter) {
boolean elementExists = false;
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 (elementExists) {
element.updateUpdateStatistics();
}
if (notifyListeners) {
notifyPutInternalListeners(element, doNotNotifyCacheReplicators, elementExists);
}
}
} else {
boolean elementExists = !compoundStore.put(element);
if (elementExists) {
element.updateUpdateStatistics();
}
notifyPutInternalListeners(element, doNotNotifyCacheReplicators, elementExists);
}
}
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) {
//do not care if this happens
}
}
}
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 {
checkStatus();
if (disabled) {
return null;
}
if (isStatisticsEnabled()) {
long start = System.currentTimeMillis();
Element element = searchInStoreWithStats(key);
//todo is this expensive. Maybe ditch.
long end = System.currentTimeMillis();
liveCacheStatisticsData.addGetTimeMillis(end - start);
return element;
} else {
return searchInStoreWithoutStats(key, false, true);
}
}
/**
* This method will return, from the cache, the Element associated with the argument "key".
*
* If the Element is not in the cache, the associated cache loader will be called. That is either the CacheLoader passed in, or if null,
* the one associated with the cache. If both are null, no load is performed and null is returned.
*
* If the loader decides to assign a null value to the Element, an Element with a null value is created and stored in the cache.
*
* Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
* are synchronized.
*
* @param key key whose associated value is to be returned.
* @param loader the override loader to use. If null, the cache's default loader will be used
* @param loaderArgument an argument to pass to the CacheLoader.
* @return an element if it existed or could be loaded, otherwise null
* @throws CacheException
*/
public Element getWithLoader(Object key, CacheLoader loader, Object loaderArgument) throws CacheException {
Element element = get(key);
if (element != null) {
return element;
}
if (registeredCacheLoaders.size() == 0 && loader == null) {
return null;
}
try {
//check again in case the last thread loaded it
element = getQuiet(key);
if (element != null) {
return element;
}
Future future = asynchronousLoad(key, loader, loaderArgument);
//wait for result
long cacheLoaderTimeoutMillis = configuration.getCacheLoaderTimeoutMillis();
if (cacheLoaderTimeoutMillis > 0) {
future.get(cacheLoaderTimeoutMillis, TimeUnit.MILLISECONDS);
} else {
future.get();
}
} catch (TimeoutException e) {
throw new LoaderTimeoutException("Timeout on load for key " + key, e);
} catch (Exception e) {
throw new CacheException("Exception on load for key " + key, e);
}
return getQuiet(key);
}
/**
* The load method provides a means to "pre load" the cache. This method will, asynchronously, load the specified
* object into the cache using the associated CacheLoader. If the object already exists in the cache, no action is
* taken. If no loader is associated with the object, no object will be loaded into the cache. If a problem is
* encountered during the retrieving or loading of the object, an exception should be logged. If the "arg" argument
* is set, the arg object will be passed to the CacheLoader.load method. The cache will not dereference the object.
* If no "arg" value is provided a null will be passed to the load method. The storing of null values in the cache
* is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding
* the object in the cache. In both cases a null is returned.
*
* The Ehcache native API provides similar functionality to loaders using the
* decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
*
* @param key key whose associated value to be loaded using the associated CacheLoader if this cache doesn't contain it.
* @throws CacheException
*/
public void load(final Object key) throws CacheException {
if (registeredCacheLoaders.size() == 0) {
LOG.debug("The CacheLoader is null. Returning.");
return;
}
boolean existsOnCall = isKeyInCache(key);
if (existsOnCall) {
LOG.debug("The key {} exists in the cache. Returning.", key);
return;
}
asynchronousLoad(key, null, null);
}
/**
* The getAll method will return, from the cache, a Map of the objects associated with the Collection of keys in argument "keys".
* If the objects are not in the cache, the associated cache loader will be called. If no loader is associated with an object,
* a null is returned. If a problem is encountered during the retrieving or loading of the objects, an exception will be thrown.
* If the "arg" argument is set, the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference
* the object. If no "arg" value is provided a null will be passed to the loadAll method. The storing of null values in the cache
* is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding the object in
* the cache. In both cases a null is returned.
*
*
* Note. If the getAll exceeds the maximum cache size, the returned map will necessarily be less than the number specified.
*
* Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
* are synchronized.
*
* The constructs package provides similar functionality using the
* decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
*
* @param keys a collection of keys to be returned/loaded
* @param loaderArgument an argument to pass to the CacheLoader.
* @return a Map populated from the Cache. If there are no elements, an empty Map is returned.
* @throws CacheException
*/
public Map getAllWithLoader(Collection keys, Object loaderArgument) throws CacheException {
if (keys == null) {
return new HashMap(0);
}
Map