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 }