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