View Javadoc

1   /**
2    *  Copyright 2003-2007 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.bootstrap.BootstrapCacheLoader;
20  import net.sf.ehcache.event.CacheEventListener;
21  import net.sf.ehcache.event.RegisteredEventListeners;
22  import net.sf.ehcache.store.DiskStore;
23  import net.sf.ehcache.store.MemoryStore;
24  import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
25  import net.sf.ehcache.store.Store;
26  import net.sf.ehcache.config.CacheConfiguration;
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  
30  import java.io.IOException;
31  import java.io.Serializable;
32  import java.net.InetAddress;
33  import java.net.UnknownHostException;
34  import java.rmi.server.UID;
35  import java.util.ArrayList;
36  import java.util.Arrays;
37  import java.util.HashSet;
38  import java.util.Iterator;
39  import java.util.List;
40  import java.util.Set;
41  
42  /**
43   * Cache is the central class in ehcache. Caches have {@link Element}s and are managed
44   * by the {@link CacheManager}. The Cache performs logical actions. It delegates physical
45   * implementations to its {@link net.sf.ehcache.store.Store}s.
46   * <p/>
47   * A reference to a Cache can be obtained through the {@link CacheManager}. A Cache thus obtained
48   * is guaranteed to have status {@link Status#STATUS_ALIVE}. This status is checked for any method which
49   * throws {@link IllegalStateException} and the same thrown if it is not alive. This would normally
50   * happen if a call is made after {@link CacheManager#shutdown} is invoked.
51   * <p/>
52   * Cache is threadsafe.
53   * <p/>
54   * Statistics on cache usage are collected and made available through public methods.
55   *
56   * @author Greg Luck
57   * @version $Id: Cache.java 512 2007-07-10 09:18:45Z gregluck $
58   */
59  public class Cache implements Ehcache {
60  
61      /**
62       * A reserved word for cache names. It denotes a default configuration
63       * which is applied to caches created without configuration.
64       */
65      public static final String DEFAULT_CACHE_NAME = "default";
66  
67      /**
68       * System Property based method of disabling ehcache. If disabled no elements will be added to a cache.
69       * <p/>
70       * Set the property "net.sf.ehcache.disabled=true" to disable ehcache.
71       * <p/>
72       * This can easily be done using <code>java -Dnet.sf.ehcache.disabled=true</code> in the command line.
73       */
74      public static final String NET_SF_EHCACHE_DISABLED = "net.sf.ehcache.disabled";
75  
76      {
77          String value = System.getProperty(NET_SF_EHCACHE_DISABLED);
78          if (value != null) {
79              disabled = value.equalsIgnoreCase("true");
80          }
81      }
82  
83      /**
84       * The default interval between runs of the expiry thread.
85       */
86      public static final long DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS = 120;
87  
88      /**
89       * Set a buffer size for the spool of approx 30MB
90       */
91      private static final int DEFAULT_SPOOL_BUFFER_SIZE = 30;
92  
93      private static final Log LOG = LogFactory.getLog(Cache.class.getName());
94  
95      private static final MemoryStoreEvictionPolicy DEFAULT_MEMORY_STORE_EVICTION_POLICY = MemoryStoreEvictionPolicy.LRU;
96  
97      private static InetAddress localhost;
98  
99      /**
100      * The amount of time to wait if a store gets backed up
101      */
102     private static final int BACK_OFF_TIME_MILLIS = 50;
103 
104     static {
105         try {
106             localhost = InetAddress.getLocalHost();
107         } catch (UnknownHostException e) {
108             LOG.error("Unable to set localhost. This prevents creation of a GUID. Cause was: " + e.getMessage(), e);
109         }
110     }
111 
112     private boolean disabled;
113 
114     private Store diskStore;
115 
116     private String diskStorePath;
117 
118     private Status status;
119 
120     private CacheConfiguration configuration;
121 
122     /**
123      * Cache hit count.
124      */
125     private long hitCount;
126 
127     /**
128      * Memory cache hit count.
129      */
130     private long memoryStoreHitCount;
131 
132     /**
133      * DiskStore hit count.
134      */
135     private long diskStoreHitCount;
136 
137     /**
138      * Count of misses where element was not found.
139      */
140     private long missCountNotFound;
141 
142     /**
143      * Count of misses where element was expired.
144      */
145     private long missCountExpired;
146 
147     /**
148      * The {@link MemoryStore} of this {@link Cache}. All caches have a memory store.
149      */
150     private MemoryStore memoryStore;
151 
152     private RegisteredEventListeners registeredEventListeners;
153 
154     private String guid;
155 
156     private CacheManager cacheManager;
157 
158     private BootstrapCacheLoader bootstrapCacheLoader;
159 
160     private int statisticsAccuracy;
161 
162     /**
163      * 1.0 Constructor.
164      * <p/>
165      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
166      * <p/>
167      * A client can specify their own settings here and pass the {@link Cache} object
168      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
169      * <p/>
170      * Only the CacheManager can initialise them.
171      * <p/>
172      * This constructor creates disk stores, if specified, that do not persist between restarts.
173      * <p/>
174      * The default expiry thread interval of 120 seconds is used. This is the interval between runs
175      * of the expiry thread, where it checks the disk store for expired elements. It is not the
176      * the timeToLiveSeconds.
177      *
178      * @param name                the name of the cache. Note that "default" is a reserved name for the defaultCache.
179      * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
180      * @param overflowToDisk      whether to use the disk store
181      * @param eternal             whether the elements in the cache are eternal, i.e. never expire
182      * @param timeToLiveSeconds   the default amount of time to live for an element from its creation date
183      * @param timeToIdleSeconds   the default amount of time to live for an element from its last accessed or modified date
184      * @since 1.0
185      */
186     public Cache(String name, int maxElementsInMemory, boolean overflowToDisk,
187                  boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds) {
188         this(name, maxElementsInMemory, DEFAULT_MEMORY_STORE_EVICTION_POLICY, overflowToDisk,
189                 null, eternal, timeToLiveSeconds, timeToIdleSeconds, false, DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS, null, null);
190     }
191 
192 
193     /**
194      * 1.1 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      *
203      * @param name                the name of the cache. Note that "default" is a reserved name for the defaultCache.
204      * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
205      * @param overflowToDisk      whether to use the disk store
206      * @param eternal             whether the elements in the cache are eternal, i.e. never expire
207      * @param timeToLiveSeconds   the default amount of time to live for an element from its creation date
208      * @param timeToIdleSeconds   the default amount of time to live for an element from its last accessed or modified date
209      * @param diskPersistent      whether to persist the cache to disk between JVM restarts
210      * @param diskExpiryThreadIntervalSeconds
211      *                            how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
212      * @since 1.1
213      */
214     public Cache(String name,
215                  int maxElementsInMemory,
216                  boolean overflowToDisk,
217                  boolean eternal,
218                  long timeToLiveSeconds,
219                  long timeToIdleSeconds,
220                  boolean diskPersistent,
221                  long diskExpiryThreadIntervalSeconds) {
222         this(name, maxElementsInMemory, DEFAULT_MEMORY_STORE_EVICTION_POLICY, overflowToDisk, null,
223                 eternal, timeToLiveSeconds, timeToIdleSeconds, diskPersistent, diskExpiryThreadIntervalSeconds, null, null);
224         LOG.warn("An API change between ehcache-1.1 and ehcache-1.2 results in the persistence path being set to java.io.tmpdir" +
225                 " when the ehcache-1.1 constructor is used. Please change to the 1.2 constructor");
226     }
227 
228 
229     /**
230      * 1.2 Constructor
231      * <p/>
232      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
233      * <p/>
234      * A client can specify their own settings here and pass the {@link Cache} object
235      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
236      * <p/>
237      * Only the CacheManager can initialise them.
238      *
239      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
240      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted
241      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
242      * @param overflowToDisk            whether to use the disk store
243      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
244      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
245      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
246      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
247      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
248      * @param diskExpiryThreadIntervalSeconds
249      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
250      * @param registeredEventListeners  a notification service. Optionally null, in which case a new
251      *                                  one with no registered listeners will be created.
252      * @since 1.2
253      */
254     public Cache(String name,
255                  int maxElementsInMemory,
256                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
257                  boolean overflowToDisk,
258                  String diskStorePath,
259                  boolean eternal,
260                  long timeToLiveSeconds,
261                  long timeToIdleSeconds,
262                  boolean diskPersistent,
263                  long diskExpiryThreadIntervalSeconds,
264                  RegisteredEventListeners registeredEventListeners) {
265         this(name,
266                 maxElementsInMemory,
267                 memoryStoreEvictionPolicy,
268                 overflowToDisk,
269                 diskStorePath,
270                 eternal,
271                 timeToLiveSeconds,
272                 timeToIdleSeconds,
273                 diskPersistent,
274                 diskExpiryThreadIntervalSeconds,
275                 registeredEventListeners,
276                 null);
277     }
278 
279 
280     /**
281      * 1.2.1 Constructor
282      * <p/>
283      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
284      * <p/>
285      * A client can specify their own settings here and pass the {@link Cache} object
286      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
287      * <p/>
288      * Only the CacheManager can initialise them.
289      *
290      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
291      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted
292      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
293      * @param overflowToDisk            whether to use the disk store
294      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
295      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
296      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
297      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
298      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
299      * @param diskExpiryThreadIntervalSeconds
300      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
301      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
302      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
303      * @since 1.2.1
304      */
305     public Cache(String name,
306                  int maxElementsInMemory,
307                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
308                  boolean overflowToDisk,
309                  String diskStorePath,
310                  boolean eternal,
311                  long timeToLiveSeconds,
312                  long timeToIdleSeconds,
313                  boolean diskPersistent,
314                  long diskExpiryThreadIntervalSeconds,
315                  RegisteredEventListeners registeredEventListeners,
316                  BootstrapCacheLoader bootstrapCacheLoader) {
317 
318         this(name,
319                 maxElementsInMemory,
320                 memoryStoreEvictionPolicy,
321                 overflowToDisk,
322                 diskStorePath,
323                 eternal,
324                 timeToLiveSeconds,
325                 timeToIdleSeconds,
326                 diskPersistent,
327                 diskExpiryThreadIntervalSeconds,
328                 registeredEventListeners,
329                 bootstrapCacheLoader,
330                 0);
331     }
332 
333     /**
334      * 1.2.4 Constructor
335      * <p/>
336      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
337      * <p/>
338      * A client can specify their own settings here and pass the {@link Cache} object
339      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
340      * <p/>
341      * Only the CacheManager can initialise them.
342      *
343      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
344      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted
345      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
346      * @param overflowToDisk            whether to use the disk store
347      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
348      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
349      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
350      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
351      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
352      * @param diskExpiryThreadIntervalSeconds
353      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
354      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
355      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
356      * @since 1.2.4
357      */
358     public Cache(String name,
359                  int maxElementsInMemory,
360                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
361                  boolean overflowToDisk,
362                  String diskStorePath,
363                  boolean eternal,
364                  long timeToLiveSeconds,
365                  long timeToIdleSeconds,
366                  boolean diskPersistent,
367                  long diskExpiryThreadIntervalSeconds,
368                  RegisteredEventListeners registeredEventListeners,
369                  BootstrapCacheLoader bootstrapCacheLoader,
370                  int maxElementsOnDisk) {
371 
372 
373         this(name,
374                 maxElementsInMemory,
375                 memoryStoreEvictionPolicy,
376                 overflowToDisk,
377                 diskStorePath,
378                 eternal,
379                 timeToLiveSeconds,
380                 timeToIdleSeconds,
381                 diskPersistent,
382                 diskExpiryThreadIntervalSeconds,
383                 registeredEventListeners,
384                 bootstrapCacheLoader,
385                 maxElementsOnDisk,
386                 0);
387 
388     }
389 
390     /**
391      * 1.2.4 Constructor
392      * <p/>
393      * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
394      * <p/>
395      * A client can specify their own settings here and pass the {@link Cache} object
396      * into {@link CacheManager#addCache} to specify parameters other than the defaults.
397      * <p/>
398      * Only the CacheManager can initialise them.
399      *
400      * @param name                      the name of the cache. Note that "default" is a reserved name for the defaultCache.
401      * @param maxElementsInMemory       the maximum number of elements in memory, before they are evicted
402      * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
403      * @param overflowToDisk            whether to use the disk store
404      * @param diskStorePath             this parameter is ignored. CacheManager sets it using setter injection.
405      * @param eternal                   whether the elements in the cache are eternal, i.e. never expire
406      * @param timeToLiveSeconds         the default amount of time to live for an element from its creation date
407      * @param timeToIdleSeconds         the default amount of time to live for an element from its last accessed or modified date
408      * @param diskPersistent            whether to persist the cache to disk between JVM restarts
409      * @param diskExpiryThreadIntervalSeconds
410      *                                  how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
411      * @param registeredEventListeners  a notification service. Optionally null, in which case a new one with no registered listeners will be created.
412      * @param bootstrapCacheLoader      the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
413      * @param diskSpoolBufferSizeMB     the amount of memory to allocate the write buffer for puts to the DiskStore.
414      * @since 1.2.4
415      */
416     public Cache(String name,
417                  int maxElementsInMemory,
418                  MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
419                  boolean overflowToDisk,
420                  String diskStorePath,
421                  boolean eternal,
422                  long timeToLiveSeconds,
423                  long timeToIdleSeconds,
424                  boolean diskPersistent,
425                  long diskExpiryThreadIntervalSeconds,
426                  RegisteredEventListeners registeredEventListeners,
427                  BootstrapCacheLoader bootstrapCacheLoader,
428                  int maxElementsOnDisk,
429                  int diskSpoolBufferSizeMB) {
430 
431         changeStatus(Status.STATUS_UNINITIALISED);
432 
433         guid = createGuid();
434 
435         configuration = new CacheConfiguration();
436         configuration.setName(name);
437         configuration.setMaxElementsInMemory(maxElementsInMemory);
438         configuration.setMemoryStoreEvictionPolicyFromObject(memoryStoreEvictionPolicy);
439         configuration.setOverflowToDisk(overflowToDisk);
440         configuration.setEternal(eternal);
441         configuration.setTimeToLiveSeconds(timeToLiveSeconds);
442         configuration.setTimeToIdleSeconds(timeToIdleSeconds);
443         configuration.setDiskPersistent(diskPersistent);
444         configuration.setMaxElementsOnDisk(maxElementsOnDisk);
445 
446 
447         if (diskStorePath == null) {
448             this.diskStorePath = System.getProperty("java.io.tmpdir");
449         } else {
450             this.diskStorePath = diskStorePath;
451         }
452 
453         if (registeredEventListeners == null) {
454             this.registeredEventListeners = new RegisteredEventListeners(this);
455         } else {
456             this.registeredEventListeners = registeredEventListeners;
457         }
458 
459         //Set this to a safe value.
460         if (diskExpiryThreadIntervalSeconds == 0) {
461             configuration.setDiskExpiryThreadIntervalSeconds(DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS);
462         } else {
463             configuration.setDiskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds);
464         }
465 
466         if (diskSpoolBufferSizeMB == 0) {
467             configuration.setDiskSpoolBufferSizeMB(DEFAULT_SPOOL_BUFFER_SIZE);
468         } else {
469             configuration.setDiskSpoolBufferSizeMB(diskSpoolBufferSizeMB);
470         }
471 
472         // For backward compatibility with 1.1 and earlier
473         if (memoryStoreEvictionPolicy == null) {
474             configuration.setMemoryStoreEvictionPolicyFromObject(DEFAULT_MEMORY_STORE_EVICTION_POLICY);
475         }
476 
477         this.bootstrapCacheLoader = bootstrapCacheLoader;
478 
479         statisticsAccuracy = Statistics.STATISTICS_ACCURACY_BEST_EFFORT;
480 
481     }
482 
483     /**
484      * Newly created caches do not have a {@link net.sf.ehcache.store.MemoryStore} or a {@link net.sf.ehcache.store.DiskStore}.
485      * <p/>
486      * This method creates those and makes the cache ready to accept elements
487      */
488     public void initialise() {
489         synchronized (this) {
490             if (!status.equals(Status.STATUS_UNINITIALISED)) {
491                 throw new IllegalStateException("Cannot initialise the " + configuration.getName()
492                         + " cache because its status is not STATUS_UNINITIALISED");
493             }
494 
495             if (configuration.getMaxElementsInMemory() == 0) {
496                 if (LOG.isWarnEnabled()) {
497                     LOG.warn("Cache: " + configuration.getName()
498                             + " has a maxElementsInMemory of 0. It is strongly recommended to " +
499                             "have a maximumSize of at least 1. Performance is halved by not using a MemoryStore.");
500                 }
501             }
502 
503             this.diskStore = createDiskStore();
504 
505             memoryStore = MemoryStore.create(this, diskStore);
506             changeStatus(Status.STATUS_ALIVE);
507         }
508 
509         if (LOG.isDebugEnabled()) {
510             LOG.debug("Initialised cache: " + configuration.getName());
511         }
512 
513         if (disabled) {
514             if (LOG.isWarnEnabled()) {
515                 LOG.warn("Cache: " + configuration.getName() + " is disabled because the " + NET_SF_EHCACHE_DISABLED
516                         + " property was set to true. No elements will be added to the cache.");
517             }
518         }
519     }
520 
521     /**
522      * Creates a disk store.
523      */
524     protected Store createDiskStore() {
525         if (configuration.isOverflowToDisk()) {
526             return new DiskStore(this, diskStorePath);
527         } else {
528             return null;
529         }
530     }
531 
532     /**
533      * Bootstrap command. This must be called after the Cache is intialised, during
534      * CacheManager initialisation. If loads are synchronous, they will complete before the CacheManager
535      * initialise completes, otherwise they will happen in the background.
536      */
537     public void bootstrap() {
538         if (!disabled && bootstrapCacheLoader != null) {
539             bootstrapCacheLoader.load(this);
540         }
541 
542     }
543 
544     private void changeStatus(Status status) {
545         this.status = status;
546     }
547 
548 
549     /**
550      * Put an element in the cache.
551      * <p/>
552      * Resets the access statistics on the element, which would be the case if it has previously been
553      * gotten from a cache, and is now being put back.
554      * <p/>
555      * Also notifies the CacheEventListener that:
556      * <ul>
557      * <li>the element was put, but only if the Element was actually put.
558      * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
559      * if it was requested
560      * </ul>
561      * Synchronization is handled within the method.
562      * <p/>
563      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
564      * This exception should be caught in those cirucmstances.
565      *
566      * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
567      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
568      * @throws IllegalArgumentException if the element is null
569      * @throws CacheException
570      */
571     public final void put(Element element) throws IllegalArgumentException, IllegalStateException,
572             CacheException {
573         put(element, false);
574     }
575 
576 
577     /**
578      * Put an element in the cache.
579      * <p/>
580      * Resets the access statistics on the element, which would be the case if it has previously been
581      * gotten from a cache, and is now being put back.
582      * <p/>
583      * Also notifies the CacheEventListener that:
584      * <ul>
585      * <li>the element was put, but only if the Element was actually put.
586      * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
587      * if it was requested
588      * </ul>
589      * Synchronization is handled within the method.
590      * <p/>
591      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
592      * This exception should be caught in those cirucmstances.
593      *
594      * @param element                     An object. If Serializable it can fully participate in replication and the DiskStore.
595      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
596      *                                    further notification to doNotNotifyCacheReplicators cache peers
597      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
598      * @throws IllegalArgumentException if the element is null
599      */
600     public final void put(Element element, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
601             IllegalStateException,
602             CacheException {
603         checkStatus();
604 
605         if (disabled) {
606             return;
607         }
608 
609         if (element == null) {
610             throw new IllegalArgumentException("Element cannot be null");
611         }
612 
613         element.resetAccessStatistics();
614         boolean elementExists;
615         Object key = element.getObjectKey();
616         elementExists = isElementInMemory(key) || isElementOnDisk(key);
617         if (elementExists) {
618             element.updateUpdateStatistics();
619         }
620         applyDefaultsToElementWithoutLifespanSet(element);
621 
622         backOffIfDiskSpoolFull();
623 
624 
625         synchronized (this) {
626             memoryStore.put(element);
627         }
628 
629         if (elementExists) {
630             registeredEventListeners.notifyElementUpdated(element, doNotNotifyCacheReplicators);
631         } else {
632             registeredEventListeners.notifyElementPut(element, doNotNotifyCacheReplicators);
633         }
634 
635     }
636 
637     /**
638      * wait outside of synchronized block so as not to block readers
639      * If the disk store spool is full wait a short time to give it a chance to
640      * catch up.
641      */
642     private void backOffIfDiskSpoolFull() {
643 
644         if (diskStore != null && diskStore.backedUp()) {
645             //back off to avoid OutOfMemoryError
646             try {
647                 Thread.sleep(BACK_OFF_TIME_MILLIS);
648             } catch (InterruptedException e) {
649                 //do not care if this happens
650             }
651         }
652     }
653 
654     private void applyDefaultsToElementWithoutLifespanSet(Element element) {
655         if (!element.isLifespanSet()) {
656             //Setting with Cache defaults
657             element.setTimeToLive((int) configuration.getTimeToLiveSeconds());
658             element.setTimeToIdle((int) configuration.getTimeToIdleSeconds());
659             element.setEternal(configuration.isEternal());
660         }
661     }
662 
663 
664     /**
665      * Put an element in the cache, without updating statistics, or updating listeners. This is meant to be used
666      * in conjunction with {@link #getQuiet}.
667      * Synchronization is handled within the method.
668      * <p/>
669      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
670      * This exception should be caught in those cirucmstances.
671      * <p/>
672      *
673      * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
674      * @throws IllegalStateException    if the cache is not {@link Status#STATUS_ALIVE}
675      * @throws IllegalArgumentException if the element is null
676      */
677     public final void putQuiet(Element element) throws IllegalArgumentException, IllegalStateException,
678             CacheException {
679         checkStatus();
680 
681         if (disabled) {
682             return;
683         }
684 
685         if (element == null) {
686             throw new IllegalArgumentException("Element cannot be null");
687         }
688 
689         applyDefaultsToElementWithoutLifespanSet(element);
690 
691         synchronized (this) {
692             memoryStore.put(element);
693         }
694     }
695 
696     /**
697      * Gets an element from the cache. Updates Element Statistics
698      * <p/>
699      * Note that the Element's lastAccessTime is always the time of this get.
700      * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
701      * <p/>
702      * Synchronization is handled within the method.
703      *
704      * @param key a serializable value
705      * @return the element, or null, if it does not exist.
706      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
707      * @see #isExpired
708      */
709     public final Element get(Serializable key) throws IllegalStateException, CacheException {
710         return get((Object) key);
711     }
712 
713 
714     /**
715      * Gets an element from the cache. Updates Element Statistics
716      * <p/>
717      * Note that the Element's lastAccessTime is always the time of this get.
718      * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
719      * <p/>
720      * Synchronization is handled within the method.
721      *
722      * @param key an Object value
723      * @return the element, or null, if it does not exist.
724      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
725      * @see #isExpired
726      * @since 1.2
727      */
728     public final Element get(Object key) throws IllegalStateException, CacheException {
729         checkStatus();
730         Element element;
731 
732         synchronized (this) {
733             element = searchInMemoryStore(key, true);
734             if (element == null && configuration.isOverflowToDisk()) {
735                 element = searchInDiskStore(key, true);
736             }
737             if (element == null) {
738                 missCountNotFound++;
739                 if (LOG.isTraceEnabled()) {
740                     LOG.trace(configuration.getName() + " cache - Miss");
741                 }
742             } else {
743                 hitCount++;
744             }
745         }
746         return element;
747     }
748 
749     /**
750      * Gets an element from the cache, without updating Element statistics. Cache statistics are
751      * still updated.
752      * <p/>
753      * Synchronization is handled within the method.
754      *
755      * @param key a serializable value
756      * @return the element, or null, if it does not exist.
757      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
758      * @see #isExpired
759      */
760     public final Element getQuiet(Serializable key) throws IllegalStateException, CacheException {
761         return getQuiet((Object) key);
762     }
763 
764     /**
765      * Gets an element from the cache, without updating Element statistics. Cache statistics are
766      * not updated.
767      * <p/>
768      * Synchronization is handled within the method.
769      *
770      * @param key a serializable value
771      * @return the element, or null, if it does not exist.
772      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
773      * @see #isExpired
774      * @since 1.2
775      */
776     public final Element getQuiet(Object key) throws IllegalStateException, CacheException {
777         checkStatus();
778         Element element;
779 
780         synchronized (this) {
781             element = searchInMemoryStore(key, false);
782             if (element == null && configuration.isOverflowToDisk()) {
783                 element = searchInDiskStore(key, false);
784             }
785         }
786         return element;
787     }
788 
789     /**
790      * Returns a list of all element keys in the cache, whether or not they are expired.
791      * <p/>
792      * The returned keys are unique and can be considered a set.
793      * <p/>
794      * The List returned is not live. It is a copy.
795      * <p/>
796      * The time taken is O(n). On a single cpu 1.8Ghz P4, approximately 8ms is required
797      * for each 1000 entries.
798      *
799      * @return a list of {@link Object} keys
800      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
801      */
802     public final synchronized List getKeys() throws IllegalStateException, CacheException {
803         checkStatus();
804         /* An element with the same key can exist in both the memory store and the
805             disk store at the same time. Because the memory store is always searched first
806             these duplicates do not cause problems when getting elements/
807 
808             This method removes these duplicates before returning the list of keys*/
809         List allKeyList = new ArrayList();
810         List keyList = Arrays.asList(memoryStore.getKeyArray());
811         allKeyList.addAll(keyList);
812         if (configuration.isOverflowToDisk()) {
813             Set allKeys = new HashSet();
814             //within the store keys will be unique
815             allKeys.addAll(keyList);
816             Object[] diskKeys = diskStore.getKeyArray();
817             for (int i = 0; i < diskKeys.length; i++) {
818                 Object diskKey = diskKeys[i];
819                 if (allKeys.add(diskKey)) {
820                     //Unique, so add it to the list
821                     allKeyList.add(diskKey);
822                 }
823             }
824         }
825         return allKeyList;
826     }
827 
828     /**
829      * Returns a list of all element keys in the cache. Only keys of non-expired
830      * elements are returned.
831      * <p/>
832      * The returned keys are unique and can be considered a set.
833      * <p/>
834      * The List returned is not live. It is a copy.
835      * <p/>
836      * The time taken is O(n), where n is the number of elements in the cache. On
837      * a 1.8Ghz P4, the time taken is approximately 200ms per 1000 entries. This method
838      * is not syncrhonized, because it relies on a non-live list returned from {@link #getKeys()}
839      * , which is synchronised, and which takes 8ms per 1000 entries. This way
840      * cache liveness is preserved, even if this method is very slow to return.
841      * <p/>
842      * Consider whether your usage requires checking for expired keys. Because
843      * this method takes so long, depending on cache settings, the list could be
844      * quite out of date by the time you get it.
845      *
846      * @return a list of {@link Object} keys
847      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
848      */
849     public final List getKeysWithExpiryCheck() throws IllegalStateException, CacheException {
850         List allKeyList = getKeys();
851         //remove keys of expired elements
852         ArrayList nonExpiredKeys = new ArrayList(allKeyList.size());
853         int allKeyListSize = allKeyList.size();
854         for (int i = 0; i < allKeyListSize; i++) {
855             Object key = allKeyList.get(i);
856             Element element = getQuiet(key);
857             if (element != null) {
858                 nonExpiredKeys.add(key);
859             }
860         }
861         nonExpiredKeys.trimToSize();
862         return nonExpiredKeys;
863     }
864 
865 
866     /**
867      * Returns a list of all elements in the cache, whether or not they are expired.
868      * <p/>
869      * The returned keys are not unique and may contain duplicates. If the cache is only
870      * using the memory store, the list will be unique. If the disk store is being used
871      * as well, it will likely contain duplicates, because of the internal store design.
872      * <p/>
873      * The List returned is not live. It is a copy.
874      * <p/>
875      * The time taken is O(log n). On a single cpu 1.8Ghz P4, approximately 6ms is required
876      * for 1000 entries and 36 for 50000.
877      * <p/>
878      * This is the fastest getKeys method
879      *
880      * @return a list of {@link Object} keys
881      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
882      */
883     public final synchronized List getKeysNoDuplicateCheck() throws IllegalStateException {
884         checkStatus();
885         ArrayList allKeys = new ArrayList();
886         List memoryKeySet = Arrays.asList(memoryStore.getKeyArray());
887         allKeys.addAll(memoryKeySet);
888         if (configuration.isOverflowToDisk()) {
889             List diskKeySet = Arrays.asList(diskStore.getKeyArray());
890             allKeys.addAll(diskKeySet);
891         }
892         return allKeys;
893     }
894 
895     private Element searchInMemoryStore(Object key, boolean updateStatistics) {
896         Element element;
897         if (updateStatistics) {
898             element = memoryStore.get(key);
899         } else {
900             element = memoryStore.getQuiet(key);
901         }
902         if (element != null) {
903             if (isExpired(element)) {
904                 if (LOG.isDebugEnabled()) {
905                     LOG.debug(configuration.getName() + " Memory cache hit, but element expired");
906                 }
907                 missCountExpired++;
908                 remove(key, true, true, false);
909                 element = null;
910             } else {
911                 memoryStoreHitCount++;
912             }
913         }
914         return element;
915     }
916 
917     private Element searchInDiskStore(Object key, boolean updateStatistics) {
918         if (!(key instanceof Serializable)) {
919             return null;
920         }
921         Serializable serializableKey = (Serializable) key;
922         Element element;
923         if (updateStatistics) {
924             element = diskStore.get(serializableKey);
925         } else {
926             element = diskStore.getQuiet(serializableKey);
927         }
928         if (element != null) {
929             if (isExpired(element)) {
930                 if (LOG.isDebugEnabled()) {
931                     LOG.debug(configuration.getName() + " cache - Disk Store hit, but element expired");
932                 }
933                 missCountExpired++;
934                 remove(key, true, true, false);
935                 element = null;
936             } else {
937                 diskStoreHitCount++;
938                 //Put the item back into memory to preserve policies in the memory store and to save updated statistics
939                 memoryStore.put(element);
940             }
941         }
942         return element;
943     }
944 
945     /**
946      * Removes an {@link Element} from the Cache. This also removes it from any
947      * stores it may be in.
948      * <p/>
949      * Also notifies the CacheEventListener after the element was removed, but only if an Element
950      * with the key actually existed.
951      * <p/>
952      * Synchronization is handled within the method.
953      * <p/>
954      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
955      * This exception should be caught in those cirucmstances.
956      *
957      * @param key the element key to operate on
958      * @return true if the element was removed, false if it was not found in the cache
959      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
960      */
961     public final boolean remove(Serializable key) throws IllegalStateException {
962         return remove((Object) key);
963     }
964 
965     /**
966      * Removes an {@link Element} from the Cache. This also removes it from any
967      * stores it may be in.
968      * <p/>
969      * Also notifies the CacheEventListener after the element was removed, but only if an Element
970      * with the key actually existed.
971      * <p/>
972      * Synchronization is handled within the method.
973      * <p/>
974      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
975      * This exception should be caught in those cirucmstances.
976      * <p/>
977      *
978      * @param key the element key to operate on
979      * @return true if the element was removed, false if it was not found in the cache
980      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
981      * @since 1.2
982      */
983     public final boolean remove(Object key) throws IllegalStateException {
984         return remove(key, false);
985     }
986 
987 
988     /**
989      * Removes an {@link Element} from the Cache. This also removes it from any
990      * stores it may be in.
991      * <p/>
992      * Also notifies the CacheEventListener after the element was removed, but only if an Element
993      * with the key actually existed.
994      * <p/>
995      * Synchronization is handled within the method.
996      * <p/>
997      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
998      * This exception should be caught in those cirucmstances.
999      *
1000      * @param key                         the element key to operate on
1001      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1002      *                                    further notification to doNotNotifyCacheReplicators cache peers
1003      * @return true if the element was removed, false if it was not found in the cache
1004      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1005      * @noinspection SameParameterValue
1006      */
1007     public final boolean remove(Serializable key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
1008         return remove((Object) key, doNotNotifyCacheReplicators);
1009     }
1010 
1011     /**
1012      * Removes an {@link Element} from the Cache. This also removes it from any
1013      * stores it may be in.
1014      * <p/>
1015      * Also notifies the CacheEventListener after the element was removed, but only if an Element
1016      * with the key actually existed.
1017      * <p/>
1018      * Synchronization is handled within the method.
1019      *
1020      * @param key                         the element key to operate on
1021      * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1022      *                                    further notification to doNotNotifyCacheReplicators cache peers
1023      * @return true if the element was removed, false if it was not found in the cache
1024      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1025      */
1026     public final boolean remove(Object key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
1027         return remove(key, false, true, doNotNotifyCacheReplicators);
1028     }
1029 
1030     /**
1031      * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
1032      * stores it may be in.
1033      * <p/>
1034      * Synchronization is handled within the method.
1035      *
1036      * @param key the element key to operate on
1037      * @return true if the element was removed, false if it was not found in the cache
1038      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1039      */
1040     public final boolean removeQuiet(Serializable key) throws IllegalStateException {
1041         return remove(key, false, false, false);
1042     }
1043 
1044     /**
1045      * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
1046      * stores it may be in.
1047      * <p/>
1048      * Synchronization is handled within the method.
1049      * <p/>
1050      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1051      * This exception should be caught in those cirucmstances.
1052      *
1053      * @param key the element key to operate on
1054      * @return true if the element was removed, false if it was not found in the cache
1055      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1056      * @since 1.2
1057      */
1058     public final boolean removeQuiet(Object key) throws IllegalStateException {
1059         return remove(key, false, false, false);
1060     }
1061 
1062 
1063     /**
1064      * Removes or expires an {@link Element} from the Cache after an attempt to get it determined that it should be expired.
1065      * This also removes it from any stores it may be in.
1066      * <p/>
1067      * Also notifies the CacheEventListener after the element has expired, but only if an Element
1068      * with the key actually existed.
1069      * <p/>
1070      * Synchronization is handled within the method.
1071      * <p/>
1072      * If a remove was called, listeners are notified, regardless of whether the element existed or not.
1073      * This allows distributed cache listeners to remove elements from a cluster regardless of whether they
1074      * existed locally.
1075      * <p/>
1076      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1077      * This exception should be caught in those cirucmstances.
1078      *
1079      * @param key                         the element key to operate on
1080      * @param expiry                      if the reason this method is being called is to expire the element
1081      * @param notifyListeners             whether to notify listeners
1082      * @param doNotNotifyCacheReplicators whether not to notify cache replicators
1083      * @return true if the element was removed, false if it was not found in the cache
1084      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1085      */
1086     private boolean remove(Object key, boolean expiry, boolean notifyListeners,
1087                            boolean doNotNotifyCacheReplicators)
1088             throws IllegalStateException {
1089         checkStatus();
1090         boolean removed = false;
1091         Element elementFromMemoryStore;
1092         Element elementFromDiskStore;
1093         synchronized (this) {
1094             elementFromMemoryStore = memoryStore.remove(key);
1095 
1096             //could have been removed from both places, if there are two copies in the cache
1097             elementFromDiskStore = null;
1098             if (configuration.isOverflowToDisk()) {
1099                 if ((key instanceof Serializable)) {
1100                     Serializable serializableKey = (Serializable) key;
1101                     elementFromDiskStore = diskStore.remove(serializableKey);
1102                 }
1103 
1104             }
1105         }
1106 
1107         boolean removeNotified = false;
1108 
1109         if (elementFromMemoryStore != null) {
1110             if (notifyListeners) {
1111                 if (expiry) {
1112                     registeredEventListeners.notifyElementExpiry(elementFromMemoryStore, doNotNotifyCacheReplicators);
1113                 } else {
1114                     removeNotified = true;
1115                     registeredEventListeners.notifyElementRemoved(elementFromMemoryStore, doNotNotifyCacheReplicators);
1116                 }
1117             }
1118             removed = true;
1119         }
1120         if (elementFromDiskStore != null) {
1121             if (expiry) {
1122                 registeredEventListeners.notifyElementExpiry(elementFromDiskStore, doNotNotifyCacheReplicators);
1123             } else {
1124                 removeNotified = true;
1125                 registeredEventListeners.notifyElementRemoved(elementFromDiskStore, doNotNotifyCacheReplicators);
1126             }
1127             removed = true;
1128         }
1129         //If we are trying to remove an element which does not exist locally, we should still notify so that
1130         //cluster invalidations work.
1131         if (!expiry && !removeNotified) {
1132             Element syntheticElement = new Element(key, null);
1133             registeredEventListeners.notifyElementRemoved(syntheticElement, doNotNotifyCacheReplicators);
1134         }
1135 
1136         return removed;
1137     }
1138 
1139     /**
1140      * Removes all cached items.
1141      * Synchronization is handled within the method.
1142      * <p/>
1143      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1144      * This exception should be caught in those cirucmstances.
1145      *
1146      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1147      */
1148     public void removeAll() throws IllegalStateException, CacheException {
1149         removeAll(false);
1150     }
1151 
1152 
1153     /**
1154      * Removes all cached items.
1155      * Synchronization is handled within the method.
1156      * <p/>
1157      * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1158      * This exception should be caught in those cirucmstances.
1159      *
1160      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1161      */
1162     public void removeAll(boolean doNotNotifyCacheReplicators) throws IllegalStateException, CacheException {
1163         checkStatus();
1164         synchronized (this) {
1165             memoryStore.removeAll();
1166             if (configuration.isOverflowToDisk()) {
1167                 diskStore.removeAll();
1168             }
1169         }
1170         registeredEventListeners.notifyRemoveAll(doNotNotifyCacheReplicators);
1171     }
1172 
1173     /**
1174      * Flushes all cache items from memory to auxilliary caches and close the auxilliary caches.
1175      * <p/>
1176      * Should be invoked only by CacheManager.
1177      *
1178      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1179      */
1180     public synchronized void dispose() throws IllegalStateException {
1181         checkStatus();
1182         memoryStore.dispose();
1183         memoryStore = null;
1184         if (configuration.isOverflowToDisk()) {
1185             diskStore.dispose();
1186             diskStore = null;
1187         }
1188 
1189         registeredEventListeners.dispose();
1190 
1191         changeStatus(Status.STATUS_SHUTDOWN);
1192     }
1193 
1194     /**
1195      * Gets the cache configuration this cache was created with.
1196      * <p/>
1197      * Things like listeners that are added dynamically are excluded.
1198      */
1199     public CacheConfiguration getCacheConfiguration() {
1200         return configuration;
1201     }
1202 
1203 
1204     /**
1205      * Flushes all cache items from memory to the disk store, and from the DiskStore to disk.
1206      *
1207      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1208      */
1209     public final synchronized void flush() throws IllegalStateException, CacheException {
1210         checkStatus();
1211         try {
1212             memoryStore.flush();
1213             if (configuration.isOverflowToDisk()) {
1214                 diskStore.flush();
1215             }
1216         } catch (IOException e) {
1217             throw new CacheException("Unable to flush cache: " + configuration.getName()
1218                     + ". Initial cause was " + e.getMessage(), e);
1219         }
1220     }
1221 
1222 
1223     /**
1224      * Gets the size of the cache. This is a subtle concept. See below.
1225      * <p/>
1226      * The size is the number of {@link Element}s in the {@link MemoryStore} plus
1227      * the number of {@link Element}s in the {@link DiskStore}.
1228      * <p/>
1229      * This number is the actual number of elements, including expired elements that have
1230      * not been removed.
1231      * <p/>
1232      * Expired elements are removed from the the memory store when
1233      * getting an expired element, or when attempting to spool an expired element to
1234      * disk.
1235      * <p/>
1236      * Expired elements are removed from the disk store when getting an expired element,
1237      * or when the expiry thread runs, which is once every five minutes.
1238      * <p/>
1239      * To get an exact size, which would exclude expired elements, use {@link #getKeysWithExpiryCheck()}.size(),
1240      * although see that method for the approximate time that would take.
1241      * <p/>
1242      * To get a very fast result, use {@link #getKeysNoDuplicateCheck()}.size(). If the disk store
1243      * is being used, there will be some duplicates.
1244      *
1245      * @return The size value
1246      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1247      */
1248     public final synchronized int getSize() throws IllegalStateException, CacheException {
1249         checkStatus();
1250         /* The memory store and the disk store can simultaneously contain elements with the same key
1251             Cache size is the size of the union of the two key sets.*/
1252         return getKeys().size();
1253     }
1254 
1255     /**
1256      * Gets the size of the memory store for this cache. This method relies on calculating
1257      * Serialized sizes. If the Element values are not Serializable they will show as zero.
1258      * <p/>
1259      * Warning: This method can be very expensive to run. Allow approximately 1 second
1260      * per 1MB of entries. Running this method could create liveness problems
1261      * because the object lock is held for a long period
1262      * <p/>
1263      *
1264      * @return the approximate size of the memory store in bytes
1265      * @throws IllegalStateException
1266      */
1267     public final synchronized long calculateInMemorySize() throws IllegalStateException, CacheException {
1268         checkStatus();
1269         return memoryStore.getSizeInBytes();
1270     }
1271 
1272 
1273     /**
1274      * Returns the number of elements in the memory store.
1275      *
1276      * @return the number of elements in the memory store
1277      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1278      */
1279     public final long getMemoryStoreSize() throws IllegalStateException {
1280         checkStatus();
1281         return memoryStore.getSize();
1282     }
1283 
1284     /**
1285      * Returns the number of elements in the disk store.
1286      *
1287      * @return the number of elements in the disk store.
1288      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1289      */
1290     public final int getDiskStoreSize() throws IllegalStateException {
1291         checkStatus();
1292         if (configuration.isOverflowToDisk()) {
1293             return diskStore.getSize();
1294         } else {
1295             return 0;
1296         }
1297     }
1298 
1299     /**
1300      * Gets the status attribute of the Cache.
1301      *
1302      * @return The status value from the Status enum class
1303      */
1304     public final Status getStatus() {
1305         return status;
1306     }
1307 
1308 
1309     private void checkStatus() throws IllegalStateException {
1310         if (!status.equals(Status.STATUS_ALIVE)) {
1311             throw new IllegalStateException("The " + configuration.getName() + " Cache is not alive.");
1312         }
1313     }
1314 
1315 
1316     /**
1317      * The number of times a requested item was found in the cache.
1318      * <p/>
1319      * The internal representation of this statistic has been changed to a long
1320      * to cater for real world situations when an int is not sufficient to hold it.
1321      * If the statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value
1322      * will be returned. Use {@link net.sf.ehcache.Statistics} which contains this statistic.
1323      * <p/>
1324      *
1325      * @return the number of times a requested item was found in the cache
1326      * @deprecated Use {@link net.sf.ehcache.Statistics}
1327      */
1328     public final int getHitCount() {
1329         return (int) hitCount;
1330     }
1331 
1332     /**
1333      * Number of times a requested item was found in the Memory Store.
1334      * <p/>
1335      * The internal representation of this statistic has been changed to a long
1336      * to cater for real world situations when an int is not sufficient to hold it.
1337      * If the statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value
1338      * will be returned. Use {@link net.sf.ehcache.Statistics} which contains this statistic.
1339      * <p/>
1340      *
1341      * @return Number of times a requested item was found in the Memory Store.
1342      * @deprecated Use {@link net.sf.ehcache.Statistics}
1343      */
1344     public final int getMemoryStoreHitCount() {
1345         return (int) memoryStoreHitCount;
1346     }
1347 
1348     /**
1349      * Number of times a requested item was found in the Disk Store.
1350      * <p/>
1351      * The internal representation of this statistic has been changed to a long
1352      * to cater for real world situations when an int is not sufficient to hold it.
1353      * If the statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value
1354      * will be returned. Use {@link net.sf.ehcache.Statistics} which contains this statistic.
1355      * <p/>
1356      *
1357      * @deprecated Use {@link net.sf.ehcache.Statistics}
1358      */
1359     public final int getDiskStoreHitCount() {
1360         return (int) diskStoreHitCount;
1361     }
1362 
1363     /**
1364      * Number of times a requested element was not found in the cache. This
1365      * may be because it expired, in which case this will also be recorded in {@link #getMissCountExpired},
1366      * or because it was simply not there.
1367      * <p/>
1368      * The internal representation of this statistic has been changed to a long
1369      * to cater for real world situations when an int is not sufficient to hold it.
1370      * If the statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value
1371      * will be returned. Use {@link net.sf.ehcache.Statistics} which contains this statistic.
1372      * <p/>
1373      *
1374      * @deprecated Use {@link net.sf.ehcache.Statistics}
1375      */
1376     public final int getMissCountNotFound() {
1377         return (int) missCountNotFound;
1378     }
1379 
1380     /**
1381      * Number of times a requested element was found but was expired.
1382      * <p/>
1383      * The internal representation of this statistic has been changed to a long
1384      * to cater for real world situations when an int is not sufficient to hold it.
1385      * If the statistic is larger than <code>Integer.MAX_VALUE</code> a nonsensical value
1386      * will be returned. Use {@link net.sf.ehcache.Statistics} which contains this statistic.
1387      * <p/>
1388      *
1389      * @deprecated Use {@link net.sf.ehcache.Statistics}
1390      */
1391     public final int getMissCountExpired() {
1392         return (int) missCountExpired;
1393     }
1394 
1395     /**
1396      * Gets the cache name.
1397      */
1398     public final String getName() {
1399         return configuration.getName();
1400     }
1401 
1402     /**
1403      * Sets the cache name which will name.
1404      *
1405      * @param name the name of the cache. Should not be null. Should also not contain any '/' characters, as these interfere
1406      *             with distribution
1407      * @throws IllegalArgumentException if an illegal name is used.
1408      */
1409     public final void setName(String name) throws IllegalArgumentException {
1410         if (!status.equals(Status.STATUS_UNINITIALISED)) {
1411             throw new IllegalStateException("Only unitialised caches can have their names set.");
1412         }
1413         configuration.setName(name);
1414     }
1415 
1416     /**
1417      * Gets timeToIdleSeconds.
1418      *
1419      * @deprecated Get this from the configuration
1420      */
1421     public final long getTimeToIdleSeconds() {
1422         return configuration.getTimeToIdleSeconds();
1423     }
1424 
1425     /**
1426      * Gets timeToLiveSeconds.
1427      *
1428      * @deprecated Get this from the configuration
1429      */
1430     public final long getTimeToLiveSeconds() {
1431         return configuration.getTimeToLiveSeconds();
1432     }
1433 
1434     /**
1435      * Are elements eternal.
1436      *
1437      * @deprecated Get this from the configuration
1438      */
1439     public final boolean isEternal() {
1440         return configuration.isEternal();
1441     }
1442 
1443     /**
1444      * Does the overflow go to disk.
1445      *
1446      * @deprecated Get this from the configuration
1447      */
1448     public final boolean isOverflowToDisk() {
1449         return configuration.isOverflowToDisk();
1450     }
1451 
1452     /**
1453      * Gets the maximum number of elements to hold in memory.
1454      *
1455      * @deprecated Get this from the configuration
1456      */
1457     public final int getMaxElementsInMemory() {
1458         return configuration.getMaxElementsInMemory();
1459     }
1460 
1461     /**
1462      * Gets the maximum number of elements to hold on Disk
1463      *
1464      * @deprecated Get this from the configuration
1465      */
1466     public int getMaxElementsOnDisk() {
1467         return configuration.getMaxElementsOnDisk();
1468     }
1469 
1470     /**
1471      * The policy used to evict elements from the {@link net.sf.ehcache.store.MemoryStore}.
1472      * This can be one of:
1473      * <ol>
1474      * <li>LRU - least recently used
1475      * <li>LFU - least frequently used
1476      * <li>FIFO - first in first out, the oldest element by creation time
1477      * </ol>
1478      * The default value is LRU
1479      *
1480      * @since 1.2
1481      * @deprecated Get this from the configuration
1482      */
1483     public final MemoryStoreEvictionPolicy getMemoryStoreEvictionPolicy() {
1484         return configuration.getMemoryStoreEvictionPolicy();
1485     }
1486 
1487     /**
1488      * @return true if the cache overflows to disk and the disk is persistent between restarts
1489      * @deprecated Get this from the configuration     *
1490      */
1491     public final boolean isDiskPersistent() {
1492         return configuration.isDiskPersistent();
1493     }
1494 
1495     /**
1496      * @return the interval between runs
1497      *         of the expiry thread, where it checks the disk store for expired elements. It is not the
1498      *         the timeToLiveSeconds.
1499      * @deprecated Get this from the configuration
1500      */
1501     public final long getDiskExpiryThreadIntervalSeconds() {
1502         return configuration.getDiskExpiryThreadIntervalSeconds();
1503     }
1504 
1505 
1506     /**
1507      * Returns a {@link String} representation of {@link Cache}.
1508      */
1509     public final String toString() {
1510         StringBuffer dump = new StringBuffer();
1511 
1512         dump.append("[")
1513                 .append(" name = ").append(configuration.getName())
1514                 .append(" status = ").append(status)
1515                 .append(" eternal = ").append(configuration.isEternal())
1516                 .append(" overflowToDisk = ").append(configuration.isOverflowToDisk())
1517                 .append(" maxElementsInMemory = ").append(configuration.getMaxElementsInMemory())
1518                 .append(" maxElementsOnDisk = ").append(configuration.getMaxElementsOnDisk())
1519                 .append(" memoryStoreEvictionPolicy = ").append(configuration.getMemoryStoreEvictionPolicy())
1520                 .append(" timeToLiveSeconds = ").append(configuration.getTimeToLiveSeconds())
1521                 .append(" timeToIdleSeconds = ").append(configuration.getTimeToIdleSeconds())
1522                 .append(" diskPersistent = ").append(configuration.isDiskPersistent())
1523                 .append(" diskExpiryThreadIntervalSeconds = ").append(configuration.getDiskExpiryThreadIntervalSeconds())
1524                 .append(registeredEventListeners)
1525                 .append(" hitCount = ").append(hitCount)
1526                 .append(" memoryStoreHitCount = ").append(memoryStoreHitCount)
1527                 .append(" diskStoreHitCount = ").append(diskStoreHitCount)
1528                 .append(" missCountNotFound = ").append(missCountNotFound)
1529                 .append(" missCountExpired = ").append(missCountExpired)
1530                 .append(" ]");
1531 
1532         return dump.toString();
1533     }
1534 
1535 
1536     /**
1537      * Checks whether this cache element has expired.
1538      * <p/>
1539      * The element is expired if:
1540      * <ol>
1541      * <li> the idle time is non-zero and has elapsed, unless the cache is eternal; or
1542      * <li> the time to live is non-zero and has elapsed, unless the cache is eternal; or
1543      * <li> the value of the element is null.
1544      * </ol>
1545      *
1546      * @return true if it has expired
1547      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1548      * @throws NullPointerException  if the element is null
1549      */
1550     public final boolean isExpired(Element element) throws IllegalStateException, NullPointerException {
1551         checkStatus();
1552         synchronized (element) {
1553             return element.isExpired();
1554         }
1555     }
1556 
1557 
1558     /**
1559      * Clones a cache. This is only legal if the cache has not been
1560      * initialized. At that point only primitives have been set and no
1561      * {@link net.sf.ehcache.store.LruMemoryStore} or {@link net.sf.ehcache.store.DiskStore} has been created.
1562      * <p/>
1563      * A new, empty, RegisteredEventListeners is created on clone.
1564      * <p/>
1565      *
1566      * @return an object of type {@link Cache}
1567      * @throws CloneNotSupportedException
1568      */
1569     public final Object clone() throws CloneNotSupportedException {
1570         if (!(memoryStore == null && diskStore == null)) {
1571             throw new CloneNotSupportedException("Cannot clone an initialized cache.");
1572         }
1573         Cache copy = (Cache) super.clone();
1574         copy.configuration = (CacheConfiguration) configuration.clone();
1575         copy.guid = createGuid();
1576 
1577         RegisteredEventListeners registeredEventListenersFromCopy = copy.getCacheEventNotificationService();
1578         if (registeredEventListenersFromCopy == null || registeredEventListenersFromCopy.getCacheEventListeners().size() == 0) {
1579             copy.registeredEventListeners = new RegisteredEventListeners(copy);
1580         } else {
1581             copy.registeredEventListeners = new RegisteredEventListeners(copy);
1582             Set cacheEventListeners = registeredEventListeners.getCacheEventListeners();
1583             for (Iterator iterator = cacheEventListeners.iterator(); iterator.hasNext();) {
1584                 CacheEventListener cacheEventListener = (CacheEventListener) iterator.next();
1585                 CacheEventListener cacheEventListenerClone = (CacheEventListener) cacheEventListener.clone();
1586                 copy.registeredEventListeners.registerListener(cacheEventListenerClone);
1587             }
1588         }
1589 
1590         if (bootstrapCacheLoader != null) {
1591             BootstrapCacheLoader bootstrapCacheLoaderClone = (BootstrapCacheLoader) bootstrapCacheLoader.clone();
1592             copy.setBootstrapCacheLoader(bootstrapCacheLoaderClone);
1593         }
1594 
1595         return copy;
1596     }
1597 
1598     /**
1599      * Gets the internal DiskStore.
1600      *
1601      * @return the DiskStore referenced by this cache
1602      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1603      */
1604     final Store getDiskStore() throws IllegalStateException {
1605         checkStatus();
1606         return diskStore;
1607     }
1608 
1609     /**
1610      * Gets the internal MemoryStore.
1611      *
1612      * @return the MemoryStore referenced by this cache
1613      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1614      */
1615     final MemoryStore getMemoryStore() throws IllegalStateException {
1616         checkStatus();
1617         return memoryStore;
1618     }
1619 
1620 
1621     /**
1622      * Use this to access the service in order to register and unregister listeners
1623      *
1624      * @return the RegisteredEventListeners instance for this cache.
1625      */
1626     public final RegisteredEventListeners getCacheEventNotificationService() {
1627         return registeredEventListeners;
1628     }
1629 
1630 
1631     /**
1632      * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
1633      *
1634      * @return true if an element matching the key is found in memory
1635      */
1636     public final boolean isElementInMemory(Serializable key) {
1637         return isElementInMemory((Object) key);
1638     }
1639 
1640     /**
1641      * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
1642      *
1643      * @return true if an element matching the key is found in memory
1644      * @since 1.2
1645      */
1646     public final boolean isElementInMemory(Object key) {
1647         return memoryStore.containsKey(key);
1648     }
1649 
1650     /**
1651      * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
1652      *
1653      * @return true if an element matching the key is found in the diskStore
1654      */
1655     public final boolean isElementOnDisk(Serializable key) {
1656         return isElementOnDisk((Object) key);
1657     }
1658 
1659     /**
1660      * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
1661      *
1662      * @return true if an element matching the key is found in the diskStore
1663      * @since 1.2
1664      */
1665     public final boolean isElementOnDisk(Object key) {
1666         if (!(key instanceof Serializable)) {
1667             return false;
1668         }
1669         Serializable serializableKey = (Serializable) key;
1670         if (!configuration.isOverflowToDisk()) {
1671             return false;
1672         } else {
1673             return diskStore != null && diskStore.containsKey(serializableKey);
1674         }
1675     }
1676 
1677     /**
1678      * The GUID for this cache instance can be used to determine whether two cache instance references
1679      * are pointing to the same cache.
1680      *
1681      * @return the globally unique identifier for this cache instance. This is guaranteed to be unique.
1682      * @since 1.2
1683      */
1684     public final String getGuid() {
1685         return guid;
1686     }
1687 
1688     /**
1689      * Gets the CacheManager managing this cache. For a newly created cache this will be null until
1690      * it has been added to a CacheManager.
1691      *
1692      * @return the manager or null if there is none
1693      */
1694     public final CacheManager getCacheManager() {
1695         return cacheManager;
1696     }
1697 
1698 
1699     /**
1700      * Resets statistics counters back to 0.
1701      *
1702      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1703      */
1704     public synchronized void clearStatistics() throws IllegalStateException {
1705         checkStatus();
1706         hitCount = 0;
1707         memoryStoreHitCount = 0;
1708         diskStoreHitCount = 0;
1709         missCountExpired = 0;
1710         missCountNotFound = 0;
1711     }
1712 
1713     /**
1714      * Accurately measuring statistics can be expensive. Returns the current accuracy setting.
1715      *
1716      * @return one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
1717      */
1718     public int getStatisticsAccuracy() {
1719         return statisticsAccuracy;
1720     }
1721 
1722     /**
1723      * Sets the statistics accuracy.
1724      *
1725      * @param statisticsAccuracy one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
1726      */
1727     public void setStatisticsAccuracy(int statisticsAccuracy) {
1728         this.statisticsAccuracy = statisticsAccuracy;
1729     }
1730 
1731     /**
1732      * Causes all elements stored in the Cache to be synchronously checked for expiry, and if expired, evicted.
1733      */
1734     public void evictExpiredElements() {
1735         Object[] keys = memoryStore.getKeyArray();
1736         synchronized (this) {
1737             for (int i = 0; i < keys.length; i++) {
1738                 Object key = keys[i];
1739                 searchInMemoryStore(key, false);
1740             }
1741         }
1742         //This is called regularly by the expiry thread, but call it here synchronously
1743         if (configuration.isOverflowToDisk()) {
1744             diskStore.expireElements();
1745         }
1746     }
1747 
1748     /**
1749      * An inexpensive check to see if the key exists in the cache.
1750      * <p/>
1751      * This method is not synchronized. It is possible that an element may exist in the cache aned be removed
1752      * before the check gets to it, or vice versa.
1753      *
1754      * @param key the key to check.
1755      * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
1756      */
1757     public boolean isKeyInCache(Object key) {
1758         return isElementInMemory(key) || isElementOnDisk(key);
1759     }
1760 
1761     /**
1762      * An extremely expensive check to see if the value exists in the cache. This implementation is O(n). Ehcache
1763      * is not designed for efficient access in this manner.
1764      * <p/>
1765      * This method is not synchronized. It is possible that an element may exist in the cache aned be removed
1766      * before the check gets to it, or vice versa. Because it is slow to execute the probability of that this will
1767      * have happened.
1768      *
1769      * @param value to check for
1770      * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
1771      */
1772     public boolean isValueInCache(Object value) {
1773         boolean isSerializable = value instanceof Serializable;
1774         List keys;
1775         if (isSerializable) {
1776             keys = getKeys();
1777         } else {
1778             keys = Arrays.asList(memoryStore.getKeyArray());
1779         }
1780 
1781         for (int i = 0; i < keys.size(); i++) {
1782             Object key = keys.get(i);
1783             Element element = get(key);
1784             if (element != null) {
1785                 Object elementValue = element.getValue();
1786                 if (elementValue == null) {
1787                     if (value == null) {
1788                         return true;
1789                     }
1790                 } else {
1791                     if (elementValue.equals(value)) {
1792                         return true;
1793                     }
1794                 }
1795             }
1796         }
1797         return false;
1798     }
1799 
1800     /**
1801      * {@inheritDoc}
1802      * <p/>
1803      * Note, the {@link #getSize} method will have the same value as the size reported by Statistics
1804      * for the statistics accuracy of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}.
1805      */
1806     public Statistics getStatistics() throws IllegalStateException {
1807         int size = 0;
1808         if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_BEST_EFFORT) {
1809             size = getSize();
1810         } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_GUARANTEED) {
1811             size = getKeysWithExpiryCheck().size();
1812         } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_NONE) {
1813             size = getKeysNoDuplicateCheck().size();
1814         }
1815         return new Statistics(this, statisticsAccuracy, hitCount, diskStoreHitCount, memoryStoreHitCount,
1816                 missCountExpired + missCountNotFound, size);
1817     }
1818 
1819     /**
1820      * For use by CacheManager.
1821      *
1822      * @param cacheManager the CacheManager for this cache to use.
1823      */
1824     public void setCacheManager(CacheManager cacheManager) {
1825         this.cacheManager = cacheManager;
1826     }
1827 
1828     /**
1829      * Accessor for the BootstrapCacheLoader associated with this cache. For testing purposes.
1830      */
1831     public BootstrapCacheLoader getBootstrapCacheLoader() {
1832         return bootstrapCacheLoader;
1833     }
1834 
1835     /**
1836      * Sets the bootstrap cache loader.
1837      *
1838      * @param bootstrapCacheLoader the loader to be used
1839      * @throws CacheException if this method is called after the cache is initialized
1840      */
1841     public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader) throws CacheException {
1842         if (!status.equals(Status.STATUS_UNINITIALISED)) {
1843             throw new CacheException("A bootstrap cache loader can only be set before the cache is initialized. "
1844                     + configuration.getName());
1845         }
1846         this.bootstrapCacheLoader = bootstrapCacheLoader;
1847     }
1848 
1849     /**
1850      * DiskStore paths can conflict between CacheManager instances. This method allows the path to be changed.
1851      *
1852      * @param diskStorePath the new path to be used.
1853      * @throws CacheException if this method is called after the cache is initialized
1854      */
1855     public void setDiskStorePath(String diskStorePath) throws CacheException {
1856         if (!status.equals(Status.STATUS_UNINITIALISED)) {
1857             throw new CacheException("A DiskStore path can only be set before the cache is initialized. "
1858                     + configuration.getName());
1859         }
1860         this.diskStorePath = diskStorePath;
1861     }
1862 
1863     /**
1864      * An equals method which follows the contract of {@link Object#equals(Object)}
1865      *
1866      * @param object the reference object with which to compare.
1867      * @return <code>true</code> if this object is the same as the obj
1868      *         argument; <code>false</code> otherwise. Same for a Cache means, the same GUID
1869      * @see #hashCode()
1870      * @see java.util.Hashtable
1871      */
1872     public boolean equals(Object object) {
1873         if (object == null) {
1874             return false;
1875         }
1876         if (!(object instanceof Ehcache)) {
1877             return false;
1878         }
1879         Ehcache other = (Ehcache) object;
1880         return guid.equals(other.getGuid());
1881     }
1882 
1883     /**
1884      * Returns a hash code value for the object. This method is
1885      * supported for the benefit of hashtables such as those provided by
1886      * <code>java.util.Hashtable</code>.
1887      * <p/>
1888      * The general contract of <code>hashCode</code> is:
1889      * <ul>
1890      * <li>Whenever it is invoked on the same object more than once during
1891      * an execution of a Java application, the <tt>hashCode</tt> method
1892      * must consistently return the same integer, provided no information
1893      * used in <tt>equals</tt> comparisons on the object is modified.
1894      * This integer need not remain consistent from one execution of an
1895      * application to another execution of the same application.
1896      * <li>If two objects are equal according to the <tt>equals(Object)</tt>
1897      * method, then calling the <code>hashCode</code> method on each of
1898      * the two objects must produce the same integer result.
1899      * <li>It is <em>not</em> required that if two objects are unequal
1900      * according to the {@link Object#equals(Object)}
1901      * method, then calling the <tt>hashCode</tt> method on each of the
1902      * two objects must produce distinct integer results.  However, the
1903      * programmer should be aware that producing distinct integer results
1904      * for unequal objects may improve the performance of hashtables.
1905      * </ul>
1906      * <p/>
1907      * As much as is reasonably practical, the hashCode method defined by
1908      * class <tt>Object</tt> does return distinct integers for distinct
1909      * objects. (This is typically implemented by converting the internal
1910      * address of the object into an integer, but this implementation
1911      * technique is not required by the
1912      * Java<font size="-2"><sup>TM</sup></font> programming language.)
1913      * <p/>
1914      * This implementation use the GUID of the cache.
1915      *
1916      * @return a hash code value for this object.
1917      * @see Object#equals(Object)
1918      * @see java.util.Hashtable
1919      */
1920     public int hashCode() {
1921         return guid.hashCode();
1922     }
1923 
1924 
1925     /**
1926      * Create globally unique ID for this cache.
1927      */
1928     private String createGuid() {
1929         return new StringBuffer().append(localhost).append("-").append(new UID()).toString();
1930     }
1931 
1932 }