1 /***
2 * Copyright 2003-2009 Terracotta, Inc.
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 java.io.IOException;
20 import java.io.Serializable;
21 import java.net.InetAddress;
22 import java.net.UnknownHostException;
23 import java.rmi.server.UID;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.concurrent.AbstractExecutorService;
34 import java.util.concurrent.CopyOnWriteArrayList;
35 import java.util.concurrent.ExecutionException;
36 import java.util.concurrent.ExecutorService;
37 import java.util.concurrent.Future;
38 import java.util.concurrent.LinkedBlockingQueue;
39 import java.util.concurrent.ThreadPoolExecutor;
40 import java.util.concurrent.TimeUnit;
41 import java.util.logging.Level;
42 import java.util.logging.Logger;
43
44 import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
45 import net.sf.ehcache.config.CacheConfiguration;
46 import net.sf.ehcache.config.DiskStoreConfiguration;
47 import net.sf.ehcache.config.TerracottaConfiguration;
48 import net.sf.ehcache.event.CacheEventListener;
49 import net.sf.ehcache.event.RegisteredEventListeners;
50 import net.sf.ehcache.exceptionhandler.CacheExceptionHandler;
51 import net.sf.ehcache.extension.CacheExtension;
52 import net.sf.ehcache.loader.CacheLoader;
53 import net.sf.ehcache.statistics.CacheUsageListener;
54 import net.sf.ehcache.statistics.LiveCacheStatistics;
55 import net.sf.ehcache.statistics.LiveCacheStatisticsData;
56 import net.sf.ehcache.statistics.LiveCacheStatisticsWrapper;
57 import net.sf.ehcache.statistics.sampled.SampledCacheStatistics;
58 import net.sf.ehcache.statistics.sampled.SampledCacheStatisticsWrapper;
59 import net.sf.ehcache.store.DiskStore;
60 import net.sf.ehcache.store.LruMemoryStore;
61 import net.sf.ehcache.store.MemoryStore;
62 import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
63 import net.sf.ehcache.store.Policy;
64 import net.sf.ehcache.store.Store;
65 import net.sf.ehcache.util.TimeUtil;
66
67 /***
68 * Cache is the central class in ehcache. Caches have {@link Element}s and are managed
69 * by the {@link CacheManager}. The Cache performs logical actions. It delegates physical
70 * implementations to its {@link net.sf.ehcache.store.Store}s.
71 * <p/>
72 * A reference to a Cache can be obtained through the {@link CacheManager}. A Cache thus obtained
73 * is guaranteed to have status {@link Status#STATUS_ALIVE}. This status is checked for any method which
74 * throws {@link IllegalStateException} and the same thrown if it is not alive. This would normally
75 * happen if a call is made after {@link CacheManager#shutdown} is invoked.
76 * <p/>
77 * Cache is threadsafe.
78 * <p/>
79 * Statistics on cache usage are collected and made available through the {@link #getStatistics()} methods.
80 * <p/>
81 * Various decorators are available for Cache, such as BlockingCache, SelfPopulatingCache and the dynamic proxy
82 * ExceptionHandlingDynamicCacheProxy. See each class for details.
83 *
84 * @author Greg Luck
85 * @version $Id: Cache.java 1401 2009-11-05 01:15:50Z gluck $
86 */
87 public class Cache implements Ehcache {
88
89 /***
90 * A reserved word for cache names. It denotes a default configuration
91 * which is applied to caches created without configuration.
92 */
93 public static final String DEFAULT_CACHE_NAME = "default";
94
95 /***
96 * System Property based method of disabling ehcache. If disabled no elements will be added to a cache.
97 * <p/>
98 * Set the property "net.sf.ehcache.disabled=true" to disable ehcache.
99 * <p/>
100 * This can easily be done using <code>java -Dnet.sf.ehcache.disabled=true</code> in the command line.
101 */
102 public static final String NET_SF_EHCACHE_DISABLED = "net.sf.ehcache.disabled";
103
104 /***
105 * System Property based method of selecting the LruMemoryStore in use up to ehcache 1.5. This is provided
106 * for ease of migration.
107 * <p/>
108 * Set the property "net.sf.ehcache.use.classic.lru=true" to use the older LruMemoryStore implementation
109 * when LRU is selected as the eviction policy.
110 * <p/>
111 * This can easily be done using <code>java -Dnet.sf.ehcache.use.classic.lru=true</code> in the command line.
112 */
113 public static final String NET_SF_EHCACHE_USE_CLASSIC_LRU = "net.sf.ehcache.use.classic.lru";
114
115 /***
116 * The default interval between runs of the expiry thread.
117 */
118 public static final long DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS = 120;
119
120 /***
121 * Set a buffer size for the spool of approx 30MB
122 */
123 private static final int DEFAULT_SPOOL_BUFFER_SIZE = 30;
124
125 private static final Logger LOG = Logger.getLogger(Cache.class.getName());
126
127 private static final MemoryStoreEvictionPolicy DEFAULT_MEMORY_STORE_EVICTION_POLICY = MemoryStoreEvictionPolicy.LRU;
128
129 private static InetAddress localhost;
130
131 /***
132 * The amount of time to wait if a store gets backed up
133 */
134 private static final int BACK_OFF_TIME_MILLIS = 50;
135
136 private static final int EXECUTOR_KEEP_ALIVE_TIME = 60000;
137 private static final int EXECUTOR_MAXIMUM_POOL_SIZE = Math.min(10, Runtime.getRuntime().availableProcessors());
138 private static final int EXECUTOR_CORE_POOL_SIZE = 1;
139
140 static {
141 try {
142 localhost = InetAddress.getLocalHost();
143 } catch (UnknownHostException e) {
144 LOG.log(Level.SEVERE, "Unable to set localhost. This prevents creation of a GUID. Cause was: " + e.getMessage(), e);
145 } catch (java.lang.NoClassDefFoundError e) {
146 LOG.log(Level.FINE, "InetAddress is being blocked by your runtime environment. e.g. Google App Engine." +
147 " Ehcache will work as a local cache.");
148 }
149 }
150
151 private volatile boolean disabled = Boolean.getBoolean(NET_SF_EHCACHE_DISABLED);
152
153 private final boolean useClassicLru = Boolean.getBoolean(NET_SF_EHCACHE_USE_CLASSIC_LRU);
154
155 private volatile Store diskStore;
156
157 private volatile String diskStorePath;
158
159 private volatile Status status;
160
161 private volatile CacheConfiguration configuration;
162
163 /***
164 * The {@link MemoryStore} of this {@link Cache}. All caches have a memory store.
165 */
166 private volatile Store memoryStore;
167
168 private volatile RegisteredEventListeners registeredEventListeners;
169
170 private volatile List<CacheExtension> registeredCacheExtensions;
171
172 private volatile String guid;
173
174 private volatile CacheManager cacheManager;
175
176 private volatile BootstrapCacheLoader bootstrapCacheLoader;
177
178 private volatile CacheExceptionHandler cacheExceptionHandler;
179
180 private volatile List<CacheLoader> registeredCacheLoaders;
181
182 /***
183 * A ThreadPoolExecutor which uses a thread pool to schedule loads in the order in which they are requested.
184 * <p/>
185 * Each cache has its own one of these, if required. Because the Core Thread Pool is zero, no threads
186 * are used until actually needed. Threads are added to the pool up to a maximum of 10. The keep alive
187 * time is 60 seconds, after which, if they are not required they will be stopped and collected.
188 * <p/>
189 * The executorService is only used for cache loading, and is created lazily on demand to avoid unnecessary resource
190 * usage.
191 * <p/>
192 * Use {@link #getExecutorService()} to ensure that it is initialised.
193 */
194 private volatile ExecutorService executorService;
195
196 private volatile LiveCacheStatisticsData liveCacheStatisticsData;
197
198 private volatile SampledCacheStatisticsWrapper sampledCacheStatistics;
199
200
201 /***
202 * 1.0 Constructor.
203 * <p/>
204 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
205 * <p/>
206 * A client can specify their own settings here and pass the {@link Cache} object
207 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
208 * <p/>
209 * Only the CacheManager can initialise them.
210 * <p/>
211 * This constructor creates disk stores, if specified, that do not persist between restarts.
212 * <p/>
213 * The default expiry thread interval of 120 seconds is used. This is the interval between runs
214 * of the expiry thread, where it checks the disk store for expired elements. It is not the
215 * the timeToLiveSeconds.
216 *
217 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
218 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
219 * @param overflowToDisk whether to use the disk store
220 * @param eternal whether the elements in the cache are eternal, i.e. never expire
221 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
222 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
223 * @since 1.0
224 */
225 public Cache(String name, int maxElementsInMemory, boolean overflowToDisk,
226 boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds) {
227 this(name, maxElementsInMemory, DEFAULT_MEMORY_STORE_EVICTION_POLICY, overflowToDisk,
228 null, eternal, timeToLiveSeconds, timeToIdleSeconds, false, DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS, null, null);
229 }
230
231
232 /***
233 * 1.1 Constructor.
234 * <p/>
235 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
236 * <p/>
237 * A client can specify their own settings here and pass the {@link Cache} object
238 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
239 * <p/>
240 * Only the CacheManager can initialise them.
241 *
242 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
243 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
244 * @param overflowToDisk whether to use the disk store
245 * @param eternal whether the elements in the cache are eternal, i.e. never expire
246 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
247 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
248 * @param diskPersistent whether to persist the cache to disk between JVM restarts
249 * @param diskExpiryThreadIntervalSeconds
250 * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
251 * @since 1.1
252 */
253 public Cache(String name,
254 int maxElementsInMemory,
255 boolean overflowToDisk,
256 boolean eternal,
257 long timeToLiveSeconds,
258 long timeToIdleSeconds,
259 boolean diskPersistent,
260 long diskExpiryThreadIntervalSeconds) {
261 this(name, maxElementsInMemory, DEFAULT_MEMORY_STORE_EVICTION_POLICY, overflowToDisk, null,
262 eternal, timeToLiveSeconds, timeToIdleSeconds, diskPersistent, diskExpiryThreadIntervalSeconds, null, null);
263 LOG.log(Level.WARNING, "An API change between ehcache-1.1 and ehcache-1.2 results in the persistence path being set to " +
264 DiskStoreConfiguration.getDefaultPath() + " when the ehcache-1.1 constructor is used. " +
265 "Please change to the 1.2 constructor.");
266 }
267
268
269 /***
270 * 1.2 Constructor
271 * <p/>
272 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
273 * <p/>
274 * A client can specify their own settings here and pass the {@link Cache} object
275 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
276 * <p/>
277 * Only the CacheManager can initialise them.
278 *
279 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
280 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
281 * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
282 * @param overflowToDisk whether to use the disk store
283 * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
284 * @param eternal whether the elements in the cache are eternal, i.e. never expire
285 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
286 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
287 * @param diskPersistent whether to persist the cache to disk between JVM restarts
288 * @param diskExpiryThreadIntervalSeconds
289 * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
290 * @param registeredEventListeners a notification service. Optionally null, in which case a new
291 * one with no registered listeners will be created.
292 * @since 1.2
293 */
294 public Cache(String name,
295 int maxElementsInMemory,
296 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
297 boolean overflowToDisk,
298 String diskStorePath,
299 boolean eternal,
300 long timeToLiveSeconds,
301 long timeToIdleSeconds,
302 boolean diskPersistent,
303 long diskExpiryThreadIntervalSeconds,
304 RegisteredEventListeners registeredEventListeners) {
305 this(name,
306 maxElementsInMemory,
307 memoryStoreEvictionPolicy,
308 overflowToDisk,
309 diskStorePath,
310 eternal,
311 timeToLiveSeconds,
312 timeToIdleSeconds,
313 diskPersistent,
314 diskExpiryThreadIntervalSeconds,
315 registeredEventListeners,
316 null);
317 }
318
319
320 /***
321 * 1.2.1 Constructor
322 * <p/>
323 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
324 * <p/>
325 * A client can specify their own settings here and pass the {@link Cache} object
326 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
327 * <p/>
328 * Only the CacheManager can initialise them.
329 *
330 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
331 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
332 * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
333 * @param overflowToDisk whether to use the disk store
334 * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
335 * @param eternal whether the elements in the cache are eternal, i.e. never expire
336 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
337 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
338 * @param diskPersistent whether to persist the cache to disk between JVM restarts
339 * @param diskExpiryThreadIntervalSeconds
340 * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
341 * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
342 * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
343 * @since 1.2.1
344 */
345 public Cache(String name,
346 int maxElementsInMemory,
347 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
348 boolean overflowToDisk,
349 String diskStorePath,
350 boolean eternal,
351 long timeToLiveSeconds,
352 long timeToIdleSeconds,
353 boolean diskPersistent,
354 long diskExpiryThreadIntervalSeconds,
355 RegisteredEventListeners registeredEventListeners,
356 BootstrapCacheLoader bootstrapCacheLoader) {
357
358 this(name,
359 maxElementsInMemory,
360 memoryStoreEvictionPolicy,
361 overflowToDisk,
362 diskStorePath,
363 eternal,
364 timeToLiveSeconds,
365 timeToIdleSeconds,
366 diskPersistent,
367 diskExpiryThreadIntervalSeconds,
368 registeredEventListeners,
369 bootstrapCacheLoader,
370 0);
371 }
372
373 /***
374 * 1.2.4 Constructor
375 * <p/>
376 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
377 * <p/>
378 * A client can specify their own settings here and pass the {@link Cache} object
379 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
380 * <p/>
381 * Only the CacheManager can initialise them.
382 *
383 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
384 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
385 * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
386 * @param overflowToDisk whether to use the disk store
387 * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
388 * @param eternal whether the elements in the cache are eternal, i.e. never expire
389 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
390 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
391 * @param diskPersistent whether to persist the cache to disk between JVM restarts
392 * @param diskExpiryThreadIntervalSeconds
393 * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
394 * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
395 * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
396 * @param maxElementsOnDisk the maximum number of Elements to allow on the disk. 0 means unlimited.
397 * @since 1.2.4
398 */
399 public Cache(String name,
400 int maxElementsInMemory,
401 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
402 boolean overflowToDisk,
403 String diskStorePath,
404 boolean eternal,
405 long timeToLiveSeconds,
406 long timeToIdleSeconds,
407 boolean diskPersistent,
408 long diskExpiryThreadIntervalSeconds,
409 RegisteredEventListeners registeredEventListeners,
410 BootstrapCacheLoader bootstrapCacheLoader,
411 int maxElementsOnDisk) {
412
413
414 this(name,
415 maxElementsInMemory,
416 memoryStoreEvictionPolicy,
417 overflowToDisk,
418 diskStorePath,
419 eternal,
420 timeToLiveSeconds,
421 timeToIdleSeconds,
422 diskPersistent,
423 diskExpiryThreadIntervalSeconds,
424 registeredEventListeners,
425 bootstrapCacheLoader,
426 maxElementsOnDisk,
427 0,
428 true,
429 false,
430 null,
431 true);
432
433 }
434
435 /***
436 * 1.3 Constructor
437 * <p/>
438 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
439 * <p/>
440 * A client can specify their own settings here and pass the {@link Cache} object
441 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
442 * <p/>
443 * Only the CacheManager can initialise them.
444 *
445 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
446 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
447 * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
448 * @param overflowToDisk whether to use the disk store
449 * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
450 * @param eternal whether the elements in the cache are eternal, i.e. never expire
451 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
452 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
453 * @param diskPersistent whether to persist the cache to disk between JVM restarts
454 * @param diskExpiryThreadIntervalSeconds
455 * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
456 * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
457 * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
458 * @param maxElementsOnDisk the maximum number of Elements to allow on the disk. 0 means unlimited.
459 * @param diskSpoolBufferSizeMB the amount of memory to allocate the write buffer for puts to the DiskStore.
460 * @since 1.3
461 */
462 public Cache(String name,
463 int maxElementsInMemory,
464 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
465 boolean overflowToDisk,
466 String diskStorePath,
467 boolean eternal,
468 long timeToLiveSeconds,
469 long timeToIdleSeconds,
470 boolean diskPersistent,
471 long diskExpiryThreadIntervalSeconds,
472 RegisteredEventListeners registeredEventListeners,
473 BootstrapCacheLoader bootstrapCacheLoader,
474 int maxElementsOnDisk,
475 int diskSpoolBufferSizeMB) {
476 this(name,
477 maxElementsInMemory,
478 memoryStoreEvictionPolicy,
479 overflowToDisk,
480 diskStorePath,
481 eternal,
482 timeToLiveSeconds,
483 timeToIdleSeconds,
484 diskPersistent,
485 diskExpiryThreadIntervalSeconds,
486 registeredEventListeners,
487 bootstrapCacheLoader,
488 maxElementsOnDisk,
489 diskSpoolBufferSizeMB,
490 true,
491 false,
492 null,
493 true);
494
495 }
496
497 /***
498 * 1.6.0 Constructor
499 * <p/>
500 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
501 * <p/>
502 * A client can specify their own settings here and pass the {@link Cache} object
503 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
504 * <p/>
505 * Only the CacheManager can initialise them.
506 *
507 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
508 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
509 * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
510 * @param overflowToDisk whether to use the disk store
511 * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
512 * @param eternal whether the elements in the cache are eternal, i.e. never expire
513 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
514 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
515 * @param diskPersistent whether to persist the cache to disk between JVM restarts
516 * @param diskExpiryThreadIntervalSeconds
517 * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
518 * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
519 * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
520 * @param maxElementsOnDisk the maximum number of Elements to allow on the disk. 0 means unlimited.
521 * @param diskSpoolBufferSizeMB the amount of memory to allocate the write buffer for puts to the DiskStore.
522 * @param clearOnFlush whether the MemoryStore should be cleared when {@link #flush flush()} is called on the cache
523 * @since 1.6.0
524 */
525 public Cache(String name,
526 int maxElementsInMemory,
527 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
528 boolean overflowToDisk,
529 String diskStorePath,
530 boolean eternal,
531 long timeToLiveSeconds,
532 long timeToIdleSeconds,
533 boolean diskPersistent,
534 long diskExpiryThreadIntervalSeconds,
535 RegisteredEventListeners registeredEventListeners,
536 BootstrapCacheLoader bootstrapCacheLoader,
537 int maxElementsOnDisk,
538 int diskSpoolBufferSizeMB,
539 boolean clearOnFlush) {
540
541 this(name, maxElementsInMemory, memoryStoreEvictionPolicy, overflowToDisk, diskStorePath, eternal, timeToLiveSeconds,
542 timeToIdleSeconds, diskPersistent, diskExpiryThreadIntervalSeconds, registeredEventListeners,
543 bootstrapCacheLoader, maxElementsOnDisk, diskSpoolBufferSizeMB, clearOnFlush, false, null, true);
544 }
545
546 /***
547 * 1.7.0 Constructor
548 * <p/>
549 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
550 * <p/>
551 * A client can specify their own settings here and pass the {@link Cache} object
552 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
553 * <p/>
554 * Only the CacheManager can initialise them.
555 *
556 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
557 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted
558 * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
559 * @param overflowToDisk whether to use the disk store
560 * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
561 * @param eternal whether the elements in the cache are eternal, i.e. never expire
562 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
563 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
564 * @param diskPersistent whether to persist the cache to disk between JVM restarts
565 * @param diskExpiryThreadIntervalSeconds
566 * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
567 * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
568 * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
569 * @param maxElementsOnDisk the maximum number of Elements to allow on the disk. 0 means unlimited.
570 * @param diskSpoolBufferSizeMB the amount of memory to allocate the write buffer for puts to the DiskStore.
571 * @param clearOnFlush whether the MemoryStore should be cleared when {@link #flush flush()} is called on the cache
572 * @param isTerracottaClustered whether to cluster this cache with Terracotta
573 * @param terracottaValueMode either "SERIALIZATION" or "IDENTITY" mode, only used if isTerracottaClustered=true
574 * @param terracottaCoherentReads whether this cache should use coherent reads (usually should be true) unless optimizing for read-only
575 * @since 1.7.0
576 */
577 public Cache(String name,
578 int maxElementsInMemory,
579 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
580 boolean overflowToDisk,
581 String diskStorePath,
582 boolean eternal,
583 long timeToLiveSeconds,
584 long timeToIdleSeconds,
585 boolean diskPersistent,
586 long diskExpiryThreadIntervalSeconds,
587 RegisteredEventListeners registeredEventListeners,
588 BootstrapCacheLoader bootstrapCacheLoader,
589 int maxElementsOnDisk,
590 int diskSpoolBufferSizeMB,
591 boolean clearOnFlush,
592 boolean isTerracottaClustered,
593 String terracottaValueMode,
594 boolean terracottaCoherentReads) {
595
596 changeStatus(Status.STATUS_UNINITIALISED);
597
598
599 configuration = new CacheConfiguration();
600 configuration.setName(name);
601 configuration.setMaxElementsInMemory(maxElementsInMemory);
602 configuration.setMemoryStoreEvictionPolicyFromObject(memoryStoreEvictionPolicy);
603 configuration.setOverflowToDisk(overflowToDisk);
604 configuration.setEternal(eternal);
605 configuration.setTimeToLiveSeconds(timeToLiveSeconds);
606 configuration.setTimeToIdleSeconds(timeToIdleSeconds);
607 configuration.setDiskPersistent(diskPersistent);
608 configuration.setMaxElementsOnDisk(maxElementsOnDisk);
609 configuration.setClearOnFlush(clearOnFlush);
610
611 guid = createGuid();
612
613 if (diskStorePath == null) {
614 this.diskStorePath = DiskStoreConfiguration.getDefaultPath();
615 } else {
616 this.diskStorePath = diskStorePath;
617 }
618
619 if (registeredEventListeners == null) {
620 this.registeredEventListeners = new RegisteredEventListeners(this);
621 } else {
622 this.registeredEventListeners = registeredEventListeners;
623 }
624
625 registeredCacheExtensions = new CopyOnWriteArrayList<CacheExtension>();
626 registeredCacheLoaders = new CopyOnWriteArrayList<CacheLoader>();
627
628
629 if (diskExpiryThreadIntervalSeconds == 0) {
630 configuration.setDiskExpiryThreadIntervalSeconds(DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS);
631 } else {
632 configuration.setDiskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds);
633 }
634
635 if (diskSpoolBufferSizeMB == 0) {
636 configuration.setDiskSpoolBufferSizeMB(DEFAULT_SPOOL_BUFFER_SIZE);
637 } else {
638 configuration.setDiskSpoolBufferSizeMB(diskSpoolBufferSizeMB);
639 }
640
641
642 if (memoryStoreEvictionPolicy == null) {
643 configuration.setMemoryStoreEvictionPolicyFromObject(DEFAULT_MEMORY_STORE_EVICTION_POLICY);
644 }
645
646 this.bootstrapCacheLoader = bootstrapCacheLoader;
647
648 TerracottaConfiguration tcConfig = new TerracottaConfiguration();
649 tcConfig.setClustered(isTerracottaClustered);
650 if (terracottaValueMode != null) {
651 tcConfig.setValueMode(terracottaValueMode);
652 }
653 tcConfig.setCoherentReads(terracottaCoherentReads);
654 configuration.addTerracotta(tcConfig);
655
656
657 liveCacheStatisticsData = new LiveCacheStatisticsWrapper(this);
658 sampledCacheStatistics = new SampledCacheStatisticsWrapper();
659 }
660
661 /***
662 * Newly created caches do not have a {@link net.sf.ehcache.store.MemoryStore} or a {@link net.sf.ehcache.store.DiskStore}.
663 * <p/>
664 * This method creates those and makes the cache ready to accept elements
665 */
666 public void initialise() {
667 synchronized (this) {
668 if (!status.equals(Status.STATUS_UNINITIALISED)) {
669 throw new IllegalStateException("Cannot initialise the " + configuration.getName()
670 + " cache because its status is not STATUS_UNINITIALISED");
671 }
672
673 if (configuration.getMaxElementsInMemory() == 0) {
674 if (LOG.isLoggable(Level.WARNING)) {
675 LOG.log(Level.WARNING, "Cache: " + configuration.getName()
676 + " has a maxElementsInMemory of 0. It is strongly recommended to " +
677 "have a maximumSize of at least 1. Performance is halved by not using a MemoryStore.");
678 }
679 }
680
681 this.diskStore = createDiskStore();
682
683 if (isTerracottaClustered()) {
684 memoryStore = cacheManager.createTerracottaStore(this);
685 } else {
686 if (useClassicLru && configuration.getMemoryStoreEvictionPolicy().equals(MemoryStoreEvictionPolicy.LRU)) {
687 memoryStore = new LruMemoryStore(this, diskStore);
688 } else {
689 memoryStore = MemoryStore.create(this, diskStore);
690 }
691 }
692 changeStatus(Status.STATUS_ALIVE);
693 initialiseRegisteredCacheExtensions();
694 initialiseRegisteredCacheLoaders();
695
696
697
698
699 getCacheEventNotificationService().registerListener(
700 liveCacheStatisticsData);
701
702 liveCacheStatisticsData
703 .setStatisticsAccuracy(Statistics.STATISTICS_ACCURACY_BEST_EFFORT);
704 liveCacheStatisticsData.setStatisticsEnabled(true);
705
706
707 this.registerCacheUsageListener(sampledCacheStatistics);
708 }
709
710 if (LOG.isLoggable(Level.FINE)) {
711 LOG.log(Level.FINE, "Initialised cache: " + configuration.getName());
712 }
713
714 if (disabled) {
715 if (LOG.isLoggable(Level.WARNING)) {
716 LOG.log(Level.WARNING, "Cache: " + configuration.getName() + " is disabled because the " + NET_SF_EHCACHE_DISABLED
717 + " property was set to true. No elements will be added to the cache.");
718 }
719 }
720 }
721
722 /***
723 * Creates a disk store when either:
724 * <ol>
725 * <li>overflowToDisk is enabled
726 * <li>diskPersistent is enabled
727 * </ol>
728 *
729 * @return the disk store
730 */
731 protected Store createDiskStore() {
732 if (isDiskStore()) {
733 return new DiskStore(this, diskStorePath);
734 } else {
735 return null;
736 }
737 }
738
739 /***
740 * Whether this cache uses a disk store
741 *
742 * @return true if the cache either overflows to disk or is disk persistent
743 */
744 protected boolean isDiskStore() {
745 return configuration.isOverflowToDisk() || configuration.isDiskPersistent();
746 }
747
748 /***
749 * Indicates whether this cache is clustered by Terracotta
750 *
751 * @return {@code true} when the cache is clustered by Terracotta; or {@code false} otherwise
752 */
753 protected boolean isTerracottaClustered() {
754 return configuration.isTerracottaClustered();
755 }
756
757 /***
758 * Bootstrap command. This must be called after the Cache is initialised, during
759 * CacheManager initialisation. If loads are synchronous, they will complete before the CacheManager
760 * initialise completes, otherwise they will happen in the background.
761 */
762 public void bootstrap() {
763 if (!disabled && bootstrapCacheLoader != null) {
764 bootstrapCacheLoader.load(this);
765 }
766
767 }
768
769 private void changeStatus(Status status) {
770 this.status = status;
771 }
772
773
774 /***
775 * Put an element in the cache.
776 * <p/>
777 * Resets the access statistics on the element, which would be the case if it has previously been
778 * gotten from a cache, and is now being put back.
779 * <p/>
780 * Also notifies the CacheEventListener that:
781 * <ul>
782 * <li>the element was put, but only if the Element was actually put.
783 * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
784 * if it was requested
785 * </ul>
786 * <p/>
787 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
788 * This exception should be caught in those circumstances.
789 *
790 * @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
791 * <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
792 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
793 * @throws CacheException
794 */
795 public final void put(Element element) throws IllegalArgumentException, IllegalStateException,
796 CacheException {
797 put(element, false);
798 }
799
800
801 /***
802 * Put an element in the cache.
803 * <p/>
804 * Resets the access statistics on the element, which would be the case if it has previously been
805 * gotten from a cache, and is now being put back.
806 * <p/>
807 * Also notifies the CacheEventListener that:
808 * <ul>
809 * <li>the element was put, but only if the Element was actually put.
810 * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
811 * if it was requested
812 * </ul>
813 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
814 * This exception should be caught in those circumstances.
815 *
816 * @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
817 * <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
818 * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
819 * further notification to doNotNotifyCacheReplicators cache peers
820 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
821 * @throws IllegalArgumentException if the element is null
822 */
823 public final void put(Element element, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
824 IllegalStateException,
825 CacheException {
826 checkStatus();
827
828 if (disabled) {
829 return;
830 }
831
832 if (element == null) {
833 if (doNotNotifyCacheReplicators) {
834 if (LOG.isLoggable(Level.FINE)) {
835 LOG.fine("Element from replicated put is null. This happens because the element is a SoftReference" +
836 " and it has been collected.Increase heap memory on the JVM or set -Xms to be the same as " +
837 "-Xmx to avoid this problem.");
838 }
839 }
840
841 return;
842 }
843
844
845 if (element.getObjectKey() == null) {
846
847 return;
848 }
849
850 element.resetAccessStatistics();
851 boolean elementExists;
852 Object key = element.getObjectKey();
853 elementExists = isElementInMemory(key) || isElementOnDisk(key);
854 if (elementExists) {
855 element.updateUpdateStatistics();
856 }
857 applyDefaultsToElementWithoutLifespanSet(element);
858
859 backOffIfDiskSpoolFull();
860
861
862 memoryStore.put(element);
863
864 if (elementExists) {
865 registeredEventListeners.notifyElementUpdated(element, doNotNotifyCacheReplicators);
866 } else {
867 registeredEventListeners.notifyElementPut(element, doNotNotifyCacheReplicators);
868 }
869
870 }
871
872 /***
873 * wait outside of synchronized block so as not to block readers
874 * If the disk store spool is full wait a short time to give it a chance to
875 * catch up.
876 * todo maybe provide a warning if this is continually happening or monitor via JMX
877 */
878 private void backOffIfDiskSpoolFull() {
879
880 if (diskStore != null && diskStore.bufferFull()) {
881
882 try {
883 Thread.sleep(BACK_OFF_TIME_MILLIS);
884 } catch (InterruptedException e) {
885
886 }
887 }
888 }
889
890 private void applyDefaultsToElementWithoutLifespanSet(Element element) {
891 if (!element.isLifespanSet()) {
892
893 element.setTimeToLive(TimeUtil.convertTimeToInt(configuration.getTimeToLiveSeconds()));
894 element.setTimeToIdle(TimeUtil.convertTimeToInt(configuration.getTimeToIdleSeconds()));
895 element.setEternal(configuration.isEternal());
896 }
897 }
898
899 /***
900 * Put an element in the cache, without updating statistics, or updating listeners. This is meant to be used
901 * in conjunction with {@link #getQuiet}.
902 * Synchronization is handled within the method.
903 * <p/>
904 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
905 * This exception should be caught in those circumstances.
906 * <p/>
907 *
908 * @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
909 * <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
910 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
911 * @throws IllegalArgumentException if the element is null
912 */
913 public final void putQuiet(Element element) throws IllegalArgumentException, IllegalStateException,
914 CacheException {
915 checkStatus();
916
917 if (disabled) {
918 return;
919 }
920
921 if (element == null || element.getObjectKey() == null) {
922
923 return;
924 }
925
926 applyDefaultsToElementWithoutLifespanSet(element);
927
928 memoryStore.put(element);
929 }
930
931 /***
932 * Gets an element from the cache. Updates Element Statistics
933 * <p/>
934 * Note that the Element's lastAccessTime is always the time of this get.
935 * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
936 * <p/>
937 * Synchronization is handled within the method.
938 *
939 * @param key a serializable value. Null keys are not stored so get(null) always returns null
940 * @return the element, or null, if it does not exist.
941 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
942 * @see #isExpired
943 */
944 public final Element get(Serializable key) throws IllegalStateException, CacheException {
945 return get((Object) key);
946 }
947
948
949 /***
950 * Gets an element from the cache. Updates Element Statistics
951 * <p/>
952 * Note that the Element's lastAccessTime is always the time of this get.
953 * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
954 * <p/>
955 * Synchronization is handled within the method.
956 *
957 * @param key an Object value
958 * @return the element, or null, if it does not exist.
959 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
960 * @see #isExpired
961 * @since 1.2
962 */
963 public final Element get(Object key) throws IllegalStateException, CacheException {
964 checkStatus();
965 Element element;
966 long start = System.currentTimeMillis();
967
968 element = searchInMemoryStore(key, true, true);
969 if (element == null && isDiskStore()) {
970 element = searchInDiskStore(key, true, true);
971 }
972 if (element == null) {
973 liveCacheStatisticsData.cacheMissNotFound();
974 if (LOG.isLoggable(Level.FINE)) {
975 LOG.fine(configuration.getName() + " cache - Miss");
976 }
977 }
978
979 long end = System.currentTimeMillis();
980 liveCacheStatisticsData.addGetTimeMillis(end - start);
981 return element;
982 }
983
984 /***
985 * This method will return, from the cache, the Element associated with the argument "key".
986 * <p/>
987 * 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,
988 * the one associated with the cache. If both are null, no load is performed and null is returned.
989 * <p/>
990 * 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.
991 * <p/>
992 * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
993 * are synchronized.
994 *
995 * @param key key whose associated value is to be returned.
996 * @param loader the override loader to use. If null, the cache's default loader will be used
997 * @param loaderArgument an argument to pass to the CacheLoader.
998 * @return an element if it existed or could be loaded, otherwise null
999 * @throws CacheException
1000 */
1001 public Element getWithLoader(Object key, CacheLoader loader, Object loaderArgument) throws CacheException {
1002
1003 Element element = get(key);
1004 if (element != null) {
1005 return element;
1006 }
1007
1008 if (registeredCacheLoaders.size() == 0 && loader == null) {
1009 return null;
1010 }
1011
1012 try {
1013
1014 element = getQuiet(key);
1015 if (element != null) {
1016 return element;
1017 }
1018 Future future = asynchronousLoad(key, loader, loaderArgument);
1019
1020 future.get();
1021 } catch (Exception e) {
1022 throw new CacheException("Exception on load for key " + key, e);
1023 }
1024 return getQuiet(key);
1025 }
1026
1027 /***
1028 * The load method provides a means to "pre load" the cache. This method will, asynchronously, load the specified
1029 * object into the cache using the associated CacheLoader. If the object already exists in the cache, no action is
1030 * taken. If no loader is associated with the object, no object will be loaded into the cache. If a problem is
1031 * encountered during the retrieving or loading of the object, an exception should be logged. If the "arg" argument
1032 * is set, the arg object will be passed to the CacheLoader.load method. The cache will not dereference the object.
1033 * If no "arg" value is provided a null will be passed to the load method. The storing of null values in the cache
1034 * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding
1035 * the object in the cache. In both cases a null is returned.
1036 * <p/>
1037 * The Ehcache native API provides similar functionality to loaders using the
1038 * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1039 *
1040 * @param key key whose associated value to be loaded using the associated CacheLoader if this cache doesn't contain it.
1041 * @throws CacheException
1042 */
1043 public void load(final Object key) throws CacheException {
1044 if (registeredCacheLoaders.size() == 0) {
1045 if (LOG.isLoggable(Level.FINE)) {
1046 LOG.fine("The CacheLoader is null. Returning.");
1047 }
1048 return;
1049 }
1050
1051 boolean existsOnCall = isKeyInCache(key);
1052 if (existsOnCall) {
1053 if (LOG.isLoggable(Level.FINE)) {
1054 LOG.log(Level.FINE, "The key " + key + " exists in the cache. Returning.");
1055 }
1056 return;
1057 }
1058
1059 asynchronousLoad(key, null, null);
1060 }
1061
1062 /***
1063 * The getAll method will return, from the cache, a Map of the objects associated with the Collection of keys in argument "keys".
1064 * If the objects are not in the cache, the associated cache loader will be called. If no loader is associated with an object,
1065 * a null is returned. If a problem is encountered during the retrieving or loading of the objects, an exception will be thrown.
1066 * If the "arg" argument is set, the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference
1067 * 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
1068 * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding the object in
1069 * the cache. In both cases a null is returned.
1070 * <p/>
1071 * <p/>
1072 * Note. If the getAll exceeds the maximum cache size, the returned map will necessarily be less than the number specified.
1073 * <p/>
1074 * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
1075 * are synchronized.
1076 * <p/>
1077 * The constructs package provides similar functionality using the
1078 * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1079 *
1080 * @param keys a collection of keys to be returned/loaded
1081 * @param loaderArgument an argument to pass to the CacheLoader.
1082 * @return a Map populated from the Cache. If there are no elements, an empty Map is returned.
1083 * @throws CacheException
1084 */
1085 public Map getAllWithLoader(Collection keys, Object loaderArgument) throws CacheException {
1086 if (keys == null) {
1087 return new HashMap(0);
1088 }
1089 Map<Object, Object> map = new HashMap<Object, Object>(keys.size());
1090
1091 List<Object> missingKeys = new ArrayList<Object>(keys.size());
1092
1093 if (registeredCacheLoaders.size() > 0) {
1094 Object key = null;
1095 try {
1096 map = new HashMap<Object, Object>(keys.size());
1097
1098 for (Object key1 : keys) {
1099 key = key1;
1100
1101 if (isKeyInCache(key)) {
1102 Element element = get(key);
1103 if (element != null) {
1104 map.put(key, element.getObjectValue());
1105 } else {
1106 map.put(key, null);
1107 }
1108 } else {
1109 missingKeys.add(key);
1110 }
1111 }
1112
1113
1114 Future future = asynchronousLoadAll(missingKeys, loaderArgument);
1115 future.get();
1116
1117
1118 for (Object missingKey : missingKeys) {
1119 key = missingKey;
1120 Element element = get(key);
1121 if (element != null) {
1122 map.put(key, element.getObjectValue());
1123 } else {
1124 map.put(key, null);
1125 }
1126 }
1127
1128 } catch (InterruptedException e) {
1129 throw new CacheException(e.getMessage() + " for key " + key, e);
1130 } catch (ExecutionException e) {
1131 throw new CacheException(e.getMessage() + " for key " + key, e);
1132 }
1133 } else {
1134 for (Object key : keys) {
1135 Element element = get(key);
1136 if (element != null) {
1137 map.put(key, element.getObjectValue());
1138 } else {
1139 map.put(key, null);
1140 }
1141 }
1142 }
1143 return map;
1144 }
1145
1146
1147 /***
1148 * The loadAll method provides a means to "pre load" objects into the cache. This method will, asynchronously, load
1149 * the specified objects into the cache using the associated cache loader(s). If the an object already exists in the
1150 * cache, no action is taken. If no loader is associated with the object, no object will be loaded into the cache.
1151 * If a problem is encountered during the retrieving or loading of the objects, an exception (to be defined)
1152 * should be logged. The getAll method will return, from the cache, a Map of the objects associated with the
1153 * Collection of keys in argument "keys". If the objects are not in the cache, the associated cache loader will be
1154 * called. If no loader is associated with an object, a null is returned. If a problem is encountered during the
1155 * retrieving or loading of the objects, an exception (to be defined) will be thrown. If the "arg" argument is set,
1156 * the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference the object.
1157 * If no "arg" value is provided a null will be passed to the loadAll method.
1158 * <p/>
1159 * keys - collection of the keys whose associated values to be loaded into this cache by using the associated
1160 * CacheLoader if this cache doesn't contain them.
1161 * <p/>
1162 * The Ehcache native API provides similar functionality to loaders using the
1163 * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1164 */
1165 public void loadAll(final Collection keys, final Object argument) throws CacheException {
1166
1167 if (registeredCacheLoaders.size() == 0) {
1168 if (LOG.isLoggable(Level.FINE)) {
1169 LOG.log(Level.FINE, "The CacheLoader is null. Returning.");
1170 }
1171 return;
1172 }
1173 if (keys == null) {
1174 return;
1175 }
1176 asynchronousLoadAll(keys, argument);
1177 }
1178
1179 /***
1180 * Gets an element from the cache, without updating Element statistics. Cache statistics are
1181 * still updated. Listeners are not called.
1182 * <p/>
1183 * @param key a serializable value
1184 * @return the element, or null, if it does not exist.
1185 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1186 * @see #isExpired
1187 */
1188 public final Element getQuiet(Serializable key) throws IllegalStateException, CacheException {
1189 return getQuiet((Object) key);
1190 }
1191
1192 /***
1193 * Gets an element from the cache, without updating Element statistics. Cache statistics are
1194 * not updated.
1195 * <p/>
1196 * Listeners are not called.
1197 *
1198 * @param key a serializable value
1199 * @return the element, or null, if it does not exist.
1200 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1201 * @see #isExpired
1202 * @since 1.2
1203 */
1204 public final Element getQuiet(Object key) throws IllegalStateException, CacheException {
1205 checkStatus();
1206 Element element;
1207
1208 element = searchInMemoryStore(key, false, false);
1209 if (element == null && isDiskStore()) {
1210 element = searchInDiskStore(key, false, false);
1211 }
1212 return element;
1213 }
1214
1215 /***
1216 * Returns a list of all element keys in the cache, whether or not they are expired.
1217 * <p/>
1218 * The returned keys are unique and can be considered a set.
1219 * <p/>
1220 * The List returned is not live. It is a copy.
1221 * <p/>
1222 * The time taken is O(n). On a single CPU 1.8Ghz P4, approximately 8ms is required
1223 * for each 1000 entries.
1224 *
1225 * @return a list of {@link Object} keys
1226 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1227 */
1228 public final List getKeys() throws IllegalStateException, CacheException {
1229 checkStatus();
1230
1231
1232
1233
1234
1235 List<Object> allKeyList = new ArrayList<Object>();
1236 List<Object> keyList = Arrays.asList(memoryStore.getKeyArray());
1237 allKeyList.addAll(keyList);
1238 if (isDiskStore()) {
1239 Set<Object> allKeys = new HashSet<Object>();
1240
1241 allKeys.addAll(keyList);
1242 Object[] diskKeys = diskStore.getKeyArray();
1243 for (Object diskKey : diskKeys) {
1244 if (allKeys.add(diskKey)) {
1245
1246 allKeyList.add(diskKey);
1247 }
1248 }
1249 }
1250 return allKeyList;
1251 }
1252
1253 /***
1254 * Returns a list of all element keys in the cache. Only keys of non-expired
1255 * elements are returned.
1256 * <p/>
1257 * The returned keys are unique and can be considered a set.
1258 * <p/>
1259 * The List returned is not live. It is a copy.
1260 * <p/>
1261 * The time taken is O(n), where n is the number of elements in the cache. On
1262 * a 1.8Ghz P4, the time taken is approximately 200ms per 1000 entries. This method
1263 * is not synchronized, because it relies on a non-live list returned from {@link #getKeys()}
1264 * , which is synchronised, and which takes 8ms per 1000 entries. This way
1265 * cache liveness is preserved, even if this method is very slow to return.
1266 * <p/>
1267 * Consider whether your usage requires checking for expired keys. Because
1268 * this method takes so long, depending on cache settings, the list could be
1269 * quite out of date by the time you get it.
1270 *
1271 * @return a list of {@link Object} keys
1272 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1273 */
1274 public final List getKeysWithExpiryCheck() throws IllegalStateException, CacheException {
1275 List allKeyList = getKeys();
1276
1277 ArrayList<Object> nonExpiredKeys = new ArrayList<Object>(allKeyList.size());
1278 int allKeyListSize = allKeyList.size();
1279 for (int i = 0; i < allKeyListSize; i++) {
1280 Object key = allKeyList.get(i);
1281 Element element = getQuiet(key);
1282 if (element != null) {
1283 nonExpiredKeys.add(key);
1284 }
1285 }
1286 nonExpiredKeys.trimToSize();
1287 return nonExpiredKeys;
1288 }
1289
1290
1291 /***
1292 * Returns a list of all elements in the cache, whether or not they are expired.
1293 * <p/>
1294 * The returned keys are not unique and may contain duplicates. If the cache is only
1295 * using the memory store, the list will be unique. If the disk store is being used
1296 * as well, it will likely contain duplicates, because of the internal store design.
1297 * <p/>
1298 * The List returned is not live. It is a copy.
1299 * <p/>
1300 * The time taken is O(log n). On a single CPU 1.8Ghz P4, approximately 6ms is required
1301 * for 1000 entries and 36 for 50000.
1302 * <p/>
1303 * This is the fastest getKeys method
1304 *
1305 * @return a list of {@link Object} keys
1306 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1307 */
1308 public final List getKeysNoDuplicateCheck() throws IllegalStateException {
1309 checkStatus();
1310 ArrayList<Object> allKeys = new ArrayList<Object>();
1311 List<Object> memoryKeySet = Arrays.asList(memoryStore.getKeyArray());
1312 allKeys.addAll(memoryKeySet);
1313 if (isDiskStore()) {
1314 List<Object> diskKeySet = Arrays.asList(diskStore.getKeyArray());
1315 allKeys.addAll(diskKeySet);
1316 }
1317 return allKeys;
1318 }
1319
1320 private Element searchInMemoryStore(Object key, boolean updateStatistics, boolean notifyListeners) {
1321 Element element;
1322 if (updateStatistics) {
1323 element = memoryStore.get(key);
1324 } else {
1325 element = memoryStore.getQuiet(key);
1326 }
1327
1328 if (element != null) {
1329 if (isExpired(element)) {
1330 if (LOG.isLoggable(Level.FINE)) {
1331 LOG.log(Level.FINE, configuration.getName() + " Memory cache hit, but element expired");
1332 }
1333 if (updateStatistics) {
1334 liveCacheStatisticsData.cacheMissExpired();
1335 }
1336 remove(key, true, notifyListeners, false);
1337 element = null;
1338 } else {
1339 if (updateStatistics) {
1340 element.updateAccessStatistics();
1341 if (LOG.isLoggable(Level.FINE)) {
1342 LOG.fine(getName() + "Cache: " + getName() + "MemoryStore hit for " + key);
1343 }
1344
1345 liveCacheStatisticsData.cacheHitInMemory();
1346 }
1347 }
1348 } else if (LOG.isLoggable(Level.FINE)) {
1349 LOG.fine(getName() + "Cache: " + getName() + "MemoryStore miss for " + key);
1350 }
1351 return element;
1352 }
1353
1354 private Element searchInDiskStore(Object key, boolean updateStatistics, boolean notifyListeners) {
1355 if (!(key instanceof Serializable)) {
1356 return null;
1357 }
1358 Serializable serializableKey = (Serializable) key;
1359 Element element;
1360 if (updateStatistics) {
1361 element = diskStore.get(serializableKey);
1362 } else {
1363 element = diskStore.getQuiet(serializableKey);
1364 }
1365 if (element != null) {
1366 if (isExpired(element)) {
1367 if (LOG.isLoggable(Level.FINE)) {
1368 LOG.log(Level.FINE, configuration.getName() + " cache - Disk Store hit, but element expired");
1369 }
1370 liveCacheStatisticsData.cacheMissExpired();
1371 remove(key, true, notifyListeners, false);
1372 element = null;
1373 } else {
1374 if (updateStatistics) {
1375 element.updateAccessStatistics();
1376 }
1377 liveCacheStatisticsData.cacheHitOnDisk();
1378
1379
1380 memoryStore.put(element);
1381 }
1382 }
1383 return element;
1384 }
1385
1386 /***
1387 * Removes an {@link Element} from the Cache. This also removes it from any
1388 * stores it may be in.
1389 * <p/>
1390 * Also notifies the CacheEventListener after the element was removed.
1391 * <p/>
1392 * Synchronization is handled within the method.
1393 * <p/>
1394 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1395 * This exception should be caught in those circumstances.
1396 *
1397 * @param key the element key to operate on
1398 * @return true if the element was removed, false if it was not found in the cache
1399 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1400 */
1401 public final boolean remove(Serializable key) throws IllegalStateException {
1402 return remove((Object) key);
1403 }
1404
1405 /***
1406 * Removes an {@link Element} from the Cache. This also removes it from any
1407 * stores it may be in.
1408 * <p/>
1409 * Also notifies the CacheEventListener after the element was removed, but only if an Element
1410 * with the key actually existed.
1411 * <p/>
1412 * Synchronization is handled within the method.
1413 * <p/>
1414 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1415 * This exception should be caught in those circumstances.
1416 * <p/>
1417 *
1418 * @param key the element key to operate on
1419 * @return true if the element was removed, false if it was not found in the cache
1420 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1421 * @since 1.2
1422 */
1423 public final boolean remove(Object key) throws IllegalStateException {
1424 return remove(key, false);
1425 }
1426
1427
1428 /***
1429 * Removes an {@link Element} from the Cache. This also removes it from any
1430 * stores it may be in.
1431 * <p/>
1432 * Also notifies the CacheEventListener after the element was removed, but only if an Element
1433 * with the key actually existed.
1434 * <p/>
1435 * Synchronization is handled within the method.
1436 * <p/>
1437 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1438 * This exception should be caught in those circumstances.
1439 *
1440 * @param key the element key to operate on
1441 * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1442 * further notification to doNotNotifyCacheReplicators cache peers
1443 * @return true if the element was removed, false if it was not found in the cache
1444 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1445 */
1446 public final boolean remove(Serializable key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
1447 return remove((Object) key, doNotNotifyCacheReplicators);
1448 }
1449
1450 /***
1451 * Removes an {@link Element} from the Cache. This also removes it from any
1452 * stores it may be in.
1453 * <p/>
1454 * Also notifies the CacheEventListener after the element was removed, but only if an Element
1455 * with the key actually existed.
1456 * <p/>
1457 * Synchronization is handled within the method.
1458 *
1459 * @param key the element key to operate on
1460 * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1461 * further notification to doNotNotifyCacheReplicators cache peers
1462 * @return true if the element was removed, false if it was not found in the cache
1463 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1464 */
1465 public final boolean remove(Object key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
1466 return remove(key, false, true, doNotNotifyCacheReplicators);
1467 }
1468
1469 /***
1470 * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
1471 * stores it may be in.
1472 * <p/>
1473 * Listeners are not called.
1474 *
1475 * @param key the element key to operate on
1476 * @return true if the element was removed, false if it was not found in the cache
1477 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1478 */
1479 public final boolean removeQuiet(Serializable key) throws IllegalStateException {
1480 return remove(key, false, false, false);
1481 }
1482
1483 /***
1484 * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
1485 * stores it may be in.
1486 * <p/>
1487 * Listeners are not called.
1488 * <p/>
1489 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1490 * This exception should be caught in those circumstances.
1491 *
1492 * @param key the element key to operate on
1493 * @return true if the element was removed, false if it was not found in the cache
1494 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1495 * @since 1.2
1496 */
1497 public final boolean removeQuiet(Object key) throws IllegalStateException {
1498 return remove(key, false, false, false);
1499 }
1500
1501
1502 /***
1503 * Removes or expires an {@link Element} from the Cache after an attempt to get it determined that it should be expired.
1504 * This also removes it from any stores it may be in.
1505 * <p/>
1506 * Also notifies the CacheEventListener after the element has expired, but only if an Element
1507 * with the key actually existed.
1508 * <p/>
1509 * Synchronization is handled within the method.
1510 * <p/>
1511 * If a remove was called, listeners are notified, regardless of whether the element existed or not.
1512 * This allows distributed cache listeners to remove elements from a cluster regardless of whether they
1513 * existed locally.
1514 * <p/>
1515 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1516 * This exception should be caught in those circumstances.
1517 *
1518 * @param key the element key to operate on
1519 * @param expiry if the reason this method is being called is to expire the element
1520 * @param notifyListeners whether to notify listeners
1521 * @param doNotNotifyCacheReplicators whether not to notify cache replicators
1522 * @return true if the element was removed, false if it was not found in the cache
1523 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1524 */
1525 private boolean remove(Object key, boolean expiry, boolean notifyListeners,
1526 boolean doNotNotifyCacheReplicators)
1527 throws IllegalStateException {
1528 checkStatus();
1529 boolean removed = false;
1530 Element elementFromMemoryStore;
1531 Element elementFromDiskStore;
1532 elementFromMemoryStore = memoryStore.remove(key);
1533
1534
1535 elementFromDiskStore = null;
1536 if (isDiskStore()) {
1537 if ((key instanceof Serializable)) {
1538 Serializable serializableKey = (Serializable) key;
1539 elementFromDiskStore = diskStore.remove(serializableKey);
1540 }
1541
1542 }
1543
1544 boolean removeNotified = false;
1545
1546
1547 if (elementFromMemoryStore != null) {
1548 if (expiry) {
1549
1550 registeredEventListeners.notifyElementExpiry(elementFromMemoryStore, doNotNotifyCacheReplicators);
1551 } else if (notifyListeners) {
1552 removeNotified = true;
1553 registeredEventListeners.notifyElementRemoved(elementFromMemoryStore, doNotNotifyCacheReplicators);
1554 }
1555 removed = true;
1556 } else if (elementFromDiskStore != null) {
1557 if (expiry) {
1558 registeredEventListeners.notifyElementExpiry(elementFromDiskStore, doNotNotifyCacheReplicators);
1559 } else if (notifyListeners) {
1560 removeNotified = true;
1561 registeredEventListeners.notifyElementRemoved(elementFromDiskStore, doNotNotifyCacheReplicators);
1562 }
1563 removed = true;
1564 }
1565
1566
1567
1568 if (notifyListeners && !expiry && !removeNotified) {
1569 Element syntheticElement = new Element(key, null);
1570 registeredEventListeners.notifyElementRemoved(syntheticElement, doNotNotifyCacheReplicators);
1571 }
1572
1573 return removed;
1574 }
1575
1576 /***
1577 * Removes all cached items.
1578 * Synchronization is handled within the method.
1579 * <p/>
1580 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1581 * This exception should be caught in those circumstances.
1582 *
1583 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1584 */
1585 public void removeAll() throws IllegalStateException, CacheException {
1586 removeAll(false);
1587 }
1588
1589
1590 /***
1591 * Removes all cached items.
1592 * Synchronization is handled within the method.
1593 * <p/>
1594 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1595 * This exception should be caught in those circumstances.
1596 *
1597 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1598 */
1599 public void removeAll(boolean doNotNotifyCacheReplicators) throws IllegalStateException, CacheException {
1600 checkStatus();
1601 memoryStore.removeAll();
1602 if (isDiskStore()) {
1603 diskStore.removeAll();
1604 }
1605 registeredEventListeners.notifyRemoveAll(doNotNotifyCacheReplicators);
1606 }
1607
1608 /***
1609 * Starts an orderly shutdown of the Cache. Steps are:
1610 * <ol>
1611 * <li>Completes any outstanding CacheLoader loads.
1612 * <li>Disposes any cache extensions.
1613 * <li>Disposes any cache event listeners. The listeners normally complete, so for example distributed caching operations will complete.
1614 * <li>Flushes all cache items from memory to the disk store, if any
1615 * <li>changes status to shutdown, so that any cache operations after this point throw IllegalStateException
1616 * </ol>
1617 * This method should be invoked only by CacheManager, as a cache's lifecycle is bound into that of it's cache manager.
1618 *
1619 * @throws IllegalStateException if the cache is already {@link Status#STATUS_SHUTDOWN}
1620 */
1621 public synchronized void dispose() throws IllegalStateException {
1622 checkStatusNotDisposed();
1623
1624 if (executorService != null) {
1625 executorService.shutdown();
1626 }
1627 disposeRegisteredCacheExtensions();
1628 disposeRegisteredCacheLoaders();
1629 registeredEventListeners.dispose();
1630
1631 if (memoryStore != null) {
1632 memoryStore.dispose();
1633 }
1634 if (diskStore != null) {
1635 diskStore.dispose();
1636 }
1637 changeStatus(Status.STATUS_SHUTDOWN);
1638 }
1639
1640 private void initialiseRegisteredCacheExtensions() {
1641 for (CacheExtension cacheExtension : registeredCacheExtensions) {
1642 cacheExtension.init();
1643 }
1644 }
1645
1646 private void disposeRegisteredCacheExtensions() {
1647 for (CacheExtension cacheExtension : registeredCacheExtensions) {
1648 cacheExtension.dispose();
1649 }
1650 }
1651
1652 private void initialiseRegisteredCacheLoaders() {
1653 for (CacheLoader cacheLoader : registeredCacheLoaders) {
1654 cacheLoader.init();
1655 }
1656 }
1657
1658 private void disposeRegisteredCacheLoaders() {
1659 for (CacheLoader cacheLoader : registeredCacheLoaders) {
1660 cacheLoader.dispose();
1661 }
1662 }
1663
1664 /***
1665 * Gets the cache configuration this cache was created with.
1666 * <p/>
1667 * Things like listeners that are added dynamically are excluded.
1668 */
1669 public CacheConfiguration getCacheConfiguration() {
1670 return configuration;
1671 }
1672
1673
1674 /***
1675 * Flushes all cache items from memory to the disk store, and from the DiskStore to disk.
1676 *
1677 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1678 */
1679 public final synchronized void flush() throws IllegalStateException, CacheException {
1680 checkStatus();
1681 try {
1682 memoryStore.flush();
1683 if (isDiskStore()) {
1684 diskStore.flush();
1685 }
1686 } catch (IOException e) {
1687 throw new CacheException("Unable to flush cache: " + configuration.getName()
1688 + ". Initial cause was " + e.getMessage(), e);
1689 }
1690 }
1691
1692 /***
1693 * Gets the size of the cache. This is a subtle concept. See below.
1694 * <p/>
1695 * The size is the number of {@link Element}s in the {@link MemoryStore}
1696 * plus the number of {@link Element}s in the {@link DiskStore}. However, if
1697 * the cache is Terracotta clustered, the underlying store has a coherent
1698 * view of the all the elements in the cache and doesn't have to be
1699 * aggregated from an underlying {@code MemoryStore} and {@code DiskStore}.
1700 * <p/>
1701 * This number is the actual number of elements, including expired elements
1702 * that have not been removed.
1703 * <p/>
1704 * Expired elements are removed from the the memory store when getting an
1705 * expired element, or when attempting to spool an expired element to disk.
1706 * <p/>
1707 * Expired elements are removed from the disk store when getting an expired
1708 * element, or when the expiry thread runs, which is once every five
1709 * minutes.
1710 * <p/>
1711 * To get an exact size, which would exclude expired elements, use
1712 * {@link #getKeysWithExpiryCheck()}.size(), although see that method for
1713 * the approximate time that would take.
1714 * <p/>
1715 * To get a very fast result, use {@link #getKeysNoDuplicateCheck()}.size().
1716 * If the disk store is being used, there will be some duplicates.
1717 *
1718 * @return The size value
1719 * @throws IllegalStateException
1720 * if the cache is not {@link Status#STATUS_ALIVE}
1721 */
1722 public final int getSize() throws IllegalStateException, CacheException {
1723 checkStatus();
1724
1725 if (memoryStore.isCacheCoherent()) {
1726 return memoryStore.getTerracottaClusteredSize();
1727 } else {
1728
1729
1730 return getKeys().size();
1731 }
1732 }
1733
1734 /***
1735 * {@inheritDoc}
1736 */
1737 public int getSizeBasedOnAccuracy(int statisticsAccuracy)
1738 throws IllegalStateException, CacheException {
1739 if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_BEST_EFFORT) {
1740 return getSize();
1741 } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_GUARANTEED) {
1742 return getKeysWithExpiryCheck().size();
1743 } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_NONE) {
1744 return getKeysNoDuplicateCheck().size();
1745 }
1746 throw new IllegalArgumentException("Unknown statistics accuracy: "
1747 + statisticsAccuracy);
1748 }
1749
1750 /***
1751 * Gets the size of the memory store for this cache. This method relies on calculating
1752 * Serialized sizes. If the Element values are not Serializable they will show as zero.
1753 * <p/>
1754 * Warning: This method can be very expensive to run. Allow approximately 1 second
1755 * per 1MB of entries. Running this method could create liveness problems
1756 * because the object lock is held for a long period
1757 * <p/>
1758 *
1759 * @return the approximate size of the memory store in bytes
1760 * @throws IllegalStateException
1761 */
1762 public final long calculateInMemorySize() throws IllegalStateException, CacheException {
1763 checkStatus();
1764 return memoryStore.getSizeInBytes();
1765 }
1766
1767
1768 /***
1769 * Returns the number of elements in the memory store.
1770 *
1771 * @return the number of elements in the memory store
1772 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1773 */
1774 public final long getMemoryStoreSize() throws IllegalStateException {
1775 checkStatus();
1776 return memoryStore.getSize();
1777 }
1778
1779 /***
1780 * Returns the number of elements in the disk store.
1781 *
1782 * @return the number of elements in the disk store.
1783 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1784 */
1785 public final int getDiskStoreSize() throws IllegalStateException {
1786 checkStatus();
1787 if (isTerracottaClustered()) {
1788 return memoryStore.getTerracottaClusteredSize();
1789 }
1790 if (isDiskStore()) {
1791 return diskStore.getSize();
1792 } else {
1793 return 0;
1794 }
1795 }
1796
1797 /***
1798 * Gets the status attribute of the Cache.
1799 *
1800 * @return The status value from the Status enum class
1801 */
1802 public final Status getStatus() {
1803 return status;
1804 }
1805
1806
1807 private void checkStatus() throws IllegalStateException {
1808 if (!status.equals(Status.STATUS_ALIVE)) {
1809 throw new IllegalStateException("The " + configuration.getName() + " Cache is not alive.");
1810 }
1811 }
1812
1813 private void checkStatusNotDisposed() throws IllegalStateException {
1814 if (status.equals(Status.STATUS_SHUTDOWN)) {
1815 throw new IllegalStateException("The " + configuration.getName() + " Cache is disposed.");
1816 }
1817 }
1818
1819
1820 /***
1821 * Gets the cache name.
1822 */
1823 public final String getName() {
1824 return configuration.getName();
1825 }
1826
1827 /***
1828 * Sets the cache name which will name.
1829 *
1830 * @param name the name of the cache. Should not be null. Should also not contain any '/' characters, as these interfere
1831 * with distribution
1832 * @throws IllegalArgumentException if an illegal name is used.
1833 */
1834 public final void setName(String name) throws IllegalArgumentException {
1835 if (!status.equals(Status.STATUS_UNINITIALISED)) {
1836 throw new IllegalStateException("Only uninitialised caches can have their names set.");
1837 }
1838 configuration.setName(name);
1839 }
1840
1841 /***
1842 * Returns a {@link String} representation of {@link Cache}.
1843 */
1844 @Override
1845 public final String toString() {
1846 StringBuffer dump = new StringBuffer();
1847
1848 dump.append("[")
1849 .append(" name = ").append(configuration.getName())
1850 .append(" status = ").append(status)
1851 .append(" eternal = ").append(configuration.isEternal())
1852 .append(" overflowToDisk = ").append(configuration.isOverflowToDisk())
1853 .append(" maxElementsInMemory = ").append(configuration.getMaxElementsInMemory())
1854 .append(" maxElementsOnDisk = ").append(configuration.getMaxElementsOnDisk())
1855 .append(" memoryStoreEvictionPolicy = ").append(configuration.getMemoryStoreEvictionPolicy())
1856 .append(" timeToLiveSeconds = ").append(configuration.getTimeToLiveSeconds())
1857 .append(" timeToIdleSeconds = ").append(configuration.getTimeToIdleSeconds())
1858 .append(" diskPersistent = ").append(configuration.isDiskPersistent())
1859 .append(" diskExpiryThreadIntervalSeconds = ").append(configuration.getDiskExpiryThreadIntervalSeconds())
1860 .append(registeredEventListeners)
1861 .append(" hitCount = ").append(getLiveCacheStatisticsNoCheck().getCacheHitCount())
1862 .append(" memoryStoreHitCount = ").append(getLiveCacheStatisticsNoCheck().getInMemoryHitCount())
1863 .append(" diskStoreHitCount = ").append(getLiveCacheStatisticsNoCheck().getOnDiskHitCount())
1864 .append(" missCountNotFound = ").append(getLiveCacheStatisticsNoCheck().getCacheMissCount())
1865 .append(" missCountExpired = ").append(getLiveCacheStatisticsNoCheck().getCacheMissCountExpired())
1866 .append(" ]");
1867
1868 return dump.toString();
1869 }
1870
1871
1872 /***
1873 * Checks whether this cache element has expired.
1874 * <p/>
1875 * The element is expired if:
1876 * <ol>
1877 * <li> the idle time is non-zero and has elapsed, unless the cache is eternal; or
1878 * <li> the time to live is non-zero and has elapsed, unless the cache is eternal; or
1879 * <li> the value of the element is null.
1880 * </ol>
1881 *
1882 * @return true if it has expired
1883 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1884 * @throws NullPointerException if the element is null
1885 */
1886 public final boolean isExpired(Element element) throws IllegalStateException, NullPointerException {
1887 checkStatus();
1888 synchronized (element) {
1889 return element.isExpired();
1890 }
1891 }
1892
1893
1894 /***
1895 * Clones a cache. This is only legal if the cache has not been
1896 * initialized. At that point only primitives have been set and no
1897 * stores have been created.
1898 * <p/>
1899 * A new, empty, RegisteredEventListeners is created on clone.
1900 * <p/>
1901 *
1902 * @return an object of type {@link Cache}
1903 * @throws CloneNotSupportedException
1904 */
1905 @Override
1906 public final Object clone() throws CloneNotSupportedException {
1907 if (!(memoryStore == null && diskStore == null)) {
1908 throw new CloneNotSupportedException("Cannot clone an initialized cache.");
1909 }
1910 Cache copy = (Cache) super.clone();
1911
1912 copy.liveCacheStatisticsData = new LiveCacheStatisticsWrapper(copy);
1913 copy.sampledCacheStatistics = new SampledCacheStatisticsWrapper();
1914
1915 copy.configuration = configuration.clone();
1916 copy.guid = createGuid();
1917
1918 RegisteredEventListeners registeredEventListenersFromCopy = copy.getCacheEventNotificationService();
1919 if (registeredEventListenersFromCopy == null || registeredEventListenersFromCopy.getCacheEventListeners().size() == 0) {
1920 copy.registeredEventListeners = new RegisteredEventListeners(copy);
1921 } else {
1922 copy.registeredEventListeners = new RegisteredEventListeners(copy);
1923 Set cacheEventListeners = registeredEventListeners.getCacheEventListeners();
1924 for (Object cacheEventListener1 : cacheEventListeners) {
1925 CacheEventListener cacheEventListener = (CacheEventListener) cacheEventListener1;
1926 CacheEventListener cacheEventListenerClone = (CacheEventListener) cacheEventListener.clone();
1927 copy.registeredEventListeners.registerListener(cacheEventListenerClone);
1928 }
1929 }
1930
1931
1932 copy.registeredCacheExtensions = new CopyOnWriteArrayList<CacheExtension>();
1933 for (CacheExtension registeredCacheExtension : registeredCacheExtensions) {
1934 copy.registerCacheExtension(registeredCacheExtension.clone(copy));
1935 }
1936
1937 copy.registeredCacheLoaders = new CopyOnWriteArrayList<CacheLoader>();
1938 for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
1939 copy.registerCacheLoader(registeredCacheLoader.clone(copy));
1940 }
1941
1942 if (bootstrapCacheLoader != null) {
1943 BootstrapCacheLoader bootstrapCacheLoaderClone = (BootstrapCacheLoader) bootstrapCacheLoader.clone();
1944 copy.setBootstrapCacheLoader(bootstrapCacheLoaderClone);
1945 }
1946
1947 return copy;
1948 }
1949
1950 /***
1951 * Gets the internal DiskStore.
1952 *
1953 * @return the DiskStore referenced by this cache
1954 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1955 */
1956 final Store getDiskStore() throws IllegalStateException {
1957 checkStatus();
1958 return diskStore;
1959 }
1960
1961 /***
1962 * Gets the internal MemoryStore.
1963 *
1964 * @return the MemoryStore referenced by this cache
1965 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1966 */
1967 final Store getMemoryStore() throws IllegalStateException {
1968 checkStatus();
1969 return memoryStore;
1970 }
1971
1972
1973 /***
1974 * Use this to access the service in order to register and unregister listeners
1975 *
1976 * @return the RegisteredEventListeners instance for this cache.
1977 */
1978 public final RegisteredEventListeners getCacheEventNotificationService() {
1979 return registeredEventListeners;
1980 }
1981
1982
1983 /***
1984 * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
1985 *
1986 * @return true if an element matching the key is found in memory
1987 */
1988 public final boolean isElementInMemory(Serializable key) {
1989 return isElementInMemory((Object) key);
1990 }
1991
1992 /***
1993 * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
1994 *
1995 * @return true if an element matching the key is found in memory
1996 * @since 1.2
1997 */
1998 public final boolean isElementInMemory(Object key) {
1999 return memoryStore.containsKey(key);
2000 }
2001
2002 /***
2003 * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
2004 *
2005 * @return true if an element matching the key is found in the diskStore
2006 */
2007 public final boolean isElementOnDisk(Serializable key) {
2008 return isElementOnDisk((Object) key);
2009 }
2010
2011 /***
2012 * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
2013 *
2014 * @return true if an element matching the key is found in the diskStore
2015 * @since 1.2
2016 */
2017 public final boolean isElementOnDisk(Object key) {
2018 if (!(key instanceof Serializable)) {
2019 return false;
2020 }
2021 Serializable serializableKey = (Serializable) key;
2022 if (isDiskStore()) {
2023 return diskStore != null && diskStore.containsKey(serializableKey);
2024 } else {
2025 return false;
2026 }
2027 }
2028
2029 /***
2030 * The GUID for this cache instance can be used to determine whether two cache instance references
2031 * are pointing to the same cache.
2032 *
2033 * @return the globally unique identifier for this cache instance. This is guaranteed to be unique.
2034 * @since 1.2
2035 */
2036 public final String getGuid() {
2037 return guid;
2038 }
2039
2040 /***
2041 * Gets the CacheManager managing this cache. For a newly created cache this will be null until
2042 * it has been added to a CacheManager.
2043 *
2044 * @return the manager or null if there is none
2045 */
2046 public final CacheManager getCacheManager() {
2047 return cacheManager;
2048 }
2049
2050
2051 /***
2052 * Resets statistics counters back to 0.
2053 *
2054 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2055 */
2056 public void clearStatistics() throws IllegalStateException {
2057 checkStatus();
2058 liveCacheStatisticsData.clearStatistics();
2059 registeredEventListeners.clearCounters();
2060 }
2061
2062 /***
2063 * Accurately measuring statistics can be expensive. Returns the current accuracy setting.
2064 *
2065 * @return one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
2066 */
2067 public int getStatisticsAccuracy() {
2068 return getLiveCacheStatistics().getStatisticsAccuracy();
2069 }
2070
2071 /***
2072 * Sets the statistics accuracy.
2073 *
2074 * @param statisticsAccuracy one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
2075 */
2076 public void setStatisticsAccuracy(int statisticsAccuracy) {
2077 liveCacheStatisticsData.setStatisticsAccuracy(statisticsAccuracy);
2078 }
2079
2080 /***
2081 * Causes all elements stored in the Cache to be synchronously checked for expiry, and if expired, evicted.
2082 */
2083 public void evictExpiredElements() {
2084 Object[] keys = memoryStore.getKeyArray();
2085
2086 for (Object key : keys) {
2087 searchInMemoryStore(key, false, true);
2088 }
2089
2090
2091 if (isDiskStore()) {
2092 diskStore.expireElements();
2093 }
2094 }
2095
2096 /***
2097 * An inexpensive check to see if the key exists in the cache.
2098 * <p/>
2099 * This method is not synchronized. It is possible that an element may exist in the cache and be removed
2100 * before the check gets to it, or vice versa.
2101 *
2102 * @param key the key to check.
2103 * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
2104 */
2105 public boolean isKeyInCache(Object key) {
2106 if (key == null) {
2107 return false;
2108 }
2109 return isElementInMemory(key) || isElementOnDisk(key);
2110 }
2111
2112 /***
2113 * An extremely expensive check to see if the value exists in the cache. This implementation is O(n). Ehcache
2114 * is not designed for efficient access in this manner.
2115 * <p/>
2116 * This method is not synchronized. It is possible that an element may exist in the cache and be removed
2117 * before the check gets to it, or vice versa. Because it is slow to execute the probability of that this will
2118 * have happened.
2119 *
2120 * @param value to check for
2121 * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
2122 */
2123 public boolean isValueInCache(Object value) {
2124 boolean isSerializable = value instanceof Serializable;
2125 List keys;
2126 if (isSerializable) {
2127 keys = getKeys();
2128 } else {
2129 keys = Arrays.asList(memoryStore.getKeyArray());
2130 }
2131
2132 for (Object key : keys) {
2133 Element element = get(key);
2134 if (element != null) {
2135 Object elementValue = element.getValue();
2136 if (elementValue == null) {
2137 if (value == null) {
2138 return true;
2139 }
2140 } else {
2141 if (elementValue.equals(value)) {
2142 return true;
2143 }
2144 }
2145 }
2146 }
2147 return false;
2148 }
2149
2150 /***
2151 * {@inheritDoc}
2152 * <p/>
2153 * Note, the {@link #getSize} method will have the same value as the size
2154 * reported by Statistics for the statistics accuracy of
2155 * {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}.
2156 */
2157 public Statistics getStatistics() throws IllegalStateException {
2158 int size = getSizeBasedOnAccuracy(getLiveCacheStatistics()
2159 .getStatisticsAccuracy());
2160 return new Statistics(this, getLiveCacheStatistics()
2161 .getStatisticsAccuracy(), getLiveCacheStatistics()
2162 .getCacheHitCount(), getLiveCacheStatistics()
2163 .getOnDiskHitCount(), getLiveCacheStatistics()
2164 .getInMemoryHitCount(), getLiveCacheStatistics()
2165 .getCacheMissCount(), size, getAverageGetTime(),
2166 getLiveCacheStatistics().getEvictedCount(),
2167 getMemoryStoreSize(), getDiskStoreSize());
2168 }
2169
2170 /***
2171 * For use by CacheManager.
2172 *
2173 * @param cacheManager the CacheManager for this cache to use.
2174 */
2175 public void setCacheManager(CacheManager cacheManager) {
2176 this.cacheManager = cacheManager;
2177 }
2178
2179 /***
2180 * Accessor for the BootstrapCacheLoader associated with this cache. For testing purposes.
2181 */
2182 public BootstrapCacheLoader getBootstrapCacheLoader() {
2183 return bootstrapCacheLoader;
2184 }
2185
2186 /***
2187 * Sets the bootstrap cache loader.
2188 *
2189 * @param bootstrapCacheLoader the loader to be used
2190 * @throws CacheException if this method is called after the cache is initialized
2191 */
2192 public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader) throws CacheException {
2193 if (!status.equals(Status.STATUS_UNINITIALISED)) {
2194 throw new CacheException("A bootstrap cache loader can only be set before the cache is initialized. "
2195 + configuration.getName());
2196 }
2197 this.bootstrapCacheLoader = bootstrapCacheLoader;
2198 }
2199
2200 /***
2201 * DiskStore paths can conflict between CacheManager instances. This method allows the path to be changed.
2202 *
2203 * @param diskStorePath the new path to be used.
2204 * @throws CacheException if this method is called after the cache is initialized
2205 */
2206 public void setDiskStorePath(String diskStorePath) throws CacheException {
2207 if (!status.equals(Status.STATUS_UNINITIALISED)) {
2208 throw new CacheException("A DiskStore path can only be set before the cache is initialized. "
2209 + configuration.getName());
2210 }
2211 this.diskStorePath = diskStorePath;
2212 }
2213
2214 /***
2215 * An equals method which follows the contract of {@link Object#equals(Object)}
2216 * <p/>
2217 * An Cache is equal to another one if it implements Ehcache and has the same GUID.
2218 *
2219 * @param object the reference object with which to compare.
2220 * @return <code>true</code> if this object is the same as the obj
2221 * argument; <code>false</code> otherwise.
2222 * @see #hashCode()
2223 * @see java.util.Hashtable
2224 */
2225 @Override
2226 public boolean equals(Object object) {
2227 if (object == null) {
2228 return false;
2229 }
2230 if (!(object instanceof Ehcache)) {
2231 return false;
2232 }
2233 Ehcache other = (Ehcache) object;
2234 return guid.equals(other.getGuid());
2235 }
2236
2237 /***
2238 * Returns a hash code value for the object. This method is
2239 * supported for the benefit of hashtables such as those provided by
2240 * <code>java.util.Hashtable</code>.
2241 * <p/>
2242 * The general contract of <code>hashCode</code> is:
2243 * <ul>
2244 * <li>Whenever it is invoked on the same object more than once during
2245 * an execution of a Java application, the <tt>hashCode</tt> method
2246 * must consistently return the same integer, provided no information
2247 * used in <tt>equals</tt> comparisons on the object is modified.
2248 * This integer need not remain consistent from one execution of an
2249 * application to another execution of the same application.
2250 * <li>If two objects are equal according to the <tt>equals(Object)</tt>
2251 * method, then calling the <code>hashCode</code> method on each of
2252 * the two objects must produce the same integer result.
2253 * <li>It is <em>not</em> required that if two objects are unequal
2254 * according to the {@link Object#equals(Object)}
2255 * method, then calling the <tt>hashCode</tt> method on each of the
2256 * two objects must produce distinct integer results. However, the
2257 * programmer should be aware that producing distinct integer results
2258 * for unequal objects may improve the performance of hashtables.
2259 * </ul>
2260 * <p/>
2261 * As much as is reasonably practical, the hashCode method defined by
2262 * class <tt>Object</tt> does return distinct integers for distinct
2263 * objects. (This is typically implemented by converting the internal
2264 * address of the object into an integer, but this implementation
2265 * technique is not required by the
2266 * Java<font size="-2"><sup>TM</sup></font> programming language.)
2267 * <p/>
2268 * This implementation use the GUID of the cache.
2269 *
2270 * @return a hash code value for this object.
2271 * @see Object#equals(Object)
2272 * @see java.util.Hashtable
2273 */
2274 @Override
2275 public int hashCode() {
2276 return guid.hashCode();
2277 }
2278
2279
2280 /***
2281 * Create globally unique ID for this cache.
2282 */
2283 private String createGuid() {
2284 StringBuffer buffer = null;
2285 try {
2286 buffer = new StringBuffer().append(localhost).append("-").append(new UID());
2287 } catch (java.lang.NoClassDefFoundError e) {
2288
2289
2290 buffer = new StringBuffer().append(configuration.getName()).append("-").append(System.currentTimeMillis());
2291 }
2292
2293 return buffer.toString();
2294 }
2295
2296 /***
2297 * Register a {@link CacheExtension} with the cache. It will then be tied into the cache lifecycle.
2298 * <p/>
2299 * If the CacheExtension is not initialised, initialise it.
2300 */
2301 public void registerCacheExtension(CacheExtension cacheExtension) {
2302 registeredCacheExtensions.add(cacheExtension);
2303 }
2304
2305 /***
2306 * @return the cache extensions as a live list
2307 */
2308 public List<CacheExtension> getRegisteredCacheExtensions() {
2309 return registeredCacheExtensions;
2310 }
2311
2312
2313 /***
2314 * Unregister a {@link CacheExtension} with the cache. It will then be detached from the cache lifecycle.
2315 */
2316 public void unregisterCacheExtension(CacheExtension cacheExtension) {
2317 cacheExtension.dispose();
2318 registeredCacheExtensions.remove(cacheExtension);
2319 }
2320
2321
2322 /***
2323 * The average get time in ms.
2324 */
2325 public float getAverageGetTime() {
2326 return getLiveCacheStatistics().getAverageGetTimeMillis();
2327 }
2328
2329 /***
2330 * Sets an ExceptionHandler on the Cache. If one is already set, it is overwritten.
2331 * <p/>
2332 * The ExceptionHandler is only used if this Cache's methods are accessed using
2333 * {@link net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy}.
2334 *
2335 * @see net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy
2336 */
2337 public void setCacheExceptionHandler(CacheExceptionHandler cacheExceptionHandler) {
2338 this.cacheExceptionHandler = cacheExceptionHandler;
2339 }
2340
2341 /***
2342 * Gets the ExceptionHandler on this Cache, or null if there isn't one.
2343 * <p/>
2344 * The ExceptionHandler is only used if this Cache's methods are accessed using
2345 * {@link net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy}.
2346 *
2347 * @see net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy
2348 */
2349 public CacheExceptionHandler getCacheExceptionHandler() {
2350 return cacheExceptionHandler;
2351 }
2352
2353 /***
2354 * Register a {@link CacheLoader} with the cache. It will then be tied into the cache lifecycle.
2355 * <p/>
2356 * If the CacheLoader is not initialised, initialise it.
2357 *
2358 * @param cacheLoader A Cache Loader to register
2359 */
2360 public void registerCacheLoader(CacheLoader cacheLoader) {
2361 registeredCacheLoaders.add(cacheLoader);
2362 }
2363
2364 /***
2365 * Unregister a {@link CacheLoader} with the cache. It will then be detached from the cache lifecycle.
2366 *
2367 * @param cacheLoader A Cache Loader to unregister
2368 */
2369 public void unregisterCacheLoader(CacheLoader cacheLoader) {
2370 registeredCacheLoaders.remove(cacheLoader);
2371 }
2372
2373
2374 /***
2375 * @return the cache loaders as a live list
2376 */
2377 public List<CacheLoader> getRegisteredCacheLoaders() {
2378 return registeredCacheLoaders;
2379 }
2380
2381 /***
2382 * Does the asynchronous loading.
2383 *
2384 * @param key
2385 * @param specificLoader a specific loader to use. If null the default loader is used.
2386 * @param argument
2387 * @return a Future which can be used to monitor execution
2388 */
2389 Future asynchronousLoad(final Object key, final CacheLoader specificLoader, final Object argument) {
2390 return getExecutorService().submit(new Runnable() {
2391
2392 /***
2393 * Calls the CacheLoader and puts the result in the Cache
2394 */
2395 public void run() throws CacheException {
2396 try {
2397
2398 boolean existsOnRun = isKeyInCache(key);
2399 if (!existsOnRun) {
2400 Object value;
2401 if (specificLoader == null) {
2402 if (registeredCacheLoaders.size() == 0) {
2403 return;
2404 }
2405 value = loadWithRegisteredLoaders(argument, key);
2406 } else {
2407 if (argument == null) {
2408 value = specificLoader.load(key);
2409 } else {
2410 value = specificLoader.load(key, argument);
2411 }
2412 }
2413 if (value != null) {
2414 put(new Element(key, value), false);
2415 }
2416 }
2417 } catch (Throwable e) {
2418 if (LOG.isLoggable(Level.FINE)) {
2419 LOG.log(Level.FINE, "Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
2420 }
2421 throw new CacheException("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
2422 }
2423 }
2424 });
2425 }
2426
2427 private Object loadWithRegisteredLoaders(Object argument, Object key) throws CacheException {
2428
2429 Object value = null;
2430
2431 if (argument == null) {
2432 for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
2433 value = registeredCacheLoader.load(key);
2434 if (value != null) {
2435 break;
2436 }
2437 }
2438 } else {
2439 for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
2440 value = registeredCacheLoader.load(key, argument);
2441 if (value != null) {
2442 break;
2443 }
2444 }
2445 }
2446 return value;
2447 }
2448
2449
2450 /***
2451 * Creates a future to perform the load
2452 *
2453 * @param keys
2454 * @param argument the loader argument
2455 * @return a Future which can be used to monitor execution
2456 */
2457 Future asynchronousLoadAll(final Collection keys, final Object argument) {
2458 return getExecutorService().submit(new Runnable() {
2459 /***
2460 * Calls the CacheLoader and puts the result in the Cache
2461 */
2462 public void run() {
2463 try {
2464 Set<Object> nonLoadedKeys = new HashSet<Object>();
2465 for (Object key : keys) {
2466 if (!isKeyInCache(key)) {
2467 nonLoadedKeys.add(key);
2468 }
2469 }
2470 Map map = loadWithRegisteredLoaders(argument, nonLoadedKeys);
2471 for (Object key : map.keySet()) {
2472 put(new Element(key, map.get(key)));
2473 }
2474 } catch (Throwable e) {
2475 if (LOG.isLoggable(Level.SEVERE)) {
2476 LOG.log(Level.SEVERE, "Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
2477 }
2478 }
2479 }
2480 });
2481 }
2482
2483 /***
2484 * Does the asynchronous loading.
2485 * @param argument the loader argument
2486 * @param nonLoadedKeys the Set of keys that are already in the Cache
2487 * @return A map of loaded elements
2488 */
2489 Map loadWithRegisteredLoaders(Object argument, Set<Object> nonLoadedKeys) {
2490 Map result = new HashMap();
2491 for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
2492 if (nonLoadedKeys.isEmpty()) {
2493 break;
2494 }
2495
2496 Map resultForThisCacheLoader = null;
2497 if (argument == null) {
2498 resultForThisCacheLoader = registeredCacheLoader.loadAll(nonLoadedKeys);
2499 } else {
2500 resultForThisCacheLoader = registeredCacheLoader.loadAll(nonLoadedKeys, argument);
2501 }
2502 if (resultForThisCacheLoader != null) {
2503 nonLoadedKeys.removeAll(resultForThisCacheLoader.keySet());
2504 result.putAll(resultForThisCacheLoader);
2505 }
2506 }
2507 return result;
2508 }
2509
2510 /***
2511 * @return Gets the executor service. This is not publically accessible.
2512 */
2513 ExecutorService getExecutorService() {
2514 if (executorService == null) {
2515 synchronized (this) {
2516 boolean inGoogleAppEngine;
2517
2518 try {
2519 Class.forName("com.google.apphosting.api.DeadlineExceededException");
2520 inGoogleAppEngine = true;
2521 } catch (ClassNotFoundException cnfe) {
2522 inGoogleAppEngine = false;
2523 }
2524
2525 if (inGoogleAppEngine) {
2526
2527
2528 executorService = new AbstractExecutorService() {
2529 /*** {@inheritDoc} */
2530 public void execute(Runnable command) {
2531 command.run();
2532 }
2533 /*** {@inheritDoc} */
2534 public List<Runnable> shutdownNow() {
2535 return Collections.emptyList();
2536 }
2537 /*** {@inheritDoc} */
2538 public void shutdown() {
2539 }
2540 /*** {@inheritDoc} */
2541 public boolean isTerminated() {
2542 return isShutdown();
2543 }
2544 /*** {@inheritDoc} */
2545 public boolean isShutdown() {
2546 return false;
2547 }
2548 /*** {@inheritDoc} */
2549 public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
2550 return true;
2551 }
2552 };
2553 } else {
2554
2555 executorService = new ThreadPoolExecutor(EXECUTOR_CORE_POOL_SIZE, EXECUTOR_MAXIMUM_POOL_SIZE,
2556 EXECUTOR_KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
2557 }
2558 }
2559 }
2560 return executorService;
2561 }
2562
2563
2564 /***
2565 * Whether this cache is disabled. "Disabled" means:
2566 * <ol>
2567 * <li>bootstrap is disabled
2568 * <li>puts are discarded
2569 * <li>putQuites are discarded
2570 * </ol>
2571 * In all other respects the cache continues as it is.
2572 * <p/>
2573 * You can disable and enable a cache programmatically through the {@link #setDisabled(boolean)} method.
2574 * <p/>
2575 * By default caches are enabled on creation, unless the <code>net.sf.ehcache.disabled</code> system
2576 * property is set.
2577 *
2578 * @return true if the cache is disabled.
2579 * @see #NET_SF_EHCACHE_DISABLED ?
2580 */
2581 public boolean isDisabled() {
2582 return disabled;
2583 }
2584
2585 /***
2586 * Disables or enables this cache. This call overrides the previous value of disabled, even if the
2587 * <code>net.sf.ehcache.disabled</code> system property is set
2588 * <p/>
2589 *
2590 * @param disabled true if you wish to disable, false to enable
2591 * @see #isDisabled()
2592 */
2593 public void setDisabled(boolean disabled) {
2594 this.disabled = disabled;
2595 }
2596
2597 /***
2598 * @return the current MemoryStore policy. This may not be the configured policy, if it has been
2599 * dynamically set.
2600 */
2601 public Policy getMemoryStoreEvictionPolicy() {
2602 return memoryStore.getEvictionPolicy();
2603 }
2604
2605 /***
2606 * Sets the eviction policy strategy. The Cache will use a policy at startup. There are three policies
2607 * which can be configured: LRU, LFU and FIFO. However many other policies are possible. That the policy
2608 * has access to the whole element enables policies based on the key, value, metadata, statistics, or a combination of
2609 * any of the above. It is safe to change the policy of a store at any time. The new policy takes effect
2610 * immediately.
2611 *
2612 * @param policy the new policy
2613 */
2614 public void setMemoryStoreEvictionPolicy(Policy policy) {
2615 memoryStore.setEvictionPolicy(policy);
2616 }
2617
2618 /***
2619 * {@inheritDoc}
2620 */
2621 public LiveCacheStatistics getLiveCacheStatistics()
2622 throws IllegalStateException {
2623 checkStatus();
2624 return (LiveCacheStatistics) liveCacheStatisticsData;
2625 }
2626
2627 private LiveCacheStatistics getLiveCacheStatisticsNoCheck() {
2628 return (LiveCacheStatistics) liveCacheStatisticsData;
2629 }
2630
2631 /***
2632 * {@inheritDoc}
2633 */
2634 public void registerCacheUsageListener(CacheUsageListener cacheUsageListener)
2635 throws IllegalStateException {
2636 checkStatus();
2637 liveCacheStatisticsData.registerCacheUsageListener(cacheUsageListener);
2638 }
2639
2640 /***
2641 * {@inheritDoc}
2642 */
2643 public void removeCacheUsageListener(CacheUsageListener cacheUsageListener)
2644 throws IllegalStateException {
2645 checkStatus();
2646 liveCacheStatisticsData.removeCacheUsageListener(cacheUsageListener);
2647 }
2648
2649 /***
2650 * {@inheritDoc}
2651 */
2652 public boolean isStatisticsEnabled() {
2653 return getLiveCacheStatistics().isStatisticsEnabled();
2654 }
2655
2656 /***
2657 * {@inheritDoc}
2658 */
2659 public void setStatisticsEnabled(boolean enableStatistics) {
2660 liveCacheStatisticsData.setStatisticsEnabled(enableStatistics);
2661 if (!enableStatistics) {
2662 setSampledStatisticsEnabled(false);
2663 }
2664 }
2665
2666 /***
2667 * {@inheritDoc}
2668 */
2669 public SampledCacheStatistics getSampledCacheStatistics() {
2670 return sampledCacheStatistics;
2671 }
2672
2673 /***
2674 * {@inheritDoc}
2675 */
2676 public void setSampledStatisticsEnabled(final boolean enableStatistics) {
2677 if (cacheManager == null) {
2678 throw new IllegalStateException(
2679 "You must add the cache to a CacheManager before enabling/disabling sampled statistics.");
2680 }
2681 if (enableStatistics) {
2682 setStatisticsEnabled(true);
2683 sampledCacheStatistics.enableSampledStatistics(cacheManager
2684 .getTimer());
2685 } else {
2686 sampledCacheStatistics.disableSampledStatistics();
2687 }
2688 }
2689
2690 /***
2691 * {@inheritDoc}
2692 *
2693 * @see net.sf.ehcache.Ehcache#isSampledStatisticsEnabled()
2694 */
2695 public boolean isSampledStatisticsEnabled() {
2696 return sampledCacheStatistics.isSampledStatisticsEnabled();
2697 }
2698
2699 /***
2700 * {@inheritDoc}
2701 */
2702 public Object getInternalContext() {
2703 return memoryStore.getInternalContext();
2704 }
2705 }