View Javadoc

1   /***
2    *  Copyright 2003-2010 Terracotta, Inc.
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package net.sf.ehcache;
17  
18  import java.io.File;
19  import java.io.InputStream;
20  import java.net.URL;
21  import java.util.Collection;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Properties;
28  import java.util.Set;
29  import java.util.concurrent.Callable;
30  import java.util.concurrent.ConcurrentHashMap;
31  import java.util.concurrent.ConcurrentMap;
32  import java.util.concurrent.CopyOnWriteArrayList;
33  import java.util.concurrent.TimeoutException;
34  
35  import net.sf.ehcache.cluster.CacheCluster;
36  import net.sf.ehcache.cluster.ClusterScheme;
37  import net.sf.ehcache.cluster.ClusterSchemeNotAvailableException;
38  import net.sf.ehcache.cluster.NoopCacheCluster;
39  import net.sf.ehcache.config.CacheConfiguration;
40  import net.sf.ehcache.config.Configuration;
41  import net.sf.ehcache.config.ConfigurationFactory;
42  import net.sf.ehcache.config.ConfigurationHelper;
43  import net.sf.ehcache.config.DiskStoreConfiguration;
44  import net.sf.ehcache.config.FactoryConfiguration;
45  import net.sf.ehcache.config.InvalidConfigurationException;
46  import net.sf.ehcache.config.TerracottaClientConfiguration;
47  import net.sf.ehcache.config.TerracottaConfiguration.Consistency;
48  import net.sf.ehcache.config.TerracottaConfiguration.StorageStrategy;
49  import net.sf.ehcache.config.generator.ConfigurationUtil;
50  import net.sf.ehcache.constructs.nonstop.CacheManagerExecutorServiceFactory;
51  import net.sf.ehcache.constructs.nonstop.NonStopCacheException;
52  import net.sf.ehcache.constructs.nonstop.NonstopExecutorService;
53  import net.sf.ehcache.constructs.nonstop.NonstopExecutorServiceFactory;
54  import net.sf.ehcache.distribution.CacheManagerPeerListener;
55  import net.sf.ehcache.distribution.CacheManagerPeerProvider;
56  import net.sf.ehcache.event.CacheEventListener;
57  import net.sf.ehcache.event.CacheManagerEventListener;
58  import net.sf.ehcache.event.CacheManagerEventListenerRegistry;
59  import net.sf.ehcache.management.provider.MBeanRegistrationProvider;
60  import net.sf.ehcache.management.provider.MBeanRegistrationProviderException;
61  import net.sf.ehcache.management.provider.MBeanRegistrationProviderFactory;
62  import net.sf.ehcache.management.provider.MBeanRegistrationProviderFactoryImpl;
63  import net.sf.ehcache.pool.Pool;
64  import net.sf.ehcache.pool.PoolEvictor;
65  import net.sf.ehcache.pool.PoolableStore;
66  import net.sf.ehcache.pool.SizeOfEngine;
67  import net.sf.ehcache.pool.impl.BalancedAccessOnDiskPoolEvictor;
68  import net.sf.ehcache.pool.impl.BalancedAccessOnHeapPoolEvictor;
69  import net.sf.ehcache.pool.impl.BoundedPool;
70  import net.sf.ehcache.pool.impl.DefaultSizeOfEngine;
71  import net.sf.ehcache.store.Store;
72  import net.sf.ehcache.store.disk.DiskStore;
73  import net.sf.ehcache.terracotta.ClusteredInstanceFactory;
74  import net.sf.ehcache.terracotta.TerracottaClient;
75  import net.sf.ehcache.terracotta.TerracottaClientRejoinListener;
76  import net.sf.ehcache.transaction.ReadCommittedSoftLockFactoryImpl;
77  import net.sf.ehcache.transaction.SoftLockFactory;
78  import net.sf.ehcache.transaction.TransactionIDFactory;
79  import net.sf.ehcache.transaction.TransactionIDFactoryImpl;
80  import net.sf.ehcache.transaction.manager.TransactionManagerLookup;
81  import net.sf.ehcache.transaction.xa.processor.XARequestProcessor;
82  import net.sf.ehcache.util.FailSafeTimer;
83  import net.sf.ehcache.util.PropertyUtil;
84  import net.sf.ehcache.util.UpdateChecker;
85  import net.sf.ehcache.writer.writebehind.WriteBehind;
86  
87  import org.slf4j.Logger;
88  import org.slf4j.LoggerFactory;
89  
90  /***
91   * A container for {@link Ehcache}s that maintain all aspects of their lifecycle.
92   * <p/>
93   * CacheManager may be either be a singleton if created with factory methods, or multiple instances may exist, in which case resources
94   * required by each must be unique.
95   * <p/>
96   * A CacheManager holds references to Caches and Ehcaches and manages their creation and lifecycle.
97   *
98   * @author Greg Luck
99   * @version $Id: CacheManager.html 13146 2011-08-01 17:12:39Z oletizi $
100  */
101 public class CacheManager {
102 
103     /***
104      * Default name if not specified in the configuration/
105      */
106     public static final String DEFAULT_NAME = "__DEFAULT__";
107 
108     /***
109      * Threshold, in percent of the available heap, above which the CacheManager will warn if the configured memory
110      */
111     public static final double ON_HEAP_THRESHOLD = 0.8;
112 
113     /***
114      * Keeps track of all known CacheManagers. Used to check on conflicts.
115      * CacheManagers should remove themselves from this list during shut down.
116      */
117     public static final List<CacheManager> ALL_CACHE_MANAGERS = new CopyOnWriteArrayList<CacheManager>();
118 
119     /***
120      * System property to enable creation of a shutdown hook for CacheManager.
121      */
122     public static final String ENABLE_SHUTDOWN_HOOK_PROPERTY = "net.sf.ehcache.enableShutdownHook";
123 
124     private static final Logger LOG = LoggerFactory.getLogger(CacheManager.class);
125 
126     /***
127      * Update check interval - one week in milliseconds
128      */
129     private static final long EVERY_WEEK = 7 * 24 * 60 * 60 * 1000;
130 
131     /***
132      * delay period before doing update check
133      */
134     private static final long DELAY_UPDATE_CHECK = 1000;
135 
136     /***
137      * The Singleton Instance.
138      */
139     private static volatile CacheManager singleton;
140 
141     /***
142      * The factory to use for creating MBeanRegistrationProvider's
143      */
144     private static final MBeanRegistrationProviderFactory MBEAN_REGISTRATION_PROVIDER_FACTORY = new MBeanRegistrationProviderFactoryImpl();
145 
146     private static final String NO_DEFAULT_CACHE_ERROR_MSG = "Caches cannot be added by name when default cache config is not specified"
147             + " in the config. Please add a default cache config in the configuration.";
148     private static final int HUNDRED = 100;
149 
150     /***
151      * A name for this CacheManager to distinguish it from others.
152      */
153     protected volatile String name;
154 
155     /***
156      * Status of the Cache Manager
157      */
158     protected volatile Status status;
159 
160     /***
161      * The map of providers
162      */
163     protected final Map<String, CacheManagerPeerProvider> cacheManagerPeerProviders = new ConcurrentHashMap<String, CacheManagerPeerProvider>();
164 
165     /***
166      * The map of listeners
167      */
168     protected final Map<String, CacheManagerPeerListener> cacheManagerPeerListeners = new ConcurrentHashMap<String, CacheManagerPeerListener>();
169 
170     /***
171      * The listener registry
172      */
173     protected final CacheManagerEventListenerRegistry cacheManagerEventListenerRegistry = new CacheManagerEventListenerRegistry();
174 
175     /***
176      * The shutdown hook thread for CacheManager. This ensures that the CacheManager and Caches are left in a
177      * consistent state on a CTRL-C or kill.
178      * <p/>
179      * This thread must be unregistered as a shutdown hook, when the CacheManager is disposed. Otherwise the CacheManager is not GC-able.
180      * <p/>
181      * Of course kill -9 or abrupt termination will not run the shutdown hook. In this case, various sanity checks are made at start up.
182      */
183     protected Thread shutdownHook;
184 
185     /***
186      * Ehcaches managed by this manager.
187      */
188     private final ConcurrentMap<String, Ehcache> ehcaches = new ConcurrentHashMap<String, Ehcache>();
189 
190     /***
191      * Default cache cache.
192      */
193     private Ehcache defaultCache;
194 
195     /***
196      * The path for the directory in which disk caches are created.
197      */
198     private String diskStorePath;
199 
200     private MBeanRegistrationProvider mbeanRegistrationProvider;
201 
202     private FailSafeTimer cacheManagerTimer;
203 
204     private volatile TerracottaClient terracottaClient;
205 
206     /***
207      * The {@link TerracottaClientConfiguration} used for this {@link CacheManager}
208      */
209     private TerracottaClientConfiguration terracottaClientConfiguration;
210 
211     private Configuration configuration;
212 
213     private volatile boolean allowsDynamicCacheConfig = true;
214 
215     private volatile TransactionManagerLookup transactionManagerLookup;
216 
217     private volatile TransactionController transactionController;
218 
219     private final ConcurrentMap<String, SoftLockFactory> softLockFactories = new ConcurrentHashMap<String, SoftLockFactory>();
220 
221     private volatile Pool onHeapPool;
222 
223     private volatile Pool onDiskPool;
224 
225     private final NonstopExecutorServiceFactory nonstopExecutorServiceFactory = CacheManagerExecutorServiceFactory.getInstance();
226 
227     /***
228      * An constructor for CacheManager, which takes a configuration object, rather than one created by parsing
229      * an ehcache.xml file. This constructor gives complete control over the creation of the CacheManager.
230      * <p/>
231      * Care should be taken to ensure that, if multiple CacheManages are created, they do now overwrite each others disk store files, as
232      * would happend if two were created which used the same diskStore path.
233      * <p/>
234      * This method does not act as a singleton. Callers must maintain their own reference to it.
235      * <p/>
236      * Note that if one of the {@link #create()} methods are called, a new singleton instance will be created, separate from any instances
237      * created in this method.
238      *
239      * @param configuration
240      * @throws CacheException
241      */
242     public CacheManager(Configuration configuration) throws CacheException {
243         status = Status.STATUS_UNINITIALISED;
244         init(configuration, null, null, null);
245     }
246 
247     /***
248      * An ordinary constructor for CacheManager.
249      * This method does not act as a singleton. Callers must maintain a reference to it.
250      * Note that if one of the {@link #create()} methods are called, a new singleton will be created,
251      * separate from any instances created in this method.
252      *
253      * @param configurationFileName
254      *            an xml configuration file available through a file name. The configuration {@link File} is created
255      *            using new <code>File(configurationFileName)</code>
256      * @throws CacheException
257      * @see #create(String)
258      */
259     public CacheManager(String configurationFileName) throws CacheException {
260         status = Status.STATUS_UNINITIALISED;
261         init(null, configurationFileName, null, null);
262     }
263 
264     /***
265      * An ordinary constructor for CacheManager.
266      * This method does not act as a singleton. Callers must maintain a reference to it.
267      * Note that if one of the {@link #create()} methods are called, a new singleton will be created,
268      * separate from any instances created in this method.
269      * <p/>
270      * This method can be used to specify a configuration resource in the classpath other than the default of \"/ehcache.xml\":
271      *
272      * <pre>
273      * URL url = this.getClass().getResource(&quot;/ehcache-2.xml&quot;);
274      * </pre>
275      *
276      * Note that {@link Class#getResource(String)} will look for resources in the same package unless a leading "/" is used, in which case it will
277      * look in the root of the classpath.
278      * <p/>
279      * You can also load a resource using other class loaders. e.g. {@link Thread#getContextClassLoader()}
280      *
281      * @param configurationURL
282      *            an xml configuration available through a URL.
283      * @throws CacheException
284      * @see #create(java.net.URL)
285      * @since 1.2
286      */
287     public CacheManager(URL configurationURL) throws CacheException {
288         status = Status.STATUS_UNINITIALISED;
289         init(null, null, configurationURL, null);
290     }
291 
292     /***
293      * An ordinary constructor for CacheManager.
294      * This method does not act as a singleton. Callers must maintain a reference to it.
295      * Note that if one of the {@link #create()} methods are called, a new singleton will be created,
296      * separate from any instances created in this method.
297      *
298      * @param configurationInputStream
299      *            an xml configuration file available through an inputstream
300      * @throws CacheException
301      * @see #create(java.io.InputStream)
302      */
303     public CacheManager(InputStream configurationInputStream) throws CacheException {
304         status = Status.STATUS_UNINITIALISED;
305         init(null, null, null, configurationInputStream);
306     }
307 
308     /***
309      * Constructor.
310      *
311      * @throws CacheException
312      */
313     public CacheManager() throws CacheException {
314         // default config will be done
315         status = Status.STATUS_UNINITIALISED;
316         init(null, null, null, null);
317     }
318 
319     /***
320      * initialises the CacheManager
321      */
322     protected void init(Configuration initialConfiguration, String configurationFileName, URL configurationURL,
323             InputStream configurationInputStream) {
324         Configuration localConfiguration = initialConfiguration;
325         if (initialConfiguration == null) {
326             localConfiguration = parseConfiguration(configurationFileName, configurationURL, configurationInputStream);
327             this.configuration = localConfiguration;
328         } else {
329             this.configuration = initialConfiguration;
330         }
331 
332         if (this.configuration.getTerracottaConfiguration() != null) {
333             this.configuration.getTerracottaConfiguration().freezeConfig();
334         }
335         validateConfiguration();
336 
337         if (this.configuration.isMaxBytesLocalHeapSet()) {
338             PoolEvictor<PoolableStore> evictor = new BalancedAccessOnHeapPoolEvictor();
339             SizeOfEngine sizeOfEngine = createSizeOfEngine(null);
340             this.onHeapPool = new BoundedPool(this.configuration.getMaxBytesLocalHeap(), evictor, sizeOfEngine);
341         }
342         if (this.configuration.isMaxBytesLocalDiskSet()) {
343             PoolEvictor<PoolableStore> evictor = new BalancedAccessOnDiskPoolEvictor();
344             this.onDiskPool = new BoundedPool(this.configuration.getMaxBytesLocalDisk(), evictor, null);
345         }
346 
347         if (localConfiguration.getName() != null) {
348             this.name = localConfiguration.getName();
349         }
350 
351         this.allowsDynamicCacheConfig = localConfiguration.getDynamicConfig();
352         this.terracottaClientConfiguration = localConfiguration.getTerracottaConfiguration();
353 
354         terracottaClient = new TerracottaClient(this, new TerracottaClientRejoinListener() {
355             public void clusterRejoinStarted() {
356                 CacheManager.this.clusterRejoinStarted();
357             }
358 
359             public void clusterRejoinComplete() {
360                 CacheManager.this.clusterRejoinComplete();
361             }
362         }, localConfiguration.getTerracottaConfiguration());
363 
364         Map<String, CacheConfiguration> cacheConfigs = localConfiguration.getCacheConfigurations();
365         if (localConfiguration.getDefaultCacheConfiguration() != null
366                 && localConfiguration.getDefaultCacheConfiguration().isTerracottaClustered()) {
367             terracottaClient.createClusteredInstanceFactory(cacheConfigs);
368         } else {
369             for (CacheConfiguration config : cacheConfigs.values()) {
370                 if (config.isTerracottaClustered()) {
371                     terracottaClient.createClusteredInstanceFactory(cacheConfigs);
372                     break;
373                 }
374             }
375         }
376 
377         if (terracottaClient.getClusteredInstanceFactory() != null && this.name == null) {
378             this.name = CacheManager.DEFAULT_NAME;
379         }
380 
381         TransactionIDFactory transactionIDFactory = createTransactionIDFactory();
382         this.transactionController = new TransactionController(transactionIDFactory, configuration.getDefaultTransactionTimeoutInSeconds());
383 
384         ConfigurationHelper configurationHelper = new ConfigurationHelper(this, localConfiguration);
385         configure(configurationHelper);
386         status = Status.STATUS_ALIVE;
387 
388         for (CacheManagerPeerProvider cacheManagerPeerProvider : cacheManagerPeerProviders.values()) {
389             cacheManagerPeerProvider.init();
390         }
391 
392         cacheManagerEventListenerRegistry.init();
393         addShutdownHookIfRequired();
394 
395         cacheManagerTimer = new FailSafeTimer(getName());
396         checkForUpdateIfNeeded(localConfiguration.getUpdateCheck());
397 
398         mbeanRegistrationProvider = MBEAN_REGISTRATION_PROVIDER_FACTORY.createMBeanRegistrationProvider(localConfiguration);
399 
400         // do this last
401         addConfiguredCaches(configurationHelper);
402 
403         try {
404             mbeanRegistrationProvider.initialize(this, terracottaClient.getClusteredInstanceFactory());
405         } catch (MBeanRegistrationProviderException e) {
406             LOG.warn("Failed to initialize the MBeanRegistrationProvider - " + mbeanRegistrationProvider.getClass().getName(), e);
407         }
408     }
409 
410     /***
411      * Return this cache manager's shared on-heap pool
412      *
413      * @return this cache manager's shared on-heap pool
414      */
415     public Pool getOnHeapPool() {
416         return onHeapPool;
417     }
418 
419     /***
420      * Return this cache manager's shared on-disk pool
421      *
422      * @return this cache manager's shared on-disk pool
423      */
424     public Pool getOnDiskPool() {
425         return onDiskPool;
426     }
427 
428     private boolean isTerracottaRejoinEnabled() {
429         TerracottaClientConfiguration terracottaConfiguration = configuration.getTerracottaConfiguration();
430         return terracottaConfiguration != null && terracottaConfiguration.isRejoin();
431     }
432 
433     private void validateConfiguration() {
434         if (isTerracottaRejoinEnabled()) {
435             validateCacheConfigs(configuration.getCacheConfigurations().values());
436         }
437     }
438 
439     private void validateCacheConfigs(Collection<CacheConfiguration> cacheConfigs) {
440         boolean invalid = false;
441         final StringBuilder error = new StringBuilder();
442         for (CacheConfiguration config : cacheConfigs) {
443             if (config.isTerracottaClustered()) {
444                 if (config.getTerracottaConfiguration().getStorageStrategy().equals(StorageStrategy.CLASSIC)) {
445                     if (config.getTerracottaConfiguration().isNonstopEnabled()) {
446                         invalid = true;
447                         error.append("\n").append(
448                                 "NONSTOP can't be enabled with " + StorageStrategy.CLASSIC.name() + " strategy. Invalid Cache: "
449                                         + config.getName());
450                     }
451 
452                     if (isTerracottaRejoinEnabled()) {
453                         invalid = true;
454                         error.append("\n").append(
455                                 "REJOIN can't be enabled with " + StorageStrategy.CLASSIC.name() + " strategy. Invalid Cache: "
456                                         + config.getName());
457                     }
458 
459                     if (config.getTerracottaConsistency().equals(Consistency.EVENTUAL)) {
460                         invalid = true;
461                         error.append("\n").append(
462                                 Consistency.EVENTUAL.name() + " consistency can't be enabled with " + StorageStrategy.CLASSIC.name()
463                                         + " strategy. Invalid Cache: " + config.getName());
464                     }
465                 }
466 
467                 if (isTerracottaRejoinEnabled() && !config.getTerracottaConfiguration().isNonstopEnabled()) {
468                     invalid = true;
469                     error.append("\n").append(
470                             "Terracotta clustered caches must be nonstop when rejoin is enabled. Invalid cache: " + config.getName());
471                 }
472             }
473         }
474 
475         if (invalid) {
476             String errorMessage = "Errors:" + error.toString();
477             throw new InvalidConfigurationException(errorMessage);
478         }
479     }
480 
481     /***
482      * Returns unique cluster-wide id for this cache-manager. Only applicable when running in "cluster" mode, e.g. when this cache-manager
483      * contains caches clustered with Terracotta. Otherwise returns blank string.
484      *
485      * @return Returns unique cluster-wide id for this cache-manager when it contains clustered caches (e.g. Terracotta clustered caches).
486      *         Otherwise returns blank string.
487      */
488     public String getClusterUUID() {
489         if (terracottaClient.getClusteredInstanceFactory() != null) {
490             return getClientUUID(terracottaClient.getClusteredInstanceFactory());
491         } else {
492             return "";
493         }
494     }
495 
496     private static String getClientUUID(ClusteredInstanceFactory clusteredInstanceFactory) {
497         return clusteredInstanceFactory.getUUID();
498     }
499 
500     /***
501      * Create/access the appropriate terracotta clustered store for the given cache
502      *
503      * @param cache The cache for which the Store should be created
504      * @return a new (or existing) clustered store
505      */
506     public Store createTerracottaStore(Ehcache cache) {
507         return getClusteredInstanceFactory(cache).createStore(cache);
508     }
509 
510     /***
511      * Create/access the appropriate clustered write behind queue for the given cache
512      *
513      * @param cache The cache for which the write behind queue should be created
514      * @return a new (or existing) write behind queue
515      */
516     public WriteBehind createTerracottaWriteBehind(Ehcache cache) {
517         return getClusteredInstanceFactory(cache).createWriteBehind(cache);
518     }
519 
520     /***
521      * Create/access the appropriate clustered cache event replicator for the given cache
522      *
523      * @param cache The cache for which the clustered event replicator should be created
524      * @return a new cache event replicator
525      */
526     public CacheEventListener createTerracottaEventReplicator(Ehcache cache) {
527         return getClusteredInstanceFactory(cache).createEventReplicator(cache);
528     }
529 
530     /***
531      * Return the clustered instance factory for a cache of this cache manager.
532      *
533      * @param cache the cache the clustered instance factory has to be returned for
534      * @return the clustered instance factory
535      */
536     private ClusteredInstanceFactory getClusteredInstanceFactory(Ehcache cache) {
537         ClusteredInstanceFactory clusteredInstanceFactory = terracottaClient.getClusteredInstanceFactory();
538         if (null == clusteredInstanceFactory) {
539             // adding a cache programmatically when there is no clustered store defined in the configuration
540             // at the time this cacheManager was created
541             Map<String, CacheConfiguration> map = new HashMap<String, CacheConfiguration>(1);
542             map.put(cache.getName(), cache.getCacheConfiguration());
543             final boolean created = terracottaClient.createClusteredInstanceFactory(map);
544             clusteredInstanceFactory = terracottaClient.getClusteredInstanceFactory();
545 
546             if (created) {
547                 try {
548                     mbeanRegistrationProvider.reinitialize(clusteredInstanceFactory);
549                 } catch (MBeanRegistrationProviderException e) {
550                     LOG.warn("Failed to initialize the MBeanRegistrationProvider - " + mbeanRegistrationProvider.getClass().getName(), e);
551                 }
552             }
553         }
554         return clusteredInstanceFactory;
555     }
556 
557     private void checkForUpdateIfNeeded(boolean updateCheckNeeded) {
558         try {
559             if (updateCheckNeeded) {
560                 UpdateChecker updateChecker = new UpdateChecker();
561                 cacheManagerTimer.scheduleAtFixedRate(updateChecker, DELAY_UPDATE_CHECK, EVERY_WEEK);
562             }
563         } catch (Throwable t) {
564             LOG.debug("Failed to set up update checker", t);
565         }
566     }
567 
568     /***
569      * Loads configuration, either from the supplied {@link ConfigurationHelper} or by creating a new Configuration instance
570      * from the configuration file referred to by file, inputstream or URL.
571      * <p/>
572      * Should only be called once.
573      *
574      * @param configurationFileName
575      *            the file name to parse, or null
576      * @param configurationURL
577      *            the URL to pass, or null
578      * @param configurationInputStream
579      *            , the InputStream to parse, or null
580      * @return the loaded configuration
581      * @throws CacheException
582      *             if the configuration cannot be parsed
583      */
584     private synchronized Configuration parseConfiguration(String configurationFileName, URL configurationURL,
585             InputStream configurationInputStream) throws CacheException {
586         reinitialisationCheck();
587         Configuration parsedConfig;
588         if (configurationFileName != null) {
589 
590             LOG.debug("Configuring CacheManager from {}", configurationFileName);
591             parsedConfig = ConfigurationFactory.parseConfiguration(new File(configurationFileName));
592         } else if (configurationURL != null) {
593             parsedConfig = ConfigurationFactory.parseConfiguration(configurationURL);
594         } else if (configurationInputStream != null) {
595             parsedConfig = ConfigurationFactory.parseConfiguration(configurationInputStream);
596         } else {
597             LOG.debug("Configuring ehcache from classpath.");
598             parsedConfig = ConfigurationFactory.parseConfiguration();
599         }
600         return parsedConfig;
601 
602     }
603 
604     private void configure(ConfigurationHelper configurationHelper) {
605 
606         diskStorePath = configurationHelper.getDiskStorePath();
607         int cachesRequiringDiskStores = configurationHelper.numberOfCachesThatOverflowToDisk().intValue()
608                 + configurationHelper.numberOfCachesThatAreDiskPersistent().intValue();
609 
610         if (diskStorePath == null && cachesRequiringDiskStores > 0) {
611             diskStorePath = DiskStoreConfiguration.getDefaultPath();
612             LOG.warn("One or more caches require a DiskStore but there is no diskStore element configured."
613                     + " Using the default disk store path of " + DiskStoreConfiguration.getDefaultPath()
614                     + ". Please explicitly configure the diskStore element in ehcache.xml.");
615         }
616 
617         FactoryConfiguration lookupConfiguration = configuration.getTransactionManagerLookupConfiguration();
618         try {
619             Properties properties = PropertyUtil.parseProperties(lookupConfiguration.getProperties(), lookupConfiguration
620                     .getPropertySeparator());
621             Class<TransactionManagerLookup> transactionManagerLookupClass = (Class<TransactionManagerLookup>) Class
622                     .forName(lookupConfiguration.getFullyQualifiedClassPath());
623             this.transactionManagerLookup = transactionManagerLookupClass.newInstance();
624             this.transactionManagerLookup.setProperties(properties);
625         } catch (Exception e) {
626             LOG.error("could not instantiate transaction manager lookup class: {}", lookupConfiguration.getFullyQualifiedClassPath(), e);
627         }
628 
629         detectAndFixDiskStorePathConflict(configurationHelper);
630 
631         cacheManagerEventListenerRegistry.registerListener(configurationHelper.createCacheManagerEventListener(this));
632 
633         cacheManagerPeerListeners.putAll(configurationHelper.createCachePeerListeners());
634         for (CacheManagerPeerListener cacheManagerPeerListener : cacheManagerPeerListeners.values()) {
635             cacheManagerEventListenerRegistry.registerListener(cacheManagerPeerListener);
636         }
637 
638         detectAndFixCacheManagerPeerListenerConflict(configurationHelper);
639 
640         ALL_CACHE_MANAGERS.add(this);
641 
642         cacheManagerPeerProviders.putAll(configurationHelper.createCachePeerProviders());
643         defaultCache = configurationHelper.createDefaultCache();
644     }
645 
646     private void detectAndFixDiskStorePathConflict(ConfigurationHelper configurationHelper) {
647         if (diskStorePath == null) {
648             LOG.debug("No disk store path defined. Skipping disk store path conflict test.");
649             return;
650         }
651 
652         for (CacheManager cacheManager : ALL_CACHE_MANAGERS) {
653             if (diskStorePath.equals(cacheManager.diskStorePath)) {
654                 String newDiskStorePath = diskStorePath + File.separator + DiskStore.generateUniqueDirectory();
655                 LOG.warn("Creating a new instance of CacheManager using the diskStorePath \"" + diskStorePath + "\" which is already used"
656                         + " by an existing CacheManager.\nThe source of the configuration was "
657                         + configurationHelper.getConfigurationBean().getConfigurationSource() + ".\n"
658                         + "The diskStore path for this CacheManager will be set to " + newDiskStorePath + ".\nTo avoid this"
659                         + " warning consider using the CacheManager factory methods to create a singleton CacheManager "
660                         + "or specifying a separate ehcache configuration (ehcache.xml) for each CacheManager instance.");
661                 diskStorePath = newDiskStorePath;
662                 break;
663             }
664         }
665     }
666 
667     private void detectAndFixCacheManagerPeerListenerConflict(ConfigurationHelper configurationHelper) {
668         if (cacheManagerPeerListeners == null) {
669             return;
670         }
671         for (CacheManagerPeerListener cacheManagerPeerListener : cacheManagerPeerListeners.values()) {
672             String uniqueResourceIdentifier = cacheManagerPeerListener.getUniqueResourceIdentifier();
673             for (CacheManager cacheManager : ALL_CACHE_MANAGERS) {
674                 for (CacheManagerPeerListener otherCacheManagerPeerListener : cacheManager.cacheManagerPeerListeners.values()) {
675                     if (otherCacheManagerPeerListener == null) {
676                         continue;
677                     }
678                     String otherUniqueResourceIdentifier = otherCacheManagerPeerListener.getUniqueResourceIdentifier();
679                     if (uniqueResourceIdentifier.equals(otherUniqueResourceIdentifier)) {
680                         LOG.warn("Creating a new instance of CacheManager with a CacheManagerPeerListener which "
681                                 + "has a conflict on a resource that must be unique.\n" + "The resource is " + uniqueResourceIdentifier
682                                 + ".\n" + "Attempting automatic resolution. The source of the configuration was "
683                                 + configurationHelper.getConfigurationBean().getConfigurationSource() + ".\n"
684                                 + "To avoid this warning consider using the CacheManager factory methods to create a "
685                                 + "singleton CacheManager "
686                                 + "or specifying a separate ehcache configuration (ehcache.xml) for each CacheManager instance.");
687                         cacheManagerPeerListener.attemptResolutionOfUniqueResourceConflict();
688                         break;
689                     }
690                 }
691 
692             }
693         }
694     }
695 
696     private void addConfiguredCaches(ConfigurationHelper configurationHelper) {
697         Set unitialisedCaches = configurationHelper.createCaches();
698         for (Iterator iterator = unitialisedCaches.iterator(); iterator.hasNext();) {
699             Ehcache unitialisedCache = (Ehcache) iterator.next();
700             addCacheNoCheck(unitialisedCache, true);
701 
702             // add the cache decorators for the cache, if any
703             List<Ehcache> cacheDecorators = configurationHelper.createCacheDecorators(unitialisedCache);
704             for (Ehcache decoratedCache : cacheDecorators) {
705                 addOrReplaceDecoratedCache(unitialisedCache, decoratedCache);
706             }
707         }
708     }
709 
710     private void addOrReplaceDecoratedCache(final Ehcache underlyingCache, final Ehcache decoratedCache) {
711         if (decoratedCache.getName().equals(underlyingCache.getName())) {
712             this.replaceCacheWithDecoratedCache(underlyingCache, decoratedCache);
713         } else {
714             addDecoratedCache(decoratedCache);
715         }
716     }
717 
718     private void reinitialisationCheck() throws IllegalStateException {
719         if (diskStorePath != null || ehcaches.size() != 0 || status.equals(Status.STATUS_SHUTDOWN)) {
720             throw new IllegalStateException("Attempt to reinitialise the CacheManager");
721         }
722     }
723 
724     /***
725      * A factory method to create a singleton CacheManager with default config, or return it if it exists.
726      * <p/>
727      * The configuration will be read, {@link Ehcache}s created and required stores initialized. When the {@link CacheManager} is no longer
728      * required, call shutdown to free resources.
729      *
730      * @return the singleton CacheManager
731      * @throws CacheException
732      *             if the CacheManager cannot be created
733      */
734     public static CacheManager create() throws CacheException {
735         if (singleton != null) {
736             return singleton;
737         }
738         synchronized (CacheManager.class) {
739             if (singleton == null) {
740                 LOG.debug("Creating new CacheManager with default config");
741                 singleton = new CacheManager();
742             } else {
743                 LOG.debug("Attempting to create an existing singleton. Existing singleton returned.");
744             }
745             return singleton;
746         }
747     }
748 
749     /***
750      * A factory method to create a singleton CacheManager with default config, or return it if it exists.
751      * <p/>
752      * This has the same effect as {@link CacheManager#create}
753      * <p/>
754      * Same as {@link #create()}
755      *
756      * @return the singleton CacheManager
757      * @throws CacheException
758      *             if the CacheManager cannot be created
759      */
760     public static CacheManager getInstance() throws CacheException {
761         return CacheManager.create();
762     }
763 
764     /***
765      * A factory method to create a singleton CacheManager with a specified configuration.
766      *
767      * @param configurationFileName
768      *            an xml file compliant with the ehcache.xsd schema
769      *            <p/>
770      *            The configuration will be read, {@link Ehcache}s created and required stores initialized. When the {@link CacheManager} is
771      *            no longer required, call shutdown to free resources.
772      */
773     public static CacheManager create(String configurationFileName) throws CacheException {
774         if (singleton != null) {
775             return singleton;
776         }
777         synchronized (CacheManager.class) {
778             if (singleton == null) {
779                 LOG.debug("Creating new CacheManager with config file: {}", configurationFileName);
780                 singleton = new CacheManager(configurationFileName);
781             }
782             return singleton;
783         }
784     }
785 
786     /***
787      * A factory method to create a singleton CacheManager from an URL.
788      * <p/>
789      * This method can be used to specify a configuration resource in the classpath other than the default of \"/ehcache.xml\": This method
790      * can be used to specify a configuration resource in the classpath other than the default of \"/ehcache.xml\":
791      *
792      * <pre>
793      * URL url = this.getClass().getResource(&quot;/ehcache-2.xml&quot;);
794      * </pre>
795      *
796      * Note that {@link Class#getResource(String)} will look for resources in the same package unless a leading "/" is used, in which case it will
797      * look in the root of the classpath.
798      * <p/>
799      * You can also load a resource using other class loaders. e.g. {@link Thread#getContextClassLoader()}
800      *
801      * @param configurationFileURL
802      *            an URL to an xml file compliant with the ehcache.xsd schema
803      *            <p/>
804      *            The configuration will be read, {@link Ehcache}s created and required stores initialized. When the {@link CacheManager} is
805      *            no longer required, call shutdown to free resources.
806      */
807     public static CacheManager create(URL configurationFileURL) throws CacheException {
808         if (singleton != null) {
809             return singleton;
810         }
811         synchronized (CacheManager.class) {
812             if (singleton == null) {
813                 LOG.debug("Creating new CacheManager with config URL: {}", configurationFileURL);
814                 singleton = new CacheManager(configurationFileURL);
815             }
816             return singleton;
817         }
818     }
819 
820     /***
821      * A factory method to create a singleton CacheManager from a java.io.InputStream.
822      * <p/>
823      * This method makes it possible to use an inputstream for configuration. Note: it is the clients responsibility to close the
824      * inputstream.
825      * <p/>
826      *
827      * @param inputStream
828      *            InputStream of xml compliant with the ehcache.xsd schema
829      *            <p/>
830      *            The configuration will be read, {@link Ehcache}s created and required stores initialized. When the {@link CacheManager} is
831      *            no longer required, call shutdown to free resources.
832      */
833     public static CacheManager create(InputStream inputStream) throws CacheException {
834         if (singleton != null) {
835             return singleton;
836         }
837         synchronized (CacheManager.class) {
838             if (singleton == null) {
839                 LOG.debug("Creating new CacheManager with InputStream");
840                 singleton = new CacheManager(inputStream);
841             }
842             return singleton;
843         }
844     }
845 
846     /***
847      * A factory method to create a singleton CacheManager from a net.sf.ehcache.config.Configuration.
848      * <p/>
849      * This method makes it possible to use an inputstream for configuration. Note: it is the clients responsibility to close the
850      * inputstream.
851      * <p/>
852      *
853      * @param config
854      */
855     public static CacheManager create(Configuration config) throws CacheException {
856         if (singleton != null) {
857             return singleton;
858         }
859         synchronized (CacheManager.class) {
860             if (singleton == null) {
861                 LOG.debug("Creating new CacheManager with InputStream");
862                 singleton = new CacheManager(config);
863             }
864             return singleton;
865         }
866     }
867 
868     /***
869      * Returns a concrete implementation of Cache, it it is available in the CacheManager.
870      * Consider using getEhcache(String name) instead, which will return decorated caches that are registered.
871      * <p/>
872      * If a decorated ehcache is registered in CacheManager, an undecorated Cache with the same name may also exist.
873      *
874      * Since version ehcache-core-2.1.0, when an {@link Ehcache} decorator is present in the CacheManager, its not necessary that a
875      * {@link Cache} instance is also present for the same name. Decorators can have different names other than the name of the cache its
876      * decorating.
877      *
878      * @return a Cache, if an object of that type exists by that name, else null
879      * @throws IllegalStateException
880      *             if the cache is not {@link Status#STATUS_ALIVE}
881      * @see #getEhcache(String)
882      */
883     public Cache getCache(String name) throws IllegalStateException, ClassCastException {
884         checkStatus();
885         return ehcaches.get(name) instanceof Cache ? (Cache) ehcaches.get(name) : null;
886     }
887 
888     /***
889      * Gets an Ehcache
890      * <p/>
891      *
892      * @return a Cache, if an object of type Cache exists by that name, else null
893      * @throws IllegalStateException
894      *             if the cache is not {@link Status#STATUS_ALIVE}
895      */
896     public Ehcache getEhcache(String name) throws IllegalStateException {
897         checkStatus();
898         return ehcaches.get(name);
899     }
900 
901     /***
902      * Some caches might be persistent, so we want to add a shutdown hook if that is the
903      * case, so that the data and index can be written to disk.
904      */
905     private void addShutdownHookIfRequired() {
906 
907         String shutdownHookProperty = System.getProperty(ENABLE_SHUTDOWN_HOOK_PROPERTY);
908         boolean enabled = PropertyUtil.parseBoolean(shutdownHookProperty);
909         if (!enabled) {
910             return;
911         } else {
912             LOG.info("The CacheManager shutdown hook is enabled because {} is set to true.", ENABLE_SHUTDOWN_HOOK_PROPERTY);
913 
914             Thread localShutdownHook = new Thread() {
915                 @Override
916                 public void run() {
917                     synchronized (this) {
918                         if (status.equals(Status.STATUS_ALIVE)) {
919                             // clear shutdown hook reference to prevent
920                             // removeShutdownHook to remove it during shutdown
921                             shutdownHook = null;
922                             LOG.info("VM shutting down with the CacheManager still active. Calling shutdown.");
923                             shutdown();
924                         }
925                     }
926                 }
927             };
928 
929             Runtime.getRuntime().addShutdownHook(localShutdownHook);
930             shutdownHook = localShutdownHook;
931         }
932     }
933 
934     /***
935      * Remove the shutdown hook to prevent leaving orphaned CacheManagers around. This
936      * is called by {@link #shutdown()} AFTER the status has been set to shutdown.
937      */
938     private void removeShutdownHook() {
939         if (shutdownHook != null) {
940             // remove shutdown hook
941             try {
942                 Runtime.getRuntime().removeShutdownHook(shutdownHook);
943             } catch (IllegalStateException e) {
944                 // This will be thrown if the VM is shutting down. In this case
945                 // we do not need to worry about leaving references to CacheManagers lying
946                 // around and the call is ok to fail.
947                 LOG.debug("IllegalStateException due to attempt to remove a shutdown" + "hook while the VM is actually shutting down.", e);
948             }
949             shutdownHook = null;
950         }
951     }
952 
953     /***
954      * Adds a {@link Ehcache} based on the defaultCache with the given name.
955      * <p/>
956      * Memory and Disk stores will be configured for it and it will be added to the map of caches.
957      * <p/>
958      * Also notifies the CacheManagerEventListener after the cache was initialised and added.
959      * <p/>
960      * It will be created with the defaultCache attributes specified in ehcache.xml
961      *
962      * @param cacheName
963      *            the name for the cache
964      * @throws ObjectExistsException
965      *             if the cache already exists
966      * @throws CacheException
967      *             if there was an error creating the cache.
968      */
969     public void addCache(String cacheName) throws IllegalStateException, ObjectExistsException, CacheException {
970         checkStatus();
971 
972         // NPE guard
973         if (cacheName == null || cacheName.length() == 0) {
974             return;
975         }
976 
977         if (ehcaches.get(cacheName) != null) {
978             throw new ObjectExistsException("Cache " + cacheName + " already exists");
979         }
980         Ehcache clonedDefaultCache = cloneDefaultCache(cacheName);
981         if (clonedDefaultCache == null) {
982             throw new CacheException(NO_DEFAULT_CACHE_ERROR_MSG);
983         }
984         addCache(clonedDefaultCache);
985         for (Ehcache ehcache : createDefaultCacheDecorators(clonedDefaultCache)) {
986             addOrReplaceDecoratedCache(clonedDefaultCache, ehcache);
987         }
988     }
989 
990     /***
991      * Adds a {@link Cache} to the CacheManager.
992      * <p/>
993      * Memory and Disk stores will be configured for it and it will be added to the map of caches. Also notifies the
994      * CacheManagerEventListener after the cache was initialised and added.
995      *
996      * @param cache
997      * @throws IllegalStateException
998      *             if the cache is not {@link Status#STATUS_UNINITIALISED} before this method is called.
999      * @throws ObjectExistsException
1000      *             if the cache already exists in the CacheManager
1001      * @throws CacheException
1002      *             if there was an error adding the cache to the CacheManager
1003      */
1004     public void addCache(Cache cache) throws IllegalStateException, ObjectExistsException, CacheException {
1005         checkStatus();
1006         if (cache == null) {
1007             return;
1008         }
1009         addCache((Ehcache) cache);
1010     }
1011 
1012     /***
1013      * Adds an {@link Ehcache} to the CacheManager.
1014      * <p/>
1015      * Memory and Disk stores will be configured for it and it will be added to the map of caches. Also notifies the
1016      * CacheManagerEventListener after the cache was initialised and added.
1017      *
1018      * @param cache
1019      * @throws IllegalStateException
1020      *             if the cache is not {@link Status#STATUS_UNINITIALISED} before this method is called.
1021      * @throws ObjectExistsException
1022      *             if the cache already exists in the CacheManager
1023      * @throws CacheException
1024      *             if there was an error adding the cache to the CacheManager
1025      */
1026     public void addCache(Ehcache cache) throws IllegalStateException, ObjectExistsException, CacheException {
1027         checkStatus();
1028         if (cache == null) {
1029             return;
1030         }
1031         addCacheNoCheck(cache, true);
1032     }
1033 
1034     /***
1035      * Adds a decorated {@link Ehcache} to the CacheManager. This method neither creates the memory/disk store
1036      * nor initializes the cache. It only adds the cache reference to the map of caches held by this
1037      * cacheManager.
1038      * <p/>
1039      * It is generally required that a decorated cache, once constructed, is made available to other execution threads. The simplest way of
1040      * doing this is to either add it to the cacheManager with a different name or substitute the original cache with the decorated one.
1041      * <p/>
1042      * This method adds the decorated cache assuming it has a different name. If another cache (decorated or not) with the same name already
1043      * exists, it will throw {@link ObjectExistsException}. For replacing existing cache with another decorated cache having same name,
1044      * please use {@link #replaceCacheWithDecoratedCache(Ehcache, Ehcache)}
1045      * <p/>
1046      * Note that any overridden Ehcache methods by the decorator will take on new behaviours without casting. Casting is only required for
1047      * new methods that the decorator introduces. For more information see the well known Gang of Four Decorator pattern.
1048      *
1049      * @param decoratedCache
1050      * @throws ObjectExistsException
1051      *             if another cache with the same name already exists.
1052      */
1053     public void addDecoratedCache(Ehcache decoratedCache) throws ObjectExistsException {
1054         internalAddDecoratedCache(decoratedCache, true);
1055     }
1056 
1057     /***
1058      * Same as {@link #addDecoratedCache(Ehcache)} but does not throw exception if another cache with same name already exists.
1059      *
1060      * @param decoratedCache
1061      * @throws ObjectExistsException
1062      */
1063     public void addDecoratedCacheIfAbsent(Ehcache decoratedCache) throws ObjectExistsException {
1064         internalAddDecoratedCache(decoratedCache, false);
1065     }
1066 
1067     private void internalAddDecoratedCache(final Ehcache decoratedCache, final boolean strict) {
1068         Ehcache old = ehcaches.putIfAbsent(decoratedCache.getName(), decoratedCache);
1069         if (strict && old != null) {
1070             throw new ObjectExistsException("Cache " + decoratedCache.getName() + " already exists in the CacheManager");
1071         }
1072     }
1073 
1074     private void configCachePools(CacheConfiguration cacheConfiguration) {
1075 
1076         long cacheAssignedMem;
1077         if (cacheConfiguration.getMaxBytesLocalHeapPercentage() != null) {
1078             cacheAssignedMem = configuration.getMaxBytesLocalHeap() * cacheConfiguration.getMaxBytesLocalHeapPercentage() / HUNDRED;
1079             cacheConfiguration.setMaxBytesLocalHeap(cacheAssignedMem);
1080         }
1081 
1082         if (cacheConfiguration.getMaxBytesLocalOffHeapPercentage() != null) {
1083             cacheAssignedMem = configuration.getMaxBytesLocalOffHeap() * cacheConfiguration.getMaxBytesLocalOffHeapPercentage() / HUNDRED;
1084             cacheConfiguration.setMaxBytesLocalOffHeap(cacheAssignedMem);
1085         }
1086 
1087         if (cacheConfiguration.getMaxBytesLocalDiskPercentage() != null) {
1088             cacheAssignedMem = configuration.getMaxBytesLocalDisk() * cacheConfiguration.getMaxBytesLocalDiskPercentage() / HUNDRED;
1089             cacheConfiguration.setMaxBytesLocalDisk(cacheAssignedMem);
1090         }
1091 
1092     }
1093 
1094     private void validatePoolConfig(CacheConfiguration cacheConfiguration) {
1095 
1096         if (configuration.isMaxBytesLocalHeapSet() && Runtime.getRuntime().maxMemory() - configuration.getMaxBytesLocalHeap() < 0) {
1097             throw new InvalidConfigurationException("You've assigned more memory to the on-heap than the VM can sustain, " +
1098                                                     "please adjust your -Xmx setting accordingly");
1099         }
1100 
1101         // todo Verify that these are the real constraints ?
1102         if (cacheConfiguration.isMaxBytesLocalHeapPercentageSet() && !configuration.isMaxBytesLocalHeapSet()) {
1103             throw new InvalidConfigurationException("Cache '" + cacheConfiguration.getName() +
1104                                                     "' defines a percentage maxBytesOnHeap value but no CacheManager " +
1105                                                     "wide value was configured");
1106         }
1107         if (cacheConfiguration.isMaxBytesLocalOffHeapPercentageSet() && !configuration.isMaxBytesLocalOffHeapSet()) {
1108             throw new InvalidConfigurationException("Cache '" + cacheConfiguration.getName() +
1109                                                     "' defines a percentage maxBytesOffHeap value but no CacheManager " +
1110                                                     "wide value was configured");
1111         }
1112         if (cacheConfiguration.isMaxBytesLocalDiskPercentageSet() && !configuration.isMaxBytesLocalDiskSet()) {
1113             throw new InvalidConfigurationException("Cache '" + cacheConfiguration.getName() +
1114                                                     "' defines a percentage maxBytesOnDisk value but no CacheManager " +
1115                                                     "wide value was configured");
1116         }
1117     }
1118 
1119     private Ehcache addCacheNoCheck(final Ehcache cache, final boolean strict) throws IllegalStateException, ObjectExistsException,
1120             CacheException {
1121 
1122         if (isTerracottaRejoinEnabled()) {
1123             validateCacheConfigs(Collections.singletonList(cache.getCacheConfiguration()));
1124         }
1125 
1126         if (cache.getStatus() != Status.STATUS_UNINITIALISED) {
1127             throw new CacheException("Trying to add an already initialized cache." + " If you are adding a decorated cache, "
1128                     + "use CacheManager.addDecoratedCache" + "(Ehcache decoratedCache) instead.");
1129         }
1130 
1131         validatePoolConfig(cache.getCacheConfiguration());
1132 
1133         Ehcache ehcache = ehcaches.get(cache.getName());
1134         if (ehcache != null) {
1135             if (strict) {
1136                 throw new ObjectExistsException("Cache " + cache.getName() + " already exists");
1137             } else {
1138                 return ehcache;
1139             }
1140         }
1141         configCachePools(cache.getCacheConfiguration());
1142         verifyPoolAllocationsBeforeAdding(cache.getCacheConfiguration());
1143         if (configuration.isMaxBytesLocalHeapSet()) {
1144             onHeapPool.setMaxSize(onHeapPool.getMaxSize() - cache.getCacheConfiguration().getMaxBytesLocalHeap());
1145         }
1146         if (configuration.isMaxBytesLocalDiskSet()) {
1147             onDiskPool.setMaxSize(onDiskPool.getMaxSize() - cache.getCacheConfiguration().getMaxBytesLocalDisk());
1148         }
1149 
1150         cache.setCacheManager(this);
1151         if (cache.getCacheConfiguration().getDiskStorePath() == null) {
1152             cache.setDiskStorePath(diskStorePath);
1153         }
1154         cache.setTransactionManagerLookup(transactionManagerLookup);
1155 
1156         Map<String, CacheConfiguration> configMap = configuration.getCacheConfigurations();
1157         if (!configMap.containsKey(cache.getName())) {
1158             CacheConfiguration cacheConfig = cache.getCacheConfiguration();
1159             if (cacheConfig != null) {
1160                 configuration.addCache(cacheConfig);
1161             }
1162         }
1163 
1164         if (isTerracottaRejoinEnabled() && cache.getCacheConfiguration().isTerracottaClustered()) {
1165             final long timeoutMillis = cache.getCacheConfiguration().getTerracottaConfiguration().getNonstopConfiguration()
1166                     .getTimeoutMillis();
1167             try {
1168                 getNonstopExecutorService().execute(new Callable<Void>() {
1169                     public Void call() throws Exception {
1170                         cache.initialise();
1171                         return null;
1172                     }
1173                 }, timeoutMillis);
1174             } catch (TimeoutException e) {
1175                 throw new NonStopCacheException("Unable to add cache [" + cache.getCacheConfiguration().getName() + "] within "
1176                         + timeoutMillis + " msecs", e);
1177             } catch (InterruptedException e) {
1178                 throw new CacheException(e);
1179             }
1180         } else {
1181             cache.initialise();
1182         }
1183 
1184         if (!allowsDynamicCacheConfig) {
1185             cache.disableDynamicFeatures();
1186         }
1187 
1188         try {
1189             cache.bootstrap();
1190         } catch (CacheException e) {
1191             LOG.warn("Cache " + cache.getName() + "requested bootstrap but a CacheException occured. " + e.getMessage(), e);
1192         }
1193         ehcache = ehcaches.putIfAbsent(cache.getName(), cache);
1194         if (ehcache != null) {
1195             if (strict) {
1196                 throw new ObjectExistsException("Cache " + cache.getName() + " already exists");
1197             } else {
1198                 return ehcache;
1199             }
1200         }
1201 
1202         // Don't notify initial config. The init method of each listener should take care of this.
1203         if (status.equals(Status.STATUS_ALIVE)) {
1204             cacheManagerEventListenerRegistry.notifyCacheAdded(cache.getName());
1205         }
1206 
1207         return cache;
1208     }
1209 
1210     private void verifyPoolAllocationsBeforeAdding(final CacheConfiguration cacheConfiguration) {
1211         long totalOnHeapAssignedMemory  = 0;
1212         long totalOffHeapAssignedMemory = 0;
1213         long totalOnDiskAssignedMemory  = 0;
1214 
1215         for (Ehcache ehcache : ehcaches.values()) {
1216             totalOnHeapAssignedMemory += ehcache.getCacheConfiguration().getMaxBytesLocalHeap();
1217             totalOffHeapAssignedMemory += ehcache.getCacheConfiguration().getMaxBytesLocalOffHeap();
1218             totalOnDiskAssignedMemory += ehcache.getCacheConfiguration().getMaxBytesLocalDisk();
1219         }
1220 
1221         totalOnHeapAssignedMemory += cacheConfiguration.getMaxBytesLocalHeap();
1222         totalOffHeapAssignedMemory += cacheConfiguration.getMaxBytesLocalOffHeap();
1223         totalOnDiskAssignedMemory += cacheConfiguration.getMaxBytesLocalDisk();
1224 
1225         if (configuration.isMaxBytesLocalHeapSet() && configuration.getMaxBytesLocalHeap() - totalOnHeapAssignedMemory < 0) {
1226             throw new InvalidConfigurationException("Adding cache '" + cacheConfiguration.getName()
1227                                                     + "' to the CacheManager over-allocates onHeap memory!");
1228         }
1229         if (configuration.isMaxBytesLocalOffHeapSet() && configuration.getMaxBytesLocalOffHeap() - totalOffHeapAssignedMemory < 0) {
1230             throw new InvalidConfigurationException("Adding cache '" + cacheConfiguration.getName()
1231                                                     + "' to the CacheManager over-allocates offHeap memory!");
1232         }
1233         if (configuration.isMaxBytesLocalDiskSet() && configuration.getMaxBytesLocalDisk() - totalOnDiskAssignedMemory < 0) {
1234             throw new InvalidConfigurationException("Adding cache '" + cacheConfiguration.getName()
1235                                                     + "' to the CacheManager over-allocates onDisk space!");
1236         }
1237 
1238         if (configuration.isMaxBytesLocalHeapSet() && configuration.getMaxBytesLocalHeap() - totalOnHeapAssignedMemory == 0) {
1239             LOG.warn("All the onHeap memory has been assigned, there is none left for dynamically added caches");
1240         }
1241 
1242         if (Runtime.getRuntime().maxMemory() - totalOnHeapAssignedMemory < 0) {
1243             // todo this could be a nicer message (with actual values)
1244             throw new InvalidConfigurationException("You've assigned more memory to the on-heap than the VM can sustain, " +
1245                                                     "please adjust your -Xmx setting accordingly");
1246         }
1247 
1248         if (totalOnHeapAssignedMemory / (float) Runtime.getRuntime().maxMemory() > ON_HEAP_THRESHOLD) {
1249             LOG.warn("You've assigned over 80% of your VM's heap to be used by the cache!");
1250         }
1251     }
1252 
1253     /***
1254      * Checks whether a cache of type ehcache exists.
1255      * <p/>
1256      *
1257      * @param cacheName
1258      *            the cache name to check for
1259      * @return true if it exists
1260      * @throws IllegalStateException
1261      *             if the cache is not {@link Status#STATUS_ALIVE}
1262      */
1263     public boolean cacheExists(String cacheName) throws IllegalStateException {
1264         checkStatus();
1265         return (ehcaches.get(cacheName) != null);
1266     }
1267 
1268     /***
1269      * Removes all caches using {@link #removeCache(String)} for each cache.
1270      */
1271     public void removalAll() {
1272         String[] cacheNames = getCacheNames();
1273         for (String cacheName : cacheNames) {
1274             removeCache(cacheName);
1275         }
1276     }
1277 
1278     /***
1279      * Remove a cache from the CacheManager. The cache is disposed of.
1280      *
1281      * @param cacheName
1282      *            the cache name
1283      * @throws IllegalStateException
1284      *             if the cache is not {@link Status#STATUS_ALIVE}
1285      */
1286     public void removeCache(String cacheName) throws IllegalStateException {
1287         checkStatus();
1288 
1289         // NPE guard
1290         if (cacheName == null || cacheName.length() == 0) {
1291             return;
1292         }
1293         Ehcache cache = ehcaches.remove(cacheName);
1294         if (cache != null && cache.getStatus().equals(Status.STATUS_ALIVE)) {
1295             cache.dispose();
1296             if (configuration.isMaxBytesLocalHeapSet()) {
1297                 onHeapPool.setMaxSize(onHeapPool.getMaxSize() + cache.getCacheConfiguration().getMaxBytesLocalHeap());
1298             }
1299             if (configuration.isMaxBytesLocalDiskSet()) {
1300                 onDiskPool.setMaxSize(onDiskPool.getMaxSize() + cache.getCacheConfiguration().getMaxBytesLocalDisk());
1301             }
1302             configuration.getCacheConfigurations().remove(cacheName);
1303             cacheManagerEventListenerRegistry.notifyCacheRemoved(cache.getName());
1304         }
1305     }
1306 
1307     /***
1308      * Shuts down the CacheManager.
1309      * <p/>
1310      * If the shutdown occurs on the singleton, then the singleton is removed, so that if a singleton access method is called, a new
1311      * singleton will be created.
1312      * <p/>
1313      * By default there is no shutdown hook (ehcache-1.3-beta2 and higher).
1314      * <p/>
1315      * Set the system property net.sf.ehcache.enableShutdownHook=true to turn it on.
1316      */
1317     public void shutdown() {
1318         synchronized (CacheManager.class) {
1319             if (status.equals(Status.STATUS_SHUTDOWN)) {
1320                 LOG.debug("CacheManager already shutdown");
1321                 return;
1322             }
1323             for (CacheManagerPeerProvider cacheManagerPeerProvider : cacheManagerPeerProviders.values()) {
1324                 if (cacheManagerPeerProvider != null) {
1325                     cacheManagerPeerProvider.dispose();
1326                 }
1327             }
1328 
1329             // cancel the cacheManager timer and all tasks
1330             if (cacheManagerTimer != null) {
1331                 cacheManagerTimer.cancel();
1332                 cacheManagerTimer.purge();
1333             }
1334 
1335             cacheManagerEventListenerRegistry.dispose();
1336 
1337             synchronized (CacheManager.class) {
1338                 ALL_CACHE_MANAGERS.remove(this);
1339 
1340                 for (Ehcache cache : ehcaches.values()) {
1341                     if (cache != null) {
1342                         cache.dispose();
1343                     }
1344                 }
1345                 if (defaultCache != null) {
1346                     defaultCache.dispose();
1347                 }
1348                 status = Status.STATUS_SHUTDOWN;
1349                 XARequestProcessor.shutdown();
1350 
1351                 // only delete singleton if the singleton is shutting down.
1352                 if (this == singleton) {
1353                     singleton = null;
1354                 }
1355                 terracottaClient.shutdown();
1356                 transactionController = null;
1357                 removeShutdownHook();
1358                 nonstopExecutorServiceFactory.shutdown(this);
1359             }
1360         }
1361     }
1362 
1363     /***
1364      * Returns a list of the current cache names.
1365      *
1366      * @return an array of {@link String}s
1367      * @throws IllegalStateException
1368      *             if the cache is not {@link Status#STATUS_ALIVE}
1369      */
1370     public String[] getCacheNames() throws IllegalStateException {
1371         checkStatus();
1372         String[] list = new String[ehcaches.size()];
1373         return ehcaches.keySet().toArray(list);
1374     }
1375 
1376     /***
1377      * Checks the state of the CacheManager for legal operation
1378      */
1379     protected void checkStatus() {
1380         if (!(status.equals(Status.STATUS_ALIVE))) {
1381             if (status.equals(Status.STATUS_UNINITIALISED)) {
1382                 throw new IllegalStateException("The CacheManager has not yet been initialised. It cannot be used yet.");
1383             } else if (status.equals(Status.STATUS_SHUTDOWN)) {
1384                 throw new IllegalStateException("The CacheManager has been shut down. It can no longer be used.");
1385             }
1386         }
1387     }
1388 
1389     /***
1390      * Gets the status attribute of the Ehcache
1391      *
1392      * @return The status value from the Status enum class
1393      */
1394     public Status getStatus() {
1395         return status;
1396     }
1397 
1398     /***
1399      * Clears the contents of all caches in the CacheManager, but without
1400      * removing any caches.
1401      * <p/>
1402      * This method is not synchronized. It only guarantees to clear those elements in a cache at the time that the
1403      * {@link Ehcache#removeAll()} mehod on each cache is called.
1404      */
1405     public void clearAll() throws CacheException {
1406         String[] cacheNames = getCacheNames();
1407 
1408         LOG.debug("Clearing all caches");
1409         for (String cacheName : cacheNames) {
1410             Ehcache cache = getEhcache(cacheName);
1411             cache.removeAll();
1412         }
1413     }
1414 
1415     /***
1416      * Clears the contents of all caches in the CacheManager with a name starting with the prefix,
1417      * but without removing them.
1418      * <p/>
1419      * This method is not synchronized. It only guarantees to clear those elements in a cache at the time that the
1420      * {@link Ehcache#removeAll()} method on each cache is called.
1421      *
1422      * @param prefix
1423      *            The prefix the cache name should start with
1424      * @throws CacheException
1425      * @since 1.7.2
1426      */
1427     public void clearAllStartingWith(String prefix) throws CacheException {
1428         // NPE guard
1429         if (prefix == null || prefix.length() == 0) {
1430             return;
1431         }
1432 
1433         for (Object o : ehcaches.entrySet()) {
1434             Map.Entry entry = (Map.Entry) o;
1435             String cacheName = (String) entry.getKey();
1436             if (cacheName.startsWith(prefix)) {
1437                 if (LOG.isDebugEnabled()) {
1438                     LOG.debug("Clearing cache named '" + cacheName + "' (matches '" + prefix + "' prefix");
1439                 }
1440                 ((Ehcache) entry.getValue()).removeAll();
1441             }
1442         }
1443     }
1444 
1445     /***
1446      * Gets the <code>CacheManagerPeerProvider</code>, matching the given scheme
1447      * For distributed caches, the peer provider finds other cache managers and their caches in the same cluster
1448      *
1449      * @param scheme
1450      *            the replication scheme to use. Schemes shipped with ehcache are RMI, JGROUPS, JMS
1451      * @return the provider, or null if one does not exist
1452      */
1453     public CacheManagerPeerProvider getCacheManagerPeerProvider(String scheme) {
1454         return cacheManagerPeerProviders.get(scheme);
1455     }
1456 
1457     /***
1458      * @return Read-only map of the registered {@link CacheManagerPeerProvider}s keyed by scheme.
1459      */
1460     public Map<String, CacheManagerPeerProvider> getCacheManagerPeerProviders() {
1461         return Collections.unmodifiableMap(this.cacheManagerPeerProviders);
1462     }
1463 
1464     /***
1465      * When CacheManage is configured as part of a cluster, a CacheManagerPeerListener will
1466      * be registered in it. Use this to access the individual cache listeners
1467      *
1468      * @param scheme
1469      *            the replication scheme to use. Schemes shipped with ehcache are RMI, JGROUPS, JMS
1470      * @return the listener, or null if one does not exist
1471      */
1472     public CacheManagerPeerListener getCachePeerListener(String scheme) {
1473         return cacheManagerPeerListeners.get(scheme);
1474     }
1475 
1476     /***
1477      * Returns the composite listener. A notification sent to this listener will notify all registered
1478      * listeners.
1479      *
1480      * @return null if none
1481      * @see "getCacheManagerEventListenerRegistry"
1482      */
1483     public CacheManagerEventListener getCacheManagerEventListener() {
1484         return cacheManagerEventListenerRegistry;
1485     }
1486 
1487     /***
1488      * Same as getCacheManagerEventListenerRegistry().registerListener(cacheManagerEventListener);
1489      * Left for backward compatiblity
1490      *
1491      * @param cacheManagerEventListener
1492      *            the listener to set.
1493      * @see "getCacheManagerEventListenerRegistry"
1494      */
1495     public void setCacheManagerEventListener(CacheManagerEventListener cacheManagerEventListener) {
1496         getCacheManagerEventListenerRegistry().registerListener(cacheManagerEventListener);
1497     }
1498 
1499     /***
1500      * Gets the CacheManagerEventListenerRegistry. Add and remove listeners here.
1501      */
1502     public CacheManagerEventListenerRegistry getCacheManagerEventListenerRegistry() {
1503         return cacheManagerEventListenerRegistry;
1504     }
1505 
1506     /***
1507      * Replaces in the map of Caches managed by this CacheManager an Ehcache with a decorated version of the same
1508      * Ehcache. CacheManager can operate fully with a decorated Ehcache.
1509      * <p/>
1510      * Ehcache Decorators can be used to obtain different behaviour from an Ehcache in a very flexible way. Some examples in ehcache are:
1511      * <ol>
1512      * <li>{@link net.sf.ehcache.constructs.blocking.BlockingCache} - A cache that blocks other threads from getting a null element until
1513      * the first thread has placed a value in it.
1514      * <li>{@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache} - A BlockingCache that has the additional property of knowing how
1515      * to load its own entries.
1516      * </ol>
1517      * Many other kinds are possible.
1518      * <p/>
1519      * It is generally required that a decorated cache, once constructed, is made available to other execution threads. The simplest way of
1520      * doing this is to substitute the original cache for the decorated one here.
1521      * <p/>
1522      * Note that any overwritten Ehcache methods will take on new behaviours without casting. Casting is only required for new methods that
1523      * the decorator introduces. For more information see the well known Gang of Four Decorator pattern.
1524      *
1525      * @param ehcache
1526      * @param decoratedCache
1527      *            An implementation of Ehcache that wraps the original cache.
1528      * @throws CacheException
1529      *             if the two caches do not equal each other.
1530      */
1531     public void replaceCacheWithDecoratedCache(Ehcache ehcache, Ehcache decoratedCache) throws CacheException {
1532         if (!ehcache.equals(decoratedCache)) {
1533             throw new CacheException("Cannot replace " + decoratedCache.getName() + " It does not equal the incumbent cache.");
1534         }
1535 
1536         String cacheName = ehcache.getName();
1537         if (!ehcaches.replace(cacheName, ehcache, decoratedCache)) {
1538             if (cacheExists(cacheName)) {
1539                 throw new CacheException("Cache '" + ehcache.getName() + "' managed with this CacheManager doesn't match!");
1540             } else {
1541                 throw new CacheException("Cache '" + cacheName + "' isn't associated with this manager (anymore?)");
1542             }
1543         }
1544     }
1545 
1546     /***
1547      * Gets the name of the CacheManager. This is useful for distinguishing multiple CacheManagers
1548      *
1549      * @return the name, or the output of toString() if it is not set.
1550      * @see #toString() which uses either the name or Object.toString()
1551      */
1552     public String getName() {
1553         if (name != null) {
1554             return name;
1555         } else {
1556             return super.toString();
1557         }
1558     }
1559 
1560     /***
1561      * Indicate whether the CacheManager is named or not.
1562      *
1563      * @return True if named
1564      */
1565     public boolean isNamed() {
1566         return name != null;
1567     }
1568 
1569     /***
1570      * Sets the name of the CacheManager. This is useful for distinguishing multiple CacheManagers
1571      * in a monitoring situation.
1572      *
1573      * @param name
1574      *            a name with characters legal in a JMX ObjectName
1575      */
1576     public void setName(String name) {
1577         this.name = name;
1578         try {
1579             mbeanRegistrationProvider.reinitialize(terracottaClient.getClusteredInstanceFactory());
1580         } catch (MBeanRegistrationProviderException e) {
1581             throw new CacheException("Problem in reinitializing MBeanRegistrationProvider - "
1582                     + mbeanRegistrationProvider.getClass().getName(), e);
1583         }
1584     }
1585 
1586     /***
1587      * @return either the name of this CacheManager, or if unset, Object.toString()
1588      */
1589     @Override
1590     public String toString() {
1591         return getName();
1592     }
1593 
1594     /***
1595      * Returns the disk store path. This may be null if no caches need a DiskStore and none was configured.
1596      * The path cannot be changed after creation of the CacheManager. All caches take the disk store path
1597      * from this value.
1598      *
1599      * @return the disk store path.
1600      */
1601     public String getDiskStorePath() {
1602         return diskStorePath;
1603     }
1604 
1605     /***
1606      * Returns a {@link FailSafeTimer} associated with this {@link CacheManager}
1607      *
1608      * @return The {@link FailSafeTimer} associated with this cache manager
1609      * @since 1.7
1610      */
1611     public FailSafeTimer getTimer() {
1612         return cacheManagerTimer;
1613     }
1614 
1615     /***
1616      * Returns access to information about the cache cluster.
1617      *
1618      * @param scheme The clustering scheme to retrieve information about (such as "Terracotta")
1619      * @return Cluster API (never null, but possibly a simple single node implementation)
1620      * @throws ClusterSchemeNotAvailableException If the CacheCluster specified by scheme is not available.
1621      * @see ClusterScheme
1622      * @since 2.0
1623      */
1624     public CacheCluster getCluster(ClusterScheme scheme) throws ClusterSchemeNotAvailableException {
1625         switch (scheme) {
1626             case TERRACOTTA:
1627                 if (null == terracottaClient.getClusteredInstanceFactory()) {
1628                     throw new ClusterSchemeNotAvailableException(ClusterScheme.TERRACOTTA, "Terracotta cluster scheme is not available");
1629                 }
1630                 return terracottaClient.getCacheCluster();
1631             default:
1632                 return NoopCacheCluster.INSTANCE;
1633         }
1634     }
1635 
1636     /***
1637      * Returns the original configuration text for this {@link CacheManager}
1638      *
1639      * @return Returns the original configuration text for this {@link CacheManager}
1640      */
1641     public String getOriginalConfigurationText() {
1642         if (configuration.getConfigurationSource() == null) {
1643             return "Originally configured programmatically. No original configuration source text.";
1644         } else {
1645             Configuration originalConfiguration = configuration.getConfigurationSource().createConfiguration();
1646             return ConfigurationUtil.generateCacheManagerConfigurationText(originalConfiguration);
1647         }
1648     }
1649 
1650     /***
1651      * Returns the active configuration text for this {@link CacheManager}
1652      *
1653      * @return Returns the active configuration text for this {@link CacheManager}
1654      */
1655     public String getActiveConfigurationText() {
1656         return ConfigurationUtil.generateCacheManagerConfigurationText(configuration);
1657     }
1658 
1659     /***
1660      * Returns the original configuration text for the input cacheName
1661      *
1662      * @param cacheName
1663      * @return Returns the original configuration text for the input cacheName
1664      * @throws CacheException if the cache with <code>cacheName</code> does not exist in the original config
1665      */
1666     public String getOriginalConfigurationText(String cacheName) throws CacheException {
1667         if (configuration.getConfigurationSource() == null) {
1668             return "Originally configured programmatically. No original configuration source text.";
1669         } else {
1670             Configuration originalConfiguration = configuration.getConfigurationSource().createConfiguration();
1671             CacheConfiguration cacheConfiguration = originalConfiguration.getCacheConfigurations().get(cacheName);
1672             if (cacheConfiguration == null) {
1673                 throw new CacheException("Cache with name '" + cacheName + "' does not exist in the original configuration");
1674             }
1675             return ConfigurationUtil.generateCacheConfigurationText(cacheConfiguration);
1676         }
1677     }
1678 
1679     /***
1680      * Returns the active configuration text for the input cacheName
1681      *
1682      * @param cacheName
1683      * @return Returns the active configuration text for the input cacheName
1684      * @throws CacheException if the cache with <code>cacheName</code> does not exist
1685      */
1686     public String getActiveConfigurationText(String cacheName) throws CacheException {
1687         CacheConfiguration config = configuration.getCacheConfigurations().get(cacheName);
1688         if (config == null) {
1689             throw new CacheException("Cache with name '" + cacheName + "' does not exist");
1690         }
1691         return ConfigurationUtil.generateCacheConfigurationText(config);
1692     }
1693 
1694     /***
1695      * Get the CacheManager configuration
1696      *
1697      * @return the configuration
1698      */
1699     public Configuration getConfiguration() {
1700         return configuration;
1701     }
1702 
1703     /***
1704      * {@inheritDoc}
1705      */
1706     @Override
1707     public int hashCode() {
1708         if (name != null) {
1709             return name.hashCode();
1710         } else {
1711             return super.hashCode();
1712         }
1713     }
1714 
1715     /***
1716      * Only adds the cache to the CacheManager should not one with the same name already be present
1717      *
1718      * @param cache The Ehcache to be added
1719      * @return the instance registered with the CacheManager, the cache instance passed in if it was added; or null if Ehcache is null
1720      */
1721     public Ehcache addCacheIfAbsent(final Ehcache cache) {
1722         checkStatus();
1723         return cache == null ? null : addCacheNoCheck(cache, false);
1724     }
1725 
1726     /***
1727      * Only creates and adds the cache to the CacheManager should not one with the same name already be present
1728      *
1729      * @param cacheName the name of the Cache to be created
1730      * @return the Ehcache instance created and registered; null if cacheName was null or of length 0
1731      */
1732     public Ehcache addCacheIfAbsent(final String cacheName) {
1733         checkStatus();
1734 
1735         // NPE guard
1736         if (cacheName == null || cacheName.length() == 0) {
1737             return null;
1738         }
1739 
1740         Ehcache ehcache = ehcaches.get(cacheName);
1741         if (ehcache == null) {
1742             Ehcache clonedDefaultCache = cloneDefaultCache(cacheName);
1743             if (clonedDefaultCache == null) {
1744                 throw new CacheException(NO_DEFAULT_CACHE_ERROR_MSG);
1745             }
1746             addCacheIfAbsent(clonedDefaultCache);
1747             for (Ehcache createdCache : createDefaultCacheDecorators(clonedDefaultCache)) {
1748                 addOrReplaceDecoratedCache(clonedDefaultCache, createdCache);
1749             }
1750         }
1751         return ehcaches.get(cacheName);
1752     }
1753 
1754     private Ehcache cloneDefaultCache(final String cacheName) {
1755         if (defaultCache == null) {
1756             return null;
1757         }
1758         Ehcache cache;
1759         try {
1760             cache = (Ehcache) defaultCache.clone();
1761         } catch (CloneNotSupportedException e) {
1762             throw new CacheException("Failure cloning default cache. Initial cause was " + e.getMessage(), e);
1763         }
1764         if (cache != null) {
1765             cache.setName(cacheName);
1766         }
1767         return cache;
1768     }
1769 
1770     private List<Ehcache> createDefaultCacheDecorators(Ehcache underlyingCache) {
1771         return ConfigurationHelper.createDefaultCacheDecorators(underlyingCache, configuration.getDefaultCacheConfiguration());
1772     }
1773 
1774     /***
1775      * Get the TransactionController
1776      *
1777      * @return the TransactionController
1778      */
1779     public TransactionController getTransactionController() {
1780         return transactionController;
1781     }
1782 
1783     /***
1784      * Create a TransactionIDFactory
1785      *
1786      * @return a TransactionIDFactory
1787      */
1788     TransactionIDFactory createTransactionIDFactory() {
1789         TransactionIDFactory transactionIDFactory;
1790         if (terracottaClient.getClusteredInstanceFactory() != null) {
1791             transactionIDFactory = terracottaClient.getClusteredInstanceFactory().createTransactionIDFactory(getClusterUUID());
1792         } else {
1793             transactionIDFactory = new TransactionIDFactoryImpl();
1794         }
1795         return transactionIDFactory;
1796     }
1797 
1798     /***
1799      * Create a soft lock factory for a specific cache
1800      *
1801      * @param cache the cache to create the soft lock factory for
1802      * @return a SoftLockFactory
1803      */
1804     SoftLockFactory createSoftLockFactory(Ehcache cache) {
1805         SoftLockFactory softLockFactory;
1806         if (cache.getCacheConfiguration().isTerracottaClustered()) {
1807             softLockFactory = getClusteredInstanceFactory(cache).getOrCreateSoftLockFactory(cache.getName());
1808         } else {
1809             softLockFactory = softLockFactories.get(cache.getName());
1810             if (softLockFactory == null) {
1811                 softLockFactory = new ReadCommittedSoftLockFactoryImpl(getName(), cache.getName());
1812                 SoftLockFactory old = softLockFactories.putIfAbsent(cache.getName(), softLockFactory);
1813                 if (old != null) {
1814                     softLockFactory = old;
1815                 }
1816             }
1817         }
1818         return softLockFactory;
1819     }
1820 
1821     /***
1822      * Get the SoftLockFactory of a cache
1823      *
1824      * @param cacheName the cache name
1825      * @return the SoftLockFactory or null if there was no soft lock factory created for the specified cache
1826      */
1827     public SoftLockFactory getSoftLockFactory(String cacheName) {
1828         return softLockFactories.get(cacheName);
1829     }
1830 
1831 
1832     private void clusterRejoinStarted() {
1833         for (Ehcache cache : ehcaches.values()) {
1834             if (cache instanceof Cache) {
1835                 if (cache.getCacheConfiguration().isTerracottaClustered()) {
1836                     ((Cache) cache).clusterRejoinStarted();
1837                 }
1838             }
1839         }
1840         // shutdown the current nonstop executor service
1841         nonstopExecutorServiceFactory.shutdown(this);
1842     }
1843 
1844     /***
1845      * This method is called when the Terracotta Cluster is rejoined. Reinitializes all terracotta clustered caches in this cache manager
1846      */
1847     private void clusterRejoinComplete() {
1848         // restart nonstop executor service
1849         nonstopExecutorServiceFactory.getOrCreateNonstopExecutorService(this);
1850         for (Ehcache cache : ehcaches.values()) {
1851             if (cache instanceof Cache) {
1852                 if (cache.getCacheConfiguration().isTerracottaClustered()) {
1853                     ((Cache) cache).clusterRejoinComplete();
1854                 }
1855             }
1856         }
1857         if (mbeanRegistrationProvider.isInitialized()) {
1858             // re-register mbeans
1859             try {
1860                 mbeanRegistrationProvider.reinitialize(terracottaClient.getClusteredInstanceFactory());
1861             } catch (MBeanRegistrationProviderException e) {
1862                 throw new CacheException("Problem in reinitializing MBeanRegistrationProvider - "
1863                         + mbeanRegistrationProvider.getClass().getName(), e);
1864             }
1865         }
1866         // recreate TransactionController with fresh TransactionIDFactory
1867         transactionController = new TransactionController(createTransactionIDFactory(), configuration
1868                 .getDefaultTransactionTimeoutInSeconds());
1869     }
1870 
1871     /***
1872      * Creates a SizeOfEngine for a cache.
1873      * It will check for a System property on what class to instantiate.
1874      * @param cache The cache to be sized by the engine
1875      * @return a SizeOfEngine instance
1876      */
1877     SizeOfEngine createSizeOfEngine(final Cache cache) {
1878         String prop = "net.sf.ehcache.sizeofengine";
1879 
1880         if (isNamed()) {
1881             prop += "." + getName();
1882         } else {
1883           prop += ".default";
1884         }
1885 
1886         if (cache != null) {
1887             prop += "." + cache.getName();
1888         }
1889 
1890         String className = System.getProperty(prop);
1891 
1892         if (className != null) {
1893             try {
1894                 Class<? extends SizeOfEngine> aClass = (Class<? extends SizeOfEngine>) Class.forName(className);
1895                 return aClass.newInstance();
1896             } catch (Exception exception) {
1897                 throw new RuntimeException("Couldn't load and instantiate custom " +
1898                                            (cache != null ? "SizeOfEngine for cache '" + cache.getName() + "'" : "default SizeOfEngine"),
1899                     exception);
1900             }
1901         } else {
1902             return new DefaultSizeOfEngine();
1903         }
1904     }
1905 
1906     /***
1907      * Return the {@link NonstopExecutorService} associated with this cacheManager
1908      * @return the {@link NonstopExecutorService} associated with this cacheManager
1909      */
1910     protected NonstopExecutorService getNonstopExecutorService() {
1911         return nonstopExecutorServiceFactory.getOrCreateNonstopExecutorService(this);
1912     }
1913 }