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.constructs.blocking;
18
19 import net.sf.ehcache.CacheException;
20 import net.sf.ehcache.CacheManager;
21 import net.sf.ehcache.Ehcache;
22 import net.sf.ehcache.Element;
23 import net.sf.ehcache.Statistics;
24 import net.sf.ehcache.Status;
25 import net.sf.ehcache.config.CacheConfiguration;
26 import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
27 import net.sf.ehcache.constructs.concurrent.ConcurrencyUtil;
28 import net.sf.ehcache.constructs.concurrent.Mutex;
29 import net.sf.ehcache.event.RegisteredEventListeners;
30 import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33
34 import java.io.Serializable;
35 import java.util.List;
36
37
38 /**
39 * A blocking decorator for an Ehcache, backed by a {@link Ehcache}.
40 * <p/>
41 * It allows concurrent read access to elements already in the cache. If the element is null, other
42 * reads will block until an element with the same key is put into the cache.
43 * <p/>
44 * This is useful for constructing read-through or self-populating caches.
45 * <p/>
46 * This implementation uses the {@link Mutex} class from Doug Lea's concurrency package. If you wish to use
47 * this class, you will need the concurrent package in your class path.
48 * <p/>
49 * It features:
50 * <ul>
51 * <li>Excellent liveness.
52 * <li>Fine-grained locking on each element, rather than the cache as a whole.
53 * <li>Scalability to a large number of threads.
54 * </ul>
55 * <p/>
56 * This class has been updated to use Lock striping. The Mutex implementation gives scalability but creates
57 * many Mutex objects of 24 bytes each. Lock striping limits their number to 100.
58 * <p/>
59 * A version of this class is planned which will dynamically use JDK5's concurrency package, which is
60 * based on Doug Lea's, so as to avoid a dependency on his package for JDK5 systems. This will not
61 * be implemented until JDK5 is released on MacOSX and Linux, as JDK5 will be required to compile
62 * it, though any version from JDK1.2 up will be able to run the code, falling back to Doug
63 * Lea's concurrency package, if the JDK5 package is not found in the classpath.
64 * <p/>
65 * The <code>Mutex</code> class does not appear in the JDK5 concurrency package. Doug Lea has
66 * generously offered the following advice:
67 * <p/>
68 * "You should just be able to use ReentrantLock here. We supply
69 * ReentrantLock, but not Mutex because the number of cases where a
70 * non-reentrant mutex is preferable is small, and most people are more
71 * familiar with reentrant seamantics. If you really need a non-reentrant
72 * one, the javadocs for class AbstractQueuedSynchronizer include sample
73 * code for them."
74 * <p/>
75 * -Doug
76 * <p/>
77 * "Hashtable / synchronizedMap uses the "one big fat lock" approach to guard the mutable state of the map.
78 * That works, but is a big concurrency bottleneck, as you've observed. You went to the opposite extreme, one lock per key.
79 * That works (as long as you've got sufficient synchronization in the cache itself to protect its own data structures.)
80 * <p/>
81 * Lock striping is a middle ground, partitioning keys into a fixed number of subsets, like the trick used at large
82 * theaters for will-call ticket pickup -- there are separate lines for "A-F, G-M, N-R, and S-Z".
83 * This way, there are a fixed number of locks, each guarding (hopefully) 1/Nth of the keys."
84 * - Brian Goetz
85 * <p/>
86 * Further improvements to hashing suggested by Joe Bowbeer.
87 *
88 * @author Greg Luck
89 * @version $Id: BlockingCache.java 512 2007-07-10 09:18:45Z gregluck $
90 */
91 public class BlockingCache implements Ehcache {
92
93 /**
94 * The default number of locks to use. Must be a power of 2
95 */
96 public static final int LOCK_NUMBER = 2048;
97
98 private static final Log LOG = LogFactory.getLog(BlockingCache.class.getName());
99
100 /**
101 * Based on the lock striping concept from Brian Goetz. See Java Concurrency in Practice 11.4.3
102 */
103 protected final Mutex[] locks = new Mutex[LOCK_NUMBER];
104
105 {
106 for (int i = 0; i < LOCK_NUMBER; i++) {
107 locks[i] = new Mutex();
108 }
109 }
110
111
112 /**
113 * The backing Cache
114 */
115 protected final Ehcache cache;
116
117 /**
118 * The amount of time to block a thread before a LockTimeoutException is thrown
119 */
120 protected int timeoutMillis;
121
122 /**
123 * Creates a BlockingCache which decorates the supplied cache.
124 *
125 * @param cache a backing ehcache.
126 * @throws CacheException
127 * @since 1.2
128 */
129 public BlockingCache(final Ehcache cache) throws CacheException {
130 this.cache = cache;
131 }
132
133 /**
134 * Retrieve the EHCache backing cache
135 */
136 protected Ehcache getCache() {
137 return cache;
138 }
139
140 /**
141 * Returns this cache's name
142 */
143 public String getName() {
144 return cache.getName();
145 }
146
147 /**
148 * Sets the cache name which will name.
149 *
150 * @param name the name of the cache. Should not be null.
151 */
152 public void setName(String name) {
153 cache.setName(name);
154 }
155
156 /**
157 * Gets timeToIdleSeconds.
158 */
159 public long getTimeToIdleSeconds() {
160 return cache.getTimeToIdleSeconds();
161 }
162
163 /**
164 * Gets timeToLiveSeconds.
165 */
166 public long getTimeToLiveSeconds() {
167 return cache.getTimeToLiveSeconds();
168 }
169
170 /**
171 * Are elements eternal.
172 */
173 public boolean isEternal() {
174 return cache.isEternal();
175 }
176
177 /**
178 * Does the overflow go to disk.
179 */
180 public boolean isOverflowToDisk() {
181 return cache.isOverflowToDisk();
182 }
183
184 /**
185 * Gets the maximum number of elements to hold in memory.
186 */
187 public int getMaxElementsInMemory() {
188 return cache.getMaxElementsInMemory();
189 }
190
191 /**
192 * @return the maximum number of elements on Disk, or 0 if unlimited
193 * @see net.sf.ehcache.Cache#getMaxElementsOnDisk
194 */
195 public int getMaxElementsOnDisk() {
196 return cache.getMaxElementsOnDisk();
197 }
198
199 /**
200 * The policy used to evict elements from the {@link net.sf.ehcache.store.MemoryStore}.
201 * This can be one of:
202 * <ol>
203 * <li>LRU - least recently used
204 * <li>LFU - least frequently used
205 * <li>FIFO - first in first out, the oldest element by creation time
206 * </ol>
207 * The default value is LRU
208 *
209 * @since 1.2
210 */
211 public MemoryStoreEvictionPolicy getMemoryStoreEvictionPolicy() {
212 return cache.getMemoryStoreEvictionPolicy();
213 }
214
215 /**
216 * Checks whether this cache element has expired.
217 * <p/>
218 * The element is expired if:
219 * <ol>
220 * <li> the idle time is non-zero and has elapsed, unless the cache is eternal; or
221 * <li> the time to live is non-zero and has elapsed, unless the cache is eternal; or
222 * <li> the value of the element is null.
223 * </ol>
224 *
225 * @return true if it has expired
226 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
227 * @throws NullPointerException if the element is null
228 */
229 public boolean isExpired(Element element) throws IllegalStateException, NullPointerException {
230 return cache.isExpired(element);
231 }
232
233 /**
234 * Clones a cache. This is only legal if the cache has not been
235 * initialized. At that point only primitives have been set and no
236 * {@link net.sf.ehcache.store.LruMemoryStore} or {@link net.sf.ehcache.store.DiskStore} has been created.
237 * <p/>
238 * A new, empty, RegisteredEventListeners is created on clone.
239 * <p/>
240 *
241 * @return an object of type {@link net.sf.ehcache.Cache}
242 * @throws CloneNotSupportedException
243 */
244 public Object clone() throws CloneNotSupportedException {
245 return super.clone();
246 }
247
248 /**
249 * @return true if the cache overflows to disk and the disk is persistent between restarts
250 */
251 public boolean isDiskPersistent() {
252 return cache.isDiskPersistent();
253 }
254
255 /**
256 * @return the interval between runs
257 * of the expiry thread, where it checks the disk store for expired elements. It is not the
258 * the timeToLiveSeconds.
259 */
260 public long getDiskExpiryThreadIntervalSeconds() {
261 return cache.getDiskExpiryThreadIntervalSeconds();
262 }
263
264 /**
265 * Use this to access the service in order to register and unregister listeners
266 *
267 * @return the RegisteredEventListeners instance for this cache.
268 */
269 public RegisteredEventListeners getCacheEventNotificationService() {
270 return cache.getCacheEventNotificationService();
271 }
272
273 /**
274 * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
275 *
276 * @return true if an element matching the key is found in memory
277 */
278 public boolean isElementInMemory(Serializable key) {
279 return cache.isElementInMemory(key);
280 }
281
282 /**
283 * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
284 *
285 * @return true if an element matching the key is found in memory
286 * @since 1.2
287 */
288 public boolean isElementInMemory(Object key) {
289 return cache.isElementInMemory(key);
290 }
291
292 /**
293 * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
294 *
295 * @return true if an element matching the key is found in the diskStore
296 */
297 public boolean isElementOnDisk(Serializable key) {
298 return cache.isElementOnDisk(key);
299 }
300
301 /**
302 * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
303 *
304 * @return true if an element matching the key is found in the diskStore
305 * @since 1.2
306 */
307 public boolean isElementOnDisk(Object key) {
308 return cache.isElementOnDisk(key);
309 }
310
311 /**
312 * The GUID for this cache instance can be used to determine whether two cache instance references
313 * are pointing to the same cache.
314 *
315 * @return the globally unique identifier for this cache instance. This is guaranteed to be unique.
316 * @since 1.2
317 */
318 public String getGuid() {
319 return cache.getGuid();
320 }
321
322 /**
323 * Gets the CacheManager managing this cache. For a newly created cache this will be null until
324 * it has been added to a CacheManager.
325 *
326 * @return the manager or null if there is none
327 */
328 public CacheManager getCacheManager() {
329 return cache.getCacheManager();
330 }
331
332 /**
333 * Resets statistics counters back to 0.
334 */
335 public void clearStatistics() {
336 cache.clearStatistics();
337 }
338
339 /**
340 * Accurately measuring statistics can be expensive. Returns the current accuracy setting.
341 *
342 * @return one of {@link net.sf.ehcache.Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link net.sf.ehcache.Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link net.sf.ehcache.Statistics#STATISTICS_ACCURACY_NONE}
343 */
344 public int getStatisticsAccuracy() {
345 return cache.getStatisticsAccuracy();
346 }
347
348 /**
349 * Sets the statistics accuracy.
350 *
351 * @param statisticsAccuracy one of {@link net.sf.ehcache.Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link net.sf.ehcache.Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link net.sf.ehcache.Statistics#STATISTICS_ACCURACY_NONE}
352 */
353 public void setStatisticsAccuracy(int statisticsAccuracy) {
354 cache.setStatisticsAccuracy(statisticsAccuracy);
355 }
356
357 /**
358 * Causes all elements stored in the Cache to be synchronously checked for expiry, and if expired, evicted.
359 */
360 public void evictExpiredElements() {
361 cache.evictExpiredElements();
362 }
363
364 /**
365 * An inexpensive check to see if the key exists in the cache.
366 *
367 * @param key the key to check for
368 * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
369 */
370 public boolean isKeyInCache(Object key) {
371 return cache.isKeyInCache(key);
372 }
373
374 /**
375 * An extremely expensive check to see if the value exists in the cache.
376 *
377 * @param value to check for
378 * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
379 */
380 public boolean isValueInCache(Object value) {
381 return cache.isValueInCache(value);
382 }
383
384 /**
385 * Gets an immutable Statistics object representing the Cache statistics at the time. How the statistics are calculated
386 * depends on the statistics accuracy setting. The only aspect of statistics sensitive to the accuracy setting is
387 * object size. How that is calculated is discussed below.
388 * <h3>Best Effort Size</h3>
389 * This result is returned when the statistics accuracy setting is {@link net.sf.ehcache.Statistics#STATISTICS_ACCURACY_BEST_EFFORT}.
390 * <p/>
391 * The size is the number of {@link net.sf.ehcache.Element}s in the {@link net.sf.ehcache.store.MemoryStore} plus
392 * the number of {@link net.sf.ehcache.Element}s in the {@link net.sf.ehcache.store.DiskStore}.
393 * <p/>
394 * This number is the actual number of elements, including expired elements that have
395 * not been removed. Any duplicates between stores are accounted for.
396 * <p/>
397 * Expired elements are removed from the the memory store when
398 * getting an expired element, or when attempting to spool an expired element to
399 * disk.
400 * <p/>
401 * Expired elements are removed from the disk store when getting an expired element,
402 * or when the expiry thread runs, which is once every five minutes.
403 * <p/>
404 * <h3>Guaranteed Accuracy Size</h3>
405 * This result is returned when the statistics accuracy setting is {@link net.sf.ehcache.Statistics#STATISTICS_ACCURACY_GUARANTEED}.
406 * <p/>
407 * This method accounts for elements which might be expired or duplicated between stores. It take approximately
408 * 200ms per 1000 elements to execute.
409 * <h3>Fast but non-accurate Size</h3>
410 * This result is returned when the statistics accuracy setting is {@link net.sf.ehcache.Statistics#STATISTICS_ACCURACY_NONE}.
411 * <p/>
412 * The number given may contain expired elements. In addition if the DiskStore is used it may contain some double
413 * counting of elements. It takes 6ms for 1000 elements to execute. Time to execute is O(log n). 50,000 elements take
414 * 36ms.
415 *
416 * @return the number of elements in the ehcache, with a varying degree of accuracy, depending on accuracy setting.
417 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
418 */
419 public Statistics getStatistics() throws IllegalStateException {
420 return cache.getStatistics();
421 }
422
423 /**
424 * Sets the CacheManager
425 *
426 * @param cacheManager
427 */
428 public void setCacheManager(CacheManager cacheManager) {
429 cache.setCacheManager(cacheManager);
430 }
431
432 /**
433 * Accessor for the BootstrapCacheLoader associated with this cache. For testing purposes.
434 */
435 public BootstrapCacheLoader getBootstrapCacheLoader() {
436 return cache.getBootstrapCacheLoader();
437 }
438
439 /**
440 * Sets the bootstrap cache loader.
441 *
442 * @param bootstrapCacheLoader the loader to be used
443 * @throws net.sf.ehcache.CacheException if this method is called after the cache is initialized
444 */
445 public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader) throws CacheException {
446 cache.setBootstrapCacheLoader(bootstrapCacheLoader);
447 }
448
449 /**
450 * DiskStore paths can conflict between CacheManager instances. This method allows the path to be changed.
451 *
452 * @param diskStorePath the new path to be used.
453 * @throws net.sf.ehcache.CacheException if this method is called after the cache is initialized
454 */
455 public void setDiskStorePath(String diskStorePath) throws CacheException {
456 cache.setDiskStorePath(diskStorePath);
457 }
458
459 /**
460 * Newly created caches do not have a {@link net.sf.ehcache.store.MemoryStore} or a {@link net.sf.ehcache.store.DiskStore}.
461 * <p/>
462 * This method creates those and makes the cache ready to accept elements
463 */
464 public void initialise() {
465 cache.initialise();
466 }
467
468 /**
469 * Bootstrap command. This must be called after the Cache is intialised, during
470 * CacheManager initialisation. If loads are synchronous, they will complete before the CacheManager
471 * initialise completes, otherwise they will happen in the background.
472 */
473 public void bootstrap() {
474 cache.bootstrap();
475 }
476
477 /**
478 * Flushes all cache items from memory to auxilliary caches and close the auxilliary caches.
479 * <p/>
480 * Should be invoked only by CacheManager.
481 *
482 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
483 */
484 public void dispose() throws IllegalStateException {
485 cache.dispose();
486 }
487
488 /**
489 * Gets the cache configuration this cache was created with.
490 * <p/>
491 * Things like listeners that are added dynamically are excluded.
492 */
493 public CacheConfiguration getCacheConfiguration() {
494 return cache.getCacheConfiguration();
495 }
496
497 /**
498 * Looks up an entry. Blocks if the entry is null until a call to {@link #put} is done
499 * to put an Element in.
500 * <p/>
501 * If a put is not done, the lock is never released
502 * <p/>
503 * Note. If a LockTimeoutException is thrown while doing a {@link #get} it means the lock was never acquired,
504 * therefore it is a threading error to call {@link #put}
505 * @throws LockTimeoutException if timeout millis is non zero and this method has been unable to
506 * acquire a lock in that time
507 */
508 public Element get(final Object key) throws LockTimeoutException {
509 Mutex lock = getLockForKey(key);
510 try {
511 if (timeoutMillis == 0) {
512 lock.acquire();
513 } else {
514 boolean acquired = lock.attempt(timeoutMillis);
515 if (!acquired) {
516 StringBuffer message = new StringBuffer("Lock timeout. Waited more than ")
517 .append(timeoutMillis)
518 .append("ms to acquire lock for key ")
519 .append(key).append(" on blocking cache ").append(cache.getName());
520 throw new LockTimeoutException(message.toString());
521 }
522 }
523 final Element element = cache.get(key);
524 if (element != null) {
525 //ok let the other threads in
526 lock.release();
527 return element;
528 } else {
529 //don't release the read lock until we put
530 return null;
531 }
532 } catch (InterruptedException e) {
533 throw new CacheException("Interrupted. Message was: " + e.getMessage());
534 }
535 }
536
537
538 /**
539 * Gets the Mutex to use for a given key.
540 * @param key the key
541 * @return one of a limited number of Mutexes.
542 */
543 protected Mutex getLockForKey(final Object key) {
544 int lockNumber = ConcurrencyUtil.selectLock(key, LOCK_NUMBER);
545 return locks[lockNumber];
546 }
547
548 /**
549 * Adds an entry and unlocks it
550 */
551 public void put(Element element) {
552
553 if (element == null) {
554 return;
555 }
556 Object key = element.getObjectKey();
557 Object value = element.getObjectValue();
558
559 Mutex lock = getLockForKey(key);
560 try {
561 if (value != null) {
562 cache.put(element);
563 } else {
564 cache.remove(key);
565 }
566 } finally {
567 //Release the readlock here. This will have been acquired in the get, where the element was null
568 lock.release();
569 }
570 }
571
572 /**
573 * Put an element in the cache.
574 * <p/>
575 * Resets the access statistics on the element, which would be the case if it has previously been
576 * gotten from a cache, and is now being put back.
577 * <p/>
578 * Also notifies the CacheEventListener that:
579 * <ul>
580 * <li>the element was put, but only if the Element was actually put.
581 * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
582 * if it was requested
583 * </ul>
584 *
585 * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
586 * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
587 * further notification to doNotNotifyCacheReplicators cache peers
588 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
589 * @throws IllegalArgumentException if the element is null
590 */
591 public void put(Element element, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
592 IllegalStateException, CacheException {
593 cache.put(element, doNotNotifyCacheReplicators);
594 }
595
596 /**
597 * Put an element in the cache, without updating statistics, or updating listeners. This is meant to be used
598 * in conjunction with {@link #getQuiet}
599 *
600 * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
601 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
602 * @throws IllegalArgumentException if the element is null
603 */
604 public void putQuiet(Element element) throws IllegalArgumentException, IllegalStateException, CacheException {
605 cache.putQuiet(element);
606 }
607
608 /**
609 * Gets an element from the cache. Updates Element Statistics
610 * <p/>
611 * Note that the Element's lastAccessTime is always the time of this get.
612 * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
613 *
614 * @param key a serializable value
615 * @return the element, or null, if it does not exist.
616 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
617 * @see #isExpired
618 */
619 public Element get(Serializable key) throws IllegalStateException, CacheException {
620 return this.get((Object) key);
621 }
622
623
624 /**
625 * Gets an element from the cache, without updating Element statistics. Cache statistics are
626 * still updated.
627 * <p/>
628 *
629 * @param key a serializable value
630 * @return the element, or null, if it does not exist.
631 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
632 * @see #isExpired
633 */
634 public Element getQuiet(Serializable key) throws IllegalStateException, CacheException {
635 return cache.getQuiet(key);
636 }
637
638 /**
639 * Gets an element from the cache, without updating Element statistics. Cache statistics are
640 * still updated.
641 * <p/>
642 *
643 * @param key a serializable value
644 * @return the element, or null, if it does not exist.
645 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
646 * @see #isExpired
647 * @since 1.2
648 */
649 public Element getQuiet(Object key) throws IllegalStateException, CacheException {
650 return cache.getQuiet(key);
651 }
652
653 /**
654 * Returns the keys for this cache.
655 *
656 * @return The keys of this cache. This is not a live set, so it will not track changes to the key set.
657 */
658 public List getKeys() throws CacheException {
659 return cache.getKeys();
660 }
661
662 /**
663 * Returns a list of all elements in the cache. Only keys of non-expired
664 * elements are returned.
665 * <p/>
666 * The returned keys are unique and can be considered a set.
667 * <p/>
668 * The List returned is not live. It is a copy.
669 * <p/>
670 * The time taken is O(n), where n is the number of elements in the cache. On
671 * a 1.8Ghz P4, the time taken is approximately 200ms per 1000 entries. This method
672 * is not synchronized, because it relies on a non-live list returned from {@link #getKeys()}
673 * , which is synchronised, and which takes 8ms per 1000 entries. This way
674 * cache liveness is preserved, even if this method is very slow to return.
675 * <p/>
676 * Consider whether your usage requires checking for expired keys. Because
677 * this method takes so long, depending on cache settings, the list could be
678 * quite out of date by the time you get it.
679 *
680 * @return a list of {@link Object} keys
681 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
682 */
683 public List getKeysWithExpiryCheck() throws IllegalStateException, CacheException {
684 return cache.getKeysWithExpiryCheck();
685 }
686
687 /**
688 * Returns a list of all elements in the cache, whether or not they are expired.
689 * <p/>
690 * The returned keys are not unique and may contain duplicates. If the cache is only
691 * using the memory store, the list will be unique. If the disk store is being used
692 * as well, it will likely contain duplicates, because of the internal store design.
693 * <p/>
694 * The List returned is not live. It is a copy.
695 * <p/>
696 * The time taken is O(log n). On a single cpu 1.8Ghz P4, approximately 6ms is required
697 * for 1000 entries and 36 for 50000.
698 * <p/>
699 * This is the fastest getKeys method
700 *
701 * @return a list of {@link Object} keys
702 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
703 */
704 public List getKeysNoDuplicateCheck() throws IllegalStateException {
705 return cache.getKeysNoDuplicateCheck();
706 }
707
708 /**
709 * Removes an {@link net.sf.ehcache.Element} from the Cache. This also removes it from any
710 * stores it may be in.
711 * <p/>
712 * Also notifies the CacheEventListener after the element was removed, but only if an Element
713 * with the key actually existed.
714 *
715 * @param key
716 * @return true if the element was removed, false if it was not found in the cache
717 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
718 */
719 public boolean remove(Serializable key) throws IllegalStateException {
720 return cache.remove(key);
721 }
722
723 /**
724 * Removes an {@link net.sf.ehcache.Element} from the Cache. This also removes it from any
725 * stores it may be in.
726 * <p/>
727 * Also notifies the CacheEventListener after the element was removed, but only if an Element
728 * with the key actually existed.
729 *
730 * @param key
731 * @return true if the element was removed, false if it was not found in the cache
732 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
733 * @since 1.2
734 */
735 public boolean remove(Object key) throws IllegalStateException {
736 return cache.remove(key);
737 }
738
739 /**
740 * Removes an {@link net.sf.ehcache.Element} from the Cache. This also removes it from any
741 * stores it may be in.
742 * <p/>
743 * Also notifies the CacheEventListener after the element was removed, but only if an Element
744 * with the key actually existed.
745 *
746 * @param key
747 * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
748 * further notification to doNotNotifyCacheReplicators cache peers
749 * @return true if the element was removed, false if it was not found in the cache
750 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
751 * @noinspection SameParameterValue
752 */
753 public boolean remove(Serializable key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
754 return cache.remove(key, doNotNotifyCacheReplicators);
755 }
756
757 /**
758 * Removes an {@link net.sf.ehcache.Element} from the Cache. This also removes it from any
759 * stores it may be in.
760 * <p/>
761 * Also notifies the CacheEventListener after the element was removed, but only if an Element
762 * with the key actually existed.
763 *
764 * @param key
765 * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
766 * further notification to doNotNotifyCacheReplicators cache peers
767 * @return true if the element was removed, false if it was not found in the cache
768 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
769 */
770 public boolean remove(Object key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
771 return cache.remove(key, doNotNotifyCacheReplicators);
772 }
773
774 /**
775 * Removes an {@link net.sf.ehcache.Element} from the Cache, without notifying listeners. This also removes it from any
776 * stores it may be in.
777 * <p/>
778 *
779 * @param key
780 * @return true if the element was removed, false if it was not found in the cache
781 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
782 */
783 public boolean removeQuiet(Serializable key) throws IllegalStateException {
784 return cache.removeQuiet(key);
785 }
786
787 /**
788 * Removes an {@link net.sf.ehcache.Element} from the Cache, without notifying listeners. This also removes it from any
789 * stores it may be in.
790 * <p/>
791 *
792 * @param key
793 * @return true if the element was removed, false if it was not found in the cache
794 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
795 * @since 1.2
796 */
797 public boolean removeQuiet(Object key) throws IllegalStateException {
798 return cache.removeQuiet(key);
799 }
800
801 /**
802 * Removes all cached items.
803 *
804 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
805 */
806 public void removeAll() throws IllegalStateException, CacheException {
807 cache.removeAll();
808 }
809
810 /**
811 * Removes all cached items.
812 *
813 * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer,
814 * in which case this put should not initiate a further notification to doNotNotifyCacheReplicators cache peers
815 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
816 */
817 public void removeAll(boolean doNotNotifyCacheReplicators) throws IllegalStateException, CacheException {
818 cache.removeAll(doNotNotifyCacheReplicators);
819 }
820
821 /**
822 * Flushes all cache items from memory to the disk store, and from the DiskStore to disk.
823 *
824 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
825 */
826 public void flush() throws IllegalStateException, CacheException {
827 cache.flush();
828 }
829
830 /**
831 * Gets the size of the cache. This is a subtle concept. See below.
832 * <p/>
833 * The size is the number of {@link net.sf.ehcache.Element}s in the {@link net.sf.ehcache.store.MemoryStore} plus
834 * the number of {@link net.sf.ehcache.Element}s in the {@link net.sf.ehcache.store.DiskStore}.
835 * <p/>
836 * This number is the actual number of elements, including expired elements that have
837 * not been removed.
838 * <p/>
839 * Expired elements are removed from the the memory store when
840 * getting an expired element, or when attempting to spool an expired element to
841 * disk.
842 * <p/>
843 * Expired elements are removed from the disk store when getting an expired element,
844 * or when the expiry thread runs, which is once every five minutes.
845 * <p/>
846 * To get an exact size, which would exclude expired elements, use {@link #getKeysWithExpiryCheck()}.size(),
847 * although see that method for the approximate time that would take.
848 * <p/>
849 * To get a very fast result, use {@link #getKeysNoDuplicateCheck()}.size(). If the disk store
850 * is being used, there will be some duplicates.
851 *
852 * @return The size value
853 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
854 */
855 public int getSize() throws IllegalStateException, CacheException {
856 return cache.getSize();
857 }
858
859 /**
860 * Gets the size of the memory store for this cache
861 * <p/>
862 * Warning: This method can be very expensive to run. Allow approximately 1 second
863 * per 1MB of entries. Running this method could create liveness problems
864 * because the object lock is held for a long period
865 * <p/>
866 *
867 * @return the approximate size of the memory store in bytes
868 * @throws IllegalStateException
869 */
870 public long calculateInMemorySize() throws IllegalStateException, CacheException {
871 return cache.calculateInMemorySize();
872 }
873
874 /**
875 * Returns the number of elements in the memory store.
876 *
877 * @return the number of elements in the memory store
878 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
879 */
880 public long getMemoryStoreSize() throws IllegalStateException {
881 return cache.getMemoryStoreSize();
882 }
883
884 /**
885 * Returns the number of elements in the disk store.
886 *
887 * @return the number of elements in the disk store.
888 * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
889 */
890 public int getDiskStoreSize() throws IllegalStateException {
891 return cache.getDiskStoreSize();
892 }
893
894 /**
895 * Gets the status attribute of the Cache.
896 *
897 * @return The status value from the Status enum class
898 */
899 public Status getStatus() {
900 return cache.getStatus();
901 }
902
903 /**
904 * Synchronized version of getName to test liveness of the object lock.
905 * <p/>
906 * The time taken for this method to return is a useful measure of runtime contention on the cache.
907 */
908 public synchronized String liveness() {
909 return getName();
910 }
911
912 /**
913 * Sets the time to wait to acquire a lock. This may be modified at any time.
914 * <p/>
915 * The consequences of setting a timeout are:
916 * <ol>
917 * <li>if a lock cannot be acquired in the given time a LockTimeoutException is thrown.
918 * <li>if there is a queue of threads waiting for the first thread to complete, but it does not complete within
919 * the time out period, the successive threads may find that they have exceeded their lock timeouts and fail. This
920 * is usually a good thing because it stops a build up of threads from overwhelming a busy resource, but it does
921 * need to be considered in the design of user interfaces. The timeout should be set no greater than the time a user
922 * would be expected to wait before considering the action will never return
923 * <li>it will be common to see a number of threads timeout trying to get the same lock. This is a normal and desired
924 * consequence.
925 * </ol>
926 * The consequences of not setting a timeout (or setting it to 0) are:
927 * <ol>
928 * <li>There are no partial failures in the system. But there is a greater possibility that a temporary overload
929 * in one part of the system can cause a back up that may take a long time to recover from.
930 * <li>A failing method that perhaps fails because a resource is overloaded will be hit by each thread in turn, no matter whether there is a still a user who
931 * cares about getting a response.
932 * </ol>
933 *
934 * @param timeoutMillis the time in ms. Must be a positive number. 0 means wait forever.
935 */
936 public void setTimeoutMillis(int timeoutMillis) {
937 if (timeoutMillis < 0) {
938 throw new CacheException("The lock timeout must be a positive number of ms. Value was " + timeoutMillis);
939 }
940 this.timeoutMillis = timeoutMillis;
941 }
942
943 /**
944 * Gets the time to wait to acquire a lock.
945 *
946 * @return the time in ms.
947 */
948 public int getTimeoutMillis() {
949 return timeoutMillis;
950 }
951
952
953 }
954
955
956