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, nullnull);
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, falsetrue);
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 nullif 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 nullif 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, falsetrue);
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, nullnull);
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 nullif 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 nullif 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, truefalse);
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, falsetrue, 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, falsefalse);
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, falsetruefalsefalse);
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, falsetrue, 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, falsetrue, 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, falsefalsefalsefalse) != 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, falsefalsefalsefalse) != null);
2280     }
2281
2282     /**
2283      * {@inheritDoc}
2284      */

2285     public boolean removeWithWriter(Object key) throws IllegalStateException {
2286         return (removeInternal(key, falsetruefalsetrue) != 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(), falsetruefalse, 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, falsetrue);
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, falsetrue);
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(truefalse)) {
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