View Javadoc

1   /***
2    *  Copyright 2003-2009 Terracotta, Inc.
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package net.sf.ehcache;
18  
19  import java.io.IOException;
20  import java.io.Serializable;
21  import java.net.InetAddress;
22  import java.net.UnknownHostException;
23  import java.rmi.server.UID;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.HashMap;
29  import java.util.HashSet;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Set;
33  import java.util.concurrent.AbstractExecutorService;
34  import java.util.concurrent.CopyOnWriteArrayList;
35  import java.util.concurrent.ExecutionException;
36  import java.util.concurrent.ExecutorService;
37  import java.util.concurrent.Future;
38  import java.util.concurrent.LinkedBlockingQueue;
39  import java.util.concurrent.ThreadPoolExecutor;
40  import java.util.concurrent.TimeUnit;
41  import java.util.logging.Level;
42  import java.util.logging.Logger;
43  
44  import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
45  import net.sf.ehcache.config.CacheConfiguration;
46  import net.sf.ehcache.config.DiskStoreConfiguration;
47  import net.sf.ehcache.config.TerracottaConfiguration;
48  import net.sf.ehcache.event.CacheEventListener;
49  import net.sf.ehcache.event.RegisteredEventListeners;
50  import net.sf.ehcache.exceptionhandler.CacheExceptionHandler;
51  import net.sf.ehcache.extension.CacheExtension;
52  import net.sf.ehcache.loader.CacheLoader;
53  import net.sf.ehcache.statistics.CacheUsageListener;
54  import net.sf.ehcache.statistics.LiveCacheStatistics;
55  import net.sf.ehcache.statistics.LiveCacheStatisticsData;
56  import net.sf.ehcache.statistics.LiveCacheStatisticsWrapper;
57  import net.sf.ehcache.statistics.sampled.SampledCacheStatistics;
58  import net.sf.ehcache.statistics.sampled.SampledCacheStatisticsWrapper;
59  import net.sf.ehcache.store.DiskStore;
60  import net.sf.ehcache.store.LruMemoryStore;
61  import net.sf.ehcache.store.MemoryStore;
62  import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
63  import net.sf.ehcache.store.Policy;
64  import net.sf.ehcache.store.Store;
65  import net.sf.ehcache.util.TimeUtil;
66  
67  /***
68   * Cache is the central class in ehcache. Caches have {@link Element}s and are managed
69   * by the {@link CacheManager}. The Cache performs logical actions. It delegates physical
70   * implementations to its {@link net.sf.ehcache.store.Store}s.
71   * <p/>
72   * A reference to a Cache can be obtained through the {@link CacheManager}. A Cache thus obtained
73   * is guaranteed to have status {@link Status#STATUS_ALIVE}. This status is checked for any method which
74   * throws {@link IllegalStateException} and the same thrown if it is not alive. This would normally
75   * happen if a call is made after {@link CacheManager#shutdown} is invoked.
76   * <p/>
77   * Cache is threadsafe.
78   * <p/>
79   * Statistics on cache usage are collected and made available through the {@link #getStatistics()} methods.
80   * <p/>
81   * Various decorators are available for Cache, such as BlockingCache, SelfPopulatingCache and the dynamic proxy
82   * ExceptionHandlingDynamicCacheProxy. See each class for details.
83   *
84   * @author Greg Luck
85   * @version $Id: Cache.java 1401 2009-11-05 01:15:50Z gluck $
86   */
87  public class Cache implements Ehcache {
88  
89      /***
90       * A reserved word for cache names. It denotes a default configuration
91       * which is applied to caches created without configuration.
92       */
93      public static final String DEFAULT_CACHE_NAME = "default";
94  
95      /***
96       * System Property based method of disabling ehcache. If disabled no elements will be added to a cache.
97       * <p/>
98       * Set the property "net.sf.ehcache.disabled=true" to disable ehcache.
99       * <p/>
100      * This can easily be done using <code>java -Dnet.sf.ehcache.disabled=true</code> in the command line.
101      */
102     public static final String NET_SF_EHCACHE_DISABLED = "net.sf.ehcache.disabled";
103 
104     /***
105      * System Property based method of selecting the LruMemoryStore in use up to ehcache 1.5. This is provided
106      * for ease of migration.
107      * <p/>
108      * Set the property "net.sf.ehcache.use.classic.lru=true" to use the older LruMemoryStore implementation
109      * when LRU is selected as the eviction policy.
110      * <p/>
111      * This can easily be done using <code>java -Dnet.sf.ehcache.use.classic.lru=true</code> in the command line.
112      */
113     public static final String NET_SF_EHCACHE_USE_CLASSIC_LRU = "net.sf.ehcache.use.classic.lru";
114 
115     /***
116      * The default interval between runs of the expiry thread.
117      */
118     public static final long DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS = 120;
119 
120     /***
121      * Set a buffer size for the spool of approx 30MB
122      */
123     private static final int DEFAULT_SPOOL_BUFFER_SIZE = 30;
124 
125     private static final Logger LOG = Logger.getLogger(Cache.class.getName());
126 
127     private static final MemoryStoreEvictionPolicy DEFAULT_MEMORY_STORE_EVICTION_POLICY = MemoryStoreEvictionPolicy.LRU;
128 
129     private static InetAddress localhost;
130 
131     /***
132      * The amount of time to wait if a store gets backed up
133      */
134     private static final int BACK_OFF_TIME_MILLIS = 50;
135 
136     private static final int EXECUTOR_KEEP_ALIVE_TIME = 60000;
137     private static final int EXECUTOR_MAXIMUM_POOL_SIZE = Math.min(10, Runtime.getRuntime().availableProcessors());
138     private static final int EXECUTOR_CORE_POOL_SIZE = 1;
139 
140     static {
141         try {
142             localhost = InetAddress.getLocalHost();
143         } catch (UnknownHostException e) {
144             LOG.log(Level.SEVERE, "Unable to set localhost. This prevents creation of a GUID. Cause was: " + e.getMessage(), e);
145         } catch (java.lang.NoClassDefFoundError e) {
146             LOG.log(Level.FINE, "InetAddress is being blocked by your runtime environment. e.g. Google App Engine." +
147                     " Ehcache will work as a local cache.");
148         }
149     }
150 
151     private volatile boolean disabled = Boolean.getBoolean(NET_SF_EHCACHE_DISABLED);
152     
153     private final boolean useClassicLru = Boolean.getBoolean(NET_SF_EHCACHE_USE_CLASSIC_LRU);
154 
155     private volatile Store diskStore;
156 
157     private volatile String diskStorePath;
158 
159     private volatile Status status;
160 
161     private volatile CacheConfiguration configuration;
162 
163     /***
164      * The {@link MemoryStore} of this {@link Cache}. All caches have a memory store.
165      */
166     private volatile Store memoryStore;
167 
168     private volatile RegisteredEventListeners registeredEventListeners;
169 
170     private volatile List<CacheExtension> registeredCacheExtensions;
171 
172     private volatile String guid;
173 
174     private volatile CacheManager cacheManager;
175 
176     private volatile BootstrapCacheLoader bootstrapCacheLoader;
177 
178     private volatile CacheExceptionHandler cacheExceptionHandler;
179 
180     private volatile List<CacheLoader> registeredCacheLoaders;
181 
182     /***
183      * A ThreadPoolExecutor which uses a thread pool to schedule loads in the order in which they are requested.
184      * <p/>
185      * Each cache has its own one of these, if required. Because the Core Thread Pool is zero, no threads
186      * are used until actually needed. Threads are added to the pool up to a maximum of 10. The keep alive
187      * time is 60 seconds, after which, if they are not required they will be stopped and collected.
188      * <p/>
189      * The executorService is only used for cache loading, and is created lazily on demand to avoid unnecessary resource
190      * usage.
191      * <p/>
192      * Use {@link #getExecutorService()} to ensure that it is initialised.
193      */
194     private volatile ExecutorService executorService;
195 
196     private volatile LiveCacheStatisticsData liveCacheStatisticsData;
197 
198     private volatile SampledCacheStatisticsWrapper sampledCacheStatistics;
199 
200 
201     /***
202      * 1.0 Constructor.
203      * <p/>
204      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
205      * <p/>
206      * A client can specify their own settings here and pass the {@link Cache} object
207      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
208      * <p/>
209      * Only the CacheManager can initialise them.
210      * <p/>
211      * This constructor creates disk stores, if specified, that do not persist between restarts.
212      * <p/>
213      * The default expiry thread interval of 120 seconds is used. This is the interval between runs
214      * of the expiry thread, where it checks the disk store for expired elements. It is not the
215      * the timeToLiveSeconds.
216      *
217      * @param name                the name of the cache. Note that "default" is a reserved name for the defaultCache.
218      * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
219      * @param overflowToDisk      whether to use the disk store
220      * @param eternal             whether the elements in the cache are eternal, i.e. never expire
221      * @param timeToLiveSeconds   the default amount of time to live for an element from its creation date
222      * @param timeToIdleSeconds   the default amount of time to live for an element from its last accessed or modified date
223      * @since 1.0
224      */
225     public Cache(String name, int maxElementsInMemory, boolean overflowToDisk,
226                  boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds) {
227         this(name, maxElementsInMemory, DEFAULT_MEMORY_STORE_EVICTION_POLICY, overflowToDisk,
228                 null, eternal, timeToLiveSeconds, timeToIdleSeconds, false, DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS, null, null);
229     }
230 
231 
232     /***
233      * 1.1 Constructor.
234      * <p/>
235      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
236      * <p/>
237      * A client can specify their own settings here and pass the {@link Cache} object
238      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
239      * <p/>
240      * Only the CacheManager can initialise them.
241      *
242      * @param name                the name of the cache. Note that "default" is a reserved name for the defaultCache.
243      * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
244      * @param overflowToDisk      whether to use the disk store
245      * @param eternal             whether the elements in the cache are eternal, i.e. never expire
246      * @param timeToLiveSeconds   the default amount of time to live for an element from its creation date
247      * @param timeToIdleSeconds   the default amount of time to live for an element from its last accessed or modified date
248      * @param diskPersistent      whether to persist the cache to disk between JVM restarts
249      * @param diskExpiryThreadIntervalSeconds
250      *                            how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
251      * @since 1.1
252      */
253     public Cache(String name,
254                  int maxElementsInMemory,
255                  boolean overflowToDisk,
256                  boolean eternal,
257                  long timeToLiveSeconds,
258                  long timeToIdleSeconds,
259                  boolean diskPersistent,
260                  long diskExpiryThreadIntervalSeconds) {
261         this(name, maxElementsInMemory, DEFAULT_MEMORY_STORE_EVICTION_POLICY, overflowToDisk, null,
262                 eternal, timeToLiveSeconds, timeToIdleSeconds, diskPersistent, diskExpiryThreadIntervalSeconds, null, null);
263         LOG.log(Level.WARNING, "An API change between ehcache-1.1 and ehcache-1.2 results in the persistence path being set to " +
264                 DiskStoreConfiguration.getDefaultPath() + " when the ehcache-1.1 constructor is used. " +
265                 "Please change to the 1.2 constructor.");
266     }
267 
268 
269     /***
270      * 1.2 Constructor
271      * <p/>
272      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
273      * <p/>
274      * A client can specify their own settings here and pass the {@link Cache} object
275      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
276      * <p/>
277      * Only the CacheManager can initialise them.
278      *
279      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
280      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted
281      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
282      * @param overflowToDisk            whether to use the disk store
283      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
284      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
285      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
286      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
287      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
288      * @param diskExpiryThreadIntervalSeconds
289      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
290      * @param registeredEventListeners  a notification service. Optionally null, in which case a new
291      *                                  one with no registered listeners will be created.
292      * @since 1.2
293      */
294     public Cache(String name,
295                  int maxElementsInMemory,
296                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
297                  boolean overflowToDisk,
298                  String diskStorePath,
299                  boolean eternal,
300                  long timeToLiveSeconds,
301                  long timeToIdleSeconds,
302                  boolean diskPersistent,
303                  long diskExpiryThreadIntervalSeconds,
304                  RegisteredEventListeners registeredEventListeners) {
305         this(name,
306                 maxElementsInMemory,
307                 memoryStoreEvictionPolicy,
308                 overflowToDisk,
309                 diskStorePath,
310                 eternal,
311                 timeToLiveSeconds,
312                 timeToIdleSeconds,
313                 diskPersistent,
314                 diskExpiryThreadIntervalSeconds,
315                 registeredEventListeners,
316                 null);
317     }
318 
319 
320     /***
321      * 1.2.1 Constructor
322      * <p/>
323      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
324      * <p/>
325      * A client can specify their own settings here and pass the {@link Cache} object
326      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
327      * <p/>
328      * Only the CacheManager can initialise them.
329      *
330      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
331      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted
332      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
333      * @param overflowToDisk            whether to use the disk store
334      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
335      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
336      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
337      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
338      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
339      * @param diskExpiryThreadIntervalSeconds
340      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
341      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
342      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
343      * @since 1.2.1
344      */
345     public Cache(String name,
346                  int maxElementsInMemory,
347                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
348                  boolean overflowToDisk,
349                  String diskStorePath,
350                  boolean eternal,
351                  long timeToLiveSeconds,
352                  long timeToIdleSeconds,
353                  boolean diskPersistent,
354                  long diskExpiryThreadIntervalSeconds,
355                  RegisteredEventListeners registeredEventListeners,
356                  BootstrapCacheLoader bootstrapCacheLoader) {
357 
358         this(name,
359                 maxElementsInMemory,
360                 memoryStoreEvictionPolicy,
361                 overflowToDisk,
362                 diskStorePath,
363                 eternal,
364                 timeToLiveSeconds,
365                 timeToIdleSeconds,
366                 diskPersistent,
367                 diskExpiryThreadIntervalSeconds,
368                 registeredEventListeners,
369                 bootstrapCacheLoader,
370                 0);
371     }
372 
373     /***
374      * 1.2.4 Constructor
375      * <p/>
376      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
377      * <p/>
378      * A client can specify their own settings here and pass the {@link Cache} object
379      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
380      * <p/>
381      * Only the CacheManager can initialise them.
382      *
383      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
384      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted
385      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
386      * @param overflowToDisk            whether to use the disk store
387      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
388      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
389      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
390      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
391      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
392      * @param diskExpiryThreadIntervalSeconds
393      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
394      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
395      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
396      * @param maxElementsOnDisk         the maximum number of Elements to allow on the disk. 0 means unlimited.
397      * @since 1.2.4
398      */
399     public Cache(String name,
400                  int maxElementsInMemory,
401                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
402                  boolean overflowToDisk,
403                  String diskStorePath,
404                  boolean eternal,
405                  long timeToLiveSeconds,
406                  long timeToIdleSeconds,
407                  boolean diskPersistent,
408                  long diskExpiryThreadIntervalSeconds,
409                  RegisteredEventListeners registeredEventListeners,
410                  BootstrapCacheLoader bootstrapCacheLoader,
411                  int maxElementsOnDisk) {
412 
413 
414         this(name,
415                 maxElementsInMemory,
416                 memoryStoreEvictionPolicy,
417                 overflowToDisk,
418                 diskStorePath,
419                 eternal,
420                 timeToLiveSeconds,
421                 timeToIdleSeconds,
422                 diskPersistent,
423                 diskExpiryThreadIntervalSeconds,
424                 registeredEventListeners,
425                 bootstrapCacheLoader,
426                 maxElementsOnDisk,
427                 0,
428                 true,
429                 false,
430                 null,
431                 true);
432 
433     }
434 
435     /***
436      * 1.3 Constructor
437      * <p/>
438      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
439      * <p/>
440      * A client can specify their own settings here and pass the {@link Cache} object
441      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
442      * <p/>
443      * Only the CacheManager can initialise them.
444      *
445      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
446      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted
447      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
448      * @param overflowToDisk            whether to use the disk store
449      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
450      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
451      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
452      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
453      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
454      * @param diskExpiryThreadIntervalSeconds
455      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
456      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
457      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
458      * @param maxElementsOnDisk         the maximum number of Elements to allow on the disk. 0 means unlimited.
459      * @param diskSpoolBufferSizeMB     the amount of memory to allocate the write buffer for puts to the DiskStore.
460      * @since 1.3
461      */
462     public Cache(String name,
463                  int maxElementsInMemory,
464                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
465                  boolean overflowToDisk,
466                  String diskStorePath,
467                  boolean eternal,
468                  long timeToLiveSeconds,
469                  long timeToIdleSeconds,
470                  boolean diskPersistent,
471                  long diskExpiryThreadIntervalSeconds,
472                  RegisteredEventListeners registeredEventListeners,
473                  BootstrapCacheLoader bootstrapCacheLoader,
474                  int maxElementsOnDisk,
475                  int diskSpoolBufferSizeMB) {
476         this(name,
477                 maxElementsInMemory,
478                 memoryStoreEvictionPolicy,
479                 overflowToDisk,
480                 diskStorePath,
481                 eternal,
482                 timeToLiveSeconds,
483                 timeToIdleSeconds,
484                 diskPersistent,
485                 diskExpiryThreadIntervalSeconds,
486                 registeredEventListeners,
487                 bootstrapCacheLoader,
488                 maxElementsOnDisk,
489                 diskSpoolBufferSizeMB,
490                 true,
491                 false,
492                 null,
493                 true);
494 
495     }
496 
497     /***
498      * 1.6.0 Constructor
499      * <p/>
500      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
501      * <p/>
502      * A client can specify their own settings here and pass the {@link Cache} object
503      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
504      * <p/>
505      * Only the CacheManager can initialise them.
506      *
507      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
508      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted
509      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
510      * @param overflowToDisk            whether to use the disk store
511      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
512      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
513      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
514      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
515      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
516      * @param diskExpiryThreadIntervalSeconds
517      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
518      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
519      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
520      * @param maxElementsOnDisk         the maximum number of Elements to allow on the disk. 0 means unlimited.
521      * @param diskSpoolBufferSizeMB     the amount of memory to allocate the write buffer for puts to the DiskStore.
522      * @param clearOnFlush              whether the MemoryStore should be cleared when {@link #flush flush()} is called on the cache
523      * @since 1.6.0
524      */
525     public Cache(String name,
526                  int maxElementsInMemory,
527                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
528                  boolean overflowToDisk,
529                  String diskStorePath,
530                  boolean eternal,
531                  long timeToLiveSeconds,
532                  long timeToIdleSeconds,
533                  boolean diskPersistent,
534                  long diskExpiryThreadIntervalSeconds,
535                  RegisteredEventListeners registeredEventListeners,
536                  BootstrapCacheLoader bootstrapCacheLoader,
537                  int maxElementsOnDisk,
538                  int diskSpoolBufferSizeMB,
539                  boolean clearOnFlush) {
540     
541         this(name, maxElementsInMemory, memoryStoreEvictionPolicy, overflowToDisk, diskStorePath, eternal, timeToLiveSeconds,
542                 timeToIdleSeconds, diskPersistent, diskExpiryThreadIntervalSeconds, registeredEventListeners, 
543                 bootstrapCacheLoader, maxElementsOnDisk, diskSpoolBufferSizeMB, clearOnFlush, false, null, true);
544     }
545 
546     /***
547      * 1.7.0 Constructor
548      * <p/>
549      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
550      * <p/>
551      * A client can specify their own settings here and pass the {@link Cache} object
552      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
553      * <p/>
554      * Only the CacheManager can initialise them.
555      *
556      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
557      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted
558      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
559      * @param overflowToDisk            whether to use the disk store
560      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
561      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
562      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
563      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
564      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
565      * @param diskExpiryThreadIntervalSeconds
566      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
567      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
568      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
569      * @param maxElementsOnDisk         the maximum number of Elements to allow on the disk. 0 means unlimited.
570      * @param diskSpoolBufferSizeMB     the amount of memory to allocate the write buffer for puts to the DiskStore.
571      * @param clearOnFlush              whether the MemoryStore should be cleared when {@link #flush flush()} is called on the cache
572      * @param isTerracottaClustered     whether to cluster this cache with Terracotta
573      * @param terracottaValueMode       either "SERIALIZATION" or "IDENTITY" mode, only used if isTerracottaClustered=true
574      * @param terracottaCoherentReads   whether this cache should use coherent reads (usually should be true) unless optimizing for read-only
575      * @since 1.7.0
576      */
577     public Cache(String name,
578                  int maxElementsInMemory,
579                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
580                  boolean overflowToDisk,
581                  String diskStorePath,
582                  boolean eternal,
583                  long timeToLiveSeconds,
584                  long timeToIdleSeconds,
585                  boolean diskPersistent,
586                  long diskExpiryThreadIntervalSeconds,
587                  RegisteredEventListeners registeredEventListeners,
588                  BootstrapCacheLoader bootstrapCacheLoader,
589                  int maxElementsOnDisk,
590                  int diskSpoolBufferSizeMB,
591                  boolean clearOnFlush,
592                  boolean isTerracottaClustered,
593                  String terracottaValueMode,
594                  boolean terracottaCoherentReads) {
595 
596         changeStatus(Status.STATUS_UNINITIALISED);
597 
598 
599         configuration = new CacheConfiguration();
600         configuration.setName(name);
601         configuration.setMaxElementsInMemory(maxElementsInMemory);
602         configuration.setMemoryStoreEvictionPolicyFromObject(memoryStoreEvictionPolicy);
603         configuration.setOverflowToDisk(overflowToDisk);
604         configuration.setEternal(eternal);
605         configuration.setTimeToLiveSeconds(timeToLiveSeconds);
606         configuration.setTimeToIdleSeconds(timeToIdleSeconds);
607         configuration.setDiskPersistent(diskPersistent);
608         configuration.setMaxElementsOnDisk(maxElementsOnDisk);
609         configuration.setClearOnFlush(clearOnFlush);
610 
611         guid = createGuid();
612 
613         if (diskStorePath == null) {
614             this.diskStorePath = DiskStoreConfiguration.getDefaultPath();
615         } else {
616             this.diskStorePath = diskStorePath;
617         }
618 
619         if (registeredEventListeners == null) {
620             this.registeredEventListeners = new RegisteredEventListeners(this);
621         } else {
622             this.registeredEventListeners = registeredEventListeners;
623         }
624 
625         registeredCacheExtensions = new CopyOnWriteArrayList<CacheExtension>();
626         registeredCacheLoaders = new CopyOnWriteArrayList<CacheLoader>();
627 
628         //Set this to a safe value.
629         if (diskExpiryThreadIntervalSeconds == 0) {
630             configuration.setDiskExpiryThreadIntervalSeconds(DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS);
631         } else {
632             configuration.setDiskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds);
633         }
634 
635         if (diskSpoolBufferSizeMB == 0) {
636             configuration.setDiskSpoolBufferSizeMB(DEFAULT_SPOOL_BUFFER_SIZE);
637         } else {
638             configuration.setDiskSpoolBufferSizeMB(diskSpoolBufferSizeMB);
639         }
640 
641         // For backward compatibility with 1.1 and earlier
642         if (memoryStoreEvictionPolicy == null) {
643             configuration.setMemoryStoreEvictionPolicyFromObject(DEFAULT_MEMORY_STORE_EVICTION_POLICY);
644         }
645 
646         this.bootstrapCacheLoader = bootstrapCacheLoader;
647 
648         TerracottaConfiguration tcConfig = new TerracottaConfiguration();
649         tcConfig.setClustered(isTerracottaClustered);
650         if (terracottaValueMode != null) {
651             tcConfig.setValueMode(terracottaValueMode);
652         }
653         tcConfig.setCoherentReads(terracottaCoherentReads);
654         configuration.addTerracotta(tcConfig);
655         
656         //initialize statistics
657         liveCacheStatisticsData = new LiveCacheStatisticsWrapper(this);
658         sampledCacheStatistics = new SampledCacheStatisticsWrapper();
659     }
660 
661     /***
662      * Newly created caches do not have a {@link net.sf.ehcache.store.MemoryStore} or a {@link net.sf.ehcache.store.DiskStore}.
663      * <p/>
664      * This method creates those and makes the cache ready to accept elements
665      */
666     public void initialise() {
667         synchronized (this) {
668             if (!status.equals(Status.STATUS_UNINITIALISED)) {
669                 throw new IllegalStateException("Cannot initialise the " + configuration.getName()
670                         + " cache because its status is not STATUS_UNINITIALISED");
671             }
672 
673             if (configuration.getMaxElementsInMemory() == 0) {
674                 if (LOG.isLoggable(Level.WARNING)) {
675                     LOG.log(Level.WARNING, "Cache: " + configuration.getName()
676                             + " has a maxElementsInMemory of 0. It is strongly recommended to " +
677                             "have a maximumSize of at least 1. Performance is halved by not using a MemoryStore.");
678                 }
679             }
680 
681             this.diskStore = createDiskStore();
682 
683             if (isTerracottaClustered()) {
684                 memoryStore = cacheManager.createTerracottaStore(this);
685             } else {
686                 if (useClassicLru && configuration.getMemoryStoreEvictionPolicy().equals(MemoryStoreEvictionPolicy.LRU)) {
687                     memoryStore = new LruMemoryStore(this, diskStore);
688                 } else {
689                     memoryStore = MemoryStore.create(this, diskStore);
690                 }
691             }
692             changeStatus(Status.STATUS_ALIVE);
693             initialiseRegisteredCacheExtensions();
694             initialiseRegisteredCacheLoaders();
695 
696             // initialize live statistics
697             // register to get notifications of
698             // put/update/remove/expiry/eviction
699             getCacheEventNotificationService().registerListener(
700                     liveCacheStatisticsData);
701             // set up default values
702             liveCacheStatisticsData
703                     .setStatisticsAccuracy(Statistics.STATISTICS_ACCURACY_BEST_EFFORT);
704             liveCacheStatisticsData.setStatisticsEnabled(true);
705             
706             // register the sampled cache statistics
707             this.registerCacheUsageListener(sampledCacheStatistics);
708         }
709 
710         if (LOG.isLoggable(Level.FINE)) {
711             LOG.log(Level.FINE, "Initialised cache: " + configuration.getName());
712         }
713 
714         if (disabled) {
715             if (LOG.isLoggable(Level.WARNING)) {
716                 LOG.log(Level.WARNING, "Cache: " + configuration.getName() + " is disabled because the " + NET_SF_EHCACHE_DISABLED
717                         + " property was set to true. No elements will be added to the cache.");
718             }
719         }
720     }
721 
722     /***
723      * Creates a disk store when either:
724      * <ol>
725      * <li>overflowToDisk is enabled
726      * <li>diskPersistent is enabled
727      * </ol>
728      *
729      * @return the disk store
730      */
731     protected Store createDiskStore() {
732         if (isDiskStore()) {
733             return new DiskStore(this, diskStorePath);
734         } else {
735             return null;
736         }
737     }
738 
739     /***
740      * Whether this cache uses a disk store
741      *
742      * @return true if the cache either overflows to disk or is disk persistent
743      */
744     protected boolean isDiskStore() {
745         return configuration.isOverflowToDisk() || configuration.isDiskPersistent();
746     }
747     
748     /***
749      * Indicates whether this cache is clustered by Terracotta
750      * 
751      * @return {@code true} when the cache is clustered by Terracotta; or {@code false} otherwise
752      */
753     protected boolean isTerracottaClustered() {
754         return configuration.isTerracottaClustered();
755     }
756 
757     /***
758      * Bootstrap command. This must be called after the Cache is initialised, during
759      * CacheManager initialisation. If loads are synchronous, they will complete before the CacheManager
760      * initialise completes, otherwise they will happen in the background.
761      */
762     public void bootstrap() {
763         if (!disabled && bootstrapCacheLoader != null) {
764             bootstrapCacheLoader.load(this);
765         }
766 
767     }
768 
769     private void changeStatus(Status status) {
770         this.status = status;
771     }
772 
773 
774     /***
775      * Put an element in the cache.
776      * <p/>
777      * Resets the access statistics on the element, which would be the case if it has previously been
778      * gotten from a cache, and is now being put back.
779      * <p/>
780      * Also notifies the CacheEventListener that:
781      * <ul>
782      * <li>the element was put, but only if the Element was actually put.
783      * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
784      * if it was requested
785      * </ul>
786      * <p/>
787      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
788      * This exception should be caught in those circumstances.
789      *
790      * @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
791      *                <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
792      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
793      * @throws CacheException
794      */
795     public final void put(Element element) throws IllegalArgumentException, IllegalStateException,
796             CacheException {
797         put(element, false);
798     }
799 
800 
801     /***
802      * Put an element in the cache.
803      * <p/>
804      * Resets the access statistics on the element, which would be the case if it has previously been
805      * gotten from a cache, and is now being put back.
806      * <p/>
807      * Also notifies the CacheEventListener that:
808      * <ul>
809      * <li>the element was put, but only if the Element was actually put.
810      * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
811      * if it was requested
812      * </ul>
813      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
814      * This exception should be caught in those circumstances.
815      *
816      * @param element                     A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
817      *                                    <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
818      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
819      *                                    further notification to doNotNotifyCacheReplicators cache peers
820      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
821      * @throws IllegalArgumentException if the element is null
822      */
823     public final void put(Element element, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
824             IllegalStateException,
825             CacheException {
826         checkStatus();
827 
828         if (disabled) {
829             return;
830         }
831 
832         if (element == null) {
833             if (doNotNotifyCacheReplicators) {
834                 if (LOG.isLoggable(Level.FINE)) {
835                     LOG.fine("Element from replicated put is null. This happens because the element is a SoftReference" +
836                             " and it has been collected.Increase heap memory on the JVM or set -Xms to be the same as " +
837                             "-Xmx to avoid this problem.");
838                 }
839             }
840             //nulls are ignored
841             return;
842         }
843 
844 
845         if (element.getObjectKey() == null) {
846             //nulls are ignored
847             return;
848         }
849 
850         element.resetAccessStatistics();
851         boolean elementExists;
852         Object key = element.getObjectKey();
853         elementExists = isElementInMemory(key) || isElementOnDisk(key);
854         if (elementExists) {
855             element.updateUpdateStatistics();
856         }
857         applyDefaultsToElementWithoutLifespanSet(element);
858 
859         backOffIfDiskSpoolFull();
860 
861 
862         memoryStore.put(element);
863 
864         if (elementExists) {
865             registeredEventListeners.notifyElementUpdated(element, doNotNotifyCacheReplicators);
866         } else {
867             registeredEventListeners.notifyElementPut(element, doNotNotifyCacheReplicators);
868         }
869 
870     }
871 
872     /***
873      * wait outside of synchronized block so as not to block readers
874      * If the disk store spool is full wait a short time to give it a chance to
875      * catch up.
876      * todo maybe provide a warning if this is continually happening or monitor via JMX
877      */
878     private void backOffIfDiskSpoolFull() {
879 
880         if (diskStore != null && diskStore.bufferFull()) {
881             //back off to avoid OutOfMemoryError
882             try {
883                 Thread.sleep(BACK_OFF_TIME_MILLIS);
884             } catch (InterruptedException e) {
885                 //do not care if this happens
886             }
887         }
888     }
889 
890     private void applyDefaultsToElementWithoutLifespanSet(Element element) {
891         if (!element.isLifespanSet()) {
892             //Setting with Cache defaults
893             element.setTimeToLive(TimeUtil.convertTimeToInt(configuration.getTimeToLiveSeconds()));
894             element.setTimeToIdle(TimeUtil.convertTimeToInt(configuration.getTimeToIdleSeconds()));
895             element.setEternal(configuration.isEternal());
896         }
897     }
898 
899     /***
900      * Put an element in the cache, without updating statistics, or updating listeners. This is meant to be used
901      * in conjunction with {@link #getQuiet}.
902      * Synchronization is handled within the method.
903      * <p/>
904      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
905      * This exception should be caught in those circumstances.
906      * <p/>
907      *
908      * @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
909      *                <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
910      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
911      * @throws IllegalArgumentException if the element is null
912      */
913     public final void putQuiet(Element element) throws IllegalArgumentException, IllegalStateException,
914             CacheException {
915         checkStatus();
916 
917         if (disabled) {
918             return;
919         }
920 
921         if (element == null || element.getObjectKey() == null) {
922             //nulls are ignored
923             return;
924         }
925 
926         applyDefaultsToElementWithoutLifespanSet(element);
927 
928         memoryStore.put(element);
929     }
930 
931     /***
932      * Gets an element from the cache. Updates Element Statistics
933      * <p/>
934      * Note that the Element's lastAccessTime is always the time of this get.
935      * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
936      * <p/>
937      * Synchronization is handled within the method.
938      *
939      * @param key a serializable value. Null keys are not stored so get(null) always returns null
940      * @return the element, or null, if it does not exist.
941      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
942      * @see #isExpired
943      */
944     public final Element get(Serializable key) throws IllegalStateException, CacheException {
945         return get((Object) key);
946     }
947 
948 
949     /***
950      * Gets an element from the cache. Updates Element Statistics
951      * <p/>
952      * Note that the Element's lastAccessTime is always the time of this get.
953      * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
954      * <p/>
955      * Synchronization is handled within the method.
956      *
957      * @param key an Object value
958      * @return the element, or null, if it does not exist.
959      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
960      * @see #isExpired
961      * @since 1.2
962      */
963     public final Element get(Object key) throws IllegalStateException, CacheException {
964         checkStatus();
965         Element element;
966         long start = System.currentTimeMillis();
967 
968         element = searchInMemoryStore(key, true, true);
969         if (element == null && isDiskStore()) {
970             element = searchInDiskStore(key, true, true);
971         }
972         if (element == null) {
973             liveCacheStatisticsData.cacheMissNotFound();
974             if (LOG.isLoggable(Level.FINE)) {
975                 LOG.fine(configuration.getName() + " cache - Miss");
976             }
977         }
978         //todo is this expensive. Maybe ditch.
979         long end = System.currentTimeMillis();
980         liveCacheStatisticsData.addGetTimeMillis(end - start);
981         return element;
982     }
983 
984     /***
985      * This method will return, from the cache, the Element associated with the argument "key".
986      * <p/>
987      * 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,
988      * the one associated with the cache. If both are null, no load is performed and null is returned.
989      * <p/>
990      * 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.
991      * <p/>
992      * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
993      * are synchronized.
994      *
995      * @param key            key whose associated value is to be returned.
996      * @param loader         the override loader to use. If null, the cache's default loader will be used
997      * @param loaderArgument an argument to pass to the CacheLoader.
998      * @return an element if it existed or could be loaded, otherwise null
999      * @throws CacheException
1000      */
1001     public Element getWithLoader(Object key, CacheLoader loader, Object loaderArgument) throws CacheException {
1002 
1003         Element element = get(key);
1004         if (element != null) {
1005             return element;
1006         }
1007 
1008         if (registeredCacheLoaders.size() == 0 && loader == null) {
1009             return null;
1010         }
1011 
1012         try {
1013             //check again in case the last thread loaded it
1014             element = getQuiet(key);
1015             if (element != null) {
1016                 return element;
1017             }
1018             Future future = asynchronousLoad(key, loader, loaderArgument);
1019             //wait for result
1020             future.get();
1021         } catch (Exception e) {
1022             throw new CacheException("Exception on load for key " + key, e);
1023         }
1024         return getQuiet(key);
1025     }
1026 
1027     /***
1028      * The load method provides a means to "pre load" the cache. This method will, asynchronously, load the specified
1029      * object into the cache using the associated CacheLoader. If the object already exists in the cache, no action is
1030      * taken. If no loader is associated with the object, no object will be loaded into the cache. If a problem is
1031      * encountered during the retrieving or loading of the object, an exception should be logged. If the "arg" argument
1032      * is set, the arg object will be passed to the CacheLoader.load method. The cache will not dereference the object.
1033      * If no "arg" value is provided a null will be passed to the load method. The storing of null values in the cache
1034      * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding
1035      * the object in the cache. In both cases a null is returned.
1036      * <p/>
1037      * The Ehcache native API provides similar functionality to loaders using the
1038      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1039      *
1040      * @param key key whose associated value to be loaded using the associated CacheLoader if this cache doesn't contain it.
1041      * @throws CacheException
1042      */
1043     public void load(final Object key) throws CacheException {
1044         if (registeredCacheLoaders.size() == 0) {
1045             if (LOG.isLoggable(Level.FINE)) {
1046                 LOG.fine("The CacheLoader is null. Returning.");
1047             }
1048             return;
1049         }
1050 
1051         boolean existsOnCall = isKeyInCache(key);
1052         if (existsOnCall) {
1053             if (LOG.isLoggable(Level.FINE)) {
1054                 LOG.log(Level.FINE, "The key " + key + " exists in the cache. Returning.");
1055             }
1056             return;
1057         }
1058 
1059         asynchronousLoad(key, null, null);
1060     }
1061 
1062     /***
1063      * The getAll method will return, from the cache, a Map of the objects associated with the Collection of keys in argument "keys".
1064      * If the objects are not in the cache, the associated cache loader will be called. If no loader is associated with an object,
1065      * a null is returned. If a problem is encountered during the retrieving or loading of the objects, an exception will be thrown.
1066      * If the "arg" argument is set, the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference
1067      * 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
1068      * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding the object in
1069      * the cache. In both cases a null is returned.
1070      * <p/>
1071      * <p/>
1072      * Note. If the getAll exceeds the maximum cache size, the returned map will necessarily be less than the number specified.
1073      * <p/>
1074      * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
1075      * are synchronized.
1076      * <p/>
1077      * The constructs package provides similar functionality using the
1078      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1079      *
1080      * @param keys           a collection of keys to be returned/loaded
1081      * @param loaderArgument an argument to pass to the CacheLoader.
1082      * @return a Map populated from the Cache. If there are no elements, an empty Map is returned.
1083      * @throws CacheException
1084      */
1085     public Map getAllWithLoader(Collection keys, Object loaderArgument) throws CacheException {
1086         if (keys == null) {
1087             return new HashMap(0);
1088         }
1089         Map<Object, Object> map = new HashMap<Object, Object>(keys.size());
1090 
1091         List<Object> missingKeys = new ArrayList<Object>(keys.size());
1092 
1093         if (registeredCacheLoaders.size() > 0) {
1094             Object key = null;
1095             try {
1096                 map = new HashMap<Object, Object>(keys.size());
1097 
1098                 for (Object key1 : keys) {
1099                     key = key1;
1100 
1101                     if (isKeyInCache(key)) {
1102                         Element element = get(key);
1103                         if (element != null) {
1104                             map.put(key, element.getObjectValue());
1105                         } else {
1106                             map.put(key, null);
1107                         }
1108                     } else {
1109                         missingKeys.add(key);
1110                     }
1111                 }
1112 
1113                 //now load everything that's missing.
1114                 Future future = asynchronousLoadAll(missingKeys, loaderArgument);
1115                 future.get();
1116 
1117 
1118                 for (Object missingKey : missingKeys) {
1119                     key = missingKey;
1120                     Element element = get(key);
1121                     if (element != null) {
1122                         map.put(key, element.getObjectValue());
1123                     } else {
1124                         map.put(key, null);
1125                     }
1126                 }
1127 
1128             } catch (InterruptedException e) {
1129                 throw new CacheException(e.getMessage() + " for key " + key, e);
1130             } catch (ExecutionException e) {
1131                 throw new CacheException(e.getMessage() + " for key " + key, e);
1132             }
1133         } else {
1134             for (Object key : keys) {
1135                 Element element = get(key);
1136                 if (element != null) {
1137                     map.put(key, element.getObjectValue());
1138                 } else {
1139                     map.put(key, null);
1140                 }
1141             }
1142         }
1143         return map;
1144     }
1145 
1146 
1147     /***
1148      * The loadAll method provides a means to "pre load" objects into the cache. This method will, asynchronously, load
1149      * the specified objects into the cache using the associated cache loader(s). If the an object already exists in the
1150      * cache, no action is taken. If no loader is associated with the object, no object will be loaded into the cache.
1151      * If a problem is encountered during the retrieving or loading of the objects, an exception (to be defined)
1152      * should be logged. The getAll method will return, from the cache, a Map of the objects associated with the
1153      * Collection of keys in argument "keys". If the objects are not in the cache, the associated cache loader will be
1154      * called. If no loader is associated with an object, a null is returned. If a problem is encountered during the
1155      * retrieving or loading of the objects, an exception (to be defined) will be thrown. If the "arg" argument is set,
1156      * the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference the object.
1157      * If no "arg" value is provided a null will be passed to the loadAll method.
1158      * <p/>
1159      * keys - collection of the keys whose associated values to be loaded into this cache by using the associated
1160      * CacheLoader if this cache doesn't contain them.
1161      * <p/>
1162      * The Ehcache native API provides similar functionality to loaders using the
1163      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1164      */
1165     public void loadAll(final Collection keys, final Object argument) throws CacheException {
1166 
1167         if (registeredCacheLoaders.size() == 0) {
1168             if (LOG.isLoggable(Level.FINE)) {
1169                 LOG.log(Level.FINE, "The CacheLoader is null. Returning.");
1170             }
1171             return;
1172         }
1173         if (keys == null) {
1174             return;
1175         }
1176         asynchronousLoadAll(keys, argument);
1177     }
1178 
1179     /***
1180      * Gets an element from the cache, without updating Element statistics. Cache statistics are
1181      * still updated. Listeners are not called.
1182      * <p/>
1183      * @param key a serializable value
1184      * @return the element, or null, if it does not exist.
1185      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1186      * @see #isExpired
1187      */
1188     public final Element getQuiet(Serializable key) throws IllegalStateException, CacheException {
1189         return getQuiet((Object) key);
1190     }
1191 
1192     /***
1193      * Gets an element from the cache, without updating Element statistics. Cache statistics are
1194      * not updated.
1195      * <p/>
1196      * Listeners are not called.
1197      *
1198      * @param key a serializable value
1199      * @return the element, or null, if it does not exist.
1200      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1201      * @see #isExpired
1202      * @since 1.2
1203      */
1204     public final Element getQuiet(Object key) throws IllegalStateException, CacheException {
1205         checkStatus();
1206         Element element;
1207 
1208         element = searchInMemoryStore(key, false, false);
1209         if (element == null && isDiskStore()) {
1210             element = searchInDiskStore(key, false, false);
1211         }
1212         return element;
1213     }
1214 
1215     /***
1216      * Returns a list of all element keys in the cache, whether or not they are expired.
1217      * <p/>
1218      * The returned keys are unique and can be considered a set.
1219      * <p/>
1220      * The List returned is not live. It is a copy.
1221      * <p/>
1222      * The time taken is O(n). On a single CPU 1.8Ghz P4, approximately 8ms is required
1223      * for each 1000 entries.
1224      *
1225      * @return a list of {@link Object} keys
1226      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1227      */
1228     public final List getKeys() throws IllegalStateException, CacheException {
1229         checkStatus();
1230         /* An element with the same key can exist in both the memory store and the
1231             disk store at the same time. Because the memory store is always searched first
1232             these duplicates do not cause problems when getting elements/
1233 
1234             This method removes these duplicates before returning the list of keys*/
1235         List<Object> allKeyList = new ArrayList<Object>();
1236         List<Object> keyList = Arrays.asList(memoryStore.getKeyArray());
1237         allKeyList.addAll(keyList);
1238         if (isDiskStore()) {
1239             Set<Object> allKeys = new HashSet<Object>();
1240             //within the store keys will be unique
1241             allKeys.addAll(keyList);
1242             Object[] diskKeys = diskStore.getKeyArray();
1243             for (Object diskKey : diskKeys) {
1244                 if (allKeys.add(diskKey)) {
1245                     //Unique, so add it to the list
1246                     allKeyList.add(diskKey);
1247                 }
1248             }
1249         }
1250         return allKeyList;
1251     }
1252 
1253     /***
1254      * Returns a list of all element keys in the cache. Only keys of non-expired
1255      * elements are returned.
1256      * <p/>
1257      * The returned keys are unique and can be considered a set.
1258      * <p/>
1259      * The List returned is not live. It is a copy.
1260      * <p/>
1261      * The time taken is O(n), where n is the number of elements in the cache. On
1262      * a 1.8Ghz P4, the time taken is approximately 200ms per 1000 entries. This method
1263      * is not synchronized, because it relies on a non-live list returned from {@link #getKeys()}
1264      * , which is synchronised, and which takes 8ms per 1000 entries. This way
1265      * cache liveness is preserved, even if this method is very slow to return.
1266      * <p/>
1267      * Consider whether your usage requires checking for expired keys. Because
1268      * this method takes so long, depending on cache settings, the list could be
1269      * quite out of date by the time you get it.
1270      *
1271      * @return a list of {@link Object} keys
1272      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1273      */
1274     public final List getKeysWithExpiryCheck() throws IllegalStateException, CacheException {
1275         List allKeyList = getKeys();
1276         //remove keys of expired elements
1277         ArrayList<Object> nonExpiredKeys = new ArrayList<Object>(allKeyList.size());
1278         int allKeyListSize = allKeyList.size();
1279         for (int i = 0; i < allKeyListSize; i++) {
1280             Object key = allKeyList.get(i);
1281             Element element = getQuiet(key);
1282             if (element != null) {
1283                 nonExpiredKeys.add(key);
1284             }
1285         }
1286         nonExpiredKeys.trimToSize();
1287         return nonExpiredKeys;
1288     }
1289 
1290 
1291     /***
1292      * Returns a list of all elements in the cache, whether or not they are expired.
1293      * <p/>
1294      * The returned keys are not unique and may contain duplicates. If the cache is only
1295      * using the memory store, the list will be unique. If the disk store is being used
1296      * as well, it will likely contain duplicates, because of the internal store design.
1297      * <p/>
1298      * The List returned is not live. It is a copy.
1299      * <p/>
1300      * The time taken is O(log n). On a single CPU 1.8Ghz P4, approximately 6ms is required
1301      * for 1000 entries and 36 for 50000.
1302      * <p/>
1303      * This is the fastest getKeys method
1304      *
1305      * @return a list of {@link Object} keys
1306      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1307      */
1308     public final List getKeysNoDuplicateCheck() throws IllegalStateException {
1309         checkStatus();
1310         ArrayList<Object> allKeys = new ArrayList<Object>();
1311         List<Object> memoryKeySet = Arrays.asList(memoryStore.getKeyArray());
1312         allKeys.addAll(memoryKeySet);
1313         if (isDiskStore()) {
1314             List<Object> diskKeySet = Arrays.asList(diskStore.getKeyArray());
1315             allKeys.addAll(diskKeySet);
1316         }
1317         return allKeys;
1318     }
1319 
1320     private Element searchInMemoryStore(Object key, boolean updateStatistics, boolean notifyListeners) {
1321         Element element;
1322         if (updateStatistics) {
1323             element = memoryStore.get(key);
1324         } else {
1325             element = memoryStore.getQuiet(key);
1326         }
1327 
1328         if (element != null) {
1329             if (isExpired(element)) {
1330                 if (LOG.isLoggable(Level.FINE)) {
1331                     LOG.log(Level.FINE, configuration.getName() + " Memory cache hit, but element expired");
1332                 }
1333                 if (updateStatistics) {
1334                     liveCacheStatisticsData.cacheMissExpired();
1335                 }
1336                 remove(key, true, notifyListeners, false);
1337                 element = null;
1338             } else {
1339                 if (updateStatistics) {
1340                     element.updateAccessStatistics();
1341                     if (LOG.isLoggable(Level.FINE)) {
1342                         LOG.fine(getName() + "Cache: " + getName() + "MemoryStore hit for " + key);
1343                     }
1344 
1345                     liveCacheStatisticsData.cacheHitInMemory();
1346                 }
1347             }
1348         } else if (LOG.isLoggable(Level.FINE)) {
1349             LOG.fine(getName() + "Cache: " + getName() + "MemoryStore miss for " + key);
1350         }
1351         return element;
1352     }
1353 
1354     private Element searchInDiskStore(Object key, boolean updateStatistics, boolean notifyListeners) {
1355         if (!(key instanceof Serializable)) {
1356             return null;
1357         }
1358         Serializable serializableKey = (Serializable) key;
1359         Element element;
1360         if (updateStatistics) {
1361             element = diskStore.get(serializableKey);
1362         } else {
1363             element = diskStore.getQuiet(serializableKey);
1364         }
1365         if (element != null) {
1366             if (isExpired(element)) {
1367                 if (LOG.isLoggable(Level.FINE)) {
1368                     LOG.log(Level.FINE, configuration.getName() + " cache - Disk Store hit, but element expired");
1369                 }
1370                 liveCacheStatisticsData.cacheMissExpired();
1371                 remove(key, true, notifyListeners, false);
1372                 element = null;
1373             } else {
1374                 if (updateStatistics) {
1375                     element.updateAccessStatistics();
1376                 }
1377                 liveCacheStatisticsData.cacheHitOnDisk();
1378                 //Put the item back into memory to preserve policies in the memory store and to save updated statistics
1379                 //todo - maybe make the DiskStore a one-way evict. i.e. Do not replace See testGetSpeedMostlyDisk for speed comp.
1380                 memoryStore.put(element);
1381             }
1382         }
1383         return element;
1384     }
1385 
1386     /***
1387      * Removes an {@link Element} from the Cache. This also removes it from any
1388      * stores it may be in.
1389      * <p/>
1390      * Also notifies the CacheEventListener after the element was removed.
1391      * <p/>
1392      * Synchronization is handled within the method.
1393      * <p/>
1394      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1395      * This exception should be caught in those circumstances.
1396      *
1397      * @param key the element key to operate on
1398      * @return true if the element was removed, false if it was not found in the cache
1399      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1400      */
1401     public final boolean remove(Serializable key) throws IllegalStateException {
1402         return remove((Object) key);
1403     }
1404 
1405     /***
1406      * Removes an {@link Element} from the Cache. This also removes it from any
1407      * stores it may be in.
1408      * <p/>
1409      * Also notifies the CacheEventListener after the element was removed, but only if an Element
1410      * with the key actually existed.
1411      * <p/>
1412      * Synchronization is handled within the method.
1413      * <p/>
1414      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1415      * This exception should be caught in those circumstances.
1416      * <p/>
1417      *
1418      * @param key the element key to operate on
1419      * @return true if the element was removed, false if it was not found in the cache
1420      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1421      * @since 1.2
1422      */
1423     public final boolean remove(Object key) throws IllegalStateException {
1424         return remove(key, false);
1425     }
1426 
1427 
1428     /***
1429      * Removes an {@link Element} from the Cache. This also removes it from any
1430      * stores it may be in.
1431      * <p/>
1432      * Also notifies the CacheEventListener after the element was removed, but only if an Element
1433      * with the key actually existed.
1434      * <p/>
1435      * Synchronization is handled within the method.
1436      * <p/>
1437      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1438      * This exception should be caught in those circumstances.
1439      *
1440      * @param key                         the element key to operate on
1441      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1442      *                                    further notification to doNotNotifyCacheReplicators cache peers
1443      * @return true if the element was removed, false if it was not found in the cache
1444      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1445      */
1446     public final boolean remove(Serializable key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
1447         return remove((Object) key, doNotNotifyCacheReplicators);
1448     }
1449 
1450     /***
1451      * Removes an {@link Element} from the Cache. This also removes it from any
1452      * stores it may be in.
1453      * <p/>
1454      * Also notifies the CacheEventListener after the element was removed, but only if an Element
1455      * with the key actually existed.
1456      * <p/>
1457      * Synchronization is handled within the method.
1458      *
1459      * @param key                         the element key to operate on
1460      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1461      *                                    further notification to doNotNotifyCacheReplicators cache peers
1462      * @return true if the element was removed, false if it was not found in the cache
1463      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1464      */
1465     public final boolean remove(Object key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
1466         return remove(key, false, true, doNotNotifyCacheReplicators);
1467     }
1468 
1469     /***
1470      * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
1471      * stores it may be in.
1472      * <p/>
1473      * Listeners are not called.
1474      *
1475      * @param key the element key to operate on
1476      * @return true if the element was removed, false if it was not found in the cache
1477      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1478      */
1479     public final boolean removeQuiet(Serializable key) throws IllegalStateException {
1480         return remove(key, false, false, false);
1481     }
1482 
1483     /***
1484      * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
1485      * stores it may be in.
1486      * <p/>
1487      * Listeners are not called.
1488      * <p/>
1489      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1490      * This exception should be caught in those circumstances.
1491      *
1492      * @param key the element key to operate on
1493      * @return true if the element was removed, false if it was not found in the cache
1494      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1495      * @since 1.2
1496      */
1497     public final boolean removeQuiet(Object key) throws IllegalStateException {
1498         return remove(key, false, false, false);
1499     }
1500 
1501 
1502     /***
1503      * Removes or expires an {@link Element} from the Cache after an attempt to get it determined that it should be expired.
1504      * This also removes it from any stores it may be in.
1505      * <p/>
1506      * Also notifies the CacheEventListener after the element has expired, but only if an Element
1507      * with the key actually existed.
1508      * <p/>
1509      * Synchronization is handled within the method.
1510      * <p/>
1511      * If a remove was called, listeners are notified, regardless of whether the element existed or not.
1512      * This allows distributed cache listeners to remove elements from a cluster regardless of whether they
1513      * existed locally.
1514      * <p/>
1515      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1516      * This exception should be caught in those circumstances.
1517      *
1518      * @param key                         the element key to operate on
1519      * @param expiry                      if the reason this method is being called is to expire the element
1520      * @param notifyListeners             whether to notify listeners
1521      * @param doNotNotifyCacheReplicators whether not to notify cache replicators
1522      * @return true if the element was removed, false if it was not found in the cache
1523      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1524      */
1525     private boolean remove(Object key, boolean expiry, boolean notifyListeners,
1526                            boolean doNotNotifyCacheReplicators)
1527             throws IllegalStateException {
1528         checkStatus();
1529         boolean removed = false;
1530         Element elementFromMemoryStore;
1531         Element elementFromDiskStore;
1532         elementFromMemoryStore = memoryStore.remove(key);
1533 
1534         //could have been removed from both places, if there are two copies in the cache
1535         elementFromDiskStore = null;
1536         if (isDiskStore()) {
1537             if ((key instanceof Serializable)) {
1538                 Serializable serializableKey = (Serializable) key;
1539                 elementFromDiskStore = diskStore.remove(serializableKey);
1540             }
1541 
1542         }
1543 
1544         boolean removeNotified = false;
1545 
1546         //Elements may be in both places. Always notify the MemoryStore version if there are two
1547         if (elementFromMemoryStore != null) {
1548             if (expiry) {
1549                 //always notify expire which is lazy regardless of the removeQuiet
1550                 registeredEventListeners.notifyElementExpiry(elementFromMemoryStore, doNotNotifyCacheReplicators);
1551             } else if (notifyListeners) {
1552                 removeNotified = true;
1553                 registeredEventListeners.notifyElementRemoved(elementFromMemoryStore, doNotNotifyCacheReplicators);
1554             }
1555             removed = true;
1556         } else if (elementFromDiskStore != null) {
1557             if (expiry) {
1558                 registeredEventListeners.notifyElementExpiry(elementFromDiskStore, doNotNotifyCacheReplicators);
1559             } else if (notifyListeners) {
1560                 removeNotified = true;
1561                 registeredEventListeners.notifyElementRemoved(elementFromDiskStore, doNotNotifyCacheReplicators);
1562             }
1563             removed = true;
1564         }
1565 
1566         //If we are trying to remove an element which does not exist locally, we should still notify so that
1567         //cluster invalidations work.
1568         if (notifyListeners && !expiry && !removeNotified) {
1569             Element syntheticElement = new Element(key, null);
1570             registeredEventListeners.notifyElementRemoved(syntheticElement, doNotNotifyCacheReplicators);
1571         }
1572 
1573         return removed;
1574     }
1575 
1576     /***
1577      * Removes all cached items.
1578      * Synchronization is handled within the method.
1579      * <p/>
1580      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1581      * This exception should be caught in those circumstances.
1582      *
1583      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1584      */
1585     public void removeAll() throws IllegalStateException, CacheException {
1586         removeAll(false);
1587     }
1588 
1589 
1590     /***
1591      * Removes all cached items.
1592      * Synchronization is handled within the method.
1593      * <p/>
1594      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1595      * This exception should be caught in those circumstances.
1596      *
1597      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1598      */
1599     public void removeAll(boolean doNotNotifyCacheReplicators) throws IllegalStateException, CacheException {
1600         checkStatus();
1601         memoryStore.removeAll();
1602         if (isDiskStore()) {
1603             diskStore.removeAll();
1604         }
1605         registeredEventListeners.notifyRemoveAll(doNotNotifyCacheReplicators);
1606     }
1607 
1608     /***
1609      * Starts an orderly shutdown of the Cache. Steps are:
1610      * <ol>
1611      * <li>Completes any outstanding CacheLoader loads.
1612      * <li>Disposes any cache extensions.
1613      * <li>Disposes any cache event listeners. The listeners normally complete, so for example distributed caching operations will complete.
1614      * <li>Flushes all cache items from memory to the disk store, if any
1615      * <li>changes status to shutdown, so that any cache operations after this point throw IllegalStateException
1616      * </ol>
1617      * This method should be invoked only by CacheManager, as a cache's lifecycle is bound into that of it's cache manager.
1618      *
1619      * @throws IllegalStateException if the cache is already {@link Status#STATUS_SHUTDOWN}
1620      */
1621     public synchronized void dispose() throws IllegalStateException {
1622         checkStatusNotDisposed();
1623 
1624         if (executorService != null) {
1625             executorService.shutdown();
1626         }
1627         disposeRegisteredCacheExtensions();
1628         disposeRegisteredCacheLoaders();
1629         registeredEventListeners.dispose();
1630 
1631         if (memoryStore != null) {
1632             memoryStore.dispose();
1633         }
1634         if (diskStore != null) {
1635             diskStore.dispose();
1636         }
1637         changeStatus(Status.STATUS_SHUTDOWN);
1638     }
1639 
1640     private void initialiseRegisteredCacheExtensions() {
1641         for (CacheExtension cacheExtension : registeredCacheExtensions) {
1642             cacheExtension.init();
1643         }
1644     }
1645 
1646     private void disposeRegisteredCacheExtensions() {
1647         for (CacheExtension cacheExtension : registeredCacheExtensions) {
1648             cacheExtension.dispose();
1649         }
1650     }
1651 
1652     private void initialiseRegisteredCacheLoaders() {
1653         for (CacheLoader cacheLoader : registeredCacheLoaders) {
1654             cacheLoader.init();
1655         }
1656     }
1657 
1658     private void disposeRegisteredCacheLoaders() {
1659         for (CacheLoader cacheLoader : registeredCacheLoaders) {
1660             cacheLoader.dispose();
1661         }
1662     }
1663 
1664     /***
1665      * Gets the cache configuration this cache was created with.
1666      * <p/>
1667      * Things like listeners that are added dynamically are excluded.
1668      */
1669     public CacheConfiguration getCacheConfiguration() {
1670         return configuration;
1671     }
1672 
1673 
1674     /***
1675      * Flushes all cache items from memory to the disk store, and from the DiskStore to disk.
1676      *
1677      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1678      */
1679     public final synchronized void flush() throws IllegalStateException, CacheException {
1680         checkStatus();
1681         try {
1682             memoryStore.flush();
1683             if (isDiskStore()) {
1684                 diskStore.flush();
1685             }
1686         } catch (IOException e) {
1687             throw new CacheException("Unable to flush cache: " + configuration.getName()
1688                     + ". Initial cause was " + e.getMessage(), e);
1689         }
1690     }
1691 
1692     /***
1693      * Gets the size of the cache. This is a subtle concept. See below.
1694      * <p/>
1695      * The size is the number of {@link Element}s in the {@link MemoryStore}
1696      * plus the number of {@link Element}s in the {@link DiskStore}. However, if
1697      * the cache is Terracotta clustered, the underlying store has a coherent
1698      * view of the all the elements in the cache and doesn't have to be
1699      * aggregated from an underlying {@code MemoryStore} and {@code DiskStore}.
1700      * <p/>
1701      * This number is the actual number of elements, including expired elements
1702      * that have not been removed.
1703      * <p/>
1704      * Expired elements are removed from the the memory store when getting an
1705      * expired element, or when attempting to spool an expired element to disk.
1706      * <p/>
1707      * Expired elements are removed from the disk store when getting an expired
1708      * element, or when the expiry thread runs, which is once every five
1709      * minutes.
1710      * <p/>
1711      * To get an exact size, which would exclude expired elements, use
1712      * {@link #getKeysWithExpiryCheck()}.size(), although see that method for
1713      * the approximate time that would take.
1714      * <p/>
1715      * To get a very fast result, use {@link #getKeysNoDuplicateCheck()}.size().
1716      * If the disk store is being used, there will be some duplicates.
1717      * 
1718      * @return The size value
1719      * @throws IllegalStateException
1720      *             if the cache is not {@link Status#STATUS_ALIVE}
1721      */
1722     public final int getSize() throws IllegalStateException, CacheException {
1723         checkStatus();
1724         
1725         if (memoryStore.isCacheCoherent()) {
1726             return memoryStore.getTerracottaClusteredSize();
1727         } else {
1728             /* The memory store and the disk store can simultaneously contain elements with the same key
1729                 Cache size is the size of the union of the two key sets.*/
1730             return getKeys().size();
1731         }
1732     }
1733 
1734     /***
1735      * {@inheritDoc}
1736      */
1737     public int getSizeBasedOnAccuracy(int statisticsAccuracy)
1738             throws IllegalStateException, CacheException {
1739         if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_BEST_EFFORT) {
1740             return getSize();
1741         } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_GUARANTEED) {
1742             return getKeysWithExpiryCheck().size();
1743         } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_NONE) {
1744             return getKeysNoDuplicateCheck().size();
1745         }
1746         throw new IllegalArgumentException("Unknown statistics accuracy: "
1747                 + statisticsAccuracy);
1748     }
1749 
1750     /***
1751      * Gets the size of the memory store for this cache. This method relies on calculating
1752      * Serialized sizes. If the Element values are not Serializable they will show as zero.
1753      * <p/>
1754      * Warning: This method can be very expensive to run. Allow approximately 1 second
1755      * per 1MB of entries. Running this method could create liveness problems
1756      * because the object lock is held for a long period
1757      * <p/>
1758      *
1759      * @return the approximate size of the memory store in bytes
1760      * @throws IllegalStateException
1761      */
1762     public final long calculateInMemorySize() throws IllegalStateException, CacheException {
1763         checkStatus();
1764         return memoryStore.getSizeInBytes();
1765     }
1766 
1767 
1768     /***
1769      * Returns the number of elements in the memory store.
1770      *
1771      * @return the number of elements in the memory store
1772      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1773      */
1774     public final long getMemoryStoreSize() throws IllegalStateException {
1775         checkStatus();
1776         return memoryStore.getSize();
1777     }
1778 
1779     /***
1780      * Returns the number of elements in the disk store.
1781      *
1782      * @return the number of elements in the disk store.
1783      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1784      */
1785     public final int getDiskStoreSize() throws IllegalStateException {
1786         checkStatus();
1787         if (isTerracottaClustered()) {
1788             return memoryStore.getTerracottaClusteredSize();
1789         }
1790         if (isDiskStore()) {
1791             return diskStore.getSize();
1792         } else {
1793             return 0;
1794         }
1795     }
1796 
1797     /***
1798      * Gets the status attribute of the Cache.
1799      *
1800      * @return The status value from the Status enum class
1801      */
1802     public final Status getStatus() {
1803         return status;
1804     }
1805 
1806 
1807     private void checkStatus() throws IllegalStateException {
1808         if (!status.equals(Status.STATUS_ALIVE)) {
1809             throw new IllegalStateException("The " + configuration.getName() + " Cache is not alive.");
1810         }
1811     }
1812 
1813     private void checkStatusNotDisposed() throws IllegalStateException {
1814         if (status.equals(Status.STATUS_SHUTDOWN)) {
1815             throw new IllegalStateException("The " + configuration.getName() + " Cache is disposed.");
1816         }
1817     }
1818 
1819 
1820     /***
1821      * Gets the cache name.
1822      */
1823     public final String getName() {
1824         return configuration.getName();
1825     }
1826 
1827     /***
1828      * Sets the cache name which will name.
1829      *
1830      * @param name the name of the cache. Should not be null. Should also not contain any '/' characters, as these interfere
1831      *             with distribution
1832      * @throws IllegalArgumentException if an illegal name is used.
1833      */
1834     public final void setName(String name) throws IllegalArgumentException {
1835         if (!status.equals(Status.STATUS_UNINITIALISED)) {
1836             throw new IllegalStateException("Only uninitialised caches can have their names set.");
1837         }
1838         configuration.setName(name);
1839     }
1840 
1841     /***
1842      * Returns a {@link String} representation of {@link Cache}.
1843      */
1844     @Override
1845     public final String toString() {
1846         StringBuffer dump = new StringBuffer();
1847 
1848         dump.append("[")
1849                 .append(" name = ").append(configuration.getName())
1850                 .append(" status = ").append(status)
1851                 .append(" eternal = ").append(configuration.isEternal())
1852                 .append(" overflowToDisk = ").append(configuration.isOverflowToDisk())
1853                 .append(" maxElementsInMemory = ").append(configuration.getMaxElementsInMemory())
1854                 .append(" maxElementsOnDisk = ").append(configuration.getMaxElementsOnDisk())
1855                 .append(" memoryStoreEvictionPolicy = ").append(configuration.getMemoryStoreEvictionPolicy())
1856                 .append(" timeToLiveSeconds = ").append(configuration.getTimeToLiveSeconds())
1857                 .append(" timeToIdleSeconds = ").append(configuration.getTimeToIdleSeconds())
1858                 .append(" diskPersistent = ").append(configuration.isDiskPersistent())
1859                 .append(" diskExpiryThreadIntervalSeconds = ").append(configuration.getDiskExpiryThreadIntervalSeconds())
1860                 .append(registeredEventListeners)
1861                 .append(" hitCount = ").append(getLiveCacheStatisticsNoCheck().getCacheHitCount())
1862                 .append(" memoryStoreHitCount = ").append(getLiveCacheStatisticsNoCheck().getInMemoryHitCount())
1863                 .append(" diskStoreHitCount = ").append(getLiveCacheStatisticsNoCheck().getOnDiskHitCount())
1864                 .append(" missCountNotFound = ").append(getLiveCacheStatisticsNoCheck().getCacheMissCount())
1865                 .append(" missCountExpired = ").append(getLiveCacheStatisticsNoCheck().getCacheMissCountExpired())
1866                 .append(" ]");
1867 
1868         return dump.toString();
1869     }
1870 
1871 
1872     /***
1873      * Checks whether this cache element has expired.
1874      * <p/>
1875      * The element is expired if:
1876      * <ol>
1877      * <li> the idle time is non-zero and has elapsed, unless the cache is eternal; or
1878      * <li> the time to live is non-zero and has elapsed, unless the cache is eternal; or
1879      * <li> the value of the element is null.
1880      * </ol>
1881      *
1882      * @return true if it has expired
1883      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1884      * @throws NullPointerException  if the element is null
1885      */
1886     public final boolean isExpired(Element element) throws IllegalStateException, NullPointerException {
1887         checkStatus();
1888         synchronized (element) {
1889             return element.isExpired();
1890         }
1891     }
1892 
1893 
1894     /***
1895      * Clones a cache. This is only legal if the cache has not been
1896      * initialized. At that point only primitives have been set and no
1897      * stores have been created.
1898      * <p/>
1899      * A new, empty, RegisteredEventListeners is created on clone.
1900      * <p/>
1901      *
1902      * @return an object of type {@link Cache}
1903      * @throws CloneNotSupportedException
1904      */
1905     @Override
1906     public final Object clone() throws CloneNotSupportedException {
1907         if (!(memoryStore == null && diskStore == null)) {
1908             throw new CloneNotSupportedException("Cannot clone an initialized cache.");
1909         }
1910         Cache copy = (Cache) super.clone();
1911         // create new copies of the statistics
1912         copy.liveCacheStatisticsData = new LiveCacheStatisticsWrapper(copy);
1913         copy.sampledCacheStatistics = new SampledCacheStatisticsWrapper();
1914         
1915         copy.configuration = configuration.clone();
1916         copy.guid = createGuid();
1917 
1918         RegisteredEventListeners registeredEventListenersFromCopy = copy.getCacheEventNotificationService();
1919         if (registeredEventListenersFromCopy == null || registeredEventListenersFromCopy.getCacheEventListeners().size() == 0) {
1920             copy.registeredEventListeners = new RegisteredEventListeners(copy);
1921         } else {
1922             copy.registeredEventListeners = new RegisteredEventListeners(copy);
1923             Set cacheEventListeners = registeredEventListeners.getCacheEventListeners();
1924             for (Object cacheEventListener1 : cacheEventListeners) {
1925                 CacheEventListener cacheEventListener = (CacheEventListener) cacheEventListener1;
1926                 CacheEventListener cacheEventListenerClone = (CacheEventListener) cacheEventListener.clone();
1927                 copy.registeredEventListeners.registerListener(cacheEventListenerClone);
1928             }
1929         }
1930 
1931 
1932         copy.registeredCacheExtensions = new CopyOnWriteArrayList<CacheExtension>();
1933         for (CacheExtension registeredCacheExtension : registeredCacheExtensions) {
1934             copy.registerCacheExtension(registeredCacheExtension.clone(copy));
1935         }
1936 
1937         copy.registeredCacheLoaders = new CopyOnWriteArrayList<CacheLoader>();
1938         for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
1939             copy.registerCacheLoader(registeredCacheLoader.clone(copy));
1940         }
1941 
1942         if (bootstrapCacheLoader != null) {
1943             BootstrapCacheLoader bootstrapCacheLoaderClone = (BootstrapCacheLoader) bootstrapCacheLoader.clone();
1944             copy.setBootstrapCacheLoader(bootstrapCacheLoaderClone);
1945         }
1946 
1947         return copy;
1948     }
1949 
1950     /***
1951      * Gets the internal DiskStore.
1952      *
1953      * @return the DiskStore referenced by this cache
1954      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1955      */
1956     final Store getDiskStore() throws IllegalStateException {
1957         checkStatus();
1958         return diskStore;
1959     }
1960 
1961     /***
1962      * Gets the internal MemoryStore.
1963      *
1964      * @return the MemoryStore referenced by this cache
1965      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1966      */
1967     final Store getMemoryStore() throws IllegalStateException {
1968         checkStatus();
1969         return memoryStore;
1970     }
1971 
1972 
1973     /***
1974      * Use this to access the service in order to register and unregister listeners
1975      *
1976      * @return the RegisteredEventListeners instance for this cache.
1977      */
1978     public final RegisteredEventListeners getCacheEventNotificationService() {
1979         return registeredEventListeners;
1980     }
1981 
1982 
1983     /***
1984      * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
1985      *
1986      * @return true if an element matching the key is found in memory
1987      */
1988     public final boolean isElementInMemory(Serializable key) {
1989         return isElementInMemory((Object) key);
1990     }
1991 
1992     /***
1993      * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
1994      *
1995      * @return true if an element matching the key is found in memory
1996      * @since 1.2
1997      */
1998     public final boolean isElementInMemory(Object key) {
1999         return memoryStore.containsKey(key);
2000     }
2001 
2002     /***
2003      * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
2004      *
2005      * @return true if an element matching the key is found in the diskStore
2006      */
2007     public final boolean isElementOnDisk(Serializable key) {
2008         return isElementOnDisk((Object) key);
2009     }
2010 
2011     /***
2012      * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
2013      *
2014      * @return true if an element matching the key is found in the diskStore
2015      * @since 1.2
2016      */
2017     public final boolean isElementOnDisk(Object key) {
2018         if (!(key instanceof Serializable)) {
2019             return false;
2020         }
2021         Serializable serializableKey = (Serializable) key;
2022         if (isDiskStore()) {
2023             return diskStore != null && diskStore.containsKey(serializableKey);
2024         } else {
2025             return false;
2026         }
2027     }
2028 
2029     /***
2030      * The GUID for this cache instance can be used to determine whether two cache instance references
2031      * are pointing to the same cache.
2032      *
2033      * @return the globally unique identifier for this cache instance. This is guaranteed to be unique.
2034      * @since 1.2
2035      */
2036     public final String getGuid() {
2037         return guid;
2038     }
2039 
2040     /***
2041      * Gets the CacheManager managing this cache. For a newly created cache this will be null until
2042      * it has been added to a CacheManager.
2043      *
2044      * @return the manager or null if there is none
2045      */
2046     public final CacheManager getCacheManager() {
2047         return cacheManager;
2048     }
2049 
2050 
2051     /***
2052      * Resets statistics counters back to 0.
2053      *
2054      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2055      */
2056     public void clearStatistics() throws IllegalStateException {
2057         checkStatus();
2058         liveCacheStatisticsData.clearStatistics();
2059         registeredEventListeners.clearCounters();
2060     }
2061 
2062     /***
2063      * Accurately measuring statistics can be expensive. Returns the current accuracy setting.
2064      *
2065      * @return one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
2066      */
2067     public int getStatisticsAccuracy() {
2068         return getLiveCacheStatistics().getStatisticsAccuracy();
2069     }
2070 
2071     /***
2072      * Sets the statistics accuracy.
2073      *
2074      * @param statisticsAccuracy one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
2075      */
2076     public void setStatisticsAccuracy(int statisticsAccuracy) {
2077         liveCacheStatisticsData.setStatisticsAccuracy(statisticsAccuracy);
2078     }
2079 
2080     /***
2081      * Causes all elements stored in the Cache to be synchronously checked for expiry, and if expired, evicted.
2082      */
2083     public void evictExpiredElements() {
2084         Object[] keys = memoryStore.getKeyArray();
2085 
2086         for (Object key : keys) {
2087             searchInMemoryStore(key, false, true);
2088         }
2089 
2090         //This is called regularly by the expiry thread, but call it here synchronously
2091         if (isDiskStore()) {
2092             diskStore.expireElements();
2093         }
2094     }
2095 
2096     /***
2097      * An inexpensive check to see if the key exists in the cache.
2098      * <p/>
2099      * This method is not synchronized. It is possible that an element may exist in the cache and be removed
2100      * before the check gets to it, or vice versa.
2101      *
2102      * @param key the key to check.
2103      * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
2104      */
2105     public boolean isKeyInCache(Object key) {
2106         if (key == null) {
2107             return false;
2108         }
2109         return isElementInMemory(key) || isElementOnDisk(key);
2110     }
2111 
2112     /***
2113      * An extremely expensive check to see if the value exists in the cache. This implementation is O(n). Ehcache
2114      * is not designed for efficient access in this manner.
2115      * <p/>
2116      * This method is not synchronized. It is possible that an element may exist in the cache and be removed
2117      * before the check gets to it, or vice versa. Because it is slow to execute the probability of that this will
2118      * have happened.
2119      *
2120      * @param value to check for
2121      * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
2122      */
2123     public boolean isValueInCache(Object value) {
2124         boolean isSerializable = value instanceof Serializable;
2125         List keys;
2126         if (isSerializable) {
2127             keys = getKeys();
2128         } else {
2129             keys = Arrays.asList(memoryStore.getKeyArray());
2130         }
2131 
2132         for (Object key : keys) {
2133             Element element = get(key);
2134             if (element != null) {
2135                 Object elementValue = element.getValue();
2136                 if (elementValue == null) {
2137                     if (value == null) {
2138                         return true;
2139                     }
2140                 } else {
2141                     if (elementValue.equals(value)) {
2142                         return true;
2143                     }
2144                 }
2145             }
2146         }
2147         return false;
2148     }
2149 
2150     /***
2151      * {@inheritDoc}
2152      * <p/>
2153      * Note, the {@link #getSize} method will have the same value as the size
2154      * reported by Statistics for the statistics accuracy of
2155      * {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}.
2156      */
2157     public Statistics getStatistics() throws IllegalStateException {
2158         int size = getSizeBasedOnAccuracy(getLiveCacheStatistics()
2159                 .getStatisticsAccuracy());
2160         return new Statistics(this, getLiveCacheStatistics()
2161                 .getStatisticsAccuracy(), getLiveCacheStatistics()
2162                 .getCacheHitCount(), getLiveCacheStatistics()
2163                 .getOnDiskHitCount(), getLiveCacheStatistics()
2164                 .getInMemoryHitCount(), getLiveCacheStatistics()
2165                 .getCacheMissCount(), size, getAverageGetTime(),
2166                 getLiveCacheStatistics().getEvictedCount(),
2167                 getMemoryStoreSize(), getDiskStoreSize());
2168     }
2169 
2170     /***
2171      * For use by CacheManager.
2172      *
2173      * @param cacheManager the CacheManager for this cache to use.
2174      */
2175     public void setCacheManager(CacheManager cacheManager) {
2176         this.cacheManager = cacheManager;
2177     }
2178 
2179     /***
2180      * Accessor for the BootstrapCacheLoader associated with this cache. For testing purposes.
2181      */
2182     public BootstrapCacheLoader getBootstrapCacheLoader() {
2183         return bootstrapCacheLoader;
2184     }
2185 
2186     /***
2187      * Sets the bootstrap cache loader.
2188      *
2189      * @param bootstrapCacheLoader the loader to be used
2190      * @throws CacheException if this method is called after the cache is initialized
2191      */
2192     public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader) throws CacheException {
2193         if (!status.equals(Status.STATUS_UNINITIALISED)) {
2194             throw new CacheException("A bootstrap cache loader can only be set before the cache is initialized. "
2195                     + configuration.getName());
2196         }
2197         this.bootstrapCacheLoader = bootstrapCacheLoader;
2198     }
2199 
2200     /***
2201      * DiskStore paths can conflict between CacheManager instances. This method allows the path to be changed.
2202      *
2203      * @param diskStorePath the new path to be used.
2204      * @throws CacheException if this method is called after the cache is initialized
2205      */
2206     public void setDiskStorePath(String diskStorePath) throws CacheException {
2207         if (!status.equals(Status.STATUS_UNINITIALISED)) {
2208             throw new CacheException("A DiskStore path can only be set before the cache is initialized. "
2209                     + configuration.getName());
2210         }
2211         this.diskStorePath = diskStorePath;
2212     }
2213 
2214     /***
2215      * An equals method which follows the contract of {@link Object#equals(Object)}
2216      * <p/>
2217      * An Cache is equal to another one if it implements Ehcache and has the same GUID.
2218      *
2219      * @param object the reference object with which to compare.
2220      * @return <code>true</code> if this object is the same as the obj
2221      *         argument; <code>false</code> otherwise.
2222      * @see #hashCode()
2223      * @see java.util.Hashtable
2224      */
2225     @Override
2226     public boolean equals(Object object) {
2227         if (object == null) {
2228             return false;
2229         }
2230         if (!(object instanceof Ehcache)) {
2231             return false;
2232         }
2233         Ehcache other = (Ehcache) object;
2234         return guid.equals(other.getGuid());
2235     }
2236 
2237     /***
2238      * Returns a hash code value for the object. This method is
2239      * supported for the benefit of hashtables such as those provided by
2240      * <code>java.util.Hashtable</code>.
2241      * <p/>
2242      * The general contract of <code>hashCode</code> is:
2243      * <ul>
2244      * <li>Whenever it is invoked on the same object more than once during
2245      * an execution of a Java application, the <tt>hashCode</tt> method
2246      * must consistently return the same integer, provided no information
2247      * used in <tt>equals</tt> comparisons on the object is modified.
2248      * This integer need not remain consistent from one execution of an
2249      * application to another execution of the same application.
2250      * <li>If two objects are equal according to the <tt>equals(Object)</tt>
2251      * method, then calling the <code>hashCode</code> method on each of
2252      * the two objects must produce the same integer result.
2253      * <li>It is <em>not</em> required that if two objects are unequal
2254      * according to the {@link Object#equals(Object)}
2255      * method, then calling the <tt>hashCode</tt> method on each of the
2256      * two objects must produce distinct integer results.  However, the
2257      * programmer should be aware that producing distinct integer results
2258      * for unequal objects may improve the performance of hashtables.
2259      * </ul>
2260      * <p/>
2261      * As much as is reasonably practical, the hashCode method defined by
2262      * class <tt>Object</tt> does return distinct integers for distinct
2263      * objects. (This is typically implemented by converting the internal
2264      * address of the object into an integer, but this implementation
2265      * technique is not required by the
2266      * Java<font size="-2"><sup>TM</sup></font> programming language.)
2267      * <p/>
2268      * This implementation use the GUID of the cache.
2269      *
2270      * @return a hash code value for this object.
2271      * @see Object#equals(Object)
2272      * @see java.util.Hashtable
2273      */
2274     @Override
2275     public int hashCode() {
2276         return guid.hashCode();
2277     }
2278 
2279 
2280     /***
2281      * Create globally unique ID for this cache.
2282      */
2283     private String createGuid() {
2284         StringBuffer buffer = null;
2285         try {
2286             buffer = new StringBuffer().append(localhost).append("-").append(new UID());
2287         } catch (java.lang.NoClassDefFoundError e) {
2288             //Runtime environment restriction. e.g. Google App Engine
2289             //Weaker form of GUID. but we do not need it if networking is blocked.
2290             buffer = new StringBuffer().append(configuration.getName()).append("-").append(System.currentTimeMillis());
2291         }
2292 
2293         return buffer.toString();
2294     }
2295 
2296     /***
2297      * Register a {@link CacheExtension} with the cache. It will then be tied into the cache lifecycle.
2298      * <p/>
2299      * If the CacheExtension is not initialised, initialise it.
2300      */
2301     public void registerCacheExtension(CacheExtension cacheExtension) {
2302         registeredCacheExtensions.add(cacheExtension);
2303     }
2304 
2305     /***
2306      * @return the cache extensions as a live list
2307      */
2308     public List<CacheExtension> getRegisteredCacheExtensions() {
2309         return registeredCacheExtensions;
2310     }
2311 
2312 
2313     /***
2314      * Unregister a {@link CacheExtension} with the cache. It will then be detached from the cache lifecycle.
2315      */
2316     public void unregisterCacheExtension(CacheExtension cacheExtension) {
2317         cacheExtension.dispose();
2318         registeredCacheExtensions.remove(cacheExtension);
2319     }
2320 
2321 
2322     /***
2323      * The average get time in ms.
2324      */
2325     public float getAverageGetTime() {
2326         return getLiveCacheStatistics().getAverageGetTimeMillis();
2327     }
2328 
2329     /***
2330      * Sets an ExceptionHandler on the Cache. If one is already set, it is overwritten.
2331      * <p/>
2332      * The ExceptionHandler is only used if this Cache's methods are accessed using
2333      * {@link net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy}.
2334      *
2335      * @see net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy
2336      */
2337     public void setCacheExceptionHandler(CacheExceptionHandler cacheExceptionHandler) {
2338         this.cacheExceptionHandler = cacheExceptionHandler;
2339     }
2340 
2341     /***
2342      * Gets the ExceptionHandler on this Cache, or null if there isn't one.
2343      * <p/>
2344      * The ExceptionHandler is only used if this Cache's methods are accessed using
2345      * {@link net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy}.
2346      *
2347      * @see net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy
2348      */
2349     public CacheExceptionHandler getCacheExceptionHandler() {
2350         return cacheExceptionHandler;
2351     }
2352 
2353     /***
2354      * Register a {@link CacheLoader} with the cache. It will then be tied into the cache lifecycle.
2355      * <p/>
2356      * If the CacheLoader is not initialised, initialise it.
2357      *
2358      * @param cacheLoader A Cache Loader to register
2359      */
2360     public void registerCacheLoader(CacheLoader cacheLoader) {
2361         registeredCacheLoaders.add(cacheLoader);
2362     }
2363 
2364     /***
2365      * Unregister a {@link CacheLoader} with the cache. It will then be detached from the cache lifecycle.
2366      *
2367      * @param cacheLoader A Cache Loader to unregister
2368      */
2369     public void unregisterCacheLoader(CacheLoader cacheLoader) {
2370         registeredCacheLoaders.remove(cacheLoader);
2371     }
2372 
2373 
2374     /***
2375      * @return the cache loaders as a live list
2376      */
2377     public List<CacheLoader> getRegisteredCacheLoaders() {
2378         return registeredCacheLoaders;
2379     }
2380 
2381     /***
2382      * Does the asynchronous loading.
2383      *
2384      * @param key
2385      * @param specificLoader a specific loader to use. If null the default loader is used.
2386      * @param argument
2387      * @return a Future which can be used to monitor execution
2388      */
2389     Future asynchronousLoad(final Object key, final CacheLoader specificLoader, final Object argument) {
2390         return getExecutorService().submit(new Runnable() {
2391 
2392             /***
2393              * Calls the CacheLoader and puts the result in the Cache
2394              */
2395             public void run() throws CacheException {
2396                 try {
2397                     //Test to see if it has turned up in the meantime
2398                     boolean existsOnRun = isKeyInCache(key);
2399                     if (!existsOnRun) {
2400                         Object value;
2401                         if (specificLoader == null) {
2402                             if (registeredCacheLoaders.size() == 0) {
2403                                 return;
2404                             }
2405                             value = loadWithRegisteredLoaders(argument, key);
2406                         } else {
2407                             if (argument == null) {
2408                                 value = specificLoader.load(key);
2409                             } else {
2410                                 value = specificLoader.load(key, argument);
2411                             }
2412                         }
2413                         if (value != null) {
2414                             put(new Element(key, value), false);
2415                         }
2416                     }
2417                 } catch (Throwable e) {
2418                     if (LOG.isLoggable(Level.FINE)) {
2419                         LOG.log(Level.FINE, "Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
2420                     }
2421                     throw new CacheException("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
2422                 }
2423             }
2424         });
2425     }
2426 
2427     private Object loadWithRegisteredLoaders(Object argument, Object key) throws CacheException {
2428 
2429         Object value = null;
2430 
2431         if (argument == null) {
2432             for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
2433                 value = registeredCacheLoader.load(key);
2434                 if (value != null) {
2435                     break;
2436                 }
2437             }
2438         } else {
2439             for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
2440                 value = registeredCacheLoader.load(key, argument);
2441                 if (value != null) {
2442                     break;
2443                 }
2444             }
2445         }
2446         return value;
2447     }
2448 
2449 
2450     /***
2451      * Creates a future to perform the load
2452      *
2453      * @param keys
2454      * @param argument the loader argument
2455      * @return a Future which can be used to monitor execution
2456      */
2457     Future asynchronousLoadAll(final Collection keys, final Object argument) {
2458         return getExecutorService().submit(new Runnable() {
2459             /***
2460              * Calls the CacheLoader and puts the result in the Cache
2461              */
2462             public void run() {
2463                 try {
2464                     Set<Object> nonLoadedKeys = new HashSet<Object>();
2465                     for (Object key : keys) {
2466                         if (!isKeyInCache(key)) {
2467                             nonLoadedKeys.add(key);
2468                         }
2469                     }
2470                     Map map = loadWithRegisteredLoaders(argument, nonLoadedKeys);
2471                     for (Object key : map.keySet()) {
2472                         put(new Element(key, map.get(key)));
2473                     }
2474                 } catch (Throwable e) {
2475                     if (LOG.isLoggable(Level.SEVERE)) {
2476                         LOG.log(Level.SEVERE, "Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
2477                     }
2478                 }
2479             }
2480         });
2481     }
2482 
2483     /***
2484      * Does the asynchronous loading.
2485      * @param argument the loader argument
2486      * @param nonLoadedKeys the Set of keys that are already in the Cache
2487      * @return A map of loaded elements
2488      */
2489     Map loadWithRegisteredLoaders(Object argument, Set<Object> nonLoadedKeys) {
2490         Map result = new HashMap();
2491         for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
2492             if (nonLoadedKeys.isEmpty()) {
2493                 break;
2494             }
2495 
2496             Map resultForThisCacheLoader = null;
2497             if (argument == null) {
2498                 resultForThisCacheLoader = registeredCacheLoader.loadAll(nonLoadedKeys);
2499             } else {
2500                 resultForThisCacheLoader = registeredCacheLoader.loadAll(nonLoadedKeys, argument);
2501             }
2502             if (resultForThisCacheLoader != null) {
2503                 nonLoadedKeys.removeAll(resultForThisCacheLoader.keySet());
2504                 result.putAll(resultForThisCacheLoader);
2505             }
2506         }
2507         return result;
2508     }
2509 
2510     /***
2511      * @return Gets the executor service. This is not publically accessible.
2512      */
2513     ExecutorService getExecutorService() {
2514         if (executorService == null) {
2515             synchronized (this) {
2516                 boolean inGoogleAppEngine;
2517 
2518                 try {
2519                     Class.forName("com.google.apphosting.api.DeadlineExceededException");
2520                     inGoogleAppEngine = true;
2521                 } catch (ClassNotFoundException cnfe) {
2522                     inGoogleAppEngine = false;
2523                 }
2524 
2525                 if (inGoogleAppEngine) {
2526 
2527                     // no Thread support. Run all tasks on the caller thread
2528                     executorService = new AbstractExecutorService() {
2529                         /*** {@inheritDoc} */
2530                         public void execute(Runnable command) {
2531                             command.run();
2532                         }
2533                         /*** {@inheritDoc} */
2534                         public List<Runnable> shutdownNow() {
2535                             return Collections.emptyList();
2536                         }
2537                         /*** {@inheritDoc} */
2538                         public void shutdown() {
2539                         }
2540                         /*** {@inheritDoc} */
2541                         public boolean isTerminated() {
2542                             return isShutdown();
2543                         }
2544                         /*** {@inheritDoc} */
2545                         public boolean isShutdown() {
2546                             return false;
2547                         }
2548                         /*** {@inheritDoc} */
2549                         public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
2550                             return true;
2551                         }
2552                     };
2553                 } else {
2554                     // we can create Threads
2555                     executorService = new ThreadPoolExecutor(EXECUTOR_CORE_POOL_SIZE, EXECUTOR_MAXIMUM_POOL_SIZE,
2556                             EXECUTOR_KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
2557                 }
2558             }
2559         }
2560         return executorService;
2561     }
2562 
2563 
2564     /***
2565      * Whether this cache is disabled. "Disabled" means:
2566      * <ol>
2567      * <li>bootstrap is disabled
2568      * <li>puts are discarded
2569      * <li>putQuites are discarded
2570      * </ol>
2571      * In all other respects the cache continues as it is.
2572      * <p/>
2573      * You can disable and enable a cache programmatically through the {@link #setDisabled(boolean)} method.
2574      * <p/>
2575      * By default caches are enabled on creation, unless the <code>net.sf.ehcache.disabled</code> system
2576      * property is set.
2577      *
2578      * @return true if the cache is disabled.
2579      * @see #NET_SF_EHCACHE_DISABLED ?
2580      */
2581     public boolean isDisabled() {
2582         return disabled;
2583     }
2584 
2585     /***
2586      * Disables or enables this cache. This call overrides the previous value of disabled, even if the
2587      * <code>net.sf.ehcache.disabled</code> system property is set
2588      * <p/>
2589      *
2590      * @param disabled true if you wish to disable, false to enable
2591      * @see #isDisabled()
2592      */
2593     public void setDisabled(boolean disabled) {
2594         this.disabled = disabled;
2595     }
2596 
2597     /***
2598      * @return the current MemoryStore policy. This may not be the configured policy, if it has been
2599      *         dynamically set.
2600      */
2601     public Policy getMemoryStoreEvictionPolicy() {
2602         return memoryStore.getEvictionPolicy();
2603     }
2604 
2605     /***
2606      * Sets the eviction policy strategy. The Cache will use a policy at startup. There are three policies
2607      * which can be configured: LRU, LFU and FIFO. However many other policies are possible. That the policy
2608      * has access to the whole element enables policies based on the key, value, metadata, statistics, or a combination of
2609      * any of the above. It is safe to change the policy of a store at any time. The new policy takes effect
2610      * immediately.
2611      *
2612      * @param policy the new policy
2613      */
2614     public void setMemoryStoreEvictionPolicy(Policy policy) {
2615         memoryStore.setEvictionPolicy(policy);
2616     }
2617 
2618     /***
2619      * {@inheritDoc}
2620      */
2621     public LiveCacheStatistics getLiveCacheStatistics()
2622             throws IllegalStateException {
2623         checkStatus();
2624         return (LiveCacheStatistics) liveCacheStatisticsData;
2625     }
2626     
2627     private LiveCacheStatistics getLiveCacheStatisticsNoCheck() {
2628         return (LiveCacheStatistics) liveCacheStatisticsData;
2629     }
2630 
2631     /***
2632      * {@inheritDoc}
2633      */
2634     public void registerCacheUsageListener(CacheUsageListener cacheUsageListener)
2635             throws IllegalStateException {
2636         checkStatus();
2637         liveCacheStatisticsData.registerCacheUsageListener(cacheUsageListener);
2638     }
2639 
2640     /***
2641      * {@inheritDoc}
2642      */
2643     public void removeCacheUsageListener(CacheUsageListener cacheUsageListener)
2644             throws IllegalStateException {
2645         checkStatus();
2646         liveCacheStatisticsData.removeCacheUsageListener(cacheUsageListener);
2647     }
2648 
2649     /***
2650      * {@inheritDoc}
2651      */
2652     public boolean isStatisticsEnabled() {
2653         return getLiveCacheStatistics().isStatisticsEnabled();
2654     }
2655 
2656     /***
2657      * {@inheritDoc}
2658      */
2659     public void setStatisticsEnabled(boolean enableStatistics) {
2660         liveCacheStatisticsData.setStatisticsEnabled(enableStatistics);
2661         if (!enableStatistics) {
2662             setSampledStatisticsEnabled(false);
2663         }
2664     }
2665 
2666     /***
2667      * {@inheritDoc}
2668      */
2669     public SampledCacheStatistics getSampledCacheStatistics() {
2670         return sampledCacheStatistics;
2671     }
2672     
2673     /***
2674      * {@inheritDoc}
2675      */
2676     public void setSampledStatisticsEnabled(final boolean enableStatistics) {
2677         if (cacheManager == null) {
2678             throw new IllegalStateException(
2679                     "You must add the cache to a CacheManager before enabling/disabling sampled statistics.");
2680         }
2681         if (enableStatistics) {
2682             setStatisticsEnabled(true);
2683             sampledCacheStatistics.enableSampledStatistics(cacheManager
2684                     .getTimer());
2685         } else {
2686             sampledCacheStatistics.disableSampledStatistics();
2687         }
2688     }
2689 
2690     /***
2691      * {@inheritDoc}
2692      * 
2693      * @see net.sf.ehcache.Ehcache#isSampledStatisticsEnabled()
2694      */
2695     public boolean isSampledStatisticsEnabled() {
2696         return sampledCacheStatistics.isSampledStatisticsEnabled();
2697     }
2698 
2699     /***
2700      * {@inheritDoc}
2701      */
2702     public Object getInternalContext() {
2703         return memoryStore.getInternalContext();
2704     }
2705 }