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