View Javadoc

1   /**
2    *  Copyright 2003-2006 Greg Luck
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.event.RegisteredEventListeners;
20  import net.sf.ehcache.store.DiskStore;
21  import net.sf.ehcache.store.MemoryStore;
22  import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  
26  import java.io.IOException;
27  import java.io.Serializable;
28  import java.net.InetAddress;
29  import java.net.UnknownHostException;
30  import java.rmi.server.UID;
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.HashSet;
34  import java.util.List;
35  import java.util.Set;
36  
37  /**
38   * Cache is the central class in ehcache. Caches have {@link Element}s and are managed
39   * by the {@link CacheManager}. The Cache performs logical actions. It delegates physical
40   * implementations to its {@link net.sf.ehcache.store.Store}s.
41   * <p/>
42   * A reference to a Cache can be obtained through the {@link CacheManager}. A Cache thus obtained
43   * is guaranteed to have status {@link Status#STATUS_ALIVE}. This status is checked for any method which
44   * throws {@link IllegalStateException} and the same thrown if it is not alive. This would normally
45   * happen if a call is made after {@link CacheManager#shutdown} is invoked.
46   * <p/>
47   * Cache is threadsafe.
48   * <p/>
49   * Statistics on cache usage are collected and made available through public methods.
50   *
51   * @author Greg Luck
52   * @version $Id: Cache.java 52 2006-04-24 14:50:03Z gregluck $
53   */
54  public final class Cache implements Cloneable {
55  
56      /**
57       * A reserved word for cache names. It denotes a default configuration
58       * which is applied to caches created without configuration.
59       */
60      public static final String DEFAULT_CACHE_NAME = "default";
61  
62      /**
63       * System Property based method of disabling ehcache. If disabled no elements will be added to a cache.
64       * <p/>
65       * Set the property "net.sf.ehcache.disabled=true" to disable ehcache.
66       * <p/>
67       * This can easily be done using <code>java -Dnet.sf.ehcache.disabled=true</code> in the command line.
68       */
69      public static final String NET_SF_EHCACHE_DISABLED = "net.sf.ehcache.disabled";
70  
71      {
72          String value = System.getProperty(NET_SF_EHCACHE_DISABLED);
73          if (value != null) {
74              disabled = value.equalsIgnoreCase("true");
75          }
76      }
77  
78      /**
79       * The default interval between runs of the expiry thread.
80       */
81      public static final long DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS = 120;
82  
83      private static final Log LOG = LogFactory.getLog(Cache.class.getName());
84  
85      private static final int MS_PER_SECOND = 1000;
86  
87      private static final MemoryStoreEvictionPolicy DEFAULT_MEMORY_STORE_EVICTION_POLICY = MemoryStoreEvictionPolicy.LRU;
88  
89      private boolean disabled;
90  
91  
92      private String name;
93  
94      private DiskStore diskStore;
95  
96      private final String diskStorePath;
97  
98      private Status status;
99  
100     private final int maxElementsInMemory;
101 
102     private MemoryStoreEvictionPolicy memoryStoreEvictionPolicy;
103 
104     /**
105      * Whether cache elements in this cache overflowToDisk.
106      */
107     private final boolean overflowToDisk;
108 
109     /**
110      * The interval in seconds between runs of the disk expiry thread. 2 minutes is the default.
111      * This is not the same thing as time to live or time to idle. When the thread runs it checks
112      * these things. So this value is how often we check for expiry.
113      */
114     private final long diskExpiryThreadIntervalSeconds;
115 
116     /**
117      * For caches that overflow to disk, does the disk cache persist between CacheManager instances.
118      */
119     private final boolean diskPersistent;
120 
121     /**
122      * The shutdown hook thread for {@link #diskPersistent} caches. This thread
123      * must be unregistered as a shutdown hook, when the cache is disposed.
124      * Otherwise the cache is not GC-able.
125      */
126     private Thread shutdownHook;
127 
128     /**
129      * Whether elements are eternal, which is the same as non-expiring.
130      */
131     private final boolean eternal;
132 
133     /**
134      * The maximum time between creation time and when an element expires.
135      * Is only used if the element is not eternal.
136      */
137     private final long timeToLiveSeconds;
138 
139     /**
140      * The maximum amount of time between {@link #get(Object)}s before an element expires.
141      */
142     private final long timeToIdleSeconds;
143 
144 
145     /**
146      * Cache hit count.
147      */
148     private int hitCount;
149 
150     /**
151      * Memory cache hit count.
152      */
153     private int memoryStoreHitCount;
154 
155     /**
156      * Auxiliary hit counts broken down by auxiliary.
157      */
158     private int diskStoreHitCount;
159 
160     /**
161      * Count of misses where element was not found.
162      */
163     private int missCountNotFound;
164 
165     /**
166      * Count of misses where element was expired.
167      */
168     private int missCountExpired;
169 
170     /**
171      * The {@link MemoryStore} of this {@link Cache}. All caches have a memory store.
172      */
173     private MemoryStore memoryStore;
174 
175     private RegisteredEventListeners registeredEventListeners;
176 
177     private String guid;
178 
179     {
180         try {
181             guid = new StringBuffer()
182                     .append(InetAddress.getLocalHost())
183                     .append("-")
184                     .append(new UID())
185                     .toString();
186         } catch (UnknownHostException e) {
187             LOG.error("Could not create GUID: " + e.getMessage());
188         }
189     }
190 
191     private CacheManager cacheManager;
192 
193     /**
194      * 1.0 Constructor.
195      * <p/>
196      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
197      * <p/>
198      * A client can specify their own settings here and pass the {@link Cache} object
199      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
200      * <p/>
201      * Only the CacheManager can initialise them.
202      * <p/>
203      * This constructor creates disk stores, if specified, that do not persist between restarts.
204      * <p/>
205      * The default expiry thread interval of 120 seconds is used. This is the interval between runs
206      * of the expiry thread, where it checks the disk store for expired elements. It is not the
207      * the timeToLiveSeconds.
208      *
209      * @param name                Cache name
210      * @param maxElementsInMemory Max elements in memory
211      * @param overflowToDisk      Overflow to disk (boolean)
212      * @param eternal             Whether the elements expire
213      * @param timeToLiveSeconds
214      * @param timeToIdleSeconds
215      * @since 1.0
216      */
217     public Cache(String name, int maxElementsInMemory, boolean overflowToDisk,
218                  boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds) {
219         this(name, maxElementsInMemory, DEFAULT_MEMORY_STORE_EVICTION_POLICY, overflowToDisk,
220                 null, eternal, timeToLiveSeconds, timeToIdleSeconds, false, DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS, null);
221     }
222 
223 
224     /**
225      * 1.1 Constructor.
226      * <p/>
227      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
228      * <p/>
229      * A client can specify their own settings here and pass the {@link Cache} object
230      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
231      * <p/>
232      * Only the CacheManager can initialise them.
233      *
234      * @param name
235      * @param maxElementsInMemory
236      * @param overflowToDisk
237      * @param eternal
238      * @param timeToLiveSeconds
239      * @param timeToIdleSeconds
240      * @param diskPersistent
241      * @param diskExpiryThreadIntervalSeconds
242      *
243      * @since 1.1
244      */
245     public Cache(String name,
246                  int maxElementsInMemory,
247                  boolean overflowToDisk,
248                  boolean eternal,
249                  long timeToLiveSeconds,
250                  long timeToIdleSeconds,
251                  boolean diskPersistent,
252                  long diskExpiryThreadIntervalSeconds) {
253         this(name, maxElementsInMemory, DEFAULT_MEMORY_STORE_EVICTION_POLICY, overflowToDisk, null,
254                 eternal, timeToLiveSeconds, timeToIdleSeconds, diskPersistent, diskExpiryThreadIntervalSeconds, null);
255     }
256 
257     /**
258      * 1.2 Constructor
259      * <p/>
260      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
261      * <p/>
262      * A client can specify their own settings here and pass the {@link Cache} object
263      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
264      * <p/>
265      * Only the CacheManager can initialise them.
266      *
267      * @param name
268      * @param maxElementsInMemory
269      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
270      * @param overflowToDisk
271      * @param diskStorePath
272      * @param eternal
273      * @param timeToLiveSeconds
274      * @param timeToIdleSeconds
275      * @param diskPersistent
276      * @param diskExpiryThreadIntervalSeconds
277      *
278      * @param registeredEventListeners  a notification service. Optionally null, in which case a new
279      *                                  one with no registered listeners will be created.
280      * @since 1.2
281      */
282     public Cache(String name,
283                  int maxElementsInMemory,
284                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
285                  boolean overflowToDisk,
286                  String diskStorePath,
287                  boolean eternal,
288                  long timeToLiveSeconds,
289                  long timeToIdleSeconds,
290                  boolean diskPersistent,
291                  long diskExpiryThreadIntervalSeconds,
292                  RegisteredEventListeners registeredEventListeners) {
293         this.name = name;
294         this.maxElementsInMemory = maxElementsInMemory;
295         this.memoryStoreEvictionPolicy = memoryStoreEvictionPolicy;
296         this.overflowToDisk = overflowToDisk;
297         this.eternal = eternal;
298         this.timeToLiveSeconds = timeToLiveSeconds;
299         this.timeToIdleSeconds = timeToIdleSeconds;
300         this.diskPersistent = diskPersistent;
301         if (diskStorePath == null) {
302             this.diskStorePath = System.getProperty("java.io.tmpdir");
303         } else {
304             this.diskStorePath = diskStorePath;
305         }
306 
307         if (registeredEventListeners == null) {
308             this.registeredEventListeners = new RegisteredEventListeners(this);
309         } else {
310             this.registeredEventListeners = registeredEventListeners;
311         }
312 
313         //Set this to a safe value.
314         if (diskExpiryThreadIntervalSeconds == 0) {
315             this.diskExpiryThreadIntervalSeconds = DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS;
316         } else {
317             this.diskExpiryThreadIntervalSeconds = diskExpiryThreadIntervalSeconds;
318         }
319 
320         // For backward compatibility with 1.1 and earlier
321         if (memoryStoreEvictionPolicy == null) {
322             this.memoryStoreEvictionPolicy = DEFAULT_MEMORY_STORE_EVICTION_POLICY;
323         }
324 
325         changeStatus(Status.STATUS_UNINITIALISED);
326     }
327 
328 
329     /**
330      * Newly created caches do not have a {@link net.sf.ehcache.store.MemoryStore} or a {@link net.sf.ehcache.store.DiskStore}.
331      * <p/>
332      * This method creates those and makes the cache ready to accept elements
333      */
334     final synchronized void initialise() {
335         if (!status.equals(Status.STATUS_UNINITIALISED)) {
336             throw new IllegalStateException("Cannot initialise the " + name
337                     + " cache because its status is not STATUS_UNINITIALISED");
338         }
339 
340         if (maxElementsInMemory == 0) {
341             if (LOG.isWarnEnabled()) {
342                 LOG.warn("Cache: " + name + " has a maxElementsInMemory of 0. It is strongly recommended to " +
343                         "have a maximumSize of at least 1. Performance is halved by not using a MemoryStore.");
344             }
345         }
346 
347         if (overflowToDisk) {
348             diskStore = new DiskStore(this, diskStorePath);
349         }
350 
351         memoryStore = MemoryStore.create(this, diskStore);
352 
353 
354         if (diskPersistent) {
355             addShutdownHook();
356         }
357 
358         changeStatus(Status.STATUS_ALIVE);
359 
360         if (LOG.isDebugEnabled()) {
361             LOG.debug("Initialised cache: " + name);
362         }
363 
364         if (disabled) {
365             if (LOG.isWarnEnabled()) {
366                 LOG.warn("Cache: " + name + " is disabled because the " + NET_SF_EHCACHE_DISABLED
367                         + " property was set to true. No elements will be added to the cache.");
368             }
369         }
370 
371 
372     }
373 
374     private void changeStatus(Status status) {
375         this.status = status;
376     }
377 
378 
379     /**
380      * Some caches might be persistent, so we want to add a shutdown hook if that is the
381      * case, so that the data and index can be written to disk.
382      */
383     private void addShutdownHook() {
384         Thread localShutdownHook = new Thread() {
385             public void run() {
386                 synchronized (this) {
387                     if (status.equals(Status.STATUS_ALIVE)) {
388                         // clear shutdown hook reference to prevent
389                         // removeShutdownHook to remove it during shutdown
390                         Cache.this.shutdownHook = null;
391 
392                         LOG.debug("VM shutting down with the disk store for " + name
393                                 + " still active. The disk store is persistent. Calling dispose...");
394                         dispose();
395                     }
396                 }
397             }
398         };
399 
400         Runtime.getRuntime().addShutdownHook(localShutdownHook);
401         shutdownHook = localShutdownHook;
402     }
403 
404 
405     /**
406      * Remove the shutdown hook to prevent leaving orphaned caches around. This
407      * is called by {@link #dispose()} AFTER the status has been set to shutdown.
408      */
409     private void removeShutdownHook() {
410         if (shutdownHook != null) {
411             // remove shutdown hook
412             Runtime.getRuntime().removeShutdownHook(shutdownHook);
413 
414             // run the shutdown thread to remove it from its thread group
415             shutdownHook.start();
416 
417             shutdownHook = null;
418         }
419     }
420 
421 
422     /**
423      * Put an element in the cache.
424      * <p/>
425      * Resets the access statistics on the element, which would be the case if it has previously been
426      * gotten from a cache, and is now being put back.
427      * <p/>
428      * Also notifies the CacheEventListener that:
429      * <ul>
430      * <li>the element was put, but only if the Element was actually put.
431      * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
432      * if it was requested
433      * </ul>
434      *
435      * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
436      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
437      * @throws IllegalArgumentException if the element is null
438      */
439     public final synchronized void put(Element element) throws IllegalArgumentException, IllegalStateException,
440             CacheException {
441         put(element, false);
442     }
443 
444 
445     /**
446      * Put an element in the cache.
447      * <p/>
448      * Resets the access statistics on the element, which would be the case if it has previously been
449      * gotten from a cache, and is now being put back.
450      * <p/>
451      * Also notifies the CacheEventListener that:
452      * <ul>
453      * <li>the element was put, but only if the Element was actually put.
454      * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
455      * if it was requested
456      * </ul>
457      *
458      * @param element                     An object. If Serializable it can fully participate in replication and the DiskStore.
459      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
460      *                                    further notification to doNotNotifyCacheReplicators cache peers
461      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
462      * @throws IllegalArgumentException if the element is null
463      */
464     public final synchronized void put(Element element, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
465             IllegalStateException,
466             CacheException {
467         checkStatus();
468 
469         if (disabled) {
470             return;
471         }
472 
473         if (element == null) {
474             throw new IllegalArgumentException("Element cannot be null");
475         }
476         element.resetAccessStatistics();
477 
478         boolean elementExists = false;
479         if (registeredEventListeners != null) {
480             Object key = element.getObjectKey();
481             elementExists = isElementInMemory(key) || isElementOnDisk(key);
482         }
483 
484         memoryStore.put(element);
485 
486         if (elementExists) {
487             registeredEventListeners.notifyElementUpdated(element, doNotNotifyCacheReplicators);
488         } else {
489             registeredEventListeners.notifyElementPut(element, doNotNotifyCacheReplicators);
490         }
491 
492     }
493 
494 
495     /**
496      * Put an element in the cache, without updating statistics, or updating listeners. This is meant to be used
497      * in conjunction with {@link #getQuiet}
498      *
499      * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
500      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
501      * @throws IllegalArgumentException if the element is null
502      */
503     public final synchronized void putQuiet(Element element) throws IllegalArgumentException, IllegalStateException,
504             CacheException {
505         checkStatus();
506 
507         if (disabled) {
508             return;
509         }
510 
511         if (element == null) {
512             throw new IllegalArgumentException("Element cannot be null");
513         }
514         memoryStore.put(element);
515     }
516 
517     /**
518      * Gets an element from the cache. Updates Element Statistics
519      * <p/>
520      * Note that the Element's lastAccessTime is always the time of this get.
521      * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
522      *
523      * @param key a serializable value
524      * @return the element, or null, if it does not exist.
525      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
526      * @see #isExpired
527      */
528     public final synchronized Element get(Serializable key) throws IllegalStateException, CacheException {
529         return get((Object) key);
530     }
531 
532 
533     /**
534      * Gets an element from the cache. Updates Element Statistics
535      * <p/>
536      * Note that the Element's lastAccessTime is always the time of this get.
537      * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
538      *
539      * @param key an Object value
540      * @return the element, or null, if it does not exist.
541      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
542      * @see #isExpired
543      * @since 1.2
544      */
545     public final synchronized Element get(Object key) throws IllegalStateException, CacheException {
546         checkStatus();
547         Element element;
548 
549         element = searchInMemoryStore(key, true);
550         if (element == null && overflowToDisk) {
551             element = searchInDiskStore(key, true);
552         }
553 
554         if (element == null) {
555             missCountNotFound++;
556             if (LOG.isTraceEnabled()) {
557                 LOG.trace(name + " cache - Miss");
558             }
559             return null;
560         } else {
561             hitCount++;
562             return element;
563         }
564     }
565 
566     /**
567      * Gets an element from the cache, without updating Element statistics. Cache statistics are
568      * still updated.
569      * <p/>
570      *
571      * @param key a serializable value
572      * @return the element, or null, if it does not exist.
573      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
574      * @see #isExpired
575      */
576     public final synchronized Element getQuiet(Serializable key) throws IllegalStateException, CacheException {
577         return getQuiet((Object) key);
578     }
579 
580     /**
581      * Gets an element from the cache, without updating Element statistics. Cache statistics are
582      * still updated.
583      * <p/>
584      *
585      * @param key a serializable value
586      * @return the element, or null, if it does not exist.
587      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
588      * @see #isExpired
589      * @since 1.2
590      */
591     public final synchronized Element getQuiet(Object key) throws IllegalStateException, CacheException {
592         checkStatus();
593         Element element;
594 
595         element = searchInMemoryStore(key, false);
596         if (element == null && overflowToDisk) {
597             element = searchInDiskStore(key, false);
598         }
599 
600         if (element == null) {
601             missCountNotFound++;
602             if (LOG.isTraceEnabled()) {
603                 LOG.trace(name + " cache - Miss");
604             }
605             return null;
606         } else {
607             hitCount++;
608             return element;
609         }
610     }
611 
612     /**
613      * Returns a list of all elements in the cache, whether or not they are expired.
614      * <p/>
615      * The returned keys are unique and can be considered a set.
616      * <p/>
617      * The List returned is not live. It is a copy.
618      * <p/>
619      * The time taken is O(n). On a single cpu 1.8Ghz P4, approximately 8ms is required
620      * for each 1000 entries.
621      *
622      * @return a list of {@link Object} keys
623      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
624      */
625     public final synchronized List getKeys() throws IllegalStateException, CacheException {
626         checkStatus();
627         /* An element with the same key can exist in both the memory store and the
628             disk store at the same time. Because the memory store is always searched first
629             these duplicates do not cause problems when getting elements/
630 
631             This method removes these duplicates before returning the list of keys*/
632         List allKeyList = new ArrayList();
633         List keyList = Arrays.asList(memoryStore.getKeyArray());
634         allKeyList.addAll(keyList);
635         if (overflowToDisk) {
636             Set allKeys = new HashSet();
637             //within the store keys will be unique
638             allKeys.addAll(keyList);
639             Object[] diskKeys = diskStore.getKeyArray();
640             for (int i = 0; i < diskKeys.length; i++) {
641                 Object diskKey = diskKeys[i];
642                 if (allKeys.add(diskKey)) {
643                     //Unique, so add it to the list
644                     allKeyList.add(diskKey);
645                 }
646             }
647         }
648         return allKeyList;
649     }
650 
651     /**
652      * Returns a list of all elements in the cache. Only keys of non-expired
653      * elements are returned.
654      * <p/>
655      * The returned keys are unique and can be considered a set.
656      * <p/>
657      * The List returned is not live. It is a copy.
658      * <p/>
659      * The time taken is O(n), where n is the number of elements in the cache. On
660      * a 1.8Ghz P4, the time taken is approximately 200ms per 1000 entries. This method
661      * is not syncrhonized, because it relies on a non-live list returned from {@link #getKeys()}
662      * , which is synchronised, and which takes 8ms per 1000 entries. This way
663      * cache liveness is preserved, even if this method is very slow to return.
664      * <p/>
665      * Consider whether your usage requires checking for expired keys. Because
666      * this method takes so long, depending on cache settings, the list could be
667      * quite out of date by the time you get it.
668      *
669      * @return a list of {@link Object} keys
670      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
671      */
672     public final List getKeysWithExpiryCheck() throws IllegalStateException, CacheException {
673         List allKeyList = getKeys();
674         //remove keys of expired elements
675         ArrayList nonExpiredKeys = new ArrayList(allKeyList.size());
676         int allKeyListSize = allKeyList.size();
677         for (int i = 0; i < allKeyListSize; i++) {
678             Object key = allKeyList.get(i);
679             Element element = getQuiet(key);
680             if (element != null) {
681                 nonExpiredKeys.add(key);
682             }
683         }
684         nonExpiredKeys.trimToSize();
685         return nonExpiredKeys;
686     }
687 
688 
689     /**
690      * Returns a list of all elements in the cache, whether or not they are expired.
691      * <p/>
692      * The returned keys are not unique and may contain duplicates. If the cache is only
693      * using the memory store, the list will be unique. If the disk store is being used
694      * as well, it will likely contain duplicates, because of the internal store design.
695      * <p/>
696      * The List returned is not live. It is a copy.
697      * <p/>
698      * The time taken is O(log n). On a single cpu 1.8Ghz P4, approximately 6ms is required
699      * for 1000 entries and 36 for 50000.
700      * <p/>
701      * This is the fastest getKeys method
702      *
703      * @return a list of {@link Object} keys
704      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
705      */
706     public final synchronized List getKeysNoDuplicateCheck() throws IllegalStateException {
707         checkStatus();
708         ArrayList allKeys = new ArrayList();
709         List memoryKeySet = Arrays.asList(memoryStore.getKeyArray());
710         allKeys.addAll(memoryKeySet);
711         if (overflowToDisk) {
712             List diskKeySet = Arrays.asList(diskStore.getKeyArray());
713             allKeys.addAll(diskKeySet);
714         }
715         return allKeys;
716     }
717 
718     private Element searchInMemoryStore(Object key, boolean updateStatistics) {
719         Element element;
720         if (updateStatistics) {
721             element = memoryStore.get(key);
722         } else {
723             element = memoryStore.getQuiet(key);
724         }
725         if (element != null) {
726             if (isExpired(element)) {
727                 if (LOG.isDebugEnabled()) {
728                     LOG.debug(name + " Memory cache hit, but element expired");
729                 }
730                 missCountExpired++;
731                 remove(key, true, true, false);
732                 element = null;
733             } else {
734                 memoryStoreHitCount++;
735             }
736         }
737         return element;
738     }
739 
740     private Element searchInDiskStore(Object key, boolean updateStatistics) {
741         if (!(key instanceof Serializable)) {
742             return null;
743         }
744         Serializable serializableKey = (Serializable) key;
745         Element element;
746         if (updateStatistics) {
747             element = diskStore.get(serializableKey);
748         } else {
749             element = diskStore.getQuiet(serializableKey);
750         }
751         if (element != null) {
752             if (isExpired(element)) {
753                 if (LOG.isDebugEnabled()) {
754                     LOG.debug(name + " cache - Disk Store hit, but element expired");
755                 }
756                 missCountExpired++;
757                 remove(key, true, true, false);
758                 element = null;
759             } else {
760                 diskStoreHitCount++;
761                 //Put the item back into memory to preserve policies in the memory store and to save updated statistics
762                 memoryStore.put(element);
763             }
764         }
765         return element;
766     }
767 
768     /**
769      * Removes an {@link Element} from the Cache. This also removes it from any
770      * stores it may be in.
771      * <p/>
772      * Also notifies the CacheEventListener after the element was removed, but only if an Element
773      * with the key actually existed.
774      *
775      * @param key
776      * @return true if the element was removed, false if it was not found in the cache
777      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
778      */
779     public final synchronized boolean remove(Serializable key) throws IllegalStateException {
780         return remove((Object) key);
781     }
782 
783     /**
784      * Removes an {@link Element} from the Cache. This also removes it from any
785      * stores it may be in.
786      * <p/>
787      * Also notifies the CacheEventListener after the element was removed, but only if an Element
788      * with the key actually existed.
789      *
790      * @param key
791      * @return true if the element was removed, false if it was not found in the cache
792      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
793      * @since 1.2
794      */
795     public final synchronized boolean remove(Object key) throws IllegalStateException {
796         return remove(key, false);
797     }
798 
799 
800     /**
801      * Removes an {@link Element} from the Cache. This also removes it from any
802      * stores it may be in.
803      * <p/>
804      * Also notifies the CacheEventListener after the element was removed, but only if an Element
805      * with the key actually existed.
806      *
807      * @param key
808      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
809      *                                    further notification to doNotNotifyCacheReplicators cache peers
810      * @return true if the element was removed, false if it was not found in the cache
811      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
812      * @noinspection SameParameterValue
813      */
814     public final synchronized boolean remove(Serializable key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
815         return remove((Object) key, doNotNotifyCacheReplicators);
816     }
817 
818     /**
819      * Removes an {@link Element} from the Cache. This also removes it from any
820      * stores it may be in.
821      * <p/>
822      * Also notifies the CacheEventListener after the element was removed, but only if an Element
823      * with the key actually existed.
824      *
825      * @param key
826      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
827      *                                    further notification to doNotNotifyCacheReplicators cache peers
828      * @return true if the element was removed, false if it was not found in the cache
829      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
830      */
831     public final synchronized boolean remove(Object key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
832         return remove(key, false, true, doNotNotifyCacheReplicators);
833     }
834 
835     /**
836      * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
837      * stores it may be in.
838      * <p/>
839      *
840      * @param key
841      * @return true if the element was removed, false if it was not found in the cache
842      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
843      */
844     public final synchronized boolean removeQuiet(Serializable key) throws IllegalStateException {
845         return remove(key, false, false, false);
846     }
847 
848     /**
849      * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
850      * stores it may be in.
851      * <p/>
852      *
853      * @param key
854      * @return true if the element was removed, false if it was not found in the cache
855      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
856      * @since 1.2
857      */
858     public final synchronized boolean removeQuiet(Object key) throws IllegalStateException {
859         return remove(key, false, false, false);
860     }
861 
862 
863     /**
864      * Expires an {@link Element} from the Cache after an attempt to get it determined that it should be expired.
865      * This also removes it from any stores it may be in.
866      * <p/>
867      * Also notifies the CacheEventListener after the element has expired, but only if an Element
868      * with the key actually existed.
869      *
870      * @param key
871      * @param expiry                      if the reason this method is being called is to expire the element
872      * @param notifyListeners             whether to notify listeners
873      * @param doNotNotifyCacheReplicators whether not to notify cache replicators
874      * @return true if the element was removed, false if it was not found in the cache
875      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
876      */
877     private synchronized boolean remove(Object key, boolean expiry, boolean notifyListeners,
878                                         boolean doNotNotifyCacheReplicators)
879             throws IllegalStateException, CacheException {
880         checkStatus();
881         boolean removed = false;
882         Element elementFromMemoryStore;
883         elementFromMemoryStore = memoryStore.remove(key);
884         if (elementFromMemoryStore != null) {
885             if (notifyListeners) {
886                 if (expiry) {
887                     registeredEventListeners.notifyElementExpiry(elementFromMemoryStore, doNotNotifyCacheReplicators);
888                 } else {
889                     registeredEventListeners.notifyElementRemoved(elementFromMemoryStore, doNotNotifyCacheReplicators);
890                 }
891             }
892             removed = true;
893         }
894 
895         //could have been removed from both places, if there are two copies in the cache
896         Element elementFromDiskStore = null;
897         if (overflowToDisk) {
898             if (!(key instanceof Serializable)) {
899                 return false;
900             }
901             Serializable serializableKey = (Serializable) key;
902             elementFromDiskStore = diskStore.remove(serializableKey);
903         }
904 
905         if (elementFromDiskStore != null) {
906             if (expiry) {
907                 registeredEventListeners.notifyElementExpiry(elementFromDiskStore, doNotNotifyCacheReplicators);
908             } else {
909                 registeredEventListeners.notifyElementRemoved(elementFromDiskStore, doNotNotifyCacheReplicators);
910             }
911             removed = true;
912         }
913 
914         return removed;
915     }
916 
917 
918     /**
919      * Removes all cached items.
920      *
921      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
922      */
923     public final synchronized void removeAll() throws IllegalStateException, IOException, CacheException {
924         checkStatus();
925         memoryStore.removeAll();
926         if (overflowToDisk) {
927             diskStore.removeAll();
928         }
929     }
930 
931     /**
932      * Flushes all cache items from memory to auxilliary caches and close the auxilliary caches.
933      * <p/>
934      * Should be invoked only by CacheManager.
935      *
936      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
937      */
938     final synchronized void dispose() throws IllegalStateException {
939         checkStatus();
940         memoryStore.dispose();
941         memoryStore = null;
942         if (overflowToDisk) {
943             diskStore.dispose();
944             diskStore = null;
945         }
946 
947         registeredEventListeners.dispose();
948 
949         changeStatus(Status.STATUS_SHUTDOWN);
950 
951         if (diskPersistent) {
952             removeShutdownHook();
953         }
954     }
955 
956 
957     /**
958      * Flushes all cache items from memory to the disk store, and from the DiskStore to disk.
959      *
960      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
961      */
962     public final synchronized void flush() throws IllegalStateException, CacheException {
963         checkStatus();
964         try {
965             memoryStore.flush();
966             if (overflowToDisk) {
967                 diskStore.flush();
968             }
969         } catch (IOException e) {
970             throw new CacheException("Unable to flush cache: " + name + ". Initial cause was " + e.getMessage(), e);
971         }
972     }
973 
974 
975     /**
976      * Gets the size of the cache. This is a subtle concept. See below.
977      * <p/>
978      * The size is the number of {@link Element}s in the {@link MemoryStore} plus
979      * the number of {@link Element}s in the {@link DiskStore}.
980      * <p/>
981      * This number is the actual number of elements, including expired elements that have
982      * not been removed.
983      * <p/>
984      * Expired elements are removed from the the memory store when
985      * getting an expired element, or when attempting to spool an expired element to
986      * disk.
987      * <p/>
988      * Expired elements are removed from the disk store when getting an expired element,
989      * or when the expiry thread runs, which is once every five minutes.
990      * <p/>
991      * To get an exact size, which would exclude expired elements, use {@link #getKeysWithExpiryCheck()}.size(),
992      * although see that method for the approximate time that would take.
993      * <p/>
994      * To get a very fast result, use {@link #getKeysNoDuplicateCheck()}.size(). If the disk store
995      * is being used, there will be some duplicates.
996      *
997      * @return The size value
998      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
999      */
1000     public final synchronized int getSize() throws IllegalStateException, CacheException {
1001         checkStatus();
1002         /* The memory store and the disk store can simultaneously contain elements with the same key
1003 Cache size is the size of the union of the two key sets.*/
1004         return getKeys().size();
1005     }
1006 
1007     /**
1008      * Gets the size of the memory store for this cache
1009      * <p/>
1010      * Warning: This method can be very expensive to run. Allow approximately 1 second
1011      * per 1MB of entries. Running this method could create liveness problems
1012      * because the object lock is held for a long period
1013      * <p/>
1014      *
1015      * @return the approximate size of the memory store in bytes
1016      * @throws IllegalStateException
1017      */
1018     public final synchronized long calculateInMemorySize() throws IllegalStateException, CacheException {
1019         checkStatus();
1020         return memoryStore.getSizeInBytes();
1021     }
1022 
1023 
1024     /**
1025      * Returns the number of elements in the memory store.
1026      *
1027      * @return the number of elements in the memory store
1028      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1029      */
1030     public final long getMemoryStoreSize() throws IllegalStateException {
1031         checkStatus();
1032         return memoryStore.getSize();
1033     }
1034 
1035     /**
1036      * Returns the number of elements in the disk store.
1037      *
1038      * @return the number of elements in the disk store.
1039      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1040      */
1041     public final int getDiskStoreSize() throws IllegalStateException {
1042         checkStatus();
1043         if (overflowToDisk) {
1044             return diskStore.getSize();
1045         } else {
1046             return 0;
1047         }
1048     }
1049 
1050     /**
1051      * Gets the status attribute of the Cache.
1052      *
1053      * @return The status value from the Status enum class
1054      */
1055     public final Status getStatus() {
1056         return status;
1057     }
1058 
1059 
1060     private void checkStatus() throws IllegalStateException {
1061         if (!status.equals(Status.STATUS_ALIVE)) {
1062             throw new IllegalStateException("The " + name + " Cache is not alive.");
1063         }
1064     }
1065 
1066 
1067     /**
1068      * The number of times a requested item was found in the cache.
1069      *
1070      * @return the number of times a requested item was found in the cache
1071      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1072      */
1073     public final int getHitCount()
1074             throws IllegalStateException {
1075         checkStatus();
1076         return hitCount;
1077     }
1078 
1079     /**
1080      * Number of times a requested item was found in the Memory Store.
1081      *
1082      * @return Number of times a requested item was found in the Memory Store.
1083      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1084      */
1085     public final int getMemoryStoreHitCount() throws IllegalStateException {
1086         checkStatus();
1087         return memoryStoreHitCount;
1088     }
1089 
1090     /**
1091      * Number of times a requested item was found in the Disk Store.
1092      *
1093      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1094      */
1095     public final int getDiskStoreHitCount() throws IllegalStateException {
1096         checkStatus();
1097         return diskStoreHitCount;
1098     }
1099 
1100     /**
1101      * Number of times a requested element was not found in the cache. This
1102      * may be because it expired, in which case this will also be recorded in {@link #getMissCountExpired},
1103      * or because it was simply not there.
1104      *
1105      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1106      */
1107     public final int getMissCountNotFound() throws IllegalStateException {
1108         checkStatus();
1109         return missCountNotFound;
1110     }
1111 
1112     /**
1113      * Number of times a requested element was found but was expired.
1114      *
1115      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1116      */
1117     public final int getMissCountExpired() throws IllegalStateException {
1118         checkStatus();
1119         return missCountExpired;
1120     }
1121 
1122     /**
1123      * Gets the cache name.
1124      */
1125     public final String getName() {
1126         return name;
1127     }
1128 
1129     /**
1130      * Sets the cache name which will name.
1131      *
1132      * @param name the name of the cache. Should not be null.
1133      */
1134     final void setName(String name) {
1135         this.name = name;
1136     }
1137 
1138     /**
1139      * Gets timeToIdleSeconds.
1140      */
1141     public final long getTimeToIdleSeconds() {
1142         return timeToIdleSeconds;
1143     }
1144 
1145     /**
1146      * Gets timeToLiveSeconds.
1147      */
1148     public final long getTimeToLiveSeconds() {
1149         return timeToLiveSeconds;
1150     }
1151 
1152     /**
1153      * Are elements eternal.
1154      */
1155     public final boolean isEternal() {
1156         return eternal;
1157     }
1158 
1159     /**
1160      * Does the overflow go to disk.
1161      */
1162     public final boolean isOverflowToDisk() {
1163         return overflowToDisk;
1164     }
1165 
1166     /**
1167      * Gets the maximum number of elements to hold in memory.
1168      */
1169     public final int getMaxElementsInMemory() {
1170         return maxElementsInMemory;
1171     }
1172 
1173     /**
1174      * The policy used to evict elements from the {@link net.sf.ehcache.store.MemoryStore}.
1175      * This can be one of:
1176      * <ol>
1177      * <li>LRU - least recently used
1178      * <li>LFU - least frequently used
1179      * <li>FIFO - first in first out, the oldest element by creation time
1180      * </ol>
1181      * The default value is LRU
1182      *
1183      * @since 1.2
1184      */
1185     public final MemoryStoreEvictionPolicy getMemoryStoreEvictionPolicy() {
1186         return memoryStoreEvictionPolicy;
1187     }
1188 
1189     /**
1190      * Returns a {@link String} representation of {@link Cache}.
1191      */
1192     public final String toString() {
1193         StringBuffer dump = new StringBuffer();
1194 
1195         dump.append("[ ")
1196                 .append(" name = ").append(name)
1197                 .append(" status = ").append(status)
1198                 .append(" eternal = ").append(eternal)
1199                 .append(" overflowToDisk = ").append(overflowToDisk)
1200                 .append(" maxElementsInMemory = ").append(maxElementsInMemory)
1201                 .append(" memoryStoreEvictionPolicy = ").append(memoryStoreEvictionPolicy)
1202                 .append(" timeToLiveSeconds = ").append(timeToLiveSeconds)
1203                 .append(" timeToIdleSeconds = ").append(timeToIdleSeconds)
1204                 .append(" diskPersistent = ").append(diskPersistent)
1205                 .append(" diskExpiryThreadIntervalSeconds = ").append(diskExpiryThreadIntervalSeconds)
1206                 .append(registeredEventListeners)
1207                 .append(" hitCount = ").append(hitCount)
1208                 .append(" memoryStoreHitCount = ").append(memoryStoreHitCount)
1209                 .append(" diskStoreHitCount = ").append(diskStoreHitCount)
1210                 .append(" missCountNotFound = ").append(missCountNotFound)
1211                 .append(" missCountExpired = ").append(missCountExpired)
1212                 .append(" ]");
1213 
1214         return dump.toString();
1215     }
1216 
1217 
1218     /**
1219      * Checks whether this cache element has expired.
1220      * <p/>
1221      * The element is expired if:
1222      * <ol>
1223      * <li> the idle time is non-zero and has elapsed, unless the cache is eternal; or
1224      * <li> the time to live is non-zero and has elapsed, unless the cache is eternal; or
1225      * <li> the value of the element is null.
1226      * </ol>
1227      *
1228      * @return true if it has expired
1229      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1230      * @throws NullPointerException  if the element is null
1231      */
1232     public final boolean isExpired(Element element) throws IllegalStateException, NullPointerException {
1233         checkStatus();
1234         boolean expired;
1235         synchronized (element) {
1236             if (!eternal) {
1237                 expired = checkExpirationForNotEternal(element);
1238             } else {
1239                 expired = false;
1240             }
1241             if (LOG.isDebugEnabled()) {
1242                 LOG.debug(getName() + ": Is element with key " + element.getObjectKey() + " expired?: " + expired);
1243             }
1244             return expired;
1245         }
1246     }
1247 
1248     private boolean checkExpirationForNotEternal(Element element) {
1249         boolean expired;
1250         long now = System.currentTimeMillis();
1251         long creationTime = element.getCreationTime();
1252         long ageLived = now - creationTime;
1253         long ageToLive = timeToLiveSeconds * MS_PER_SECOND;
1254         long nextToLastAccessTime = element.getNextToLastAccessTime();
1255         long mostRecentTime = Math.max(creationTime, nextToLastAccessTime);
1256         long ageIdled = now - mostRecentTime;
1257         long ageToIdle = timeToIdleSeconds * MS_PER_SECOND;
1258 
1259         if (LOG.isTraceEnabled()) {
1260             LOG.trace(element.getObjectKey() + " now: " + now);
1261             LOG.trace(element.getObjectKey() + " Creation Time: " + creationTime
1262                     + " Next To Last Access Time: " + nextToLastAccessTime);
1263             LOG.trace(element.getObjectKey() + " mostRecentTime: " + mostRecentTime);
1264             LOG.trace(element.getObjectKey() + " Age to Idle: " + ageToIdle + " Age Idled: " + ageIdled);
1265         }
1266 
1267         if (timeToLiveSeconds != 0 && (ageLived > ageToLive)) {
1268             if (LOG.isTraceEnabled()) {
1269                 LOG.trace("timeToLiveSeconds exceeded : " + element.getObjectKey());
1270             }
1271             expired = true;
1272         } else if (timeToIdleSeconds != 0 && (ageIdled > ageToIdle)) {
1273             if (LOG.isTraceEnabled()) {
1274                 LOG.trace("timeToIdleSeconds exceeded : " + element.getObjectKey());
1275             }
1276             expired = true;
1277         } else {
1278             expired = false;
1279         }
1280         return expired;
1281     }
1282 
1283 
1284     /**
1285      * Clones a cache. This is only legal if the cache has not been
1286      * initialized. At that point only primitives have been set and no
1287      * {@link net.sf.ehcache.store.LruMemoryStore} or {@link net.sf.ehcache.store.DiskStore} has been created.
1288      * <p/>
1289      * A new, empty, RegisteredEventListeners is created on clone.
1290      * <p/>
1291      *
1292      * @return an object of type {@link Cache}
1293      * @throws CloneNotSupportedException
1294      */
1295     public final Object clone() throws CloneNotSupportedException {
1296         if (!(memoryStore == null && diskStore == null)) {
1297             throw new CloneNotSupportedException("Cannot clone an initialized cache.");
1298         }
1299         Cache copy = (Cache) super.clone();
1300         RegisteredEventListeners registeredEventListenersFromCopy = copy.getCacheEventNotificationService();
1301         if (registeredEventListenersFromCopy == null || registeredEventListenersFromCopy.getCacheEventListeners().size() == 0)
1302         {
1303             copy.registeredEventListeners = new RegisteredEventListeners(copy);
1304         } else {
1305             throw new CloneNotSupportedException("Cannot clone a cache with registered event listeners");
1306         }
1307         return copy;
1308     }
1309 
1310     /**
1311      * Gets the internal DiskStore.
1312      *
1313      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1314      */
1315     final DiskStore getDiskStore() throws IllegalStateException {
1316         checkStatus();
1317         return diskStore;
1318     }
1319 
1320     /**
1321      * Gets the internal MemoryStore.
1322      *
1323      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1324      */
1325     final MemoryStore getMemoryStore() throws IllegalStateException {
1326         checkStatus();
1327         return memoryStore;
1328     }
1329 
1330     /**
1331      * @return true if the cache overflows to disk and the disk is persistent between restarts
1332      */
1333     public final boolean isDiskPersistent() {
1334         return diskPersistent;
1335     }
1336 
1337     /**
1338      * @return the interval between runs
1339      *         of the expiry thread, where it checks the disk store for expired elements. It is not the
1340      *         the timeToLiveSeconds.
1341      */
1342     public final long getDiskExpiryThreadIntervalSeconds() {
1343         return diskExpiryThreadIntervalSeconds;
1344     }
1345 
1346     /**
1347      * Use this to access the service in order to register and unregister listeners
1348      *
1349      * @return the RegisteredEventListeners instance for this cache.
1350      */
1351     public final RegisteredEventListeners getCacheEventNotificationService() {
1352         return registeredEventListeners;
1353     }
1354 
1355 
1356     /**
1357      * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
1358      *
1359      * @return true if an element matching the key is found in memory
1360      */
1361     public final boolean isElementInMemory(Serializable key) {
1362         return isElementInMemory((Object) key);
1363     }
1364 
1365     /**
1366      * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
1367      *
1368      * @return true if an element matching the key is found in memory
1369      * @since 1.2
1370      */
1371     public final boolean isElementInMemory(Object key) {
1372         return memoryStore.containsKey(key);
1373     }
1374 
1375     /**
1376      * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
1377      *
1378      * @return true if an element matching the key is found in the diskStore
1379      */
1380     public final boolean isElementOnDisk(Serializable key) {
1381         return isElementOnDisk((Object) key);
1382     }
1383 
1384     /**
1385      * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
1386      *
1387      * @return true if an element matching the key is found in the diskStore
1388      * @since 1.2
1389      */
1390     public final boolean isElementOnDisk(Object key) {
1391         if (!(key instanceof Serializable)) {
1392             return false;
1393         }
1394         Serializable serializableKey = (Serializable) key;
1395         return diskStore != null && diskStore.containsKey(serializableKey);
1396     }
1397 
1398     /**
1399      * The GUID for this cache instance can be used to determine whether two cache instance references
1400      * are pointing to the same cache.
1401      *
1402      * @return the globally unique identifier for this cache instance. This is guaranteed to be unique.
1403      * @since 1.2
1404      */
1405     public final String getGuid() {
1406         return guid;
1407     }
1408 
1409     /**
1410      * Gets the CacheManager managing this cache. For a newly created cache this will be null until
1411      * it has been added to a CacheManager.
1412      *
1413      * @return the manager or null if there is none
1414      */
1415     public final CacheManager getCacheManager() {
1416         return cacheManager;
1417     }
1418 
1419     /**
1420      * Package local setter for use by CacheManager
1421      *
1422      * @param cacheManager
1423      */
1424     final void setCacheManager(CacheManager cacheManager) {
1425         this.cacheManager = cacheManager;
1426     }
1427 
1428 
1429 }