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