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 java.beans.PropertyChangeListener;
20 import java.beans.PropertyChangeSupport;
21 import java.io.IOException;
22 import java.io.Serializable;
23 import java.lang.reflect.InvocationTargetException;
24 import java.net.InetAddress;
25 import java.net.UnknownHostException;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Map.Entry;
35 import java.util.Properties;
36 import java.util.Set;
37 import java.util.UUID;
38 import java.util.concurrent.AbstractExecutorService;
39 import java.util.concurrent.CopyOnWriteArrayList;
40 import java.util.concurrent.ExecutionException;
41 import java.util.concurrent.ExecutorService;
42 import java.util.concurrent.Future;
43 import java.util.concurrent.LinkedBlockingQueue;
44 import java.util.concurrent.ThreadPoolExecutor;
45 import java.util.concurrent.TimeUnit;
46 import java.util.concurrent.TimeoutException;
47 import java.util.concurrent.atomic.AtomicBoolean;
48 import java.util.concurrent.atomic.AtomicReference;
49 import java.util.concurrent.locks.ReentrantLock;
50
51 import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
52 import net.sf.ehcache.bootstrap.BootstrapCacheLoaderFactory;
53 import net.sf.ehcache.cluster.CacheCluster;
54 import net.sf.ehcache.cluster.ClusterScheme;
55 import net.sf.ehcache.cluster.ClusterSchemeNotAvailableException;
56 import net.sf.ehcache.concurrent.CacheLockProvider;
57 import net.sf.ehcache.concurrent.LockType;
58 import net.sf.ehcache.concurrent.StripedReadWriteLockSync;
59 import net.sf.ehcache.concurrent.Sync;
60 import net.sf.ehcache.config.CacheConfiguration;
61 import net.sf.ehcache.config.CacheWriterConfiguration;
62 import net.sf.ehcache.config.DiskStoreConfiguration;
63 import net.sf.ehcache.config.InvalidConfigurationException;
64 import net.sf.ehcache.config.NonstopConfiguration;
65 import net.sf.ehcache.config.SearchAttribute;
66 import net.sf.ehcache.config.TerracottaConfiguration;
67 import net.sf.ehcache.config.TerracottaConfiguration.Consistency;
68 import net.sf.ehcache.config.TerracottaConfiguration.StorageStrategy;
69 import net.sf.ehcache.constructs.nonstop.NonstopActiveDelegateHolder;
70 import net.sf.ehcache.constructs.nonstop.NonstopExecutorService;
71 import net.sf.ehcache.constructs.nonstop.store.NonstopStoreImpl;
72 import net.sf.ehcache.constructs.nonstop.store.RejoinAwareNonstopStore;
73 import net.sf.ehcache.event.CacheEventListener;
74 import net.sf.ehcache.event.CacheEventListenerFactory;
75 import net.sf.ehcache.event.RegisteredEventListeners;
76 import net.sf.ehcache.exceptionhandler.CacheExceptionHandler;
77 import net.sf.ehcache.extension.CacheExtension;
78 import net.sf.ehcache.extension.CacheExtensionFactory;
79 import net.sf.ehcache.loader.CacheLoader;
80 import net.sf.ehcache.loader.CacheLoaderFactory;
81 import net.sf.ehcache.pool.Pool;
82 import net.sf.ehcache.pool.PoolEvictor;
83 import net.sf.ehcache.pool.PoolableStore;
84 import net.sf.ehcache.pool.SizeOfEngine;
85 import net.sf.ehcache.pool.impl.BoundedPool;
86 import net.sf.ehcache.pool.impl.FromLargestCacheOnDiskPoolEvictor;
87 import net.sf.ehcache.pool.impl.FromLargestCacheOnHeapPoolEvictor;
88 import net.sf.ehcache.pool.impl.UnboundedPool;
89 import net.sf.ehcache.search.Attribute;
90 import net.sf.ehcache.search.Query;
91 import net.sf.ehcache.search.Results;
92 import net.sf.ehcache.search.SearchException;
93 import net.sf.ehcache.search.attribute.AttributeExtractor;
94 import net.sf.ehcache.statistics.CacheUsageListener;
95 import net.sf.ehcache.statistics.LiveCacheStatistics;
96 import net.sf.ehcache.statistics.LiveCacheStatisticsWrapper;
97 import net.sf.ehcache.statistics.sampled.SampledCacheStatistics;
98 import net.sf.ehcache.statistics.sampled.SampledCacheStatisticsWrapper;
99 import net.sf.ehcache.store.DiskBackedMemoryStore;
100 import net.sf.ehcache.store.ElementValueComparator;
101 import net.sf.ehcache.store.LegacyStoreWrapper;
102 import net.sf.ehcache.store.LruMemoryStore;
103 import net.sf.ehcache.store.MemoryOnlyStore;
104 import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
105 import net.sf.ehcache.store.Policy;
106 import net.sf.ehcache.store.Store;
107 import net.sf.ehcache.store.StoreListener;
108 import net.sf.ehcache.store.StoreQuery;
109 import net.sf.ehcache.store.TerracottaStore;
110 import net.sf.ehcache.store.compound.ImmutableValueElementCopyStrategy;
111 import net.sf.ehcache.store.compound.ReadWriteCopyStrategy;
112 import net.sf.ehcache.store.disk.StoreUpdateException;
113 import net.sf.ehcache.store.disk.DiskStore;
114 import net.sf.ehcache.terracotta.TerracottaClient;
115 import net.sf.ehcache.terracotta.TerracottaNotRunningException;
116 import net.sf.ehcache.transaction.SoftLockFactory;
117 import net.sf.ehcache.transaction.TransactionIDFactory;
118 import net.sf.ehcache.transaction.local.JtaLocalTransactionStore;
119 import net.sf.ehcache.transaction.local.LocalTransactionStore;
120 import net.sf.ehcache.transaction.manager.TransactionManagerLookup;
121 import net.sf.ehcache.transaction.xa.EhcacheXAResource;
122 import net.sf.ehcache.transaction.xa.EhcacheXAResourceImpl;
123 import net.sf.ehcache.transaction.xa.XATransactionStore;
124 import net.sf.ehcache.util.ClassLoaderUtil;
125 import net.sf.ehcache.util.NamedThreadFactory;
126 import net.sf.ehcache.util.PropertyUtil;
127 import net.sf.ehcache.util.TimeUtil;
128 import net.sf.ehcache.util.VmUtils;
129 import net.sf.ehcache.writer.CacheWriter;
130 import net.sf.ehcache.writer.CacheWriterFactory;
131 import net.sf.ehcache.writer.CacheWriterManager;
132 import net.sf.ehcache.writer.CacheWriterManagerException;
133
134 import org.slf4j.Logger;
135 import org.slf4j.LoggerFactory;
136
137 /***
138 * Cache is the central class in ehcache. Caches have {@link Element}s and are managed
139 * by the {@link CacheManager}. The Cache performs logical actions. It delegates physical
140 * implementations to its {@link net.sf.ehcache.store.Store}s.
141 * <p/>
142 * A reference to a Cache can be obtained through the {@link CacheManager}. A Cache thus obtained
143 * is guaranteed to have status {@link Status#STATUS_ALIVE}. This status is checked for any method which
144 * throws {@link IllegalStateException} and the same thrown if it is not alive. This would normally
145 * happen if a call is made after {@link CacheManager#shutdown} is invoked.
146 * <p/>
147 * Cache is threadsafe.
148 * <p/>
149 * Statistics on cache usage are collected and made available through the {@link #getStatistics()} methods.
150 * <p/>
151 * Various decorators are available for Cache, such as BlockingCache, SelfPopulatingCache and the dynamic proxy
152 * ExceptionHandlingDynamicCacheProxy. See each class for details.
153 *
154 * @author Greg Luck
155 * @author Geert Bevin
156 * @version $Id: Cache.html 13146 2011-08-01 17:12:39Z oletizi $
157 */
158 public class Cache implements Ehcache, StoreListener {
159
160 /***
161 * A reserved word for cache names. It denotes a default configuration
162 * which is applied to caches created without configuration.
163 */
164 public static final String DEFAULT_CACHE_NAME = "default";
165
166 /***
167 * System Property based method of disabling ehcache. If disabled no elements will be added to a cache.
168 * <p/>
169 * Set the property "net.sf.ehcache.disabled=true" to disable ehcache.
170 * <p/>
171 * This can easily be done using <code>java -Dnet.sf.ehcache.disabled=true</code> in the command line.
172 */
173 public static final String NET_SF_EHCACHE_DISABLED = "net.sf.ehcache.disabled";
174
175 /***
176 * System Property based method of selecting the LruMemoryStore in use up to ehcache 1.5. This is provided
177 * for ease of migration.
178 * <p/>
179 * Set the property "net.sf.ehcache.use.classic.lru=true" to use the older LruMemoryStore implementation
180 * when LRU is selected as the eviction policy.
181 * <p/>
182 * This can easily be done using <code>java -Dnet.sf.ehcache.use.classic.lru=true</code> in the command line.
183 */
184 public static final String NET_SF_EHCACHE_USE_CLASSIC_LRU = "net.sf.ehcache.use.classic.lru";
185
186 /***
187 * The default interval between runs of the expiry thread.
188 * @see CacheConfiguration#DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS CacheConfiguration#DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS for a preferred way of setting
189 */
190 public static final long DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS = CacheConfiguration.DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS;
191
192 private static final String OFF_HEAP_STORE_CLASSNAME = "net.sf.ehcache.store.offheap.OffHeapStore";
193
194 private static final Logger LOG = LoggerFactory.getLogger(Cache.class.getName());
195
196 private static InetAddress localhost;
197
198 /***
199 * The amount of time to wait if a store gets backed up
200 */
201 private static final int BACK_OFF_TIME_MILLIS = 50;
202
203 private static final int EXECUTOR_KEEP_ALIVE_TIME = 60000;
204 private static final int EXECUTOR_MAXIMUM_POOL_SIZE = Math.min(10, Runtime.getRuntime().availableProcessors());
205 private static final int EXECUTOR_CORE_POOL_SIZE = 1;
206 private static final String EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY_PROP = "ehcache.clusteredStore.maxConcurrency";
207 private static final int DEFAULT_EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY = 4096;
208
209 static {
210 try {
211 localhost = InetAddress.getLocalHost();
212 } catch (UnknownHostException e) {
213 LOG.error("Unable to set localhost. This prevents creation of a GUID. Cause was: " + e.getMessage(), e);
214 } catch (java.lang.NoClassDefFoundError e) {
215 LOG.debug("InetAddress is being blocked by your runtime environment. e.g. Google App Engine." +
216 " Ehcache will work as a local cache.");
217 }
218 }
219
220 private volatile boolean disabled = Boolean.getBoolean(NET_SF_EHCACHE_DISABLED);
221
222 private final boolean useClassicLru = Boolean.getBoolean(NET_SF_EHCACHE_USE_CLASSIC_LRU);
223
224 private volatile String diskStorePath;
225
226 private volatile CacheStatus cacheStatus = new CacheStatus();
227
228 private volatile CacheConfiguration configuration;
229
230 /***
231 * The {@link import net.sf.ehcache.store.Store} of this {@link Cache}.
232 */
233 private volatile Store compoundStore;
234 private volatile CacheLockProvider lockProvider;
235
236 private volatile RegisteredEventListeners registeredEventListeners;
237
238 private volatile List<CacheExtension> registeredCacheExtensions;
239
240 private volatile String guid;
241
242 private volatile CacheManager cacheManager;
243
244 private volatile BootstrapCacheLoader bootstrapCacheLoader;
245
246 private volatile CacheExceptionHandler cacheExceptionHandler;
247
248 private volatile List<CacheLoader> registeredCacheLoaders;
249
250 private volatile CacheWriterManager cacheWriterManager;
251
252 private final AtomicBoolean cacheWriterManagerInitFlag = new AtomicBoolean(false);
253
254 private final ReentrantLock cacheWriterManagerInitLock = new ReentrantLock();
255
256 private volatile CacheWriter registeredCacheWriter;
257
258 /***
259 * A ThreadPoolExecutor which uses a thread pool to schedule loads in the order in which they are requested.
260 * <p/>
261 * Each cache has its own one of these, if required. Because the Core Thread Pool is zero, no threads
262 * are used until actually needed. Threads are added to the pool up to a maximum of 10. The keep alive
263 * time is 60 seconds, after which, if they are not required they will be stopped and collected.
264 * <p/>
265 * The executorService is only used for cache loading, and is created lazily on demand to avoid unnecessary resource
266 * usage.
267 * <p/>
268 * Use {@link #getExecutorService()} to ensure that it is initialised.
269 */
270 private volatile ExecutorService executorService;
271
272 private volatile LiveCacheStatisticsWrapper liveCacheStatisticsData;
273
274 private volatile SampledCacheStatisticsWrapper sampledCacheStatistics;
275
276 private volatile TransactionManagerLookup transactionManagerLookup;
277
278 private volatile boolean allowDisable = true;
279
280 private volatile PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
281
282 private volatile ElementValueComparator elementValueComparator;
283
284 private volatile NonstopActiveDelegateHolderImpl nonstopActiveDelegateHolder;
285
286 /***
287 * 2.0 and higher Constructor
288 * <p/>
289 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
290 * <p/>
291 * A client can specify their own settings here and pass the {@link Cache} object
292 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
293 * <p/>
294 * Only the CacheManager can initialise them.
295 *
296 * @param cacheConfiguration the configuration that should be used to create the cache with
297 */
298 public Cache(CacheConfiguration cacheConfiguration) {
299 this(cacheConfiguration, null, null);
300 }
301
302 /***
303 * 2.0 and higher Constructor
304 * <p/>
305 * The {@link net.sf.ehcache.config.ConfigurationFactory}
306 * and clients can create these.
307 * <p/>
308 * A client can specify their own settings here and pass the {@link Cache}
309 * object into {@link CacheManager#addCache} to specify parameters other
310 * than the defaults.
311 * <p/>
312 * Only the CacheManager can initialise them.
313 *
314 * @param cacheConfiguration the configuration that should be used to create the cache with
315 * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
316 * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
317 */
318 public Cache(CacheConfiguration cacheConfiguration,
319 RegisteredEventListeners registeredEventListeners,
320 BootstrapCacheLoader bootstrapCacheLoader) {
321 cacheStatus.changeState(Status.STATUS_UNINITIALISED);
322
323 this.configuration = cacheConfiguration.clone();
324 configuration.validateCompleteConfiguration();
325 elementValueComparator = cacheConfiguration.getElementValueComparatorConfiguration().getElementComparatorInstance();
326
327 guid = createGuid();
328
329 this.diskStorePath = cacheConfiguration.getDiskStorePath();
330
331 if (registeredEventListeners == null) {
332 this.registeredEventListeners = new RegisteredEventListeners(this);
333 } else {
334 this.registeredEventListeners = registeredEventListeners;
335 }
336
337 registeredCacheExtensions = new CopyOnWriteArrayList<CacheExtension>();
338 registeredCacheLoaders = new CopyOnWriteArrayList<CacheLoader>();
339
340
341 liveCacheStatisticsData = new LiveCacheStatisticsWrapper(this);
342 sampledCacheStatistics = new SampledCacheStatisticsWrapper();
343
344 RegisteredEventListeners listeners = getCacheEventNotificationService();
345 registerCacheListeners(configuration, listeners);
346 registerCacheExtensions(configuration, this);
347
348 if (null == bootstrapCacheLoader) {
349 this.bootstrapCacheLoader = createBootstrapCacheLoader(configuration.getBootstrapCacheLoaderFactoryConfiguration());
350 } else {
351 this.bootstrapCacheLoader = bootstrapCacheLoader;
352 }
353 registerCacheLoaders(configuration, this);
354 registerCacheWriter(configuration, this);
355
356 this.nonstopActiveDelegateHolder = new NonstopActiveDelegateHolderImpl(this);
357 }
358
359
360
361 /***
362 * 1.0 Constructor.
363 * <p/>
364 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
365 * <p/>
366 * A client can specify their own settings here and pass the {@link Cache} object
367 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
368 * <p/>
369 * Only the CacheManager can initialise them.
370 * <p/>
371 * This constructor creates disk stores, if specified, that do not persist between restarts.
372 * <p/>
373 * The default expiry thread interval of 120 seconds is used. This is the interval between runs
374 * of the expiry thread, where it checks the disk store for expired elements. It is not the
375 * the timeToLiveSeconds.
376 *
377 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
378 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
379 * @param overflowToDisk whether to use the disk store
380 * @param eternal whether the elements in the cache are eternal, i.e. never expire
381 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
382 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
383 * @since 1.0
384 * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
385 * for full construction support of version 2.0 and higher features.
386 */
387 public Cache(String name, int maxElementsInMemory, boolean overflowToDisk,
388 boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds) {
389
390 this(new CacheConfiguration(name, maxElementsInMemory)
391 .overflowToDisk(overflowToDisk)
392 .eternal(eternal)
393 .timeToLiveSeconds(timeToLiveSeconds)
394 .timeToIdleSeconds(timeToIdleSeconds));
395 }
396
397
398 /***
399 * 1.1 Constructor.
400 * <p/>
401 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
402 * <p/>
403 * A client can specify their own settings here and pass the {@link Cache} object
404 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
405 * <p/>
406 * Only the CacheManager can initialise them.
407 *
408 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
409 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
410 * @param overflowToDisk whether to use the disk store
411 * @param eternal whether the elements in the cache are eternal, i.e. never expire
412 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
413 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
414 * @param diskPersistent whether to persist the cache to disk between JVM restarts
415 * @param diskExpiryThreadIntervalSeconds
416 * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
417 * @since 1.1
418 * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
419 * for full construction support of version 2.0 and higher features.
420 */
421 public Cache(String name,
422 int maxElementsInMemory,
423 boolean overflowToDisk,
424 boolean eternal,
425 long timeToLiveSeconds,
426 long timeToIdleSeconds,
427 boolean diskPersistent,
428 long diskExpiryThreadIntervalSeconds) {
429
430 this(new CacheConfiguration(name, maxElementsInMemory)
431 .overflowToDisk(overflowToDisk)
432 .eternal(eternal)
433 .timeToLiveSeconds(timeToLiveSeconds)
434 .timeToIdleSeconds(timeToIdleSeconds)
435 .diskPersistent(diskPersistent)
436 .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds));
437
438 LOG.warn("An API change between ehcache-1.1 and ehcache-1.2 results in the persistence path being set to " +
439 DiskStoreConfiguration.getDefaultPath() + " when the ehcache-1.1 constructor is used. " +
440 "Please change to the 1.2 constructor.");
441 }
442
443
444 /***
445 * 1.2 Constructor
446 * <p/>
447 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
448 * <p/>
449 * A client can specify their own settings here and pass the {@link Cache} object
450 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
451 * <p/>
452 * Only the CacheManager can initialise them.
453 *
454 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
455 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
456 * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
457 * @param overflowToDisk whether to use the disk store
458 * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
459 * @param eternal whether the elements in the cache are eternal, i.e. never expire
460 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
461 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
462 * @param diskPersistent whether to persist the cache to disk between JVM restarts
463 * @param diskExpiryThreadIntervalSeconds
464 * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
465 * @param registeredEventListeners a notification service. Optionally null, in which case a new
466 * one with no registered listeners will be created.
467 * @since 1.2
468 * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
469 * for full construction support of version 2.0 and higher features.
470 */
471 public Cache(String name,
472 int maxElementsInMemory,
473 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
474 boolean overflowToDisk,
475 String diskStorePath,
476 boolean eternal,
477 long timeToLiveSeconds,
478 long timeToIdleSeconds,
479 boolean diskPersistent,
480 long diskExpiryThreadIntervalSeconds,
481 RegisteredEventListeners registeredEventListeners) {
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 null);
494
495 }
496
497 /***
498 * 1.2.1 Constructor
499 * <p/>
500 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
501 * <p/>
502 * A client can specify their own settings here and pass the {@link Cache} object
503 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
504 * <p/>
505 * Only the CacheManager can initialise them.
506 *
507 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
508 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
509 * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
510 * @param overflowToDisk whether to use the disk store
511 * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
512 * @param eternal whether the elements in the cache are eternal, i.e. never expire
513 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
514 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
515 * @param diskPersistent whether to persist the cache to disk between JVM restarts
516 * @param diskExpiryThreadIntervalSeconds
517 * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
518 * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
519 * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
520 * @since 1.2.1
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
537 this(new CacheConfiguration(name, maxElementsInMemory)
538 .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
539 .overflowToDisk(overflowToDisk)
540 .diskStorePath(diskStorePath)
541 .eternal(eternal)
542 .timeToLiveSeconds(timeToLiveSeconds)
543 .timeToIdleSeconds(timeToIdleSeconds)
544 .diskPersistent(diskPersistent)
545 .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds),
546 registeredEventListeners,
547 bootstrapCacheLoader);
548 }
549
550 /***
551 * 1.2.4 Constructor
552 * <p/>
553 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
554 * <p/>
555 * A client can specify their own settings here and pass the {@link Cache} object
556 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
557 * <p/>
558 * Only the CacheManager can initialise them.
559 *
560 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
561 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
562 * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
563 * @param overflowToDisk whether to use the disk store
564 * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
565 * @param eternal whether the elements in the cache are eternal, i.e. never expire
566 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
567 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
568 * @param diskPersistent whether to persist the cache to disk between JVM restarts
569 * @param diskExpiryThreadIntervalSeconds
570 * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
571 * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
572 * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
573 * @param maxElementsOnDisk the maximum number of Elements to allow on the disk. 0 means unlimited.
574 * @since 1.2.4
575 * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
576 * for full construction support of version 2.0 and higher features.
577 */
578 public Cache(String name,
579 int maxElementsInMemory,
580 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
581 boolean overflowToDisk,
582 String diskStorePath,
583 boolean eternal,
584 long timeToLiveSeconds,
585 long timeToIdleSeconds,
586 boolean diskPersistent,
587 long diskExpiryThreadIntervalSeconds,
588 RegisteredEventListeners registeredEventListeners,
589 BootstrapCacheLoader bootstrapCacheLoader,
590 int maxElementsOnDisk) {
591
592 this(new CacheConfiguration(name, maxElementsInMemory)
593 .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
594 .overflowToDisk(overflowToDisk)
595 .diskStorePath(diskStorePath)
596 .eternal(eternal)
597 .timeToLiveSeconds(timeToLiveSeconds)
598 .timeToIdleSeconds(timeToIdleSeconds)
599 .diskPersistent(diskPersistent)
600 .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
601 .maxElementsOnDisk(maxElementsOnDisk),
602 registeredEventListeners,
603 bootstrapCacheLoader);
604 }
605
606 /***
607 * 1.3 Constructor
608 * <p/>
609 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
610 * <p/>
611 * A client can specify their own settings here and pass the {@link Cache} object
612 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
613 * <p/>
614 * Only the CacheManager can initialise them.
615 *
616 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
617 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
618 * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
619 * @param overflowToDisk whether to use the disk store
620 * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
621 * @param eternal whether the elements in the cache are eternal, i.e. never expire
622 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
623 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
624 * @param diskPersistent whether to persist the cache to disk between JVM restarts
625 * @param diskExpiryThreadIntervalSeconds
626 * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
627 * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
628 * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
629 * @param maxElementsOnDisk the maximum number of Elements to allow on the disk. 0 means unlimited.
630 * @param diskSpoolBufferSizeMB the amount of memory to allocate the write buffer for puts to the DiskStore.
631 * @since 1.3
632 * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
633 * for full construction support of version 2.0 and higher features.
634 */
635 public Cache(String name,
636 int maxElementsInMemory,
637 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
638 boolean overflowToDisk,
639 String diskStorePath,
640 boolean eternal,
641 long timeToLiveSeconds,
642 long timeToIdleSeconds,
643 boolean diskPersistent,
644 long diskExpiryThreadIntervalSeconds,
645 RegisteredEventListeners registeredEventListeners,
646 BootstrapCacheLoader bootstrapCacheLoader,
647 int maxElementsOnDisk,
648 int diskSpoolBufferSizeMB) {
649
650 this(new CacheConfiguration(name, maxElementsInMemory)
651 .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
652 .overflowToDisk(overflowToDisk)
653 .diskStorePath(diskStorePath)
654 .eternal(eternal)
655 .timeToLiveSeconds(timeToLiveSeconds)
656 .timeToIdleSeconds(timeToIdleSeconds)
657 .diskPersistent(diskPersistent)
658 .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
659 .maxElementsOnDisk(maxElementsOnDisk)
660 .diskSpoolBufferSizeMB(diskSpoolBufferSizeMB),
661 registeredEventListeners,
662 bootstrapCacheLoader);
663 }
664
665 /***
666 * 1.6.0 Constructor
667 * <p/>
668 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
669 * <p/>
670 * A client can specify their own settings here and pass the {@link Cache} object
671 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
672 * <p/>
673 * Only the CacheManager can initialise them.
674 *
675 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
676 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
677 * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
678 * @param overflowToDisk whether to use the disk store
679 * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
680 * @param eternal whether the elements in the cache are eternal, i.e. never expire
681 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
682 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
683 * @param diskPersistent whether to persist the cache to disk between JVM restarts
684 * @param diskExpiryThreadIntervalSeconds
685 * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
686 * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
687 * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
688 * @param maxElementsOnDisk the maximum number of Elements to allow on the disk. 0 means unlimited.
689 * @param diskSpoolBufferSizeMB the amount of memory to allocate the write buffer for puts to the DiskStore.
690 * @param clearOnFlush whether the in-memory storage should be cleared when {@link #flush flush()} is called on the cache
691 * @since 1.6.0
692 * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
693 * for full construction support of version 2.0 and higher features.
694 */
695 public Cache(String name,
696 int maxElementsInMemory,
697 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
698 boolean overflowToDisk,
699 String diskStorePath,
700 boolean eternal,
701 long timeToLiveSeconds,
702 long timeToIdleSeconds,
703 boolean diskPersistent,
704 long diskExpiryThreadIntervalSeconds,
705 RegisteredEventListeners registeredEventListeners,
706 BootstrapCacheLoader bootstrapCacheLoader,
707 int maxElementsOnDisk,
708 int diskSpoolBufferSizeMB,
709 boolean clearOnFlush) {
710
711 this(new CacheConfiguration(name, maxElementsInMemory)
712 .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
713 .overflowToDisk(overflowToDisk)
714 .diskStorePath(diskStorePath)
715 .eternal(eternal)
716 .timeToLiveSeconds(timeToLiveSeconds)
717 .timeToIdleSeconds(timeToIdleSeconds)
718 .diskPersistent(diskPersistent)
719 .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
720 .maxElementsOnDisk(maxElementsOnDisk)
721 .diskSpoolBufferSizeMB(diskSpoolBufferSizeMB)
722 .clearOnFlush(clearOnFlush),
723 registeredEventListeners,
724 bootstrapCacheLoader);
725 }
726
727 /***
728 * 1.7.0 Constructor
729 * <p/>
730 * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
731 * <p/>
732 * A client can specify their own settings here and pass the {@link Cache} object
733 * into {@link CacheManager#addCache} to specify parameters other than the defaults.
734 * <p/>
735 * Only the CacheManager can initialise them.
736 *
737 * @param name the name of the cache. Note that "default" is a reserved name for the defaultCache.
738 * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit)
739 * @param memoryStoreEvictionPolicy one of LRU, LFU and FIFO. Optionally null, in which case it will be set to LRU.
740 * @param overflowToDisk whether to use the disk store
741 * @param diskStorePath this parameter is ignored. CacheManager sets it using setter injection.
742 * @param eternal whether the elements in the cache are eternal, i.e. never expire
743 * @param timeToLiveSeconds the default amount of time to live for an element from its creation date
744 * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date
745 * @param diskPersistent whether to persist the cache to disk between JVM restarts
746 * @param diskExpiryThreadIntervalSeconds
747 * how often to run the disk store expiry thread. A large number of 120 seconds plus is recommended
748 * @param registeredEventListeners a notification service. Optionally null, in which case a new one with no registered listeners will be created.
749 * @param bootstrapCacheLoader the BootstrapCacheLoader to use to populate the cache when it is first initialised. Null if none is required.
750 * @param maxElementsOnDisk the maximum number of Elements to allow on the disk. 0 means unlimited.
751 * @param diskSpoolBufferSizeMB the amount of memory to allocate the write buffer for puts to the DiskStore.
752 * @param clearOnFlush whether the in-memory storage should be cleared when {@link #flush flush()} is called on the cache
753 * @param isTerracottaClustered whether to cluster this cache with Terracotta
754 * @param terracottaValueMode either "SERIALIZATION" or "IDENTITY" mode, only used if isTerracottaClustered=true
755 * @param terracottaCoherentReads whether this cache should use coherent reads (usually should be true) unless optimizing for read-only
756 * @since 1.7.0
757 * @see #Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader) Cache(CacheConfiguration, RegisteredEventListeners, BootstrapCacheLoader),
758 * for full construction support of version 2.0 and higher features.
759 */
760 public Cache(String name, int maxElementsInMemory, MemoryStoreEvictionPolicy memoryStoreEvictionPolicy, boolean overflowToDisk,
761 String diskStorePath, boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds, boolean diskPersistent,
762 long diskExpiryThreadIntervalSeconds, RegisteredEventListeners registeredEventListeners,
763 BootstrapCacheLoader bootstrapCacheLoader, int maxElementsOnDisk, int diskSpoolBufferSizeMB, boolean clearOnFlush,
764 boolean isTerracottaClustered, String terracottaValueMode, boolean terracottaCoherentReads) {
765
766 this(new CacheConfiguration(name, maxElementsInMemory)
767 .memoryStoreEvictionPolicy(memoryStoreEvictionPolicy)
768 .overflowToDisk(overflowToDisk)
769 .diskStorePath(diskStorePath)
770 .eternal(eternal)
771 .timeToLiveSeconds(timeToLiveSeconds)
772 .timeToIdleSeconds(timeToIdleSeconds)
773 .diskPersistent(diskPersistent)
774 .diskExpiryThreadIntervalSeconds(diskExpiryThreadIntervalSeconds)
775 .maxElementsOnDisk(maxElementsOnDisk)
776 .diskSpoolBufferSizeMB(diskSpoolBufferSizeMB)
777 .clearOnFlush(clearOnFlush)
778 .terracotta(new TerracottaConfiguration()
779 .clustered(isTerracottaClustered)
780 .valueMode(terracottaValueMode)
781 .coherentReads(terracottaCoherentReads)),
782 registeredEventListeners,
783 bootstrapCacheLoader);
784 }
785
786
787 /***
788 * A factory method to create a RegisteredEventListeners
789 */
790 private static void registerCacheListeners(CacheConfiguration cacheConfiguration,
791 RegisteredEventListeners registeredEventListeners) {
792 List cacheEventListenerConfigurations = cacheConfiguration.getCacheEventListenerConfigurations();
793 for (Object cacheEventListenerConfiguration : cacheEventListenerConfigurations) {
794 CacheConfiguration.CacheEventListenerFactoryConfiguration factoryConfiguration =
795 (CacheConfiguration.CacheEventListenerFactoryConfiguration) cacheEventListenerConfiguration;
796 CacheEventListener cacheEventListener = createCacheEventListener(factoryConfiguration);
797 registeredEventListeners.registerListener(cacheEventListener, factoryConfiguration.getListenFor());
798 }
799 }
800
801 /***
802 * A factory method to register cache extensions
803 *
804 * @param cacheConfiguration the cache configuration
805 * @param cache the cache
806 */
807 private static void registerCacheExtensions(CacheConfiguration cacheConfiguration, Ehcache cache) {
808 List cacheExtensionConfigurations = cacheConfiguration.getCacheExtensionConfigurations();
809 for (Object cacheExtensionConfiguration : cacheExtensionConfigurations) {
810 CacheConfiguration.CacheExtensionFactoryConfiguration factoryConfiguration =
811 (CacheConfiguration.CacheExtensionFactoryConfiguration) cacheExtensionConfiguration;
812 CacheExtension cacheExtension = createCacheExtension(factoryConfiguration, cache);
813 cache.registerCacheExtension(cacheExtension);
814 }
815 }
816
817 /***
818 * A factory method to register cache Loaders
819 *
820 * @param cacheConfiguration the cache configuration
821 * @param cache the cache
822 */
823 private static void registerCacheLoaders(CacheConfiguration cacheConfiguration, Ehcache cache) {
824 List cacheLoaderConfigurations = cacheConfiguration.getCacheLoaderConfigurations();
825 for (Object cacheLoaderConfiguration : cacheLoaderConfigurations) {
826 CacheConfiguration.CacheLoaderFactoryConfiguration factoryConfiguration =
827 (CacheConfiguration.CacheLoaderFactoryConfiguration) cacheLoaderConfiguration;
828 CacheLoader cacheLoader = createCacheLoader(factoryConfiguration, cache);
829 cache.registerCacheLoader(cacheLoader);
830 }
831 }
832
833 /***
834 * A factory method to register cache writers
835 *
836 * @param cacheConfiguration the cache configuration
837 * @param cache the cache
838 */
839 private static void registerCacheWriter(CacheConfiguration cacheConfiguration, Ehcache cache) {
840 CacheWriterConfiguration config = cacheConfiguration.getCacheWriterConfiguration();
841 if (config != null) {
842 CacheWriter cacheWriter = createCacheWriter(config, cache);
843 cache.registerCacheWriter(cacheWriter);
844 }
845 }
846
847
848 /***
849 * Tries to load the class specified otherwise defaults to null.
850 *
851 * @param factoryConfiguration
852 */
853 private static CacheEventListener createCacheEventListener(
854 CacheConfiguration.CacheEventListenerFactoryConfiguration factoryConfiguration) {
855 String className = null;
856 CacheEventListener cacheEventListener = null;
857 if (factoryConfiguration != null) {
858 className = factoryConfiguration.getFullyQualifiedClassPath();
859 }
860 if (className == null) {
861 LOG.debug("CacheEventListener factory not configured. Skipping...");
862 } else {
863 CacheEventListenerFactory factory = (CacheEventListenerFactory)
864 ClassLoaderUtil.createNewInstance(className);
865 Properties properties =
866
867 PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
868 factoryConfiguration.getPropertySeparator());
869 cacheEventListener =
870 factory.createCacheEventListener(properties);
871 }
872 return cacheEventListener;
873 }
874
875 /***
876 * Tries to load the class specified otherwise defaults to null.
877 *
878 * @param factoryConfiguration
879 */
880 private static CacheExtension createCacheExtension(
881 CacheConfiguration.CacheExtensionFactoryConfiguration factoryConfiguration, Ehcache cache) {
882 String className = null;
883 CacheExtension cacheExtension = null;
884 if (factoryConfiguration != null) {
885 className = factoryConfiguration.getFullyQualifiedClassPath();
886 }
887 if (className == null) {
888 LOG.debug("CacheExtension factory not configured. Skipping...");
889 } else {
890 CacheExtensionFactory factory = (CacheExtensionFactory) ClassLoaderUtil.createNewInstance(className);
891 Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
892 factoryConfiguration.getPropertySeparator());
893 cacheExtension = factory.createCacheExtension(cache, properties);
894 }
895 return cacheExtension;
896 }
897
898 /***
899 * Tries to load the class specified otherwise defaults to null.
900 *
901 * @param factoryConfiguration
902 */
903 private static CacheLoader createCacheLoader(
904 CacheConfiguration.CacheLoaderFactoryConfiguration factoryConfiguration, Ehcache cache) {
905 String className = null;
906 CacheLoader cacheLoader = null;
907 if (factoryConfiguration != null) {
908 className = factoryConfiguration.getFullyQualifiedClassPath();
909 }
910 if (className == null) {
911 LOG.debug("CacheLoader factory not configured. Skipping...");
912 } else {
913 CacheLoaderFactory factory = (CacheLoaderFactory) ClassLoaderUtil.createNewInstance(className);
914 Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
915 factoryConfiguration.getPropertySeparator());
916 cacheLoader = factory.createCacheLoader(cache, properties);
917 }
918 return cacheLoader;
919 }
920
921 /***
922 * Tries to load the class specified otherwise defaults to null.
923 *
924 * @param config
925 */
926 private static CacheWriter createCacheWriter(CacheWriterConfiguration config, Ehcache cache) {
927 String className = null;
928 CacheWriter cacheWriter = null;
929 CacheWriterConfiguration.CacheWriterFactoryConfiguration factoryConfiguration = config.getCacheWriterFactoryConfiguration();
930 if (factoryConfiguration != null) {
931 className = factoryConfiguration.getFullyQualifiedClassPath();
932 }
933 if (null == className) {
934 LOG.debug("CacheWriter factory not configured. Skipping...");
935 } else {
936 CacheWriterFactory factory = (CacheWriterFactory) ClassLoaderUtil.createNewInstance(className);
937 Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
938 factoryConfiguration.getPropertySeparator());
939 if (null == properties) {
940 properties = new Properties();
941 }
942 cacheWriter = factory.createCacheWriter(cache, properties);
943 }
944 return cacheWriter;
945 }
946
947 /***
948 * Tries to load a BootstrapCacheLoader from the class specified.
949 *
950 * @return If there is none returns null.
951 */
952 private static final BootstrapCacheLoader createBootstrapCacheLoader(
953 CacheConfiguration.BootstrapCacheLoaderFactoryConfiguration factoryConfiguration) throws CacheException {
954 String className = null;
955 BootstrapCacheLoader bootstrapCacheLoader = null;
956 if (factoryConfiguration != null) {
957 className = factoryConfiguration.getFullyQualifiedClassPath();
958 }
959 if (className == null || className.length() == 0) {
960 LOG.debug("No BootstrapCacheLoaderFactory class specified. Skipping...");
961 } else {
962 BootstrapCacheLoaderFactory factory = (BootstrapCacheLoaderFactory)
963 ClassLoaderUtil.createNewInstance(className);
964 Properties properties = PropertyUtil.parseProperties(factoryConfiguration.getProperties(),
965 factoryConfiguration.getPropertySeparator());
966 return factory.createBootstrapCacheLoader(properties);
967 }
968 return bootstrapCacheLoader;
969 }
970
971 /***
972 * Get the TransactionManagerLookup implementation used to lookup the TransactionManager.
973 * This is generally only set for XA transactional caches
974 * @return The {@link net.sf.ehcache.transaction.manager.TransactionManagerLookup} instance
975 */
976 public TransactionManagerLookup getTransactionManagerLookup() {
977 return transactionManagerLookup;
978 }
979
980 /***
981 * Sets the TransactionManagerLookup that needs to be used for this cache to lookup the TransactionManager
982 * This needs to be set before {@link Cache#initialise()} is called
983 * @param lookup The {@link net.sf.ehcache.transaction.manager.TransactionManagerLookup} instance
984 */
985 public void setTransactionManagerLookup(TransactionManagerLookup lookup) {
986 TransactionManagerLookup oldValue = getTransactionManagerLookup();
987 this.transactionManagerLookup = lookup;
988 firePropertyChange("TransactionManagerLookup", oldValue, lookup);
989 }
990
991 /***
992 * Newly created caches do not have a {@link net.sf.ehcache.store.Store}.
993 * <p/>
994 * This method creates the store and makes the cache ready to accept elements
995 */
996 public void initialise() {
997 synchronized (this) {
998 if (!cacheStatus.canInitialize()) {
999 throw new IllegalStateException("Cannot initialise the " + configuration.getName()
1000 + " cache because its status is not STATUS_UNINITIALISED");
1001 }
1002
1003
1004 if (getCacheManager() != null
1005 && getCacheManager().getConfiguration().isMaxBytesLocalHeapSet()
1006 && configuration.getMaxElementsInMemory() > 0) {
1007 throw new InvalidConfigurationException(configuration.getName() +
1008 ": MaxElementsInMemory is not compatible with " +
1009 "MaxBytesOnHeap set on cache manager");
1010 }
1011 if (configuration.getMaxBytesLocalHeap() > 0 && configuration.getMaxElementsInMemory() > 0) {
1012 throw new InvalidConfigurationException(configuration.getName() +
1013 ": MaxElementsInMemory is not compatible with " +
1014 "MaxBytesOnHeap set on cache");
1015 }
1016
1017
1018 Pool onHeapPool;
1019 if (configuration.getMaxBytesLocalHeap() > 0) {
1020 PoolEvictor<PoolableStore> evictor = new FromLargestCacheOnHeapPoolEvictor();
1021 SizeOfEngine sizeOfEngine = cacheManager.createSizeOfEngine(this);
1022 onHeapPool = new BoundedPool(configuration.getMaxBytesLocalHeap(), evictor, sizeOfEngine);
1023 } else if (getCacheManager() != null && getCacheManager().getConfiguration().isMaxBytesLocalHeapSet()) {
1024 onHeapPool = getCacheManager().getOnHeapPool();
1025 } else {
1026 onHeapPool = new UnboundedPool();
1027 if (configuration.getMaxElementsInMemory() == 0) {
1028 LOG.warn("Cache: " + configuration.getName() +
1029 " has a maxElementsInMemory of 0. " +
1030 "In Ehcache 2.0 this has been changed to mean a store" +
1031 " with no capacity limit. Set it to 1 if you want" +
1032 " no elements cached in memory");
1033 }
1034 }
1035
1036
1037 Pool onDiskPool;
1038 if (configuration.getMaxBytesLocalDisk() > 0) {
1039 PoolEvictor<PoolableStore> evictor = new FromLargestCacheOnDiskPoolEvictor();
1040 onDiskPool = new BoundedPool(configuration.getMaxBytesLocalDisk(), evictor, null);
1041 } else if (getCacheManager() != null && getCacheManager().getConfiguration().isMaxBytesLocalDiskSet()) {
1042 onDiskPool = getCacheManager().getOnDiskPool();
1043 } else {
1044 onDiskPool = new UnboundedPool();
1045 }
1046
1047
1048
1049 ReadWriteCopyStrategy<Element> copyStrategy = null;
1050 if (configuration.getTransactionalMode().isTransactional()) {
1051 configuration.getCopyStrategyConfiguration().setCopyStrategyInstance(null);
1052 copyStrategy = configuration.getCopyStrategyConfiguration().getCopyStrategyInstance();
1053 configuration.getCopyStrategyConfiguration().setCopyStrategyInstance(new ImmutableValueElementCopyStrategy());
1054 }
1055 if (configuration.getTransactionalMode().isTransactional()
1056 && configuration.isTerracottaClustered()
1057 && configuration.getTerracottaConfiguration().getValueMode() != TerracottaConfiguration.ValueMode.SERIALIZATION) {
1058 throw new CacheException("To be transactional, a Terracotta clustered cache needs to be in Serialization value mode");
1059 }
1060 final Store store;
1061 if (isTerracottaClustered()) {
1062 checkClusteredConfig();
1063 int maxConcurrency = Integer.getInteger(EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY_PROP,
1064 DEFAULT_EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY);
1065 if (getCacheConfiguration().getTerracottaConfiguration().getConcurrency() > maxConcurrency) {
1066 throw new InvalidConfigurationException("Maximum supported concurrency for Terracotta clustered caches is "
1067 + maxConcurrency + ". Please reconfigure cache '" + getName() + "' with concurrency value <= " + maxConcurrency
1068 + " or use system property '" + EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY_PROP + "' to override the default");
1069 }
1070 if (getCacheConfiguration().getMaxElementsOnDisk() == 0) {
1071 LOG.warn("Performance may degrade and server disks could run out of space!\nThe distributed cache {} does not have " +
1072 "maxElementsOnDisk set. Failing to set maxElementsOnDisk could mean no eviction of its elements from the " +
1073 "Terracotta Server Array disk store. To avoid this, set maxElementsOnDisk to a non-zero value.", getName());
1074 }
1075 if (!getCacheConfiguration().getTerracottaConfiguration().isStorageStrategySet()) {
1076 getCacheConfiguration().getTerracottaConfiguration().storageStrategy(
1077 TerracottaClient.getTerracottaDefaultStrategyForCurrentRuntime(getCacheConfiguration()));
1078 }
1079 Store tempStore = cacheManager.createTerracottaStore(this);
1080 if (!(tempStore instanceof TerracottaStore)) {
1081 throw new CacheException(
1082 "CacheManager should create instances of TerracottaStore for Terracotta Clustered caches instead of - "
1083 + (tempStore == null ? "null" : tempStore.getClass().getName()));
1084 }
1085 TerracottaStore terracottaStore = (TerracottaStore) makeXaStrictTransactionalIfNeeded(tempStore, copyStrategy);
1086
1087 NonstopConfiguration nonstopConfig = getCacheConfiguration().getTerracottaConfiguration().getNonstopConfiguration();
1088
1089 if (nonstopConfig != null) {
1090 nonstopConfig.freezeConfig();
1091 }
1092 if (getCacheConfiguration().getTerracottaConfiguration().isNonstopEnabled()) {
1093 nonstopActiveDelegateHolder.terracottaStoreInitialized(terracottaStore);
1094 store = nonstopActiveDelegateHolder.getNonstopStore();
1095 } else {
1096 store = terracottaStore;
1097 }
1098 } else if (getCacheConfiguration().isOverflowToOffHeap()) {
1099 store = createOffHeapStore(onHeapPool, onDiskPool, copyStrategy);
1100 } else {
1101 if (useClassicLru && configuration.getMemoryStoreEvictionPolicy().equals(MemoryStoreEvictionPolicy.LRU)) {
1102 Store disk = createDiskStore();
1103 store = makeXaStrictTransactionalIfNeeded(new LegacyStoreWrapper(
1104 new LruMemoryStore(this, disk), disk, registeredEventListeners, configuration), copyStrategy);
1105 } else {
1106 if (configuration.isDiskPersistent() || configuration.isOverflowToDisk()) {
1107 store = makeXaStrictTransactionalIfNeeded(DiskBackedMemoryStore.create(this, diskStorePath,
1108 onHeapPool, onDiskPool), copyStrategy);
1109 } else {
1110 store = makeXaStrictTransactionalIfNeeded(MemoryOnlyStore.create(this, onHeapPool), copyStrategy);
1111 }
1112 }
1113 }
1114
1115
1116
1117
1118 if (configuration.isXaTransactional()) {
1119 SoftLockFactory softLockFactory = cacheManager.createSoftLockFactory(this);
1120 LocalTransactionStore localTransactionStore = new LocalTransactionStore(getCacheManager().getTransactionController(),
1121 softLockFactory, this, store, copyStrategy);
1122 this.compoundStore = new JtaLocalTransactionStore(localTransactionStore, transactionManagerLookup,
1123 cacheManager.getTransactionController());
1124 } else if (configuration.isLocalTransactional()) {
1125 SoftLockFactory softLockFactory = cacheManager.createSoftLockFactory(this);
1126 this.compoundStore = new LocalTransactionStore(getCacheManager().getTransactionController(), softLockFactory, this, store,
1127 copyStrategy);
1128 } else {
1129 this.compoundStore = store;
1130 }
1131 Map<String, AttributeExtractor> extractors = new HashMap<String, AttributeExtractor>();
1132 for (SearchAttribute sa : configuration.getSearchAttributes().values()) {
1133 extractors.put(sa.getName(), sa.constructExtractor());
1134 }
1135 compoundStore.setAttributeExtractors(extractors);
1136 this.cacheWriterManager = configuration.getCacheWriterConfiguration().getWriteMode().createWriterManager(this);
1137 initialiseCacheWriterManager(false);
1138 cacheStatus.changeState(Status.STATUS_ALIVE);
1139 initialiseRegisteredCacheExtensions();
1140 initialiseRegisteredCacheLoaders();
1141 initialiseRegisteredCacheWriter();
1142
1143
1144
1145 getCacheEventNotificationService().registerListener(liveCacheStatisticsData);
1146
1147 liveCacheStatisticsData.setStatisticsAccuracy(Statistics.STATISTICS_ACCURACY_BEST_EFFORT);
1148 liveCacheStatisticsData.setStatisticsEnabled(configuration.getStatistics());
1149
1150
1151 this.registerCacheUsageListener(sampledCacheStatistics);
1152
1153 if (isTerracottaClustered()) {
1154
1155
1156 cacheManager.createTerracottaEventReplicator(this);
1157 }
1158
1159 Object context = compoundStore.getInternalContext();
1160 if (context instanceof CacheLockProvider) {
1161 lockProvider = (CacheLockProvider) context;
1162 } else {
1163 this.lockProvider = new StripedReadWriteLockSync(StripedReadWriteLockSync.DEFAULT_NUMBER_OF_MUTEXES);
1164 }
1165 }
1166
1167 compoundStore.addStoreListener(this);
1168
1169 if (LOG.isDebugEnabled()) {
1170 LOG.debug("Initialised cache: " + configuration.getName());
1171 }
1172
1173 if (disabled) {
1174 LOG.warn("Cache: " + configuration.getName() + " is disabled because the " + NET_SF_EHCACHE_DISABLED
1175 + " property was set to true. No elements will be added to the cache.");
1176 }
1177 }
1178
1179 private Store createOffHeapStore(Pool onHeapPool, Pool onDiskPool, ReadWriteCopyStrategy<Element> copyStrategy) {
1180 try {
1181 Class<Store> offHeapStoreClass = ClassLoaderUtil.loadClass(OFF_HEAP_STORE_CLASSNAME);
1182
1183 try {
1184 return makeXaStrictTransactionalIfNeeded((Store) offHeapStoreClass.getMethod("create",
1185 Ehcache.class, String.class, Pool.class, Pool.class)
1186 .invoke(null, this, diskStorePath, onHeapPool, onDiskPool), copyStrategy);
1187 } catch (NoSuchMethodException e) {
1188 throw new CacheException("Cache: " + configuration.getName() + " cannot find static factory"
1189 + " method create(Ehcache, String)" + " in store class " + OFF_HEAP_STORE_CLASSNAME, e);
1190 } catch (InvocationTargetException e) {
1191 Throwable cause = e.getCause();
1192 if (cause instanceof CacheException) {
1193 throw (CacheException) cause;
1194 } else {
1195 throw new CacheException("Cache: " + configuration.getName() + " cannot instantiate store "
1196 + OFF_HEAP_STORE_CLASSNAME, cause);
1197 }
1198 } catch (IllegalAccessException e) {
1199 throw new CacheException("Cache: " + configuration.getName() + " cannot instantiate store "
1200 + OFF_HEAP_STORE_CLASSNAME, e);
1201 }
1202 } catch (ClassNotFoundException e) {
1203 throw new CacheException("Cache " + configuration.getName()
1204 + " cannot be configured because the off-heap store class could not be found. "
1205 + "You must use an enterprise version of Ehcache to successfully enable overflowToOffHeap.");
1206 }
1207 }
1208
1209 private void checkClusteredConfig() {
1210 final Consistency consistency = getCacheConfiguration().getTerracottaConfiguration().getConsistency();
1211 final boolean coherent = getCacheConfiguration().getTerracottaConfiguration().isCoherent();
1212 if (getCacheConfiguration().isOverflowToOffHeap()) {
1213 throw new InvalidConfigurationException(
1214 "Terracotta clustered caches with local overflow to offheap are not supported yet."
1215 + " You can fix this by disabling overflow to offheap for clustered caches.");
1216 }
1217 if (getCacheConfiguration().getTerracottaConfiguration().isSynchronousWrites() && consistency == Consistency.EVENTUAL) {
1218 throw new InvalidConfigurationException(
1219 "Terracotta clustered caches with eventual consistency and synchronous writes are not supported yet."
1220 + " You can fix this by either making the cache in 'strong' consistency mode "
1221 + "(<terracotta consistency=\"strong\"/>) or turning off synchronous writes.");
1222 }
1223 if (getCacheConfiguration().getTransactionalMode().isTransactional() && consistency == Consistency.EVENTUAL) {
1224 throw new InvalidConfigurationException("Consistency should be " + Consistency.STRONG
1225 + " when cache is configured with transactions enabled. "
1226 + "You can fix this by either making the cache in 'strong' consistency mode "
1227 + "(<terracotta consistency=\"strong\"/>) or turning off transactions.");
1228 }
1229 if (getCacheConfiguration().getTransactionalMode().isTransactional()
1230 && !getCacheConfiguration().getTransactionalMode().equals(CacheConfiguration.TransactionalMode.XA_STRICT)
1231 && getCacheConfiguration().getTerracottaConfiguration().isNonstopEnabled()) {
1232 LOG.warn("Cache: " + configuration.getName() + " configured both NonStop and transactional non xa_strict."
1233 + " NonStop features won't work for this cache!");
1234 }
1235 if ((coherent && consistency == Consistency.EVENTUAL) || (!coherent && consistency == Consistency.STRONG)) {
1236 throw new InvalidConfigurationException("Coherent and consistency attribute values are conflicting. "
1237 + "Please remove the coherent attribute as its deprecated.");
1238 }
1239 if (getCacheConfiguration().getTerracottaConfiguration().getStorageStrategy() == StorageStrategy.CLASSIC
1240 && !getCacheConfiguration().getTerracottaConfiguration().isLocalCacheEnabled()) {
1241 throw new InvalidConfigurationException("Local Cache cannot be disabled with " + StorageStrategy.CLASSIC
1242 + " Storage strategy. Please enable local cache or use " + StorageStrategy.DCV2);
1243 }
1244 }
1245
1246
1247
1248
1249 private Store makeXaStrictTransactionalIfNeeded(Store clusteredStore, ReadWriteCopyStrategy<Element> copyStrategy) {
1250 Store wrappedStore;
1251
1252 if (configuration.isXaStrictTransactional()) {
1253 if (transactionManagerLookup.getTransactionManager() == null) {
1254 throw new CacheException("You've configured cache " + cacheManager.getName() + "."
1255 + configuration.getName() + " to be transactional, but no TransactionManager could be found!");
1256 }
1257
1258 if (configuration.isTerracottaClustered()) {
1259 configuration.getTerracottaConfiguration().setCacheXA(true);
1260 }
1261 SoftLockFactory softLockFactory = cacheManager.createSoftLockFactory(this);
1262 TransactionIDFactory transactionIDFactory = cacheManager.createTransactionIDFactory();
1263
1264
1265 EhcacheXAResource xaResource = new EhcacheXAResourceImpl(this, clusteredStore, transactionManagerLookup,
1266 softLockFactory, transactionIDFactory);
1267 transactionManagerLookup.register(xaResource);
1268
1269 wrappedStore = new XATransactionStore(transactionManagerLookup, softLockFactory, transactionIDFactory, this, clusteredStore,
1270 copyStrategy);
1271 } else {
1272 wrappedStore = clusteredStore;
1273 }
1274
1275 return wrappedStore;
1276 }
1277
1278 private CacheCluster getCacheCluster() {
1279 CacheCluster cacheCluster;
1280 try {
1281 cacheCluster = getCacheManager().getCluster(ClusterScheme.TERRACOTTA);
1282 } catch (ClusterSchemeNotAvailableException e) {
1283 LOG.info("Terracotta ClusterScheme is not available, using ClusterScheme.NONE");
1284 cacheCluster = getCacheManager().getCluster(ClusterScheme.NONE);
1285 }
1286 return cacheCluster;
1287 }
1288
1289 /***
1290 * The CacheWriterManager's initialisation can be deferred until an actual CacheWriter has been registered.
1291 * <p/>
1292 * 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.
1293 *
1294 * @param imperative indicates whether it's imperative for the cache writer manager to be initialised before operations can continue
1295 * @throws CacheException when the CacheWriterManager couldn't be initialised but it was imperative to do so
1296 */
1297 private void initialiseCacheWriterManager(boolean imperative) throws CacheException {
1298 if (!cacheWriterManagerInitFlag.get()) {
1299 cacheWriterManagerInitLock.lock();
1300 try {
1301 if (!cacheWriterManagerInitFlag.get()) {
1302 if (cacheWriterManager != null && registeredCacheWriter != null) {
1303 cacheWriterManager.init(this);
1304 cacheWriterManagerInitFlag.set(true);
1305 } else if (imperative) {
1306 throw new CacheException("Cache: " + configuration.getName() + " was being used with cache writer " +
1307 "features, but it wasn't properly registered beforehand.");
1308 }
1309 }
1310 } finally {
1311 cacheWriterManagerInitLock.unlock();
1312 }
1313 }
1314 }
1315
1316 /***
1317 * {@inheritDoc}
1318 */
1319 public CacheWriterManager getWriterManager() {
1320 return cacheWriterManager;
1321 }
1322
1323 /***
1324 * Creates a disk store when either:
1325 * <ol>
1326 * <li>overflowToDisk is enabled
1327 * <li>diskPersistent is enabled
1328 * </ol>
1329 *
1330 * @return the disk store
1331 */
1332 protected DiskStore createDiskStore() {
1333 if (isDiskStore()) {
1334 return DiskStore.create(this, diskStorePath);
1335 } else {
1336 return null;
1337 }
1338 }
1339
1340 /***
1341 * Whether this cache uses a disk store
1342 *
1343 * @return true if the cache either overflows to disk or is disk persistent
1344 */
1345 protected boolean isDiskStore() {
1346 return configuration.isOverflowToDisk() || configuration.isDiskPersistent();
1347 }
1348
1349 /***
1350 * Indicates whether this cache is clustered by Terracotta
1351 *
1352 * @return {@code true} when the cache is clustered by Terracotta; or {@code false} otherwise
1353 */
1354 public boolean isTerracottaClustered() {
1355 return configuration.isTerracottaClustered();
1356 }
1357
1358 /***
1359 * Bootstrap command. This must be called after the Cache is initialised, during
1360 * CacheManager initialisation. If loads are synchronous, they will complete before the CacheManager
1361 * initialise completes, otherwise they will happen in the background.
1362 */
1363 public void bootstrap() {
1364 if (!disabled && bootstrapCacheLoader != null) {
1365 bootstrapCacheLoader.load(this);
1366 }
1367
1368 }
1369
1370 /***
1371 * Put an element in the cache.
1372 * <p/>
1373 * Resets the access statistics on the element, which would be the case if it has previously been
1374 * gotten from a cache, and is now being put back.
1375 * <p/>
1376 * Also notifies the CacheEventListener that:
1377 * <ul>
1378 * <li>the element was put, but only if the Element was actually put.
1379 * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
1380 * if it was requested
1381 * </ul>
1382 * <p/>
1383 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1384 * This exception should be caught in those circumstances.
1385 *
1386 * @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
1387 * <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
1388 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1389 * @throws CacheException
1390 */
1391 public final void put(Element element) throws IllegalArgumentException, IllegalStateException,
1392 CacheException {
1393 put(element, false);
1394 }
1395
1396 /***
1397 * {@inheritDoc}
1398 */
1399 public void putAll(Collection<Element> elements) throws IllegalArgumentException, IllegalStateException, CacheException {
1400 putAll(elements, false);
1401 }
1402
1403
1404 /***
1405 * Put an element in the cache.
1406 * <p/>
1407 * Resets the access statistics on the element, which would be the case if it has previously been
1408 * gotten from a cache, and is now being put back.
1409 * <p/>
1410 * Also notifies the CacheEventListener that:
1411 * <ul>
1412 * <li>the element was put, but only if the Element was actually put.
1413 * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
1414 * if it was requested
1415 * </ul>
1416 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1417 * This exception should be caught in those circumstances.
1418 *
1419 * @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
1420 * <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
1421 * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
1422 * further notification to doNotNotifyCacheReplicators cache peers
1423 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1424 * @throws IllegalArgumentException if the element is null
1425 */
1426 public final void put(Element element, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
1427 IllegalStateException, CacheException {
1428 putInternal(element, doNotNotifyCacheReplicators, false);
1429 }
1430
1431 /***
1432 * {@inheritDoc}
1433 */
1434 private void putAll(Collection<Element> elements, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
1435 IllegalStateException, CacheException {
1436 putAllInternal(elements, doNotNotifyCacheReplicators);
1437 }
1438
1439 /***
1440 * {@inheritDoc}
1441 */
1442 public void putWithWriter(Element element) throws IllegalArgumentException, IllegalStateException, CacheException {
1443 putInternal(element, false, true);
1444 }
1445
1446 private void putInternal(Element element, boolean doNotNotifyCacheReplicators, boolean useCacheWriter) {
1447 if (useCacheWriter) {
1448 initialiseCacheWriterManager(true);
1449 }
1450
1451 checkStatus();
1452
1453 if (disabled) {
1454 return;
1455 }
1456
1457 if (element == null) {
1458 if (doNotNotifyCacheReplicators) {
1459
1460 LOG.debug("Element from replicated put is null. This happens because the element is a SoftReference" +
1461 " and it has been collected. Increase heap memory on the JVM or set -Xms to be the same as " +
1462 "-Xmx to avoid this problem.");
1463
1464 }
1465
1466 return;
1467 }
1468
1469
1470 if (element.getObjectKey() == null) {
1471
1472 return;
1473 }
1474
1475 element.resetAccessStatistics();
1476
1477 applyDefaultsToElementWithoutLifespanSet(element);
1478
1479 backOffIfDiskSpoolFull();
1480
1481 if (useCacheWriter) {
1482 boolean elementExists = false;
1483 boolean notifyListeners = true;
1484 try {
1485 elementExists = !compoundStore.putWithWriter(element, cacheWriterManager);
1486 } catch (StoreUpdateException e) {
1487 elementExists = e.isUpdate();
1488 notifyListeners = configuration.getCacheWriterConfiguration().getNotifyListenersOnException();
1489 RuntimeException cause = e.getCause();
1490 if (cause instanceof CacheWriterManagerException) {
1491 throw ((CacheWriterManagerException)cause).getCause();
1492 }
1493 throw cause;
1494 } finally {
1495 if (elementExists) {
1496 element.updateUpdateStatistics();
1497 }
1498 if (notifyListeners) {
1499 notifyPutInternalListeners(element, doNotNotifyCacheReplicators, elementExists);
1500 }
1501 }
1502 } else {
1503 boolean elementExists = !compoundStore.put(element);
1504 if (elementExists) {
1505 element.updateUpdateStatistics();
1506 }
1507 notifyPutInternalListeners(element, doNotNotifyCacheReplicators, elementExists);
1508 }
1509 }
1510
1511 private void putAllInternal(Collection<Element> elements, boolean doNotNotifyCacheReplicators) {
1512 checkStatus();
1513
1514 if (disabled || elements.isEmpty()) {
1515 return;
1516 }
1517
1518 backOffIfDiskSpoolFull();
1519
1520 compoundStore.putAll(elements);
1521 for (Element element : elements) {
1522 element.resetAccessStatistics();
1523 applyDefaultsToElementWithoutLifespanSet(element);
1524 notifyPutInternalListeners(element, doNotNotifyCacheReplicators, false);
1525 }
1526 }
1527
1528 private void notifyPutInternalListeners(Element element, boolean doNotNotifyCacheReplicators, boolean elementExists) {
1529 if (elementExists) {
1530 registeredEventListeners.notifyElementUpdated(element, doNotNotifyCacheReplicators);
1531 } else {
1532 registeredEventListeners.notifyElementPut(element, doNotNotifyCacheReplicators);
1533 }
1534 }
1535
1536 /***
1537 * wait outside of synchronized block so as not to block readers
1538 * If the disk store spool is full wait a short time to give it a chance to
1539 * catch up.
1540 * todo maybe provide a warning if this is continually happening or monitor via JMX
1541 */
1542 private void backOffIfDiskSpoolFull() {
1543
1544 if (compoundStore.bufferFull()) {
1545
1546 try {
1547 Thread.sleep(BACK_OFF_TIME_MILLIS);
1548 } catch (InterruptedException e) {
1549 Thread.currentThread().interrupt();
1550 }
1551 }
1552 }
1553
1554 private void applyDefaultsToElementWithoutLifespanSet(Element element) {
1555 if (!element.isLifespanSet()) {
1556 element.setLifespanDefaults(TimeUtil.convertTimeToInt(configuration.getTimeToIdleSeconds()),
1557 TimeUtil.convertTimeToInt(configuration.getTimeToLiveSeconds()),
1558 configuration.isEternal());
1559 }
1560 }
1561
1562 /***
1563 * Put an element in the cache, without updating statistics, or updating listeners. This is meant to be used
1564 * in conjunction with {@link #getQuiet}.
1565 * Synchronization is handled within the method.
1566 * <p/>
1567 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
1568 * This exception should be caught in those circumstances.
1569 * <p/>
1570 *
1571 * @param element A cache Element. If Serializable it can fully participate in replication and the DiskStore. If it is
1572 * <code>null</code> or the key is <code>null</code>, it is ignored as a NOOP.
1573 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1574 * @throws IllegalArgumentException if the element is null
1575 */
1576 public final void putQuiet(Element element) throws IllegalArgumentException, IllegalStateException,
1577 CacheException {
1578 checkStatus();
1579
1580 if (disabled) {
1581 return;
1582 }
1583
1584 if (element == null || element.getObjectKey() == null) {
1585
1586 return;
1587 }
1588
1589 applyDefaultsToElementWithoutLifespanSet(element);
1590
1591 compoundStore.put(element);
1592 }
1593
1594 /***
1595 * Gets an element from the cache. Updates Element Statistics
1596 * <p/>
1597 * Note that the Element's lastAccessTime is always the time of this get.
1598 * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
1599 * <p/>
1600 * Synchronization is handled within the method.
1601 *
1602 * @param key a serializable value. Null keys are not stored so get(null) always returns null
1603 * @return the element, or null, if it does not exist.
1604 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1605 * @see #isExpired
1606 */
1607 public final Element get(Serializable key) throws IllegalStateException, CacheException {
1608 return get((Object) key);
1609 }
1610
1611
1612 /***
1613 * Gets an element from the cache. Updates Element Statistics
1614 * <p/>
1615 * Note that the Element's lastAccessTime is always the time of this get.
1616 * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
1617 * <p/>
1618 * Synchronization is handled within the method.
1619 *
1620 * @param key an Object value
1621 * @return the element, or null, if it does not exist.
1622 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1623 * @see #isExpired
1624 * @since 1.2
1625 */
1626 public final Element get(Object key) throws IllegalStateException, CacheException {
1627 checkStatus();
1628
1629 if (disabled) {
1630 return null;
1631 }
1632
1633 if (isStatisticsEnabled()) {
1634 long start = System.currentTimeMillis();
1635 Element element = searchInStoreWithStats(key);
1636
1637 long end = System.currentTimeMillis();
1638 liveCacheStatisticsData.addGetTimeMillis(end - start);
1639 return element;
1640 } else {
1641 return searchInStoreWithoutStats(key, false, true);
1642 }
1643 }
1644
1645 /***
1646 * {@inheritDoc}
1647 */
1648 public Map<Object, Element> getAll(Collection<Object> keys) throws IllegalStateException, CacheException {
1649 checkStatus();
1650
1651 if (disabled || keys.isEmpty()) {
1652 return null;
1653 }
1654
1655 Map<Object, Element> retMap = new HashMap<Object, Element>();
1656 for (Object key : keys) {
1657 Element element = get(key);
1658 retMap.put(key, element);
1659 }
1660 return retMap;
1661 }
1662
1663 /***
1664 * This method will return, from the cache, the Element associated with the argument "key".
1665 * <p/>
1666 * 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,
1667 * the one associated with the cache. If both are null, no load is performed and null is returned.
1668 * <p/>
1669 * 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.
1670 * <p/>
1671 * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
1672 * are synchronized.
1673 *
1674 * @param key key whose associated value is to be returned.
1675 * @param loader the override loader to use. If null, the cache's default loader will be used
1676 * @param loaderArgument an argument to pass to the CacheLoader.
1677 * @return an element if it existed or could be loaded, otherwise null
1678 * @throws CacheException
1679 */
1680 public Element getWithLoader(Object key, CacheLoader loader, Object loaderArgument) throws CacheException {
1681
1682 Element element = get(key);
1683 if (element != null) {
1684 return element;
1685 }
1686
1687 if (registeredCacheLoaders.size() == 0 && loader == null) {
1688 return null;
1689 }
1690
1691 try {
1692
1693 element = getQuiet(key);
1694 if (element != null) {
1695 return element;
1696 }
1697
1698
1699 long cacheLoaderTimeoutMillis = configuration.getCacheLoaderTimeoutMillis();
1700 final Object value;
1701 if (cacheLoaderTimeoutMillis > 0) {
1702 final Future<AtomicReference<Object>> future = asynchronousLoad(key, loader, loaderArgument);
1703 value = future.get(cacheLoaderTimeoutMillis, TimeUnit.MILLISECONDS).get();
1704 } else {
1705 value = loadValueUsingLoader(key, loader, loaderArgument);
1706 }
1707 if (value != null) {
1708 put(new Element(key, value), false);
1709 }
1710 } catch (TimeoutException e) {
1711 throw new LoaderTimeoutException("Timeout on load for key " + key, e);
1712 } catch (Exception e) {
1713 throw new CacheException("Exception on load for key " + key, e);
1714 }
1715 return getQuiet(key);
1716 }
1717
1718 /***
1719 * The load method provides a means to "pre-load" the cache. This method will, asynchronously, load the specified
1720 * object into the cache using the associated CacheLoader. If the object already exists in the cache, no action is
1721 * taken. If no loader is associated with the object, no object will be loaded into the cache. If a problem is
1722 * encountered during the retrieving or loading of the object, an exception should be logged. If the "arg" argument
1723 * is set, the arg object will be passed to the CacheLoader.load method. The cache will not dereference the object.
1724 * If no "arg" value is provided a null will be passed to the load method. The storing of null values in the cache
1725 * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding
1726 * the object in the cache. In both cases a null is returned.
1727 * <p/>
1728 * The Ehcache native API provides similar functionality to loaders using the
1729 * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1730 *
1731 * @param key key whose associated value to be loaded using the associated CacheLoader if this cache doesn't contain it.
1732 * @throws CacheException
1733 */
1734 public void load(final Object key) throws CacheException {
1735 if (registeredCacheLoaders.size() == 0) {
1736
1737 LOG.debug("The CacheLoader is null. Returning.");
1738 return;
1739 }
1740
1741 boolean existsOnCall = isKeyInCache(key);
1742 if (existsOnCall) {
1743
1744 LOG.debug("The key {} exists in the cache. Returning.", key);
1745 return;
1746 }
1747
1748 asynchronousPut(key, null, null);
1749 }
1750
1751 /***
1752 * The getAll method will return, from the cache, a Map of the objects associated with the Collection of keys in argument "keys".
1753 * If the objects are not in the cache, the associated cache loader will be called. If no loader is associated with an object,
1754 * a null is returned. If a problem is encountered during the retrieving or loading of the objects, an exception will be thrown.
1755 * If the "arg" argument is set, the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference
1756 * 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
1757 * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding the object in
1758 * the cache. In both cases a null is returned.
1759 * <p/>
1760 * <p/>
1761 * Note. If the getAll exceeds the maximum cache size, the returned map will necessarily be less than the number specified.
1762 * <p/>
1763 * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
1764 * are synchronized.
1765 * <p/>
1766 * The constructs package provides similar functionality using the
1767 * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1768 *
1769 * @param keys a collection of keys to be returned/loaded
1770 * @param loaderArgument an argument to pass to the CacheLoader.
1771 * @return a Map populated from the Cache. If there are no elements, an empty Map is returned.
1772 * @throws CacheException
1773 */
1774 public Map getAllWithLoader(Collection keys, Object loaderArgument) throws CacheException {
1775 if (keys == null) {
1776 return new HashMap(0);
1777 }
1778 Map<Object, Object> map = new HashMap<Object, Object>(keys.size());
1779
1780 List<Object> missingKeys = new ArrayList<Object>(keys.size());
1781
1782 if (registeredCacheLoaders.size() > 0) {
1783 Object key = null;
1784 try {
1785 map = new HashMap<Object, Object>(keys.size());
1786
1787 for (Object key1 : keys) {
1788 key = key1;
1789 Element element = get(key);
1790
1791 if (element != null) {
1792 map.put(key, element.getObjectValue());
1793 } else if (isKeyInCache(key)) {
1794 map.put(key, null);
1795 } else {
1796 missingKeys.add(key);
1797 }
1798 }
1799
1800
1801 Future future = asynchronousLoadAll(missingKeys, loaderArgument);
1802
1803
1804 long cacheLoaderTimeoutMillis = configuration.getCacheLoaderTimeoutMillis();
1805 if (cacheLoaderTimeoutMillis > 0) {
1806 try {
1807 future.get(cacheLoaderTimeoutMillis, TimeUnit.MILLISECONDS);
1808 } catch (TimeoutException e) {
1809 throw new LoaderTimeoutException("Timeout on load for key " + key, e);
1810 }
1811 } else {
1812 future.get();
1813 }
1814
1815
1816 for (Object missingKey : missingKeys) {
1817 key = missingKey;
1818 Element element = get(key);
1819 if (element != null) {
1820 map.put(key, element.getObjectValue());
1821 } else {
1822 map.put(key, null);
1823 }
1824 }
1825
1826 } catch (InterruptedException e) {
1827 throw new CacheException(e.getMessage() + " for key " + key, e);
1828 } catch (ExecutionException e) {
1829 throw new CacheException(e.getMessage() + " for key " + key, e);
1830 }
1831 } else {
1832 for (Object key : keys) {
1833 Element element = get(key);
1834 if (element != null) {
1835 map.put(key, element.getObjectValue());
1836 } else {
1837 map.put(key, null);
1838 }
1839 }
1840 }
1841 return map;
1842 }
1843
1844
1845 /***
1846 * The loadAll method provides a means to "pre load" objects into the cache. This method will, asynchronously, load
1847 * the specified objects into the cache using the associated cache loader(s). If the an object already exists in the
1848 * cache, no action is taken. If no loader is associated with the object, no object will be loaded into the cache.
1849 * If a problem is encountered during the retrieving or loading of the objects, an exception (to be defined)
1850 * should be logged. The getAll method will return, from the cache, a Map of the objects associated with the
1851 * Collection of keys in argument "keys". If the objects are not in the cache, the associated cache loader will be
1852 * called. If no loader is associated with an object, a null is returned. If a problem is encountered during the
1853 * retrieving or loading of the objects, an exception (to be defined) will be thrown. If the "arg" argument is set,
1854 * the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference the object.
1855 * If no "arg" value is provided a null will be passed to the loadAll method.
1856 * <p/>
1857 * keys - collection of the keys whose associated values to be loaded into this cache by using the associated
1858 * CacheLoader if this cache doesn't contain them.
1859 * <p/>
1860 * The Ehcache native API provides similar functionality to loaders using the
1861 * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
1862 */
1863 public void loadAll(final Collection keys, final Object argument) throws CacheException {
1864
1865 if (registeredCacheLoaders.size() == 0) {
1866
1867 LOG.debug("The CacheLoader is null. Returning.");
1868 return;
1869 }
1870 if (keys == null) {
1871 return;
1872 }
1873 asynchronousLoadAll(keys, argument);
1874 }
1875
1876 /***
1877 * Gets an element from the cache, without updating Element statistics. Cache statistics are
1878 * still updated. Listeners are not called.
1879 * <p/>
1880 *
1881 * @param key a serializable value
1882 * @return the element, or null, if it does not exist.
1883 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1884 * @see #isExpired
1885 */
1886 public final Element getQuiet(Serializable key) throws IllegalStateException, CacheException {
1887 return getQuiet((Object) key);
1888 }
1889
1890 /***
1891 * Gets an element from the cache, without updating Element statistics. Cache statistics are
1892 * not updated.
1893 * <p/>
1894 * Listeners are not called.
1895 *
1896 * @param key a serializable value
1897 * @return the element, or null, if it does not exist.
1898 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1899 * @see #isExpired
1900 * @since 1.2
1901 */
1902 public final Element getQuiet(Object key) throws IllegalStateException, CacheException {
1903 checkStatus();
1904 return searchInStoreWithoutStats(key, true, false);
1905 }
1906
1907 /***
1908 * Returns a list of all element keys in the cache, whether or not they are expired.
1909 * <p/>
1910 * The returned keys are unique and can be considered a set.
1911 * <p/>
1912 * The List returned is not live. It is a copy.
1913 * <p/>
1914 * The time taken is O(n). On a single CPU 1.8Ghz P4, approximately 8ms is required
1915 * for each 1000 entries.
1916 *
1917 * @return a list of {@link Object} keys
1918 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1919 */
1920 public final List getKeys() throws IllegalStateException, CacheException {
1921 checkStatus();
1922 return compoundStore.getKeys();
1923 }
1924
1925 /***
1926 * Returns a list of all element keys in the cache. Only keys of non-expired
1927 * elements are returned.
1928 * <p/>
1929 * The returned keys are unique and can be considered a set.
1930 * <p/>
1931 * The List returned is not live. It is a copy.
1932 * <p/>
1933 * The time taken is O(n), where n is the number of elements in the cache. On
1934 * a 1.8Ghz P4, the time taken is approximately 200ms per 1000 entries. This method
1935 * is not synchronized, because it relies on a non-live list returned from {@link #getKeys()}
1936 * , which is synchronised, and which takes 8ms per 1000 entries. This way
1937 * cache liveness is preserved, even if this method is very slow to return.
1938 * <p/>
1939 * Consider whether your usage requires checking for expired keys. Because
1940 * this method takes so long, depending on cache settings, the list could be
1941 * quite out of date by the time you get it.
1942 *
1943 * @return a list of {@link Object} keys
1944 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1945 */
1946 public final List getKeysWithExpiryCheck() throws IllegalStateException, CacheException {
1947 List allKeyList = getKeys();
1948
1949 ArrayList < Object > nonExpiredKeys = new ArrayList(allKeyList.size());
1950 for (Iterator iter = allKeyList.iterator(); iter.hasNext();) {
1951 Object key = iter.next();
1952 Element element = getQuiet(key);
1953 if (element != null) {
1954 nonExpiredKeys.add(key);
1955 }
1956 }
1957 nonExpiredKeys.trimToSize();
1958 return nonExpiredKeys;
1959 }
1960
1961
1962 /***
1963 * Returns a list of all elements in the cache, whether or not they are expired.
1964 * <p/>
1965 * The returned keys are not unique and may contain duplicates. If the cache is only
1966 * using the memory store, the list will be unique. If the disk store is being used
1967 * as well, it will likely contain duplicates, because of the internal store design.
1968 * <p/>
1969 * The List returned is not live. It is a copy.
1970 * <p/>
1971 * The time taken is O(log n). On a single CPU 1.8Ghz P4, approximately 6ms is required
1972 * for 1000 entries and 36 for 50000.
1973 * <p/>
1974 * This is the fastest getKeys method
1975 *
1976 * @return a list of {@link Object} keys
1977 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
1978 */
1979 public final List getKeysNoDuplicateCheck() throws IllegalStateException {
1980 checkStatus();
1981 return getKeys();
1982 }
1983
1984 private Element searchInStoreWithStats(Object key) {
1985 boolean wasInMemory = compoundStore.containsKeyInMemory(key);
1986 boolean wasOffHeap = false;
1987 boolean hasOffHeap = getCacheConfiguration().isOverflowToOffHeap();
1988 boolean isTCClustered = getCacheConfiguration().isTerracottaClustered();
1989 boolean hasOnDisk = isTCClustered || getCacheConfiguration().isOverflowToDisk() || getCacheConfiguration().isDiskPersistent();
1990 Element element;
1991
1992 if (!wasInMemory) {
1993 liveCacheStatisticsData.cacheMissInMemory();
1994 if (hasOffHeap) {
1995 wasOffHeap = compoundStore.containsKeyOffHeap(key);
1996 }
1997 }
1998
1999 element = compoundStore.get(key);
2000
2001 if (element != null) {
2002 if (isExpired(element)) {
2003 if (LOG.isDebugEnabled()) {
2004 LOG.debug(configuration.getName() + " cache hit, but element expired");
2005 }
2006 liveCacheStatisticsData.cacheMissExpired();
2007 tryRemoveImmediately(key, true);
2008 element = null;
2009 } else {
2010 element.updateAccessStatistics();
2011 if (LOG.isDebugEnabled()) {
2012 LOG.debug("Cache: " + getName() + " store hit for " + key);
2013 }
2014
2015 if (wasInMemory) {
2016 liveCacheStatisticsData.cacheHitInMemory();
2017 } else if (wasOffHeap) {
2018 liveCacheStatisticsData.cacheHitOffHeap();
2019 } else if (hasOffHeap) {
2020 liveCacheStatisticsData.cacheMissOffHeap();
2021 liveCacheStatisticsData.cacheHitOnDisk();
2022 } else {
2023 liveCacheStatisticsData.cacheHitOnDisk();
2024 }
2025 }
2026 } else {
2027 liveCacheStatisticsData.cacheMissNotFound();
2028 if (hasOffHeap) {
2029 liveCacheStatisticsData.cacheMissOffHeap();
2030 }
2031 if (hasOnDisk) {
2032 liveCacheStatisticsData.cacheMissOnDisk();
2033 }
2034
2035 if (LOG.isDebugEnabled()) {
2036 LOG.debug(configuration.getName() + " cache - Miss");
2037 }
2038 }
2039 return element;
2040 }
2041
2042 private Element searchInStoreWithoutStats(Object key, boolean quiet, boolean notifyListeners) {
2043 Element element = compoundStore.getQuiet(key);
2044
2045 if (element != null) {
2046 if (isExpired(element)) {
2047 tryRemoveImmediately(key, notifyListeners);
2048 element = null;
2049 } else if (!(quiet || skipUpdateAccessStatistics(element))) {
2050 element.updateAccessStatistics();
2051 }
2052 }
2053 return element;
2054 }
2055
2056 private void tryRemoveImmediately(final Object key, final boolean notifyListeners) {
2057 Sync syncForKey = ((CacheLockProvider)getInternalContext()).getSyncForKey(key);
2058 boolean writeLocked = false;
2059 try {
2060 writeLocked = syncForKey.tryLock(LockType.WRITE, 0);
2061 } catch (InterruptedException e) {
2062 Thread.currentThread().interrupt();
2063 } catch (Error e) {
2064 if (!(e.getClass().getName().equals("com.tc.exception.TCLockUpgradeNotSupportedError"))) {
2065 throw e;
2066 }
2067 }
2068 if (writeLocked) {
2069 try {
2070 removeInternal(key, true, notifyListeners, false, false);
2071 } finally {
2072 syncForKey.unlock(LockType.WRITE);
2073 }
2074 } else {
2075 if (LOG.isDebugEnabled()) {
2076 LOG.debug(configuration.getName() + " cache: element " + key + " expired, but couldn't be inline evicted");
2077 }
2078 }
2079 }
2080
2081 private boolean skipUpdateAccessStatistics(Element element) {
2082 return configuration.isFrozen() && element.isEternal()
2083 && (configuration.getMaxElementsInMemory() == 0)
2084 && (!configuration.isOverflowToDisk() || configuration.getMaxElementsOnDisk() == 0);
2085 }
2086
2087 /***
2088 * Removes an {@link Element} from the Cache. This also removes it from any
2089 * stores it may be in.
2090 * <p/>
2091 * Also notifies the CacheEventListener after the element was removed.
2092 * <p/>
2093 * Synchronization is handled within the method.
2094 * <p/>
2095 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
2096 * This exception should be caught in those circumstances.
2097 *
2098 * @param key the element key to operate on
2099 * @return true if the element was removed, false if it was not found in the cache
2100 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2101 */
2102 public final boolean remove(Serializable key) throws IllegalStateException {
2103 return remove((Object) key);
2104 }
2105
2106 /***
2107 * Removes an {@link Element} from the Cache. This also removes it from any
2108 * stores it may be in.
2109 * <p/>
2110 * Also notifies the CacheEventListener after the element was removed, but only if an Element
2111 * with the key actually existed.
2112 * <p/>
2113 * Synchronization is handled within the method.
2114 * <p/>
2115 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
2116 * This exception should be caught in those circumstances.
2117 * <p/>
2118 *
2119 * @param key the element key to operate on
2120 * @return true if the element was removed, false if it was not found in the cache
2121 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2122 * @since 1.2
2123 */
2124 public final boolean remove(Object key) throws IllegalStateException {
2125 return remove(key, false);
2126 }
2127
2128 /***
2129 * {@inheritDoc}
2130 */
2131 public void removeAll(final Collection<Object> keys) throws IllegalStateException {
2132 removeAll(keys, false);
2133 }
2134
2135
2136 /***
2137 * {@inheritDoc}
2138 */
2139 public final void removeAll(final Collection<Object> keys, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
2140 removeAllInternal(keys, false, true, doNotNotifyCacheReplicators);
2141 }
2142
2143 /***
2144 * Removes an {@link Element} from the Cache. This also removes it from any
2145 * stores it may be in.
2146 * <p/>
2147 * Also notifies the CacheEventListener after the element was removed, but only if an Element
2148 * with the key actually existed.
2149 * <p/>
2150 * Synchronization is handled within the method.
2151 * <p/>
2152 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
2153 * This exception should be caught in those circumstances.
2154 *
2155 * @param key the element key to operate on
2156 * @param doNotNotifyCacheReplicators whether the remove is coming from a doNotNotifyCacheReplicators cache peer, in which case this remove should not initiate a
2157 * further notification to doNotNotifyCacheReplicators cache peers
2158 * @return true if the element was removed, false if it was not found in the cache
2159 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2160 */
2161 public final boolean remove(Serializable key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
2162 return remove((Object) key, doNotNotifyCacheReplicators);
2163 }
2164
2165 /***
2166 * Removes an {@link Element} from the Cache. This also removes it from any
2167 * stores it may be in.
2168 * <p/>
2169 * Also notifies the CacheEventListener after the element was removed, but only if an Element
2170 * with the key actually existed.
2171 * <p/>
2172 * Synchronization is handled within the method.
2173 *
2174 * @param key the element key to operate on
2175 * @param doNotNotifyCacheReplicators whether the remove is coming from a doNotNotifyCacheReplicators cache peer, in which case this remove should not initiate a
2176 * further notification to doNotNotifyCacheReplicators cache peers
2177 * @return true if the element was removed, false if it was not found in the cache
2178 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2179 */
2180 public final boolean remove(Object key, boolean doNotNotifyCacheReplicators) throws IllegalStateException {
2181 return removeInternal(key, false, true, doNotNotifyCacheReplicators, false);
2182 }
2183
2184 /***
2185 * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
2186 * stores it may be in.
2187 * <p/>
2188 * Listeners are not called.
2189 *
2190 * @param key the element key to operate on
2191 * @return true if the element was removed, false if it was not found in the cache
2192 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2193 */
2194 public final boolean removeQuiet(Serializable key) throws IllegalStateException {
2195 return removeInternal(key, false, false, false, false);
2196 }
2197
2198 /***
2199 * Removes an {@link Element} from the Cache, without notifying listeners. This also removes it from any
2200 * stores it may be in.
2201 * <p/>
2202 * Listeners are not called.
2203 * <p/>
2204 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
2205 * This exception should be caught in those circumstances.
2206 *
2207 * @param key the element key to operate on
2208 * @return true if the element was removed, false if it was not found in the cache
2209 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2210 * @since 1.2
2211 */
2212 public final boolean removeQuiet(Object key) throws IllegalStateException {
2213 return removeInternal(key, false, false, false, false);
2214 }
2215
2216 /***
2217 * {@inheritDoc}
2218 */
2219 public boolean removeWithWriter(Object key) throws IllegalStateException {
2220 return removeInternal(key, false, true, false, true);
2221 }
2222
2223 /***
2224 * Removes or expires an {@link Element} from the Cache after an attempt to get it determined that it should be expired.
2225 * This also removes it from any stores it may be in.
2226 * <p/>
2227 * Also notifies the CacheEventListener after the element has expired.
2228 * <p/>
2229 * Synchronization is handled within the method.
2230 * <p/>
2231 * If a remove was called, listeners are notified, regardless of whether the element existed or not.
2232 * This allows distributed cache listeners to remove elements from a cluster regardless of whether they
2233 * existed locally.
2234 * <p/>
2235 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
2236 * This exception should be caught in those circumstances.
2237 *
2238 * @param key the element key to operate on
2239 * @param expiry if the reason this method is being called is to expire the element
2240 * @param notifyListeners whether to notify listeners
2241 * @param doNotNotifyCacheReplicators whether not to notify cache replicators
2242 * @param useCacheWriter if the element should else be removed from the cache writer
2243 * @return true if the element was removed, false if it was not found in the cache
2244 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2245 */
2246 private boolean removeInternal(Object key, boolean expiry, boolean notifyListeners,
2247 boolean doNotNotifyCacheReplicators, boolean useCacheWriter)
2248 throws IllegalStateException {
2249
2250 if (useCacheWriter) {
2251 initialiseCacheWriterManager(true);
2252 }
2253
2254 checkStatus();
2255 Element elementFromStore = null;
2256
2257 if (useCacheWriter) {
2258 try {
2259 elementFromStore = compoundStore.removeWithWriter(key, cacheWriterManager);
2260 } catch (CacheWriterManagerException e) {
2261 if (configuration.getCacheWriterConfiguration().getNotifyListenersOnException()) {
2262 notifyRemoveInternalListeners(key, expiry, notifyListeners, doNotNotifyCacheReplicators,
2263 elementFromStore);
2264 }
2265 throw e.getCause();
2266 }
2267 } else {
2268 elementFromStore = compoundStore.remove(key);
2269 }
2270
2271 return notifyRemoveInternalListeners(key, expiry, notifyListeners, doNotNotifyCacheReplicators,
2272 elementFromStore);
2273 }
2274
2275 private boolean notifyRemoveInternalListeners(Object key, boolean expiry, boolean notifyListeners, boolean doNotNotifyCacheReplicators,
2276 Element elementFromStore) {
2277 boolean removed = false;
2278 boolean removeNotified = false;
2279
2280 if (elementFromStore != null) {
2281 if (expiry) {
2282
2283 registeredEventListeners.notifyElementExpiry(elementFromStore, doNotNotifyCacheReplicators);
2284 } else if (notifyListeners) {
2285 removeNotified = true;
2286 registeredEventListeners.notifyElementRemoved(elementFromStore, doNotNotifyCacheReplicators);
2287 }
2288 removed = true;
2289 }
2290
2291
2292
2293 if (notifyListeners && !expiry && !removeNotified) {
2294 Element syntheticElement = new Element(key, null);
2295 registeredEventListeners.notifyElementRemoved(syntheticElement, doNotNotifyCacheReplicators);
2296 }
2297
2298 return removed;
2299 }
2300
2301 /***
2302 * Removes or expires a collection of {@link Element}s from the Cache after an attempt to get it determined that it should be expired.
2303 * This also removes it from any stores it may be in.
2304 * <p/>
2305 * Also notifies the CacheEventListener after the element has expired.
2306 * <p/>
2307 * Synchronization is handled within the method.
2308 * <p/>
2309 * If a removeAll was called, listeners are notified, regardless of whether the element existed or not. This allows distributed cache
2310 * listeners to remove elements from a cluster regardless of whether they existed locally.
2311 * <p/>
2312 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails. This exception
2313 * should be caught in those circumstances.
2314 *
2315 * @param keys a collection of keys to operate on
2316 * @param expiry if the reason this method is being called is to expire the element
2317 * @param notifyListeners whether to notify listeners
2318 * @param doNotNotifyCacheReplicators whether not to notify cache replicators
2319 * @return true if the element was removed, false if it was not found in the cache
2320 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2321 */
2322 private void removeAllInternal(final Collection<Object> keys, boolean expiry, boolean notifyListeners,
2323 boolean doNotNotifyCacheReplicators) throws IllegalStateException {
2324 checkStatus();
2325
2326 if (disabled || keys.isEmpty()) {
2327 return;
2328 }
2329
2330 compoundStore.removeAll(keys);
2331 for (Object key : keys) {
2332 Element syntheticElement = new Element(key, null);
2333 notifyRemoveInternalListeners(key, false, notifyListeners, doNotNotifyCacheReplicators, syntheticElement);
2334 }
2335 }
2336
2337 /***
2338 * Removes all cached items.
2339 * Synchronization is handled within the method.
2340 * <p/>
2341 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
2342 * This exception should be caught in those circumstances.
2343 *
2344 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2345 */
2346 public void removeAll() throws IllegalStateException, CacheException {
2347 removeAll(false);
2348 }
2349
2350
2351 /***
2352 * Removes all cached items.
2353 * Synchronization is handled within the method.
2354 * <p/>
2355 * Caches which use synchronous replication can throw RemoteCacheException here if the replication to the cluster fails.
2356 * This exception should be caught in those circumstances.
2357 *
2358 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2359 */
2360 public void removeAll(boolean doNotNotifyCacheReplicators) throws IllegalStateException, CacheException {
2361 checkStatus();
2362 compoundStore.removeAll();
2363 registeredEventListeners.notifyRemoveAll(doNotNotifyCacheReplicators);
2364 }
2365
2366 /***
2367 * Starts an orderly shutdown of the Cache. Steps are:
2368 * <ol>
2369 * <li>Completes any outstanding CacheLoader loads.
2370 * <li>Completes any outstanding CacheWriter operations.
2371 * <li>Disposes any cache extensions.
2372 * <li>Disposes any cache event listeners. The listeners normally complete, so for example distributed caching operations will complete.
2373 * <li>Flushes all cache items from memory to the disk store, if any
2374 * <li>changes status to shutdown, so that any cache operations after this point throw IllegalStateException
2375 * </ol>
2376 * This method should be invoked only by CacheManager, as a cache's lifecycle is bound into that of it's cache manager.
2377 *
2378 * @throws IllegalStateException if the cache is already {@link Status#STATUS_SHUTDOWN}
2379 */
2380 public synchronized void dispose() throws IllegalStateException {
2381 if (checkStatusAlreadyDisposed()) {
2382 return;
2383 }
2384
2385 if (bootstrapCacheLoader != null && bootstrapCacheLoader instanceof Disposable) {
2386 ((Disposable)bootstrapCacheLoader).dispose();
2387 }
2388
2389 if (executorService != null) {
2390 executorService.shutdown();
2391 }
2392
2393 disposeRegisteredCacheExtensions();
2394 disposeRegisteredCacheLoaders();
2395 disposeRegisteredCacheWriter();
2396 registeredEventListeners.dispose();
2397
2398 if (cacheWriterManager != null) {
2399 cacheWriterManager.dispose();
2400 }
2401
2402 if (compoundStore != null) {
2403 compoundStore.removeStoreListener(this);
2404 compoundStore.dispose();
2405 }
2406
2407 cacheStatus.changeState(Status.STATUS_SHUTDOWN);
2408 }
2409
2410 private void initialiseRegisteredCacheExtensions() {
2411 for (CacheExtension cacheExtension : registeredCacheExtensions) {
2412 cacheExtension.init();
2413 }
2414 }
2415
2416 private void disposeRegisteredCacheExtensions() {
2417 for (CacheExtension cacheExtension : registeredCacheExtensions) {
2418 cacheExtension.dispose();
2419 }
2420 }
2421
2422 private void initialiseRegisteredCacheLoaders() {
2423 for (CacheLoader cacheLoader : registeredCacheLoaders) {
2424 cacheLoader.init();
2425 }
2426 }
2427
2428 private void disposeRegisteredCacheLoaders() {
2429 for (CacheLoader cacheLoader : registeredCacheLoaders) {
2430 cacheLoader.dispose();
2431 }
2432 }
2433
2434 private void initialiseRegisteredCacheWriter() {
2435 CacheWriter writer = registeredCacheWriter;
2436 if (writer != null) {
2437 writer.init();
2438 }
2439 }
2440
2441 private void disposeRegisteredCacheWriter() {
2442 CacheWriter writer = registeredCacheWriter;
2443 if (writer != null) {
2444 writer.dispose();
2445 }
2446 }
2447
2448 /***
2449 * Gets the cache configuration this cache was created with.
2450 * <p/>
2451 * Things like listeners that are added dynamically are excluded.
2452 */
2453 public CacheConfiguration getCacheConfiguration() {
2454 return configuration;
2455 }
2456
2457
2458 /***
2459 * Flushes all cache items from memory to the disk store, and from the DiskStore to disk.
2460 *
2461 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2462 */
2463 public final synchronized void flush() throws IllegalStateException, CacheException {
2464 checkStatus();
2465 try {
2466 compoundStore.flush();
2467 } catch (IOException e) {
2468 throw new CacheException("Unable to flush cache: " + configuration.getName()
2469 + ". Initial cause was " + e.getMessage(), e);
2470 }
2471 }
2472
2473 /***
2474 * Gets the size of the cache. This is a subtle concept. See below.
2475 * <p/>
2476 * This number is the actual number of elements, including expired elements
2477 * that have not been removed.
2478 * <p/>
2479 * Expired elements are removed from the the memory store when getting an
2480 * expired element, or when attempting to spool an expired element to disk.
2481 * <p/>
2482 * Expired elements are removed from the disk store when getting an expired
2483 * element, or when the expiry thread runs, which is once every five
2484 * minutes.
2485 * <p/>
2486 * To get an exact size, which would exclude expired elements, use
2487 * {@link #getKeysWithExpiryCheck()}.size(), although see that method for
2488 * the approximate time that would take.
2489 * <p/>
2490 * To get a very fast result, use {@link #getKeysNoDuplicateCheck()}.size().
2491 * If the disk store is being used, there will be some duplicates.
2492 * <p/>
2493 * Note:getSize() is a very expensive operation in off-heap, disk and Terracotta implementations.
2494 *
2495 * @return The size value
2496 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2497 */
2498 public final int getSize() throws IllegalStateException, CacheException {
2499 checkStatus();
2500
2501 if (isTerracottaClustered()) {
2502 return compoundStore.getTerracottaClusteredSize();
2503 } else {
2504 return compoundStore.getSize();
2505 }
2506 }
2507
2508 /***
2509 * {@inheritDoc}
2510 */
2511 public int getSizeBasedOnAccuracy(int statisticsAccuracy)
2512 throws IllegalStateException, CacheException {
2513 if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_BEST_EFFORT) {
2514 return getSize();
2515 } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_GUARANTEED) {
2516 return getKeysWithExpiryCheck().size();
2517 } else if (statisticsAccuracy == Statistics.STATISTICS_ACCURACY_NONE) {
2518 return getKeysNoDuplicateCheck().size();
2519 }
2520 throw new IllegalArgumentException("Unknown statistics accuracy: "
2521 + statisticsAccuracy);
2522 }
2523
2524 /***
2525 * Gets the size of the memory store for this cache. This method relies on calculating
2526 * Serialized sizes. If the Element values are not Serializable they will show as zero.
2527 * <p/>
2528 * Warning: This method can be very expensive to run. Allow approximately 1 second
2529 * per 1MB of entries. Running this method could create liveness problems
2530 * because the object lock is held for a long period
2531 * <p/>
2532 *
2533 * @return the approximate size of the memory store in bytes
2534 * @throws IllegalStateException
2535 */
2536 public final long calculateInMemorySize() throws IllegalStateException, CacheException {
2537 checkStatus();
2538 return compoundStore.getInMemorySizeInBytes();
2539 }
2540
2541 /***
2542 * Gets the size of the off-heap store for this cache.
2543 *
2544 * @return the size of the off-heap store in bytes
2545 * @throws IllegalStateException
2546 */
2547 public final long calculateOffHeapSize() throws IllegalStateException, CacheException {
2548 checkStatus();
2549 return compoundStore.getOffHeapSizeInBytes();
2550 }
2551
2552 /***
2553 * Gets the size of the on-disk store for this cache
2554 *
2555 * @return the size of the on-disk store in bytes
2556 * @throws IllegalStateException
2557 */
2558 public final long calculateOnDiskSize() throws IllegalStateException, CacheException {
2559 checkStatus();
2560 return compoundStore.getOnDiskSizeInBytes();
2561 }
2562
2563 /***
2564 * Returns the number of elements in the memory store.
2565 *
2566 * @return the number of elements in the memory store
2567 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2568 */
2569 public final long getMemoryStoreSize() throws IllegalStateException {
2570 checkStatus();
2571 return compoundStore.getInMemorySize();
2572 }
2573
2574 /***
2575 * Returns the number of elements in the off-heap store.
2576 *
2577 * @return the number of elements in the off-heap store
2578 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2579 */
2580 public long getOffHeapStoreSize() throws IllegalStateException {
2581 checkStatus();
2582 return compoundStore.getOffHeapSize();
2583 }
2584
2585 /***
2586 * Returns the number of elements in the disk store.
2587 *
2588 * @return the number of elements in the disk store.
2589 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2590 */
2591 public final int getDiskStoreSize() throws IllegalStateException {
2592 checkStatus();
2593 if (isTerracottaClustered()) {
2594 return compoundStore.getTerracottaClusteredSize();
2595 } else {
2596 return compoundStore.getOnDiskSize();
2597 }
2598 }
2599
2600 /***
2601 * Gets the status attribute of the Cache.
2602 *
2603 * @return The status value from the Status enum class
2604 */
2605 public final Status getStatus() {
2606 return cacheStatus.getStatus();
2607 }
2608
2609
2610 private void checkStatus() throws IllegalStateException {
2611 cacheStatus.checkAlive(configuration);
2612 }
2613
2614 private boolean checkStatusAlreadyDisposed() throws IllegalStateException {
2615 return cacheStatus.isShutdown();
2616 }
2617
2618
2619 /***
2620 * Gets the cache name.
2621 */
2622 public final String getName() {
2623 return configuration.getName();
2624 }
2625
2626 /***
2627 * Sets the cache name which will name.
2628 *
2629 * @param name the name of the cache. Should not be null. Should also not contain any '/' characters, as these interfere
2630 * with distribution
2631 * @throws IllegalArgumentException if an illegal name is used.
2632 */
2633 public final void setName(String name) throws IllegalArgumentException {
2634 if (!cacheStatus.isUninitialized()) {
2635 throw new IllegalStateException("Only uninitialised caches can have their names set.");
2636 }
2637 configuration.setName(name);
2638 }
2639
2640 /***
2641 * Returns a {@link String} representation of {@link Cache}.
2642 */
2643 @Override
2644 public String toString() {
2645 StringBuilder dump = new StringBuilder();
2646
2647 dump.append("[")
2648 .append(" name = ").append(configuration.getName())
2649 .append(" status = ").append(cacheStatus.getStatus())
2650 .append(" eternal = ").append(configuration.isEternal())
2651 .append(" overflowToDisk = ").append(configuration.isOverflowToDisk())
2652 .append(" maxElementsInMemory = ").append(configuration.getMaxElementsInMemory())
2653 .append(" maxElementsOnDisk = ").append(configuration.getMaxElementsOnDisk())
2654 .append(" memoryStoreEvictionPolicy = ").append(configuration.getMemoryStoreEvictionPolicy())
2655 .append(" timeToLiveSeconds = ").append(configuration.getTimeToLiveSeconds())
2656 .append(" timeToIdleSeconds = ").append(configuration.getTimeToIdleSeconds())
2657 .append(" diskPersistent = ").append(configuration.isDiskPersistent())
2658 .append(" diskExpiryThreadIntervalSeconds = ").append(configuration.getDiskExpiryThreadIntervalSeconds())
2659 .append(registeredEventListeners)
2660 .append(" hitCount = ").append(getLiveCacheStatisticsNoCheck().getCacheHitCount())
2661 .append(" memoryStoreHitCount = ").append(getLiveCacheStatisticsNoCheck().getInMemoryHitCount())
2662 .append(" diskStoreHitCount = ").append(getLiveCacheStatisticsNoCheck().getOnDiskHitCount())
2663 .append(" missCountNotFound = ").append(getLiveCacheStatisticsNoCheck().getCacheMissCount())
2664 .append(" missCountExpired = ").append(getLiveCacheStatisticsNoCheck().getCacheMissCountExpired())
2665 .append(" overflowToOffHeap = ").append(configuration.isOverflowToOffHeap())
2666 .append(" maxMemoryOffHeap = ").append(configuration.getMaxMemoryOffHeap())
2667 .append(" ]");
2668
2669 return dump.toString();
2670 }
2671
2672
2673 /***
2674 * Checks whether this cache element has expired.
2675 * <p/>
2676 * The element is expired if:
2677 * <ol>
2678 * <li> the idle time is non-zero and has elapsed, unless the cache is eternal; or
2679 * <li> the time to live is non-zero and has elapsed, unless the cache is eternal; or
2680 * <li> the value of the element is null.
2681 * </ol>
2682 *
2683 * @return true if it has expired
2684 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2685 * @throws NullPointerException if the element is null
2686 * todo this does not need to be synchronized
2687 */
2688 public final boolean isExpired(Element element) throws IllegalStateException, NullPointerException {
2689 checkStatus();
2690 return element.isExpired(configuration);
2691 }
2692
2693
2694 /***
2695 * Clones a cache. This is only legal if the cache has not been
2696 * initialized. At that point only primitives have been set and no
2697 * stores have been created.
2698 * <p/>
2699 * A new, empty, RegisteredEventListeners is created on clone.
2700 * <p/>
2701 *
2702 * @return an object of type {@link Cache}
2703 * @throws CloneNotSupportedException
2704 */
2705 @Override
2706 public final Cache clone() throws CloneNotSupportedException {
2707 if (compoundStore != null) {
2708 throw new CloneNotSupportedException("Cannot clone an initialized cache.");
2709 }
2710 Cache copy = (Cache) super.clone();
2711
2712 copy.liveCacheStatisticsData = new LiveCacheStatisticsWrapper(copy);
2713 copy.sampledCacheStatistics = new SampledCacheStatisticsWrapper();
2714
2715 copy.configuration = configuration.clone();
2716 copy.guid = createGuid();
2717 copy.cacheStatus = new CacheStatus();
2718 copy.cacheStatus.changeState(Status.STATUS_UNINITIALISED);
2719 copy.elementValueComparator = copy.configuration.getElementValueComparatorConfiguration().getElementComparatorInstance();
2720 copy.propertyChangeSupport = new PropertyChangeSupport(copy);
2721 copy.nonstopActiveDelegateHolder = new NonstopActiveDelegateHolderImpl(copy);
2722 for (PropertyChangeListener propertyChangeListener : propertyChangeSupport.getPropertyChangeListeners()) {
2723 copy.addPropertyChangeListener(propertyChangeListener);
2724 }
2725
2726 RegisteredEventListeners registeredEventListenersFromCopy = copy.getCacheEventNotificationService();
2727 if (registeredEventListenersFromCopy == null || registeredEventListenersFromCopy.getCacheEventListeners().size() == 0) {
2728 copy.registeredEventListeners = new RegisteredEventListeners(copy);
2729 } else {
2730 copy.registeredEventListeners = new RegisteredEventListeners(copy);
2731 Set cacheEventListeners = registeredEventListeners.getCacheEventListeners();
2732 for (Object cacheEventListener1 : cacheEventListeners) {
2733 CacheEventListener cacheEventListener = (CacheEventListener) cacheEventListener1;
2734 CacheEventListener cacheEventListenerClone = (CacheEventListener) cacheEventListener.clone();
2735 copy.registeredEventListeners.registerListener(cacheEventListenerClone);
2736 }
2737 }
2738
2739
2740 copy.registeredCacheExtensions = new CopyOnWriteArrayList<CacheExtension>();
2741 for (CacheExtension registeredCacheExtension : registeredCacheExtensions) {
2742 copy.registerCacheExtension(registeredCacheExtension.clone(copy));
2743 }
2744
2745 copy.registeredCacheLoaders = new CopyOnWriteArrayList<CacheLoader>();
2746 for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
2747 copy.registerCacheLoader(registeredCacheLoader.clone(copy));
2748 }
2749
2750 if (registeredCacheWriter != null) {
2751 copy.registerCacheWriter(registeredCacheWriter.clone(copy));
2752 }
2753
2754 if (bootstrapCacheLoader != null) {
2755 BootstrapCacheLoader bootstrapCacheLoaderClone = (BootstrapCacheLoader) bootstrapCacheLoader.clone();
2756 copy.setBootstrapCacheLoader(bootstrapCacheLoaderClone);
2757 }
2758
2759 return copy;
2760 }
2761
2762 /***
2763 * Gets the internal Store.
2764 *
2765 * @return the Store referenced by this cache
2766 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2767 */
2768 final Store getStore() throws IllegalStateException {
2769 checkStatus();
2770 return compoundStore;
2771 }
2772
2773 /***
2774 * Get the optional store management bean for this cache.
2775 */
2776 public final Object getStoreMBean() {
2777 return getStore().getMBean();
2778 }
2779
2780 /***
2781 * Use this to access the service in order to register and unregister listeners
2782 *
2783 * @return the RegisteredEventListeners instance for this cache.
2784 */
2785 public final RegisteredEventListeners getCacheEventNotificationService() {
2786 return registeredEventListeners;
2787 }
2788
2789
2790 /***
2791 * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
2792 * <p>
2793 * Since no assertions are made about the state of the Element it is possible that the
2794 * Element is expired, but this method still returns true.
2795 *
2796 * @return true if an element matching the key is found in memory
2797 */
2798 public final boolean isElementInMemory(Serializable key) {
2799 return isElementInMemory((Object) key);
2800 }
2801
2802 /***
2803 * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
2804 * <p>
2805 * Since no assertions are made about the state of the Element it is possible that the
2806 * Element is expired, but this method still returns true.
2807 *
2808 * @return true if an element matching the key is found in memory
2809 * @since 1.2
2810 */
2811 public final boolean isElementInMemory(Object key) {
2812 return compoundStore.containsKeyInMemory(key);
2813 }
2814
2815 /***
2816 * Whether an Element is stored in the cache in off-heap memory, indicating an intermediate cost of retrieval.
2817 * <p>
2818 * Since no assertions are made about the state of the Element it is possible that the
2819 * Element is expired, but this method still returns true.
2820 *
2821 * @return true if an element matching the key is found in off-heap
2822 * @since 2.3
2823 */
2824 public final boolean isElementOffHeap(Object key) {
2825 return compoundStore.containsKeyOffHeap(key);
2826 }
2827
2828 /***
2829 * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
2830 * <p>
2831 * Since no assertions are made about the state of the Element it is possible that the
2832 * Element is expired, but this method still returns true.
2833 *
2834 * @return true if an element matching the key is found in the diskStore
2835 */
2836 public final boolean isElementOnDisk(Serializable key) {
2837 return isElementOnDisk((Object) key);
2838 }
2839
2840 /***
2841 * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
2842 * <p>
2843 * Since no assertions are made about the state of the Element it is possible that the
2844 * Element is expired, but this method still returns true.
2845 *
2846 * @return true if an element matching the key is found in the diskStore
2847 * @since 1.2
2848 */
2849 public final boolean isElementOnDisk(Object key) {
2850 return compoundStore.containsKeyOnDisk(key);
2851 }
2852
2853 /***
2854 * The GUID for this cache instance can be used to determine whether two cache instance references
2855 * are pointing to the same cache.
2856 *
2857 * @return the globally unique identifier for this cache instance. This is guaranteed to be unique.
2858 * @since 1.2
2859 */
2860 public final String getGuid() {
2861 return guid;
2862 }
2863
2864 /***
2865 * Gets the CacheManager managing this cache. For a newly created cache this will be null until
2866 * it has been added to a CacheManager.
2867 *
2868 * @return the manager or null if there is none
2869 */
2870 public final CacheManager getCacheManager() {
2871 return cacheManager;
2872 }
2873
2874
2875 /***
2876 * Resets statistics counters back to 0.
2877 *
2878 * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
2879 */
2880 public void clearStatistics() throws IllegalStateException {
2881 checkStatus();
2882 liveCacheStatisticsData.clearStatistics();
2883 sampledCacheStatistics.clearStatistics();
2884 registeredEventListeners.clearCounters();
2885 }
2886
2887 /***
2888 * Accurately measuring statistics can be expensive. Returns the current accuracy setting.
2889 *
2890 * @return one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
2891 */
2892 public int getStatisticsAccuracy() {
2893 return getLiveCacheStatistics().getStatisticsAccuracy();
2894 }
2895
2896 /***
2897 * Sets the statistics accuracy.
2898 *
2899 * @param statisticsAccuracy one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
2900 */
2901 public void setStatisticsAccuracy(int statisticsAccuracy) {
2902 int oldValue = getStatisticsAccuracy();
2903 if (statisticsAccuracy != oldValue) {
2904 liveCacheStatisticsData.setStatisticsAccuracy(statisticsAccuracy);
2905 firePropertyChange("StatisticsAccuracy", oldValue, statisticsAccuracy);
2906 }
2907 }
2908
2909 /***
2910 * Causes all elements stored in the Cache to be synchronously checked for expiry, and if expired, evicted.
2911 */
2912 public void evictExpiredElements() {
2913 compoundStore.expireElements();
2914 }
2915
2916 /***
2917 * An inexpensive check to see if the key exists in the cache.
2918 * <p/>
2919 * This method is not synchronized. It is possible that an element may exist in the cache and be removed
2920 * before the check gets to it, or vice versa. Since no assertions are made about the state of the Element
2921 * it is possible that the Element is expired, but this method still returns true.
2922
2923 *
2924 * @param key the key to check.
2925 * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
2926 */
2927 public boolean isKeyInCache(Object key) {
2928 if (key == null) {
2929 return false;
2930 }
2931 return isElementInMemory(key) || isElementOffHeap(key) || isElementOnDisk(key);
2932 }
2933
2934 /***
2935 * An extremely expensive check to see if the value exists in the cache. This implementation is O(n). Ehcache
2936 * is not designed for efficient access in this manner.
2937 * <p/>
2938 * This method is not synchronized. It is possible that an element may exist in the cache and be removed
2939 * before the check gets to it, or vice versa. Because it is slow to execute the probability of that this will
2940 * have happened.
2941 *
2942 * @param value to check for
2943 * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
2944 */
2945 public boolean isValueInCache(Object value) {
2946 for (Object key : getKeys()) {
2947 Element element = get(key);
2948 if (element != null) {
2949 Object elementValue = element.getValue();
2950 if (elementValue == null) {
2951 if (value == null) {
2952 return true;
2953 }
2954 } else {
2955 if (elementValue.equals(value)) {
2956 return true;
2957 }
2958 }
2959 }
2960 }
2961 return false;
2962 }
2963
2964 /***
2965 * {@inheritDoc}
2966 * <p/>
2967 * Note, the {@link #getSize} method will have the same value as the size
2968 * reported by Statistics for the statistics accuracy of
2969 * {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}.
2970 */
2971 public Statistics getStatistics() throws IllegalStateException {
2972 int size = getSizeBasedOnAccuracy(getLiveCacheStatistics()
2973 .getStatisticsAccuracy());
2974 return new Statistics(this, getLiveCacheStatistics()
2975 .getStatisticsAccuracy(), getLiveCacheStatistics()
2976 .getCacheHitCount(), getLiveCacheStatistics()
2977 .getOnDiskHitCount(), getLiveCacheStatistics()
2978 .getOffHeapHitCount(), getLiveCacheStatistics()
2979 .getInMemoryHitCount(), getLiveCacheStatistics()
2980 .getCacheMissCount(), getLiveCacheStatistics()
2981 .getOnDiskMissCount(), getLiveCacheStatistics()
2982 .getOffHeapMissCount(), getLiveCacheStatistics()
2983 .getInMemoryMissCount(), size, getAverageGetTime(),
2984 getLiveCacheStatistics().getEvictedCount(),
2985 getMemoryStoreSize(), getOffHeapStoreSize(), getDiskStoreSize(),
2986 getSearchesPerSecond(), getAverageSearchTime(), getLiveCacheStatistics().getWriterQueueLength());
2987 }
2988
2989 /***
2990 * {@inheritDoc}
2991 */
2992 public long getAverageSearchTime() {
2993 return sampledCacheStatistics.getAverageSearchTime();
2994 }
2995
2996 /***
2997 * {@inheritDoc}
2998 */
2999 public long getSearchesPerSecond() {
3000 return sampledCacheStatistics.getSearchesPerSecond();
3001 }
3002
3003 /***
3004 * For use by CacheManager.
3005 *
3006 * @param cacheManager the CacheManager for this cache to use.
3007 */
3008 public void setCacheManager(CacheManager cacheManager) {
3009 CacheManager oldValue = getCacheManager();
3010 this.cacheManager = cacheManager;
3011 firePropertyChange("CacheManager", oldValue, cacheManager);
3012 }
3013
3014 /***
3015 * Accessor for the BootstrapCacheLoader associated with this cache. For testing purposes.
3016 */
3017 public BootstrapCacheLoader getBootstrapCacheLoader() {
3018 return bootstrapCacheLoader;
3019 }
3020
3021 /***
3022 * Sets the bootstrap cache loader.
3023 *
3024 * @param bootstrapCacheLoader the loader to be used
3025 * @throws CacheException if this method is called after the cache is initialized
3026 */
3027 public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader) throws CacheException {
3028 if (!cacheStatus.isUninitialized()) {
3029 throw new CacheException("A bootstrap cache loader can only be set before the cache is initialized. "
3030 + configuration.getName());
3031 }
3032 BootstrapCacheLoader oldValue = getBootstrapCacheLoader();
3033 this.bootstrapCacheLoader = bootstrapCacheLoader;
3034 firePropertyChange("BootstrapCacheLoader", oldValue, bootstrapCacheLoader);
3035 }
3036
3037 /***
3038 * DiskStore paths can conflict between CacheManager instances. This method allows the path to be changed.
3039 *
3040 * @param diskStorePath the new path to be used.
3041 * @throws CacheException if this method is called after the cache is initialized
3042 */
3043 public void setDiskStorePath(String diskStorePath) throws CacheException {
3044 if (!cacheStatus.isUninitialized()) {
3045 throw new CacheException("A DiskStore path can only be set before the cache is initialized. "
3046 + configuration.getName());
3047 }
3048 String oldValue = this.diskStorePath;
3049 synchronized (this) {
3050 this.diskStorePath = diskStorePath;
3051 }
3052 firePropertyChange("DiskStorePath", oldValue, diskStorePath);
3053 }
3054
3055 /***
3056 * An equals method which follows the contract of {@link Object#equals(Object)}
3057 * <p/>
3058 * An Cache is equal to another one if it implements Ehcache and has the same GUID.
3059 *
3060 * @param object the reference object with which to compare.
3061 * @return <code>true</code> if this object is the same as the obj
3062 * argument; <code>false</code> otherwise.
3063 * @see #hashCode()
3064 * @see java.util.Hashtable
3065 */
3066 @Override
3067 public boolean equals(Object object) {
3068 if (object == null) {
3069 return false;
3070 }
3071 if (!(object instanceof Ehcache)) {
3072 return false;
3073 }
3074 Ehcache other = (Ehcache) object;
3075 return guid.equals(other.getGuid());
3076 }
3077
3078 /***
3079 * Returns a hash code value for the object. This method is
3080 * supported for the benefit of hashtables such as those provided by
3081 * <code>java.util.Hashtable</code>.
3082 * <p/>
3083 * The general contract of <code>hashCode</code> is:
3084 * <ul>
3085 * <li>Whenever it is invoked on the same object more than once during
3086 * an execution of a Java application, the <tt>hashCode</tt> method
3087 * must consistently return the same integer, provided no information
3088 * used in <tt>equals</tt> comparisons on the object is modified.
3089 * This integer need not remain consistent from one execution of an
3090 * application to another execution of the same application.
3091 * <li>If two objects are equal according to the <tt>equals(Object)</tt>
3092 * method, then calling the <code>hashCode</code> method on each of
3093 * the two objects must produce the same integer result.
3094 * <li>It is <em>not</em> required that if two objects are unequal
3095 * according to the {@link Object#equals(Object)}
3096 * method, then calling the <tt>hashCode</tt> method on each of the
3097 * two objects must produce distinct integer results. However, the
3098 * programmer should be aware that producing distinct integer results
3099 * for unequal objects may improve the performance of hashtables.
3100 * </ul>
3101 * <p/>
3102 * As much as is reasonably practical, the hashCode method defined by
3103 * class <tt>Object</tt> does return distinct integers for distinct
3104 * objects. (This is typically implemented by converting the internal
3105 * address of the object into an integer, but this implementation
3106 * technique is not required by the
3107 * Java(TM) programming language.)
3108 * <p/>
3109 * This implementation use the GUID of the cache.
3110 *
3111 * @return a hash code value for this object.
3112 * @see Object#equals(Object)
3113 * @see java.util.Hashtable
3114 */
3115 @Override
3116 public int hashCode() {
3117 return guid.hashCode();
3118 }
3119
3120
3121 /***
3122 * Create globally unique ID for this cache.
3123 */
3124 private String createGuid() {
3125 StringBuilder buffer = new StringBuilder().append(localhost).append("-").append(UUID.randomUUID());
3126 return buffer.toString();
3127 }
3128
3129 /***
3130 * Register a {@link CacheExtension} with the cache. It will then be tied into the cache lifecycle.
3131 * <p/>
3132 * If the CacheExtension is not initialised, initialise it.
3133 */
3134 public void registerCacheExtension(CacheExtension cacheExtension) {
3135 registeredCacheExtensions.add(cacheExtension);
3136 }
3137
3138 /***
3139 * @return the cache extensions as a live list
3140 */
3141 public List<CacheExtension> getRegisteredCacheExtensions() {
3142 return registeredCacheExtensions;
3143 }
3144
3145
3146 /***
3147 * Unregister a {@link CacheExtension} with the cache. It will then be detached from the cache lifecycle.
3148 */
3149 public void unregisterCacheExtension(CacheExtension cacheExtension) {
3150 cacheExtension.dispose();
3151 registeredCacheExtensions.remove(cacheExtension);
3152 }
3153
3154
3155 /***
3156 * The average get time in ms.
3157 */
3158 public float getAverageGetTime() {
3159 return getLiveCacheStatistics().getAverageGetTimeMillis();
3160 }
3161
3162 /***
3163 * Sets an ExceptionHandler on the Cache. If one is already set, it is overwritten.
3164 * <p/>
3165 * The ExceptionHandler is only used if this Cache's methods are accessed using
3166 * {@link net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy}.
3167 *
3168 * @see net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy
3169 */
3170 public void setCacheExceptionHandler(CacheExceptionHandler cacheExceptionHandler) {
3171 CacheExceptionHandler oldValue = getCacheExceptionHandler();
3172 this.cacheExceptionHandler = cacheExceptionHandler;
3173 firePropertyChange("CacheExceptionHandler", oldValue, cacheExceptionHandler);
3174 }
3175
3176 /***
3177 * Gets the ExceptionHandler on this Cache, or null if there isn't one.
3178 * <p/>
3179 * The ExceptionHandler is only used if this Cache's methods are accessed using
3180 * {@link net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy}.
3181 *
3182 * @see net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy
3183 */
3184 public CacheExceptionHandler getCacheExceptionHandler() {
3185 return cacheExceptionHandler;
3186 }
3187
3188 /***
3189 * Register a {@link CacheLoader} with the cache. It will then be tied into the cache lifecycle.
3190 * <p/>
3191 * If the CacheLoader is not initialised, initialise it.
3192 *
3193 * @param cacheLoader A Cache Loader to register
3194 */
3195 public void registerCacheLoader(CacheLoader cacheLoader) {
3196 registeredCacheLoaders.add(cacheLoader);
3197 }
3198
3199 /***
3200 * Unregister a {@link CacheLoader} with the cache. It will then be detached from the cache lifecycle.
3201 *
3202 * @param cacheLoader A Cache Loader to unregister
3203 */
3204 public void unregisterCacheLoader(CacheLoader cacheLoader) {
3205 registeredCacheLoaders.remove(cacheLoader);
3206 }
3207
3208
3209 /***
3210 * @return the cache loaders as a live list
3211 */
3212 public List<CacheLoader> getRegisteredCacheLoaders() {
3213 return registeredCacheLoaders;
3214 }
3215
3216 /***
3217 * {@inheritDoc}
3218 */
3219 public void registerCacheWriter(CacheWriter cacheWriter) {
3220 synchronized (this) {
3221 this.registeredCacheWriter = cacheWriter;
3222 if (cacheStatus.isAlive()) {
3223 initialiseRegisteredCacheWriter();
3224 }
3225 }
3226 initialiseCacheWriterManager(false);
3227 }
3228
3229 /***
3230 * {@inheritDoc}
3231 */
3232 public void unregisterCacheWriter() {
3233 if (cacheWriterManagerInitFlag.get()) {
3234 throw new CacheException("Cache: " + configuration.getName() + " has its cache writer being unregistered " +
3235 "after it was already initialised.");
3236 }
3237 this.registeredCacheWriter = null;
3238 }
3239
3240 /***
3241 * {@inheritDoc}
3242 */
3243 public CacheWriter getRegisteredCacheWriter() {
3244 return this.registeredCacheWriter;
3245 }
3246
3247 /***
3248 * Does the asynchronous put into the cache of the asynchronously loaded value.
3249 *
3250 * @param key the key to load
3251 * @param specificLoader a specific loader to use. If null the default loader is used.
3252 * @param argument the argument to pass to the writer
3253 * @return a Future which can be used to monitor execution
3254 */
3255 Future asynchronousPut(final Object key, final CacheLoader specificLoader, final Object argument) {
3256 return getExecutorService().submit(new Runnable() {
3257
3258 /***
3259 * Calls the CacheLoader and puts the result in the Cache
3260 */
3261 public void run() throws CacheException {
3262 try {
3263
3264 boolean existsOnRun = isKeyInCache(key);
3265 if (!existsOnRun) {
3266 Object value = loadValueUsingLoader(key, specificLoader, argument);
3267 if (value != null) {
3268 put(new Element(key, value), false);
3269 }
3270 }
3271 } catch (RuntimeException e) {
3272 if (LOG.isDebugEnabled()) {
3273 LOG.debug("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
3274 }
3275 throw new CacheException("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
3276 }
3277 }
3278 });
3279 }
3280
3281 /***
3282 * Does the asynchronous loading. But doesn't put it into the cache
3283 *
3284 * @param key the key to load
3285 * @param specificLoader a specific loader to use. If null the default loader is used.
3286 * @param argument the argument to pass to the writer
3287 * @return a Future which can be used to monitor execution
3288 */
3289 Future<AtomicReference<Object>> asynchronousLoad(final Object key, final CacheLoader specificLoader, final Object argument) {
3290 final AtomicReference<Object> result = new AtomicReference<Object>();
3291 return getExecutorService().submit(new Runnable() {
3292
3293 /***
3294 * Calls the CacheLoader and puts the result in the Cache
3295 */
3296 public void run() throws CacheException {
3297 try {
3298
3299 boolean existsOnRun = isKeyInCache(key);
3300 if (!existsOnRun) {
3301 Object value = loadValueUsingLoader(key, specificLoader, argument);
3302 if (value != null) {
3303 result.set(value);
3304 }
3305 }
3306 } catch (RuntimeException e) {
3307 if (LOG.isDebugEnabled()) {
3308 LOG.debug("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
3309 }
3310 throw new CacheException("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
3311 }
3312 }
3313 }, result);
3314 }
3315
3316 /***
3317 * Will attempt to load the value for a key, either using the passedin loader, or falling back to registered ones
3318 * @param key the key to load for
3319 * @param specificLoader the loader to use, can be null to fallback to Cache registered loaders
3320 * @param argument the argument to pass the loader
3321 * @return null if not present in the underlying SoR or if no loader available, otherwise the loaded object
3322 */
3323 private Object loadValueUsingLoader(final Object key, final CacheLoader specificLoader, final Object argument) {
3324 Object value = null;
3325 if (specificLoader != null) {
3326 if (argument == null) {
3327 value = specificLoader.load(key);
3328 } else {
3329 value = specificLoader.load(key, argument);
3330 }
3331 } else if (!registeredCacheLoaders.isEmpty()) {
3332 value = loadWithRegisteredLoaders(argument, key);
3333 }
3334 return value;
3335 }
3336
3337 private Object loadWithRegisteredLoaders(Object argument, Object key) throws CacheException {
3338
3339 Object value = null;
3340
3341 if (argument == null) {
3342 for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
3343 value = registeredCacheLoader.load(key);
3344 if (value != null) {
3345 break;
3346 }
3347 }
3348 } else {
3349 for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
3350 value = registeredCacheLoader.load(key, argument);
3351 if (value != null) {
3352 break;
3353 }
3354 }
3355 }
3356 return value;
3357 }
3358
3359
3360 /***
3361 * Creates a future to perform the load
3362 *
3363 * @param keys
3364 * @param argument the loader argument
3365 * @return a Future which can be used to monitor execution
3366 */
3367 Future asynchronousLoadAll(final Collection keys, final Object argument) {
3368 return getExecutorService().submit(new Runnable() {
3369 /***
3370 * Calls the CacheLoader and puts the result in the Cache
3371 */
3372 public void run() {
3373 try {
3374 Set<Object> nonLoadedKeys = new HashSet<Object>();
3375 for (Object key : keys) {
3376 if (!isKeyInCache(key)) {
3377 nonLoadedKeys.add(key);
3378 }
3379 }
3380 Map<?, ?> map = loadWithRegisteredLoaders(argument, nonLoadedKeys);
3381 for (Entry<?, ?> e : map.entrySet()) {
3382 put(new Element(e.getKey(), e.getValue()));
3383 }
3384 } catch (Throwable e) {
3385 if (LOG.isErrorEnabled()) {
3386 LOG.error("Problem during load. Load will not be completed. Cause was " + e.getCause(), e);
3387 }
3388 }
3389 }
3390 });
3391 }
3392
3393 /***
3394 * Does the asynchronous loading.
3395 *
3396 * @param argument the loader argument
3397 * @param nonLoadedKeys the Set of keys that are already in the Cache
3398 * @return A map of loaded elements
3399 */
3400 Map loadWithRegisteredLoaders(Object argument, Set<Object> nonLoadedKeys) {
3401 Map result = new HashMap();
3402 for (CacheLoader registeredCacheLoader : registeredCacheLoaders) {
3403 if (nonLoadedKeys.isEmpty()) {
3404 break;
3405 }
3406
3407 Map resultForThisCacheLoader = null;
3408 if (argument == null) {
3409 resultForThisCacheLoader = registeredCacheLoader.loadAll(nonLoadedKeys);
3410 } else {
3411 resultForThisCacheLoader = registeredCacheLoader.loadAll(nonLoadedKeys, argument);
3412 }
3413 if (resultForThisCacheLoader != null) {
3414 nonLoadedKeys.removeAll(resultForThisCacheLoader.keySet());
3415 result.putAll(resultForThisCacheLoader);
3416 }
3417 }
3418 return result;
3419 }
3420
3421 /***
3422 * @return Gets the executor service. This is not publically accessible.
3423 */
3424 ExecutorService getExecutorService() {
3425 if (executorService == null) {
3426 synchronized (this) {
3427 if (VmUtils.isInGoogleAppEngine()) {
3428
3429 executorService = new AbstractExecutorService() {
3430 /*** {@inheritDoc} */
3431 public void execute(Runnable command) {
3432 command.run();
3433 }
3434
3435 /*** {@inheritDoc} */
3436 public List<Runnable> shutdownNow() {
3437 return Collections.emptyList();
3438 }
3439
3440 /*** {@inheritDoc} */
3441 public void shutdown() {
3442 }
3443
3444 /*** {@inheritDoc} */
3445 public boolean isTerminated() {
3446 return isShutdown();
3447 }
3448
3449 /*** {@inheritDoc} */
3450 public boolean isShutdown() {
3451 return false;
3452 }
3453
3454 /*** {@inheritDoc} */
3455 public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
3456 return true;
3457 }
3458 };
3459 } else {
3460
3461 executorService = new ThreadPoolExecutor(EXECUTOR_CORE_POOL_SIZE, EXECUTOR_MAXIMUM_POOL_SIZE, EXECUTOR_KEEP_ALIVE_TIME,
3462 TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), new NamedThreadFactory("Cache Executor Service"));
3463 }
3464 }
3465 }
3466 return executorService;
3467 }
3468
3469
3470 /***
3471 * Whether this cache is disabled. "Disabled" means:
3472 * <ol>
3473 * <li>bootstrap is disabled</li>
3474 * <li>puts are discarded</li>
3475 * <li>putQuiets are discarded</li>
3476 * <li>gets return null</li>
3477 * </ol>
3478 * In all other respects the cache continues as it is.
3479 * <p/>
3480 * You can disable and enable a cache programmatically through the {@link #setDisabled(boolean)} method.
3481 * <p/>
3482 * By default caches are enabled on creation, unless the <code>net.sf.ehcache.disabled</code> system
3483 * property is set.
3484 *
3485 * @return true if the cache is disabled.
3486 * @see #NET_SF_EHCACHE_DISABLED ?
3487 */
3488 public boolean isDisabled() {
3489 return disabled;
3490 }
3491
3492 /***
3493 * Disables or enables this cache. This call overrides the previous value of disabled, even if the
3494 * <code>net.sf.ehcache.disabled</code> system property is set
3495 * <p/>
3496 *
3497 * @param disabled true if you wish to disable, false to enable
3498 * @see #isDisabled()
3499 */
3500 public void setDisabled(boolean disabled) {
3501 if (allowDisable) {
3502 boolean oldValue = isDisabled();
3503 if (oldValue != disabled) {
3504 synchronized (this) {
3505 this.disabled = disabled;
3506 }
3507 firePropertyChange("Disabled", oldValue, disabled);
3508 }
3509 } else {
3510 throw new CacheException("Dynamic cache features are disabled");
3511 }
3512 }
3513
3514 /***
3515 * @return the current in-memory eviction policy. This may not be the configured policy, if it has been
3516 * dynamically set.
3517 */
3518 public Policy getMemoryStoreEvictionPolicy() {
3519 return compoundStore.getInMemoryEvictionPolicy();
3520 }
3521
3522 /***
3523 * Sets the eviction policy strategy. The Cache will use a policy at startup. There
3524 * are three policies which can be configured: LRU, LFU and FIFO. However many other
3525 * policies are possible. That the policy has access to the whole element enables policies
3526 * based on the key, value, metadata, statistics, or a combination of any of the above.
3527 * It is safe to change the policy of a store at any time. The new policy takes effect
3528 * immediately.
3529 *
3530 * @param policy the new policy
3531 */
3532 public void setMemoryStoreEvictionPolicy(Policy policy) {
3533 Policy oldValue = getMemoryStoreEvictionPolicy();
3534 compoundStore.setInMemoryEvictionPolicy(policy);
3535 firePropertyChange("MemoryStoreEvictionPolicy", oldValue, policy);
3536 }
3537
3538 /***
3539 * {@inheritDoc}
3540 */
3541 public LiveCacheStatistics getLiveCacheStatistics()
3542 throws IllegalStateException {
3543 checkStatus();
3544 return liveCacheStatisticsData;
3545 }
3546
3547 private LiveCacheStatistics getLiveCacheStatisticsNoCheck() {
3548 return liveCacheStatisticsData;
3549 }
3550
3551 /***
3552 * {@inheritDoc}
3553 */
3554 public void registerCacheUsageListener(CacheUsageListener cacheUsageListener)
3555 throws IllegalStateException {
3556 checkStatus();
3557 liveCacheStatisticsData.registerCacheUsageListener(cacheUsageListener);
3558 }
3559
3560 /***
3561 * {@inheritDoc}
3562 */
3563 public void removeCacheUsageListener(CacheUsageListener cacheUsageListener)
3564 throws IllegalStateException {
3565 checkStatus();
3566 liveCacheStatisticsData.removeCacheUsageListener(cacheUsageListener);
3567 }
3568
3569 /***
3570 * {@inheritDoc}
3571 */
3572 public boolean isStatisticsEnabled() {
3573 return getLiveCacheStatistics().isStatisticsEnabled();
3574 }
3575
3576 /***
3577 * {@inheritDoc}
3578 */
3579 public void setStatisticsEnabled(boolean enableStatistics) {
3580 boolean oldValue = isStatisticsEnabled();
3581 if (oldValue != enableStatistics) {
3582 liveCacheStatisticsData.setStatisticsEnabled(enableStatistics);
3583 if (!enableStatistics) {
3584 setSampledStatisticsEnabled(false);
3585 }
3586 firePropertyChange("StatisticsEnabled", oldValue, enableStatistics);
3587 }
3588 }
3589
3590 /***
3591 * {@inheritDoc}
3592 */
3593 public SampledCacheStatistics getSampledCacheStatistics() {
3594 return sampledCacheStatistics;
3595 }
3596
3597 /***
3598 * {@inheritDoc}
3599 */
3600 public void setSampledStatisticsEnabled(final boolean enableStatistics) {
3601 if (cacheManager == null) {
3602 throw new IllegalStateException(
3603 "You must add the cache to a CacheManager before enabling/disabling sampled statistics.");
3604 }
3605 boolean oldValue = isSampledStatisticsEnabled();
3606 if (oldValue != enableStatistics) {
3607 if (enableStatistics) {
3608 setStatisticsEnabled(true);
3609 sampledCacheStatistics.enableSampledStatistics(cacheManager.getTimer());
3610 } else {
3611 sampledCacheStatistics.disableSampledStatistics();
3612 }
3613 firePropertyChange("SampledStatisticsEnabled", oldValue, enableStatistics);
3614 }
3615 }
3616
3617 /***
3618 * {@inheritDoc}
3619 *
3620 * @see net.sf.ehcache.Ehcache#isSampledStatisticsEnabled()
3621 */
3622 public boolean isSampledStatisticsEnabled() {
3623 return sampledCacheStatistics.isSampledStatisticsEnabled();
3624 }
3625
3626 /***
3627 * {@inheritDoc}
3628 */
3629 public Object getInternalContext() {
3630 return compoundStore.getInternalContext();
3631 }
3632
3633 /***
3634 * {@inheritDoc}
3635 */
3636 public void disableDynamicFeatures() {
3637 configuration.freezeConfiguration();
3638 allowDisable = false;
3639 }
3640
3641 /***
3642 * {@inheritDoc}
3643 * @deprecated use {@link #isClusterBulkLoadEnabled()} instead
3644 */
3645 @Deprecated
3646 public boolean isClusterCoherent() {
3647 return !this.isClusterBulkLoadEnabled();
3648 }
3649
3650 /***
3651 * {@inheritDoc}
3652 * @deprecated use {@link #isNodeBulkLoadEnabled()} instead
3653 */
3654 @Deprecated
3655 public boolean isNodeCoherent() {
3656 return !this.isNodeBulkLoadEnabled();
3657 }
3658
3659 /***
3660 * {@inheritDoc}
3661 * @deprecated use {@link #setNodeBulkLoadEnabled(boolean)} instead
3662 */
3663 @Deprecated
3664 public void setNodeCoherent(boolean coherent) {
3665 this.setNodeBulkLoadEnabled(!coherent);
3666 }
3667
3668 /***
3669 * {@inheritDoc}
3670 * @deprecated use {@link #waitUntilClusterBulkLoadComplete()} instead
3671 */
3672 @Deprecated
3673 public void waitUntilClusterCoherent() {
3674 this.waitUntilClusterBulkLoadComplete();
3675 }
3676
3677
3678
3679 /***
3680 * @param listener
3681 */
3682 public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
3683 if (listener != null && propertyChangeSupport != null) {
3684 propertyChangeSupport.removePropertyChangeListener(listener);
3685 propertyChangeSupport.addPropertyChangeListener(listener);
3686 }
3687 }
3688
3689 /***
3690 * @param listener
3691 */
3692 public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
3693 if (listener != null && propertyChangeSupport != null) {
3694 propertyChangeSupport.removePropertyChangeListener(listener);
3695 }
3696 }
3697
3698 /***
3699 * @param propertyName
3700 * @param oldValue
3701 * @param newValue
3702 */
3703 public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
3704 PropertyChangeSupport pcs;
3705 synchronized (this) {
3706 pcs = propertyChangeSupport;
3707 }
3708 if (pcs != null && (oldValue != null || newValue != null)) {
3709 pcs.firePropertyChange(propertyName, oldValue, newValue);
3710 }
3711 }
3712
3713 /***
3714 * {@inheritDoc}
3715 */
3716 public Element putIfAbsent(Element element) throws NullPointerException {
3717 checkStatus();
3718
3719 if (element.getObjectKey() == null) {
3720 throw new NullPointerException();
3721 }
3722
3723 if (disabled) {
3724 return null;
3725 }
3726
3727
3728 getQuiet(element.getObjectKey());
3729
3730 element.resetAccessStatistics();
3731 applyDefaultsToElementWithoutLifespanSet(element);
3732 backOffIfDiskSpoolFull();
3733
3734 Element result = compoundStore.putIfAbsent(element);
3735 if (result == null) {
3736 notifyPutInternalListeners(element, false, false);
3737 }
3738 return result;
3739 }
3740
3741 /***
3742 * {@inheritDoc}
3743 */
3744 public boolean removeElement(Element element) throws NullPointerException {
3745 checkStatus();
3746
3747 if (element.getObjectKey() == null) {
3748 throw new NullPointerException();
3749 }
3750
3751 if (disabled) {
3752 return false;
3753 }
3754
3755
3756 getQuiet(element.getObjectKey());
3757
3758 Element result = compoundStore.removeElement(element, elementValueComparator);
3759
3760
3761 notifyRemoveInternalListeners(element.getObjectKey(), false, true, false, result);
3762 return result != null;
3763 }
3764
3765 /***
3766 * {@inheritDoc}
3767 */
3768 public boolean replace(Element old, Element element) throws NullPointerException, IllegalArgumentException {
3769 checkStatus();
3770
3771 if (old.getObjectKey() == null || element.getObjectKey() == null) {
3772 throw new NullPointerException();
3773 }
3774 if (!old.getObjectKey().equals(element.getObjectKey())) {
3775 throw new IllegalArgumentException("The keys for the element arguments to replace must be equal");
3776 }
3777
3778 if (disabled) {
3779 return false;
3780 }
3781
3782 getQuiet(old.getObjectKey());
3783
3784 element.resetAccessStatistics();
3785 applyDefaultsToElementWithoutLifespanSet(element);
3786 backOffIfDiskSpoolFull();
3787
3788 boolean result = compoundStore.replace(old, element, elementValueComparator);
3789
3790 if (result) {
3791 element.updateUpdateStatistics();
3792 notifyPutInternalListeners(element, false, true);
3793 }
3794 return result;
3795 }
3796
3797 /***
3798 * {@inheritDoc}
3799 */
3800 public Element replace(Element element) throws NullPointerException {
3801 checkStatus();
3802
3803 if (element.getObjectKey() == null) {
3804 throw new NullPointerException();
3805 }
3806
3807 if (disabled) {
3808 return null;
3809 }
3810
3811 getQuiet(element.getObjectKey());
3812
3813 element.resetAccessStatistics();
3814 applyDefaultsToElementWithoutLifespanSet(element);
3815 backOffIfDiskSpoolFull();
3816
3817 Element result = compoundStore.replace(element);
3818 if (result != null) {
3819 element.updateUpdateStatistics();
3820 notifyPutInternalListeners(element, false, true);
3821 }
3822 return result;
3823 }
3824
3825 /***
3826 * {@inheritDoc}
3827 *
3828 * @see net.sf.ehcache.store.StoreListener#clusterCoherent(boolean)
3829 */
3830 public void clusterCoherent(boolean clusterCoherent) {
3831 firePropertyChange("ClusterCoherent", !clusterCoherent, clusterCoherent);
3832 }
3833
3834
3835 /***
3836 * {@inheritDoc}
3837 */
3838 public <T> Attribute<T> getSearchAttribute(String attributeName) throws CacheException {
3839
3840 Attribute<T> searchAttribute = compoundStore.getSearchAttribute(attributeName);
3841
3842 if (searchAttribute == null) {
3843 final String msg;
3844 if (attributeName.equals(Query.KEY.getAttributeName())) {
3845 msg = "Key search attribute is disabled for cache [" + getName() + "]. It can be enabled with <searchable keys=\"true\"...";
3846 } else if (attributeName.equals(Query.VALUE.getAttributeName())) {
3847 msg = "Value search attribute is disabled for cache [" + getName() + "]. It can be enabled with <searchable values=\"true\"...";
3848 } else {
3849 msg = "No such search attribute [" + attributeName + "] defined for this cache [" + getName() + "]";
3850 }
3851
3852 throw new CacheException(msg);
3853 }
3854
3855 return searchAttribute;
3856 }
3857
3858 /***
3859 * {@inheritDoc}
3860 */
3861 public Query createQuery() {
3862 if (!isSearchable()) {
3863 throw new CacheException("This cache is not configured for search");
3864 }
3865 return new CacheQuery(this);
3866 }
3867
3868 /***
3869 * Execute the given query
3870 *
3871 * @param query query to execute
3872 * @return query results
3873 */
3874 Results executeQuery(StoreQuery query) throws SearchException {
3875
3876 if (!query.requestsKeys() && !query.requestsValues() && query.requestedAttributes().isEmpty() && query.getAggregatorInstances().isEmpty()) {
3877 String msg = "No results specified. " +
3878 "Please specify one or more of includeKeys(), includeValues(), includeAggregator() or includeAttribute()";
3879 throw new SearchException(msg);
3880 }
3881
3882 if (isStatisticsEnabled()) {
3883 long start = System.currentTimeMillis();
3884 Results results = this.compoundStore.executeQuery(query);
3885 sampledCacheStatistics.notifyCacheSearch(System.currentTimeMillis() - start);
3886 return results;
3887 }
3888
3889 return this.compoundStore.executeQuery(query);
3890 }
3891
3892 /***
3893 * {@inheritDoc}
3894 */
3895 public boolean isSearchable() {
3896 return configuration.isSearchable();
3897 }
3898
3899 /***
3900 * Start cluster rejoin
3901 */
3902 void clusterRejoinStarted() {
3903 try {
3904 nonstopActiveDelegateHolder.getUnderlyingTerracottaStore().dispose();
3905 } catch (Exception e) {
3906 LOG.info("Ignoring exception while disposing old store on rejoin - " + e.getMessage());
3907 }
3908 cacheStatus.clusterRejoinInProgress();
3909 }
3910
3911 /***
3912 * Complete cluster rejoin
3913 */
3914 void clusterRejoinComplete() {
3915
3916 initialise();
3917 cacheStatus.clusterRejoinComplete();
3918 if (compoundStore instanceof RejoinAwareNonstopStore) {
3919 ((RejoinAwareNonstopStore) compoundStore).clusterRejoined();
3920 }
3921 }
3922
3923 /***
3924 * Gets the lock for a given key
3925 *
3926 * @param key
3927 * @return the lock object for the passed in key
3928 */
3929 protected Sync getLockForKey(final Object key) {
3930 return lockProvider.getSyncForKey(key);
3931 }
3932
3933 private void acquireLockOnKey(Object key, LockType lockType) {
3934 Sync s = getLockForKey(key);
3935 s.lock(lockType);
3936 }
3937
3938 private void releaseLockOnKey(Object key, LockType lockType) {
3939 Sync s = getLockForKey(key);
3940 s.unlock(lockType);
3941 }
3942
3943 /***
3944 * Acquires the proper read lock for a given cache key
3945 *
3946 * @param key - The key that retrieves a value that you want to protect via locking
3947 */
3948 public void acquireReadLockOnKey(Object key) {
3949 this.acquireLockOnKey(key, LockType.READ);
3950 }
3951
3952 /***
3953 * Acquires the proper write lock for a given cache key
3954 *
3955 * @param key - The key that retrieves a value that you want to protect via locking
3956 */
3957 public void acquireWriteLockOnKey(Object key) {
3958 this.acquireLockOnKey(key, LockType.WRITE);
3959 }
3960
3961 /***
3962 * Try to get a read lock on a given key. If can't get it in timeout millis then
3963 * return a boolean telling that it didn't get the lock
3964 *
3965 * @param key - The key that retrieves a value that you want to protect via locking
3966 * @param timeout - millis until giveup on getting the lock
3967 * @return whether the lock was awarded
3968 * @throws InterruptedException
3969 */
3970 public boolean tryReadLockOnKey(Object key, long timeout) throws InterruptedException {
3971 Sync s = getLockForKey(key);
3972 return s.tryLock(LockType.READ, timeout);
3973 }
3974
3975 /***
3976 * Try to get a write lock on a given key. If can't get it in timeout millis then
3977 * return a boolean telling that it didn't get the lock
3978 *
3979 * @param key - The key that retrieves a value that you want to protect via locking
3980 * @param timeout - millis until giveup on getting the lock
3981 * @return whether the lock was awarded
3982 * @throws InterruptedException
3983 */
3984 public boolean tryWriteLockOnKey(Object key, long timeout) throws InterruptedException {
3985 Sync s = getLockForKey(key);
3986 return s.tryLock(LockType.WRITE, timeout);
3987 }
3988
3989 /***
3990 * Release a held read lock for the passed in key
3991 *
3992 * @param key - The key that retrieves a value that you want to protect via locking
3993 */
3994 public void releaseReadLockOnKey(Object key) {
3995 releaseLockOnKey(key, LockType.READ);
3996 }
3997
3998 /***
3999 * Release a held write lock for the passed in key
4000 *
4001 * @param key - The key that retrieves a value that you want to protect via locking
4002 */
4003 public void releaseWriteLockOnKey(Object key) {
4004 releaseLockOnKey(key, LockType.WRITE);
4005 }
4006
4007
4008 /***
4009 * {@inheritDoc}
4010 */
4011 public boolean isReadLockedByCurrentThread(Object key) {
4012 return getLockForKey(key).isHeldByCurrentThread(LockType.READ);
4013 }
4014
4015 /***
4016 * {@inheritDoc}
4017 */
4018 public boolean isWriteLockedByCurrentThread(Object key) {
4019 return getLockForKey(key).isHeldByCurrentThread(LockType.WRITE);
4020 }
4021
4022 /***
4023 * {@inheritDoc}
4024 */
4025 public boolean isClusterBulkLoadEnabled() throws UnsupportedOperationException, TerracottaNotRunningException {
4026 return !compoundStore.isClusterCoherent();
4027 }
4028
4029 /***
4030 * {@inheritDoc}
4031 */
4032 public boolean isNodeBulkLoadEnabled() throws UnsupportedOperationException, TerracottaNotRunningException {
4033 return !compoundStore.isNodeCoherent();
4034 }
4035
4036 /***
4037 * {@inheritDoc}
4038 */
4039 public void setNodeBulkLoadEnabled(boolean enabledBulkLoad) throws UnsupportedOperationException, TerracottaNotRunningException {
4040 final boolean oldValue = isNodeBulkLoadEnabled();
4041 if (oldValue != enabledBulkLoad) {
4042 compoundStore.setNodeCoherent(!enabledBulkLoad);
4043 nonstopActiveDelegateHolder.nodeBulkLoadChanged(enabledBulkLoad);
4044 firePropertyChange("NodeCoherent", oldValue, enabledBulkLoad);
4045 }
4046 }
4047
4048 /***
4049 * {@inheritDoc}
4050 */
4051 public void waitUntilClusterBulkLoadComplete() throws UnsupportedOperationException, TerracottaNotRunningException {
4052 try {
4053 compoundStore.waitUntilClusterCoherent();
4054 } catch (InterruptedException e) {
4055
4056 throw new CacheException(e);
4057 }
4058 }
4059
4060 /***
4061 * Private class maintaining status of the cache
4062 *
4063 * @author Abhishek Sanoujam
4064 *
4065 */
4066 private static class CacheStatus {
4067 private volatile Status status = Status.STATUS_UNINITIALISED;
4068 private final AtomicBoolean clusterRejoinInProgress = new AtomicBoolean(false);
4069
4070 private void clusterRejoinComplete() {
4071 clusterRejoinInProgress.set(false);
4072 }
4073
4074 public void checkAlive(CacheConfiguration configuration) {
4075 final Status readStatus = status;
4076 if (readStatus != Status.STATUS_ALIVE) {
4077 throw new IllegalStateException("The " + configuration.getName() + " Cache is not alive (" + readStatus + ")");
4078 }
4079 }
4080
4081 private void clusterRejoinInProgress() {
4082 clusterRejoinInProgress.set(true);
4083 }
4084
4085 /***
4086 * Returns true if cache can be initialized. Cache can be initialized if cache has not been shutdown yet.
4087 *
4088 * @return true if cache can be initialized
4089 */
4090 public boolean canInitialize() {
4091 return status == Status.STATUS_UNINITIALISED || clusterRejoinInProgress.get();
4092 }
4093
4094 /***
4095 * Change state to the new state
4096 *
4097 * @param newState state
4098 */
4099 public void changeState(Status newState) {
4100 this.status = newState;
4101 }
4102
4103 /***
4104 * Get the current state
4105 *
4106 * @return current state
4107 */
4108 public Status getStatus() {
4109 return status;
4110 }
4111
4112 /***
4113 * Returns true if the cache is alive
4114 *
4115 * @return true if the cache is alive
4116 */
4117 public boolean isAlive() {
4118 return status == Status.STATUS_ALIVE;
4119 }
4120
4121 /***
4122 * Returns true if the cache has been disposed
4123 *
4124 * @return true if the cache has been disposed
4125 */
4126 public boolean isShutdown() {
4127 return status == Status.STATUS_SHUTDOWN;
4128 }
4129
4130 /***
4131 * Returns true if the cache is uninitialized
4132 *
4133 * @return true if the cache is uninitialized
4134 */
4135 public boolean isUninitialized() {
4136 return status == Status.STATUS_UNINITIALISED;
4137 }
4138
4139 }
4140
4141 /***
4142 * Private Static class
4143 *
4144 * @author Abhishek Sanoujam
4145 *
4146 */
4147 private static class NonstopActiveDelegateHolderImpl implements NonstopActiveDelegateHolder {
4148
4149 private final Cache cache;
4150 private volatile NonstopStoreImpl nonstopStore;
4151 private volatile TerracottaStore underlyingTerracottaStore;
4152 private volatile NonstopExecutorService nonstopExecutorService;
4153 private volatile CacheLockProvider underlyingCacheLockProvider;
4154 private volatile boolean nodeBulkLoadEnabled;
4155
4156 public NonstopActiveDelegateHolderImpl(Cache cache) {
4157 this.cache = cache;
4158 }
4159
4160 public void nodeBulkLoadChanged(final boolean enabled) {
4161 this.nodeBulkLoadEnabled = enabled;
4162 }
4163
4164 public RejoinAwareNonstopStore getNonstopStore() {
4165 if (nonstopStore != null) {
4166 return nonstopStore;
4167 }
4168 initializeNonstopStore();
4169 return nonstopStore;
4170 }
4171
4172 private synchronized void initializeNonstopStore() {
4173 if (nonstopStore == null) {
4174 if (!cache.getCacheConfiguration().isTerracottaClustered()) {
4175 throw new AssertionError("NonstopStore supported for Terracotta clustered caches only");
4176 }
4177 if (!cache.getCacheConfiguration().getTerracottaConfiguration().isNonstopEnabled()) {
4178 throw new AssertionError("Nonstop is not enabled");
4179 }
4180 nonstopStore = new NonstopStoreImpl(this, cache.getCacheCluster(), cache.getCacheConfiguration()
4181 .getTerracottaConfiguration().getNonstopConfiguration(), cache.getCacheConfiguration().getTransactionalMode(),
4182 cache.getTransactionManagerLookup());
4183 }
4184 }
4185
4186 public synchronized void terracottaStoreInitialized(TerracottaStore newTerracottaStore) {
4187 this.underlyingTerracottaStore = newTerracottaStore;
4188
4189 if (nodeBulkLoadEnabled) {
4190 LOG.debug("Enabling bulk-load for " + cache.getName());
4191 underlyingTerracottaStore.setNodeCoherent(false);
4192 }
4193
4194
4195 nonstopExecutorService = cache.getCacheManager().getNonstopExecutorService();
4196 Object context = underlyingTerracottaStore.getInternalContext();
4197 if (context instanceof CacheLockProvider) {
4198 underlyingCacheLockProvider = (CacheLockProvider) context;
4199 } else {
4200 throw new AssertionError("TerracottaStore.getInternalContext() is not correct - "
4201 + (context == null ? "NULL" : context.getClass().getName()));
4202 }
4203 }
4204
4205 public TerracottaStore getUnderlyingTerracottaStore() {
4206 return underlyingTerracottaStore;
4207 }
4208
4209 public NonstopExecutorService getNonstopExecutorService() {
4210 return nonstopExecutorService;
4211 }
4212
4213 public CacheLockProvider getUnderlyingCacheLockProvider() {
4214 return underlyingCacheLockProvider;
4215 }
4216
4217 }
4218
4219 }