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 net.sf.ehcache.bootstrap.BootstrapCacheLoader;
20  import net.sf.ehcache.bootstrap.BootstrapCacheLoaderFactory;
21  import net.sf.ehcache.concurrent.CacheLockProvider;
22  import net.sf.ehcache.concurrent.LockType;
23  import net.sf.ehcache.concurrent.Sync;
24  import net.sf.ehcache.config.CacheConfiguration;
25  import net.sf.ehcache.config.CacheWriterConfiguration;
26  import net.sf.ehcache.config.DiskStoreConfiguration;
27  import net.sf.ehcache.config.TerracottaConfiguration;
28  import net.sf.ehcache.event.CacheEventListener;
29  import net.sf.ehcache.event.CacheEventListenerFactory;
30  import net.sf.ehcache.event.RegisteredEventListeners;
31  import net.sf.ehcache.exceptionhandler.CacheExceptionHandler;
32  import net.sf.ehcache.extension.CacheExtension;
33  import net.sf.ehcache.extension.CacheExtensionFactory;
34  import net.sf.ehcache.hibernate.tm.SyncTransactionManager;
35  import net.sf.ehcache.loader.CacheLoader;
36  import net.sf.ehcache.loader.CacheLoaderFactory;
37  import net.sf.ehcache.statistics.CacheUsageListener;
38  import net.sf.ehcache.statistics.LiveCacheStatistics;
39  import net.sf.ehcache.statistics.LiveCacheStatisticsWrapper;
40  import net.sf.ehcache.statistics.sampled.SampledCacheStatistics;
41  import net.sf.ehcache.statistics.sampled.SampledCacheStatisticsWrapper;
42  import net.sf.ehcache.store.DiskStore;
43  import net.sf.ehcache.store.LegacyStoreWrapper;
44  import net.sf.ehcache.store.LruMemoryStore;
45  import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
46  import net.sf.ehcache.store.Policy;
47  import net.sf.ehcache.store.Store;
48  import net.sf.ehcache.store.StoreListener;
49  import net.sf.ehcache.store.XATransactionalStore;
50  import net.sf.ehcache.store.compound.impl.DiskPersistentStore;
51  import net.sf.ehcache.store.compound.impl.MemoryOnlyStore;
52  import net.sf.ehcache.store.compound.impl.OverflowToDiskStore;
53  import net.sf.ehcache.transaction.manager.TransactionManagerLookup;
54  import net.sf.ehcache.transaction.xa.EhcacheXAResourceImpl;
55  import net.sf.ehcache.transaction.xa.EhcacheXAStore;
56  import net.sf.ehcache.util.ClassLoaderUtil;
57  import net.sf.ehcache.util.NamedThreadFactory;
58  import net.sf.ehcache.util.PropertyUtil;
59  import net.sf.ehcache.util.TimeUtil;
60  import net.sf.ehcache.writer.CacheWriter;
61  import net.sf.ehcache.writer.CacheWriterFactory;
62  import net.sf.ehcache.writer.CacheWriterManager;
63  import net.sf.ehcache.writer.CacheWriterManagerException;
64  import org.slf4j.Logger;
65  import org.slf4j.LoggerFactory;
66  
67  import javax.transaction.TransactionManager;
68  import java.beans.PropertyChangeListener;
69  import java.beans.PropertyChangeSupport;
70  import java.io.IOException;
71  import java.io.Serializable;
72  import java.net.InetAddress;
73  import java.net.UnknownHostException;
74  import java.util.ArrayList;
75  import java.util.Arrays;
76  import java.util.Collection;
77  import java.util.Collections;
78  import java.util.HashMap;
79  import java.util.HashSet;
80  import java.util.List;
81  import java.util.Map;
82  import java.util.Properties;
83  import java.util.Set;
84  import java.util.UUID;
85  import java.util.concurrent.AbstractExecutorService;
86  import java.util.concurrent.CopyOnWriteArrayList;
87  import java.util.concurrent.ExecutionException;
88  import java.util.concurrent.ExecutorService;
89  import java.util.concurrent.Future;
90  import java.util.concurrent.LinkedBlockingQueue;
91  import java.util.concurrent.ThreadPoolExecutor;
92  import java.util.concurrent.TimeUnit;
93  import java.util.concurrent.atomic.AtomicBoolean;
94  import java.util.concurrent.locks.ReentrantLock;
95  
96  /***
97   * Cache is the central class in ehcache. Caches have {@link Element}s and are managed
98   * by the {@link CacheManager}. The Cache performs logical actions. It delegates physical
99   * implementations to its {@link net.sf.ehcache.store.Store}s.
100  * <p/>
101  * A reference to a Cache can be obtained through the {@link CacheManager}. A Cache thus obtained
102  * is guaranteed to have status {@link Status#STATUS_ALIVE}. This status is checked for any method which
103  * throws {@link IllegalStateException} and the same thrown if it is not alive. This would normally
104  * happen if a call is made after {@link CacheManager#shutdown} is invoked.
105  * <p/>
106  * Cache is threadsafe.
107  * <p/>
108  * Statistics on cache usage are collected and made available through the {@link #getStatistics()} methods.
109  * <p/>
110  * Various decorators are available for Cache, such as BlockingCache, SelfPopulatingCache and the dynamic proxy
111  * ExceptionHandlingDynamicCacheProxy. See each class for details.
112  *
113  * @author Greg Luck
114  * @author Geert Bevin
115  * @version $Id: Cache.java 2521 2010-06-23 10:53:48Z gbevin $
116  */
117 public class Cache implements Ehcache, StoreListener {
118 
119     /***
120      * A reserved word for cache names. It denotes a default configuration
121      * which is applied to caches created without configuration.
122      */
123     public static final String DEFAULT_CACHE_NAME = "default";
124 
125     /***
126      * System Property based method of disabling ehcache. If disabled no elements will be added to a cache.
127      * <p/>
128      * Set the property "net.sf.ehcache.disabled=true" to disable ehcache.
129      * <p/>
130      * This can easily be done using <code>java -Dnet.sf.ehcache.disabled=true</code> in the command line.
131      */
132     public static final String NET_SF_EHCACHE_DISABLED = "net.sf.ehcache.disabled";
133 
134     /***
135      * System Property based method of selecting the LruMemoryStore in use up to ehcache 1.5. This is provided
136      * for ease of migration.
137      * <p/>
138      * Set the property "net.sf.ehcache.use.classic.lru=true" to use the older LruMemoryStore implementation
139      * when LRU is selected as the eviction policy.
140      * <p/>
141      * This can easily be done using <code>java -Dnet.sf.ehcache.use.classic.lru=true</code> in the command line.
142      */
143     public static final String NET_SF_EHCACHE_USE_CLASSIC_LRU = "net.sf.ehcache.use.classic.lru";
144 
145     /***
146      * The default interval between runs of the expiry thread.
147      * @see CacheConfiguration#DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS CacheConfiguration#DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS for a preferred way of setting
148      */
149     public static final long DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS = CacheConfiguration.DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS;
150 
151     private static final Logger LOG = LoggerFactory.getLogger(Cache.class.getName());
152 
153     private static InetAddress localhost;
154 
155     /***
156      * The amount of time to wait if a store gets backed up
157      */
158     private static final int BACK_OFF_TIME_MILLIS = 50;
159 
160     private static final int EXECUTOR_KEEP_ALIVE_TIME = 60000;
161     private static final int EXECUTOR_MAXIMUM_POOL_SIZE = Math.min(10, Runtime.getRuntime().availableProcessors());
162     private static final int EXECUTOR_CORE_POOL_SIZE = 1;
163 
164     static {
165         try {
166             localhost = InetAddress.getLocalHost();
167         } catch (UnknownHostException e) {
168             LOG.error("Unable to set localhost. This prevents creation of a GUID. Cause was: " + e.getMessage(), e);
169         } catch (java.lang.NoClassDefFoundError e) {
170             LOG.debug("InetAddress is being blocked by your runtime environment. e.g. Google App Engine." +
171                     " Ehcache will work as a local cache.");
172         }
173     }
174 
175     private volatile boolean disabled = Boolean.getBoolean(NET_SF_EHCACHE_DISABLED);
176 
177     private final boolean useClassicLru = Boolean.getBoolean(NET_SF_EHCACHE_USE_CLASSIC_LRU);
178 
179     private volatile String diskStorePath;
180 
181     private volatile Status status;
182 
183     private volatile CacheConfiguration configuration;
184 
185     /***
186      * The {@link import net.sf.ehcache.store.Store} of this {@link Cache}.
187      */
188     private volatile Store compoundStore;
189     private volatile CacheLockProvider lockProvider;
190     
191     private volatile RegisteredEventListeners registeredEventListeners;
192 
193     private volatile List<CacheExtension> registeredCacheExtensions;
194 
195     private volatile String guid;
196 
197     private volatile CacheManager cacheManager;
198 
199     private volatile BootstrapCacheLoader bootstrapCacheLoader;
200 
201     private volatile CacheExceptionHandler cacheExceptionHandler;
202 
203     private volatile List<CacheLoader> registeredCacheLoaders;
204 
205     private volatile CacheWriterManager cacheWriterManager;
206 
207     private final AtomicBoolean cacheWriterManagerInitFlag = new AtomicBoolean(false);
208 
209     private final ReentrantLock cacheWriterManagerInitLock = new ReentrantLock();
210 
211     private volatile CacheWriter registeredCacheWriter;
212 
213     /***
214      * A ThreadPoolExecutor which uses a thread pool to schedule loads in the order in which they are requested.
215      * <p/>
216      * Each cache has its own one of these, if required. Because the Core Thread Pool is zero, no threads
217      * are used until actually needed. Threads are added to the pool up to a maximum of 10. The keep alive
218      * time is 60 seconds, after which, if they are not required they will be stopped and collected.
219      * <p/>
220      * The executorService is only used for cache loading, and is created lazily on demand to avoid unnecessary resource
221      * usage.
222      * <p/>
223      * Use {@link #getExecutorService()} to ensure that it is initialised.
224      */
225     private volatile ExecutorService executorService;
226 
227     private volatile LiveCacheStatisticsWrapper liveCacheStatisticsData;
228 
229     private volatile SampledCacheStatisticsWrapper sampledCacheStatistics;
230 
231     private volatile TransactionManagerLookup transactionManagerLookup;
232 
233     private volatile boolean allowDisable = true;
234 
235     private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
236 
237     /***
238      * 2.0 and higher Constructor
239      * <p/>
240      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
241      * <p/>
242      * A client can specify their own settings here and pass the {@link Cache} object
243      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
244      * <p/>
245      * Only the CacheManager can initialise them.
246      *
247      * @param cacheConfiguration the configuration that should be used to create the cache with
248      */
249     public Cache(CacheConfiguration cacheConfiguration) {
250         this(cacheConfiguration, null, null);
251     }
252 
253     /***
254      * 2.0 and higher Constructor
255      * <p/>
256      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
257      * <p/>
258      * A client can specify their own settings here and pass the {@link Cache} object
259      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
260      * <p/>
261      * Only the CacheManager can initialise them.
262      *
263      * @param cacheConfiguration the configuration that should be used to create the cache with
264      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
265      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
266      */
267     public Cache(CacheConfiguration cacheConfiguration,
268                  RegisteredEventListeners registeredEventListeners,
269                  BootstrapCacheLoader bootstrapCacheLoader) {
270         changeStatus(Status.STATUS_UNINITIALISED);
271 
272 
273         this.configuration = cacheConfiguration.clone();
274 
275         guid = createGuid();
276 
277         this.diskStorePath = cacheConfiguration.getDiskStorePath();
278 
279         if (registeredEventListeners == null) {
280             this.registeredEventListeners = new RegisteredEventListeners(this);
281         } else {
282             this.registeredEventListeners = registeredEventListeners;
283         }
284 
285         registeredCacheExtensions = new CopyOnWriteArrayList<CacheExtension>();
286         registeredCacheLoaders = new CopyOnWriteArrayList<CacheLoader>();
287 
288         //initialize statistics
289         liveCacheStatisticsData = new LiveCacheStatisticsWrapper(this);
290         sampledCacheStatistics = new SampledCacheStatisticsWrapper();
291 
292         RegisteredEventListeners listeners = getCacheEventNotificationService();
293         registerCacheListeners(configuration, listeners);
294         registerCacheExtensions(configuration, this);
295 
296         if (null == bootstrapCacheLoader) {
297             this.bootstrapCacheLoader = createBootstrapCacheLoader(configuration.getBootstrapCacheLoaderFactoryConfiguration());
298         } else {
299             this.bootstrapCacheLoader = bootstrapCacheLoader;
300         }
301         registerCacheLoaders(configuration, this);
302         registerCacheWriter(configuration, this);
303     }
304 
305 
306 
307     /***
308      * 1.0 Constructor.
309      * <p/>
310      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
311      * <p/>
312      * A client can specify their own settings here and pass the {@link Cache} object
313      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
314      * <p/>
315      * Only the CacheManager can initialise them.
316      * <p/>
317      * This constructor creates disk stores, if specified, that do not persist between restarts.
318      * <p/>
319      * The default expiry thread interval of 120 seconds is used. This is the interval between runs
320      * of the expiry thread, where it checks the disk store for expired elements. It is not the
321      * the timeToLiveSeconds.
322      *
323      * @param name                the name of the cache. Note that "default" is a reserved name for the defaultCache.
324      * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
325      * @param overflowToDisk      whether to use the disk store
326      * @param eternal             whether the elements in the cache are eternal, i.e. never expire
327      * @param timeToLiveSeconds   the default amount of time to live for an element from its creation date
328      * @param timeToIdleSeconds   the default amount of time to live for an element from its last accessed or modified date
329      * @since 1.0
330      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
331      * for full construction support of version 2.0 and higher features.
332      */
333     public Cache(String name, int maxElementsInMemory, boolean overflowToDisk,
334                  boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds) {
335 
336         this(new CacheConfiguration(name, maxElementsInMemory)
337                     .overflowToDisk(overflowToDisk)
338                     .eternal(eternal)
339                     .timeToLiveSeconds(timeToLiveSeconds)
340                     .timeToIdleSeconds(timeToIdleSeconds));
341     }
342 
343 
344     /***
345      * 1.1 Constructor.
346      * <p/>
347      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
348      * <p/>
349      * A client can specify their own settings here and pass the {@link Cache} object
350      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
351      * <p/>
352      * Only the CacheManager can initialise them.
353      *
354      * @param name                the name of the cache. Note that "default" is a reserved name for the defaultCache.
355      * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
356      * @param overflowToDisk      whether to use the disk store
357      * @param eternal             whether the elements in the cache are eternal, i.e. never expire
358      * @param timeToLiveSeconds   the default amount of time to live for an element from its creation date
359      * @param timeToIdleSeconds   the default amount of time to live for an element from its last accessed or modified date
360      * @param diskPersistent      whether to persist the cache to disk between JVM restarts
361      * @param diskExpiryThreadIntervalSeconds
362      *                            how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
363      * @since 1.1
364      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
365      * for full construction support of version 2.0 and higher features.
366      */
367     public Cache(String name,
368                  int maxElementsInMemory,
369                  boolean overflowToDisk,
370                  boolean eternal,
371                  long timeToLiveSeconds,
372                  long timeToIdleSeconds,
373                  boolean diskPersistent,
374                  long diskExpiryThreadIntervalSeconds) {
375 
376         this(new CacheConfiguration(name, maxElementsInMemory)
377                     .overflowToDisk(overflowToDisk)
378                     .eternal(eternal)
379                     .timeToLiveSeconds(timeToLiveSeconds)
380                     .timeToIdleSeconds(timeToIdleSeconds)
381                     .diskPersistent(diskPersistent)
382                     .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds));
383 
384         LOG.warn("An API change between ehcache-1.1 and ehcache-1.2 results in the persistence path being set to " +
385                 DiskStoreConfiguration.getDefaultPath() + " when the ehcache-1.1 constructor is used. " +
386                 "Please change to the 1.2 constructor.");
387     }
388 
389 
390     /***
391      * 1.2 Constructor
392      * <p/>
393      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
394      * <p/>
395      * A client can specify their own settings here and pass the {@link Cache} object
396      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
397      * <p/>
398      * Only the CacheManager can initialise them.
399      *
400      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
401      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted (0 == no limit)
402      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
403      * @param overflowToDisk            whether to use the disk store
404      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
405      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
406      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
407      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
408      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
409      * @param diskExpiryThreadIntervalSeconds
410      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
411      * @param registeredEventListeners  a notification service. Optionally null, in which case a new
412      *                                  one with no registered listeners will be created.
413      * @since 1.2
414      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
415      * for full construction support of version 2.0 and higher features.
416      */
417     public Cache(String name,
418                  int maxElementsInMemory,
419                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
420                  boolean overflowToDisk,
421                  String diskStorePath,
422                  boolean eternal,
423                  long timeToLiveSeconds,
424                  long timeToIdleSeconds,
425                  boolean diskPersistent,
426                  long diskExpiryThreadIntervalSeconds,
427                  RegisteredEventListeners registeredEventListeners) {
428 
429         this(new CacheConfiguration(name, maxElementsInMemory)
430                     .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
431                     .overflowToDisk(overflowToDisk)
432                     .diskStorePath(diskStorePath)
433                     .eternal(eternal)
434                     .timeToLiveSeconds(timeToLiveSeconds)
435                     .timeToIdleSeconds(timeToIdleSeconds)
436                     .diskPersistent(diskPersistent)
437                     .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds),
438                 registeredEventListeners,
439                 null);
440 
441     }
442 
443     /***
444      * 1.2.1 Constructor
445      * <p/>
446      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
447      * <p/>
448      * A client can specify their own settings here and pass the {@link Cache} object
449      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
450      * <p/>
451      * Only the CacheManager can initialise them.
452      *
453      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
454      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted (0 == no limit)
455      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
456      * @param overflowToDisk            whether to use the disk store
457      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
458      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
459      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
460      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
461      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
462      * @param diskExpiryThreadIntervalSeconds
463      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
464      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
465      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
466      * @since 1.2.1
467      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
468      * for full construction support of version 2.0 and higher features.
469      */
470     public Cache(String name,
471                  int maxElementsInMemory,
472                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
473                  boolean overflowToDisk,
474                  String diskStorePath,
475                  boolean eternal,
476                  long timeToLiveSeconds,
477                  long timeToIdleSeconds,
478                  boolean diskPersistent,
479                  long diskExpiryThreadIntervalSeconds,
480                  RegisteredEventListeners registeredEventListeners,
481                  BootstrapCacheLoader bootstrapCacheLoader) {
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                 bootstrapCacheLoader);
494     }
495 
496     /***
497      * 1.2.4 Constructor
498      * <p/>
499      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
500      * <p/>
501      * A client can specify their own settings here and pass the {@link Cache} object
502      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
503      * <p/>
504      * Only the CacheManager can initialise them.
505      *
506      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
507      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted (0 == no limit)
508      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
509      * @param overflowToDisk            whether to use the disk store
510      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
511      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
512      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
513      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
514      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
515      * @param diskExpiryThreadIntervalSeconds
516      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
517      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
518      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
519      * @param maxElementsOnDisk         the maximum number of Elements to allow on the disk. 0 means unlimited.
520      * @since 1.2.4
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                  int maxElementsOnDisk) {
537 
538         this(new CacheConfiguration(name, maxElementsInMemory)
539                     .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
540                     .overflowToDisk(overflowToDisk)
541                     .diskStorePath(diskStorePath)
542                     .eternal(eternal)
543                     .timeToLiveSeconds(timeToLiveSeconds)
544                     .timeToIdleSeconds(timeToIdleSeconds)
545                     .diskPersistent(diskPersistent)
546                     .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
547                     .maxElementsOnDisk(maxElementsOnDisk),
548                 registeredEventListeners,
549                 bootstrapCacheLoader);
550     }
551 
552     /***
553      * 1.3 Constructor
554      * <p/>
555      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
556      * <p/>
557      * A client can specify their own settings here and pass the {@link Cache} object
558      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
559      * <p/>
560      * Only the CacheManager can initialise them.
561      *
562      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
563      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted (0 == no limit)
564      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
565      * @param overflowToDisk            whether to use the disk store
566      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
567      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
568      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
569      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
570      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
571      * @param diskExpiryThreadIntervalSeconds
572      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
573      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
574      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
575      * @param maxElementsOnDisk         the maximum number of Elements to allow on the disk. 0 means unlimited.
576      * @param diskSpoolBufferSizeMB     the amount of memory to allocate the write buffer for puts to the DiskStore.
577      * @since 1.3
578      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
579      * for full construction support of version 2.0 and higher features.
580      */
581     public Cache(String name,
582                  int maxElementsInMemory,
583                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
584                  boolean overflowToDisk,
585                  String diskStorePath,
586                  boolean eternal,
587                  long timeToLiveSeconds,
588                  long timeToIdleSeconds,
589                  boolean diskPersistent,
590                  long diskExpiryThreadIntervalSeconds,
591                  RegisteredEventListeners registeredEventListeners,
592                  BootstrapCacheLoader bootstrapCacheLoader,
593                  int maxElementsOnDisk,
594                  int diskSpoolBufferSizeMB) {
595 
596         this(new CacheConfiguration(name, maxElementsInMemory)
597                     .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
598                     .overflowToDisk(overflowToDisk)
599                     .diskStorePath(diskStorePath)
600                     .eternal(eternal)
601                     .timeToLiveSeconds(timeToLiveSeconds)
602                     .timeToIdleSeconds(timeToIdleSeconds)
603                     .diskPersistent(diskPersistent)
604                     .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
605                     .maxElementsOnDisk(maxElementsOnDisk)
606                     .diskSpoolBufferSizeMB(diskSpoolBufferSizeMB),
607                 registeredEventListeners,
608                 bootstrapCacheLoader);
609     }
610 
611     /***
612      * 1.6.0 Constructor
613      * <p/>
614      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
615      * <p/>
616      * A client can specify their own settings here and pass the {@link Cache} object
617      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
618      * <p/>
619      * Only the CacheManager can initialise them.
620      *
621      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
622      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted (0 == no limit)
623      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
624      * @param overflowToDisk            whether to use the disk store
625      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
626      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
627      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
628      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
629      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
630      * @param diskExpiryThreadIntervalSeconds
631      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
632      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
633      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
634      * @param maxElementsOnDisk         the maximum number of Elements to allow on the disk. 0 means unlimited.
635      * @param diskSpoolBufferSizeMB     the amount of memory to allocate the write buffer for puts to the DiskStore.
636      * @param clearOnFlush              whether the in-memory storage should be cleared when {@link #flush flush()} is called on the cache
637      * @since 1.6.0
638      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
639      * for full construction support of version 2.0 and higher features.
640      */
641     public Cache(String name,
642                  int maxElementsInMemory,
643                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
644                  boolean overflowToDisk,
645                  String diskStorePath,
646                  boolean eternal,
647                  long timeToLiveSeconds,
648                  long timeToIdleSeconds,
649                  boolean diskPersistent,
650                  long diskExpiryThreadIntervalSeconds,
651                  RegisteredEventListeners registeredEventListeners,
652                  BootstrapCacheLoader bootstrapCacheLoader,
653                  int maxElementsOnDisk,
654                  int diskSpoolBufferSizeMB,
655                  boolean clearOnFlush) {
656 
657         this(new CacheConfiguration(name, maxElementsInMemory)
658                     .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
659                     .overflowToDisk(overflowToDisk)
660                     .diskStorePath(diskStorePath)
661                     .eternal(eternal)
662                     .timeToLiveSeconds(timeToLiveSeconds)
663                     .timeToIdleSeconds(timeToIdleSeconds)
664                     .diskPersistent(diskPersistent)
665                     .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
666                     .maxElementsOnDisk(maxElementsOnDisk)
667                     .diskSpoolBufferSizeMB(diskSpoolBufferSizeMB)
668                     .clearOnFlush(clearOnFlush),
669                 registeredEventListeners,
670                 bootstrapCacheLoader);
671     }
672 
673     /***
674      * 1.7.0 Constructor
675      * <p/>
676      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
677      * <p/>
678      * A client can specify their own settings here and pass the {@link Cache} object
679      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
680      * <p/>
681      * Only the CacheManager can initialise them.
682      *
683      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
684      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted (0 == no limit)
685      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
686      * @param overflowToDisk            whether to use the disk store
687      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
688      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
689      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
690      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
691      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
692      * @param diskExpiryThreadIntervalSeconds
693      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
694      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
695      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
696      * @param maxElementsOnDisk         the maximum number of Elements to allow on the disk. 0 means unlimited.
697      * @param diskSpoolBufferSizeMB     the amount of memory to allocate the write buffer for puts to the DiskStore.
698      * @param clearOnFlush              whether the in-memory storage should be cleared when {@link #flush flush()} is called on the cache
699      * @param isTerracottaClustered     whether to cluster this cache with Terracotta
700      * @param terracottaValueMode       either "SERIALIZATION" or "IDENTITY" mode, only used if isTerracottaClustered=true
701      * @param terracottaCoherentReads   whether this cache should use coherent reads (usually should be true) unless optimizing for read-only
702      * @since 1.7.0
703      * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
704      * for full construction support of version 2.0 and higher features.
705      */
706     public Cache(String name, int maxElementsInMemory, MemoryStoreEvictionPolicy memoryStoreEvictionPolicy, boolean overflowToDisk,
707                  String diskStorePath, boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds, boolean diskPersistent,
708                  long diskExpiryThreadIntervalSeconds, RegisteredEventListeners registeredEventListeners,
709                  BootstrapCacheLoader bootstrapCacheLoader, int maxElementsOnDisk, int diskSpoolBufferSizeMB, boolean clearOnFlush,
710                  boolean isTerracottaClustered, String terracottaValueMode, boolean terracottaCoherentReads) {
711 
712         this(new CacheConfiguration(name, maxElementsInMemory)
713                     .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
714                     .overflowToDisk(overflowToDisk)
715                     .diskStorePath(diskStorePath)
716                     .eternal(eternal)
717                     .timeToLiveSeconds(timeToLiveSeconds)
718                     .timeToIdleSeconds(timeToIdleSeconds)
719                     .diskPersistent(diskPersistent)
720                     .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
721                     .maxElementsOnDisk(maxElementsOnDisk)
722                     .diskSpoolBufferSizeMB(diskSpoolBufferSizeMB)
723                     .clearOnFlush(clearOnFlush)
724                     .terracotta(new TerracottaConfiguration()
725                         .clustered(isTerracottaClustered)
726                         .valueMode(terracottaValueMode)
727                         .coherentReads(terracottaCoherentReads)),
728                 registeredEventListeners,
729                 bootstrapCacheLoader);
730     }
731 
732 
733     /***
734      * A factory method to create a RegisteredEventListeners
735      */
736     private static void registerCacheListeners(CacheConfiguration cacheConfiguration,
737                                                  RegisteredEventListeners registeredEventListeners) {
738         List cacheEventListenerConfigurations = cacheConfiguration.getCacheEventListenerConfigurations();
739         for (Object cacheEventListenerConfiguration : cacheEventListenerConfigurations) {
740             CacheConfiguration.CacheEventListenerFactoryConfiguration factoryConfiguration =
741                     (CacheConfiguration.CacheEventListenerFactoryConfiguration) cacheEventListenerConfiguration;
742             CacheEventListener cacheEventListener = createCacheEventListener(factoryConfiguration);
743             registeredEventListeners.registerListener(cacheEventListener, factoryConfiguration.getListenFor());
744         }
745     }
746 
747     /***
748      * A factory method to register cache extensions
749      *
750      * @param cacheConfiguration the cache configuration
751      * @param cache              the cache
752      */
753     private static void registerCacheExtensions(CacheConfiguration cacheConfiguration, Ehcache cache) {
754         List cacheExtensionConfigurations = cacheConfiguration.getCacheExtensionConfigurations();
755         for (Object cacheExtensionConfiguration : cacheExtensionConfigurations) {
756             CacheConfiguration.CacheExtensionFactoryConfiguration factoryConfiguration =
757                     (CacheConfiguration.CacheExtensionFactoryConfiguration) cacheExtensionConfiguration;
758             CacheExtension cacheExtension = createCacheExtension(factoryConfiguration, cache);
759             cache.registerCacheExtension(cacheExtension);
760         }
761     }
762 
763     /***
764      * A factory method to register cache Loaders
765      *
766      * @param cacheConfiguration the cache configuration
767      * @param cache              the cache
768      */
769     private static void registerCacheLoaders(CacheConfiguration cacheConfiguration, Ehcache cache) {
770         List cacheLoaderConfigurations = cacheConfiguration.getCacheLoaderConfigurations();
771         for (Object cacheLoaderConfiguration : cacheLoaderConfigurations) {
772             CacheConfiguration.CacheLoaderFactoryConfiguration factoryConfiguration =
773                     (CacheConfiguration.CacheLoaderFactoryConfiguration) cacheLoaderConfiguration;
774             CacheLoader cacheLoader = createCacheLoader(factoryConfiguration, cache);
775             cache.registerCacheLoader(cacheLoader);
776         }
777     }
778 
779     /***
780      * A factory method to register cache writers
781      *
782      * @param cacheConfiguration the cache configuration
783      * @param cache              the cache
784      */
785     private static void registerCacheWriter(CacheConfiguration cacheConfiguration, Ehcache cache) {
786         CacheWriterConfiguration config = cacheConfiguration.getCacheWriterConfiguration();
787         if (config != null) {
788             CacheWriter cacheWriter = createCacheWriter(config, cache);
789             cache.registerCacheWriter(cacheWriter);
790         }
791     }
792 
793 
794     /***
795      * Tries to load the class specified otherwise defaults to null.
796      *
797      * @param factoryConfiguration
798      */
799     private static CacheEventListener createCacheEventListener(
800             CacheConfiguration.CacheEventListenerFactoryConfiguration factoryConfiguration) {
801         String className = null;
802         CacheEventListener cacheEventListener = null;
803         if (factoryConfiguration != null) {
804             className = factoryConfiguration.getFullyQualifiedClassPath();
805         }
806         if (className == null) {
807             LOG.debug("CacheEventListener factory not configured. Skipping...");
808         } else {
809             CacheEventListenerFactory factory = (CacheEventListenerFactory)
810                     ClassLoaderUtil.createNewInstance(className);
811             Properties properties =
812 
813                     PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
814                             factoryConfiguration.getPropertySeparator());
815             cacheEventListener =
816                     factory.createCacheEventListener(properties);
817         }
818         return cacheEventListener;
819     }
820 
821     /***
822      * Tries to load the class specified otherwise defaults to null.
823      *
824      * @param factoryConfiguration
825      */
826     private static CacheExtension createCacheExtension(
827             CacheConfiguration.CacheExtensionFactoryConfiguration factoryConfiguration, Ehcache cache) {
828         String className = null;
829         CacheExtension cacheExtension = null;
830         if (factoryConfiguration != null) {
831             className = factoryConfiguration.getFullyQualifiedClassPath();
832         }
833         if (className == null) {
834             LOG.debug("CacheExtension factory not configured. Skipping...");
835         } else {
836             CacheExtensionFactory factory = (CacheExtensionFactory) ClassLoaderUtil.createNewInstance(className);
837             Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
838                     factoryConfiguration.getPropertySeparator());
839             cacheExtension = factory.createCacheExtension(cache, properties);
840         }
841         return cacheExtension;
842     }
843 
844     /***
845      * Tries to load the class specified otherwise defaults to null.
846      *
847      * @param factoryConfiguration
848      */
849     private static CacheLoader createCacheLoader(
850             CacheConfiguration.CacheLoaderFactoryConfiguration factoryConfiguration, Ehcache cache) {
851         String className = null;
852         CacheLoader cacheLoader = null;
853         if (factoryConfiguration != null) {
854             className = factoryConfiguration.getFullyQualifiedClassPath();
855         }
856         if (className == null) {
857             LOG.debug("CacheLoader factory not configured. Skipping...");
858         } else {
859             CacheLoaderFactory factory = (CacheLoaderFactory) ClassLoaderUtil.createNewInstance(className);
860             Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
861                     factoryConfiguration.getPropertySeparator());
862             cacheLoader = factory.createCacheLoader(cache, properties);
863         }
864         return cacheLoader;
865     }
866 
867     /***
868      * Tries to load the class specified otherwise defaults to null.
869      *
870      * @param config
871      */
872     private static CacheWriter createCacheWriter(CacheWriterConfiguration config, Ehcache cache) {
873         String className = null;
874         CacheWriter cacheWriter = null;
875         CacheWriterConfiguration.CacheWriterFactoryConfiguration factoryConfiguration = config.getCacheWriterFactoryConfiguration();
876         if (factoryConfiguration != null) {
877             className = factoryConfiguration.getFullyQualifiedClassPath();
878         }
879         if (null == className) {
880             LOG.debug("CacheWriter factory not configured. Skipping...");
881         } else {
882             CacheWriterFactory factory = (CacheWriterFactory) ClassLoaderUtil.createNewInstance(className);
883             Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
884                     factoryConfiguration.getPropertySeparator());
885             if (null == properties) {
886                 properties = new Properties();
887             }
888             cacheWriter = factory.createCacheWriter(cache, properties);
889         }
890         return cacheWriter;
891     }
892 
893     /***
894      * Tries to load a BootstrapCacheLoader from the class specified.
895      *
896      * @return If there is none returns null.
897      */
898     private static final BootstrapCacheLoader createBootstrapCacheLoader(
899             CacheConfiguration.BootstrapCacheLoaderFactoryConfiguration factoryConfiguration) throws CacheException {
900         String className = null;
901         BootstrapCacheLoader bootstrapCacheLoader = null;
902         if (factoryConfiguration != null) {
903             className = factoryConfiguration.getFullyQualifiedClassPath();
904         }
905         if (className == null || className.length() == 0) {
906             LOG.debug("No BootstrapCacheLoaderFactory class specified. Skipping...");
907         } else {
908             BootstrapCacheLoaderFactory factory = (BootstrapCacheLoaderFactory)
909                     ClassLoaderUtil.createNewInstance(className);
910             Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
911                     factoryConfiguration.getPropertySeparator());
912             return factory.createBootstrapCacheLoader(properties);
913         }
914         return bootstrapCacheLoader;
915     }
916 
917     /***
918      * Get the TransactionManagerLookup implementation used to lookup the TransactionManager.
919      * This is generally only set for XA transactional caches
920      * @return The {@link net.sf.ehcache.transaction.manager.TransactionManagerLookup} instance
921      */
922     public TransactionManagerLookup getTransactionManagerLookup() {
923        return transactionManagerLookup;
924     }
925 
926     /***
927      * Sets the TransactionManagerLookup that needs to be used for this cache to lookup the TransactionManager
928      * This needs to be set before {@link Cache#initialise()} is called
929      * @param lookup The {@link net.sf.ehcache.transaction.manager.TransactionManagerLookup} instance
930      */
931     public void setTransactionManagerLookup(TransactionManagerLookup lookup) {
932         TransactionManagerLookup oldValue = getTransactionManagerLookup();
933         this.transactionManagerLookup = lookup;
934         firePropertyChange("TransactionManagerLookup", oldValue, lookup);
935     }
936 
937     /***
938      * Newly created caches do not have a {@link net.sf.ehcache.store.Store}.
939      * <p/>
940      * This method creates the store and makes the cache ready to accept elements
941      */
942     public void initialise() {
943         synchronized (this) {
944             if (!status.equals(Status.STATUS_UNINITIALISED)) {
945                 throw new IllegalStateException("Cannot initialise the " + configuration.getName()
946                         + " cache because its status is not STATUS_UNINITIALISED");
947             }
948 
949             if (configuration.getMaxElementsInMemory() == 0) {
950                 LOG.warn("Cache: " + configuration.getName() + " has a maxElementsInMemory of 0.  " +
951                         "In Ehcache 2.0 this has been changed to mean a store with no capacity limit. Set it to 1 if you want no elements cached in memory");
952             }
953 
954             final Store store;
955             if (isTerracottaClustered()) {
956                 store = cacheManager.createTerracottaStore(this);
957                 boolean unlockedReads = !this.configuration.getTerracottaConfiguration().getCoherentReads();
958                 // if coherentReads=false, make coherent=false
959                 boolean coherent = unlockedReads ? false : this.configuration.getTerracottaConfiguration().isCoherent();
960                 store.setNodeCoherent(coherent);
961             } else {
962                 if (useClassicLru && configuration.getMemoryStoreEvictionPolicy().equals(MemoryStoreEvictionPolicy.LRU)) {
963                     Store disk = createDiskStore();
964                     store = new LegacyStoreWrapper(new LruMemoryStore(this, disk), disk, registeredEventListeners, configuration);
965                 } else {
966                     if (configuration.isDiskPersistent()) {
967                         store = DiskPersistentStore.create(this, diskStorePath);
968                     } else if (configuration.isOverflowToDisk()) {
969                         store = OverflowToDiskStore.create(this, diskStorePath);
970                     } else {
971                         store = MemoryOnlyStore.create(this, diskStorePath);
972                     }
973                 }
974             }
975 
976             if (configuration.isTransactional()) {
977                 if (configuration.isTerracottaClustered()
978                     && configuration.getTerracottaConfiguration().getValueMode() != TerracottaConfiguration.ValueMode.SERIALIZATION) {
979                     throw new CacheException("To be transactional, a Terracotta clustered cache needs to be in Serialization value mode");
980                 }
981 
982                 TransactionManager txnManager = (TransactionManager) configuration.getDefaultTransactionManager();
983                 if (txnManager == null) {
984                     txnManager = transactionManagerLookup.getTransactionManager();
985                 }
986                 if (txnManager == null) {
987                     throw new CacheException("You've configured cache " + cacheManager.getName() + "."
988                                              + configuration.getName() + " to be transactional, but no TransactionManager could be found!");
989                 }
990                 //set xa enabled
991                 if (configuration.isTerracottaClustered()) {
992                     configuration.getTerracottaConfiguration().setCacheXA(true);
993                 }
994 
995                 EhcacheXAStore ehcacheXAStore =
996                     cacheManager.createEhcacheXAStore(this, store, txnManager instanceof SyncTransactionManager);
997 
998                 // this xaresource is for initial registration and recovery
999                 EhcacheXAResourceImpl xaResource = new EhcacheXAResourceImpl(this, txnManager, ehcacheXAStore);
1000                 transactionManagerLookup.register(xaResource);
1001 
1002                 this.compoundStore = new XATransactionalStore(this, ehcacheXAStore, transactionManagerLookup, txnManager);
1003             } else {
1004                 this.compoundStore = store;
1005             }
1006             this.cacheWriterManager = configuration.getCacheWriterConfiguration().getWriteMode().createWriterManager(this);
1007             initialiseCacheWriterManager(false);
1008 
1009             changeStatus(Status.STATUS_ALIVE);
1010             initialiseRegisteredCacheExtensions();
1011             initialiseRegisteredCacheLoaders();
1012             initialiseRegisteredCacheWriter();
1013 
1014             // initialize live statistics
1015             // register to get notifications of
1016             // put/update/removeInternal/expiry/eviction
1017             getCacheEventNotificationService().registerListener(liveCacheStatisticsData);
1018             // set up default values
1019             liveCacheStatisticsData.setStatisticsAccuracy(Statistics.STATISTICS_ACCURACY_BEST_EFFORT);
1020             liveCacheStatisticsData.setStatisticsEnabled(configuration.getStatistics());
1021 
1022             // register the sampled cache statistics
1023             this.registerCacheUsageListener(sampledCacheStatistics);
1024 
1025             if (isTerracottaClustered()) {
1026                 // create this to be sure that it's present on each node to receive clustered events,
1027                 // even if this node is not sending out its events
1028                 cacheManager.createTerracottaEventReplicator(this);
1029             }
1030             
1031             Object context = compoundStore.getInternalContext();
1032             if (context instanceof CacheLockProvider) {
1033                 lockProvider = (CacheLockProvider) context; 
1034             }
1035         }
1036 
1037         compoundStore.addStoreListener(this);
1038         
1039         if (LOG.isDebugEnabled()) {
1040             LOG.debug("Initialised cache: " + configuration.getName());
1041         }
1042 
1043         if (disabled) {
1044             LOG.warn("Cache: " + configuration.getName() + " is disabled because the " + NET_SF_EHCACHE_DISABLED
1045                     + " property was set to true. No elements will be added to the cache.");
1046         }
1047     }
1048 
1049     /***
1050      * The CacheWriterManager's initialisation can be deferred until an actual CacheWriter has been registered.
1051      * <p/>
1052      * 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.
1053      *
1054      * @param imperative indicates whether it's imperative for the cache writer manager to be initialised before operations can continue
1055      * @throws CacheException when the CacheWriterManager couldn't be initialised but it was imperative to do so
1056      */
1057     private void initialiseCacheWriterManager(boolean imperative) throws CacheException {
1058         if (!cacheWriterManagerInitFlag.get()) {
1059             cacheWriterManagerInitLock.lock();
1060             try {
1061                 if (!cacheWriterManagerInitFlag.get()) {
1062                     if (cacheWriterManager != null && registeredCacheWriter != null) {
1063                         cacheWriterManager.init(this);
1064                         cacheWriterManagerInitFlag.set(true);
1065                     } else if (imperative) {
1066                         throw new CacheException("Cache: " + configuration.getName() + " was being used with cache writer " +
1067                                 "features, but it wasn't properly registered beforehand.");
1068                     }
1069                 }
1070             } finally {
1071                 cacheWriterManagerInitLock.unlock();
1072             }
1073         }
1074     }
1075 
1076     /***
1077      * {@inheritDoc}
1078      */
1079     public CacheWriterManager getWriterManager() {
1080         return cacheWriterManager;
1081     }
1082 
1083     /***
1084      * Creates a disk store when either:
1085      * <ol>
1086      * <li>overflowToDisk is enabled
1087      * <li>diskPersistent is enabled
1088      * </ol>
1089      *
1090      * @return the disk store
1091      */
1092     protected DiskStore createDiskStore() {
1093         if (isDiskStore()) {
1094             return DiskStore.create(this, diskStorePath);
1095         } else {
1096             return null;
1097         }
1098     }
1099 
1100     /***
1101      * Whether this cache uses a disk store
1102      *
1103      * @return true if the cache either overflows to disk or is disk persistent
1104      */
1105     protected boolean isDiskStore() {
1106         return configuration.isOverflowToDisk() || configuration.isDiskPersistent();
1107     }
1108 
1109     /***
1110      * Indicates whether this cache is clustered by Terracotta
1111      *
1112      * @return {@code true} when the cache is clustered by Terracotta; or {@code false} otherwise
1113      */
1114     public boolean isTerracottaClustered() {
1115         return configuration.isTerracottaClustered();
1116     }
1117 
1118     /***
1119      * Bootstrap command. This must be called after the Cache is initialised, during
1120      * CacheManager initialisation. If loads are synchronous, they will complete before the CacheManager
1121      * initialise completes, otherwise they will happen in the background.
1122      */
1123     public void bootstrap() {
1124         if (!disabled && bootstrapCacheLoader != null) {
1125             bootstrapCacheLoader.load(this);
1126         }
1127 
1128     }
1129 
1130     private void changeStatus(Status status) {
1131         this.status = status;
1132     }
1133 
1134 
1135     /***
1136      * Put an element in the cache.
1137      * <p/>
1138      * Resets the access statistics on the element, which would be the case if it has previously been
1139      * gotten from a cache, and is now being put back.
1140      * <p/>
1141      * Also notifies the CacheEventListener that:
1142      * <ul>
1143      * <li>the element was put, but only if the Element was actually put.
1144      * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
1145      * if it was requested
1146      * </ul>
1147      * <p/>
1148      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1149      * This exception should be caught in those circumstances.
1150      *
1151      * @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
1152      *                <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
1153      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1154      * @throws CacheException
1155      */
1156     public final void put(Element element) throws IllegalArgumentException, IllegalStateException,
1157             CacheException {
1158         put(element, false);
1159     }
1160 
1161 
1162     /***
1163      * Put an element in the cache.
1164      * <p/>
1165      * Resets the access statistics on the element, which would be the case if it has previously been
1166      * gotten from a cache, and is now being put back.
1167      * <p/>
1168      * Also notifies the CacheEventListener that:
1169      * <ul>
1170      * <li>the element was put, but only if the Element was actually put.
1171      * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
1172      * if it was requested
1173      * </ul>
1174      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1175      * This exception should be caught in those circumstances.
1176      *
1177      * @param element                     A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
1178      *                                    <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
1179      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1180      *                                    further notification to doNotNotifyCacheReplicators cache peers
1181      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
1182      * @throws IllegalArgumentException if the element is null
1183      */
1184     public final void put(Element element, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
1185             IllegalStateException, CacheException {
1186         putInternal(element, doNotNotifyCacheReplicators, false);
1187     }
1188 
1189     /***
1190      * {@inheritDoc}
1191      */
1192     public void putWithWriter(Element element) throws IllegalArgumentException, IllegalStateException, CacheException {
1193         putInternal(element, false, true);
1194     }
1195 
1196     private void putInternal(Element element, boolean doNotNotifyCacheReplicators, boolean useCacheWriter) {
1197         if (useCacheWriter) {
1198             initialiseCacheWriterManager(true);
1199         }
1200 
1201         checkStatus();
1202 
1203         if (disabled) {
1204             return;
1205         }
1206 
1207         if (element == null) {
1208             if (doNotNotifyCacheReplicators) {
1209 
1210                 LOG.debug("Element from replicated put is null. This happens because the element is a SoftReference" +
1211                         " and it has been collected. Increase heap memory on the JVM or set -Xms to be the same as " +
1212                         "-Xmx to avoid this problem.");
1213 
1214             }
1215             //nulls are ignored
1216             return;
1217         }
1218 
1219 
1220         if (element.getObjectKey() == null) {
1221             //nulls are ignored
1222             return;
1223         }
1224 
1225         element.resetAccessStatistics();
1226         
1227         applyDefaultsToElementWithoutLifespanSet(element);
1228 
1229         backOffIfDiskSpoolFull();
1230 
1231         if (useCacheWriter) {
1232             boolean elementExists = false;
1233             try {
1234                 elementExists = compoundStore.containsKey(element.getObjectKey());
1235                 elementExists = !compoundStore.putWithWriter(element, cacheWriterManager) || elementExists;
1236                 if (elementExists) {
1237                     element.updateUpdateStatistics();
1238                 }
1239                 notifyPutInternalListeners(element, doNotNotifyCacheReplicators, elementExists);
1240             } catch (CacheWriterManagerException e) {
1241                 if (configuration.getCacheWriterConfiguration().getNotifyListenersOnException()) {
1242                     notifyPutInternalListeners(element, doNotNotifyCacheReplicators, elementExists);
1243                 }
1244                 throw e.getCause();
1245             }
1246         } else {
1247             boolean elementExists = !compoundStore.put(element);
1248             if (elementExists) {
1249                 element.updateUpdateStatistics();
1250             }
1251             notifyPutInternalListeners(element, doNotNotifyCacheReplicators, elementExists);
1252         }
1253     }
1254 
1255     private void notifyPutInternalListeners(Element element, boolean doNotNotifyCacheReplicators, boolean elementExists) {
1256         if (elementExists) {
1257             registeredEventListeners.notifyElementUpdated(element, doNotNotifyCacheReplicators);
1258         } else {
1259             registeredEventListeners.notifyElementPut(element, doNotNotifyCacheReplicators);
1260         }
1261     }
1262 
1263     /***
1264      * wait outside of synchronized block so as not to block readers
1265      * If the disk store spool is full wait a short time to give it a chance to
1266      * catch up.
1267      * todo maybe provide a warning if this is continually happening or monitor via JMX
1268      */
1269     private void backOffIfDiskSpoolFull() {
1270 
1271         if (compoundStore.bufferFull()) {
1272             //back off to avoid OutOfMemoryError
1273             try {
1274                 Thread.sleep(BACK_OFF_TIME_MILLIS);
1275             } catch (InterruptedException e) {
1276                 //do not care if this happens
1277             }
1278         }
1279     }
1280 
1281     private void applyDefaultsToElementWithoutLifespanSet(Element element) {
1282         if (!element.isLifespanSet()) {
1283             element.setLifespanDefaults(TimeUtil.convertTimeToInt(configuration.getTimeToIdleSeconds()),
1284                     TimeUtil.convertTimeToInt(configuration.getTimeToLiveSeconds()),
1285                     configuration.isEternal());
1286         }
1287     }
1288 
1289     /***
1290      * Put an element in the cache, without updating statistics, or updating listeners. This is meant to be used
1291      * in conjunction with {@link #getQuiet}.
1292      * Synchronization is handled within the method.
1293      * <p/>
1294      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1295      * This exception should be caught in those circumstances.
1296      * <p/>
1297      *
1298      * @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
1299      *                <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
1300      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
1301      * @throws IllegalArgumentException if the element is null
1302      */
1303     public final void putQuiet(Element element) throws IllegalArgumentException, IllegalStateException,
1304             CacheException {
1305         checkStatus();
1306 
1307         if (disabled) {
1308             return;
1309         }
1310 
1311         if (element == null || element.getObjectKey() == null) {
1312             //nulls are ignored
1313             return;
1314         }
1315 
1316         applyDefaultsToElementWithoutLifespanSet(element);
1317 
1318         compoundStore.put(element);
1319     }
1320 
1321     /***
1322      * Gets an element from the cache. Updates Element Statistics
1323      * <p/>
1324      * Note that the Element's lastAccessTime is always the time of this get.
1325      * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
1326      * <p/>
1327      * Synchronization is handled within the method.
1328      *
1329      * @param key a serializable value. Null keys are not stored so get(null) always returns null
1330      * @return the element, or null, if it does not exist.
1331      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1332      * @see #isExpired
1333      */
1334     public final Element get(Serializable key) throws IllegalStateException, CacheException {
1335         return get((Object) key);
1336     }
1337 
1338 
1339     /***
1340      * Gets an element from the cache. Updates Element Statistics
1341      * <p/>
1342      * Note that the Element's lastAccessTime is always the time of this get.
1343      * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
1344      * <p/>
1345      * Synchronization is handled within the method.
1346      *
1347      * @param key an Object value
1348      * @return the element, or null, if it does not exist.
1349      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1350      * @see #isExpired
1351      * @since 1.2
1352      */
1353     public final Element get(Object key) throws IllegalStateException, CacheException {
1354         checkStatus();
1355 
1356         if (disabled) {
1357             return null;
1358         }
1359 
1360         if (isStatisticsEnabled()) {
1361             long start = System.currentTimeMillis();
1362 
1363             Element element = searchInStoreWithStats(key, false, true);
1364             if (element == null) {
1365                 liveCacheStatisticsData.cacheMissNotFound();
1366                 if (LOG.isDebugEnabled()) {
1367                     LOG.debug(configuration.getName() + " cache - Miss");
1368                 }
1369             }
1370             //todo is this expensive. Maybe ditch.
1371             long end = System.currentTimeMillis();
1372             liveCacheStatisticsData.addGetTimeMillis(end - start);
1373             return element;
1374         } else {
1375             return searchInStoreWithoutStats(key, false, true);
1376         }
1377     }
1378 
1379     /***
1380      * This method will return, from the cache, the Element associated with the argument "key".
1381      * <p/>
1382      * 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,
1383      * the one associated with the cache. If both are null, no load is performed and null is returned.
1384      * <p/>
1385      * 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.
1386      * <p/>
1387      * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
1388      * are synchronized.
1389      *
1390      * @param key            key whose associated value is to be returned.
1391      * @param loader         the override loader to use. If null, the cache's default loader will be used
1392      * @param loaderArgument an argument to pass to the CacheLoader.
1393      * @return an element if it existed or could be loaded, otherwise null
1394      * @throws CacheException
1395      */
1396     public Element getWithLoader(Object key, CacheLoader loader, Object loaderArgument) throws CacheException {
1397 
1398         Element element = get(key);
1399         if (element != null) {
1400             return element;
1401         }
1402 
1403         if (registeredCacheLoaders.size() == 0 && loader == null) {
1404             return null;
1405         }
1406 
1407         try {
1408             //check again in case the last thread loaded it
1409             element = getQuiet(key);
1410             if (element != null) {
1411                 return element;
1412             }
1413             Future future = asynchronousLoad(key, loader, loaderArgument);
1414             //wait for result
1415             future.get();
1416         } catch (Exception e) {
1417             throw new CacheException("Exception on load for key " + key, e);
1418         }
1419         return getQuiet(key);
1420     }
1421 
1422     /***
1423      * The load method provides a means to "pre load" the cache. This method will, asynchronously, load the specified
1424      * object into the cache using the associated CacheLoader. If the object already exists in the cache, no action is
1425      * taken. If no loader is associated with the object, no object will be loaded into the cache. If a problem is
1426      * encountered during the retrieving or loading of the object, an exception should be logged. If the "arg" argument
1427      * is set, the arg object will be passed to the CacheLoader.load method. The cache will not dereference the object.
1428      * If no "arg" value is provided a null will be passed to the load method. The storing of null values in the cache
1429      * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding
1430      * the object in the cache. In both cases a null is returned.
1431      * <p/>
1432      * The Ehcache native API provides similar functionality to loaders using the
1433      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1434      *
1435      * @param key key whose associated value to be loaded using the associated CacheLoader if this cache doesn't contain it.
1436      * @throws CacheException
1437      */
1438     public void load(final Object key) throws CacheException {
1439         if (registeredCacheLoaders.size() == 0) {
1440 
1441             LOG.debug("The CacheLoader is null. Returning.");
1442             return;
1443         }
1444 
1445         boolean existsOnCall = isKeyInCache(key);
1446         if (existsOnCall) {
1447 
1448             LOG.debug("The key {} exists in the cache. Returning.", key);
1449             return;
1450         }
1451 
1452         asynchronousLoad(key, null, null);
1453     }
1454 
1455     /***
1456      * The getAll method will return, from the cache, a Map of the objects associated with the Collection of keys in argument "keys".
1457      * If the objects are not in the cache, the associated cache loader will be called. If no loader is associated with an object,
1458      * a null is returned. If a problem is encountered during the retrieving or loading of the objects, an exception will be thrown.
1459      * If the "arg" argument is set, the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference
1460      * 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
1461      * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding the object in
1462      * the cache. In both cases a null is returned.
1463      * <p/>
1464      * <p/>
1465      * Note. If the getAll exceeds the maximum cache size, the returned map will necessarily be less than the number specified.
1466      * <p/>
1467      * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
1468      * are synchronized.
1469      * <p/>
1470      * The constructs package provides similar functionality using the
1471      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1472      *
1473      * @param keys           a collection of keys to be returned/loaded
1474      * @param loaderArgument an argument to pass to the CacheLoader.
1475      * @return a Map populated from the Cache. If there are no elements, an empty Map is returned.
1476      * @throws CacheException
1477      */
1478     public Map getAllWithLoader(Collection keys, Object loaderArgument) throws CacheException {
1479         if (keys == null) {
1480             return new HashMap(0);
1481         }
1482         Map<Object, Object> map = new HashMap<Object, Object>(keys.size());
1483 
1484         List<Object> missingKeys = new ArrayList<Object>(keys.size());
1485 
1486         if (registeredCacheLoaders.size() > 0) {
1487             Object key = null;
1488             try {
1489                 map = new HashMap<Object, Object>(keys.size());
1490 
1491                 for (Object key1 : keys) {
1492                     key = key1;
1493 
1494                     if (isKeyInCache(key)) {
1495                         Element element = get(key);
1496                         if (element != null) {
1497                             map.put(key, element.getObjectValue());
1498                         } else {
1499                             map.put(key, null);
1500                         }
1501                     } else {
1502                         missingKeys.add(key);
1503                     }
1504                 }
1505 
1506                 //now load everything that's missing.
1507                 Future future = asynchronousLoadAll(missingKeys, loaderArgument);
1508                 future.get();
1509 
1510 
1511                 for (Object missingKey : missingKeys) {
1512                     key = missingKey;
1513                     Element element = get(key);
1514                     if (element != null) {
1515                         map.put(key, element.getObjectValue());
1516                     } else {
1517                         map.put(key, null);
1518                     }
1519                 }
1520 
1521             } catch (InterruptedException e) {
1522                 throw new CacheException(e.getMessage() + " for key " + key, e);
1523             } catch (ExecutionException e) {
1524                 throw new CacheException(e.getMessage() + " for key " + key, e);
1525             }
1526         } else {
1527             for (Object key : keys) {
1528                 Element element = get(key);
1529                 if (element != null) {
1530                     map.put(key, element.getObjectValue());
1531                 } else {
1532                     map.put(key, null);
1533                 }
1534             }
1535         }
1536         return map;
1537     }
1538 
1539 
1540     /***
1541      * The loadAll method provides a means to "pre load" objects into the cache. This method will, asynchronously, load
1542      * the specified objects into the cache using the associated cache loader(s). If the an object already exists in the
1543      * cache, no action is taken. If no loader is associated with the object, no object will be loaded into the cache.
1544      * If a problem is encountered during the retrieving or loading of the objects, an exception (to be defined)
1545      * should be logged. The getAll method will return, from the cache, a Map of the objects associated with the
1546      * Collection of keys in argument "keys". If the objects are not in the cache, the associated cache loader will be
1547      * called. If no loader is associated with an object, a null is returned. If a problem is encountered during the
1548      * retrieving or loading of the objects, an exception (to be defined) will be thrown. If the "arg" argument is set,
1549      * the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference the object.
1550      * If no "arg" value is provided a null will be passed to the loadAll method.
1551      * <p/>
1552      * keys - collection of the keys whose associated values to be loaded into this cache by using the associated
1553      * CacheLoader if this cache doesn't contain them.
1554      * <p/>
1555      * The Ehcache native API provides similar functionality to loaders using the
1556      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1557      */
1558     public void loadAll(final Collection keys, final Object argument) throws CacheException {
1559 
1560         if (registeredCacheLoaders.size() == 0) {
1561 
1562             LOG.debug("The CacheLoader is null. Returning.");
1563             return;
1564         }
1565         if (keys == null) {
1566             return;
1567         }
1568         asynchronousLoadAll(keys, argument);
1569     }
1570 
1571     /***
1572      * Gets an element from the cache, without updating Element statistics. Cache statistics are
1573      * still updated. Listeners are not called.
1574      * <p/>
1575      *
1576      * @param key a serializable value
1577      * @return the element, or null, if it does not exist.
1578      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1579      * @see #isExpired
1580      */
1581     public final Element getQuiet(Serializable key) throws IllegalStateException, CacheException {
1582         return getQuiet((Object) key);
1583     }
1584 
1585     /***
1586      * Gets an element from the cache, without updating Element statistics. Cache statistics are
1587      * not updated.
1588      * <p/>
1589      * Listeners are not called.
1590      *
1591      * @param key a serializable value
1592      * @return the element, or null, if it does not exist.
1593      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1594      * @see #isExpired
1595      * @since 1.2
1596      */
1597     public final Element getQuiet(Object key) throws IllegalStateException, CacheException {
1598         checkStatus();
1599         return searchInStoreWithoutStats(key, true, false);
1600     }
1601 
1602     /***
1603      * Returns a list of all element keys in the cache, whether or not they are expired.
1604      * <p/>
1605      * The returned keys are unique and can be considered a set.
1606      * <p/>
1607      * The List returned is not live. It is a copy.
1608      * <p/>
1609      * The time taken is O(n). On a single CPU 1.8Ghz P4, approximately 8ms is required
1610      * for each 1000 entries.
1611      *
1612      * @return a list of {@link Object} keys
1613      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1614      */
1615     public final List getKeys() throws IllegalStateException, CacheException {
1616         checkStatus();
1617         return Arrays.asList(compoundStore.getKeyArray());
1618     }
1619 
1620     /***
1621      * Returns a list of all element keys in the cache. Only keys of non-expired
1622      * elements are returned.
1623      * <p/>
1624      * The returned keys are unique and can be considered a set.
1625      * <p/>
1626      * The List returned is not live. It is a copy.
1627      * <p/>
1628      * The time taken is O(n), where n is the number of elements in the cache. On
1629      * a 1.8Ghz P4, the time taken is approximately 200ms per 1000 entries. This method
1630      * is not synchronized, because it relies on a non-live list returned from {@link #getKeys()}
1631      * , which is synchronised, and which takes 8ms per 1000 entries. This way
1632      * cache liveness is preserved, even if this method is very slow to return.
1633      * <p/>
1634      * Consider whether your usage requires checking for expired keys. Because
1635      * this method takes so long, depending on cache settings, the list could be
1636      * quite out of date by the time you get it.
1637      *
1638      * @return a list of {@link Object} keys
1639      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1640      */
1641     public final List getKeysWithExpiryCheck() throws IllegalStateException, CacheException {
1642         List allKeyList = getKeys();
1643         //removeInternal keys of expired elements
1644         ArrayList<Object> nonExpiredKeys = new ArrayList<Object>(allKeyList.size());
1645         int allKeyListSize = allKeyList.size();
1646         for (int i = 0; i < allKeyListSize; i++) {
1647             Object key = allKeyList.get(i);
1648             Element element = getQuiet(key);
1649             if (element != null) {
1650                 nonExpiredKeys.add(key);
1651             }
1652         }
1653         nonExpiredKeys.trimToSize();
1654         return nonExpiredKeys;
1655     }
1656 
1657 
1658     /***
1659      * Returns a list of all elements in the cache, whether or not they are expired.
1660      * <p/>
1661      * The returned keys are not unique and may contain duplicates. If the cache is only
1662      * using the memory store, the list will be unique. If the disk store is being used
1663      * as well, it will likely contain duplicates, because of the internal store design.
1664      * <p/>
1665      * The List returned is not live. It is a copy.
1666      * <p/>
1667      * The time taken is O(log n). On a single CPU 1.8Ghz P4, approximately 6ms is required
1668      * for 1000 entries and 36 for 50000.
1669      * <p/>
1670      * This is the fastest getKeys method
1671      *
1672      * @return a list of {@link Object} keys
1673      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1674      */
1675     public final List getKeysNoDuplicateCheck() throws IllegalStateException {
1676         checkStatus();
1677         return getKeys();
1678     }
1679 
1680     private Element searchInStoreWithStats(Object key, boolean quiet, boolean notifyListeners) {
1681         boolean wasOnDisk = false;
1682         Element element;
1683         if (quiet) {
1684             element = compoundStore.getQuiet(key);
1685         } else {
1686             wasOnDisk = compoundStore.containsKeyOnDisk(key);
1687             element = compoundStore.get(key);
1688         }
1689 
1690         if (element != null) {
1691             if (isExpired(element)) {
1692                 if (LOG.isDebugEnabled()) {
1693                     LOG.debug(configuration.getName() + " cache hit, but element expired");
1694                 }
1695                 if (!quiet) {
1696                     liveCacheStatisticsData.cacheMissExpired();
1697                 }
1698                 tryRemoveImmediately(key, notifyListeners);
1699                 element = null;
1700             } else if (!quiet) {
1701                 element.updateAccessStatistics();
1702                 if (LOG.isDebugEnabled()) {
1703                     LOG.debug(getName() + "Cache: " + getName() + " store hit for " + key);
1704                 }
1705                 if (wasOnDisk) {
1706                     liveCacheStatisticsData.cacheHitOnDisk();
1707                 } else {
1708                     liveCacheStatisticsData.cacheHitInMemory();
1709                 }
1710             }
1711         } else if (LOG.isDebugEnabled()) {
1712             LOG.debug(getName() + "Cache: " + getName() + " store miss for " + key);
1713         }
1714         return element;
1715     }
1716 
1717     private Element searchInStoreWithoutStats(Object key, boolean quiet, boolean notifyListeners) {
1718         Element element = compoundStore.getQuiet(key);
1719 
1720         if (element != null) {
1721             if (isExpired(element)) {
1722                 tryRemoveImmediately(key, notifyListeners);
1723                 element = null;
1724             } else if (!(quiet || skipUpdateAccessStatistics(element))) {
1725                 element.updateAccessStatistics();
1726             }
1727         }
1728         return element;
1729     }
1730 
1731     private void tryRemoveImmediately(final Object key, final boolean notifyListeners) {
1732         Sync syncForKey = ((CacheLockProvider)getInternalContext()).getSyncForKey(key);
1733         boolean writeLocked = false;
1734         try {
1735             writeLocked = syncForKey.tryLock(LockType.WRITE, 0);
1736         } catch (InterruptedException e) {
1737             Thread.currentThread().interrupt();
1738         } catch (Error e) {
1739             if (e.getClass().getName().equals("com.tc.exception.TCLockUpgradeNotSupportedError")) {
1740                 // Safely ignore this
1741             } else {
1742                 throw e;
1743             }
1744         }
1745         if (writeLocked) {
1746             removeInternal(key, true, notifyListeners, false, false);
1747             syncForKey.unlock(LockType.WRITE);
1748         } else {
1749             if (LOG.isDebugEnabled()) {
1750                 LOG.debug(configuration.getName() + " cache: element " + key + " expired, but couldn't be inline evicted");
1751             }
1752         }
1753     }
1754 
1755     private boolean skipUpdateAccessStatistics(Element element) {
1756       return configuration.isFrozen() && element.isEternal()
1757               && (configuration.getMaxElementsInMemory() == 0)
1758               && (!configuration.isOverflowToDisk() || configuration.getMaxElementsOnDisk() == 0);
1759     }
1760 
1761     /***
1762      * Removes an {@link Element} from the Cache. This also removes it from any
1763      * stores it may be in.
1764      * <p/>
1765      * Also notifies the CacheEventListener after the element was removed.
1766      * <p/>
1767      * Synchronization is handled within the method.
1768      * <p/>
1769      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1770      * This exception should be caught in those circumstances.
1771      *
1772      * @param key the element key to operate on
1773      * @return true if the element was removed, false if it was not found in the cache
1774      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1775      */
1776     public final boolean remove(Serializable key) throws IllegalStateException {
1777         return remove((Object) key);
1778     }
1779 
1780     /***
1781      * Removes an {@link Element} from the Cache. This also removes it from any
1782      * stores it may be in.
1783      * <p/>
1784      * Also notifies the CacheEventListener after the element was removed, but only if an Element
1785      * with the key actually existed.
1786      * <p/>
1787      * Synchronization is handled within the method.
1788      * <p/>
1789      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1790      * This exception should be caught in those circumstances.
1791      * <p/>
1792      *
1793      * @param key the element key to operate on
1794      * @return true if the element was removed, false if it was not found in the cache
1795      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1796      * @since 1.2
1797      */
1798     public final boolean remove(Object key) throws IllegalStateException {
1799         return remove(key, false);
1800     }
1801 
1802 
1803     /***
1804      * Removes an {@link Element} from the Cache. This also removes it from any
1805      * stores it may be in.
1806      * <p/>
1807      * Also notifies the CacheEventListener after the element was removed, but only if an Element
1808      * with the key actually existed.
1809      * <p/>
1810      * Synchronization is handled within the method.
1811      * <p/>
1812      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1813      * This exception should be caught in those circumstances.
1814      *
1815      * @param key                         the element key to operate on
1816      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1817      *                                    further notification to doNotNotifyCacheReplicators cache peers
1818      * @return true if the element was removed, false if it was not found in the cache
1819      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1820      */
1821     public final boolean remove(Serializable key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
1822         return remove((Object) key, doNotNotifyCacheReplicators);
1823     }
1824 
1825     /***
1826      * Removes an {@link Element} from the Cache. This also removes it from any
1827      * stores it may be in.
1828      * <p/>
1829      * Also notifies the CacheEventListener after the element was removed, but only if an Element
1830      * with the key actually existed.
1831      * <p/>
1832      * Synchronization is handled within the method.
1833      *
1834      * @param key                         the element key to operate on
1835      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1836      *                                    further notification to doNotNotifyCacheReplicators cache peers
1837      * @return true if the element was removed, false if it was not found in the cache
1838      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1839      */
1840     public final boolean remove(Object key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
1841         return removeInternal(key, false, true, doNotNotifyCacheReplicators, false);
1842     }
1843 
1844     /***
1845      * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
1846      * stores it may be in.
1847      * <p/>
1848      * Listeners are not called.
1849      *
1850      * @param key the element key to operate on
1851      * @return true if the element was removed, false if it was not found in the cache
1852      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1853      */
1854     public final boolean removeQuiet(Serializable key) throws IllegalStateException {
1855         return removeInternal(key, false, false, false, false);
1856     }
1857 
1858     /***
1859      * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
1860      * stores it may be in.
1861      * <p/>
1862      * Listeners are not called.
1863      * <p/>
1864      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1865      * This exception should be caught in those circumstances.
1866      *
1867      * @param key the element key to operate on
1868      * @return true if the element was removed, false if it was not found in the cache
1869      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1870      * @since 1.2
1871      */
1872     public final boolean removeQuiet(Object key) throws IllegalStateException {
1873         return removeInternal(key, false, false, false, false);
1874     }
1875 
1876     /***
1877      * {@inheritDoc}
1878      */
1879     public boolean removeWithWriter(Object key) throws IllegalStateException {
1880         return removeInternal(key, false, true, false, true);
1881     }
1882 
1883     /***
1884      * Removes or expires an {@link Element} from the Cache after an attempt to get it determined that it should be expired.
1885      * This also removes it from any stores it may be in.
1886      * <p/>
1887      * Also notifies the CacheEventListener after the element has expired, but only if an Element
1888      * with the key actually existed.
1889      * <p/>
1890      * Synchronization is handled within the method.
1891      * <p/>
1892      * If a remove was called, listeners are notified, regardless of whether the element existed or not.
1893      * This allows distributed cache listeners to remove elements from a cluster regardless of whether they
1894      * existed locally.
1895      * <p/>
1896      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1897      * This exception should be caught in those circumstances.
1898      *
1899      * @param key                         the element key to operate on
1900      * @param expiry                      if the reason this method is being called is to expire the element
1901      * @param notifyListeners             whether to notify listeners
1902      * @param doNotNotifyCacheReplicators whether not to notify cache replicators
1903      * @param useCacheWriter              if the element should else be removed from the cache writer
1904      * @return true if the element was removed, false if it was not found in the cache
1905      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1906      */
1907     private boolean removeInternal(Object key, boolean expiry, boolean notifyListeners,
1908                            boolean doNotNotifyCacheReplicators, boolean useCacheWriter)
1909             throws IllegalStateException {
1910 
1911         if (useCacheWriter) {
1912             initialiseCacheWriterManager(true);
1913         }
1914 
1915         checkStatus();
1916         Element elementFromStore = null;
1917 
1918         if (useCacheWriter) {
1919             try {
1920                 elementFromStore = compoundStore.removeWithWriter(key, cacheWriterManager);
1921             } catch (CacheWriterManagerException e) {
1922                 if (configuration.getCacheWriterConfiguration().getNotifyListenersOnException()) {
1923                     notifyRemoveInternalListeners(key, expiry, notifyListeners, doNotNotifyCacheReplicators,
1924                             elementFromStore);
1925                 }
1926                 throw e.getCause();
1927             }
1928         } else {
1929             elementFromStore = compoundStore.remove(key);
1930         }
1931 
1932         return notifyRemoveInternalListeners(key, expiry, notifyListeners, doNotNotifyCacheReplicators,
1933                 elementFromStore);
1934     }
1935 
1936     private boolean notifyRemoveInternalListeners(Object key, boolean expiry, boolean notifyListeners, boolean doNotNotifyCacheReplicators,
1937                                                   Element elementFromStore) {
1938         boolean removed = false;
1939         boolean removeNotified = false;
1940 
1941         if (elementFromStore != null) {
1942             if (expiry) {
1943                 //always notify expire which is lazy regardless of the removeQuiet
1944                 registeredEventListeners.notifyElementExpiry(elementFromStore, doNotNotifyCacheReplicators);
1945             } else if (notifyListeners) {
1946                 removeNotified = true;
1947                 registeredEventListeners.notifyElementRemoved(elementFromStore, doNotNotifyCacheReplicators);
1948             }
1949             removed = true;
1950         }
1951 
1952         //If we are trying to remove an element which does not exist locally, we should still notify so that
1953         //cluster invalidations work.
1954         if (notifyListeners && !expiry && !removeNotified) {
1955             Element syntheticElement = new Element(key, null);
1956             registeredEventListeners.notifyElementRemoved(syntheticElement, doNotNotifyCacheReplicators);
1957         }
1958 
1959         return removed;
1960     }
1961 
1962     /***
1963      * Removes all cached items.
1964      * Synchronization is handled within the method.
1965      * <p/>
1966      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1967      * This exception should be caught in those circumstances.
1968      *
1969      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1970      */
1971     public void removeAll() throws IllegalStateException, CacheException {
1972         removeAll(false);
1973     }
1974 
1975 
1976     /***
1977      * Removes all cached items.
1978      * Synchronization is handled within the method.
1979      * <p/>
1980      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1981      * This exception should be caught in those circumstances.
1982      *
1983      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1984      */
1985     public void removeAll(boolean doNotNotifyCacheReplicators) throws IllegalStateException, CacheException {
1986         checkStatus();
1987         compoundStore.removeAll();
1988         registeredEventListeners.notifyRemoveAll(doNotNotifyCacheReplicators);
1989     }
1990 
1991     /***
1992      * Starts an orderly shutdown of the Cache. Steps are:
1993      * <ol>
1994      * <li>Completes any outstanding CacheLoader loads.
1995      * <li>Completes any outstanding CacheWriter operations.
1996      * <li>Disposes any cache extensions.
1997      * <li>Disposes any cache event listeners. The listeners normally complete, so for example distributed caching operations will complete.
1998      * <li>Flushes all cache items from memory to the disk store, if any
1999      * <li>changes status to shutdown, so that any cache operations after this point throw IllegalStateException
2000      * </ol>
2001      * This method should be invoked only by CacheManager, as a cache's lifecycle is bound into that of it's cache manager.
2002      *
2003      * @throws IllegalStateException if the cache is already {@link Status#STATUS_SHUTDOWN}
2004      */
2005     public synchronized void dispose() throws IllegalStateException {
2006         if (checkStatusAlreadyDisposed()) {
2007             return;
2008         }
2009 
2010         if (executorService != null) {
2011             executorService.shutdown();
2012         }
2013 
2014         disposeRegisteredCacheExtensions();
2015         disposeRegisteredCacheLoaders();
2016         disposeRegisteredCacheWriter();
2017         registeredEventListeners.dispose();
2018 
2019         if (cacheWriterManager != null) {
2020             cacheWriterManager.dispose();
2021         }
2022 
2023         if (compoundStore != null) {
2024             compoundStore.removeStoreListener(this);
2025             compoundStore.dispose();
2026         }
2027         
2028         changeStatus(Status.STATUS_SHUTDOWN);
2029     }
2030 
2031     private void initialiseRegisteredCacheExtensions() {
2032         for (CacheExtension cacheExtension : registeredCacheExtensions) {
2033             cacheExtension.init();
2034         }
2035     }
2036 
2037     private void disposeRegisteredCacheExtensions() {
2038         for (CacheExtension cacheExtension : registeredCacheExtensions) {
2039             cacheExtension.dispose();
2040         }
2041     }
2042 
2043     private void initialiseRegisteredCacheLoaders() {
2044         for (CacheLoader cacheLoader : registeredCacheLoaders) {
2045             cacheLoader.init();
2046         }
2047     }
2048 
2049     private void disposeRegisteredCacheLoaders() {
2050         for (CacheLoader cacheLoader : registeredCacheLoaders) {
2051             cacheLoader.dispose();
2052         }
2053     }
2054 
2055     private void initialiseRegisteredCacheWriter() {
2056         CacheWriter writer = registeredCacheWriter;
2057         if (writer != null) {
2058             writer.init();
2059         }
2060     }
2061 
2062     private void disposeRegisteredCacheWriter() {
2063         CacheWriter writer = registeredCacheWriter;
2064         if (writer != null) {
2065             writer.dispose();
2066         }
2067     }
2068 
2069     /***
2070      * Gets the cache configuration this cache was created with.
2071      * <p/>
2072      * Things like listeners that are added dynamically are excluded.
2073      */
2074     public CacheConfiguration getCacheConfiguration() {
2075         return configuration;
2076     }
2077 
2078 
2079     /***
2080      * Flushes all cache items from memory to the disk store, and from the DiskStore to disk.
2081      *
2082      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2083      */
2084     public final synchronized void flush() throws IllegalStateException, CacheException {
2085         checkStatus();
2086         try {
2087             compoundStore.flush();
2088         } catch (IOException e) {
2089             throw new CacheException("Unable to flush cache: " + configuration.getName()
2090                     + ". Initial cause was " + e.getMessage(), e);
2091         }
2092     }
2093 
2094     /***
2095      * Gets the size of the cache. This is a subtle concept. See below.
2096      * <p/>
2097      * This number is the actual number of elements, including expired elements
2098      * that have not been removed.
2099      * <p/>
2100      * Expired elements are removed from the the memory store when getting an
2101      * expired element, or when attempting to spool an expired element to disk.
2102      * <p/>
2103      * Expired elements are removed from the disk store when getting an expired
2104      * element, or when the expiry thread runs, which is once every five
2105      * minutes.
2106      * <p/>
2107      * To get an exact size, which would exclude expired elements, use
2108      * {@link #getKeysWithExpiryCheck()}.size(), although see that method for
2109      * the approximate time that would take.
2110      * <p/>
2111      * To get a very fast result, use {@link #getKeysNoDuplicateCheck()}.size().
2112      * If the disk store is being used, there will be some duplicates.
2113      *
2114      * @return The size value
2115      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2116      */
2117     public final int getSize() throws IllegalStateException, CacheException {
2118         checkStatus();
2119 
2120         if (compoundStore.isCacheCoherent()) {
2121             return compoundStore.getTerracottaClusteredSize();
2122         } else {
2123             return compoundStore.getSize();
2124         }
2125     }
2126 
2127     /***
2128      * {@inheritDoc}
2129      */
2130     public int getSizeBasedOnAccuracy(int statisticsAccuracy)
2131             throws IllegalStateException, CacheException {
2132         if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_BEST_EFFORT) {
2133             return getSize();
2134         } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_GUARANTEED) {
2135             return getKeysWithExpiryCheck().size();
2136         } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_NONE) {
2137             return getKeysNoDuplicateCheck().size();
2138         }
2139         throw new IllegalArgumentException("Unknown statistics accuracy: "
2140                 + statisticsAccuracy);
2141     }
2142 
2143     /***
2144      * Gets the size of the memory store for this cache. This method relies on calculating
2145      * Serialized sizes. If the Element values are not Serializable they will show as zero.
2146      * <p/>
2147      * Warning: This method can be very expensive to run. Allow approximately 1 second
2148      * per 1MB of entries. Running this method could create liveness problems
2149      * because the object lock is held for a long period
2150      * <p/>
2151      *
2152      * @return the approximate size of the memory store in bytes
2153      * @throws IllegalStateException
2154      */
2155     public final long calculateInMemorySize() throws IllegalStateException, CacheException {
2156         checkStatus();
2157         return compoundStore.getInMemorySizeInBytes();
2158     }
2159 
2160 
2161     /***
2162      * Returns the number of elements in the memory store.
2163      *
2164      * @return the number of elements in the memory store
2165      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2166      */
2167     public final long getMemoryStoreSize() throws IllegalStateException {
2168         checkStatus();
2169         return compoundStore.getInMemorySize();
2170     }
2171 
2172     /***
2173      * Returns the number of elements in the disk store.
2174      *
2175      * @return the number of elements in the disk store.
2176      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2177      */
2178     public final int getDiskStoreSize() throws IllegalStateException {
2179         checkStatus();
2180         if (isTerracottaClustered()) {
2181             return compoundStore.getTerracottaClusteredSize();
2182         } else {
2183             return compoundStore.getOnDiskSize();
2184         }
2185     }
2186 
2187     /***
2188      * Gets the status attribute of the Cache.
2189      *
2190      * @return The status value from the Status enum class
2191      */
2192     public final Status getStatus() {
2193         return status;
2194     }
2195 
2196 
2197     private void checkStatus() throws IllegalStateException {
2198         if (!status.equals(Status.STATUS_ALIVE)) {
2199             throw new IllegalStateException("The " + configuration.getName() + " Cache is not alive.");
2200         }
2201     }
2202     
2203     private boolean checkStatusAlreadyDisposed() throws IllegalStateException {
2204         return status.equals(Status.STATUS_SHUTDOWN);
2205     }
2206 
2207 
2208     /***
2209      * Gets the cache name.
2210      */
2211     public final String getName() {
2212         return configuration.getName();
2213     }
2214 
2215     /***
2216      * Sets the cache name which will name.
2217      *
2218      * @param name the name of the cache. Should not be null. Should also not contain any '/' characters, as these interfere
2219      *             with distribution
2220      * @throws IllegalArgumentException if an illegal name is used.
2221      */
2222     public final void setName(String name) throws IllegalArgumentException {
2223         if (!status.equals(Status.STATUS_UNINITIALISED)) {
2224             throw new IllegalStateException("Only uninitialised caches can have their names set.");
2225         }
2226         configuration.setName(name);
2227     }
2228 
2229     /***
2230      * Returns a {@link String} representation of {@link Cache}.
2231      */
2232     @Override
2233     public String toString() {
2234         StringBuilder dump = new StringBuilder();
2235 
2236         dump.append("[")
2237                 .append(" name = ").append(configuration.getName())
2238                 .append(" status = ").append(status)
2239                 .append(" eternal = ").append(configuration.isEternal())
2240                 .append(" overflowToDisk = ").append(configuration.isOverflowToDisk())
2241                 .append(" maxElementsInMemory = ").append(configuration.getMaxElementsInMemory())
2242                 .append(" maxElementsOnDisk = ").append(configuration.getMaxElementsOnDisk())
2243                 .append(" memoryStoreEvictionPolicy = ").append(configuration.getMemoryStoreEvictionPolicy())
2244                 .append(" timeToLiveSeconds = ").append(configuration.getTimeToLiveSeconds())
2245                 .append(" timeToIdleSeconds = ").append(configuration.getTimeToIdleSeconds())
2246                 .append(" diskPersistent = ").append(configuration.isDiskPersistent())
2247                 .append(" diskExpiryThreadIntervalSeconds = ").append(configuration.getDiskExpiryThreadIntervalSeconds())
2248                 .append(registeredEventListeners)
2249                 .append(" hitCount = ").append(getLiveCacheStatisticsNoCheck().getCacheHitCount())
2250                 .append(" memoryStoreHitCount = ").append(getLiveCacheStatisticsNoCheck().getInMemoryHitCount())
2251                 .append(" diskStoreHitCount = ").append(getLiveCacheStatisticsNoCheck().getOnDiskHitCount())
2252                 .append(" missCountNotFound = ").append(getLiveCacheStatisticsNoCheck().getCacheMissCount())
2253                 .append(" missCountExpired = ").append(getLiveCacheStatisticsNoCheck().getCacheMissCountExpired())
2254                 .append(" ]");
2255 
2256         return dump.toString();
2257     }
2258 
2259 
2260     /***
2261      * Checks whether this cache element has expired.
2262      * <p/>
2263      * The element is expired if:
2264      * <ol>
2265      * <li> the idle time is non-zero and has elapsed, unless the cache is eternal; or
2266      * <li> the time to live is non-zero and has elapsed, unless the cache is eternal; or
2267      * <li> the value of the element is null.
2268      * </ol>
2269      *
2270      * @return true if it has expired
2271      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2272      * @throws NullPointerException  if the element is null
2273      *                               todo this does not need to be synchronized
2274      */
2275     public final boolean isExpired(Element element) throws IllegalStateException, NullPointerException {
2276         checkStatus();
2277         return element.isExpired(configuration);
2278     }
2279 
2280 
2281     /***
2282      * Clones a cache. This is only legal if the cache has not been
2283      * initialized. At that point only primitives have been set and no
2284      * stores have been created.
2285      * <p/>
2286      * A new, empty, RegisteredEventListeners is created on clone.
2287      * <p/>
2288      *
2289      * @return an object of type {@link Cache}
2290      * @throws CloneNotSupportedException
2291      */
2292     @Override
2293     public final Cache clone() throws CloneNotSupportedException {
2294         if (compoundStore != null) {
2295             throw new CloneNotSupportedException("Cannot clone an initialized cache.");
2296         }
2297         Cache copy = (Cache) super.clone();
2298         // create new copies of the statistics
2299         copy.liveCacheStatisticsData = new LiveCacheStatisticsWrapper(copy);
2300         copy.sampledCacheStatistics = new SampledCacheStatisticsWrapper();
2301 
2302         copy.configuration = configuration.clone();
2303         copy.guid = createGuid();
2304 
2305         RegisteredEventListeners registeredEventListenersFromCopy = copy.getCacheEventNotificationService();
2306         if (registeredEventListenersFromCopy == null || registeredEventListenersFromCopy.getCacheEventListeners().size() == 0) {
2307             copy.registeredEventListeners = new RegisteredEventListeners(copy);
2308         } else {
2309             copy.registeredEventListeners = new RegisteredEventListeners(copy);
2310             Set cacheEventListeners = registeredEventListeners.getCacheEventListeners();
2311             for (Object cacheEventListener1 : cacheEventListeners) {
2312                 CacheEventListener cacheEventListener = (CacheEventListener) cacheEventListener1;
2313                 CacheEventListener cacheEventListenerClone = (CacheEventListener) cacheEventListener.clone();
2314                 copy.registeredEventListeners.registerListener(cacheEventListenerClone);
2315             }
2316         }
2317 
2318 
2319         copy.registeredCacheExtensions = new CopyOnWriteArrayList<CacheExtension>();
2320         for (CacheExtension registeredCacheExtension : registeredCacheExtensions) {
2321             copy.registerCacheExtension(registeredCacheExtension.clone(copy));
2322         }
2323 
2324         copy.registeredCacheLoaders = new CopyOnWriteArrayList<CacheLoader>();
2325         for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
2326             copy.registerCacheLoader(registeredCacheLoader.clone(copy));
2327         }
2328 
2329         if (registeredCacheWriter != null) {
2330             copy.registerCacheWriter(registeredCacheWriter.clone(copy));
2331         }
2332 
2333         if (bootstrapCacheLoader != null) {
2334             BootstrapCacheLoader bootstrapCacheLoaderClone = (BootstrapCacheLoader) bootstrapCacheLoader.clone();
2335             copy.setBootstrapCacheLoader(bootstrapCacheLoaderClone);
2336         }
2337 
2338         return copy;
2339     }
2340 
2341     /***
2342      * Gets the internal Store.
2343      * 
2344      * @return the Store referenced by this cache
2345      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2346      */
2347     final Store getStore() throws IllegalStateException {
2348         checkStatus();
2349         return compoundStore;
2350     }
2351     
2352     /***
2353      * Use this to access the service in order to register and unregister listeners
2354      *
2355      * @return the RegisteredEventListeners instance for this cache.
2356      */
2357     public final RegisteredEventListeners getCacheEventNotificationService() {
2358         return registeredEventListeners;
2359     }
2360 
2361 
2362     /***
2363      * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
2364      *
2365      * @return true if an element matching the key is found in memory
2366      */
2367     public final boolean isElementInMemory(Serializable key) {
2368         return isElementInMemory((Object) key);
2369     }
2370 
2371     /***
2372      * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
2373      *
2374      * @return true if an element matching the key is found in memory
2375      * @since 1.2
2376      */
2377     public final boolean isElementInMemory(Object key) {
2378         return compoundStore.containsKeyInMemory(key);
2379     }
2380 
2381     /***
2382      * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
2383      *
2384      * @return true if an element matching the key is found in the diskStore
2385      */
2386     public final boolean isElementOnDisk(Serializable key) {
2387         return isElementOnDisk((Object) key);
2388     }
2389 
2390     /***
2391      * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
2392      *
2393      * @return true if an element matching the key is found in the diskStore
2394      * @since 1.2
2395      */
2396     public final boolean isElementOnDisk(Object key) {
2397         return compoundStore.containsKeyOnDisk(key);
2398     }
2399 
2400     /***
2401      * The GUID for this cache instance can be used to determine whether two cache instance references
2402      * are pointing to the same cache.
2403      *
2404      * @return the globally unique identifier for this cache instance. This is guaranteed to be unique.
2405      * @since 1.2
2406      */
2407     public final String getGuid() {
2408         return guid;
2409     }
2410 
2411     /***
2412      * Gets the CacheManager managing this cache. For a newly created cache this will be null until
2413      * it has been added to a CacheManager.
2414      *
2415      * @return the manager or null if there is none
2416      */
2417     public final CacheManager getCacheManager() {
2418         return cacheManager;
2419     }
2420 
2421 
2422     /***
2423      * Resets statistics counters back to 0.
2424      *
2425      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2426      */
2427     public void clearStatistics() throws IllegalStateException {
2428         checkStatus();
2429         liveCacheStatisticsData.clearStatistics();
2430         sampledCacheStatistics.clearStatistics();
2431         registeredEventListeners.clearCounters();
2432     }
2433 
2434     /***
2435      * Accurately measuring statistics can be expensive. Returns the current accuracy setting.
2436      *
2437      * @return one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
2438      */
2439     public int getStatisticsAccuracy() {
2440         return getLiveCacheStatistics().getStatisticsAccuracy();
2441     }
2442 
2443     /***
2444      * Sets the statistics accuracy.
2445      *
2446      * @param statisticsAccuracy one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
2447      */
2448     public void setStatisticsAccuracy(int statisticsAccuracy) {
2449         int oldValue = getStatisticsAccuracy();
2450         if (statisticsAccuracy != oldValue) {
2451             liveCacheStatisticsData.setStatisticsAccuracy(statisticsAccuracy);
2452             firePropertyChange("StatisticsAccuracy", oldValue, statisticsAccuracy);
2453         }
2454     }
2455 
2456     /***
2457      * Causes all elements stored in the Cache to be synchronously checked for expiry, and if expired, evicted.
2458      */
2459     public void evictExpiredElements() {
2460         compoundStore.expireElements();
2461     }
2462 
2463     /***
2464      * An inexpensive check to see if the key exists in the cache.
2465      * <p/>
2466      * This method is not synchronized. It is possible that an element may exist in the cache and be removed
2467      * before the check gets to it, or vice versa.
2468      *
2469      * @param key the key to check.
2470      * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
2471      */
2472     public boolean isKeyInCache(Object key) {
2473         if (key == null) {
2474             return false;
2475         }
2476         return isElementInMemory(key) || isElementOnDisk(key);
2477     }
2478 
2479     /***
2480      * An extremely expensive check to see if the value exists in the cache. This implementation is O(n). Ehcache
2481      * is not designed for efficient access in this manner.
2482      * <p/>
2483      * This method is not synchronized. It is possible that an element may exist in the cache and be removed
2484      * before the check gets to it, or vice versa. Because it is slow to execute the probability of that this will
2485      * have happened.
2486      *
2487      * @param value to check for
2488      * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
2489      */
2490     public boolean isValueInCache(Object value) {
2491         for (Object key : getKeys()) {
2492             Element element = get(key);
2493             if (element != null) {
2494                 Object elementValue = element.getValue();
2495                 if (elementValue == null) {
2496                     if (value == null) {
2497                         return true;
2498                     }
2499                 } else {
2500                     if (elementValue.equals(value)) {
2501                         return true;
2502                     }
2503                 }
2504             }
2505         }
2506         return false;
2507     }
2508 
2509     /***
2510      * {@inheritDoc}
2511      * <p/>
2512      * Note, the {@link #getSize} method will have the same value as the size
2513      * reported by Statistics for the statistics accuracy of
2514      * {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}.
2515      */
2516     public Statistics getStatistics() throws IllegalStateException {
2517         int size = getSizeBasedOnAccuracy(getLiveCacheStatistics()
2518                 .getStatisticsAccuracy());
2519         return new Statistics(this, getLiveCacheStatistics()
2520                 .getStatisticsAccuracy(), getLiveCacheStatistics()
2521                 .getCacheHitCount(), getLiveCacheStatistics()
2522                 .getOnDiskHitCount(), getLiveCacheStatistics()
2523                 .getInMemoryHitCount(), getLiveCacheStatistics()
2524                 .getCacheMissCount(), size, getAverageGetTime(),
2525                 getLiveCacheStatistics().getEvictedCount(),
2526                 getMemoryStoreSize(), getDiskStoreSize());
2527     }
2528 
2529     /***
2530      * For use by CacheManager.
2531      *
2532      * @param cacheManager the CacheManager for this cache to use.
2533      */
2534     public void setCacheManager(CacheManager cacheManager) {
2535         CacheManager oldValue = getCacheManager();
2536         this.cacheManager = cacheManager;
2537         firePropertyChange("CacheManager", oldValue, cacheManager);
2538     }
2539 
2540     /***
2541      * Accessor for the BootstrapCacheLoader associated with this cache. For testing purposes.
2542      */
2543     public BootstrapCacheLoader getBootstrapCacheLoader() {
2544         return bootstrapCacheLoader;
2545     }
2546 
2547     /***
2548      * Sets the bootstrap cache loader.
2549      *
2550      * @param bootstrapCacheLoader the loader to be used
2551      * @throws CacheException if this method is called after the cache is initialized
2552      */
2553     public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader) throws CacheException {
2554         if (!status.equals(Status.STATUS_UNINITIALISED)) {
2555             throw new CacheException("A bootstrap cache loader can only be set before the cache is initialized. "
2556                     + configuration.getName());
2557         }
2558         BootstrapCacheLoader oldValue = getBootstrapCacheLoader();
2559         this.bootstrapCacheLoader = bootstrapCacheLoader;
2560         firePropertyChange("BootstrapCacheLoader", oldValue, bootstrapCacheLoader);
2561     }
2562 
2563     /***
2564      * DiskStore paths can conflict between CacheManager instances. This method allows the path to be changed.
2565      *
2566      * @param diskStorePath the new path to be used.
2567      * @throws CacheException if this method is called after the cache is initialized
2568      */
2569     public void setDiskStorePath(String diskStorePath) throws CacheException {
2570         if (!status.equals(Status.STATUS_UNINITIALISED)) {
2571             throw new CacheException("A DiskStore path can only be set before the cache is initialized. "
2572                     + configuration.getName());
2573         }
2574         String oldValue = this.diskStorePath;
2575         synchronized (this) {
2576             this.diskStorePath = diskStorePath;
2577         }
2578         firePropertyChange("DiskStorePath", oldValue, diskStorePath);
2579     }
2580 
2581     /***
2582      * An equals method which follows the contract of {@link Object#equals(Object)}
2583      * <p/>
2584      * An Cache is equal to another one if it implements Ehcache and has the same GUID.
2585      *
2586      * @param object the reference object with which to compare.
2587      * @return <code>true</code> if this object is the same as the obj
2588      *         argument; <code>false</code> otherwise.
2589      * @see #hashCode()
2590      * @see java.util.Hashtable
2591      */
2592     @Override
2593     public boolean equals(Object object) {
2594         if (object == null) {
2595             return false;
2596         }
2597         if (!(object instanceof Ehcache)) {
2598             return false;
2599         }
2600         Ehcache other = (Ehcache) object;
2601         return guid.equals(other.getGuid());
2602     }
2603 
2604     /***
2605      * Returns a hash code value for the object. This method is
2606      * supported for the benefit of hashtables such as those provided by
2607      * <code>java.util.Hashtable</code>.
2608      * <p/>
2609      * The general contract of <code>hashCode</code> is:
2610      * <ul>
2611      * <li>Whenever it is invoked on the same object more than once during
2612      * an execution of a Java application, the <tt>hashCode</tt> method
2613      * must consistently return the same integer, provided no information
2614      * used in <tt>equals</tt> comparisons on the object is modified.
2615      * This integer need not remain consistent from one execution of an
2616      * application to another execution of the same application.
2617      * <li>If two objects are equal according to the <tt>equals(Object)</tt>
2618      * method, then calling the <code>hashCode</code> method on each of
2619      * the two objects must produce the same integer result.
2620      * <li>It is <em>not</em> required that if two objects are unequal
2621      * according to the {@link Object#equals(Object)}
2622      * method, then calling the <tt>hashCode</tt> method on each of the
2623      * two objects must produce distinct integer results.  However, the
2624      * programmer should be aware that producing distinct integer results
2625      * for unequal objects may improve the performance of hashtables.
2626      * </ul>
2627      * <p/>
2628      * As much as is reasonably practical, the hashCode method defined by
2629      * class <tt>Object</tt> does return distinct integers for distinct
2630      * objects. (This is typically implemented by converting the internal
2631      * address of the object into an integer, but this implementation
2632      * technique is not required by the
2633      * Java<font size="-2"><sup>TM</sup></font> programming language.)
2634      * <p/>
2635      * This implementation use the GUID of the cache.
2636      *
2637      * @return a hash code value for this object.
2638      * @see Object#equals(Object)
2639      * @see java.util.Hashtable
2640      */
2641     @Override
2642     public int hashCode() {
2643         return guid.hashCode();
2644     }
2645 
2646 
2647     /***
2648      * Create globally unique ID for this cache.
2649      */
2650     private String createGuid() {
2651         StringBuilder buffer = new StringBuilder().append(localhost).append("-").append(UUID.randomUUID());
2652         return buffer.toString();
2653     }
2654 
2655     /***
2656      * Register a {@link CacheExtension} with the cache. It will then be tied into the cache lifecycle.
2657      * <p/>
2658      * If the CacheExtension is not initialised, initialise it.
2659      */
2660     public void registerCacheExtension(CacheExtension cacheExtension) {
2661         registeredCacheExtensions.add(cacheExtension);
2662     }
2663 
2664     /***
2665      * @return the cache extensions as a live list
2666      */
2667     public List<CacheExtension> getRegisteredCacheExtensions() {
2668         return registeredCacheExtensions;
2669     }
2670 
2671 
2672     /***
2673      * Unregister a {@link CacheExtension} with the cache. It will then be detached from the cache lifecycle.
2674      */
2675     public void unregisterCacheExtension(CacheExtension cacheExtension) {
2676         cacheExtension.dispose();
2677         registeredCacheExtensions.remove(cacheExtension);
2678     }
2679 
2680 
2681     /***
2682      * The average get time in ms.
2683      */
2684     public float getAverageGetTime() {
2685         return getLiveCacheStatistics().getAverageGetTimeMillis();
2686     }
2687 
2688     /***
2689      * Sets an ExceptionHandler on the Cache. If one is already set, it is overwritten.
2690      * <p/>
2691      * The ExceptionHandler is only used if this Cache's methods are accessed using
2692      * {@link net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy}.
2693      *
2694      * @see net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy
2695      */
2696     public void setCacheExceptionHandler(CacheExceptionHandler cacheExceptionHandler) {
2697         CacheExceptionHandler oldValue = getCacheExceptionHandler();
2698         this.cacheExceptionHandler = cacheExceptionHandler;
2699         firePropertyChange("CacheExceptionHandler", oldValue, cacheExceptionHandler);
2700     }
2701 
2702     /***
2703      * Gets the ExceptionHandler on this Cache, or null if there isn't one.
2704      * <p/>
2705      * The ExceptionHandler is only used if this Cache's methods are accessed using
2706      * {@link net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy}.
2707      *
2708      * @see net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy
2709      */
2710     public CacheExceptionHandler getCacheExceptionHandler() {
2711         return cacheExceptionHandler;
2712     }
2713 
2714     /***
2715      * Register a {@link CacheLoader} with the cache. It will then be tied into the cache lifecycle.
2716      * <p/>
2717      * If the CacheLoader is not initialised, initialise it.
2718      *
2719      * @param cacheLoader A Cache Loader to register
2720      */
2721     public void registerCacheLoader(CacheLoader cacheLoader) {
2722         registeredCacheLoaders.add(cacheLoader);
2723     }
2724 
2725     /***
2726      * Unregister a {@link CacheLoader} with the cache. It will then be detached from the cache lifecycle.
2727      *
2728      * @param cacheLoader A Cache Loader to unregister
2729      */
2730     public void unregisterCacheLoader(CacheLoader cacheLoader) {
2731         registeredCacheLoaders.remove(cacheLoader);
2732     }
2733 
2734 
2735     /***
2736      * @return the cache loaders as a live list
2737      */
2738     public List<CacheLoader> getRegisteredCacheLoaders() {
2739         return registeredCacheLoaders;
2740     }
2741 
2742     /***
2743      * {@inheritDoc}
2744      */
2745     public void registerCacheWriter(CacheWriter cacheWriter) {
2746         synchronized (this) {
2747             this.registeredCacheWriter = cacheWriter;
2748             if (!status.equals(Status.STATUS_UNINITIALISED)) {
2749                 initialiseRegisteredCacheWriter();
2750             }
2751         }
2752         initialiseCacheWriterManager(false);
2753     }
2754 
2755     /***
2756      * {@inheritDoc}
2757      */
2758     public void unregisterCacheWriter() {
2759         if (cacheWriterManagerInitFlag.get()) {
2760             throw new CacheException("Cache: " + configuration.getName() + " has its cache writer being unregistered " +
2761                     "after it was already initialised.");
2762         }
2763         this.registeredCacheWriter = null;
2764     }
2765 
2766     /***
2767      * {@inheritDoc}
2768      */
2769     public CacheWriter getRegisteredCacheWriter() {
2770         return this.registeredCacheWriter;
2771     }
2772 
2773     /***
2774      * Does the asynchronous loading.
2775      *
2776      * @param key
2777      * @param specificLoader a specific loader to use. If null the default loader is used.
2778      * @param argument
2779      * @return a Future which can be used to monitor execution
2780      */
2781     Future asynchronousLoad(final Object key, final CacheLoader specificLoader, final Object argument) {
2782         return getExecutorService().submit(new Runnable() {
2783 
2784             /***
2785              * Calls the CacheLoader and puts the result in the Cache
2786              */
2787             public void run() throws CacheException {
2788                 try {
2789                     //Test to see if it has turned up in the meantime
2790                     boolean existsOnRun = isKeyInCache(key);
2791                     if (!existsOnRun) {
2792                         Object value;
2793                         if (specificLoader == null) {
2794                             if (registeredCacheLoaders.size() == 0) {
2795                                 return;
2796                             }
2797                             value = loadWithRegisteredLoaders(argument, key);
2798                         } else {
2799                             if (argument == null) {
2800                                 value = specificLoader.load(key);
2801                             } else {
2802                                 value = specificLoader.load(key, argument);
2803                             }
2804                         }
2805                         if (value != null) {
2806                             put(new Element(key, value), false);
2807                         }
2808                     }
2809                 } catch (Throwable e) {
2810                     if (LOG.isDebugEnabled()) {
2811                         LOG.debug("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
2812                     }
2813                     throw new CacheException("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
2814                 }
2815             }
2816         });
2817     }
2818 
2819     private Object loadWithRegisteredLoaders(Object argument, Object key) throws CacheException {
2820 
2821         Object value = null;
2822 
2823         if (argument == null) {
2824             for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
2825                 value = registeredCacheLoader.load(key);
2826                 if (value != null) {
2827                     break;
2828                 }
2829             }
2830         } else {
2831             for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
2832                 value = registeredCacheLoader.load(key, argument);
2833                 if (value != null) {
2834                     break;
2835                 }
2836             }
2837         }
2838         return value;
2839     }
2840 
2841 
2842     /***
2843      * Creates a future to perform the load
2844      *
2845      * @param keys
2846      * @param argument the loader argument
2847      * @return a Future which can be used to monitor execution
2848      */
2849     Future asynchronousLoadAll(final Collection keys, final Object argument) {
2850         return getExecutorService().submit(new Runnable() {
2851             /***
2852              * Calls the CacheLoader and puts the result in the Cache
2853              */
2854             public void run() {
2855                 try {
2856                     Set<Object> nonLoadedKeys = new HashSet<Object>();
2857                     for (Object key : keys) {
2858                         if (!isKeyInCache(key)) {
2859                             nonLoadedKeys.add(key);
2860                         }
2861                     }
2862                     Map map = loadWithRegisteredLoaders(argument, nonLoadedKeys);
2863                     for (Object key : map.keySet()) {
2864                         put(new Element(key, map.get(key)));
2865                     }
2866                 } catch (Throwable e) {
2867                     if (LOG.isErrorEnabled()) {
2868                         LOG.error("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
2869                     }
2870                 }
2871             }
2872         });
2873     }
2874 
2875     /***
2876      * Does the asynchronous loading.
2877      *
2878      * @param argument      the loader argument
2879      * @param nonLoadedKeys the Set of keys that are already in the Cache
2880      * @return A map of loaded elements
2881      */
2882     Map loadWithRegisteredLoaders(Object argument, Set<Object> nonLoadedKeys) {
2883         Map result = new HashMap();
2884         for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
2885             if (nonLoadedKeys.isEmpty()) {
2886                 break;
2887             }
2888 
2889             Map resultForThisCacheLoader = null;
2890             if (argument == null) {
2891                 resultForThisCacheLoader = registeredCacheLoader.loadAll(nonLoadedKeys);
2892             } else {
2893                 resultForThisCacheLoader = registeredCacheLoader.loadAll(nonLoadedKeys, argument);
2894             }
2895             if (resultForThisCacheLoader != null) {
2896                 nonLoadedKeys.removeAll(resultForThisCacheLoader.keySet());
2897                 result.putAll(resultForThisCacheLoader);
2898             }
2899         }
2900         return result;
2901     }
2902 
2903     /***
2904      * @return Gets the executor service. This is not publically accessible.
2905      */
2906     ExecutorService getExecutorService() {
2907         if (executorService == null) {
2908             synchronized (this) {
2909                 boolean inGoogleAppEngine;
2910 
2911                 try {
2912                     Class.forName("com.google.apphosting.api.DeadlineExceededException");
2913                     inGoogleAppEngine = true;
2914                 } catch (ClassNotFoundException cnfe) {
2915                     inGoogleAppEngine = false;
2916                 }
2917 
2918                 if (inGoogleAppEngine) {
2919 
2920                     // no Thread support. Run all tasks on the caller thread
2921                     executorService = new AbstractExecutorService() {
2922                         /*** {@inheritDoc} */
2923                         public void execute(Runnable command) {
2924                             command.run();
2925                         }
2926 
2927                         /*** {@inheritDoc} */
2928                         public List<Runnable> shutdownNow() {
2929                             return Collections.emptyList();
2930                         }
2931 
2932                         /*** {@inheritDoc} */
2933                         public void shutdown() {
2934                         }
2935 
2936                         /*** {@inheritDoc} */
2937                         public boolean isTerminated() {
2938                             return isShutdown();
2939                         }
2940 
2941                         /*** {@inheritDoc} */
2942                         public boolean isShutdown() {
2943                             return false;
2944                         }
2945 
2946                         /*** {@inheritDoc} */
2947                         public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
2948                             return true;
2949                         }
2950                     };
2951                 } else {
2952                     // we can create Threads
2953                     executorService = new ThreadPoolExecutor(EXECUTOR_CORE_POOL_SIZE, EXECUTOR_MAXIMUM_POOL_SIZE, EXECUTOR_KEEP_ALIVE_TIME,
2954                             TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), new NamedThreadFactory("Cache Executor Service"));
2955                 }
2956             }
2957         }
2958         return executorService;
2959     }
2960 
2961 
2962     /***
2963      * Whether this cache is disabled. "Disabled" means:
2964      * <ol>
2965      * <li>bootstrap is disabled</li>
2966      * <li>puts are discarded</li>
2967      * <li>putQuiets are discarded</li>
2968      * <li>gets return null</li>
2969      * </ol>
2970      * In all other respects the cache continues as it is.
2971      * <p/>
2972      * You can disable and enable a cache programmatically through the {@link #setDisabled(boolean)} method.
2973      * <p/>
2974      * By default caches are enabled on creation, unless the <code>net.sf.ehcache.disabled</code> system
2975      * property is set.
2976      *
2977      * @return true if the cache is disabled.
2978      * @see #NET_SF_EHCACHE_DISABLED ?
2979      */
2980     public boolean isDisabled() {
2981         return disabled;
2982     }
2983 
2984     /***
2985      * Disables or enables this cache. This call overrides the previous value of disabled, even if the
2986      * <code>net.sf.ehcache.disabled</code> system property is set
2987      * <p/>
2988      *
2989      * @param disabled true if you wish to disable, false to enable
2990      * @see #isDisabled()
2991      */
2992     public void setDisabled(boolean disabled) {
2993         if (allowDisable) {
2994             boolean oldValue = isDisabled();
2995             if (oldValue != disabled) {
2996                 synchronized (this) {
2997                     this.disabled = disabled;
2998                 }
2999                 firePropertyChange("Disabled", oldValue, disabled);
3000             }
3001         } else {
3002             throw new CacheException("Dynamic cache features are disabled");
3003         }
3004     }
3005 
3006     /***
3007      * @return the current in-memory eviction policy. This may not be the configured policy, if it has been
3008      *         dynamically set.
3009      */
3010     public Policy getMemoryStoreEvictionPolicy() {
3011         return compoundStore.getInMemoryEvictionPolicy();
3012     }
3013 
3014     /***
3015      * Sets the eviction policy strategy. The Cache will use a policy at startup. There
3016      * are three policies which can be configured: LRU, LFU and FIFO. However many other
3017      * policies are possible. That the policy has access to the whole element enables policies
3018      * based on the key, value, metadata, statistics, or a combination of any of the above.
3019      * It is safe to change the policy of a store at any time. The new policy takes effect
3020      * immediately.
3021      *
3022      * @param policy the new policy
3023      */
3024     public void setMemoryStoreEvictionPolicy(Policy policy) {
3025         Policy oldValue = getMemoryStoreEvictionPolicy();
3026         compoundStore.setInMemoryEvictionPolicy(policy);
3027         firePropertyChange("MemoryStoreEvictionPolicy", oldValue, policy);
3028     }
3029 
3030     /***
3031      * {@inheritDoc}
3032      */
3033     public LiveCacheStatistics getLiveCacheStatistics()
3034             throws IllegalStateException {
3035         checkStatus();
3036         return liveCacheStatisticsData;
3037     }
3038 
3039     private LiveCacheStatistics getLiveCacheStatisticsNoCheck() {
3040         return liveCacheStatisticsData;
3041     }
3042 
3043     /***
3044      * {@inheritDoc}
3045      */
3046     public void registerCacheUsageListener(CacheUsageListener cacheUsageListener)
3047             throws IllegalStateException {
3048         checkStatus();
3049         liveCacheStatisticsData.registerCacheUsageListener(cacheUsageListener);
3050     }
3051 
3052     /***
3053      * {@inheritDoc}
3054      */
3055     public void removeCacheUsageListener(CacheUsageListener cacheUsageListener)
3056             throws IllegalStateException {
3057         checkStatus();
3058         liveCacheStatisticsData.removeCacheUsageListener(cacheUsageListener);
3059     }
3060 
3061     /***
3062      * {@inheritDoc}
3063      */
3064     public boolean isStatisticsEnabled() {
3065         return getLiveCacheStatistics().isStatisticsEnabled();
3066     }
3067 
3068     /***
3069      * {@inheritDoc}
3070      */
3071     public void setStatisticsEnabled(boolean enableStatistics) {
3072         boolean oldValue = isStatisticsEnabled();
3073         if (oldValue != enableStatistics) {
3074             liveCacheStatisticsData.setStatisticsEnabled(enableStatistics);
3075             if (!enableStatistics) {
3076                 setSampledStatisticsEnabled(false);
3077             }
3078             firePropertyChange("StatisticsEnabled", oldValue, enableStatistics);
3079         }
3080     }
3081 
3082     /***
3083      * {@inheritDoc}
3084      */
3085     public SampledCacheStatistics getSampledCacheStatistics() {
3086         return sampledCacheStatistics;
3087     }
3088 
3089     /***
3090      * {@inheritDoc}
3091      */
3092     public void setSampledStatisticsEnabled(final boolean enableStatistics) {
3093         if (cacheManager == null) {
3094             throw new IllegalStateException(
3095                     "You must add the cache to a CacheManager before enabling/disabling sampled statistics.");
3096         }
3097         boolean oldValue = isSampledStatisticsEnabled();
3098         if (oldValue != enableStatistics) {
3099             if (enableStatistics) {
3100                 setStatisticsEnabled(true);
3101                 sampledCacheStatistics.enableSampledStatistics(cacheManager.getTimer());
3102             } else {
3103                 sampledCacheStatistics.disableSampledStatistics();
3104             }
3105             firePropertyChange("SampledStatisticsEnabled", oldValue, enableStatistics);
3106         }
3107     }
3108 
3109     /***
3110      * {@inheritDoc}
3111      *
3112      * @see net.sf.ehcache.Ehcache#isSampledStatisticsEnabled()
3113      */
3114     public boolean isSampledStatisticsEnabled() {
3115         return sampledCacheStatistics.isSampledStatisticsEnabled();
3116     }
3117 
3118     /***
3119      * {@inheritDoc}
3120      */
3121     public Object getInternalContext() {
3122         return compoundStore.getInternalContext();
3123     }
3124 
3125     /***
3126      * {@inheritDoc}
3127      */
3128     public void disableDynamicFeatures() {
3129         configuration.freezeConfiguration();
3130         allowDisable = false;
3131     }
3132 
3133     /***
3134      * {@inheritDoc}
3135      */
3136     public boolean isClusterCoherent() {
3137         return compoundStore.isClusterCoherent();
3138     }
3139 
3140     /***
3141      * {@inheritDoc}
3142      */
3143     public boolean isNodeCoherent() {
3144         return compoundStore.isNodeCoherent();
3145     }
3146 
3147     /***
3148      * {@inheritDoc}
3149      */
3150     public void setNodeCoherent(boolean coherent) {
3151         boolean oldValue = isNodeCoherent();
3152         if (oldValue != coherent) {
3153             compoundStore.setNodeCoherent(coherent);
3154         }
3155     }
3156 
3157     /***
3158      * {@inheritDoc}
3159      */
3160     public void waitUntilClusterCoherent() {
3161         compoundStore.waitUntilClusterCoherent();
3162     }
3163     
3164     // PropertyChangeSupport
3165 
3166     /***
3167      * @param listener
3168      */
3169     public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
3170       if (listener != null && propertyChangeSupport != null) {
3171         propertyChangeSupport.removePropertyChangeListener(listener);
3172         propertyChangeSupport.addPropertyChangeListener(listener);
3173       }
3174     }
3175 
3176     /***
3177      * @param listener
3178      */
3179     public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
3180       if (listener != null && propertyChangeSupport != null) {
3181         propertyChangeSupport.removePropertyChangeListener(listener);
3182       }
3183     }
3184 
3185     /***
3186      * @param propertyName
3187      * @param oldValue
3188      * @param newValue
3189      */
3190     public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
3191       PropertyChangeSupport pcs;
3192       synchronized (this) {
3193         pcs = propertyChangeSupport;
3194       }
3195       if (pcs != null && (oldValue != null || newValue != null)) {
3196         pcs.firePropertyChange(propertyName, oldValue, newValue);
3197       }
3198     }
3199 
3200     /***
3201      * {@inheritDoc}
3202      */
3203     public Element putIfAbsent(Element element) throws NullPointerException {
3204         checkStatus();
3205         
3206         if (element.getObjectKey() == null) {
3207             throw new NullPointerException();
3208         }
3209         
3210         if (disabled) {
3211             return null;
3212         }
3213 
3214         //this guard currently ensures reasonable behavior on expiring elements
3215         getQuiet(element.getObjectKey());
3216 
3217         element.resetAccessStatistics();
3218         applyDefaultsToElementWithoutLifespanSet(element);
3219         backOffIfDiskSpoolFull();
3220 
3221         Element result = compoundStore.putIfAbsent(element);
3222         if (result == null) {
3223             notifyPutInternalListeners(element, false, false);
3224         }
3225         return result;
3226     }
3227 
3228     /***
3229      * {@inheritDoc}
3230      */
3231     public boolean removeElement(Element element) throws NullPointerException {
3232         checkStatus();
3233         
3234         if (element.getObjectKey() == null) {
3235             throw new NullPointerException();
3236         }
3237         
3238         if (disabled) {
3239             return false;
3240         }
3241 
3242         //this guard currently ensures reasonable behavior on expiring elements
3243         getQuiet(element.getObjectKey());
3244 
3245         Element result = compoundStore.removeElement(element);
3246         notifyRemoveInternalListeners(element.getObjectKey(), false, true, false, result);
3247         return result != null;
3248     }
3249 
3250     /***
3251      * {@inheritDoc}
3252      */
3253     public boolean replace(Element old, Element element) throws NullPointerException, IllegalArgumentException {
3254         checkStatus();
3255         
3256         if (old.getObjectKey() == null || element.getObjectKey() == null) {
3257             throw new NullPointerException();
3258         }
3259         if (!old.getObjectKey().equals(element.getObjectKey())) {
3260             throw new IllegalArgumentException("The keys for the element arguments to replace must be equal");
3261         }
3262         
3263         if (disabled) {
3264             return false;
3265         }
3266 
3267         getQuiet(old.getObjectKey());
3268 
3269         element.resetAccessStatistics();
3270         applyDefaultsToElementWithoutLifespanSet(element);
3271         backOffIfDiskSpoolFull();
3272 
3273         boolean result = compoundStore.replace(old, element);
3274         
3275         if (result) {
3276             element.updateUpdateStatistics();
3277             notifyPutInternalListeners(element, false, true);
3278         }
3279         return result;
3280     }
3281 
3282     /***
3283      * {@inheritDoc}
3284      */
3285     public Element replace(Element element) throws NullPointerException {
3286         checkStatus();
3287         
3288         if (element.getObjectKey() == null) {
3289             throw new NullPointerException();
3290         }
3291         
3292         if (disabled) {
3293             return null;
3294         }
3295 
3296         getQuiet(element.getObjectKey());
3297 
3298         element.resetAccessStatistics();
3299         applyDefaultsToElementWithoutLifespanSet(element);
3300         backOffIfDiskSpoolFull();
3301 
3302         Element result = compoundStore.replace(element);
3303         if (result != null) {
3304             element.updateUpdateStatistics();
3305             notifyPutInternalListeners(element, false, true);
3306         }
3307         return result;
3308     }
3309 
3310     /***
3311      * {@inheritDoc}
3312      * 
3313      * @see net.sf.ehcache.store.StoreListener#clusterCoherent(boolean)
3314      */
3315     public void clusterCoherent(boolean clusterCoherent) {
3316         firePropertyChange("ClusterCoherent", !clusterCoherent, clusterCoherent);
3317     }
3318 
3319     /***
3320      * {@inheritDoc}
3321      * 
3322      * @see net.sf.ehcache.store.StoreListener#nodeCoherent(boolean)
3323      */
3324     public void nodeCoherent(boolean nodeCoherent) {
3325         firePropertyChange("NodeCoherent", !nodeCoherent, nodeCoherent);
3326     }
3327 }