View Javadoc

1   /***
2    *  Copyright 2003-2010 Terracotta, Inc.
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package net.sf.ehcache;
18  
19  import java.beans.PropertyChangeListener;
20  import java.beans.PropertyChangeSupport;
21  import java.io.IOException;
22  import java.io.Serializable;
23  import java.lang.reflect.InvocationTargetException;
24  import java.net.InetAddress;
25  import java.net.UnknownHostException;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.Collections;
29  import java.util.HashMap;
30  import java.util.HashSet;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Map.Entry;
35  import java.util.Properties;
36  import java.util.Set;
37  import java.util.UUID;
38  import java.util.concurrent.AbstractExecutorService;
39  import java.util.concurrent.CopyOnWriteArrayList;
40  import java.util.concurrent.ExecutionException;
41  import java.util.concurrent.ExecutorService;
42  import java.util.concurrent.Future;
43  import java.util.concurrent.LinkedBlockingQueue;
44  import java.util.concurrent.ThreadPoolExecutor;
45  import java.util.concurrent.TimeUnit;
46  import java.util.concurrent.TimeoutException;
47  import java.util.concurrent.atomic.AtomicBoolean;
48  import java.util.concurrent.atomic.AtomicReference;
49  import java.util.concurrent.locks.ReentrantLock;
50  
51  import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
52  import net.sf.ehcache.bootstrap.BootstrapCacheLoaderFactory;
53  import net.sf.ehcache.cluster.CacheCluster;
54  import net.sf.ehcache.cluster.ClusterScheme;
55  import net.sf.ehcache.cluster.ClusterSchemeNotAvailableException;
56  import net.sf.ehcache.concurrent.CacheLockProvider;
57  import net.sf.ehcache.concurrent.LockType;
58  import net.sf.ehcache.concurrent.StripedReadWriteLockSync;
59  import net.sf.ehcache.concurrent.Sync;
60  import net.sf.ehcache.config.CacheConfiguration;
61  import net.sf.ehcache.config.CacheWriterConfiguration;
62  import net.sf.ehcache.config.DiskStoreConfiguration;
63  import net.sf.ehcache.config.InvalidConfigurationException;
64  import net.sf.ehcache.config.NonstopConfiguration;
65  import net.sf.ehcache.config.SearchAttribute;
66  import net.sf.ehcache.config.TerracottaConfiguration;
67  import net.sf.ehcache.config.TerracottaConfiguration.Consistency;
68  import net.sf.ehcache.config.TerracottaConfiguration.StorageStrategy;
69  import net.sf.ehcache.constructs.nonstop.NonstopActiveDelegateHolder;
70  import net.sf.ehcache.constructs.nonstop.NonstopExecutorService;
71  import net.sf.ehcache.constructs.nonstop.store.NonstopStoreImpl;
72  import net.sf.ehcache.constructs.nonstop.store.RejoinAwareNonstopStore;
73  import net.sf.ehcache.event.CacheEventListener;
74  import net.sf.ehcache.event.CacheEventListenerFactory;
75  import net.sf.ehcache.event.RegisteredEventListeners;
76  import net.sf.ehcache.exceptionhandler.CacheExceptionHandler;
77  import net.sf.ehcache.extension.CacheExtension;
78  import net.sf.ehcache.extension.CacheExtensionFactory;
79  import net.sf.ehcache.loader.CacheLoader;
80  import net.sf.ehcache.loader.CacheLoaderFactory;
81  import net.sf.ehcache.pool.Pool;
82  import net.sf.ehcache.pool.PoolEvictor;
83  import net.sf.ehcache.pool.PoolableStore;
84  import net.sf.ehcache.pool.SizeOfEngine;
85  import net.sf.ehcache.pool.impl.BoundedPool;
86  import net.sf.ehcache.pool.impl.FromLargestCacheOnDiskPoolEvictor;
87  import net.sf.ehcache.pool.impl.FromLargestCacheOnHeapPoolEvictor;
88  import net.sf.ehcache.pool.impl.UnboundedPool;
89  import net.sf.ehcache.search.Attribute;
90  import net.sf.ehcache.search.Query;
91  import net.sf.ehcache.search.Results;
92  import net.sf.ehcache.search.SearchException;
93  import net.sf.ehcache.search.attribute.AttributeExtractor;
94  import net.sf.ehcache.statistics.CacheUsageListener;
95  import net.sf.ehcache.statistics.LiveCacheStatistics;
96  import net.sf.ehcache.statistics.LiveCacheStatisticsWrapper;
97  import net.sf.ehcache.statistics.sampled.SampledCacheStatistics;
98  import net.sf.ehcache.statistics.sampled.SampledCacheStatisticsWrapper;
99  import net.sf.ehcache.store.DiskBackedMemoryStore;
100 import net.sf.ehcache.store.ElementValueComparator;
101 import net.sf.ehcache.store.LegacyStoreWrapper;
102 import net.sf.ehcache.store.LruMemoryStore;
103 import net.sf.ehcache.store.MemoryOnlyStore;
104 import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
105 import net.sf.ehcache.store.Policy;
106 import net.sf.ehcache.store.Store;
107 import net.sf.ehcache.store.StoreListener;
108 import net.sf.ehcache.store.StoreQuery;
109 import net.sf.ehcache.store.TerracottaStore;
110 import net.sf.ehcache.store.compound.ImmutableValueElementCopyStrategy;
111 import net.sf.ehcache.store.compound.ReadWriteCopyStrategy;
112 import net.sf.ehcache.store.disk.StoreUpdateException;
113 import net.sf.ehcache.store.disk.DiskStore;
114 import net.sf.ehcache.terracotta.TerracottaClient;
115 import net.sf.ehcache.terracotta.TerracottaNotRunningException;
116 import net.sf.ehcache.transaction.SoftLockFactory;
117 import net.sf.ehcache.transaction.TransactionIDFactory;
118 import net.sf.ehcache.transaction.local.JtaLocalTransactionStore;
119 import net.sf.ehcache.transaction.local.LocalTransactionStore;
120 import net.sf.ehcache.transaction.manager.TransactionManagerLookup;
121 import net.sf.ehcache.transaction.xa.EhcacheXAResource;
122 import net.sf.ehcache.transaction.xa.EhcacheXAResourceImpl;
123 import net.sf.ehcache.transaction.xa.XATransactionStore;
124 import net.sf.ehcache.util.ClassLoaderUtil;
125 import net.sf.ehcache.util.NamedThreadFactory;
126 import net.sf.ehcache.util.PropertyUtil;
127 import net.sf.ehcache.util.TimeUtil;
128 import net.sf.ehcache.util.VmUtils;
129 import net.sf.ehcache.writer.CacheWriter;
130 import net.sf.ehcache.writer.CacheWriterFactory;
131 import net.sf.ehcache.writer.CacheWriterManager;
132 import net.sf.ehcache.writer.CacheWriterManagerException;
133 
134 import org.slf4j.Logger;
135 import org.slf4j.LoggerFactory;
136 
137 /***
138  * Cache is the central class in ehcache. Caches have {@link Element}s and are managed
139  * by the {@link CacheManager}. The Cache performs logical actions. It delegates physical
140  * implementations to its {@link net.sf.ehcache.store.Store}s.
141  * <p/>
142  * A reference to a Cache can be obtained through the {@link CacheManager}. A Cache thus obtained
143  * is guaranteed to have status {@link Status#STATUS_ALIVE}. This status is checked for any method which
144  * throws {@link IllegalStateException} and the same thrown if it is not alive. This would normally
145  * happen if a call is made after {@link CacheManager#shutdown} is invoked.
146  * <p/>
147  * Cache is threadsafe.
148  * <p/>
149  * Statistics on cache usage are collected and made available through the {@link #getStatistics()} methods.
150  * <p/>
151  * Various decorators are available for Cache, such as BlockingCache, SelfPopulatingCache and the dynamic proxy
152  * ExceptionHandlingDynamicCacheProxy. See each class for details.
153  *
154  * @author Greg Luck
155  * @author Geert Bevin
156  * @version $Id: Cache.html 13146 2011-08-01 17:12:39Z oletizi $
157  */
158 public class Cache implements Ehcache, StoreListener {
159 
160     /***
161      * A reserved word for cache names. It denotes a default configuration
162      * which is applied to caches created without configuration.
163      */
164     public static final String DEFAULT_CACHE_NAME = "default";
165 
166     /***
167      * System Property based method of disabling ehcache. If disabled no elements will be added to a cache.
168      * <p/>
169      * Set the property "net.sf.ehcache.disabled=true" to disable ehcache.
170      * <p/>
171      * This can easily be done using <code>java -Dnet.sf.ehcache.disabled=true</code> in the command line.
172      */
173     public static final String NET_SF_EHCACHE_DISABLED = "net.sf.ehcache.disabled";
174 
175     /***
176      * System Property based method of selecting the LruMemoryStore in use up to ehcache 1.5. This is provided
177      * for ease of migration.
178      * <p/>
179      * Set the property "net.sf.ehcache.use.classic.lru=true" to use the older LruMemoryStore implementation
180      * when LRU is selected as the eviction policy.
181      * <p/>
182      * This can easily be done using <code>java -Dnet.sf.ehcache.use.classic.lru=true</code> in the command line.
183      */
184     public static final String NET_SF_EHCACHE_USE_CLASSIC_LRU = "net.sf.ehcache.use.classic.lru";
185 
186     /***
187      * The default interval between runs of the expiry thread.
188      * @see CacheConfiguration#DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS CacheConfiguration#DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS for a preferred way of setting
189      */
190     public static final long DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS = CacheConfiguration.DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS;
191 
192     private static final String OFF_HEAP_STORE_CLASSNAME = "net.sf.ehcache.store.offheap.OffHeapStore";
193 
194     private static final Logger LOG = LoggerFactory.getLogger(Cache.class.getName());
195 
196     private static InetAddress localhost;
197 
198     /***
199      * The amount of time to wait if a store gets backed up
200      */
201     private static final int BACK_OFF_TIME_MILLIS = 50;
202 
203     private static final int EXECUTOR_KEEP_ALIVE_TIME = 60000;
204     private static final int EXECUTOR_MAXIMUM_POOL_SIZE = Math.min(10, Runtime.getRuntime().availableProcessors());
205     private static final int EXECUTOR_CORE_POOL_SIZE = 1;
206     private static final String EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY_PROP = "ehcache.clusteredStore.maxConcurrency";
207     private static final int DEFAULT_EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY = 4096;
208 
209     static {
210         try {
211             localhost = InetAddress.getLocalHost();
212         } catch (UnknownHostException e) {
213             LOG.error("Unable to set localhost. This prevents creation of a GUID. Cause was: " + e.getMessage(), e);
214         } catch (java.lang.NoClassDefFoundError e) {
215             LOG.debug("InetAddress is being blocked by your runtime environment. e.g. Google App Engine." +
216                     " Ehcache will work as a local cache.");
217         }
218     }
219 
220     private volatile boolean disabled = Boolean.getBoolean(NET_SF_EHCACHE_DISABLED);
221 
222     private final boolean useClassicLru = Boolean.getBoolean(NET_SF_EHCACHE_USE_CLASSIC_LRU);
223 
224     private volatile String diskStorePath;
225 
226     private volatile CacheStatus cacheStatus = new CacheStatus();
227 
228     private volatile CacheConfiguration configuration;
229 
230     /***
231      * The {@link import net.sf.ehcache.store.Store} of this {@link Cache}.
232      */
233     private volatile Store compoundStore;
234     private volatile CacheLockProvider lockProvider;
235 
236     private volatile RegisteredEventListeners registeredEventListeners;
237 
238     private volatile List<CacheExtension> registeredCacheExtensions;
239 
240     private volatile String guid;
241 
242     private volatile CacheManager cacheManager;
243 
244     private volatile BootstrapCacheLoader bootstrapCacheLoader;
245 
246     private volatile CacheExceptionHandler cacheExceptionHandler;
247 
248     private volatile List<CacheLoader> registeredCacheLoaders;
249 
250     private volatile CacheWriterManager cacheWriterManager;
251 
252     private final AtomicBoolean cacheWriterManagerInitFlag = new AtomicBoolean(false);
253 
254     private final ReentrantLock cacheWriterManagerInitLock = new ReentrantLock();
255 
256     private volatile CacheWriter registeredCacheWriter;
257 
258     /***
259      * A ThreadPoolExecutor which uses a thread pool to schedule loads in the order in which they are requested.
260      * <p/>
261      * Each cache has its own one of these, if required. Because the Core Thread Pool is zero, no threads
262      * are used until actually needed. Threads are added to the pool up to a maximum of 10. The keep alive
263      * time is 60 seconds, after which, if they are not required they will be stopped and collected.
264      * <p/>
265      * The executorService is only used for cache loading, and is created lazily on demand to avoid unnecessary resource
266      * usage.
267      * <p/>
268      * Use {@link #getExecutorService()} to ensure that it is initialised.
269      */
270     private volatile ExecutorService executorService;
271 
272     private volatile LiveCacheStatisticsWrapper liveCacheStatisticsData;
273 
274     private volatile SampledCacheStatisticsWrapper sampledCacheStatistics;
275 
276     private volatile TransactionManagerLookup transactionManagerLookup;
277 
278     private volatile boolean allowDisable = true;
279 
280     private volatile PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
281 
282     private volatile ElementValueComparator elementValueComparator;
283 
284     private volatile NonstopActiveDelegateHolderImpl nonstopActiveDelegateHolder;
285 
286     /***
287      * 2.0 and higher Constructor
288      * <p/>
289      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
290      * <p/>
291      * A client can specify their own settings here and pass the {@link Cache} object
292      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
293      * <p/>
294      * Only the CacheManager can initialise them.
295      *
296      * @param cacheConfiguration the configuration that should be used to create the cache with
297      */
298     public Cache(CacheConfiguration cacheConfiguration) {
299         this(cacheConfiguration, null, null);
300     }
301 
302     /***
303      * 2.0 and higher Constructor
304      * <p/>
305      * The {@link net.sf.ehcache.config.ConfigurationFactory}
306      * and clients can create these.
307      * <p/>
308      * A client can specify their own settings here and pass the {@link Cache}
309      * object into {@link CacheManager#addCache} to specify parameters other
310      * than the defaults.
311      * <p/>
312      * Only the CacheManager can initialise them.
313      *
314      * @param cacheConfiguration the configuration that should be used to create the cache with
315      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
316      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
317      */
318     public Cache(CacheConfiguration cacheConfiguration,
319                  RegisteredEventListeners registeredEventListeners,
320                  BootstrapCacheLoader bootstrapCacheLoader) {
321         cacheStatus.changeState(Status.STATUS_UNINITIALISED);
322 
323         this.configuration = cacheConfiguration.clone();
324         configuration.validateCompleteConfiguration();
325         elementValueComparator = cacheConfiguration.getElementValueComparatorConfiguration().getElementComparatorInstance();
326 
327         guid = createGuid();
328 
329         this.diskStorePath = cacheConfiguration.getDiskStorePath();
330 
331         if (registeredEventListeners == null) {
332             this.registeredEventListeners = new RegisteredEventListeners(this);
333         } else {
334             this.registeredEventListeners = registeredEventListeners;
335         }
336 
337         registeredCacheExtensions = new CopyOnWriteArrayList<CacheExtension>();
338         registeredCacheLoaders = new CopyOnWriteArrayList<CacheLoader>();
339 
340         // initialize statistics
341         liveCacheStatisticsData = new LiveCacheStatisticsWrapper(this);
342         sampledCacheStatistics = new SampledCacheStatisticsWrapper();
343 
344         RegisteredEventListeners listeners = getCacheEventNotificationService();
345         registerCacheListeners(configuration, listeners);
346         registerCacheExtensions(configuration, this);
347 
348         if (null == bootstrapCacheLoader) {
349             this.bootstrapCacheLoader = createBootstrapCacheLoader(configuration.getBootstrapCacheLoaderFactoryConfiguration());
350         } else {
351             this.bootstrapCacheLoader = bootstrapCacheLoader;
352         }
353         registerCacheLoaders(configuration, this);
354         registerCacheWriter(configuration, this);
355 
356         this.nonstopActiveDelegateHolder = new NonstopActiveDelegateHolderImpl(this);
357     }
358 
359 
360 
361     /***
362      * 1.0 Constructor.
363      * <p/>
364      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
365      * <p/>
366      * A client can specify their own settings here and pass the {@link Cache} object
367      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
368      * <p/>
369      * Only the CacheManager can initialise them.
370      * <p/>
371      * This constructor creates disk stores, if specified, that do not persist between restarts.
372      * <p/>
373      * The default expiry thread interval of 120 seconds is used. This is the interval between runs
374      * of the expiry thread, where it checks the disk store for expired elements. It is not the
375      * the timeToLiveSeconds.
376      *
377      * @param name                the name of the cache. Note that "default" is a reserved name for the defaultCache.
378      * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
379      * @param overflowToDisk      whether to use the disk store
380      * @param eternal             whether the elements in the cache are eternal, i.e. never expire
381      * @param timeToLiveSeconds   the default amount of time to live for an element from its creation date
382      * @param timeToIdleSeconds   the default amount of time to live for an element from its last accessed or modified date
383      * @since 1.0
384      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
385      * for full construction support of version 2.0 and higher features.
386      */
387     public Cache(String name, int maxElementsInMemory, boolean overflowToDisk,
388                  boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds) {
389 
390         this(new CacheConfiguration(name, maxElementsInMemory)
391                     .overflowToDisk(overflowToDisk)
392                     .eternal(eternal)
393                     .timeToLiveSeconds(timeToLiveSeconds)
394                     .timeToIdleSeconds(timeToIdleSeconds));
395     }
396 
397 
398     /***
399      * 1.1 Constructor.
400      * <p/>
401      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
402      * <p/>
403      * A client can specify their own settings here and pass the {@link Cache} object
404      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
405      * <p/>
406      * Only the CacheManager can initialise them.
407      *
408      * @param name                the name of the cache. Note that "default" is a reserved name for the defaultCache.
409      * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
410      * @param overflowToDisk      whether to use the disk store
411      * @param eternal             whether the elements in the cache are eternal, i.e. never expire
412      * @param timeToLiveSeconds   the default amount of time to live for an element from its creation date
413      * @param timeToIdleSeconds   the default amount of time to live for an element from its last accessed or modified date
414      * @param diskPersistent      whether to persist the cache to disk between JVM restarts
415      * @param diskExpiryThreadIntervalSeconds
416      *                            how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
417      * @since 1.1
418      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
419      * for full construction support of version 2.0 and higher features.
420      */
421     public Cache(String name,
422                  int maxElementsInMemory,
423                  boolean overflowToDisk,
424                  boolean eternal,
425                  long timeToLiveSeconds,
426                  long timeToIdleSeconds,
427                  boolean diskPersistent,
428                  long diskExpiryThreadIntervalSeconds) {
429 
430         this(new CacheConfiguration(name, maxElementsInMemory)
431                     .overflowToDisk(overflowToDisk)
432                     .eternal(eternal)
433                     .timeToLiveSeconds(timeToLiveSeconds)
434                     .timeToIdleSeconds(timeToIdleSeconds)
435                     .diskPersistent(diskPersistent)
436                     .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds));
437 
438         LOG.warn("An API change between ehcache-1.1 and ehcache-1.2 results in the persistence path being set to " +
439                 DiskStoreConfiguration.getDefaultPath() + " when the ehcache-1.1 constructor is used. " +
440                 "Please change to the 1.2 constructor.");
441     }
442 
443 
444     /***
445      * 1.2 Constructor
446      * <p/>
447      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
448      * <p/>
449      * A client can specify their own settings here and pass the {@link Cache} object
450      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
451      * <p/>
452      * Only the CacheManager can initialise them.
453      *
454      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
455      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted (0 == no limit)
456      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
457      * @param overflowToDisk            whether to use the disk store
458      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
459      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
460      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
461      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
462      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
463      * @param diskExpiryThreadIntervalSeconds
464      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
465      * @param registeredEventListeners  a notification service. Optionally null, in which case a new
466      *                                  one with no registered listeners will be created.
467      * @since 1.2
468      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
469      * for full construction support of version 2.0 and higher features.
470      */
471     public Cache(String name,
472                  int maxElementsInMemory,
473                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
474                  boolean overflowToDisk,
475                  String diskStorePath,
476                  boolean eternal,
477                  long timeToLiveSeconds,
478                  long timeToIdleSeconds,
479                  boolean diskPersistent,
480                  long diskExpiryThreadIntervalSeconds,
481                  RegisteredEventListeners registeredEventListeners) {
482 
483         this(new CacheConfiguration(name, maxElementsInMemory)
484                     .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
485                     .overflowToDisk(overflowToDisk)
486                     .diskStorePath(diskStorePath)
487                     .eternal(eternal)
488                     .timeToLiveSeconds(timeToLiveSeconds)
489                     .timeToIdleSeconds(timeToIdleSeconds)
490                     .diskPersistent(diskPersistent)
491                     .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds),
492                 registeredEventListeners,
493                 null);
494 
495     }
496 
497     /***
498      * 1.2.1 Constructor
499      * <p/>
500      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
501      * <p/>
502      * A client can specify their own settings here and pass the {@link Cache} object
503      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
504      * <p/>
505      * Only the CacheManager can initialise them.
506      *
507      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
508      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted (0 == no limit)
509      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
510      * @param overflowToDisk            whether to use the disk store
511      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
512      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
513      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
514      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
515      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
516      * @param diskExpiryThreadIntervalSeconds
517      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
518      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
519      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
520      * @since 1.2.1
521      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
522      * for full construction support of version 2.0 and higher features.
523      */
524     public Cache(String name,
525                  int maxElementsInMemory,
526                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
527                  boolean overflowToDisk,
528                  String diskStorePath,
529                  boolean eternal,
530                  long timeToLiveSeconds,
531                  long timeToIdleSeconds,
532                  boolean diskPersistent,
533                  long diskExpiryThreadIntervalSeconds,
534                  RegisteredEventListeners registeredEventListeners,
535                  BootstrapCacheLoader bootstrapCacheLoader) {
536 
537         this(new CacheConfiguration(name, maxElementsInMemory)
538                     .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
539                     .overflowToDisk(overflowToDisk)
540                     .diskStorePath(diskStorePath)
541                     .eternal(eternal)
542                     .timeToLiveSeconds(timeToLiveSeconds)
543                     .timeToIdleSeconds(timeToIdleSeconds)
544                     .diskPersistent(diskPersistent)
545                     .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds),
546                 registeredEventListeners,
547                 bootstrapCacheLoader);
548     }
549 
550     /***
551      * 1.2.4 Constructor
552      * <p/>
553      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
554      * <p/>
555      * A client can specify their own settings here and pass the {@link Cache} object
556      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
557      * <p/>
558      * Only the CacheManager can initialise them.
559      *
560      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
561      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted (0 == no limit)
562      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
563      * @param overflowToDisk            whether to use the disk store
564      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
565      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
566      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
567      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
568      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
569      * @param diskExpiryThreadIntervalSeconds
570      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
571      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
572      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
573      * @param maxElementsOnDisk         the maximum number of Elements to allow on the disk. 0 means unlimited.
574      * @since 1.2.4
575      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
576      * for full construction support of version 2.0 and higher features.
577      */
578     public Cache(String name,
579                  int maxElementsInMemory,
580                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
581                  boolean overflowToDisk,
582                  String diskStorePath,
583                  boolean eternal,
584                  long timeToLiveSeconds,
585                  long timeToIdleSeconds,
586                  boolean diskPersistent,
587                  long diskExpiryThreadIntervalSeconds,
588                  RegisteredEventListeners registeredEventListeners,
589                  BootstrapCacheLoader bootstrapCacheLoader,
590                  int maxElementsOnDisk) {
591 
592         this(new CacheConfiguration(name, maxElementsInMemory)
593                     .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
594                     .overflowToDisk(overflowToDisk)
595                     .diskStorePath(diskStorePath)
596                     .eternal(eternal)
597                     .timeToLiveSeconds(timeToLiveSeconds)
598                     .timeToIdleSeconds(timeToIdleSeconds)
599                     .diskPersistent(diskPersistent)
600                     .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
601                     .maxElementsOnDisk(maxElementsOnDisk),
602                 registeredEventListeners,
603                 bootstrapCacheLoader);
604     }
605 
606     /***
607      * 1.3 Constructor
608      * <p/>
609      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
610      * <p/>
611      * A client can specify their own settings here and pass the {@link Cache} object
612      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
613      * <p/>
614      * Only the CacheManager can initialise them.
615      *
616      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
617      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted (0 == no limit)
618      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
619      * @param overflowToDisk            whether to use the disk store
620      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
621      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
622      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
623      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
624      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
625      * @param diskExpiryThreadIntervalSeconds
626      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
627      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
628      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
629      * @param maxElementsOnDisk         the maximum number of Elements to allow on the disk. 0 means unlimited.
630      * @param diskSpoolBufferSizeMB     the amount of memory to allocate the write buffer for puts to the DiskStore.
631      * @since 1.3
632      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
633      * for full construction support of version 2.0 and higher features.
634      */
635     public Cache(String name,
636                  int maxElementsInMemory,
637                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
638                  boolean overflowToDisk,
639                  String diskStorePath,
640                  boolean eternal,
641                  long timeToLiveSeconds,
642                  long timeToIdleSeconds,
643                  boolean diskPersistent,
644                  long diskExpiryThreadIntervalSeconds,
645                  RegisteredEventListeners registeredEventListeners,
646                  BootstrapCacheLoader bootstrapCacheLoader,
647                  int maxElementsOnDisk,
648                  int diskSpoolBufferSizeMB) {
649 
650         this(new CacheConfiguration(name, maxElementsInMemory)
651                     .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
652                     .overflowToDisk(overflowToDisk)
653                     .diskStorePath(diskStorePath)
654                     .eternal(eternal)
655                     .timeToLiveSeconds(timeToLiveSeconds)
656                     .timeToIdleSeconds(timeToIdleSeconds)
657                     .diskPersistent(diskPersistent)
658                     .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
659                     .maxElementsOnDisk(maxElementsOnDisk)
660                     .diskSpoolBufferSizeMB(diskSpoolBufferSizeMB),
661                 registeredEventListeners,
662                 bootstrapCacheLoader);
663     }
664 
665     /***
666      * 1.6.0 Constructor
667      * <p/>
668      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
669      * <p/>
670      * A client can specify their own settings here and pass the {@link Cache} object
671      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
672      * <p/>
673      * Only the CacheManager can initialise them.
674      *
675      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
676      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted (0 == no limit)
677      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
678      * @param overflowToDisk            whether to use the disk store
679      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
680      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
681      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
682      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
683      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
684      * @param diskExpiryThreadIntervalSeconds
685      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
686      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
687      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
688      * @param maxElementsOnDisk         the maximum number of Elements to allow on the disk. 0 means unlimited.
689      * @param diskSpoolBufferSizeMB     the amount of memory to allocate the write buffer for puts to the DiskStore.
690      * @param clearOnFlush              whether the in-memory storage should be cleared when {@link #flush flush()} is called on the cache
691      * @since 1.6.0
692      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
693      * for full construction support of version 2.0 and higher features.
694      */
695     public Cache(String name,
696                  int maxElementsInMemory,
697                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
698                  boolean overflowToDisk,
699                  String diskStorePath,
700                  boolean eternal,
701                  long timeToLiveSeconds,
702                  long timeToIdleSeconds,
703                  boolean diskPersistent,
704                  long diskExpiryThreadIntervalSeconds,
705                  RegisteredEventListeners registeredEventListeners,
706                  BootstrapCacheLoader bootstrapCacheLoader,
707                  int maxElementsOnDisk,
708                  int diskSpoolBufferSizeMB,
709                  boolean clearOnFlush) {
710 
711         this(new CacheConfiguration(name, maxElementsInMemory)
712                     .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
713                     .overflowToDisk(overflowToDisk)
714                     .diskStorePath(diskStorePath)
715                     .eternal(eternal)
716                     .timeToLiveSeconds(timeToLiveSeconds)
717                     .timeToIdleSeconds(timeToIdleSeconds)
718                     .diskPersistent(diskPersistent)
719                     .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
720                     .maxElementsOnDisk(maxElementsOnDisk)
721                     .diskSpoolBufferSizeMB(diskSpoolBufferSizeMB)
722                     .clearOnFlush(clearOnFlush),
723                 registeredEventListeners,
724                 bootstrapCacheLoader);
725     }
726 
727     /***
728      * 1.7.0 Constructor
729      * <p/>
730      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
731      * <p/>
732      * A client can specify their own settings here and pass the {@link Cache} object
733      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
734      * <p/>
735      * Only the CacheManager can initialise them.
736      *
737      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
738      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted (0 == no limit)
739      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
740      * @param overflowToDisk            whether to use the disk store
741      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
742      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
743      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
744      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
745      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
746      * @param diskExpiryThreadIntervalSeconds
747      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
748      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
749      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
750      * @param maxElementsOnDisk         the maximum number of Elements to allow on the disk. 0 means unlimited.
751      * @param diskSpoolBufferSizeMB     the amount of memory to allocate the write buffer for puts to the DiskStore.
752      * @param clearOnFlush              whether the in-memory storage should be cleared when {@link #flush flush()} is called on the cache
753      * @param isTerracottaClustered     whether to cluster this cache with Terracotta
754      * @param terracottaValueMode       either "SERIALIZATION" or "IDENTITY" mode, only used if isTerracottaClustered=true
755      * @param terracottaCoherentReads   whether this cache should use coherent reads (usually should be true) unless optimizing for read-only
756      * @since 1.7.0
757      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
758      * for full construction support of version 2.0 and higher features.
759      */
760     public Cache(String name, int maxElementsInMemory, MemoryStoreEvictionPolicy memoryStoreEvictionPolicy, boolean overflowToDisk,
761                  String diskStorePath, boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds, boolean diskPersistent,
762                  long diskExpiryThreadIntervalSeconds, RegisteredEventListeners registeredEventListeners,
763                  BootstrapCacheLoader bootstrapCacheLoader, int maxElementsOnDisk, int diskSpoolBufferSizeMB, boolean clearOnFlush,
764                  boolean isTerracottaClustered, String terracottaValueMode, boolean terracottaCoherentReads) {
765 
766         this(new CacheConfiguration(name, maxElementsInMemory)
767                     .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
768                     .overflowToDisk(overflowToDisk)
769                     .diskStorePath(diskStorePath)
770                     .eternal(eternal)
771                     .timeToLiveSeconds(timeToLiveSeconds)
772                     .timeToIdleSeconds(timeToIdleSeconds)
773                     .diskPersistent(diskPersistent)
774                     .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
775                     .maxElementsOnDisk(maxElementsOnDisk)
776                     .diskSpoolBufferSizeMB(diskSpoolBufferSizeMB)
777                     .clearOnFlush(clearOnFlush)
778                     .terracotta(new TerracottaConfiguration()
779                         .clustered(isTerracottaClustered)
780                         .valueMode(terracottaValueMode)
781                         .coherentReads(terracottaCoherentReads)),
782                 registeredEventListeners,
783                 bootstrapCacheLoader);
784     }
785 
786 
787     /***
788      * A factory method to create a RegisteredEventListeners
789      */
790     private static void registerCacheListeners(CacheConfiguration cacheConfiguration,
791                                                  RegisteredEventListeners registeredEventListeners) {
792         List cacheEventListenerConfigurations = cacheConfiguration.getCacheEventListenerConfigurations();
793         for (Object cacheEventListenerConfiguration : cacheEventListenerConfigurations) {
794             CacheConfiguration.CacheEventListenerFactoryConfiguration factoryConfiguration =
795                     (CacheConfiguration.CacheEventListenerFactoryConfiguration) cacheEventListenerConfiguration;
796             CacheEventListener cacheEventListener = createCacheEventListener(factoryConfiguration);
797             registeredEventListeners.registerListener(cacheEventListener, factoryConfiguration.getListenFor());
798         }
799     }
800 
801     /***
802      * A factory method to register cache extensions
803      *
804      * @param cacheConfiguration the cache configuration
805      * @param cache              the cache
806      */
807     private static void registerCacheExtensions(CacheConfiguration cacheConfiguration, Ehcache cache) {
808         List cacheExtensionConfigurations = cacheConfiguration.getCacheExtensionConfigurations();
809         for (Object cacheExtensionConfiguration : cacheExtensionConfigurations) {
810             CacheConfiguration.CacheExtensionFactoryConfiguration factoryConfiguration =
811                     (CacheConfiguration.CacheExtensionFactoryConfiguration) cacheExtensionConfiguration;
812             CacheExtension cacheExtension = createCacheExtension(factoryConfiguration, cache);
813             cache.registerCacheExtension(cacheExtension);
814         }
815     }
816 
817     /***
818      * A factory method to register cache Loaders
819      *
820      * @param cacheConfiguration the cache configuration
821      * @param cache              the cache
822      */
823     private static void registerCacheLoaders(CacheConfiguration cacheConfiguration, Ehcache cache) {
824         List cacheLoaderConfigurations = cacheConfiguration.getCacheLoaderConfigurations();
825         for (Object cacheLoaderConfiguration : cacheLoaderConfigurations) {
826             CacheConfiguration.CacheLoaderFactoryConfiguration factoryConfiguration =
827                     (CacheConfiguration.CacheLoaderFactoryConfiguration) cacheLoaderConfiguration;
828             CacheLoader cacheLoader = createCacheLoader(factoryConfiguration, cache);
829             cache.registerCacheLoader(cacheLoader);
830         }
831     }
832 
833     /***
834      * A factory method to register cache writers
835      *
836      * @param cacheConfiguration the cache configuration
837      * @param cache              the cache
838      */
839     private static void registerCacheWriter(CacheConfiguration cacheConfiguration, Ehcache cache) {
840         CacheWriterConfiguration config = cacheConfiguration.getCacheWriterConfiguration();
841         if (config != null) {
842             CacheWriter cacheWriter = createCacheWriter(config, cache);
843             cache.registerCacheWriter(cacheWriter);
844         }
845     }
846 
847 
848     /***
849      * Tries to load the class specified otherwise defaults to null.
850      *
851      * @param factoryConfiguration
852      */
853     private static CacheEventListener createCacheEventListener(
854             CacheConfiguration.CacheEventListenerFactoryConfiguration factoryConfiguration) {
855         String className = null;
856         CacheEventListener cacheEventListener = null;
857         if (factoryConfiguration != null) {
858             className = factoryConfiguration.getFullyQualifiedClassPath();
859         }
860         if (className == null) {
861             LOG.debug("CacheEventListener factory not configured. Skipping...");
862         } else {
863             CacheEventListenerFactory factory = (CacheEventListenerFactory)
864                     ClassLoaderUtil.createNewInstance(className);
865             Properties properties =
866 
867                     PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
868                             factoryConfiguration.getPropertySeparator());
869             cacheEventListener =
870                     factory.createCacheEventListener(properties);
871         }
872         return cacheEventListener;
873     }
874 
875     /***
876      * Tries to load the class specified otherwise defaults to null.
877      *
878      * @param factoryConfiguration
879      */
880     private static CacheExtension createCacheExtension(
881             CacheConfiguration.CacheExtensionFactoryConfiguration factoryConfiguration, Ehcache cache) {
882         String className = null;
883         CacheExtension cacheExtension = null;
884         if (factoryConfiguration != null) {
885             className = factoryConfiguration.getFullyQualifiedClassPath();
886         }
887         if (className == null) {
888             LOG.debug("CacheExtension factory not configured. Skipping...");
889         } else {
890             CacheExtensionFactory factory = (CacheExtensionFactory) ClassLoaderUtil.createNewInstance(className);
891             Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
892                     factoryConfiguration.getPropertySeparator());
893             cacheExtension = factory.createCacheExtension(cache, properties);
894         }
895         return cacheExtension;
896     }
897 
898     /***
899      * Tries to load the class specified otherwise defaults to null.
900      *
901      * @param factoryConfiguration
902      */
903     private static CacheLoader createCacheLoader(
904             CacheConfiguration.CacheLoaderFactoryConfiguration factoryConfiguration, Ehcache cache) {
905         String className = null;
906         CacheLoader cacheLoader = null;
907         if (factoryConfiguration != null) {
908             className = factoryConfiguration.getFullyQualifiedClassPath();
909         }
910         if (className == null) {
911             LOG.debug("CacheLoader factory not configured. Skipping...");
912         } else {
913             CacheLoaderFactory factory = (CacheLoaderFactory) ClassLoaderUtil.createNewInstance(className);
914             Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
915                     factoryConfiguration.getPropertySeparator());
916             cacheLoader = factory.createCacheLoader(cache, properties);
917         }
918         return cacheLoader;
919     }
920 
921     /***
922      * Tries to load the class specified otherwise defaults to null.
923      *
924      * @param config
925      */
926     private static CacheWriter createCacheWriter(CacheWriterConfiguration config, Ehcache cache) {
927         String className = null;
928         CacheWriter cacheWriter = null;
929         CacheWriterConfiguration.CacheWriterFactoryConfiguration factoryConfiguration = config.getCacheWriterFactoryConfiguration();
930         if (factoryConfiguration != null) {
931             className = factoryConfiguration.getFullyQualifiedClassPath();
932         }
933         if (null == className) {
934             LOG.debug("CacheWriter factory not configured. Skipping...");
935         } else {
936             CacheWriterFactory factory = (CacheWriterFactory) ClassLoaderUtil.createNewInstance(className);
937             Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
938                     factoryConfiguration.getPropertySeparator());
939             if (null == properties) {
940                 properties = new Properties();
941             }
942             cacheWriter = factory.createCacheWriter(cache, properties);
943         }
944         return cacheWriter;
945     }
946 
947     /***
948      * Tries to load a BootstrapCacheLoader from the class specified.
949      *
950      * @return If there is none returns null.
951      */
952     private static final BootstrapCacheLoader createBootstrapCacheLoader(
953             CacheConfiguration.BootstrapCacheLoaderFactoryConfiguration factoryConfiguration) throws CacheException {
954         String className = null;
955         BootstrapCacheLoader bootstrapCacheLoader = null;
956         if (factoryConfiguration != null) {
957             className = factoryConfiguration.getFullyQualifiedClassPath();
958         }
959         if (className == null || className.length() == 0) {
960             LOG.debug("No BootstrapCacheLoaderFactory class specified. Skipping...");
961         } else {
962             BootstrapCacheLoaderFactory factory = (BootstrapCacheLoaderFactory)
963                     ClassLoaderUtil.createNewInstance(className);
964             Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
965                     factoryConfiguration.getPropertySeparator());
966             return factory.createBootstrapCacheLoader(properties);
967         }
968         return bootstrapCacheLoader;
969     }
970 
971     /***
972      * Get the TransactionManagerLookup implementation used to lookup the TransactionManager.
973      * This is generally only set for XA transactional caches
974      * @return The {@link net.sf.ehcache.transaction.manager.TransactionManagerLookup} instance
975      */
976     public TransactionManagerLookup getTransactionManagerLookup() {
977        return transactionManagerLookup;
978     }
979 
980     /***
981      * Sets the TransactionManagerLookup that needs to be used for this cache to lookup the TransactionManager
982      * This needs to be set before {@link Cache#initialise()} is called
983      * @param lookup The {@link net.sf.ehcache.transaction.manager.TransactionManagerLookup} instance
984      */
985     public void setTransactionManagerLookup(TransactionManagerLookup lookup) {
986         TransactionManagerLookup oldValue = getTransactionManagerLookup();
987         this.transactionManagerLookup = lookup;
988         firePropertyChange("TransactionManagerLookup", oldValue, lookup);
989     }
990 
991     /***
992      * Newly created caches do not have a {@link net.sf.ehcache.store.Store}.
993      * <p/>
994      * This method creates the store and makes the cache ready to accept elements
995      */
996     public void initialise() {
997         synchronized (this) {
998             if (!cacheStatus.canInitialize()) {
999                 throw new IllegalStateException("Cannot initialise the " + configuration.getName()
1000                         + " cache because its status is not STATUS_UNINITIALISED");
1001             }
1002             // on-heap validation
1003             // todo is this really to be supported ? CacheManager less Caches ? Can these then be attached later ?
1004             if (getCacheManager() != null
1005                 && getCacheManager().getConfiguration().isMaxBytesLocalHeapSet()
1006                 && configuration.getMaxElementsInMemory() > 0) {
1007                 throw new InvalidConfigurationException(configuration.getName() +
1008                                                         ": MaxElementsInMemory is not compatible with " +
1009                                                         "MaxBytesOnHeap set on cache manager");
1010             }
1011             if (configuration.getMaxBytesLocalHeap() > 0 && configuration.getMaxElementsInMemory() > 0) {
1012                 throw new InvalidConfigurationException(configuration.getName() +
1013                                                         ": MaxElementsInMemory is not compatible with " +
1014                                                         "MaxBytesOnHeap set on cache");
1015             }
1016 
1017             // on-heap pool configuration
1018             Pool onHeapPool;
1019             if (configuration.getMaxBytesLocalHeap() > 0) {
1020                 PoolEvictor<PoolableStore> evictor = new FromLargestCacheOnHeapPoolEvictor();
1021                 SizeOfEngine sizeOfEngine = cacheManager.createSizeOfEngine(this);
1022                 onHeapPool = new BoundedPool(configuration.getMaxBytesLocalHeap(), evictor, sizeOfEngine);
1023             } else if (getCacheManager() != null && getCacheManager().getConfiguration().isMaxBytesLocalHeapSet()) {
1024                 onHeapPool = getCacheManager().getOnHeapPool();
1025             } else {
1026                 onHeapPool = new UnboundedPool();
1027                 if (configuration.getMaxElementsInMemory() == 0) {
1028                     LOG.warn("Cache: " + configuration.getName() +
1029                             " has a maxElementsInMemory of 0.  " +
1030                             "In Ehcache 2.0 this has been changed to mean a store" +
1031                             " with no capacity limit. Set it to 1 if you want" +
1032                             " no elements cached in memory");
1033                 }
1034             }
1035 
1036             // on-disk pool configuration
1037             Pool onDiskPool;
1038             if (configuration.getMaxBytesLocalDisk() > 0) {
1039                 PoolEvictor<PoolableStore> evictor = new FromLargestCacheOnDiskPoolEvictor();
1040                 onDiskPool = new BoundedPool(configuration.getMaxBytesLocalDisk(), evictor, null);
1041             } else if (getCacheManager() != null && getCacheManager().getConfiguration().isMaxBytesLocalDiskSet()) {
1042                 onDiskPool = getCacheManager().getOnDiskPool();
1043             } else {
1044                 onDiskPool = new UnboundedPool();
1045             }
1046 
1047 
1048 
1049             ReadWriteCopyStrategy<Element> copyStrategy = null;
1050             if (configuration.getTransactionalMode().isTransactional()) {
1051                 configuration.getCopyStrategyConfiguration().setCopyStrategyInstance(null);
1052                 copyStrategy = configuration.getCopyStrategyConfiguration().getCopyStrategyInstance();
1053                 configuration.getCopyStrategyConfiguration().setCopyStrategyInstance(new ImmutableValueElementCopyStrategy());
1054             }
1055             if (configuration.getTransactionalMode().isTransactional()
1056                 && configuration.isTerracottaClustered()
1057                 && configuration.getTerracottaConfiguration().getValueMode() != TerracottaConfiguration.ValueMode.SERIALIZATION) {
1058                 throw new CacheException("To be transactional, a Terracotta clustered cache needs to be in Serialization value mode");
1059             }
1060             final Store store;
1061             if (isTerracottaClustered()) {
1062                 checkClusteredConfig();
1063                 int maxConcurrency = Integer.getInteger(EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY_PROP,
1064                         DEFAULT_EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY);
1065                 if (getCacheConfiguration().getTerracottaConfiguration().getConcurrency() > maxConcurrency) {
1066                     throw new InvalidConfigurationException("Maximum supported concurrency for Terracotta clustered caches is "
1067                             + maxConcurrency + ". Please reconfigure cache '" + getName() + "' with concurrency value <= " + maxConcurrency
1068                             + " or use system property '" + EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY_PROP + "' to override the default");
1069                 }
1070                 if (getCacheConfiguration().getMaxElementsOnDisk() == 0) {
1071                     LOG.warn("Performance may degrade and server disks could run out of space!\nThe distributed cache {} does not have " +
1072                              "maxElementsOnDisk set. Failing to set maxElementsOnDisk could mean no eviction of its elements from the " +
1073                              "Terracotta Server Array disk store. To avoid this, set maxElementsOnDisk to a non-zero value.", getName());
1074                 }
1075                 if (!getCacheConfiguration().getTerracottaConfiguration().isStorageStrategySet()) {
1076                     getCacheConfiguration().getTerracottaConfiguration().storageStrategy(
1077                             TerracottaClient.getTerracottaDefaultStrategyForCurrentRuntime(getCacheConfiguration()));
1078                 }
1079                 Store tempStore = cacheManager.createTerracottaStore(this);
1080                 if (!(tempStore instanceof TerracottaStore)) {
1081                     throw new CacheException(
1082                             "CacheManager should create instances of TerracottaStore for Terracotta Clustered caches instead of - "
1083                                     + (tempStore == null ? "null" : tempStore.getClass().getName()));
1084                 }
1085                 TerracottaStore terracottaStore = (TerracottaStore) makeXaStrictTransactionalIfNeeded(tempStore, copyStrategy);
1086 
1087                 NonstopConfiguration nonstopConfig = getCacheConfiguration().getTerracottaConfiguration().getNonstopConfiguration();
1088                 // freeze the config whether nonstop is enabled or not
1089                 if (nonstopConfig != null) {
1090                     nonstopConfig.freezeConfig();
1091                 }
1092                 if (getCacheConfiguration().getTerracottaConfiguration().isNonstopEnabled()) {
1093                     nonstopActiveDelegateHolder.terracottaStoreInitialized(terracottaStore);
1094                     store = nonstopActiveDelegateHolder.getNonstopStore();
1095                 } else {
1096                     store = terracottaStore;
1097                 }
1098             } else if (getCacheConfiguration().isOverflowToOffHeap()) {
1099                 store = createOffHeapStore(onHeapPool, onDiskPool, copyStrategy);
1100             } else {
1101                 if (useClassicLru && configuration.getMemoryStoreEvictionPolicy().equals(MemoryStoreEvictionPolicy.LRU)) {
1102                     Store disk = createDiskStore();
1103                     store = makeXaStrictTransactionalIfNeeded(new LegacyStoreWrapper(
1104                             new LruMemoryStore(this, disk), disk, registeredEventListeners, configuration), copyStrategy);
1105                 } else {
1106                     if (configuration.isDiskPersistent() || configuration.isOverflowToDisk()) {
1107                         store = makeXaStrictTransactionalIfNeeded(DiskBackedMemoryStore.create(this, diskStorePath,
1108                                 onHeapPool, onDiskPool), copyStrategy);
1109                     } else {
1110                         store = makeXaStrictTransactionalIfNeeded(MemoryOnlyStore.create(this, onHeapPool), copyStrategy);
1111                     }
1112                 }
1113             }
1114             /* note: this isn't part of makeXaStrictTransactionalIfNeeded() as only xa_strict supports NonStop, meaning that only
1115              * that transactional store can be wrapped by NonStopStore. Other TX modes have to wrap the NonStop store due to their
1116              * lack of NonStop support (ie: lack of transaction context suspension/resuming).
1117              */
1118             if (configuration.isXaTransactional()) {
1119                 SoftLockFactory softLockFactory = cacheManager.createSoftLockFactory(this);
1120                 LocalTransactionStore localTransactionStore = new LocalTransactionStore(getCacheManager().getTransactionController(),
1121                         softLockFactory, this, store, copyStrategy);
1122                 this.compoundStore = new JtaLocalTransactionStore(localTransactionStore, transactionManagerLookup,
1123                         cacheManager.getTransactionController());
1124             } else if (configuration.isLocalTransactional()) {
1125                 SoftLockFactory softLockFactory = cacheManager.createSoftLockFactory(this);
1126                 this.compoundStore = new LocalTransactionStore(getCacheManager().getTransactionController(), softLockFactory, this, store,
1127                         copyStrategy);
1128             } else {
1129                 this.compoundStore = store;
1130             }
1131             Map<String, AttributeExtractor> extractors = new HashMap<String, AttributeExtractor>();
1132             for (SearchAttribute sa : configuration.getSearchAttributes().values()) {
1133                 extractors.put(sa.getName(), sa.constructExtractor());
1134             }
1135             compoundStore.setAttributeExtractors(extractors);
1136             this.cacheWriterManager = configuration.getCacheWriterConfiguration().getWriteMode().createWriterManager(this);
1137             initialiseCacheWriterManager(false);
1138             cacheStatus.changeState(Status.STATUS_ALIVE);
1139             initialiseRegisteredCacheExtensions();
1140             initialiseRegisteredCacheLoaders();
1141             initialiseRegisteredCacheWriter();
1142             // initialize live statistics
1143             // register to get notifications of
1144             // put/update/removeInternal/expiry/eviction
1145             getCacheEventNotificationService().registerListener(liveCacheStatisticsData);
1146             // set up default values
1147             liveCacheStatisticsData.setStatisticsAccuracy(Statistics.STATISTICS_ACCURACY_BEST_EFFORT);
1148             liveCacheStatisticsData.setStatisticsEnabled(configuration.getStatistics());
1149 
1150             // register the sampled cache statistics
1151             this.registerCacheUsageListener(sampledCacheStatistics);
1152 
1153             if (isTerracottaClustered()) {
1154                 // create this to be sure that it's present on each node to receive clustered events,
1155                 // even if this node is not sending out its events
1156                 cacheManager.createTerracottaEventReplicator(this);
1157             }
1158 
1159             Object context = compoundStore.getInternalContext();
1160             if (context instanceof CacheLockProvider) {
1161                 lockProvider = (CacheLockProvider) context;
1162             } else {
1163                 this.lockProvider = new StripedReadWriteLockSync(StripedReadWriteLockSync.DEFAULT_NUMBER_OF_MUTEXES);
1164             }
1165         }
1166 
1167         compoundStore.addStoreListener(this);
1168 
1169         if (LOG.isDebugEnabled()) {
1170             LOG.debug("Initialised cache: " + configuration.getName());
1171         }
1172 
1173         if (disabled) {
1174             LOG.warn("Cache: " + configuration.getName() + " is disabled because the " + NET_SF_EHCACHE_DISABLED
1175                     + " property was set to true. No elements will be added to the cache.");
1176         }
1177     }
1178 
1179     private Store createOffHeapStore(Pool onHeapPool, Pool onDiskPool, ReadWriteCopyStrategy<Element> copyStrategy) {
1180         try {
1181             Class<Store> offHeapStoreClass = ClassLoaderUtil.loadClass(OFF_HEAP_STORE_CLASSNAME);
1182 
1183             try {
1184                 return makeXaStrictTransactionalIfNeeded((Store) offHeapStoreClass.getMethod("create",
1185                         Ehcache.class, String.class, Pool.class, Pool.class)
1186                         .invoke(null, this, diskStorePath, onHeapPool, onDiskPool), copyStrategy);
1187             } catch (NoSuchMethodException e) {
1188                 throw new CacheException("Cache: " + configuration.getName() + " cannot find static factory"
1189                         + " method create(Ehcache, String)" + " in store class " + OFF_HEAP_STORE_CLASSNAME, e);
1190             } catch (InvocationTargetException e) {
1191                 Throwable cause = e.getCause();
1192                 if (cause instanceof CacheException) {
1193                     throw (CacheException) cause;
1194                 } else {
1195                     throw new CacheException("Cache: " + configuration.getName() + " cannot instantiate store "
1196                             + OFF_HEAP_STORE_CLASSNAME, cause);
1197                 }
1198             } catch (IllegalAccessException e) {
1199                 throw new CacheException("Cache: " + configuration.getName() + " cannot instantiate store "
1200                         + OFF_HEAP_STORE_CLASSNAME, e);
1201             }
1202         } catch (ClassNotFoundException e) {
1203             throw new CacheException("Cache " + configuration.getName()
1204                     + " cannot be configured because the off-heap store class could not be found. "
1205                     + "You must use an enterprise version of Ehcache to successfully enable overflowToOffHeap.");
1206         }
1207     }
1208 
1209     private void checkClusteredConfig() {
1210         final Consistency consistency = getCacheConfiguration().getTerracottaConfiguration().getConsistency();
1211         final boolean coherent = getCacheConfiguration().getTerracottaConfiguration().isCoherent();
1212         if (getCacheConfiguration().isOverflowToOffHeap()) {
1213             throw new InvalidConfigurationException(
1214                     "Terracotta clustered caches with local overflow to offheap are not supported yet."
1215                             + " You can fix this by disabling overflow to offheap for clustered caches.");
1216         }
1217         if (getCacheConfiguration().getTerracottaConfiguration().isSynchronousWrites() && consistency == Consistency.EVENTUAL) {
1218             throw new InvalidConfigurationException(
1219                     "Terracotta clustered caches with eventual consistency and synchronous writes are not supported yet."
1220                             + " You can fix this by either making the cache in 'strong' consistency mode "
1221                             + "(<terracotta consistency=\"strong\"/>) or turning off synchronous writes.");
1222         }
1223         if (getCacheConfiguration().getTransactionalMode().isTransactional() && consistency == Consistency.EVENTUAL) {
1224             throw new InvalidConfigurationException("Consistency should be " + Consistency.STRONG
1225                     + " when cache is configured with transactions enabled. "
1226                     + "You can fix this by either making the cache in 'strong' consistency mode "
1227                     + "(<terracotta consistency=\"strong\"/>) or turning off transactions.");
1228         }
1229         if (getCacheConfiguration().getTransactionalMode().isTransactional()
1230                 && !getCacheConfiguration().getTransactionalMode().equals(CacheConfiguration.TransactionalMode.XA_STRICT)
1231                 && getCacheConfiguration().getTerracottaConfiguration().isNonstopEnabled()) {
1232             LOG.warn("Cache: " + configuration.getName() + " configured both NonStop and transactional non xa_strict."
1233                     + " NonStop features won't work for this cache!");
1234         }
1235         if ((coherent && consistency == Consistency.EVENTUAL) || (!coherent && consistency == Consistency.STRONG)) {
1236             throw new InvalidConfigurationException("Coherent and consistency attribute values are conflicting. "
1237                     + "Please remove the coherent attribute as its deprecated.");
1238         }
1239         if (getCacheConfiguration().getTerracottaConfiguration().getStorageStrategy() == StorageStrategy.CLASSIC
1240                 && !getCacheConfiguration().getTerracottaConfiguration().isLocalCacheEnabled()) {
1241             throw new InvalidConfigurationException("Local Cache cannot be disabled with " + StorageStrategy.CLASSIC
1242                     + " Storage strategy. Please enable local cache or use " + StorageStrategy.DCV2);
1243         }
1244     }
1245 
1246     /*
1247      * Note: this method could be used for xa and local tx modes as well if they supported NonStop
1248      */
1249     private Store makeXaStrictTransactionalIfNeeded(Store clusteredStore, ReadWriteCopyStrategy<Element> copyStrategy) {
1250         Store wrappedStore;
1251 
1252         if (configuration.isXaStrictTransactional()) {
1253             if (transactionManagerLookup.getTransactionManager() == null) {
1254                 throw new CacheException("You've configured cache " + cacheManager.getName() + "."
1255                                          + configuration.getName() + " to be transactional, but no TransactionManager could be found!");
1256             }
1257             //set xa enabled
1258             if (configuration.isTerracottaClustered()) {
1259                 configuration.getTerracottaConfiguration().setCacheXA(true);
1260             }
1261             SoftLockFactory softLockFactory = cacheManager.createSoftLockFactory(this);
1262             TransactionIDFactory transactionIDFactory = cacheManager.createTransactionIDFactory();
1263 
1264             // this xaresource is for initial registration and recovery
1265             EhcacheXAResource xaResource = new EhcacheXAResourceImpl(this, clusteredStore, transactionManagerLookup,
1266                     softLockFactory, transactionIDFactory);
1267             transactionManagerLookup.register(xaResource);
1268 
1269             wrappedStore = new XATransactionStore(transactionManagerLookup, softLockFactory, transactionIDFactory, this, clusteredStore,
1270                     copyStrategy);
1271         } else {
1272             wrappedStore = clusteredStore;
1273         }
1274 
1275         return wrappedStore;
1276     }
1277 
1278     private CacheCluster getCacheCluster() {
1279         CacheCluster cacheCluster;
1280         try {
1281             cacheCluster = getCacheManager().getCluster(ClusterScheme.TERRACOTTA);
1282         } catch (ClusterSchemeNotAvailableException e) {
1283             LOG.info("Terracotta ClusterScheme is not available, using ClusterScheme.NONE");
1284             cacheCluster = getCacheManager().getCluster(ClusterScheme.NONE);
1285         }
1286         return cacheCluster;
1287     }
1288 
1289     /***
1290      * The CacheWriterManager's initialisation can be deferred until an actual CacheWriter has been registered.
1291      * <p/>
1292      * 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.
1293      *
1294      * @param imperative indicates whether it's imperative for the cache writer manager to be initialised before operations can continue
1295      * @throws CacheException when the CacheWriterManager couldn't be initialised but it was imperative to do so
1296      */
1297     private void initialiseCacheWriterManager(boolean imperative) throws CacheException {
1298         if (!cacheWriterManagerInitFlag.get()) {
1299             cacheWriterManagerInitLock.lock();
1300             try {
1301                 if (!cacheWriterManagerInitFlag.get()) {
1302                     if (cacheWriterManager != null && registeredCacheWriter != null) {
1303                         cacheWriterManager.init(this);
1304                         cacheWriterManagerInitFlag.set(true);
1305                     } else if (imperative) {
1306                         throw new CacheException("Cache: " + configuration.getName() + " was being used with cache writer " +
1307                                 "features, but it wasn't properly registered beforehand.");
1308                     }
1309                 }
1310             } finally {
1311                 cacheWriterManagerInitLock.unlock();
1312             }
1313         }
1314     }
1315 
1316     /***
1317      * {@inheritDoc}
1318      */
1319     public CacheWriterManager getWriterManager() {
1320         return cacheWriterManager;
1321     }
1322 
1323     /***
1324      * Creates a disk store when either:
1325      * <ol>
1326      * <li>overflowToDisk is enabled
1327      * <li>diskPersistent is enabled
1328      * </ol>
1329      *
1330      * @return the disk store
1331      */
1332     protected DiskStore createDiskStore() {
1333         if (isDiskStore()) {
1334             return DiskStore.create(this, diskStorePath);
1335         } else {
1336             return null;
1337         }
1338     }
1339 
1340     /***
1341      * Whether this cache uses a disk store
1342      *
1343      * @return true if the cache either overflows to disk or is disk persistent
1344      */
1345     protected boolean isDiskStore() {
1346         return configuration.isOverflowToDisk() || configuration.isDiskPersistent();
1347     }
1348 
1349     /***
1350      * Indicates whether this cache is clustered by Terracotta
1351      *
1352      * @return {@code true} when the cache is clustered by Terracotta; or {@code false} otherwise
1353      */
1354     public boolean isTerracottaClustered() {
1355         return configuration.isTerracottaClustered();
1356     }
1357 
1358     /***
1359      * Bootstrap command. This must be called after the Cache is initialised, during
1360      * CacheManager initialisation. If loads are synchronous, they will complete before the CacheManager
1361      * initialise completes, otherwise they will happen in the background.
1362      */
1363     public void bootstrap() {
1364         if (!disabled && bootstrapCacheLoader != null) {
1365             bootstrapCacheLoader.load(this);
1366         }
1367 
1368     }
1369 
1370     /***
1371      * Put an element in the cache.
1372      * <p/>
1373      * Resets the access statistics on the element, which would be the case if it has previously been
1374      * gotten from a cache, and is now being put back.
1375      * <p/>
1376      * Also notifies the CacheEventListener that:
1377      * <ul>
1378      * <li>the element was put, but only if the Element was actually put.
1379      * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
1380      * if it was requested
1381      * </ul>
1382      * <p/>
1383      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1384      * This exception should be caught in those circumstances.
1385      *
1386      * @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
1387      *                <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
1388      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1389      * @throws CacheException
1390      */
1391     public final void put(Element element) throws IllegalArgumentException, IllegalStateException,
1392             CacheException {
1393         put(element, false);
1394     }
1395 
1396     /***
1397      * {@inheritDoc}
1398      */
1399     public void putAll(Collection<Element> elements) throws IllegalArgumentException, IllegalStateException, CacheException {
1400         putAll(elements, false);
1401     }
1402 
1403 
1404     /***
1405      * Put an element in the cache.
1406      * <p/>
1407      * Resets the access statistics on the element, which would be the case if it has previously been
1408      * gotten from a cache, and is now being put back.
1409      * <p/>
1410      * Also notifies the CacheEventListener that:
1411      * <ul>
1412      * <li>the element was put, but only if the Element was actually put.
1413      * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
1414      * if it was requested
1415      * </ul>
1416      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1417      * This exception should be caught in those circumstances.
1418      *
1419      * @param element                     A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
1420      *                                    <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
1421      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1422      *                                    further notification to doNotNotifyCacheReplicators cache peers
1423      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
1424      * @throws IllegalArgumentException if the element is null
1425      */
1426     public final void put(Element element, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
1427             IllegalStateException, CacheException {
1428         putInternal(element, doNotNotifyCacheReplicators, false);
1429     }
1430 
1431     /***
1432      * {@inheritDoc}
1433      */
1434     private void putAll(Collection<Element> elements, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
1435             IllegalStateException, CacheException {
1436         putAllInternal(elements, doNotNotifyCacheReplicators);
1437     }
1438 
1439     /***
1440      * {@inheritDoc}
1441      */
1442     public void putWithWriter(Element element) throws IllegalArgumentException, IllegalStateException, CacheException {
1443         putInternal(element, false, true);
1444     }
1445 
1446     private void putInternal(Element element, boolean doNotNotifyCacheReplicators, boolean useCacheWriter) {
1447         if (useCacheWriter) {
1448             initialiseCacheWriterManager(true);
1449         }
1450 
1451         checkStatus();
1452 
1453         if (disabled) {
1454             return;
1455         }
1456 
1457         if (element == null) {
1458             if (doNotNotifyCacheReplicators) {
1459 
1460                 LOG.debug("Element from replicated put is null. This happens because the element is a SoftReference" +
1461                         " and it has been collected. Increase heap memory on the JVM or set -Xms to be the same as " +
1462                         "-Xmx to avoid this problem.");
1463 
1464             }
1465             //nulls are ignored
1466             return;
1467         }
1468 
1469 
1470         if (element.getObjectKey() == null) {
1471             //nulls are ignored
1472             return;
1473         }
1474 
1475         element.resetAccessStatistics();
1476 
1477         applyDefaultsToElementWithoutLifespanSet(element);
1478 
1479         backOffIfDiskSpoolFull();
1480 
1481         if (useCacheWriter) {
1482             boolean elementExists = false;
1483             boolean notifyListeners = true;
1484             try {
1485                 elementExists = !compoundStore.putWithWriter(element, cacheWriterManager);
1486             } catch (StoreUpdateException e) {
1487                 elementExists = e.isUpdate();
1488                 notifyListeners = configuration.getCacheWriterConfiguration().getNotifyListenersOnException();
1489                 RuntimeException cause = e.getCause();
1490                 if (cause instanceof CacheWriterManagerException) {
1491                     throw ((CacheWriterManagerException)cause).getCause();
1492                 }
1493                 throw cause;
1494             } finally {
1495                 if (elementExists) {
1496                     element.updateUpdateStatistics();
1497                 }
1498                 if (notifyListeners) {
1499                     notifyPutInternalListeners(element, doNotNotifyCacheReplicators, elementExists);
1500                 }
1501             }
1502         } else {
1503             boolean elementExists = !compoundStore.put(element);
1504             if (elementExists) {
1505                 element.updateUpdateStatistics();
1506             }
1507             notifyPutInternalListeners(element, doNotNotifyCacheReplicators, elementExists);
1508         }
1509     }
1510 
1511     private void putAllInternal(Collection<Element> elements, boolean doNotNotifyCacheReplicators) {
1512         checkStatus();
1513 
1514         if (disabled || elements.isEmpty()) {
1515             return;
1516         }
1517 
1518         backOffIfDiskSpoolFull();
1519 
1520         compoundStore.putAll(elements);
1521         for (Element element : elements) {
1522             element.resetAccessStatistics();
1523             applyDefaultsToElementWithoutLifespanSet(element);
1524             notifyPutInternalListeners(element, doNotNotifyCacheReplicators, false);
1525         }
1526     }
1527 
1528     private void notifyPutInternalListeners(Element element, boolean doNotNotifyCacheReplicators, boolean elementExists) {
1529         if (elementExists) {
1530             registeredEventListeners.notifyElementUpdated(element, doNotNotifyCacheReplicators);
1531         } else {
1532             registeredEventListeners.notifyElementPut(element, doNotNotifyCacheReplicators);
1533         }
1534     }
1535 
1536     /***
1537      * wait outside of synchronized block so as not to block readers
1538      * If the disk store spool is full wait a short time to give it a chance to
1539      * catch up.
1540      * todo maybe provide a warning if this is continually happening or monitor via JMX
1541      */
1542     private void backOffIfDiskSpoolFull() {
1543 
1544         if (compoundStore.bufferFull()) {
1545             // back off to avoid OutOfMemoryError
1546             try {
1547                 Thread.sleep(BACK_OFF_TIME_MILLIS);
1548             } catch (InterruptedException e) {
1549                 Thread.currentThread().interrupt();
1550             }
1551         }
1552     }
1553 
1554     private void applyDefaultsToElementWithoutLifespanSet(Element element) {
1555         if (!element.isLifespanSet()) {
1556             element.setLifespanDefaults(TimeUtil.convertTimeToInt(configuration.getTimeToIdleSeconds()),
1557                     TimeUtil.convertTimeToInt(configuration.getTimeToLiveSeconds()),
1558                     configuration.isEternal());
1559         }
1560     }
1561 
1562     /***
1563      * Put an element in the cache, without updating statistics, or updating listeners. This is meant to be used
1564      * in conjunction with {@link #getQuiet}.
1565      * Synchronization is handled within the method.
1566      * <p/>
1567      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1568      * This exception should be caught in those circumstances.
1569      * <p/>
1570      *
1571      * @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
1572      *                <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
1573      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
1574      * @throws IllegalArgumentException if the element is null
1575      */
1576     public final void putQuiet(Element element) throws IllegalArgumentException, IllegalStateException,
1577             CacheException {
1578         checkStatus();
1579 
1580         if (disabled) {
1581             return;
1582         }
1583 
1584         if (element == null || element.getObjectKey() == null) {
1585             //nulls are ignored
1586             return;
1587         }
1588 
1589         applyDefaultsToElementWithoutLifespanSet(element);
1590 
1591         compoundStore.put(element);
1592     }
1593 
1594     /***
1595      * Gets an element from the cache. Updates Element Statistics
1596      * <p/>
1597      * Note that the Element's lastAccessTime is always the time of this get.
1598      * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
1599      * <p/>
1600      * Synchronization is handled within the method.
1601      *
1602      * @param key a serializable value. Null keys are not stored so get(null) always returns null
1603      * @return the element, or null, if it does not exist.
1604      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1605      * @see #isExpired
1606      */
1607     public final Element get(Serializable key) throws IllegalStateException, CacheException {
1608         return get((Object) key);
1609     }
1610 
1611 
1612     /***
1613      * Gets an element from the cache. Updates Element Statistics
1614      * <p/>
1615      * Note that the Element's lastAccessTime is always the time of this get.
1616      * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
1617      * <p/>
1618      * Synchronization is handled within the method.
1619      *
1620      * @param key an Object value
1621      * @return the element, or null, if it does not exist.
1622      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1623      * @see #isExpired
1624      * @since 1.2
1625      */
1626     public final Element get(Object key) throws IllegalStateException, CacheException {
1627         checkStatus();
1628 
1629         if (disabled) {
1630             return null;
1631         }
1632 
1633         if (isStatisticsEnabled()) {
1634             long start = System.currentTimeMillis();
1635             Element element = searchInStoreWithStats(key);
1636             //todo is this expensive. Maybe ditch.
1637             long end = System.currentTimeMillis();
1638             liveCacheStatisticsData.addGetTimeMillis(end - start);
1639             return element;
1640         } else {
1641             return searchInStoreWithoutStats(key, false, true);
1642         }
1643     }
1644 
1645     /***
1646      * {@inheritDoc}
1647      */
1648     public Map<Object, Element> getAll(Collection<Object> keys) throws IllegalStateException, CacheException {
1649         checkStatus();
1650 
1651         if (disabled || keys.isEmpty()) {
1652             return null;
1653         }
1654 
1655         Map<Object, Element> retMap = new HashMap<Object, Element>();
1656         for (Object key : keys) {
1657             Element element = get(key);
1658             retMap.put(key, element);
1659         }
1660         return retMap;
1661     }
1662 
1663     /***
1664      * This method will return, from the cache, the Element associated with the argument "key".
1665      * <p/>
1666      * 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,
1667      * the one associated with the cache. If both are null, no load is performed and null is returned.
1668      * <p/>
1669      * 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.
1670      * <p/>
1671      * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
1672      * are synchronized.
1673      *
1674      * @param key            key whose associated value is to be returned.
1675      * @param loader         the override loader to use. If null, the cache's default loader will be used
1676      * @param loaderArgument an argument to pass to the CacheLoader.
1677      * @return an element if it existed or could be loaded, otherwise null
1678      * @throws CacheException
1679      */
1680     public Element getWithLoader(Object key, CacheLoader loader, Object loaderArgument) throws CacheException {
1681 
1682         Element element = get(key);
1683         if (element != null) {
1684             return element;
1685         }
1686 
1687         if (registeredCacheLoaders.size() == 0 && loader == null) {
1688             return null;
1689         }
1690 
1691         try {
1692             //check again in case the last thread loaded it
1693             element = getQuiet(key);
1694             if (element != null) {
1695                 return element;
1696             }
1697 
1698             //wait for result
1699             long cacheLoaderTimeoutMillis = configuration.getCacheLoaderTimeoutMillis();
1700             final Object value;
1701             if (cacheLoaderTimeoutMillis > 0) {
1702                 final Future<AtomicReference<Object>> future = asynchronousLoad(key, loader, loaderArgument);
1703                 value = future.get(cacheLoaderTimeoutMillis, TimeUnit.MILLISECONDS).get();
1704             } else {
1705                 value = loadValueUsingLoader(key, loader, loaderArgument);
1706             }
1707             if (value != null) {
1708                 put(new Element(key, value), false);
1709             }
1710         } catch (TimeoutException e) {
1711             throw new LoaderTimeoutException("Timeout on load for key " + key, e);
1712         } catch (Exception e) {
1713             throw new CacheException("Exception on load for key " + key, e);
1714         }
1715         return getQuiet(key);
1716     }
1717 
1718     /***
1719      * The load method provides a means to "pre-load" the cache. This method will, asynchronously, load the specified
1720      * object into the cache using the associated CacheLoader. If the object already exists in the cache, no action is
1721      * taken. If no loader is associated with the object, no object will be loaded into the cache. If a problem is
1722      * encountered during the retrieving or loading of the object, an exception should be logged. If the "arg" argument
1723      * is set, the arg object will be passed to the CacheLoader.load method. The cache will not dereference the object.
1724      * If no "arg" value is provided a null will be passed to the load method. The storing of null values in the cache
1725      * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding
1726      * the object in the cache. In both cases a null is returned.
1727      * <p/>
1728      * The Ehcache native API provides similar functionality to loaders using the
1729      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1730      *
1731      * @param key key whose associated value to be loaded using the associated CacheLoader if this cache doesn't contain it.
1732      * @throws CacheException
1733      */
1734     public void load(final Object key) throws CacheException {
1735         if (registeredCacheLoaders.size() == 0) {
1736 
1737             LOG.debug("The CacheLoader is null. Returning.");
1738             return;
1739         }
1740 
1741         boolean existsOnCall = isKeyInCache(key);
1742         if (existsOnCall) {
1743 
1744             LOG.debug("The key {} exists in the cache. Returning.", key);
1745             return;
1746         }
1747 
1748         asynchronousPut(key, null, null);
1749     }
1750 
1751     /***
1752      * The getAll method will return, from the cache, a Map of the objects associated with the Collection of keys in argument "keys".
1753      * If the objects are not in the cache, the associated cache loader will be called. If no loader is associated with an object,
1754      * a null is returned. If a problem is encountered during the retrieving or loading of the objects, an exception will be thrown.
1755      * If the "arg" argument is set, the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference
1756      * 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
1757      * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding the object in
1758      * the cache. In both cases a null is returned.
1759      * <p/>
1760      * <p/>
1761      * Note. If the getAll exceeds the maximum cache size, the returned map will necessarily be less than the number specified.
1762      * <p/>
1763      * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
1764      * are synchronized.
1765      * <p/>
1766      * The constructs package provides similar functionality using the
1767      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1768      *
1769      * @param keys           a collection of keys to be returned/loaded
1770      * @param loaderArgument an argument to pass to the CacheLoader.
1771      * @return a Map populated from the Cache. If there are no elements, an empty Map is returned.
1772      * @throws CacheException
1773      */
1774     public Map getAllWithLoader(Collection keys, Object loaderArgument) throws CacheException {
1775         if (keys == null) {
1776             return new HashMap(0);
1777         }
1778         Map<Object, Object> map = new HashMap<Object, Object>(keys.size());
1779 
1780         List<Object> missingKeys = new ArrayList<Object>(keys.size());
1781 
1782         if (registeredCacheLoaders.size() > 0) {
1783             Object key = null;
1784             try {
1785                 map = new HashMap<Object, Object>(keys.size());
1786 
1787                 for (Object key1 : keys) {
1788                     key = key1;
1789                     Element element = get(key);
1790 
1791                     if (element != null) {
1792                         map.put(key, element.getObjectValue());
1793                     } else if (isKeyInCache(key)) {
1794                         map.put(key, null);
1795                     } else {
1796                         missingKeys.add(key);
1797                     }
1798                 }
1799 
1800                 //now load everything that's missing.
1801                 Future future = asynchronousLoadAll(missingKeys, loaderArgument);
1802 
1803                 //wait for result
1804                 long cacheLoaderTimeoutMillis = configuration.getCacheLoaderTimeoutMillis();
1805                 if (cacheLoaderTimeoutMillis > 0) {
1806                     try {
1807                         future.get(cacheLoaderTimeoutMillis, TimeUnit.MILLISECONDS);
1808                     } catch (TimeoutException e) {
1809                         throw new LoaderTimeoutException("Timeout on load for key " + key, e);
1810                     }
1811                 } else {
1812                     future.get();
1813                 }
1814 
1815 
1816                 for (Object missingKey : missingKeys) {
1817                     key = missingKey;
1818                     Element element = get(key);
1819                     if (element != null) {
1820                         map.put(key, element.getObjectValue());
1821                     } else {
1822                         map.put(key, null);
1823                     }
1824                 }
1825 
1826             } catch (InterruptedException e) {
1827                 throw new CacheException(e.getMessage() + " for key " + key, e);
1828             } catch (ExecutionException e) {
1829                 throw new CacheException(e.getMessage() + " for key " + key, e);
1830             }
1831         } else {
1832             for (Object key : keys) {
1833                 Element element = get(key);
1834                 if (element != null) {
1835                     map.put(key, element.getObjectValue());
1836                 } else {
1837                     map.put(key, null);
1838                 }
1839             }
1840         }
1841         return map;
1842     }
1843 
1844 
1845     /***
1846      * The loadAll method provides a means to "pre load" objects into the cache. This method will, asynchronously, load
1847      * the specified objects into the cache using the associated cache loader(s). If the an object already exists in the
1848      * cache, no action is taken. If no loader is associated with the object, no object will be loaded into the cache.
1849      * If a problem is encountered during the retrieving or loading of the objects, an exception (to be defined)
1850      * should be logged. The getAll method will return, from the cache, a Map of the objects associated with the
1851      * Collection of keys in argument "keys". If the objects are not in the cache, the associated cache loader will be
1852      * called. If no loader is associated with an object, a null is returned. If a problem is encountered during the
1853      * retrieving or loading of the objects, an exception (to be defined) will be thrown. If the "arg" argument is set,
1854      * the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference the object.
1855      * If no "arg" value is provided a null will be passed to the loadAll method.
1856      * <p/>
1857      * keys - collection of the keys whose associated values to be loaded into this cache by using the associated
1858      * CacheLoader if this cache doesn't contain them.
1859      * <p/>
1860      * The Ehcache native API provides similar functionality to loaders using the
1861      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1862      */
1863     public void loadAll(final Collection keys, final Object argument) throws CacheException {
1864 
1865         if (registeredCacheLoaders.size() == 0) {
1866 
1867             LOG.debug("The CacheLoader is null. Returning.");
1868             return;
1869         }
1870         if (keys == null) {
1871             return;
1872         }
1873         asynchronousLoadAll(keys, argument);
1874     }
1875 
1876     /***
1877      * Gets an element from the cache, without updating Element statistics. Cache statistics are
1878      * still updated. Listeners are not called.
1879      * <p/>
1880      *
1881      * @param key a serializable value
1882      * @return the element, or null, if it does not exist.
1883      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1884      * @see #isExpired
1885      */
1886     public final Element getQuiet(Serializable key) throws IllegalStateException, CacheException {
1887         return getQuiet((Object) key);
1888     }
1889 
1890     /***
1891      * Gets an element from the cache, without updating Element statistics. Cache statistics are
1892      * not updated.
1893      * <p/>
1894      * Listeners are not called.
1895      *
1896      * @param key a serializable value
1897      * @return the element, or null, if it does not exist.
1898      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1899      * @see #isExpired
1900      * @since 1.2
1901      */
1902     public final Element getQuiet(Object key) throws IllegalStateException, CacheException {
1903         checkStatus();
1904         return searchInStoreWithoutStats(key, true, false);
1905     }
1906 
1907     /***
1908      * Returns a list of all element keys in the cache, whether or not they are expired.
1909      * <p/>
1910      * The returned keys are unique and can be considered a set.
1911      * <p/>
1912      * The List returned is not live. It is a copy.
1913      * <p/>
1914      * The time taken is O(n). On a single CPU 1.8Ghz P4, approximately 8ms is required
1915      * for each 1000 entries.
1916      *
1917      * @return a list of {@link Object} keys
1918      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1919      */
1920     public final List getKeys() throws IllegalStateException, CacheException {
1921         checkStatus();
1922         return compoundStore.getKeys();
1923     }
1924 
1925     /***
1926      * Returns a list of all element keys in the cache. Only keys of non-expired
1927      * elements are returned.
1928      * <p/>
1929      * The returned keys are unique and can be considered a set.
1930      * <p/>
1931      * The List returned is not live. It is a copy.
1932      * <p/>
1933      * The time taken is O(n), where n is the number of elements in the cache. On
1934      * a 1.8Ghz P4, the time taken is approximately 200ms per 1000 entries. This method
1935      * is not synchronized, because it relies on a non-live list returned from {@link #getKeys()}
1936      * , which is synchronised, and which takes 8ms per 1000 entries. This way
1937      * cache liveness is preserved, even if this method is very slow to return.
1938      * <p/>
1939      * Consider whether your usage requires checking for expired keys. Because
1940      * this method takes so long, depending on cache settings, the list could be
1941      * quite out of date by the time you get it.
1942      *
1943      * @return a list of {@link Object} keys
1944      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1945      */
1946     public final List getKeysWithExpiryCheck() throws IllegalStateException, CacheException {
1947          List allKeyList = getKeys();
1948          //removeInternal keys of expired elements
1949          ArrayList < Object > nonExpiredKeys = new ArrayList(allKeyList.size());
1950          for (Iterator iter = allKeyList.iterator(); iter.hasNext();) {
1951              Object key = iter.next();
1952              Element element = getQuiet(key);
1953              if (element != null) {
1954                  nonExpiredKeys.add(key);
1955              }
1956          }
1957          nonExpiredKeys.trimToSize();
1958          return nonExpiredKeys;
1959     }
1960 
1961 
1962     /***
1963      * Returns a list of all elements in the cache, whether or not they are expired.
1964      * <p/>
1965      * The returned keys are not unique and may contain duplicates. If the cache is only
1966      * using the memory store, the list will be unique. If the disk store is being used
1967      * as well, it will likely contain duplicates, because of the internal store design.
1968      * <p/>
1969      * The List returned is not live. It is a copy.
1970      * <p/>
1971      * The time taken is O(log n). On a single CPU 1.8Ghz P4, approximately 6ms is required
1972      * for 1000 entries and 36 for 50000.
1973      * <p/>
1974      * This is the fastest getKeys method
1975      *
1976      * @return a list of {@link Object} keys
1977      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1978      */
1979     public final List getKeysNoDuplicateCheck() throws IllegalStateException {
1980         checkStatus();
1981         return getKeys();
1982     }
1983 
1984     private Element searchInStoreWithStats(Object key) {
1985         boolean wasInMemory = compoundStore.containsKeyInMemory(key);
1986         boolean wasOffHeap = false;
1987         boolean hasOffHeap = getCacheConfiguration().isOverflowToOffHeap();
1988         boolean isTCClustered = getCacheConfiguration().isTerracottaClustered();
1989         boolean hasOnDisk = isTCClustered || getCacheConfiguration().isOverflowToDisk() || getCacheConfiguration().isDiskPersistent();
1990         Element element;
1991 
1992         if (!wasInMemory) {
1993             liveCacheStatisticsData.cacheMissInMemory();
1994             if (hasOffHeap) {
1995                 wasOffHeap = compoundStore.containsKeyOffHeap(key);
1996             }
1997         }
1998 
1999         element = compoundStore.get(key);
2000 
2001         if (element != null) {
2002             if (isExpired(element)) {
2003                 if (LOG.isDebugEnabled()) {
2004                     LOG.debug(configuration.getName() + " cache hit, but element expired");
2005                 }
2006                 liveCacheStatisticsData.cacheMissExpired();
2007                 tryRemoveImmediately(key, true);
2008                 element = null;
2009             } else {
2010                 element.updateAccessStatistics();
2011                 if (LOG.isDebugEnabled()) {
2012                     LOG.debug("Cache: " + getName() + " store hit for " + key);
2013                 }
2014 
2015                 if (wasInMemory) {
2016                     liveCacheStatisticsData.cacheHitInMemory();
2017                 } else if (wasOffHeap) {
2018                     liveCacheStatisticsData.cacheHitOffHeap();
2019                 } else if (hasOffHeap) {
2020                     liveCacheStatisticsData.cacheMissOffHeap();
2021                     liveCacheStatisticsData.cacheHitOnDisk();
2022                 } else {
2023                     liveCacheStatisticsData.cacheHitOnDisk();
2024                 }
2025             }
2026         } else {
2027             liveCacheStatisticsData.cacheMissNotFound();
2028             if (hasOffHeap) {
2029                 liveCacheStatisticsData.cacheMissOffHeap();
2030             }
2031             if (hasOnDisk) {
2032                 liveCacheStatisticsData.cacheMissOnDisk();
2033             }
2034 
2035             if (LOG.isDebugEnabled()) {
2036                 LOG.debug(configuration.getName() + " cache - Miss");
2037             }
2038         }
2039         return element;
2040     }
2041 
2042     private Element searchInStoreWithoutStats(Object key, boolean quiet, boolean notifyListeners) {
2043         Element element = compoundStore.getQuiet(key);
2044 
2045         if (element != null) {
2046             if (isExpired(element)) {
2047                 tryRemoveImmediately(key, notifyListeners);
2048                 element = null;
2049             } else if (!(quiet || skipUpdateAccessStatistics(element))) {
2050                 element.updateAccessStatistics();
2051             }
2052         }
2053         return element;
2054     }
2055 
2056     private void tryRemoveImmediately(final Object key, final boolean notifyListeners) {
2057         Sync syncForKey = ((CacheLockProvider)getInternalContext()).getSyncForKey(key);
2058         boolean writeLocked = false;
2059         try {
2060             writeLocked = syncForKey.tryLock(LockType.WRITE, 0);
2061         } catch (InterruptedException e) {
2062             Thread.currentThread().interrupt();
2063         } catch (Error e) {
2064             if (!(e.getClass().getName().equals("com.tc.exception.TCLockUpgradeNotSupportedError"))) {
2065                throw e;
2066             }
2067         }
2068         if (writeLocked) {
2069             try {
2070                 removeInternal(key, true, notifyListeners, false, false);
2071             } finally {
2072                 syncForKey.unlock(LockType.WRITE);
2073             }
2074         } else {
2075             if (LOG.isDebugEnabled()) {
2076                 LOG.debug(configuration.getName() + " cache: element " + key + " expired, but couldn't be inline evicted");
2077             }
2078         }
2079     }
2080 
2081     private boolean skipUpdateAccessStatistics(Element element) {
2082       return configuration.isFrozen() && element.isEternal()
2083               && (configuration.getMaxElementsInMemory() == 0)
2084               && (!configuration.isOverflowToDisk() || configuration.getMaxElementsOnDisk() == 0);
2085     }
2086 
2087     /***
2088      * Removes an {@link Element} from the Cache. This also removes it from any
2089      * stores it may be in.
2090      * <p/>
2091      * Also notifies the CacheEventListener after the element was removed.
2092      * <p/>
2093      * Synchronization is handled within the method.
2094      * <p/>
2095      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
2096      * This exception should be caught in those circumstances.
2097      *
2098      * @param key the element key to operate on
2099      * @return true if the element was removed, false if it was not found in the cache
2100      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2101      */
2102     public final boolean remove(Serializable key) throws IllegalStateException {
2103         return remove((Object) key);
2104     }
2105 
2106     /***
2107      * Removes an {@link Element} from the Cache. This also removes it from any
2108      * stores it may be in.
2109      * <p/>
2110      * Also notifies the CacheEventListener after the element was removed, but only if an Element
2111      * with the key actually existed.
2112      * <p/>
2113      * Synchronization is handled within the method.
2114      * <p/>
2115      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
2116      * This exception should be caught in those circumstances.
2117      * <p/>
2118      *
2119      * @param key the element key to operate on
2120      * @return true if the element was removed, false if it was not found in the cache
2121      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2122      * @since 1.2
2123      */
2124     public final boolean remove(Object key) throws IllegalStateException {
2125         return remove(key, false);
2126     }
2127 
2128     /***
2129      * {@inheritDoc}
2130      */
2131     public void removeAll(final Collection<Object> keys) throws IllegalStateException {
2132         removeAll(keys, false);
2133     }
2134 
2135 
2136     /***
2137      * {@inheritDoc}
2138     */
2139     public final void removeAll(final Collection<Object> keys, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
2140         removeAllInternal(keys, false, true, doNotNotifyCacheReplicators);
2141     }
2142 
2143     /***
2144      * Removes an {@link Element} from the Cache. This also removes it from any
2145      * stores it may be in.
2146      * <p/>
2147      * Also notifies the CacheEventListener after the element was removed, but only if an Element
2148      * with the key actually existed.
2149      * <p/>
2150      * Synchronization is handled within the method.
2151      * <p/>
2152      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
2153      * This exception should be caught in those circumstances.
2154      *
2155      * @param key                         the element key to operate on
2156      * @param doNotNotifyCacheReplicators whether the remove is coming from a doNotNotifyCacheReplicators cache peer, in which case this remove should not initiate a
2157      *                                    further notification to doNotNotifyCacheReplicators cache peers
2158      * @return true if the element was removed, false if it was not found in the cache
2159      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2160      */
2161     public final boolean remove(Serializable key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
2162         return remove((Object) key, doNotNotifyCacheReplicators);
2163     }
2164 
2165     /***
2166      * Removes an {@link Element} from the Cache. This also removes it from any
2167      * stores it may be in.
2168      * <p/>
2169      * Also notifies the CacheEventListener after the element was removed, but only if an Element
2170      * with the key actually existed.
2171      * <p/>
2172      * Synchronization is handled within the method.
2173      *
2174      * @param key                         the element key to operate on
2175      * @param doNotNotifyCacheReplicators whether the remove is coming from a doNotNotifyCacheReplicators cache peer, in which case this remove should not initiate a
2176      *                                    further notification to doNotNotifyCacheReplicators cache peers
2177      * @return true if the element was removed, false if it was not found in the cache
2178      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2179      */
2180     public final boolean remove(Object key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
2181         return removeInternal(key, false, true, doNotNotifyCacheReplicators, false);
2182     }
2183 
2184     /***
2185      * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
2186      * stores it may be in.
2187      * <p/>
2188      * Listeners are not called.
2189      *
2190      * @param key the element key to operate on
2191      * @return true if the element was removed, false if it was not found in the cache
2192      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2193      */
2194     public final boolean removeQuiet(Serializable key) throws IllegalStateException {
2195         return removeInternal(key, false, false, false, false);
2196     }
2197 
2198     /***
2199      * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
2200      * stores it may be in.
2201      * <p/>
2202      * Listeners are not called.
2203      * <p/>
2204      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
2205      * This exception should be caught in those circumstances.
2206      *
2207      * @param key the element key to operate on
2208      * @return true if the element was removed, false if it was not found in the cache
2209      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2210      * @since 1.2
2211      */
2212     public final boolean removeQuiet(Object key) throws IllegalStateException {
2213         return removeInternal(key, false, false, false, false);
2214     }
2215 
2216     /***
2217      * {@inheritDoc}
2218      */
2219     public boolean removeWithWriter(Object key) throws IllegalStateException {
2220         return removeInternal(key, false, true, false, true);
2221     }
2222 
2223     /***
2224      * Removes or expires an {@link Element} from the Cache after an attempt to get it determined that it should be expired.
2225      * This also removes it from any stores it may be in.
2226      * <p/>
2227      * Also notifies the CacheEventListener after the element has expired.
2228      * <p/>
2229      * Synchronization is handled within the method.
2230      * <p/>
2231      * If a remove was called, listeners are notified, regardless of whether the element existed or not.
2232      * This allows distributed cache listeners to remove elements from a cluster regardless of whether they
2233      * existed locally.
2234      * <p/>
2235      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
2236      * This exception should be caught in those circumstances.
2237      *
2238      * @param key                         the element key to operate on
2239      * @param expiry                      if the reason this method is being called is to expire the element
2240      * @param notifyListeners             whether to notify listeners
2241      * @param doNotNotifyCacheReplicators whether not to notify cache replicators
2242      * @param useCacheWriter              if the element should else be removed from the cache writer
2243      * @return true if the element was removed, false if it was not found in the cache
2244      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2245      */
2246     private boolean removeInternal(Object key, boolean expiry, boolean notifyListeners,
2247                            boolean doNotNotifyCacheReplicators, boolean useCacheWriter)
2248             throws IllegalStateException {
2249 
2250         if (useCacheWriter) {
2251             initialiseCacheWriterManager(true);
2252         }
2253 
2254         checkStatus();
2255         Element elementFromStore = null;
2256 
2257         if (useCacheWriter) {
2258             try {
2259                 elementFromStore = compoundStore.removeWithWriter(key, cacheWriterManager);
2260             } catch (CacheWriterManagerException e) {
2261                 if (configuration.getCacheWriterConfiguration().getNotifyListenersOnException()) {
2262                     notifyRemoveInternalListeners(key, expiry, notifyListeners, doNotNotifyCacheReplicators,
2263                             elementFromStore);
2264                 }
2265                 throw e.getCause();
2266             }
2267         } else {
2268             elementFromStore = compoundStore.remove(key);
2269         }
2270 
2271         return notifyRemoveInternalListeners(key, expiry, notifyListeners, doNotNotifyCacheReplicators,
2272             elementFromStore);
2273     }
2274 
2275     private boolean notifyRemoveInternalListeners(Object key, boolean expiry, boolean notifyListeners, boolean doNotNotifyCacheReplicators,
2276                                                   Element elementFromStore) {
2277         boolean removed = false;
2278         boolean removeNotified = false;
2279 
2280         if (elementFromStore != null) {
2281             if (expiry) {
2282                 //always notify expire which is lazy regardless of the removeQuiet
2283                 registeredEventListeners.notifyElementExpiry(elementFromStore, doNotNotifyCacheReplicators);
2284             } else if (notifyListeners) {
2285                 removeNotified = true;
2286                 registeredEventListeners.notifyElementRemoved(elementFromStore, doNotNotifyCacheReplicators);
2287             }
2288             removed = true;
2289         }
2290 
2291         //If we are trying to remove an element which does not exist locally, we should still notify so that
2292         //cluster invalidations work.
2293         if (notifyListeners && !expiry && !removeNotified) {
2294             Element syntheticElement = new Element(key, null);
2295             registeredEventListeners.notifyElementRemoved(syntheticElement, doNotNotifyCacheReplicators);
2296         }
2297 
2298         return removed;
2299     }
2300 
2301     /***
2302      * Removes or expires a collection of {@link Element}s from the Cache after an attempt to get it determined that it should be expired.
2303      * This also removes it from any stores it may be in.
2304      * <p/>
2305      * Also notifies the CacheEventListener after the element has expired.
2306      * <p/>
2307      * Synchronization is handled within the method.
2308      * <p/>
2309      * If a removeAll was called, listeners are notified, regardless of whether the element existed or not. This allows distributed cache
2310      * listeners to remove elements from a cluster regardless of whether they existed locally.
2311      * <p/>
2312      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails. This exception
2313      * should be caught in those circumstances.
2314      *
2315      * @param keys a collection of keys to operate on
2316      * @param expiry if the reason this method is being called is to expire the element
2317      * @param notifyListeners whether to notify listeners
2318      * @param doNotNotifyCacheReplicators whether not to notify cache replicators
2319      * @return true if the element was removed, false if it was not found in the cache
2320      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2321      */
2322     private void removeAllInternal(final Collection<Object> keys, boolean expiry, boolean notifyListeners,
2323             boolean doNotNotifyCacheReplicators) throws IllegalStateException {
2324         checkStatus();
2325 
2326         if (disabled || keys.isEmpty()) {
2327             return;
2328         }
2329 
2330         compoundStore.removeAll(keys);
2331         for (Object key : keys) {
2332             Element syntheticElement = new Element(key, null);
2333             notifyRemoveInternalListeners(key, false, notifyListeners, doNotNotifyCacheReplicators, syntheticElement);
2334         }
2335     }
2336 
2337     /***
2338      * Removes all cached items.
2339      * Synchronization is handled within the method.
2340      * <p/>
2341      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
2342      * This exception should be caught in those circumstances.
2343      *
2344      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2345      */
2346     public void removeAll() throws IllegalStateException, CacheException {
2347         removeAll(false);
2348     }
2349 
2350 
2351     /***
2352      * Removes all cached items.
2353      * Synchronization is handled within the method.
2354      * <p/>
2355      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
2356      * This exception should be caught in those circumstances.
2357      *
2358      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2359      */
2360     public void removeAll(boolean doNotNotifyCacheReplicators) throws IllegalStateException, CacheException {
2361         checkStatus();
2362         compoundStore.removeAll();
2363         registeredEventListeners.notifyRemoveAll(doNotNotifyCacheReplicators);
2364     }
2365 
2366     /***
2367      * Starts an orderly shutdown of the Cache. Steps are:
2368      * <ol>
2369      * <li>Completes any outstanding CacheLoader loads.
2370      * <li>Completes any outstanding CacheWriter operations.
2371      * <li>Disposes any cache extensions.
2372      * <li>Disposes any cache event listeners. The listeners normally complete, so for example distributed caching operations will complete.
2373      * <li>Flushes all cache items from memory to the disk store, if any
2374      * <li>changes status to shutdown, so that any cache operations after this point throw IllegalStateException
2375      * </ol>
2376      * This method should be invoked only by CacheManager, as a cache's lifecycle is bound into that of it's cache manager.
2377      *
2378      * @throws IllegalStateException if the cache is already {@link Status#STATUS_SHUTDOWN}
2379      */
2380     public synchronized void dispose() throws IllegalStateException {
2381         if (checkStatusAlreadyDisposed()) {
2382             return;
2383         }
2384 
2385         if (bootstrapCacheLoader != null && bootstrapCacheLoader instanceof Disposable) {
2386             ((Disposable)bootstrapCacheLoader).dispose();
2387         }
2388 
2389         if (executorService != null) {
2390             executorService.shutdown();
2391         }
2392 
2393         disposeRegisteredCacheExtensions();
2394         disposeRegisteredCacheLoaders();
2395         disposeRegisteredCacheWriter();
2396         registeredEventListeners.dispose();
2397 
2398         if (cacheWriterManager != null) {
2399             cacheWriterManager.dispose();
2400         }
2401 
2402         if (compoundStore != null) {
2403             compoundStore.removeStoreListener(this);
2404             compoundStore.dispose();
2405         }
2406 
2407         cacheStatus.changeState(Status.STATUS_SHUTDOWN);
2408     }
2409 
2410     private void initialiseRegisteredCacheExtensions() {
2411         for (CacheExtension cacheExtension : registeredCacheExtensions) {
2412             cacheExtension.init();
2413         }
2414     }
2415 
2416     private void disposeRegisteredCacheExtensions() {
2417         for (CacheExtension cacheExtension : registeredCacheExtensions) {
2418             cacheExtension.dispose();
2419         }
2420     }
2421 
2422     private void initialiseRegisteredCacheLoaders() {
2423         for (CacheLoader cacheLoader : registeredCacheLoaders) {
2424             cacheLoader.init();
2425         }
2426     }
2427 
2428     private void disposeRegisteredCacheLoaders() {
2429         for (CacheLoader cacheLoader : registeredCacheLoaders) {
2430             cacheLoader.dispose();
2431         }
2432     }
2433 
2434     private void initialiseRegisteredCacheWriter() {
2435         CacheWriter writer = registeredCacheWriter;
2436         if (writer != null) {
2437             writer.init();
2438         }
2439     }
2440 
2441     private void disposeRegisteredCacheWriter() {
2442         CacheWriter writer = registeredCacheWriter;
2443         if (writer != null) {
2444             writer.dispose();
2445         }
2446     }
2447 
2448     /***
2449      * Gets the cache configuration this cache was created with.
2450      * <p/>
2451      * Things like listeners that are added dynamically are excluded.
2452      */
2453     public CacheConfiguration getCacheConfiguration() {
2454         return configuration;
2455     }
2456 
2457 
2458     /***
2459      * Flushes all cache items from memory to the disk store, and from the DiskStore to disk.
2460      *
2461      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2462      */
2463     public final synchronized void flush() throws IllegalStateException, CacheException {
2464         checkStatus();
2465         try {
2466             compoundStore.flush();
2467         } catch (IOException e) {
2468             throw new CacheException("Unable to flush cache: " + configuration.getName()
2469                     + ". Initial cause was " + e.getMessage(), e);
2470         }
2471     }
2472 
2473     /***
2474      * Gets the size of the cache. This is a subtle concept. See below.
2475      * <p/>
2476      * This number is the actual number of elements, including expired elements
2477      * that have not been removed.
2478      * <p/>
2479      * Expired elements are removed from the the memory store when getting an
2480      * expired element, or when attempting to spool an expired element to disk.
2481      * <p/>
2482      * Expired elements are removed from the disk store when getting an expired
2483      * element, or when the expiry thread runs, which is once every five
2484      * minutes.
2485      * <p/>
2486      * To get an exact size, which would exclude expired elements, use
2487      * {@link #getKeysWithExpiryCheck()}.size(), although see that method for
2488      * the approximate time that would take.
2489      * <p/>
2490      * To get a very fast result, use {@link #getKeysNoDuplicateCheck()}.size().
2491      * If the disk store is being used, there will be some duplicates.
2492      * <p/>
2493      * Note:getSize() is a very expensive operation in off-heap, disk and Terracotta implementations.
2494      *
2495      * @return The size value
2496      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2497      */
2498     public final int getSize() throws IllegalStateException, CacheException {
2499         checkStatus();
2500 
2501         if (isTerracottaClustered()) {
2502             return compoundStore.getTerracottaClusteredSize();
2503         } else {
2504             return compoundStore.getSize();
2505         }
2506     }
2507 
2508     /***
2509      * {@inheritDoc}
2510      */
2511     public int getSizeBasedOnAccuracy(int statisticsAccuracy)
2512             throws IllegalStateException, CacheException {
2513         if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_BEST_EFFORT) {
2514             return getSize();
2515         } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_GUARANTEED) {
2516             return getKeysWithExpiryCheck().size();
2517         } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_NONE) {
2518             return getKeysNoDuplicateCheck().size();
2519         }
2520         throw new IllegalArgumentException("Unknown statistics accuracy: "
2521                 + statisticsAccuracy);
2522     }
2523 
2524     /***
2525      * Gets the size of the memory store for this cache. This method relies on calculating
2526      * Serialized sizes. If the Element values are not Serializable they will show as zero.
2527      * <p/>
2528      * Warning: This method can be very expensive to run. Allow approximately 1 second
2529      * per 1MB of entries. Running this method could create liveness problems
2530      * because the object lock is held for a long period
2531      * <p/>
2532      *
2533      * @return the approximate size of the memory store in bytes
2534      * @throws IllegalStateException
2535      */
2536     public final long calculateInMemorySize() throws IllegalStateException, CacheException {
2537         checkStatus();
2538         return compoundStore.getInMemorySizeInBytes();
2539     }
2540 
2541     /***
2542      * Gets the size of the off-heap store for this cache.
2543      *
2544      * @return the size of the off-heap store in bytes
2545      * @throws IllegalStateException
2546      */
2547     public final long calculateOffHeapSize() throws IllegalStateException, CacheException {
2548         checkStatus();
2549         return compoundStore.getOffHeapSizeInBytes();
2550     }
2551 
2552     /***
2553      * Gets the size of the on-disk store for this cache
2554      *
2555      * @return the size of the on-disk store in bytes
2556      * @throws IllegalStateException
2557      */
2558     public final long calculateOnDiskSize() throws IllegalStateException, CacheException {
2559         checkStatus();
2560         return compoundStore.getOnDiskSizeInBytes();
2561     }
2562 
2563     /***
2564      * Returns the number of elements in the memory store.
2565      *
2566      * @return the number of elements in the memory store
2567      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2568      */
2569     public final long getMemoryStoreSize() throws IllegalStateException {
2570         checkStatus();
2571         return compoundStore.getInMemorySize();
2572     }
2573 
2574     /***
2575      * Returns the number of elements in the off-heap store.
2576      *
2577      * @return the number of elements in the off-heap store
2578      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2579      */
2580     public long getOffHeapStoreSize() throws IllegalStateException {
2581         checkStatus();
2582         return compoundStore.getOffHeapSize();
2583     }
2584 
2585     /***
2586      * Returns the number of elements in the disk store.
2587      *
2588      * @return the number of elements in the disk store.
2589      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2590      */
2591     public final int getDiskStoreSize() throws IllegalStateException {
2592         checkStatus();
2593         if (isTerracottaClustered()) {
2594             return compoundStore.getTerracottaClusteredSize();
2595         } else {
2596             return compoundStore.getOnDiskSize();
2597         }
2598     }
2599 
2600     /***
2601      * Gets the status attribute of the Cache.
2602      *
2603      * @return The status value from the Status enum class
2604      */
2605     public final Status getStatus() {
2606         return cacheStatus.getStatus();
2607     }
2608 
2609 
2610     private void checkStatus() throws IllegalStateException {
2611         cacheStatus.checkAlive(configuration);
2612     }
2613 
2614     private boolean checkStatusAlreadyDisposed() throws IllegalStateException {
2615         return cacheStatus.isShutdown();
2616     }
2617 
2618 
2619     /***
2620      * Gets the cache name.
2621      */
2622     public final String getName() {
2623         return configuration.getName();
2624     }
2625 
2626     /***
2627      * Sets the cache name which will name.
2628      *
2629      * @param name the name of the cache. Should not be null. Should also not contain any '/' characters, as these interfere
2630      *             with distribution
2631      * @throws IllegalArgumentException if an illegal name is used.
2632      */
2633     public final void setName(String name) throws IllegalArgumentException {
2634         if (!cacheStatus.isUninitialized()) {
2635             throw new IllegalStateException("Only uninitialised caches can have their names set.");
2636         }
2637         configuration.setName(name);
2638     }
2639 
2640     /***
2641      * Returns a {@link String} representation of {@link Cache}.
2642      */
2643     @Override
2644     public String toString() {
2645         StringBuilder dump = new StringBuilder();
2646 
2647         dump.append("[")
2648                 .append(" name = ").append(configuration.getName())
2649                 .append(" status = ").append(cacheStatus.getStatus())
2650                 .append(" eternal = ").append(configuration.isEternal())
2651                 .append(" overflowToDisk = ").append(configuration.isOverflowToDisk())
2652                 .append(" maxElementsInMemory = ").append(configuration.getMaxElementsInMemory())
2653                 .append(" maxElementsOnDisk = ").append(configuration.getMaxElementsOnDisk())
2654                 .append(" memoryStoreEvictionPolicy = ").append(configuration.getMemoryStoreEvictionPolicy())
2655                 .append(" timeToLiveSeconds = ").append(configuration.getTimeToLiveSeconds())
2656                 .append(" timeToIdleSeconds = ").append(configuration.getTimeToIdleSeconds())
2657                 .append(" diskPersistent = ").append(configuration.isDiskPersistent())
2658                 .append(" diskExpiryThreadIntervalSeconds = ").append(configuration.getDiskExpiryThreadIntervalSeconds())
2659                 .append(registeredEventListeners)
2660                 .append(" hitCount = ").append(getLiveCacheStatisticsNoCheck().getCacheHitCount())
2661                 .append(" memoryStoreHitCount = ").append(getLiveCacheStatisticsNoCheck().getInMemoryHitCount())
2662                 .append(" diskStoreHitCount = ").append(getLiveCacheStatisticsNoCheck().getOnDiskHitCount())
2663                 .append(" missCountNotFound = ").append(getLiveCacheStatisticsNoCheck().getCacheMissCount())
2664                 .append(" missCountExpired = ").append(getLiveCacheStatisticsNoCheck().getCacheMissCountExpired())
2665                 .append(" overflowToOffHeap = ").append(configuration.isOverflowToOffHeap())
2666                 .append(" maxMemoryOffHeap = ").append(configuration.getMaxMemoryOffHeap())
2667                 .append(" ]");
2668 
2669         return dump.toString();
2670     }
2671 
2672 
2673     /***
2674      * Checks whether this cache element has expired.
2675      * <p/>
2676      * The element is expired if:
2677      * <ol>
2678      * <li> the idle time is non-zero and has elapsed, unless the cache is eternal; or
2679      * <li> the time to live is non-zero and has elapsed, unless the cache is eternal; or
2680      * <li> the value of the element is null.
2681      * </ol>
2682      *
2683      * @return true if it has expired
2684      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2685      * @throws NullPointerException  if the element is null
2686      *                               todo this does not need to be synchronized
2687      */
2688     public final boolean isExpired(Element element) throws IllegalStateException, NullPointerException {
2689         checkStatus();
2690         return element.isExpired(configuration);
2691     }
2692 
2693 
2694     /***
2695      * Clones a cache. This is only legal if the cache has not been
2696      * initialized. At that point only primitives have been set and no
2697      * stores have been created.
2698      * <p/>
2699      * A new, empty, RegisteredEventListeners is created on clone.
2700      * <p/>
2701      *
2702      * @return an object of type {@link Cache}
2703      * @throws CloneNotSupportedException
2704      */
2705     @Override
2706     public final Cache clone() throws CloneNotSupportedException {
2707         if (compoundStore != null) {
2708             throw new CloneNotSupportedException("Cannot clone an initialized cache.");
2709         }
2710         Cache copy = (Cache) super.clone();
2711         // create new copies of the statistics
2712         copy.liveCacheStatisticsData = new LiveCacheStatisticsWrapper(copy);
2713         copy.sampledCacheStatistics = new SampledCacheStatisticsWrapper();
2714 
2715         copy.configuration = configuration.clone();
2716         copy.guid = createGuid();
2717         copy.cacheStatus = new CacheStatus();
2718         copy.cacheStatus.changeState(Status.STATUS_UNINITIALISED);
2719         copy.elementValueComparator = copy.configuration.getElementValueComparatorConfiguration().getElementComparatorInstance();
2720         copy.propertyChangeSupport = new PropertyChangeSupport(copy);
2721         copy.nonstopActiveDelegateHolder = new NonstopActiveDelegateHolderImpl(copy);
2722         for (PropertyChangeListener propertyChangeListener : propertyChangeSupport.getPropertyChangeListeners()) {
2723             copy.addPropertyChangeListener(propertyChangeListener);
2724         }
2725 
2726         RegisteredEventListeners registeredEventListenersFromCopy = copy.getCacheEventNotificationService();
2727         if (registeredEventListenersFromCopy == null || registeredEventListenersFromCopy.getCacheEventListeners().size() == 0) {
2728             copy.registeredEventListeners = new RegisteredEventListeners(copy);
2729         } else {
2730             copy.registeredEventListeners = new RegisteredEventListeners(copy);
2731             Set cacheEventListeners = registeredEventListeners.getCacheEventListeners();
2732             for (Object cacheEventListener1 : cacheEventListeners) {
2733                 CacheEventListener cacheEventListener = (CacheEventListener) cacheEventListener1;
2734                 CacheEventListener cacheEventListenerClone = (CacheEventListener) cacheEventListener.clone();
2735                 copy.registeredEventListeners.registerListener(cacheEventListenerClone);
2736             }
2737         }
2738 
2739 
2740         copy.registeredCacheExtensions = new CopyOnWriteArrayList<CacheExtension>();
2741         for (CacheExtension registeredCacheExtension : registeredCacheExtensions) {
2742             copy.registerCacheExtension(registeredCacheExtension.clone(copy));
2743         }
2744 
2745         copy.registeredCacheLoaders = new CopyOnWriteArrayList<CacheLoader>();
2746         for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
2747             copy.registerCacheLoader(registeredCacheLoader.clone(copy));
2748         }
2749 
2750         if (registeredCacheWriter != null) {
2751             copy.registerCacheWriter(registeredCacheWriter.clone(copy));
2752         }
2753 
2754         if (bootstrapCacheLoader != null) {
2755             BootstrapCacheLoader bootstrapCacheLoaderClone = (BootstrapCacheLoader) bootstrapCacheLoader.clone();
2756             copy.setBootstrapCacheLoader(bootstrapCacheLoaderClone);
2757         }
2758 
2759         return copy;
2760     }
2761 
2762     /***
2763      * Gets the internal Store.
2764      *
2765      * @return the Store referenced by this cache
2766      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2767      */
2768     final Store getStore() throws IllegalStateException {
2769         checkStatus();
2770         return compoundStore;
2771     }
2772 
2773     /***
2774      * Get the optional store management bean for this cache.
2775      */
2776     public final Object getStoreMBean() {
2777       return getStore().getMBean();
2778     }
2779 
2780     /***
2781      * Use this to access the service in order to register and unregister listeners
2782      *
2783      * @return the RegisteredEventListeners instance for this cache.
2784      */
2785     public final RegisteredEventListeners getCacheEventNotificationService() {
2786         return registeredEventListeners;
2787     }
2788 
2789 
2790     /***
2791      * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
2792      * <p>
2793      * Since no assertions are made about the state of the Element it is possible that the
2794      * Element is expired, but this method still returns true.
2795      *
2796      * @return true if an element matching the key is found in memory
2797      */
2798     public final boolean isElementInMemory(Serializable key) {
2799         return isElementInMemory((Object) key);
2800     }
2801 
2802     /***
2803      * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
2804      * <p>
2805      * Since no assertions are made about the state of the Element it is possible that the
2806      * Element is expired, but this method still returns true.
2807      *
2808      * @return true if an element matching the key is found in memory
2809      * @since 1.2
2810      */
2811     public final boolean isElementInMemory(Object key) {
2812         return compoundStore.containsKeyInMemory(key);
2813     }
2814 
2815     /***
2816      * Whether an Element is stored in the cache in off-heap memory, indicating an intermediate cost of retrieval.
2817      * <p>
2818      * Since no assertions are made about the state of the Element it is possible that the
2819      * Element is expired, but this method still returns true.
2820      *
2821      * @return true if an element matching the key is found in off-heap
2822      * @since 2.3
2823      */
2824     public final boolean isElementOffHeap(Object key) {
2825         return compoundStore.containsKeyOffHeap(key);
2826     }
2827 
2828     /***
2829      * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
2830      * <p>
2831      * Since no assertions are made about the state of the Element it is possible that the
2832      * Element is expired, but this method still returns true.
2833      *
2834      * @return true if an element matching the key is found in the diskStore
2835      */
2836     public final boolean isElementOnDisk(Serializable key) {
2837         return isElementOnDisk((Object) key);
2838     }
2839 
2840     /***
2841      * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
2842      * <p>
2843      * Since no assertions are made about the state of the Element it is possible that the
2844      * Element is expired, but this method still returns true.
2845      *
2846      * @return true if an element matching the key is found in the diskStore
2847      * @since 1.2
2848      */
2849     public final boolean isElementOnDisk(Object key) {
2850         return compoundStore.containsKeyOnDisk(key);
2851     }
2852 
2853     /***
2854      * The GUID for this cache instance can be used to determine whether two cache instance references
2855      * are pointing to the same cache.
2856      *
2857      * @return the globally unique identifier for this cache instance. This is guaranteed to be unique.
2858      * @since 1.2
2859      */
2860     public final String getGuid() {
2861         return guid;
2862     }
2863 
2864     /***
2865      * Gets the CacheManager managing this cache. For a newly created cache this will be null until
2866      * it has been added to a CacheManager.
2867      *
2868      * @return the manager or null if there is none
2869      */
2870     public final CacheManager getCacheManager() {
2871         return cacheManager;
2872     }
2873 
2874 
2875     /***
2876      * Resets statistics counters back to 0.
2877      *
2878      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2879      */
2880     public void clearStatistics() throws IllegalStateException {
2881         checkStatus();
2882         liveCacheStatisticsData.clearStatistics();
2883         sampledCacheStatistics.clearStatistics();
2884         registeredEventListeners.clearCounters();
2885     }
2886 
2887     /***
2888      * Accurately measuring statistics can be expensive. Returns the current accuracy setting.
2889      *
2890      * @return one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
2891      */
2892     public int getStatisticsAccuracy() {
2893         return getLiveCacheStatistics().getStatisticsAccuracy();
2894     }
2895 
2896     /***
2897      * Sets the statistics accuracy.
2898      *
2899      * @param statisticsAccuracy one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
2900      */
2901     public void setStatisticsAccuracy(int statisticsAccuracy) {
2902         int oldValue = getStatisticsAccuracy();
2903         if (statisticsAccuracy != oldValue) {
2904             liveCacheStatisticsData.setStatisticsAccuracy(statisticsAccuracy);
2905             firePropertyChange("StatisticsAccuracy", oldValue, statisticsAccuracy);
2906         }
2907     }
2908 
2909     /***
2910      * Causes all elements stored in the Cache to be synchronously checked for expiry, and if expired, evicted.
2911      */
2912     public void evictExpiredElements() {
2913         compoundStore.expireElements();
2914     }
2915 
2916     /***
2917      * An inexpensive check to see if the key exists in the cache.
2918      * <p/>
2919      * This method is not synchronized. It is possible that an element may exist in the cache and be removed
2920      * before the check gets to it, or vice versa.  Since no assertions are made about the state of the Element
2921      * it is possible that the Element is expired, but this method still returns true.
2922 
2923      *
2924      * @param key the key to check.
2925      * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
2926      */
2927     public boolean isKeyInCache(Object key) {
2928         if (key == null) {
2929             return false;
2930         }
2931         return isElementInMemory(key) || isElementOffHeap(key) || isElementOnDisk(key);
2932     }
2933 
2934     /***
2935      * An extremely expensive check to see if the value exists in the cache. This implementation is O(n). Ehcache
2936      * is not designed for efficient access in this manner.
2937      * <p/>
2938      * This method is not synchronized. It is possible that an element may exist in the cache and be removed
2939      * before the check gets to it, or vice versa. Because it is slow to execute the probability of that this will
2940      * have happened.
2941      *
2942      * @param value to check for
2943      * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
2944      */
2945     public boolean isValueInCache(Object value) {
2946         for (Object key : getKeys()) {
2947             Element element = get(key);
2948             if (element != null) {
2949                 Object elementValue = element.getValue();
2950                 if (elementValue == null) {
2951                     if (value == null) {
2952                         return true;
2953                     }
2954                 } else {
2955                     if (elementValue.equals(value)) {
2956                         return true;
2957                     }
2958                 }
2959             }
2960         }
2961         return false;
2962     }
2963 
2964     /***
2965      * {@inheritDoc}
2966      * <p/>
2967      * Note, the {@link #getSize} method will have the same value as the size
2968      * reported by Statistics for the statistics accuracy of
2969      * {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}.
2970      */
2971     public Statistics getStatistics() throws IllegalStateException {
2972         int size = getSizeBasedOnAccuracy(getLiveCacheStatistics()
2973                 .getStatisticsAccuracy());
2974         return new Statistics(this, getLiveCacheStatistics()
2975                 .getStatisticsAccuracy(), getLiveCacheStatistics()
2976                 .getCacheHitCount(), getLiveCacheStatistics()
2977                 .getOnDiskHitCount(), getLiveCacheStatistics()
2978                 .getOffHeapHitCount(), getLiveCacheStatistics()
2979                 .getInMemoryHitCount(), getLiveCacheStatistics()
2980                 .getCacheMissCount(), getLiveCacheStatistics()
2981                 .getOnDiskMissCount(), getLiveCacheStatistics()
2982                 .getOffHeapMissCount(), getLiveCacheStatistics()
2983                 .getInMemoryMissCount(), size, getAverageGetTime(),
2984                 getLiveCacheStatistics().getEvictedCount(),
2985                 getMemoryStoreSize(), getOffHeapStoreSize(), getDiskStoreSize(),
2986                 getSearchesPerSecond(), getAverageSearchTime(), getLiveCacheStatistics().getWriterQueueLength());
2987     }
2988 
2989     /***
2990      * {@inheritDoc}
2991      */
2992     public long getAverageSearchTime() {
2993         return sampledCacheStatistics.getAverageSearchTime();
2994     }
2995 
2996     /***
2997      * {@inheritDoc}
2998      */
2999     public long getSearchesPerSecond() {
3000         return sampledCacheStatistics.getSearchesPerSecond();
3001     }
3002 
3003     /***
3004      * For use by CacheManager.
3005      *
3006      * @param cacheManager the CacheManager for this cache to use.
3007      */
3008     public void setCacheManager(CacheManager cacheManager) {
3009         CacheManager oldValue = getCacheManager();
3010         this.cacheManager = cacheManager;
3011         firePropertyChange("CacheManager", oldValue, cacheManager);
3012     }
3013 
3014     /***
3015      * Accessor for the BootstrapCacheLoader associated with this cache. For testing purposes.
3016      */
3017     public BootstrapCacheLoader getBootstrapCacheLoader() {
3018         return bootstrapCacheLoader;
3019     }
3020 
3021     /***
3022      * Sets the bootstrap cache loader.
3023      *
3024      * @param bootstrapCacheLoader the loader to be used
3025      * @throws CacheException if this method is called after the cache is initialized
3026      */
3027     public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader) throws CacheException {
3028         if (!cacheStatus.isUninitialized()) {
3029             throw new CacheException("A bootstrap cache loader can only be set before the cache is initialized. "
3030                     + configuration.getName());
3031         }
3032         BootstrapCacheLoader oldValue = getBootstrapCacheLoader();
3033         this.bootstrapCacheLoader = bootstrapCacheLoader;
3034         firePropertyChange("BootstrapCacheLoader", oldValue, bootstrapCacheLoader);
3035     }
3036 
3037     /***
3038      * DiskStore paths can conflict between CacheManager instances. This method allows the path to be changed.
3039      *
3040      * @param diskStorePath the new path to be used.
3041      * @throws CacheException if this method is called after the cache is initialized
3042      */
3043     public void setDiskStorePath(String diskStorePath) throws CacheException {
3044         if (!cacheStatus.isUninitialized()) {
3045             throw new CacheException("A DiskStore path can only be set before the cache is initialized. "
3046                     + configuration.getName());
3047         }
3048         String oldValue = this.diskStorePath;
3049         synchronized (this) {
3050             this.diskStorePath = diskStorePath;
3051         }
3052         firePropertyChange("DiskStorePath", oldValue, diskStorePath);
3053     }
3054 
3055     /***
3056      * An equals method which follows the contract of {@link Object#equals(Object)}
3057      * <p/>
3058      * An Cache is equal to another one if it implements Ehcache and has the same GUID.
3059      *
3060      * @param object the reference object with which to compare.
3061      * @return <code>true</code> if this object is the same as the obj
3062      *         argument; <code>false</code> otherwise.
3063      * @see #hashCode()
3064      * @see java.util.Hashtable
3065      */
3066     @Override
3067     public boolean equals(Object object) {
3068         if (object == null) {
3069             return false;
3070         }
3071         if (!(object instanceof Ehcache)) {
3072             return false;
3073         }
3074         Ehcache other = (Ehcache) object;
3075         return guid.equals(other.getGuid());
3076     }
3077 
3078     /***
3079      * Returns a hash code value for the object. This method is
3080      * supported for the benefit of hashtables such as those provided by
3081      * <code>java.util.Hashtable</code>.
3082      * <p/>
3083      * The general contract of <code>hashCode</code> is:
3084      * <ul>
3085      * <li>Whenever it is invoked on the same object more than once during
3086      * an execution of a Java application, the <tt>hashCode</tt> method
3087      * must consistently return the same integer, provided no information
3088      * used in <tt>equals</tt> comparisons on the object is modified.
3089      * This integer need not remain consistent from one execution of an
3090      * application to another execution of the same application.
3091      * <li>If two objects are equal according to the <tt>equals(Object)</tt>
3092      * method, then calling the <code>hashCode</code> method on each of
3093      * the two objects must produce the same integer result.
3094      * <li>It is <em>not</em> required that if two objects are unequal
3095      * according to the {@link Object#equals(Object)}
3096      * method, then calling the <tt>hashCode</tt> method on each of the
3097      * two objects must produce distinct integer results.  However, the
3098      * programmer should be aware that producing distinct integer results
3099      * for unequal objects may improve the performance of hashtables.
3100      * </ul>
3101      * <p/>
3102      * As much as is reasonably practical, the hashCode method defined by
3103      * class <tt>Object</tt> does return distinct integers for distinct
3104      * objects. (This is typically implemented by converting the internal
3105      * address of the object into an integer, but this implementation
3106      * technique is not required by the
3107      * Java(TM) programming language.)
3108      * <p/>
3109      * This implementation use the GUID of the cache.
3110      *
3111      * @return a hash code value for this object.
3112      * @see Object#equals(Object)
3113      * @see java.util.Hashtable
3114      */
3115     @Override
3116     public int hashCode() {
3117         return guid.hashCode();
3118     }
3119 
3120 
3121     /***
3122      * Create globally unique ID for this cache.
3123      */
3124     private String createGuid() {
3125         StringBuilder buffer = new StringBuilder().append(localhost).append("-").append(UUID.randomUUID());
3126         return buffer.toString();
3127     }
3128 
3129     /***
3130      * Register a {@link CacheExtension} with the cache. It will then be tied into the cache lifecycle.
3131      * <p/>
3132      * If the CacheExtension is not initialised, initialise it.
3133      */
3134     public void registerCacheExtension(CacheExtension cacheExtension) {
3135         registeredCacheExtensions.add(cacheExtension);
3136     }
3137 
3138     /***
3139      * @return the cache extensions as a live list
3140      */
3141     public List<CacheExtension> getRegisteredCacheExtensions() {
3142         return registeredCacheExtensions;
3143     }
3144 
3145 
3146     /***
3147      * Unregister a {@link CacheExtension} with the cache. It will then be detached from the cache lifecycle.
3148      */
3149     public void unregisterCacheExtension(CacheExtension cacheExtension) {
3150         cacheExtension.dispose();
3151         registeredCacheExtensions.remove(cacheExtension);
3152     }
3153 
3154 
3155     /***
3156      * The average get time in ms.
3157      */
3158     public float getAverageGetTime() {
3159         return getLiveCacheStatistics().getAverageGetTimeMillis();
3160     }
3161 
3162     /***
3163      * Sets an ExceptionHandler on the Cache. If one is already set, it is overwritten.
3164      * <p/>
3165      * The ExceptionHandler is only used if this Cache's methods are accessed using
3166      * {@link net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy}.
3167      *
3168      * @see net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy
3169      */
3170     public void setCacheExceptionHandler(CacheExceptionHandler cacheExceptionHandler) {
3171         CacheExceptionHandler oldValue = getCacheExceptionHandler();
3172         this.cacheExceptionHandler = cacheExceptionHandler;
3173         firePropertyChange("CacheExceptionHandler", oldValue, cacheExceptionHandler);
3174     }
3175 
3176     /***
3177      * Gets the ExceptionHandler on this Cache, or null if there isn't one.
3178      * <p/>
3179      * The ExceptionHandler is only used if this Cache's methods are accessed using
3180      * {@link net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy}.
3181      *
3182      * @see net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy
3183      */
3184     public CacheExceptionHandler getCacheExceptionHandler() {
3185         return cacheExceptionHandler;
3186     }
3187 
3188     /***
3189      * Register a {@link CacheLoader} with the cache. It will then be tied into the cache lifecycle.
3190      * <p/>
3191      * If the CacheLoader is not initialised, initialise it.
3192      *
3193      * @param cacheLoader A Cache Loader to register
3194      */
3195     public void registerCacheLoader(CacheLoader cacheLoader) {
3196         registeredCacheLoaders.add(cacheLoader);
3197     }
3198 
3199     /***
3200      * Unregister a {@link CacheLoader} with the cache. It will then be detached from the cache lifecycle.
3201      *
3202      * @param cacheLoader A Cache Loader to unregister
3203      */
3204     public void unregisterCacheLoader(CacheLoader cacheLoader) {
3205         registeredCacheLoaders.remove(cacheLoader);
3206     }
3207 
3208 
3209     /***
3210      * @return the cache loaders as a live list
3211      */
3212     public List<CacheLoader> getRegisteredCacheLoaders() {
3213         return registeredCacheLoaders;
3214     }
3215 
3216     /***
3217      * {@inheritDoc}
3218      */
3219     public void registerCacheWriter(CacheWriter cacheWriter) {
3220         synchronized (this) {
3221             this.registeredCacheWriter = cacheWriter;
3222             if (cacheStatus.isAlive()) {
3223                 initialiseRegisteredCacheWriter();
3224             }
3225         }
3226         initialiseCacheWriterManager(false);
3227     }
3228 
3229     /***
3230      * {@inheritDoc}
3231      */
3232     public void unregisterCacheWriter() {
3233         if (cacheWriterManagerInitFlag.get()) {
3234             throw new CacheException("Cache: " + configuration.getName() + " has its cache writer being unregistered " +
3235                     "after it was already initialised.");
3236         }
3237         this.registeredCacheWriter = null;
3238     }
3239 
3240     /***
3241      * {@inheritDoc}
3242      */
3243     public CacheWriter getRegisteredCacheWriter() {
3244         return this.registeredCacheWriter;
3245     }
3246 
3247     /***
3248      * Does the asynchronous put into the cache of the asynchronously loaded value.
3249      *
3250      * @param key the key to load
3251      * @param specificLoader a specific loader to use. If null the default loader is used.
3252      * @param argument the argument to pass to the writer
3253      * @return a Future which can be used to monitor execution
3254      */
3255     Future asynchronousPut(final Object key, final CacheLoader specificLoader, final Object argument) {
3256         return getExecutorService().submit(new Runnable() {
3257 
3258             /***
3259              * Calls the CacheLoader and puts the result in the Cache
3260              */
3261             public void run() throws CacheException {
3262                 try {
3263                     //Test to see if it has turned up in the meantime
3264                     boolean existsOnRun = isKeyInCache(key);
3265                     if (!existsOnRun) {
3266                         Object value = loadValueUsingLoader(key, specificLoader, argument);
3267                         if (value != null) {
3268                             put(new Element(key, value), false);
3269                         }
3270                     }
3271                 } catch (RuntimeException e) {
3272                     if (LOG.isDebugEnabled()) {
3273                         LOG.debug("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
3274                     }
3275                     throw new CacheException("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
3276                 }
3277             }
3278         });
3279     }
3280 
3281     /***
3282      * Does the asynchronous loading. But doesn't put it into the cache
3283      *
3284      * @param key the key to load
3285      * @param specificLoader a specific loader to use. If null the default loader is used.
3286      * @param argument the argument to pass to the writer
3287      * @return a Future which can be used to monitor execution
3288      */
3289     Future<AtomicReference<Object>> asynchronousLoad(final Object key, final CacheLoader specificLoader, final Object argument) {
3290         final AtomicReference<Object> result = new AtomicReference<Object>();
3291         return getExecutorService().submit(new Runnable() {
3292 
3293             /***
3294              * Calls the CacheLoader and puts the result in the Cache
3295              */
3296             public void run() throws CacheException {
3297                 try {
3298                     //Test to see if it has turned up in the meantime
3299                     boolean existsOnRun = isKeyInCache(key);
3300                     if (!existsOnRun) {
3301                         Object value = loadValueUsingLoader(key, specificLoader, argument);
3302                         if (value != null) {
3303                             result.set(value);
3304                         }
3305                     }
3306                 } catch (RuntimeException e) {
3307                     if (LOG.isDebugEnabled()) {
3308                         LOG.debug("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
3309                     }
3310                     throw new CacheException("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
3311                 }
3312             }
3313         }, result);
3314     }
3315 
3316     /***
3317      * Will attempt to load the value for a key, either using the passedin loader, or falling back to registered ones
3318      * @param key the key to load for
3319      * @param specificLoader the loader to use, can be null to fallback to Cache registered loaders
3320      * @param argument the argument to pass the loader
3321      * @return null if not present in the underlying SoR or if no loader available, otherwise the loaded object
3322      */
3323     private Object loadValueUsingLoader(final Object key, final CacheLoader specificLoader, final Object argument) {
3324         Object value = null;
3325         if (specificLoader != null) {
3326             if (argument == null) {
3327                 value = specificLoader.load(key);
3328             } else {
3329                 value = specificLoader.load(key, argument);
3330             }
3331         } else if (!registeredCacheLoaders.isEmpty()) {
3332             value = loadWithRegisteredLoaders(argument, key);
3333         }
3334         return value;
3335     }
3336 
3337     private Object loadWithRegisteredLoaders(Object argument, Object key) throws CacheException {
3338 
3339         Object value = null;
3340 
3341         if (argument == null) {
3342             for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
3343                 value = registeredCacheLoader.load(key);
3344                 if (value != null) {
3345                     break;
3346                 }
3347             }
3348         } else {
3349             for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
3350                 value = registeredCacheLoader.load(key, argument);
3351                 if (value != null) {
3352                     break;
3353                 }
3354             }
3355         }
3356         return value;
3357     }
3358 
3359 
3360     /***
3361      * Creates a future to perform the load
3362      *
3363      * @param keys
3364      * @param argument the loader argument
3365      * @return a Future which can be used to monitor execution
3366      */
3367     Future asynchronousLoadAll(final Collection keys, final Object argument) {
3368         return getExecutorService().submit(new Runnable() {
3369             /***
3370              * Calls the CacheLoader and puts the result in the Cache
3371              */
3372             public void run() {
3373                 try {
3374                     Set<Object> nonLoadedKeys = new HashSet<Object>();
3375                     for (Object key : keys) {
3376                         if (!isKeyInCache(key)) {
3377                             nonLoadedKeys.add(key);
3378                         }
3379                     }
3380                     Map<?, ?> map = loadWithRegisteredLoaders(argument, nonLoadedKeys);
3381                     for (Entry<?, ?> e : map.entrySet()) {
3382                         put(new Element(e.getKey(), e.getValue()));
3383                     }
3384                 } catch (Throwable e) {
3385                     if (LOG.isErrorEnabled()) {
3386                         LOG.error("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
3387                     }
3388                 }
3389             }
3390         });
3391     }
3392 
3393     /***
3394      * Does the asynchronous loading.
3395      *
3396      * @param argument      the loader argument
3397      * @param nonLoadedKeys the Set of keys that are already in the Cache
3398      * @return A map of loaded elements
3399      */
3400     Map loadWithRegisteredLoaders(Object argument, Set<Object> nonLoadedKeys) {
3401         Map result = new HashMap();
3402         for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
3403             if (nonLoadedKeys.isEmpty()) {
3404                 break;
3405             }
3406 
3407             Map resultForThisCacheLoader = null;
3408             if (argument == null) {
3409                 resultForThisCacheLoader = registeredCacheLoader.loadAll(nonLoadedKeys);
3410             } else {
3411                 resultForThisCacheLoader = registeredCacheLoader.loadAll(nonLoadedKeys, argument);
3412             }
3413             if (resultForThisCacheLoader != null) {
3414                 nonLoadedKeys.removeAll(resultForThisCacheLoader.keySet());
3415                 result.putAll(resultForThisCacheLoader);
3416             }
3417         }
3418         return result;
3419     }
3420 
3421     /***
3422      * @return Gets the executor service. This is not publically accessible.
3423      */
3424     ExecutorService getExecutorService() {
3425         if (executorService == null) {
3426             synchronized (this) {
3427                 if (VmUtils.isInGoogleAppEngine()) {
3428                     // no Thread support. Run all tasks on the caller thread
3429                     executorService = new AbstractExecutorService() {
3430                         /*** {@inheritDoc} */
3431                         public void execute(Runnable command) {
3432                             command.run();
3433                         }
3434 
3435                         /*** {@inheritDoc} */
3436                         public List<Runnable> shutdownNow() {
3437                             return Collections.emptyList();
3438                         }
3439 
3440                         /*** {@inheritDoc} */
3441                         public void shutdown() {
3442                         }
3443 
3444                         /*** {@inheritDoc} */
3445                         public boolean isTerminated() {
3446                             return isShutdown();
3447                         }
3448 
3449                         /*** {@inheritDoc} */
3450                         public boolean isShutdown() {
3451                             return false;
3452                         }
3453 
3454                         /*** {@inheritDoc} */
3455                         public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
3456                             return true;
3457                         }
3458                     };
3459                 } else {
3460                     // we can create Threads
3461                     executorService = new ThreadPoolExecutor(EXECUTOR_CORE_POOL_SIZE, EXECUTOR_MAXIMUM_POOL_SIZE, EXECUTOR_KEEP_ALIVE_TIME,
3462                             TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), new NamedThreadFactory("Cache Executor Service"));
3463                 }
3464             }
3465         }
3466         return executorService;
3467     }
3468 
3469 
3470     /***
3471      * Whether this cache is disabled. "Disabled" means:
3472      * <ol>
3473      * <li>bootstrap is disabled</li>
3474      * <li>puts are discarded</li>
3475      * <li>putQuiets are discarded</li>
3476      * <li>gets return null</li>
3477      * </ol>
3478      * In all other respects the cache continues as it is.
3479      * <p/>
3480      * You can disable and enable a cache programmatically through the {@link #setDisabled(boolean)} method.
3481      * <p/>
3482      * By default caches are enabled on creation, unless the <code>net.sf.ehcache.disabled</code> system
3483      * property is set.
3484      *
3485      * @return true if the cache is disabled.
3486      * @see #NET_SF_EHCACHE_DISABLED ?
3487      */
3488     public boolean isDisabled() {
3489         return disabled;
3490     }
3491 
3492     /***
3493      * Disables or enables this cache. This call overrides the previous value of disabled, even if the
3494      * <code>net.sf.ehcache.disabled</code> system property is set
3495      * <p/>
3496      *
3497      * @param disabled true if you wish to disable, false to enable
3498      * @see #isDisabled()
3499      */
3500     public void setDisabled(boolean disabled) {
3501         if (allowDisable) {
3502             boolean oldValue = isDisabled();
3503             if (oldValue != disabled) {
3504                 synchronized (this) {
3505                     this.disabled = disabled;
3506                 }
3507                 firePropertyChange("Disabled", oldValue, disabled);
3508             }
3509         } else {
3510             throw new CacheException("Dynamic cache features are disabled");
3511         }
3512     }
3513 
3514     /***
3515      * @return the current in-memory eviction policy. This may not be the configured policy, if it has been
3516      *         dynamically set.
3517      */
3518     public Policy getMemoryStoreEvictionPolicy() {
3519         return compoundStore.getInMemoryEvictionPolicy();
3520     }
3521 
3522     /***
3523      * Sets the eviction policy strategy. The Cache will use a policy at startup. There
3524      * are three policies which can be configured: LRU, LFU and FIFO. However many other
3525      * policies are possible. That the policy has access to the whole element enables policies
3526      * based on the key, value, metadata, statistics, or a combination of any of the above.
3527      * It is safe to change the policy of a store at any time. The new policy takes effect
3528      * immediately.
3529      *
3530      * @param policy the new policy
3531      */
3532     public void setMemoryStoreEvictionPolicy(Policy policy) {
3533         Policy oldValue = getMemoryStoreEvictionPolicy();
3534         compoundStore.setInMemoryEvictionPolicy(policy);
3535         firePropertyChange("MemoryStoreEvictionPolicy", oldValue, policy);
3536     }
3537 
3538     /***
3539      * {@inheritDoc}
3540      */
3541     public LiveCacheStatistics getLiveCacheStatistics()
3542             throws IllegalStateException {
3543         checkStatus();
3544         return liveCacheStatisticsData;
3545     }
3546 
3547     private LiveCacheStatistics getLiveCacheStatisticsNoCheck() {
3548         return liveCacheStatisticsData;
3549     }
3550 
3551     /***
3552      * {@inheritDoc}
3553      */
3554     public void registerCacheUsageListener(CacheUsageListener cacheUsageListener)
3555             throws IllegalStateException {
3556         checkStatus();
3557         liveCacheStatisticsData.registerCacheUsageListener(cacheUsageListener);
3558     }
3559 
3560     /***
3561      * {@inheritDoc}
3562      */
3563     public void removeCacheUsageListener(CacheUsageListener cacheUsageListener)
3564             throws IllegalStateException {
3565         checkStatus();
3566         liveCacheStatisticsData.removeCacheUsageListener(cacheUsageListener);
3567     }
3568 
3569     /***
3570      * {@inheritDoc}
3571      */
3572     public boolean isStatisticsEnabled() {
3573         return getLiveCacheStatistics().isStatisticsEnabled();
3574     }
3575 
3576     /***
3577      * {@inheritDoc}
3578      */
3579     public void setStatisticsEnabled(boolean enableStatistics) {
3580         boolean oldValue = isStatisticsEnabled();
3581         if (oldValue != enableStatistics) {
3582             liveCacheStatisticsData.setStatisticsEnabled(enableStatistics);
3583             if (!enableStatistics) {
3584                 setSampledStatisticsEnabled(false);
3585             }
3586             firePropertyChange("StatisticsEnabled", oldValue, enableStatistics);
3587         }
3588     }
3589 
3590     /***
3591      * {@inheritDoc}
3592      */
3593     public SampledCacheStatistics getSampledCacheStatistics() {
3594         return sampledCacheStatistics;
3595     }
3596 
3597     /***
3598      * {@inheritDoc}
3599      */
3600     public void setSampledStatisticsEnabled(final boolean enableStatistics) {
3601         if (cacheManager == null) {
3602             throw new IllegalStateException(
3603                     "You must add the cache to a CacheManager before enabling/disabling sampled statistics.");
3604         }
3605         boolean oldValue = isSampledStatisticsEnabled();
3606         if (oldValue != enableStatistics) {
3607             if (enableStatistics) {
3608                 setStatisticsEnabled(true);
3609                 sampledCacheStatistics.enableSampledStatistics(cacheManager.getTimer());
3610             } else {
3611                 sampledCacheStatistics.disableSampledStatistics();
3612             }
3613             firePropertyChange("SampledStatisticsEnabled", oldValue, enableStatistics);
3614         }
3615     }
3616 
3617     /***
3618      * {@inheritDoc}
3619      *
3620      * @see net.sf.ehcache.Ehcache#isSampledStatisticsEnabled()
3621      */
3622     public boolean isSampledStatisticsEnabled() {
3623         return sampledCacheStatistics.isSampledStatisticsEnabled();
3624     }
3625 
3626     /***
3627      * {@inheritDoc}
3628      */
3629     public Object getInternalContext() {
3630         return compoundStore.getInternalContext();
3631     }
3632 
3633     /***
3634      * {@inheritDoc}
3635      */
3636     public void disableDynamicFeatures() {
3637         configuration.freezeConfiguration();
3638         allowDisable = false;
3639     }
3640 
3641     /***
3642      * {@inheritDoc}
3643      * @deprecated use {@link #isClusterBulkLoadEnabled()} instead
3644      */
3645     @Deprecated
3646     public boolean isClusterCoherent() {
3647         return !this.isClusterBulkLoadEnabled();
3648     }
3649 
3650     /***
3651      * {@inheritDoc}
3652      * @deprecated use {@link #isNodeBulkLoadEnabled()} instead
3653      */
3654     @Deprecated
3655     public boolean isNodeCoherent() {
3656         return !this.isNodeBulkLoadEnabled();
3657     }
3658 
3659     /***
3660      * {@inheritDoc}
3661      * @deprecated use {@link #setNodeBulkLoadEnabled(boolean)} instead
3662      */
3663     @Deprecated
3664     public void setNodeCoherent(boolean coherent) {
3665         this.setNodeBulkLoadEnabled(!coherent);
3666     }
3667 
3668     /***
3669      * {@inheritDoc}
3670      * @deprecated use {@link #waitUntilClusterBulkLoadComplete()} instead
3671      */
3672     @Deprecated
3673     public void waitUntilClusterCoherent() {
3674         this.waitUntilClusterBulkLoadComplete();
3675     }
3676 
3677     // PropertyChangeSupport
3678 
3679     /***
3680      * @param listener
3681      */
3682     public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
3683       if (listener != null && propertyChangeSupport != null) {
3684         propertyChangeSupport.removePropertyChangeListener(listener);
3685         propertyChangeSupport.addPropertyChangeListener(listener);
3686       }
3687     }
3688 
3689     /***
3690      * @param listener
3691      */
3692     public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
3693       if (listener != null && propertyChangeSupport != null) {
3694         propertyChangeSupport.removePropertyChangeListener(listener);
3695       }
3696     }
3697 
3698     /***
3699      * @param propertyName
3700      * @param oldValue
3701      * @param newValue
3702      */
3703     public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
3704       PropertyChangeSupport pcs;
3705       synchronized (this) {
3706         pcs = propertyChangeSupport;
3707       }
3708       if (pcs != null && (oldValue != null || newValue != null)) {
3709         pcs.firePropertyChange(propertyName, oldValue, newValue);
3710       }
3711     }
3712 
3713     /***
3714      * {@inheritDoc}
3715      */
3716     public Element putIfAbsent(Element element) throws NullPointerException {
3717         checkStatus();
3718 
3719         if (element.getObjectKey() == null) {
3720             throw new NullPointerException();
3721         }
3722 
3723         if (disabled) {
3724             return null;
3725         }
3726 
3727         //this guard currently ensures reasonable behavior on expiring elements
3728         getQuiet(element.getObjectKey());
3729 
3730         element.resetAccessStatistics();
3731         applyDefaultsToElementWithoutLifespanSet(element);
3732         backOffIfDiskSpoolFull();
3733 
3734         Element result = compoundStore.putIfAbsent(element);
3735         if (result == null) {
3736             notifyPutInternalListeners(element, false, false);
3737         }
3738         return result;
3739     }
3740 
3741     /***
3742      * {@inheritDoc}
3743      */
3744     public boolean removeElement(Element element) throws NullPointerException {
3745         checkStatus();
3746 
3747         if (element.getObjectKey() == null) {
3748             throw new NullPointerException();
3749         }
3750 
3751         if (disabled) {
3752             return false;
3753         }
3754 
3755         // this guard currently ensures reasonable behavior on expiring elements
3756         getQuiet(element.getObjectKey());
3757 
3758         Element result = compoundStore.removeElement(element, elementValueComparator);
3759 
3760         // FIXME shouldn't this be done only if result != null
3761         notifyRemoveInternalListeners(element.getObjectKey(), false, true, false, result);
3762         return result != null;
3763     }
3764 
3765     /***
3766      * {@inheritDoc}
3767      */
3768     public boolean replace(Element old, Element element) throws NullPointerException, IllegalArgumentException {
3769         checkStatus();
3770 
3771         if (old.getObjectKey() == null || element.getObjectKey() == null) {
3772             throw new NullPointerException();
3773         }
3774         if (!old.getObjectKey().equals(element.getObjectKey())) {
3775             throw new IllegalArgumentException("The keys for the element arguments to replace must be equal");
3776         }
3777 
3778         if (disabled) {
3779             return false;
3780         }
3781 
3782         getQuiet(old.getObjectKey());
3783 
3784         element.resetAccessStatistics();
3785         applyDefaultsToElementWithoutLifespanSet(element);
3786         backOffIfDiskSpoolFull();
3787 
3788         boolean result = compoundStore.replace(old, element, elementValueComparator);
3789 
3790         if (result) {
3791             element.updateUpdateStatistics();
3792             notifyPutInternalListeners(element, false, true);
3793         }
3794         return result;
3795     }
3796 
3797     /***
3798      * {@inheritDoc}
3799      */
3800     public Element replace(Element element) throws NullPointerException {
3801         checkStatus();
3802 
3803         if (element.getObjectKey() == null) {
3804             throw new NullPointerException();
3805         }
3806 
3807         if (disabled) {
3808             return null;
3809         }
3810 
3811         getQuiet(element.getObjectKey());
3812 
3813         element.resetAccessStatistics();
3814         applyDefaultsToElementWithoutLifespanSet(element);
3815         backOffIfDiskSpoolFull();
3816 
3817         Element result = compoundStore.replace(element);
3818         if (result != null) {
3819             element.updateUpdateStatistics();
3820             notifyPutInternalListeners(element, false, true);
3821         }
3822         return result;
3823     }
3824 
3825     /***
3826      * {@inheritDoc}
3827      *
3828      * @see net.sf.ehcache.store.StoreListener#clusterCoherent(boolean)
3829      */
3830     public void clusterCoherent(boolean clusterCoherent) {
3831         firePropertyChange("ClusterCoherent", !clusterCoherent, clusterCoherent);
3832     }
3833 
3834 
3835     /***
3836      * {@inheritDoc}
3837      */
3838     public <T> Attribute<T> getSearchAttribute(String attributeName) throws CacheException {
3839         // We don't trust config here since the store is the real authority
3840         Attribute<T> searchAttribute = compoundStore.getSearchAttribute(attributeName);
3841 
3842         if (searchAttribute == null) {
3843             final String msg;
3844             if (attributeName.equals(Query.KEY.getAttributeName())) {
3845                 msg = "Key search attribute is disabled for cache [" + getName() + "]. It can be enabled with <searchable keys=\"true\"...";
3846             } else if (attributeName.equals(Query.VALUE.getAttributeName())) {
3847                 msg = "Value search attribute is disabled for cache [" + getName() + "]. It can be enabled with <searchable values=\"true\"...";
3848             } else {
3849                 msg = "No such search attribute [" + attributeName + "] defined for this cache [" + getName() + "]";
3850             }
3851 
3852             throw new CacheException(msg);
3853         }
3854 
3855         return searchAttribute;
3856     }
3857 
3858     /***
3859      * {@inheritDoc}
3860      */
3861     public Query createQuery() {
3862         if (!isSearchable()) {
3863             throw new CacheException("This cache is not configured for search");
3864         }
3865         return new CacheQuery(this);
3866     }
3867 
3868     /***
3869      * Execute the given query
3870      *
3871      * @param query query to execute
3872      * @return query results
3873      */
3874     Results executeQuery(StoreQuery query) throws SearchException {
3875 
3876         if (!query.requestsKeys() && !query.requestsValues() && query.requestedAttributes().isEmpty() && query.getAggregatorInstances().isEmpty()) {
3877             String msg = "No results specified. " +
3878             "Please specify one or more of includeKeys(), includeValues(), includeAggregator() or includeAttribute()";
3879             throw new SearchException(msg);
3880         }
3881 
3882         if (isStatisticsEnabled()) {
3883             long start = System.currentTimeMillis();
3884             Results results = this.compoundStore.executeQuery(query);
3885             sampledCacheStatistics.notifyCacheSearch(System.currentTimeMillis() - start);
3886             return results;
3887         }
3888 
3889         return this.compoundStore.executeQuery(query);
3890     }
3891 
3892     /***
3893      * {@inheritDoc}
3894      */
3895     public boolean isSearchable() {
3896         return configuration.isSearchable();
3897     }
3898 
3899     /***
3900      * Start cluster rejoin
3901      */
3902     void clusterRejoinStarted() {
3903         try {
3904             nonstopActiveDelegateHolder.getUnderlyingTerracottaStore().dispose();
3905         } catch (Exception e) {
3906             LOG.info("Ignoring exception while disposing old store on rejoin - " + e.getMessage());
3907         }
3908         cacheStatus.clusterRejoinInProgress();
3909     }
3910 
3911     /***
3912      * Complete cluster rejoin
3913      */
3914     void clusterRejoinComplete() {
3915         // initialize again
3916         initialise();
3917         cacheStatus.clusterRejoinComplete();
3918         if (compoundStore instanceof RejoinAwareNonstopStore) {
3919             ((RejoinAwareNonstopStore) compoundStore).clusterRejoined();
3920         }
3921     }
3922 
3923     /***
3924      * Gets the lock for a given key
3925      *
3926      * @param key
3927      * @return the lock object for the passed in key
3928      */
3929     protected Sync getLockForKey(final Object key) {
3930         return lockProvider.getSyncForKey(key);
3931     }
3932 
3933     private void acquireLockOnKey(Object key, LockType lockType) {
3934         Sync s = getLockForKey(key);
3935         s.lock(lockType);
3936     }
3937 
3938     private void releaseLockOnKey(Object key, LockType lockType) {
3939         Sync s = getLockForKey(key);
3940         s.unlock(lockType);
3941     }
3942 
3943     /***
3944      * Acquires the proper read lock for a given cache key
3945      *
3946      * @param key - The key that retrieves a value that you want to protect via locking
3947      */
3948     public void acquireReadLockOnKey(Object key) {
3949         this.acquireLockOnKey(key, LockType.READ);
3950     }
3951 
3952     /***
3953      * Acquires the proper write lock for a given cache key
3954      *
3955      * @param key - The key that retrieves a value that you want to protect via locking
3956      */
3957     public void acquireWriteLockOnKey(Object key) {
3958         this.acquireLockOnKey(key, LockType.WRITE);
3959     }
3960 
3961     /***
3962      * Try to get a read lock on a given key. If can't get it in timeout millis then
3963      * return a boolean telling that it didn't get the lock
3964      *
3965      * @param key - The key that retrieves a value that you want to protect via locking
3966      * @param timeout - millis until giveup on getting the lock
3967      * @return whether the lock was awarded
3968      * @throws InterruptedException
3969      */
3970     public boolean tryReadLockOnKey(Object key, long timeout) throws InterruptedException {
3971         Sync s = getLockForKey(key);
3972         return s.tryLock(LockType.READ, timeout);
3973     }
3974 
3975     /***
3976      * Try to get a write lock on a given key. If can't get it in timeout millis then
3977      * return a boolean telling that it didn't get the lock
3978      *
3979      * @param key - The key that retrieves a value that you want to protect via locking
3980      * @param timeout - millis until giveup on getting the lock
3981      * @return whether the lock was awarded
3982      * @throws InterruptedException
3983      */
3984     public boolean tryWriteLockOnKey(Object key, long timeout) throws InterruptedException {
3985         Sync s = getLockForKey(key);
3986         return s.tryLock(LockType.WRITE, timeout);
3987     }
3988 
3989     /***
3990      * Release a held read lock for the passed in key
3991      *
3992      * @param key - The key that retrieves a value that you want to protect via locking
3993      */
3994     public void releaseReadLockOnKey(Object key) {
3995         releaseLockOnKey(key, LockType.READ);
3996     }
3997 
3998     /***
3999      * Release a held write lock for the passed in key
4000      *
4001      * @param key - The key that retrieves a value that you want to protect via locking
4002      */
4003     public void releaseWriteLockOnKey(Object key) {
4004         releaseLockOnKey(key, LockType.WRITE);
4005     }
4006 
4007 
4008     /***
4009      * {@inheritDoc}
4010      */
4011     public boolean isReadLockedByCurrentThread(Object key) {
4012         return getLockForKey(key).isHeldByCurrentThread(LockType.READ);
4013     }
4014 
4015     /***
4016      * {@inheritDoc}
4017      */
4018     public boolean isWriteLockedByCurrentThread(Object key) {
4019         return getLockForKey(key).isHeldByCurrentThread(LockType.WRITE);
4020     }
4021 
4022     /***
4023      * {@inheritDoc}
4024      */
4025     public boolean isClusterBulkLoadEnabled() throws UnsupportedOperationException, TerracottaNotRunningException {
4026         return !compoundStore.isClusterCoherent();
4027     }
4028 
4029     /***
4030      * {@inheritDoc}
4031      */
4032     public boolean isNodeBulkLoadEnabled() throws UnsupportedOperationException, TerracottaNotRunningException {
4033         return !compoundStore.isNodeCoherent();
4034     }
4035 
4036     /***
4037      * {@inheritDoc}
4038      */
4039     public void setNodeBulkLoadEnabled(boolean enabledBulkLoad) throws UnsupportedOperationException, TerracottaNotRunningException {
4040         final boolean oldValue = isNodeBulkLoadEnabled();
4041         if (oldValue != enabledBulkLoad) {
4042             compoundStore.setNodeCoherent(!enabledBulkLoad);
4043             nonstopActiveDelegateHolder.nodeBulkLoadChanged(enabledBulkLoad);
4044             firePropertyChange("NodeCoherent", oldValue, enabledBulkLoad);
4045         }
4046     }
4047 
4048     /***
4049      * {@inheritDoc}
4050      */
4051     public void waitUntilClusterBulkLoadComplete() throws UnsupportedOperationException, TerracottaNotRunningException {
4052         try {
4053             compoundStore.waitUntilClusterCoherent();
4054         } catch (InterruptedException e) {
4055             // re-throw as cacheException
4056             throw new CacheException(e);
4057         }
4058     }
4059 
4060     /***
4061      * Private class maintaining status of the cache
4062      *
4063      * @author Abhishek Sanoujam
4064      *
4065      */
4066     private static class CacheStatus {
4067         private volatile Status status = Status.STATUS_UNINITIALISED;
4068         private final AtomicBoolean clusterRejoinInProgress = new AtomicBoolean(false);
4069 
4070         private void clusterRejoinComplete() {
4071             clusterRejoinInProgress.set(false);
4072         }
4073 
4074         public void checkAlive(CacheConfiguration configuration) {
4075             final Status readStatus = status;
4076             if (readStatus != Status.STATUS_ALIVE) {
4077                 throw new IllegalStateException("The " + configuration.getName() + " Cache is not alive (" + readStatus + ")");
4078             }
4079         }
4080 
4081         private void clusterRejoinInProgress() {
4082             clusterRejoinInProgress.set(true);
4083         }
4084 
4085         /***
4086          * Returns true if cache can be initialized. Cache can be initialized if cache has not been shutdown yet.
4087          *
4088          * @return true if cache can be initialized
4089          */
4090         public boolean canInitialize() {
4091             return status == Status.STATUS_UNINITIALISED || clusterRejoinInProgress.get();
4092         }
4093 
4094         /***
4095          * Change state to the new state
4096          *
4097          * @param newState state
4098          */
4099         public void changeState(Status newState) {
4100             this.status = newState;
4101         }
4102 
4103         /***
4104          * Get the current state
4105          *
4106          * @return current state
4107          */
4108         public Status getStatus() {
4109             return status;
4110         }
4111 
4112         /***
4113          * Returns true if the cache is alive
4114          *
4115          * @return true if the cache is alive
4116          */
4117         public boolean isAlive() {
4118             return status == Status.STATUS_ALIVE;
4119         }
4120 
4121         /***
4122          * Returns true if the cache has been disposed
4123          *
4124          * @return true if the cache has been disposed
4125          */
4126         public boolean isShutdown() {
4127             return status == Status.STATUS_SHUTDOWN;
4128         }
4129 
4130         /***
4131          * Returns true if the cache is uninitialized
4132          *
4133          * @return true if the cache is uninitialized
4134          */
4135         public boolean isUninitialized() {
4136             return status == Status.STATUS_UNINITIALISED;
4137         }
4138 
4139     }
4140 
4141     /***
4142      * Private Static class
4143      *
4144      * @author Abhishek Sanoujam
4145      *
4146      */
4147     private static class NonstopActiveDelegateHolderImpl implements NonstopActiveDelegateHolder {
4148 
4149         private final Cache cache;
4150         private volatile NonstopStoreImpl nonstopStore;
4151         private volatile TerracottaStore underlyingTerracottaStore;
4152         private volatile NonstopExecutorService nonstopExecutorService;
4153         private volatile CacheLockProvider underlyingCacheLockProvider;
4154         private volatile boolean nodeBulkLoadEnabled;
4155 
4156         public NonstopActiveDelegateHolderImpl(Cache cache) {
4157             this.cache = cache;
4158         }
4159 
4160         public void nodeBulkLoadChanged(final boolean enabled) {
4161             this.nodeBulkLoadEnabled = enabled;
4162         }
4163 
4164         public RejoinAwareNonstopStore getNonstopStore() {
4165             if (nonstopStore != null) {
4166                 return nonstopStore;
4167             }
4168             initializeNonstopStore();
4169             return nonstopStore;
4170         }
4171 
4172         private synchronized void initializeNonstopStore() {
4173             if (nonstopStore == null) {
4174                 if (!cache.getCacheConfiguration().isTerracottaClustered()) {
4175                     throw new AssertionError("NonstopStore supported for Terracotta clustered caches only");
4176                 }
4177                 if (!cache.getCacheConfiguration().getTerracottaConfiguration().isNonstopEnabled()) {
4178                     throw new AssertionError("Nonstop is not enabled");
4179                 }
4180                 nonstopStore = new NonstopStoreImpl(this, cache.getCacheCluster(), cache.getCacheConfiguration()
4181                         .getTerracottaConfiguration().getNonstopConfiguration(), cache.getCacheConfiguration().getTransactionalMode(),
4182                         cache.getTransactionManagerLookup());
4183             }
4184         }
4185 
4186         public synchronized void terracottaStoreInitialized(TerracottaStore newTerracottaStore) {
4187             this.underlyingTerracottaStore = newTerracottaStore;
4188 
4189             if (nodeBulkLoadEnabled) {
4190                 LOG.debug("Enabling bulk-load for " + cache.getName());
4191                 underlyingTerracottaStore.setNodeCoherent(false);
4192             }
4193 
4194             // reset all other holders associated with the new store
4195             nonstopExecutorService = cache.getCacheManager().getNonstopExecutorService();
4196             Object context = underlyingTerracottaStore.getInternalContext();
4197             if (context instanceof CacheLockProvider) {
4198                 underlyingCacheLockProvider = (CacheLockProvider) context;
4199             } else {
4200                 throw new AssertionError("TerracottaStore.getInternalContext() is not correct - "
4201                         + (context == null ? "NULL" : context.getClass().getName()));
4202             }
4203         }
4204 
4205         public TerracottaStore getUnderlyingTerracottaStore() {
4206             return underlyingTerracottaStore;
4207         }
4208 
4209         public NonstopExecutorService getNonstopExecutorService() {
4210             return nonstopExecutorService;
4211         }
4212 
4213         public CacheLockProvider getUnderlyingCacheLockProvider() {
4214             return underlyingCacheLockProvider;
4215         }
4216 
4217     }
4218 
4219 }