View Javadoc

1   /***
2    *  Copyright 2003-2008 Luck Consulting Pty Ltd
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package net.sf.ehcache;
18  
19  import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
20  import net.sf.ehcache.config.CacheConfiguration;
21  import net.sf.ehcache.config.DiskStoreConfiguration;
22  import net.sf.ehcache.event.CacheEventListener;
23  import net.sf.ehcache.event.RegisteredEventListeners;
24  import net.sf.ehcache.exceptionhandler.CacheExceptionHandler;
25  import net.sf.ehcache.extension.CacheExtension;
26  import net.sf.ehcache.loader.CacheLoader;
27  import net.sf.ehcache.store.DiskStore;
28  import net.sf.ehcache.store.MemoryStore;
29  import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
30  import net.sf.ehcache.store.Store;
31  
32  import java.io.IOException;
33  import java.io.Serializable;
34  import java.net.InetAddress;
35  import java.net.UnknownHostException;
36  import java.rmi.server.UID;
37  import java.util.ArrayList;
38  import java.util.Arrays;
39  import java.util.Collection;
40  import java.util.Collections;
41  import java.util.HashMap;
42  import java.util.HashSet;
43  import java.util.Iterator;
44  import java.util.List;
45  import java.util.Map;
46  import java.util.Set;
47  import java.util.logging.Logger;
48  import java.util.logging.Level;
49  import java.util.concurrent.Future;
50  import java.util.concurrent.ExecutionException;
51  import java.util.concurrent.LinkedBlockingQueue;
52  import java.util.concurrent.ThreadPoolExecutor;
53  import java.util.concurrent.TimeUnit;
54  
55  /***
56   * Cache is the central class in ehcache. Caches have {@link Element}s and are managed
57   * by the {@link CacheManager}. The Cache performs logical actions. It delegates physical
58   * implementations to its {@link net.sf.ehcache.store.Store}s.
59   * <p/>
60   * A reference to a Cache can be obtained through the {@link CacheManager}. A Cache thus obtained
61   * is guaranteed to have status {@link Status#STATUS_ALIVE}. This status is checked for any method which
62   * throws {@link IllegalStateException} and the same thrown if it is not alive. This would normally
63   * happen if a call is made after {@link CacheManager#shutdown} is invoked.
64   * <p/>
65   * Cache is threadsafe.
66   * <p/>
67   * Statistics on cache usage are collected and made available through the {@link #getStatistics()} methods.
68   * <p/>
69   * Various decorators are available for Cache, such as BlockingCache, SelfPopulatingCache and the dynamic proxy
70   * ExceptionHandlingDynamicCacheProxy. See each class for details.
71   *
72   * @author Greg Luck
73   * @version $Id: Cache.java 744 2008-08-16 20:10:49Z gregluck $
74   */
75  public class Cache implements Ehcache {
76  
77      /***
78       * A reserved word for cache names. It denotes a default configuration
79       * which is applied to caches created without configuration.
80       */
81      public static final String DEFAULT_CACHE_NAME = "default";
82  
83      /***
84       * System Property based method of disabling ehcache. If disabled no elements will be added to a cache.
85       * <p/>
86       * Set the property "net.sf.ehcache.disabled=true" to disable ehcache.
87       * <p/>
88       * This can easily be done using <code>java -Dnet.sf.ehcache.disabled=true</code> in the command line.
89       */
90      public static final String NET_SF_EHCACHE_DISABLED = "net.sf.ehcache.disabled";
91  
92      {
93          String value = System.getProperty(NET_SF_EHCACHE_DISABLED);
94          if (value != null) {
95              disabled = value.equalsIgnoreCase("true");
96          }
97      }
98  
99      /***
100      * The default interval between runs of the expiry thread.
101      */
102     public static final long DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS = 120;
103 
104     /***
105      * Set a buffer size for the spool of approx 30MB
106      */
107     private static final int DEFAULT_SPOOL_BUFFER_SIZE = 30;
108 
109     private static final Logger LOG = Logger.getLogger(Cache.class.getName());
110 
111     private static final MemoryStoreEvictionPolicy DEFAULT_MEMORY_STORE_EVICTION_POLICY = MemoryStoreEvictionPolicy.LRU;
112 
113     private static InetAddress localhost;
114 
115     /***
116      * The amount of time to wait if a store gets backed up
117      */
118     private static final int BACK_OFF_TIME_MILLIS = 50;
119 
120     private static final int EXECUTOR_KEEP_ALIVE_TIME = 60000;
121     private static final int EXECUTOR_MAXIMUM_POOL_SIZE = 10;
122     private static final int EXECUTOR_CORE_POOL_SIZE = 1;
123 
124     static {
125         try {
126             localhost = InetAddress.getLocalHost();
127         } catch (UnknownHostException e) {
128             LOG.log(Level.SEVERE, "Unable to set localhost. This prevents creation of a GUID. Cause was: " + e.getMessage(), e);
129         }
130     }
131 
132     private boolean disabled;
133 
134     private Store diskStore;
135 
136     private String diskStorePath;
137 
138     private Status status;
139 
140     private CacheConfiguration configuration;
141 
142     /***
143      * Cache hit count.
144      */
145     private long hitCount;
146 
147     /***
148      * Memory cache hit count.
149      */
150     private long memoryStoreHitCount;
151 
152     /***
153      * DiskStore hit count.
154      */
155     private long diskStoreHitCount;
156 
157     /***
158      * Count of misses where element was not found.
159      */
160     private long missCountNotFound;
161 
162     /***
163      * Count of misses where element was expired.
164      */
165     private long missCountExpired;
166 
167     /***
168      * The {@link MemoryStore} of this {@link Cache}. All caches have a memory store.
169      */
170     private MemoryStore memoryStore;
171 
172     private RegisteredEventListeners registeredEventListeners;
173 
174     private List registeredCacheExtensions;
175 
176     private String guid;
177 
178     private CacheManager cacheManager;
179 
180     private BootstrapCacheLoader bootstrapCacheLoader;
181 
182     private int statisticsAccuracy;
183 
184     private long totalGetTime;
185 
186     private CacheExceptionHandler cacheExceptionHandler;
187 
188     private CacheLoader cacheLoader;
189 
190     /***
191      * A ThreadPoolExecutor which uses a thread pool to schedule loads in the order in which they are requested.
192      * <p/>
193      * Each cache has its own one of these, if required. Because the Core Thread Pool is zero, no threads
194      * are used until actually needed. Threads are added to the pool up to a maximum of 10. The keep alive
195      * time is 60 seconds, after which, if they are not required they will be stopped and collected.
196      * <p/>
197      * The executorService is only used for cache loading, and is created lazily on demand to avoid unnecessary resource
198      * usage.
199      * <p/>
200      * Use {@link #getExecutorService()} to ensure that it is initialised.
201      */
202     private ThreadPoolExecutor executorService;
203 
204 
205     /***
206      * 1.0 Constructor.
207      * <p/>
208      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
209      * <p/>
210      * A client can specify their own settings here and pass the {@link Cache} object
211      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
212      * <p/>
213      * Only the CacheManager can initialise them.
214      * <p/>
215      * This constructor creates disk stores, if specified, that do not persist between restarts.
216      * <p/>
217      * The default expiry thread interval of 120 seconds is used. This is the interval between runs
218      * of the expiry thread, where it checks the disk store for expired elements. It is not the
219      * the timeToLiveSeconds.
220      *
221      * @param name                the name of the cache. Note that "default" is a reserved name for the defaultCache.
222      * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
223      * @param overflowToDisk      whether to use the disk store
224      * @param eternal             whether the elements in the cache are eternal, i.e. never expire
225      * @param timeToLiveSeconds   the default amount of time to live for an element from its creation date
226      * @param timeToIdleSeconds   the default amount of time to live for an element from its last accessed or modified date
227      * @since 1.0
228      */
229     public Cache(String name, int maxElementsInMemory, boolean overflowToDisk,
230                  boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds) {
231         this(name, maxElementsInMemory, DEFAULT_MEMORY_STORE_EVICTION_POLICY, overflowToDisk,
232                 null, eternal, timeToLiveSeconds, timeToIdleSeconds, false, DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS, null, null);
233     }
234 
235 
236     /***
237      * 1.1 Constructor.
238      * <p/>
239      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
240      * <p/>
241      * A client can specify their own settings here and pass the {@link Cache} object
242      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
243      * <p/>
244      * Only the CacheManager can initialise them.
245      *
246      * @param name                the name of the cache. Note that "default" is a reserved name for the defaultCache.
247      * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
248      * @param overflowToDisk      whether to use the disk store
249      * @param eternal             whether the elements in the cache are eternal, i.e. never expire
250      * @param timeToLiveSeconds   the default amount of time to live for an element from its creation date
251      * @param timeToIdleSeconds   the default amount of time to live for an element from its last accessed or modified date
252      * @param diskPersistent      whether to persist the cache to disk between JVM restarts
253      * @param diskExpiryThreadIntervalSeconds
254      *                            how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
255      * @since 1.1
256      */
257     public Cache(String name,
258                  int maxElementsInMemory,
259                  boolean overflowToDisk,
260                  boolean eternal,
261                  long timeToLiveSeconds,
262                  long timeToIdleSeconds,
263                  boolean diskPersistent,
264                  long diskExpiryThreadIntervalSeconds) {
265         this(name, maxElementsInMemory, DEFAULT_MEMORY_STORE_EVICTION_POLICY, overflowToDisk, null,
266                 eternal, timeToLiveSeconds, timeToIdleSeconds, diskPersistent, diskExpiryThreadIntervalSeconds, null, null);
267         LOG.warning("An API change between ehcache-1.1 and ehcache-1.2 results in the persistence path being set to " +
268                 DiskStoreConfiguration.getDefaultPath() + " when the ehcache-1.1 constructor is used. " +
269                 "Please change to the 1.2 constructor.");
270     }
271 
272 
273     /***
274      * 1.2 Constructor
275      * <p/>
276      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
277      * <p/>
278      * A client can specify their own settings here and pass the {@link Cache} object
279      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
280      * <p/>
281      * Only the CacheManager can initialise them.
282      *
283      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
284      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted
285      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
286      * @param overflowToDisk            whether to use the disk store
287      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
288      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
289      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
290      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
291      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
292      * @param diskExpiryThreadIntervalSeconds
293      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
294      * @param registeredEventListeners  a notification service. Optionally null, in which case a new
295      *                                  one with no registered listeners will be created.
296      * @since 1.2
297      */
298     public Cache(String name,
299                  int maxElementsInMemory,
300                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
301                  boolean overflowToDisk,
302                  String diskStorePath,
303                  boolean eternal,
304                  long timeToLiveSeconds,
305                  long timeToIdleSeconds,
306                  boolean diskPersistent,
307                  long diskExpiryThreadIntervalSeconds,
308                  RegisteredEventListeners registeredEventListeners) {
309         this(name,
310                 maxElementsInMemory,
311                 memoryStoreEvictionPolicy,
312                 overflowToDisk,
313                 diskStorePath,
314                 eternal,
315                 timeToLiveSeconds,
316                 timeToIdleSeconds,
317                 diskPersistent,
318                 diskExpiryThreadIntervalSeconds,
319                 registeredEventListeners,
320                 null);
321     }
322 
323 
324     /***
325      * 1.2.1 Constructor
326      * <p/>
327      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
328      * <p/>
329      * A client can specify their own settings here and pass the {@link Cache} object
330      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
331      * <p/>
332      * Only the CacheManager can initialise them.
333      *
334      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
335      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted
336      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
337      * @param overflowToDisk            whether to use the disk store
338      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
339      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
340      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
341      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
342      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
343      * @param diskExpiryThreadIntervalSeconds
344      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
345      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
346      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
347      * @since 1.2.1
348      */
349     public Cache(String name,
350                  int maxElementsInMemory,
351                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
352                  boolean overflowToDisk,
353                  String diskStorePath,
354                  boolean eternal,
355                  long timeToLiveSeconds,
356                  long timeToIdleSeconds,
357                  boolean diskPersistent,
358                  long diskExpiryThreadIntervalSeconds,
359                  RegisteredEventListeners registeredEventListeners,
360                  BootstrapCacheLoader bootstrapCacheLoader) {
361 
362         this(name,
363                 maxElementsInMemory,
364                 memoryStoreEvictionPolicy,
365                 overflowToDisk,
366                 diskStorePath,
367                 eternal,
368                 timeToLiveSeconds,
369                 timeToIdleSeconds,
370                 diskPersistent,
371                 diskExpiryThreadIntervalSeconds,
372                 registeredEventListeners,
373                 bootstrapCacheLoader,
374                 0);
375     }
376 
377     /***
378      * 1.2.4 Constructor
379      * <p/>
380      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
381      * <p/>
382      * A client can specify their own settings here and pass the {@link Cache} object
383      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
384      * <p/>
385      * Only the CacheManager can initialise them.
386      *
387      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
388      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted
389      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
390      * @param overflowToDisk            whether to use the disk store
391      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
392      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
393      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
394      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
395      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
396      * @param diskExpiryThreadIntervalSeconds
397      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
398      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
399      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
400      * @since 1.2.4
401      */
402     public Cache(String name,
403                  int maxElementsInMemory,
404                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
405                  boolean overflowToDisk,
406                  String diskStorePath,
407                  boolean eternal,
408                  long timeToLiveSeconds,
409                  long timeToIdleSeconds,
410                  boolean diskPersistent,
411                  long diskExpiryThreadIntervalSeconds,
412                  RegisteredEventListeners registeredEventListeners,
413                  BootstrapCacheLoader bootstrapCacheLoader,
414                  int maxElementsOnDisk) {
415 
416 
417         this(name,
418                 maxElementsInMemory,
419                 memoryStoreEvictionPolicy,
420                 overflowToDisk,
421                 diskStorePath,
422                 eternal,
423                 timeToLiveSeconds,
424                 timeToIdleSeconds,
425                 diskPersistent,
426                 diskExpiryThreadIntervalSeconds,
427                 registeredEventListeners,
428                 bootstrapCacheLoader,
429                 maxElementsOnDisk,
430                 0);
431 
432     }
433 
434     /***
435      * 1.2.4 Constructor
436      * <p/>
437      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
438      * <p/>
439      * A client can specify their own settings here and pass the {@link Cache} object
440      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
441      * <p/>
442      * Only the CacheManager can initialise them.
443      *
444      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
445      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted
446      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
447      * @param overflowToDisk            whether to use the disk store
448      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
449      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
450      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
451      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
452      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
453      * @param diskExpiryThreadIntervalSeconds
454      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
455      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
456      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
457      * @param diskSpoolBufferSizeMB     the amount of memory to allocate the write buffer for puts to the DiskStore.
458      * @since 1.2.4
459      */
460     public Cache(String name,
461                  int maxElementsInMemory,
462                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
463                  boolean overflowToDisk,
464                  String diskStorePath,
465                  boolean eternal,
466                  long timeToLiveSeconds,
467                  long timeToIdleSeconds,
468                  boolean diskPersistent,
469                  long diskExpiryThreadIntervalSeconds,
470                  RegisteredEventListeners registeredEventListeners,
471                  BootstrapCacheLoader bootstrapCacheLoader,
472                  int maxElementsOnDisk,
473                  int diskSpoolBufferSizeMB) {
474 
475         changeStatus(Status.STATUS_UNINITIALISED);
476 
477         guid = createGuid();
478 
479         configuration = new CacheConfiguration();
480         configuration.setName(name);
481         configuration.setMaxElementsInMemory(maxElementsInMemory);
482         configuration.setMemoryStoreEvictionPolicyFromObject(memoryStoreEvictionPolicy);
483         configuration.setOverflowToDisk(overflowToDisk);
484         configuration.setEternal(eternal);
485         configuration.setTimeToLiveSeconds(timeToLiveSeconds);
486         configuration.setTimeToIdleSeconds(timeToIdleSeconds);
487         configuration.setDiskPersistent(diskPersistent);
488         configuration.setMaxElementsOnDisk(maxElementsOnDisk);
489 
490 
491         if (diskStorePath == null) {
492             this.diskStorePath = DiskStoreConfiguration.getDefaultPath();
493         } else {
494             this.diskStorePath = diskStorePath;
495         }
496 
497         if (registeredEventListeners == null) {
498             this.registeredEventListeners = new RegisteredEventListeners(this);
499         } else {
500             this.registeredEventListeners = registeredEventListeners;
501         }
502 
503         registeredCacheExtensions = createNewCacheExtensionsList();
504 
505         //Set this to a safe value.
506         if (diskExpiryThreadIntervalSeconds == 0) {
507             configuration.setDiskExpiryThreadIntervalSeconds(DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS);
508         } else {
509             configuration.setDiskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds);
510         }
511 
512         if (diskSpoolBufferSizeMB == 0) {
513             configuration.setDiskSpoolBufferSizeMB(DEFAULT_SPOOL_BUFFER_SIZE);
514         } else {
515             configuration.setDiskSpoolBufferSizeMB(diskSpoolBufferSizeMB);
516         }
517 
518         // For backward compatibility with 1.1 and earlier
519         if (memoryStoreEvictionPolicy == null) {
520             configuration.setMemoryStoreEvictionPolicyFromObject(DEFAULT_MEMORY_STORE_EVICTION_POLICY);
521         }
522 
523         this.bootstrapCacheLoader = bootstrapCacheLoader;
524 
525         statisticsAccuracy = Statistics.STATISTICS_ACCURACY_BEST_EFFORT;
526 
527     }
528 
529     /***
530      * Newly created caches do not have a {@link net.sf.ehcache.store.MemoryStore} or a {@link net.sf.ehcache.store.DiskStore}.
531      * <p/>
532      * This method creates those and makes the cache ready to accept elements
533      */
534     public void initialise() {
535         synchronized (this) {
536             if (!status.equals(Status.STATUS_UNINITIALISED)) {
537                 throw new IllegalStateException("Cannot initialise the " + configuration.getName()
538                         + " cache because its status is not STATUS_UNINITIALISED");
539             }
540 
541             if (configuration.getMaxElementsInMemory() == 0) {
542                 if (LOG.isLoggable(Level.WARNING)) {
543                     LOG.warning("Cache: " + configuration.getName()
544                             + " has a maxElementsInMemory of 0. It is strongly recommended to " +
545                             "have a maximumSize of at least 1. Performance is halved by not using a MemoryStore.");
546                 }
547             }
548 
549             this.diskStore = createDiskStore();
550 
551             memoryStore = MemoryStore.create(this, diskStore);
552             changeStatus(Status.STATUS_ALIVE);
553             initialiseRegisteredCacheExtensions();
554         }
555 
556         if (LOG.isLoggable(Level.FINE)) {
557             LOG.fine("Initialised cache: " + configuration.getName());
558         }
559 
560         if (disabled) {
561             if (LOG.isLoggable(Level.WARNING)) {
562                 LOG.warning("Cache: " + configuration.getName() + " is disabled because the " + NET_SF_EHCACHE_DISABLED
563                         + " property was set to true. No elements will be added to the cache.");
564             }
565         }
566     }
567 
568     /***
569      * Creates a disk store when either:
570      * <ol>
571      * <li>overflowToDisk is enabled
572      * <li>diskPersistent is enabled
573      * </ol>
574      */
575     protected Store createDiskStore() {
576         if (isDiskStore()) {
577             return new DiskStore(this, diskStorePath);
578         } else {
579             return null;
580         }
581     }
582 
583     /***
584      * Whether this cache uses a disk store
585      * @return true if the cache either overflows to disk or is disk persistent
586      */
587     protected boolean isDiskStore() {
588         return configuration.isOverflowToDisk() || configuration.isDiskPersistent();
589     }
590 
591     /***
592      * Bootstrap command. This must be called after the Cache is intialised, during
593      * CacheManager initialisation. If loads are synchronous, they will complete before the CacheManager
594      * initialise completes, otherwise they will happen in the background.
595      */
596     public void bootstrap() {
597         if (!disabled && bootstrapCacheLoader != null) {
598             bootstrapCacheLoader.load(this);
599         }
600 
601     }
602 
603     private void changeStatus(Status status) {
604         this.status = status;
605     }
606 
607 
608     /***
609      * Put an element in the cache.
610      * <p/>
611      * Resets the access statistics on the element, which would be the case if it has previously been
612      * gotten from a cache, and is now being put back.
613      * <p/>
614      * Also notifies the CacheEventListener that:
615      * <ul>
616      * <li>the element was put, but only if the Element was actually put.
617      * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
618      * if it was requested
619      * </ul>
620      * Synchronization is handled within the method.
621      * <p/>
622      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
623      * This exception should be caught in those cirucmstances.
624      *
625      * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
626      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
627      * @throws IllegalArgumentException if the element is null
628      * @throws CacheException
629      */
630     public final void put(Element element) throws IllegalArgumentException, IllegalStateException,
631             CacheException {
632         put(element, false);
633     }
634 
635 
636     /***
637      * Put an element in the cache.
638      * <p/>
639      * Resets the access statistics on the element, which would be the case if it has previously been
640      * gotten from a cache, and is now being put back.
641      * <p/>
642      * Also notifies the CacheEventListener that:
643      * <ul>
644      * <li>the element was put, but only if the Element was actually put.
645      * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
646      * if it was requested
647      * </ul>
648      * Synchronization is handled within the method.
649      * <p/>
650      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
651      * This exception should be caught in those cirucmstances.
652      *
653      * @param element                     An object. If Serializable it can fully participate in replication and the DiskStore.
654      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
655      *                                    further notification to doNotNotifyCacheReplicators cache peers
656      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
657      * @throws IllegalArgumentException if the element is null
658      */
659     public final void put(Element element, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
660             IllegalStateException,
661             CacheException {
662         checkStatus();
663 
664         if (disabled) {
665             return;
666         }
667 
668         if (element == null) {
669             if (doNotNotifyCacheReplicators) {
670                 if (LOG.isLoggable(Level.FINE)) {
671                 LOG.fine("Element from replicated put is null. This happens because the element is a SoftReference" +
672                         " and it has been collected.Increase heap memory on the JVM or set -Xms to be the same as " +
673                         "-Xmx to avoid this problem.");
674                 }
675             } else {
676                 throw new IllegalArgumentException("Element cannot be null");
677             }
678         }
679 
680         element.resetAccessStatistics();
681         boolean elementExists;
682         Object key = element.getObjectKey();
683         elementExists = isElementInMemory(key) || isElementOnDisk(key);
684         if (elementExists) {
685             element.updateUpdateStatistics();
686         }
687         applyDefaultsToElementWithoutLifespanSet(element);
688 
689         backOffIfDiskSpoolFull();
690 
691 
692         synchronized (this) {
693             memoryStore.put(element);
694         }
695 
696         if (elementExists) {
697             registeredEventListeners.notifyElementUpdated(element, doNotNotifyCacheReplicators);
698         } else {
699             registeredEventListeners.notifyElementPut(element, doNotNotifyCacheReplicators);
700         }
701 
702     }
703 
704     /***
705      * wait outside of synchronized block so as not to block readers
706      * If the disk store spool is full wait a short time to give it a chance to
707      * catch up.
708      */
709     private void backOffIfDiskSpoolFull() {
710 
711         if (diskStore != null && diskStore.backedUp()) {
712             //back off to avoid OutOfMemoryError
713             try {
714                 Thread.sleep(BACK_OFF_TIME_MILLIS);
715             } catch (InterruptedException e) {
716                 //do not care if this happens
717             }
718         }
719     }
720 
721     private void applyDefaultsToElementWithoutLifespanSet(Element element) {
722         if (!element.isLifespanSet()) {
723             //Setting with Cache defaults
724             element.setTimeToLive((int) configuration.getTimeToLiveSeconds());
725             element.setTimeToIdle((int) configuration.getTimeToIdleSeconds());
726             element.setEternal(configuration.isEternal());
727         }
728     }
729 
730 
731     /***
732      * Put an element in the cache, without updating statistics, or updating listeners. This is meant to be used
733      * in conjunction with {@link #getQuiet}.
734      * Synchronization is handled within the method.
735      * <p/>
736      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
737      * This exception should be caught in those cirucmstances.
738      * <p/>
739      *
740      * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
741      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
742      * @throws IllegalArgumentException if the element is null
743      */
744     public final void putQuiet(Element element) throws IllegalArgumentException, IllegalStateException,
745             CacheException {
746         checkStatus();
747 
748         if (disabled) {
749             return;
750         }
751 
752         if (element == null) {
753             throw new IllegalArgumentException("Element cannot be null");
754         }
755 
756         applyDefaultsToElementWithoutLifespanSet(element);
757 
758         synchronized (this) {
759             memoryStore.put(element);
760         }
761     }
762 
763     /***
764      * Gets an element from the cache. Updates Element Statistics
765      * <p/>
766      * Note that the Element's lastAccessTime is always the time of this get.
767      * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
768      * <p/>
769      * Synchronization is handled within the method.
770      *
771      * @param key a serializable value
772      * @return the element, or null, if it does not exist.
773      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
774      * @see #isExpired
775      */
776     public final Element get(Serializable key) throws IllegalStateException, CacheException {
777         return get((Object) key);
778     }
779 
780 
781     /***
782      * Gets an element from the cache. Updates Element Statistics
783      * <p/>
784      * Note that the Element's lastAccessTime is always the time of this get.
785      * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
786      * <p/>
787      * Synchronization is handled within the method.
788      *
789      * @param key an Object value
790      * @return the element, or null, if it does not exist.
791      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
792      * @see #isExpired
793      * @since 1.2
794      */
795     public final Element get(Object key) throws IllegalStateException, CacheException {
796         checkStatus();
797         Element element;
798         long start = System.currentTimeMillis();
799 
800         synchronized (this) {
801             element = searchInMemoryStore(key, true);
802             if (element == null && isDiskStore()) {
803                 element = searchInDiskStore(key, true);
804             }
805             if (element == null) {
806                 missCountNotFound++;
807                 if (LOG.isLoggable(Level.FINEST)) {
808                     LOG.finest(configuration.getName() + " cache - Miss");
809                 }
810             } else {
811                 hitCount++;
812             }
813         }
814         long end = System.currentTimeMillis();
815         totalGetTime += (end - start);
816         return element;
817     }
818 
819     /***
820      * Warning: This method is related to the JSR107 specification, which is in draft. It is subject to change without notice.
821      * <p/>
822      * This method will return, from the cache, the Element associated with the argument "key".
823      * <p/>
824      * 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,
825      * the one associated with the cache. If both are null, no load is performed and null is returned.
826      * <p/>
827      * 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.
828      * <p/>
829      * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
830      * are synchronized.
831      *
832      * @param key            key whose associated value is to be returned.
833      * @param loader         the override loader to use. If null, the cache's default loader will be used
834      * @param loaderArgument an argument to pass to the CacheLoader.
835      * @return an element if it existed or could be loaded, otherwise null
836      * @throws CacheException
837      */
838     public Element getWithLoader(Object key, CacheLoader loader, Object loaderArgument) throws CacheException {
839 
840         Element element = get(key);
841         if (element != null) {
842             return element;
843         }
844 
845         if (cacheLoader == null && loader == null) {
846             return null;
847         }
848 
849         try {
850             //check again in case the last thread loaded it
851             element = getQuiet(key);
852             if (element != null) {
853                 return element;
854             }
855             Future future = asynchronousLoad(key, loader, loaderArgument);
856             //wait for result
857             future.get();
858         } catch (Exception e) {
859             throw new CacheException("Exception on load for key " + key, e);
860         }
861         return getQuiet(key);
862     }
863 
864     /***
865      * Warning: This method is related to the JSR107 specification, which is in draft. It is subject to change without notice.
866      * <p/>
867      * The load method provides a means to "pre load" the cache. This method will, asynchronously, load the specified
868      * object into the cache using the associated cacheloader. If the object already exists in the cache, no action is
869      * taken. If no loader is associated with the object, no object will be loaded into the cache. If a problem is
870      * encountered during the retrieving or loading of the object, an exception should be logged. If the "arg" argument
871      * is set, the arg object will be passed to the CacheLoader.load method. The cache will not dereference the object.
872      * If no "arg" value is provided a null will be passed to the load method. The storing of null values in the cache
873      * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding
874      * the object in the cache. In both cases a null is returned.
875      * <p/>
876      * The Ehcache native API provides similar functionality to loaders using the
877      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
878      *
879      * @param key key whose associated value to be loaded using the associated cacheloader if this cache doesn't contain it.
880      * @throws CacheException
881      */
882     public void load(final Object key) throws CacheException {
883         if (cacheLoader == null) {
884             if (LOG.isLoggable(Level.FINE)) {
885                 LOG.fine("The CacheLoader is null. Returning.");
886             }
887             return;
888         }
889 
890         boolean existsOnCall = isKeyInCache(key);
891         if (existsOnCall) {
892             if (LOG.isLoggable(Level.FINE)) {
893                 LOG.fine("The key " + key + " exists in the cache. Returning.");
894             }
895             return;
896         }
897 
898         asynchronousLoad(key, null, null);
899     }
900 
901     /***
902      * Warning: This method is related to the JSR107 specification, which is in draft. It is subject to change without notice.
903      * <p/>
904      * The getAll method will return, from the cache, a Map of the objects associated with the Collection of keys in argument "keys".
905      * If the objects are not in the cache, the associated cache loader will be called. If no loader is associated with an object,
906      * a null is returned. If a problem is encountered during the retrieving or loading of the objects, an exception will be thrown.
907      * If the "arg" argument is set, the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference
908      * 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
909      * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding the object in
910      * the cache. In both cases a null is returned.
911      * <p/>
912      * <p/>
913      * Note. If the getAll exceeds the maximum cache size, the returned map will necessarily be less than the number specified.
914      * <p/>
915      * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
916      * are synchronized.
917      * <p/>
918      * The constructs package provides similar functionality using the
919      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
920      *
921      * @param keys           a collection of keys to be returned/loaded
922      * @param loaderArgument an argument to pass to the CacheLoader.
923      * @return a Map populated from the Cache. If there are no elements, an empty Map is returned.
924      * @throws CacheException
925      */
926     public Map getAllWithLoader(Collection keys, Object loaderArgument) throws CacheException {
927         if (keys == null) {
928             return new HashMap(0);
929         }
930         Map map = new HashMap(keys.size());
931 
932         List missingKeys = new ArrayList(keys.size());
933 
934         if (cacheLoader != null) {
935             Object key = null;
936             try {
937                 map = new HashMap(keys.size());
938 
939                 for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
940                     key = iterator.next();
941 
942                     if (isKeyInCache(key)) {
943                         Element element = get(key);
944                         if (element != null) {
945                             map.put(key, element.getObjectValue());
946                         } else {
947                             map.put(key, null);
948                         }
949                     } else {
950                         missingKeys.add(key);
951                     }
952                 }
953 
954                 //now load everything that's missing.
955                 Future future = asynchronousLoadAll(missingKeys, loaderArgument);
956                 future.get();
957 
958 
959                 for (int i = 0; i < missingKeys.size(); i++) {
960                     key = missingKeys.get(i);
961                     Element element = get(key);
962                     if (element != null) {
963                         map.put(key, element.getObjectValue());
964                     } else {
965                         map.put(key, null);
966                     }
967                 }
968 
969             } catch (InterruptedException e) {
970                 throw new CacheException(e.getMessage() + " for key " + key, e);
971             } catch (ExecutionException e) {
972                 throw new CacheException(e.getMessage() + " for key " + key, e);
973             }
974         } else {
975             for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
976                 Object key = iterator.next();
977                 Element element = get(key);
978                 if (element != null) {
979                     map.put(key, element.getObjectValue());
980                 } else {
981                     map.put(key, null);
982                 }
983             }
984         }
985         return map;
986     }
987 
988 
989     /***
990      * Warning: This method is related to the JSR107 specification, which is in draft. It is subject to change without notice.
991      * <p/>
992      * The loadAll method provides a means to "pre load" objects into the cache. This method will, asynchronously, load
993      * the specified objects into the cache using the associated cache loader. If the an object already exists in the
994      * cache, no action is taken. If no loader is associated with the object, no object will be loaded into the cache.
995      * If a problem is encountered during the retrieving or loading of the objects, an exception (to be defined)
996      * should be logged. The getAll method will return, from the cache, a Map of the objects associated with the
997      * Collection of keys in argument "keys". If the objects are not in the cache, the associated cache loader will be
998      * called. If no loader is associated with an object, a null is returned. If a problem is encountered during the
999      * retrieving or loading of the objects, an exception (to be defined) will be thrown. If the "arg" argument is set,
1000      * the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference the object.
1001      * If no "arg" value is provided a null will be passed to the loadAll method.
1002      * <p/>
1003      * keys - collection of the keys whose associated values to be loaded into this cache by using the associated
1004      * cacheloader if this cache doesn't contain them.
1005      * <p/>
1006      * The Ehcache native API provides similar functionality to loaders using the
1007      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1008      */
1009     public void loadAll(final Collection keys, final Object argument) throws CacheException {
1010 
1011         if (cacheLoader == null) {
1012             if (LOG.isLoggable(Level.FINE)) {
1013                 LOG.fine("The CacheLoader is null. Returning.");
1014             }
1015             return;
1016         }
1017         if (keys == null) {
1018             return;
1019         }
1020         asynchronousLoadAll(keys, argument);
1021     }
1022 
1023     /***
1024      * Gets an element from the cache, without updating Element statistics. Cache statistics are
1025      * still updated.
1026      * <p/>
1027      * Synchronization is handled within the method.
1028      *
1029      * @param key a serializable value
1030      * @return the element, or null, if it does not exist.
1031      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1032      * @see #isExpired
1033      */
1034     public final Element getQuiet(Serializable key) throws IllegalStateException, CacheException {
1035         return getQuiet((Object) key);
1036     }
1037 
1038     /***
1039      * Gets an element from the cache, without updating Element statistics. Cache statistics are
1040      * not updated.
1041      * <p/>
1042      * Synchronization is handled within the method.
1043      *
1044      * @param key a serializable value
1045      * @return the element, or null, if it does not exist.
1046      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1047      * @see #isExpired
1048      * @since 1.2
1049      */
1050     public final Element getQuiet(Object key) throws IllegalStateException, CacheException {
1051         checkStatus();
1052         Element element;
1053 
1054         synchronized (this) {
1055             element = searchInMemoryStore(key, false);
1056             if (element == null && isDiskStore()) {
1057                 element = searchInDiskStore(key, false);
1058             }
1059         }
1060         return element;
1061     }
1062 
1063     /***
1064      * Returns a list of all element keys in the cache, whether or not they are expired.
1065      * <p/>
1066      * The returned keys are unique and can be considered a set.
1067      * <p/>
1068      * The List returned is not live. It is a copy.
1069      * <p/>
1070      * The time taken is O(n). On a single cpu 1.8Ghz P4, approximately 8ms is required
1071      * for each 1000 entries.
1072      *
1073      * @return a list of {@link Object} keys
1074      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1075      */
1076     public final synchronized List getKeys() throws IllegalStateException, CacheException {
1077         checkStatus();
1078         /* An element with the same key can exist in both the memory store and the
1079             disk store at the same time. Because the memory store is always searched first
1080             these duplicates do not cause problems when getting elements/
1081 
1082             This method removes these duplicates before returning the list of keys*/
1083         List allKeyList = new ArrayList();
1084         List keyList = Arrays.asList(memoryStore.getKeyArray());
1085         allKeyList.addAll(keyList);
1086         if (isDiskStore()) {
1087             Set allKeys = new HashSet();
1088             //within the store keys will be unique
1089             allKeys.addAll(keyList);
1090             Object[] diskKeys = diskStore.getKeyArray();
1091             for (int i = 0; i < diskKeys.length; i++) {
1092                 Object diskKey = diskKeys[i];
1093                 if (allKeys.add(diskKey)) {
1094                     //Unique, so add it to the list
1095                     allKeyList.add(diskKey);
1096                 }
1097             }
1098         }
1099         return allKeyList;
1100     }
1101 
1102     /***
1103      * Returns a list of all element keys in the cache. Only keys of non-expired
1104      * elements are returned.
1105      * <p/>
1106      * The returned keys are unique and can be considered a set.
1107      * <p/>
1108      * The List returned is not live. It is a copy.
1109      * <p/>
1110      * The time taken is O(n), where n is the number of elements in the cache. On
1111      * a 1.8Ghz P4, the time taken is approximately 200ms per 1000 entries. This method
1112      * is not syncrhonized, because it relies on a non-live list returned from {@link #getKeys()}
1113      * , which is synchronised, and which takes 8ms per 1000 entries. This way
1114      * cache liveness is preserved, even if this method is very slow to return.
1115      * <p/>
1116      * Consider whether your usage requires checking for expired keys. Because
1117      * this method takes so long, depending on cache settings, the list could be
1118      * quite out of date by the time you get it.
1119      *
1120      * @return a list of {@link Object} keys
1121      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1122      */
1123     public final List getKeysWithExpiryCheck() throws IllegalStateException, CacheException {
1124         List allKeyList = getKeys();
1125         //remove keys of expired elements
1126         ArrayList nonExpiredKeys = new ArrayList(allKeyList.size());
1127         int allKeyListSize = allKeyList.size();
1128         for (int i = 0; i < allKeyListSize; i++) {
1129             Object key = allKeyList.get(i);
1130             Element element = getQuiet(key);
1131             if (element != null) {
1132                 nonExpiredKeys.add(key);
1133             }
1134         }
1135         nonExpiredKeys.trimToSize();
1136         return nonExpiredKeys;
1137     }
1138 
1139 
1140     /***
1141      * Returns a list of all elements in the cache, whether or not they are expired.
1142      * <p/>
1143      * The returned keys are not unique and may contain duplicates. If the cache is only
1144      * using the memory store, the list will be unique. If the disk store is being used
1145      * as well, it will likely contain duplicates, because of the internal store design.
1146      * <p/>
1147      * The List returned is not live. It is a copy.
1148      * <p/>
1149      * The time taken is O(log n). On a single cpu 1.8Ghz P4, approximately 6ms is required
1150      * for 1000 entries and 36 for 50000.
1151      * <p/>
1152      * This is the fastest getKeys method
1153      *
1154      * @return a list of {@link Object} keys
1155      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1156      */
1157     public final synchronized List getKeysNoDuplicateCheck() throws IllegalStateException {
1158         checkStatus();
1159         ArrayList allKeys = new ArrayList();
1160         List memoryKeySet = Arrays.asList(memoryStore.getKeyArray());
1161         allKeys.addAll(memoryKeySet);
1162         if (isDiskStore()) {
1163             List diskKeySet = Arrays.asList(diskStore.getKeyArray());
1164             allKeys.addAll(diskKeySet);
1165         }
1166         return allKeys;
1167     }
1168 
1169     private Element searchInMemoryStore(Object key, boolean updateStatistics) {
1170         Element element;
1171         if (updateStatistics) {
1172             element = memoryStore.get(key);
1173         } else {
1174             element = memoryStore.getQuiet(key);
1175         }
1176         if (element != null) {
1177             if (isExpired(element)) {
1178                 if (LOG.isLoggable(Level.FINE)) {
1179                     LOG.fine(configuration.getName() + " Memory cache hit, but element expired");
1180                 }
1181                 if (updateStatistics) {
1182                     missCountExpired++;
1183                 }
1184                 remove(key, true, true, false);
1185                 element = null;
1186             } else {
1187                 if (updateStatistics) {
1188                     memoryStoreHitCount++;
1189                 }
1190             }
1191         }
1192         return element;
1193     }
1194 
1195     private Element searchInDiskStore(Object key, boolean updateStatistics) {
1196         if (!(key instanceof Serializable)) {
1197             return null;
1198         }
1199         Serializable serializableKey = (Serializable) key;
1200         Element element;
1201         if (updateStatistics) {
1202             element = diskStore.get(serializableKey);
1203         } else {
1204             element = diskStore.getQuiet(serializableKey);
1205         }
1206         if (element != null) {
1207             if (isExpired(element)) {
1208                 if (LOG.isLoggable(Level.FINE)) {
1209                     LOG.fine(configuration.getName() + " cache - Disk Store hit, but element expired");
1210                 }
1211                 missCountExpired++;
1212                 remove(key, true, true, false);
1213                 element = null;
1214             } else {
1215                 diskStoreHitCount++;
1216                 //Put the item back into memory to preserve policies in the memory store and to save updated statistics
1217                 memoryStore.put(element);
1218             }
1219         }
1220         return element;
1221     }
1222 
1223     /***
1224      * Removes an {@link Element} from the Cache. This also removes it from any
1225      * stores it may be in.
1226      * <p/>
1227      * Also notifies the CacheEventListener after the element was removed.
1228      * <p/>
1229      * Synchronization is handled within the method.
1230      * <p/>
1231      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1232      * This exception should be caught in those cirucmstances.
1233      *
1234      * @param key the element key to operate on
1235      * @return true if the element was removed, false if it was not found in the cache
1236      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1237      */
1238     public final boolean remove(Serializable key) throws IllegalStateException {
1239         return remove((Object) key);
1240     }
1241 
1242     /***
1243      * Removes an {@link Element} from the Cache. This also removes it from any
1244      * stores it may be in.
1245      * <p/>
1246      * Also notifies the CacheEventListener after the element was removed, but only if an Element
1247      * with the key actually existed.
1248      * <p/>
1249      * Synchronization is handled within the method.
1250      * <p/>
1251      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1252      * This exception should be caught in those cirucmstances.
1253      * <p/>
1254      *
1255      * @param key the element key to operate on
1256      * @return true if the element was removed, false if it was not found in the cache
1257      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1258      * @since 1.2
1259      */
1260     public final boolean remove(Object key) throws IllegalStateException {
1261         return remove(key, false);
1262     }
1263 
1264 
1265     /***
1266      * Removes an {@link Element} from the Cache. This also removes it from any
1267      * stores it may be in.
1268      * <p/>
1269      * Also notifies the CacheEventListener after the element was removed, but only if an Element
1270      * with the key actually existed.
1271      * <p/>
1272      * Synchronization is handled within the method.
1273      * <p/>
1274      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1275      * This exception should be caught in those cirucmstances.
1276      *
1277      * @param key                         the element key to operate on
1278      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1279      *                                    further notification to doNotNotifyCacheReplicators cache peers
1280      * @return true if the element was removed, false if it was not found in the cache
1281      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1282      */
1283     public final boolean remove(Serializable key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
1284         return remove((Object) key, doNotNotifyCacheReplicators);
1285     }
1286 
1287     /***
1288      * Removes an {@link Element} from the Cache. This also removes it from any
1289      * stores it may be in.
1290      * <p/>
1291      * Also notifies the CacheEventListener after the element was removed, but only if an Element
1292      * with the key actually existed.
1293      * <p/>
1294      * Synchronization is handled within the method.
1295      *
1296      * @param key                         the element key to operate on
1297      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1298      *                                    further notification to doNotNotifyCacheReplicators cache peers
1299      * @return true if the element was removed, false if it was not found in the cache
1300      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1301      */
1302     public final boolean remove(Object key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
1303         return remove(key, false, true, doNotNotifyCacheReplicators);
1304     }
1305 
1306     /***
1307      * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
1308      * stores it may be in.
1309      * <p/>
1310      * Synchronization is handled within the method.
1311      *
1312      * @param key the element key to operate on
1313      * @return true if the element was removed, false if it was not found in the cache
1314      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1315      */
1316     public final boolean removeQuiet(Serializable key) throws IllegalStateException {
1317         return remove(key, false, false, false);
1318     }
1319 
1320     /***
1321      * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
1322      * stores it may be in.
1323      * <p/>
1324      * Synchronization is handled within the method.
1325      * <p/>
1326      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1327      * This exception should be caught in those cirucmstances.
1328      *
1329      * @param key the element key to operate on
1330      * @return true if the element was removed, false if it was not found in the cache
1331      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1332      * @since 1.2
1333      */
1334     public final boolean removeQuiet(Object key) throws IllegalStateException {
1335         return remove(key, false, false, false);
1336     }
1337 
1338 
1339     /***
1340      * Removes or expires an {@link Element} from the Cache after an attempt to get it determined that it should be expired.
1341      * This also removes it from any stores it may be in.
1342      * <p/>
1343      * Also notifies the CacheEventListener after the element has expired, but only if an Element
1344      * with the key actually existed.
1345      * <p/>
1346      * Synchronization is handled within the method.
1347      * <p/>
1348      * If a remove was called, listeners are notified, regardless of whether the element existed or not.
1349      * This allows distributed cache listeners to remove elements from a cluster regardless of whether they
1350      * existed locally.
1351      * <p/>
1352      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1353      * This exception should be caught in those cirucmstances.
1354      *
1355      * @param key                         the element key to operate on
1356      * @param expiry                      if the reason this method is being called is to expire the element
1357      * @param notifyListeners             whether to notify listeners
1358      * @param doNotNotifyCacheReplicators whether not to notify cache replicators
1359      * @return true if the element was removed, false if it was not found in the cache
1360      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1361      */
1362     private boolean remove(Object key, boolean expiry, boolean notifyListeners,
1363                            boolean doNotNotifyCacheReplicators)
1364             throws IllegalStateException {
1365         checkStatus();
1366         boolean removed = false;
1367         Element elementFromMemoryStore;
1368         Element elementFromDiskStore;
1369         synchronized (this) {
1370             elementFromMemoryStore = memoryStore.remove(key);
1371 
1372             //could have been removed from both places, if there are two copies in the cache
1373             elementFromDiskStore = null;
1374             if (isDiskStore()) {
1375                 if ((key instanceof Serializable)) {
1376                     Serializable serializableKey = (Serializable) key;
1377                     elementFromDiskStore = diskStore.remove(serializableKey);
1378                 }
1379 
1380             }
1381         }
1382 
1383         boolean removeNotified = false;
1384 
1385         if (elementFromMemoryStore != null) {
1386             if (notifyListeners) {
1387                 if (expiry) {
1388                     registeredEventListeners.notifyElementExpiry(elementFromMemoryStore, doNotNotifyCacheReplicators);
1389                 } else {
1390                     removeNotified = true;
1391                     registeredEventListeners.notifyElementRemoved(elementFromMemoryStore, doNotNotifyCacheReplicators);
1392                 }
1393             }
1394             removed = true;
1395         }
1396         if (elementFromDiskStore != null) {
1397             if (expiry) {
1398                 registeredEventListeners.notifyElementExpiry(elementFromDiskStore, doNotNotifyCacheReplicators);
1399             } else {
1400                 removeNotified = true;
1401                 registeredEventListeners.notifyElementRemoved(elementFromDiskStore, doNotNotifyCacheReplicators);
1402             }
1403             removed = true;
1404         }
1405         //If we are trying to remove an element which does not exist locally, we should still notify so that
1406         //cluster invalidations work.
1407         if (!expiry && !removeNotified) {
1408             Element syntheticElement = new Element(key, null);
<