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 net.sf.ehcache.cluster.CacheCluster;
19  import net.sf.ehcache.cluster.ClusterScheme;
20  import net.sf.ehcache.cluster.ClusterSchemeNotAvailableException;
21  import net.sf.ehcache.cluster.NoopCacheCluster;
22  import net.sf.ehcache.config.CacheConfiguration;
23  import net.sf.ehcache.config.Configuration;
24  import net.sf.ehcache.config.ConfigurationFactory;
25  import net.sf.ehcache.config.ConfigurationHelper;
26  import net.sf.ehcache.config.DiskStoreConfiguration;
27  import net.sf.ehcache.config.FactoryConfiguration;
28  import net.sf.ehcache.config.TerracottaClientConfiguration;
29  import net.sf.ehcache.config.generator.ConfigurationUtil;
30  import net.sf.ehcache.distribution.CacheManagerPeerListener;
31  import net.sf.ehcache.distribution.CacheManagerPeerProvider;
32  import net.sf.ehcache.event.CacheEventListener;
33  import net.sf.ehcache.event.CacheManagerEventListener;
34  import net.sf.ehcache.event.CacheManagerEventListenerRegistry;
35  import net.sf.ehcache.management.provider.MBeanRegistrationProvider;
36  import net.sf.ehcache.management.provider.MBeanRegistrationProviderException;
37  import net.sf.ehcache.management.provider.MBeanRegistrationProviderFactory;
38  import net.sf.ehcache.management.provider.MBeanRegistrationProviderFactoryImpl;
39  import net.sf.ehcache.store.DiskStore;
40  import net.sf.ehcache.store.Store;
41  import net.sf.ehcache.store.compound.impl.MemoryOnlyStore;
42  import net.sf.ehcache.terracotta.ClusteredInstanceFactory;
43  import net.sf.ehcache.transaction.manager.TransactionManagerLookup;
44  import net.sf.ehcache.transaction.xa.EhcacheXAStore;
45  import net.sf.ehcache.transaction.xa.EhcacheXAStoreImpl;
46  import net.sf.ehcache.util.FailSafeTimer;
47  import net.sf.ehcache.util.PropertyUtil;
48  import net.sf.ehcache.util.UpdateChecker;
49  import net.sf.ehcache.writer.writebehind.WriteBehind;
50  import org.slf4j.Logger;
51  import org.slf4j.LoggerFactory;
52  
53  import java.io.File;
54  import java.io.InputStream;
55  import java.lang.reflect.Method;
56  import java.net.URL;
57  import java.util.HashMap;
58  import java.util.Iterator;
59  import java.util.List;
60  import java.util.Map;
61  import java.util.Properties;
62  import java.util.Set;
63  import java.util.concurrent.ConcurrentHashMap;
64  import java.util.concurrent.ConcurrentMap;
65  import java.util.concurrent.CopyOnWriteArrayList;
66  import java.util.concurrent.atomic.AtomicBoolean;
67  
68  /***
69   * A container for {@link Ehcache}s that maintain all aspects of their lifecycle.
70   * <p/>
71   * CacheManager may be either be a singleton if created with factory methods, or multiple instances may exist, in which case resources
72   * required by each must be unique.
73   * <p/>
74   * A CacheManager holds references to Caches and Ehcaches and manages their creation and lifecycle.
75   *
76   * @author Greg Luck
77   * @version $Id: CacheManager.java 2552 2010-07-06 21:44:59Z gkeim $
78   */
79  public class CacheManager {
80  
81      /***
82       * Default name if not specified in the configuration/
83       */
84      public static final String DEFAULT_NAME = "__DEFAULT__";
85      
86      /***
87       * Keeps track of all known CacheManagers. Used to check on conflicts.
88       * CacheManagers should remove themselves from this list during shut down.
89       */
90      public static final List<CacheManager> ALL_CACHE_MANAGERS = new CopyOnWriteArrayList<CacheManager>();
91  
92      /***
93       * System property to enable creation of a shutdown hook for CacheManager.
94       */
95      public static final String ENABLE_SHUTDOWN_HOOK_PROPERTY = "net.sf.ehcache.enableShutdownHook";
96  
97      private static final Logger LOG = LoggerFactory.getLogger(CacheManager.class);
98  
99      /***
100      * Update check interval - one week in milliseconds
101      */
102     private static final long EVERY_WEEK = 7 * 24 * 60 * 60 * 1000;
103 
104     /***
105      * delay period before doing update check
106      */
107     private static final long DELAY_UPDATE_CHECK = 1000;
108 
109     /***
110      * The Singleton Instance.
111      */
112     private static volatile CacheManager singleton;
113 
114     /***
115      * The factory to use for creating MBeanRegistrationProvider's
116      */
117     private static MBeanRegistrationProviderFactory mBeanRegistrationProviderFactory = new MBeanRegistrationProviderFactoryImpl();
118 
119     /***
120      * A name for this CacheManager to distinguish it from others.
121      */
122     protected String name;
123 
124     /***
125      * Status of the Cache Manager
126      */
127     protected Status status;
128 
129     /***
130      * The map of providers
131      */
132     protected Map<String, CacheManagerPeerProvider> cacheManagerPeerProviders = new ConcurrentHashMap<String, CacheManagerPeerProvider>();
133 
134     /***
135      * The map of listeners
136      */
137     protected Map<String, CacheManagerPeerListener> cacheManagerPeerListeners = new ConcurrentHashMap<String, CacheManagerPeerListener>();
138 
139     /***
140      * The listener registry
141      */
142     protected CacheManagerEventListenerRegistry cacheManagerEventListenerRegistry = new CacheManagerEventListenerRegistry();
143 
144     /***
145      * The shutdown hook thread for CacheManager. This ensures that the CacheManager and Caches are left in a
146      * consistent state on a CTRL-C or kill.
147      * <p/>
148      * This thread must be unregistered as a shutdown hook, when the CacheManager is disposed. Otherwise the CacheManager is not GC-able.
149      * <p/>
150      * Of course kill -9 or abrupt termination will not run the shutdown hook. In this case, various sanity checks are made at start up.
151      */
152     protected Thread shutdownHook;
153 
154     /***
155      * Ehcaches managed by this manager.
156      */
157     private final ConcurrentMap<String, Ehcache> ehcaches = new ConcurrentHashMap<String, Ehcache>();
158 
159     /***
160      * Default cache cache.
161      */
162     private Ehcache defaultCache;
163 
164     /***
165      * The path for the directory in which disk caches are created.
166      */
167     private String diskStorePath;
168 
169     private MBeanRegistrationProvider mbeanRegistrationProvider;
170 
171     private FailSafeTimer cacheManagerTimer;
172 
173     /***
174      * Factory for creating terracotta clustered memory store (may be null if this manager has no terracotta caches)
175      */
176     private volatile ClusteredInstanceFactory terracottaClusteredInstanceFactory;
177 
178     /***
179      * The {@link TerracottaClientConfiguration} used for this {@link CacheManager}
180      */
181     private TerracottaClientConfiguration terracottaClientConfiguration;
182 
183     private AtomicBoolean terracottaClusteredInstanceFactoryCreated = new AtomicBoolean(false);
184 
185     private Configuration configuration;
186 
187     private volatile boolean allowsDynamicCacheConfig = true;
188 
189     private volatile TransactionManagerLookup transactionManagerLookup;
190 
191     /***
192      * An constructor for CacheManager, which takes a configuration object, rather than one created by parsing
193      * an ehcache.xml file. This constructor gives complete control over the creation of the CacheManager.
194      * <p/>
195      * Care should be taken to ensure that, if multiple CacheManages are created, they do now overwrite each others disk store files, as
196      * would happend if two were created which used the same diskStore path.
197      * <p/>
198      * This method does not act as a singleton. Callers must maintain their own reference to it.
199      * <p/>
200      * Note that if one of the {@link #create()} methods are called, a new singleton instance will be created, separate from any instances
201      * created in this method.
202      *
203      * @param configuration
204      * @throws CacheException
205      */
206     public CacheManager(Configuration configuration) throws CacheException {
207         status = Status.STATUS_UNINITIALISED;
208         init(configuration, null, null, null);
209     }
210 
211     /***
212      * An ordinary constructor for CacheManager.
213      * This method does not act as a singleton. Callers must maintain a reference to it.
214      * Note that if one of the {@link #create()} methods are called, a new singleton will be created,
215      * separate from any instances created in this method.
216      *
217      * @param configurationFileName
218      *            an xml configuration file available through a file name. The configuration {@link File} is created
219      *            using new <code>File(configurationFileName)</code>
220      * @throws CacheException
221      * @see #create(String)
222      */
223     public CacheManager(String configurationFileName) throws CacheException {
224         status = Status.STATUS_UNINITIALISED;
225         init(null, configurationFileName, null, null);
226     }
227 
228     /***
229      * An ordinary constructor for CacheManager.
230      * This method does not act as a singleton. Callers must maintain a reference to it.
231      * Note that if one of the {@link #create()} methods are called, a new singleton will be created,
232      * separate from any instances created in this method.
233      * <p/>
234      * This method can be used to specify a configuration resource in the classpath other than the default of \"/ehcache.xml\":
235      *
236      * <pre>
237      * URL url = this.getClass().getResource(&quot;/ehcache-2.xml&quot;);
238      * </pre>
239      *
240      * Note that {@link Class#getResource} will look for resources in the same package unless a leading "/" is used, in which case it will
241      * look in the root of the classpath.
242      * <p/>
243      * You can also load a resource using other class loaders. e.g. {@link Thread#getContextClassLoader()}
244      *
245      * @param configurationURL
246      *            an xml configuration available through a URL.
247      * @throws CacheException
248      * @see #create(java.net.URL)
249      * @since 1.2
250      */
251     public CacheManager(URL configurationURL) throws CacheException {
252         status = Status.STATUS_UNINITIALISED;
253         init(null, null, configurationURL, null);
254     }
255 
256     /***
257      * An ordinary constructor for CacheManager.
258      * This method does not act as a singleton. Callers must maintain a reference to it.
259      * Note that if one of the {@link #create()} methods are called, a new singleton will be created,
260      * separate from any instances created in this method.
261      *
262      * @param configurationInputStream
263      *            an xml configuration file available through an inputstream
264      * @throws CacheException
265      * @see #create(java.io.InputStream)
266      */
267     public CacheManager(InputStream configurationInputStream) throws CacheException {
268         status = Status.STATUS_UNINITIALISED;
269         init(null, null, null, configurationInputStream);
270     }
271 
272     /***
273      * Constructor.
274      *
275      * @throws CacheException
276      */
277     public CacheManager() throws CacheException {
278         // default config will be done
279         status = Status.STATUS_UNINITIALISED;
280         init(null, null, null, null);
281     }
282 
283     /***
284      * initialises the CacheManager
285      */
286     protected void init(Configuration configuration, String configurationFileName, URL configurationURL,
287             InputStream configurationInputStream) {
288         Configuration localConfiguration = configuration;
289         if (configuration == null) {
290             localConfiguration = parseConfiguration(configurationFileName, configurationURL, configurationInputStream);
291             this.configuration = localConfiguration;
292         } else {
293             this.configuration = configuration;
294         }
295 
296         if (localConfiguration.getName() != null) {
297             this.name = localConfiguration.getName();
298         }
299 
300         this.allowsDynamicCacheConfig = localConfiguration.getDynamicConfig();
301         this.terracottaClientConfiguration = localConfiguration.getTerracottaConfiguration();
302         
303         Map<String, CacheConfiguration> cacheConfigs = localConfiguration.getCacheConfigurations();
304         if (localConfiguration.getDefaultCacheConfiguration() != null
305             && localConfiguration.getDefaultCacheConfiguration().isTerracottaClustered()) {
306             terracottaClusteredInstanceFactory = TerracottaClusteredInstanceHelper.newClusteredInstanceFactory(cacheConfigs,
307                     localConfiguration.getTerracottaConfiguration());
308         } else {
309             for (CacheConfiguration config : cacheConfigs.values()) {
310                 if (config.isTerracottaClustered()) {
311                     terracottaClusteredInstanceFactory = TerracottaClusteredInstanceHelper.newClusteredInstanceFactory(cacheConfigs,
312                             localConfiguration.getTerracottaConfiguration());
313                     break;
314                 }
315             }
316         }
317         
318         if (terracottaClusteredInstanceFactory != null && this.name == null) {
319             this.name = CacheManager.DEFAULT_NAME;
320         }
321         
322         ConfigurationHelper configurationHelper = new ConfigurationHelper(this, localConfiguration);
323         configure(configurationHelper);
324         status = Status.STATUS_ALIVE;
325 
326         for (CacheManagerPeerProvider cacheManagerPeerProvider : cacheManagerPeerProviders.values()) {
327             cacheManagerPeerProvider.init();
328         }
329 
330         cacheManagerEventListenerRegistry.init();
331         addShutdownHookIfRequired();
332 
333         cacheManagerTimer = new FailSafeTimer(getName());
334         checkForUpdateIfNeeded(localConfiguration.getUpdateCheck());
335 
336         terracottaClusteredInstanceFactoryCreated.set(terracottaClusteredInstanceFactory != null);
337 
338         // do this last
339         addConfiguredCaches(configurationHelper);
340 
341         initializeMBeanRegistrationProvider(localConfiguration);
342     }
343 
344     /***
345      * Returns unique cluster-wide id for this cache-manager. Only applicable when running in "cluster" mode, e.g. when this cache-manager
346      * contains caches clustered with Terracotta. Otherwise returns blank string.
347      *
348      * @return Returns unique cluster-wide id for this cache-manager when it contains clustered caches (e.g. Terracotta clustered caches).
349      *         Otherwise returns blank string.
350      */
351     public String getClusterUUID() {
352         if (terracottaClusteredInstanceFactory != null) {
353             return getClientUUID(terracottaClusteredInstanceFactory);
354         } else {
355             return "";
356         }
357     }
358 
359     private static String getClientUUID(ClusteredInstanceFactory clusteredInstanceFactory) {
360         try {
361             Class c = clusteredInstanceFactory.getClass();
362             Method m = c.getMethod("getUUID");
363             if (m == null) {
364                 return null;
365             }
366             return (String) m.invoke(clusteredInstanceFactory);
367         } catch (Exception e) {
368             return null;
369         }
370     }
371 
372     /***
373      * Initialize the {@link MBeanRegistrationProvider} for this {@link CacheManager}
374      *
375      * @param localConfiguration
376      */
377     private void initializeMBeanRegistrationProvider(Configuration localConfiguration) {
378         mbeanRegistrationProvider = mBeanRegistrationProviderFactory.createMBeanRegistrationProvider(localConfiguration);
379         try {
380             mbeanRegistrationProvider.initialize(this, terracottaClusteredInstanceFactory);
381         } catch (MBeanRegistrationProviderException e) {
382             LOG.warn("Failed to initialize the MBeanRegistrationProvider - " + mbeanRegistrationProvider.getClass().getName(), e);
383         }
384     }
385 
386     /***
387      * Create/access the appropriate terracotta clustered store for the given cache
388      *
389      * @param cache The cache for which the Store should be created
390      * @return a new (or existing) clustered store
391      */
392     public Store createTerracottaStore(Ehcache cache) {
393         return getClusteredInstanceFactory(cache).createStore(cache);
394     }
395 
396     /***
397      * Create/access the appropriate clustered write behind queue for the given cache
398      *
399      * @param cache The cache for which the write behind queue should be created
400      * @return a new (or existing) write behind queue
401      */
402     public WriteBehind createTerracottaWriteBehind(Ehcache cache) {
403         return getClusteredInstanceFactory(cache).createWriteBehind(cache);
404     }
405 
406     /***
407      * Create/access the appropriate clustered cache event replicator for the given cache
408      *
409      * @param cache The cache for which the clustered event replicator should be created
410      * @return a new cache event replicator
411      */
412     public CacheEventListener createTerracottaEventReplicator(Ehcache cache) {
413         return getClusteredInstanceFactory(cache).createEventReplicator(cache);
414     }
415 
416     /***
417      * Create a EhcacheXAStore instance for a cache
418      * @param cache The cache the XAResource should wrap
419      * @param store The real memory store backing the cache
420      * @param bypassValidation whether versioning for checked out elements should be traced
421      * @return the configured EhcacheXAStore impl.
422      */
423     EhcacheXAStore createEhcacheXAStore(Ehcache cache, Store store, boolean bypassValidation) {
424        EhcacheXAStore ehcacheXAStore;
425         if (cache.getCacheConfiguration().isTerracottaClustered()) {
426             ehcacheXAStore = getClusteredInstanceFactory(cache).createXAStore(cache, store, bypassValidation);
427         } else {
428             // todo check oldVersionStore's config... what about listeners?!?
429             ehcacheXAStore = new EhcacheXAStoreImpl(store, MemoryOnlyStore.create((Cache) cache, null), bypassValidation);
430         }
431         return ehcacheXAStore;
432     }
433 
434     /***
435      * Return the clustered instance factory for a cache of this cache manager.
436      *
437      * @param cache the cache the clustered instance factory has to be returned for
438      * @return the clustered instance factory
439      */
440     private ClusteredInstanceFactory getClusteredInstanceFactory(Ehcache cache) {
441         if (null == terracottaClusteredInstanceFactory) {
442             // adding a cache programmatically when there is no clustered store defined in the configuration
443             // at the time this cacheManager was created
444             // synchronized so that multiple threads will wait till the store is created
445             synchronized (this) {
446                 // only 1 thread will create the store
447                 if (!terracottaClusteredInstanceFactoryCreated.getAndSet(true)) {
448                     // use the TerracottaClientConfiguration of this CacheManager to create a new ClusteredInstanceFactory
449                     Map<String, CacheConfiguration> map = new HashMap<String, CacheConfiguration>(1);
450                     map.put(cache.getName(), cache.getCacheConfiguration());
451                     terracottaClusteredInstanceFactory = TerracottaClusteredInstanceHelper.newClusteredInstanceFactory(map,
452                             terracottaClientConfiguration);
453                     try {
454                         mbeanRegistrationProvider.reinitialize(terracottaClusteredInstanceFactory);
455                     } catch (MBeanRegistrationProviderException e) {
456                         LOG.warn("Failed to initialize the MBeanRegistrationProvider - " + mbeanRegistrationProvider.getClass().getName(),
457                                 e);
458                     }
459                 }
460             }
461         }
462         return terracottaClusteredInstanceFactory;
463     }
464 
465     private void checkForUpdateIfNeeded(boolean updateCheckNeeded) {
466         try {
467             if (updateCheckNeeded) {
468                 UpdateChecker updateChecker = new UpdateChecker();
469                 cacheManagerTimer.scheduleAtFixedRate(updateChecker, DELAY_UPDATE_CHECK, EVERY_WEEK);
470             }
471         } catch (Throwable t) {
472             LOG.debug("Failed to set up update checker", t);
473         }
474     }
475 
476     /***
477      * Loads configuration, either from the supplied {@link ConfigurationHelper} or by creating a new Configuration instance
478      * from the configuration file referred to by file, inputstream or URL.
479      * <p/>
480      * Should only be called once.
481      *
482      * @param configurationFileName
483      *            the file name to parse, or null
484      * @param configurationURL
485      *            the URL to pass, or null
486      * @param configurationInputStream
487      *            , the InputStream to parse, or null
488      * @return the loaded configuration
489      * @throws CacheException
490      *             if the configuration cannot be parsed
491      */
492     private synchronized Configuration parseConfiguration(String configurationFileName, URL configurationURL,
493                                                           InputStream configurationInputStream) throws CacheException {
494         reinitialisationCheck();
495         Configuration parsedConfig;
496         if (configurationFileName != null) {
497 
498             LOG.debug("Configuring CacheManager from {}", configurationFileName);
499             parsedConfig = ConfigurationFactory.parseConfiguration(new File(configurationFileName));
500         } else if (configurationURL != null) {
501             parsedConfig = ConfigurationFactory.parseConfiguration(configurationURL);
502         } else if (configurationInputStream != null) {
503             parsedConfig = ConfigurationFactory.parseConfiguration(configurationInputStream);
504         } else {
505             LOG.debug("Configuring ehcache from classpath.");
506             parsedConfig = ConfigurationFactory.parseConfiguration();
507         }
508         return parsedConfig;
509 
510     }
511 
512     private void configure(ConfigurationHelper configurationHelper) {
513 
514         diskStorePath = configurationHelper.getDiskStorePath();
515         int cachesRequiringDiskStores = configurationHelper.numberOfCachesThatOverflowToDisk().intValue()
516                 + configurationHelper.numberOfCachesThatAreDiskPersistent().intValue();
517 
518         if (diskStorePath == null && cachesRequiringDiskStores > 0) {
519             diskStorePath = DiskStoreConfiguration.getDefaultPath();
520             LOG.warn("One or more caches require a DiskStore but there is no diskStore element configured."
521                     + " Using the default disk store path of " + DiskStoreConfiguration.getDefaultPath()
522                     + ". Please explicitly configure the diskStore element in ehcache.xml.");
523         }
524 
525         FactoryConfiguration lookupConfiguration = configuration.getTransactionManagerLookupConfiguration();
526         try {
527             Properties properties =
528                 PropertyUtil.parseProperties(lookupConfiguration.getProperties(), lookupConfiguration.getPropertySeparator());
529             Class<TransactionManagerLookup> transactionManagerLookupClass =
530                 (Class<TransactionManagerLookup>) Class.forName(lookupConfiguration.getFullyQualifiedClassPath());
531             this.transactionManagerLookup = transactionManagerLookupClass.newInstance();
532             this.transactionManagerLookup.setProperties(properties);
533         } catch (Exception e) {
534             LOG.error("could not instantiate transaction manager lookup class: {}", lookupConfiguration.getFullyQualifiedClassPath(), e);
535         }
536 
537         detectAndFixDiskStorePathConflict(configurationHelper);
538 
539         cacheManagerEventListenerRegistry.registerListener(configurationHelper.createCacheManagerEventListener());
540 
541         cacheManagerPeerListeners = configurationHelper.createCachePeerListeners();
542         for (CacheManagerPeerListener cacheManagerPeerListener : cacheManagerPeerListeners.values()) {
543             cacheManagerEventListenerRegistry.registerListener(cacheManagerPeerListener);
544         }
545 
546         detectAndFixCacheManagerPeerListenerConflict(configurationHelper);
547 
548         ALL_CACHE_MANAGERS.add(this);
549 
550         cacheManagerPeerProviders = configurationHelper.createCachePeerProviders();
551         defaultCache = configurationHelper.createDefaultCache();
552     }
553 
554     private void detectAndFixDiskStorePathConflict(ConfigurationHelper configurationHelper) {
555         if (diskStorePath == null) {
556             LOG.debug("No disk store path defined. Skipping disk store path conflict test.");
557             return;
558         }
559 
560         for (CacheManager cacheManager : ALL_CACHE_MANAGERS) {
561             if (diskStorePath.equals(cacheManager.diskStorePath)) {
562                 String newDiskStorePath = diskStorePath + File.separator + DiskStore.generateUniqueDirectory();
563                 LOG.warn("Creating a new instance of CacheManager using the diskStorePath \"" + diskStorePath + "\" which is already used"
564                         + " by an existing CacheManager.\nThe source of the configuration was "
565                         + configurationHelper.getConfigurationBean().getConfigurationSource() + ".\n"
566                         + "The diskStore path for this CacheManager will be set to " + newDiskStorePath + ".\nTo avoid this"
567                         + " warning consider using the CacheManager factory methods to create a singleton CacheManager "
568                         + "or specifying a separate ehcache configuration (ehcache.xml) for each CacheManager instance.");
569                 diskStorePath = newDiskStorePath;
570                 break;
571             }
572         }
573     }
574 
575     private void detectAndFixCacheManagerPeerListenerConflict(ConfigurationHelper configurationHelper) {
576         if (cacheManagerPeerListeners == null) {
577             return;
578         }
579         for (CacheManagerPeerListener cacheManagerPeerListener : cacheManagerPeerListeners.values()) {
580             String uniqueResourceIdentifier = cacheManagerPeerListener.getUniqueResourceIdentifier();
581             for (CacheManager cacheManager : ALL_CACHE_MANAGERS) {
582                 for (CacheManagerPeerListener otherCacheManagerPeerListener : cacheManager.cacheManagerPeerListeners.values()) {
583                     if (otherCacheManagerPeerListener == null) {
584                         continue;
585                     }
586                     String otherUniqueResourceIdentifier = otherCacheManagerPeerListener.getUniqueResourceIdentifier();
587                     if (uniqueResourceIdentifier.equals(otherUniqueResourceIdentifier)) {
588                         LOG.warn("Creating a new instance of CacheManager with a CacheManagerPeerListener which "
589                                 + "has a conflict on a resource that must be unique.\n" + "The resource is " + uniqueResourceIdentifier
590                                 + ".\n" + "Attempting automatic resolution. The source of the configuration was "
591                                 + configurationHelper.getConfigurationBean().getConfigurationSource() + ".\n"
592                                 + "To avoid this warning consider using the CacheManager factory methods to create a "
593                                 + "singleton CacheManager "
594                                 + "or specifying a separate ehcache configuration (ehcache.xml) for each CacheManager instance.");
595                         cacheManagerPeerListener.attemptResolutionOfUniqueResourceConflict();
596                         break;
597                     }
598                 }
599 
600             }
601         }
602     }
603 
604     private void addConfiguredCaches(ConfigurationHelper configurationHelper) {
605         Set unitialisedCaches = configurationHelper.createCaches();
606         for (Iterator iterator = unitialisedCaches.iterator(); iterator.hasNext();) {
607             Ehcache unitialisedCache = (Ehcache) iterator.next();
608             addCacheNoCheck(unitialisedCache, true);
609             
610             // add the cache decorators for the cache, if any
611             List<Ehcache> cacheDecorators = configurationHelper.createCacheDecorators(unitialisedCache);
612             for (Ehcache decoratedCache : cacheDecorators) {
613                 addDecoratedCache(decoratedCache);
614             }
615         }
616     }
617 
618     private void reinitialisationCheck() throws IllegalStateException {
619         if (defaultCache != null || diskStorePath != null || ehcaches.size() != 0
620                 || status.equals(Status.STATUS_SHUTDOWN)) {
621             throw new IllegalStateException("Attempt to reinitialise the CacheManager");
622         }
623     }
624 
625     /***
626      * A factory method to create a singleton CacheManager with default config, or return it if it exists.
627      * <p/>
628      * The configuration will be read, {@link Ehcache}s created and required stores initialized. When the {@link CacheManager} is no longer
629      * required, call shutdown to free resources.
630      *
631      * @return the singleton CacheManager
632      * @throws CacheException
633      *             if the CacheManager cannot be created
634      */
635     public static CacheManager create() throws CacheException {
636         if (singleton != null) {
637             return singleton;
638         }
639         synchronized (CacheManager.class) {
640             if (singleton == null) {
641                 LOG.debug("Creating new CacheManager with default config");
642                 singleton = new CacheManager();
643             } else {
644                 LOG.debug("Attempting to create an existing singleton. Existing singleton returned.");
645             }
646             return singleton;
647         }
648     }
649 
650     /***
651      * A factory method to create a singleton CacheManager with default config, or return it if it exists.
652      * <p/>
653      * This has the same effect as {@link CacheManager#create}
654      * <p/>
655      * Same as {@link #create()}
656      *
657      * @return the singleton CacheManager
658      * @throws CacheException
659      *             if the CacheManager cannot be created
660      */
661     public static CacheManager getInstance() throws CacheException {
662         return CacheManager.create();
663     }
664 
665     /***
666      * A factory method to create a singleton CacheManager with a specified configuration.
667      *
668      * @param configurationFileName
669      *            an xml file compliant with the ehcache.xsd schema
670      *            <p/>
671      *            The configuration will be read, {@link Ehcache}s created and required stores initialized. When the {@link CacheManager} is
672      *            no longer required, call shutdown to free resources.
673      */
674     public static CacheManager create(String configurationFileName) throws CacheException {
675         if (singleton != null) {
676             return singleton;
677         }
678         synchronized (CacheManager.class) {
679             if (singleton == null) {
680                 LOG.debug("Creating new CacheManager with config file: {}", configurationFileName);
681                 singleton = new CacheManager(configurationFileName);
682             }
683             return singleton;
684         }
685     }
686 
687     /***
688      * A factory method to create a singleton CacheManager from an URL.
689      * <p/>
690      * This method can be used to specify a configuration resource in the classpath other than the default of \"/ehcache.xml\": This method
691      * can be used to specify a configuration resource in the classpath other than the default of \"/ehcache.xml\":
692      *
693      * <pre>
694      * URL url = this.getClass().getResource(&quot;/ehcache-2.xml&quot;);
695      * </pre>
696      *
697      * Note that {@link Class#getResource} will look for resources in the same package unless a leading "/" is used, in which case it will
698      * look in the root of the classpath.
699      * <p/>
700      * You can also load a resource using other class loaders. e.g. {@link Thread#getContextClassLoader()}
701      *
702      * @param configurationFileURL
703      *            an URL to an xml file compliant with the ehcache.xsd schema
704      *            <p/>
705      *            The configuration will be read, {@link Ehcache}s created and required stores initialized. When the {@link CacheManager} is
706      *            no longer required, call shutdown to free resources.
707      */
708     public static CacheManager create(URL configurationFileURL) throws CacheException {
709         if (singleton != null) {
710             return singleton;
711         }
712         synchronized (CacheManager.class) {
713             if (singleton == null) {
714                 LOG.debug("Creating new CacheManager with config URL: {}", configurationFileURL);
715                 singleton = new CacheManager(configurationFileURL);
716             }
717             return singleton;
718         }
719     }
720 
721     /***
722      * A factory method to create a singleton CacheManager from a java.io.InputStream.
723      * <p/>
724      * This method makes it possible to use an inputstream for configuration. Note: it is the clients responsibility to close the
725      * inputstream.
726      * <p/>
727      *
728      * @param inputStream
729      *            InputStream of xml compliant with the ehcache.xsd schema
730      *            <p/>
731      *            The configuration will be read, {@link Ehcache}s created and required stores initialized. When the {@link CacheManager} is
732      *            no longer required, call shutdown to free resources.
733      */
734     public static CacheManager create(InputStream inputStream) 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 InputStream");
741                 singleton = new CacheManager(inputStream);
742             }
743             return singleton;
744         }
745     }
746 
747     /***
748      * A factory method to create a singleton CacheManager from a net.sf.ehcache.config.Configuration.
749      * <p/>
750      * This method makes it possible to use an inputstream for configuration. Note: it is the clients responsibility to close the
751      * inputstream.
752      * <p/>
753      *
754      * @param config
755      */
756     public static CacheManager create(Configuration config) throws CacheException {
757         if (singleton != null) {
758             return singleton;
759         }
760         synchronized (CacheManager.class) {
761             if (singleton == null) {
762                 LOG.debug("Creating new CacheManager with InputStream");
763                 singleton = new CacheManager(config);
764             }
765             return singleton;
766         }
767     }
768 
769     /***
770      * Returns a concrete implementation of Cache, it it is available in the CacheManager.
771      * Consider using getEhcache(String name) instead, which will return decorated caches that are registered.
772      * <p/>
773      * If a decorated ehcache is registered in CacheManager, an undecorated Cache with the same name will also exist.
774      * 
775      * Since version ehcache-core-2.1.0, when an {@link Ehcache} decorator is present in the CacheManager, its not necessary that a
776      * {@link Cache} instance is also present for the same name. Decorators can have different names other than the name of the cache its
777      * decorating.
778      * 
779      * @return a Cache, if an object of that type exists by that name, else null
780      * @throws IllegalStateException
781      *             if the cache is not {@link Status#STATUS_ALIVE}
782      * @see #getEhcache(String)
783      */
784     public Cache getCache(String name) throws IllegalStateException, ClassCastException {
785         checkStatus();
786         return ehcaches.get(name) instanceof Cache ? (Cache) ehcaches.get(name) : null;
787     }
788 
789     /***
790      * Gets an Ehcache
791      * <p/>
792      *
793      * @return a Cache, if an object of type Cache exists by that name, else null
794      * @throws IllegalStateException
795      *             if the cache is not {@link Status#STATUS_ALIVE}
796      */
797     public Ehcache getEhcache(String name) throws IllegalStateException {
798         checkStatus();
799         return ehcaches.get(name);
800     }
801 
802     /***
803      * Some caches might be persistent, so we want to add a shutdown hook if that is the
804      * case, so that the data and index can be written to disk.
805      */
806     private void addShutdownHookIfRequired() {
807 
808         String shutdownHookProperty = System.getProperty(ENABLE_SHUTDOWN_HOOK_PROPERTY);
809         boolean enabled = PropertyUtil.parseBoolean(shutdownHookProperty);
810         if (!enabled) {
811             return;
812         } else {
813             LOG.info("The CacheManager shutdown hook is enabled because {} is set to true.", ENABLE_SHUTDOWN_HOOK_PROPERTY);
814 
815             Thread localShutdownHook = new Thread() {
816                 @Override
817                 public void run() {
818                     synchronized (this) {
819                         if (status.equals(Status.STATUS_ALIVE)) {
820                             // clear shutdown hook reference to prevent
821                             // removeShutdownHook to remove it during shutdown
822                             shutdownHook = null;
823                             LOG.info("VM shutting down with the CacheManager still active. Calling shutdown.");
824                             shutdown();
825                         }
826                     }
827                 }
828             };
829 
830             Runtime.getRuntime().addShutdownHook(localShutdownHook);
831             shutdownHook = localShutdownHook;
832         }
833     }
834 
835     /***
836      * Remove the shutdown hook to prevent leaving orphaned CacheManagers around. This
837      * is called by {@link #shutdown()} AFTER the status has been set to shutdown.
838      */
839     private void removeShutdownHook() {
840         if (shutdownHook != null) {
841             // remove shutdown hook
842             try {
843                 Runtime.getRuntime().removeShutdownHook(shutdownHook);
844             } catch (IllegalStateException e) {
845                 // This will be thrown if the VM is shutting down. In this case
846                 // we do not need to worry about leaving references to CacheManagers lying
847                 // around and the call is ok to fail.
848                 LOG.debug("IllegalStateException due to attempt to remove a shutdown" + "hook while the VM is actually shutting down.", e);
849             }
850             shutdownHook = null;
851         }
852     }
853 
854     /***
855      * Adds a {@link Ehcache} based on the defaultCache with the given name.
856      * <p/>
857      * Memory and Disk stores will be configured for it and it will be added to the map of caches.
858      * <p/>
859      * Also notifies the CacheManagerEventListener after the cache was initialised and added.
860      * <p/>
861      * It will be created with the defaultCache attributes specified in ehcache.xml
862      *
863      * @param cacheName
864      *            the name for the cache
865      * @throws ObjectExistsException
866      *             if the cache already exists
867      * @throws CacheException
868      *             if there was an error creating the cache.
869      */
870     public void addCache(String cacheName) throws IllegalStateException, ObjectExistsException, CacheException {
871         checkStatus();
872 
873         // NPE guard
874         if (cacheName == null || cacheName.length() == 0) {
875             return;
876         }
877 
878         if (ehcaches.get(cacheName) != null) {
879             throw new ObjectExistsException("Cache " + cacheName + " already exists");
880         }
881         addCache(cloneDefaultCache(cacheName));
882     }
883 
884     /***
885      * Adds a {@link Cache} to the CacheManager.
886      * <p/>
887      * Memory and Disk stores will be configured for it and it will be added to the map of caches. Also notifies the
888      * CacheManagerEventListener after the cache was initialised and added.
889      *
890      * @param cache
891      * @throws IllegalStateException
892      *             if the cache is not {@link Status#STATUS_UNINITIALISED} before this method is called.
893      * @throws ObjectExistsException
894      *             if the cache already exists in the CacheManager
895      * @throws CacheException
896      *             if there was an error adding the cache to the CacheManager
897      */
898     public void addCache(Cache cache) throws IllegalStateException, ObjectExistsException, CacheException {
899         checkStatus();
900         if (cache == null) {
901             return;
902         }
903         addCache((Ehcache) cache);
904     }
905 
906     /***
907      * Adds an {@link Ehcache} to the CacheManager.
908      * <p/>
909      * Memory and Disk stores will be configured for it and it will be added to the map of caches. Also notifies the
910      * CacheManagerEventListener after the cache was initialised and added.
911      *
912      * @param cache
913      * @throws IllegalStateException
914      *             if the cache is not {@link Status#STATUS_UNINITIALISED} before this method is called.
915      * @throws ObjectExistsException
916      *             if the cache already exists in the CacheManager
917      * @throws CacheException
918      *             if there was an error adding the cache to the CacheManager
919      */
920     public void addCache(Ehcache cache) throws IllegalStateException, ObjectExistsException, CacheException {
921         checkStatus();
922         if (cache == null) {
923             return;
924         }
925         addCacheNoCheck(cache, true);
926     }
927 
928     /***
929      * Adds a decorated {@link Ehcache} to the CacheManager. This method neither creates the memory/disk store nor initializes the cache.
930      * It only adds the cache reference to the map of caches held by this cacheManager.
931      * <p/>
932      * It is generally required that a decorated cache, once constructed, is made available to other execution threads. The simplest way of
933      * doing this is to either add it to the cacheManager with a different name or substitute the original cache with the decorated one.
934      * This method adds the decorated cache assuming it has a different name. If another cache (decorated or not) with the same name already
935      * exists, it will throw {@link ObjectExistsException}. For replacing existing cache with another decorated cache having same name,
936      * please use {@link #replaceCacheWithDecoratedCache(Ehcache, Ehcache)}
937      * <p/>
938      * Note that any overridden Ehcache methods by the decorator will take on new behaviours without casting. Casting is only required for
939      * new methods that the decorator introduces. For more information see the well known Gang of Four Decorator pattern.
940      * 
941      * @param decoratedCache
942      * @throws ObjectExistsException
943      *             if another cache with the same name already exists.
944      */
945     public void addDecoratedCache(Ehcache decoratedCache) throws ObjectExistsException {
946         Ehcache old = ehcaches.putIfAbsent(decoratedCache.getName(), decoratedCache);
947         if (old != null) {
948             throw new ObjectExistsException("Cache " + decoratedCache.getName() + " already exists in the CacheManager");
949         }
950     }
951 
952     private Ehcache addCacheNoCheck(Ehcache cache, final boolean strict)
953         throws IllegalStateException, ObjectExistsException, CacheException {
954         
955         if (cache.getStatus() != Status.STATUS_UNINITIALISED) {
956             throw new CacheException(
957                     "Trying to add an already initialized cache. If you are adding a decorated cache, use CacheManager.addDecoratedCache(Ehcache decoratedCache) instead.");
958         }
959         
960         Ehcache ehcache = ehcaches.get(cache.getName());
961         if (ehcache != null) {
962             if (strict) {
963                 throw new ObjectExistsException("Cache " + cache.getName() + " already exists");
964             } else {
965                 return ehcache;
966             }
967         }
968         cache.setCacheManager(this);
969         cache.setDiskStorePath(diskStorePath);
970         cache.setTransactionManagerLookup(transactionManagerLookup);
971 
972         Map<String, CacheConfiguration> configMap = configuration.getCacheConfigurations();
973         if (!configMap.containsKey(cache.getName())) {
974             CacheConfiguration cacheConfig = cache.getCacheConfiguration();
975             if (cacheConfig != null) {
976                 configuration.addCache(cacheConfig);
977             }
978         }
979         
980         cache.initialise();
981         if (!allowsDynamicCacheConfig) {
982             cache.disableDynamicFeatures();
983         }
984 
985         try {
986             cache.bootstrap();
987         } catch (CacheException e) {
988             LOG.warn("Cache " + cache.getName() + "requested bootstrap but a CacheException occured. " + e.getMessage(), e);
989         }
990         ehcache = ehcaches.putIfAbsent(cache.getName(), cache);
991         if (ehcache != null) {
992             if (strict) {
993                 throw new ObjectExistsException("Cache " + cache.getName() + " already exists");
994             } else {
995                 return ehcache;
996             }
997         }
998 
999         // Don't notify initial config. The init method of each listener should take care of this.
1000         if (status.equals(Status.STATUS_ALIVE)) {
1001             cacheManagerEventListenerRegistry.notifyCacheAdded(cache.getName());
1002         }
1003 
1004         return cache;
1005     }
1006 
1007     /***
1008      * Checks whether a cache of type ehcache exists.
1009      * <p/>
1010      *
1011      * @param cacheName
1012      *            the cache name to check for
1013      * @return true if it exists
1014      * @throws IllegalStateException
1015      *             if the cache is not {@link Status#STATUS_ALIVE}
1016      */
1017     public boolean cacheExists(String cacheName) throws IllegalStateException {
1018         checkStatus();
1019         return (ehcaches.get(cacheName) != null);
1020     }
1021 
1022     /***
1023      * Removes all caches using {@link #removeCache} for each cache.
1024      */
1025     public void removalAll() {
1026         String[] cacheNames = getCacheNames();
1027         for (String cacheName : cacheNames) {
1028             removeCache(cacheName);
1029         }
1030     }
1031 
1032     /***
1033      * Remove a cache from the CacheManager. The cache is disposed of.
1034      *
1035      * @param cacheName
1036      *            the cache name
1037      * @throws IllegalStateException
1038      *             if the cache is not {@link Status#STATUS_ALIVE}
1039      */
1040     public void removeCache(String cacheName) throws IllegalStateException {
1041         checkStatus();
1042 
1043         // NPE guard
1044         if (cacheName == null || cacheName.length() == 0) {
1045             return;
1046         }
1047         Ehcache cache = ehcaches.remove(cacheName);
1048         if (cache != null && cache.getStatus().equals(Status.STATUS_ALIVE)) {
1049             cache.dispose();
1050             cacheManagerEventListenerRegistry.notifyCacheRemoved(cache.getName());
1051         }
1052     }
1053 
1054     /***
1055      * Shuts down the CacheManager.
1056      * <p/>
1057      * If the shutdown occurs on the singleton, then the singleton is removed, so that if a singleton access method is called, a new
1058      * singleton will be created.
1059      * <p/>
1060      * By default there is no shutdown hook (ehcache-1.3-beta2 and higher).
1061      * <p/>
1062      * Set the system property net.sf.ehcache.enableShutdownHook=true to turn it on.
1063      */
1064     public void shutdown() {
1065         synchronized (CacheManager.class) {
1066             if (status.equals(Status.STATUS_SHUTDOWN)) {
1067                 LOG.debug("CacheManager already shutdown");
1068                 return;
1069             }
1070             for (CacheManagerPeerProvider cacheManagerPeerProvider : cacheManagerPeerProviders.values()) {
1071                 if (cacheManagerPeerProvider != null) {
1072                     cacheManagerPeerProvider.dispose();
1073                 }
1074             }
1075 
1076             // cancel the cacheManager timer and all tasks
1077             if (cacheManagerTimer != null) {
1078                 cacheManagerTimer.cancel();
1079                 cacheManagerTimer.purge();
1080             }
1081 
1082             cacheManagerEventListenerRegistry.dispose();
1083 
1084             synchronized (CacheManager.class) {
1085                 ALL_CACHE_MANAGERS.remove(this);
1086 
1087                 for (Ehcache cache : ehcaches.values()) {
1088                     if (cache != null) {
1089                         cache.dispose();
1090                     }
1091                 }
1092                 defaultCache.dispose();
1093                 status = Status.STATUS_SHUTDOWN;
1094 
1095                 // only delete singleton if the singleton is shutting down.
1096                 if (this == singleton) {
1097                     singleton = null;
1098                 }
1099                 if (terracottaClusteredInstanceFactory != null) {
1100                     terracottaClusteredInstanceFactory.shutdown();
1101                 }
1102                 removeShutdownHook();
1103             }
1104         }
1105     }
1106 
1107     /***
1108      * Returns a list of the current cache names.
1109      *
1110      * @return an array of {@link String}s
1111      * @throws IllegalStateException
1112      *             if the cache is not {@link Status#STATUS_ALIVE}
1113      */
1114     public String[] getCacheNames() throws IllegalStateException {
1115         checkStatus();
1116         String[] list = new String[ehcaches.size()];
1117         return ehcaches.keySet().toArray(list);
1118     }
1119 
1120     /***
1121      * Checks the state of the CacheManager for legal operation
1122      */
1123     protected void checkStatus() {
1124         if (!(status.equals(Status.STATUS_ALIVE))) {
1125             if (status.equals(Status.STATUS_UNINITIALISED)) {
1126                 throw new IllegalStateException("The CacheManager has not yet been initialised. It cannot be used yet.");
1127             } else if (status.equals(Status.STATUS_SHUTDOWN)) {
1128                 throw new IllegalStateException("The CacheManager has been shut down. It can no longer be used.");
1129             }
1130         }
1131     }
1132 
1133     /***
1134      * Gets the status attribute of the Ehcache
1135      *
1136      * @return The status value from the Status enum class
1137      */
1138     public Status getStatus() {
1139         return status;
1140     }
1141 
1142     /***
1143      * Clears the contents of all caches in the CacheManager, but without
1144      * removing any caches.
1145      * <p/>
1146      * This method is not synchronized. It only guarantees to clear those elements in a cache at the time that the
1147      * {@link Ehcache#removeAll()} mehod on each cache is called.
1148      */
1149     public void clearAll() throws CacheException {
1150         String[] cacheNames = getCacheNames();
1151 
1152         LOG.debug("Clearing all caches");
1153         for (String cacheName : cacheNames) {
1154             Ehcache cache = getEhcache(cacheName);
1155             cache.removeAll();
1156         }
1157     }
1158 
1159     /***
1160      * Clears the contents of all caches in the CacheManager with a name starting with the prefix,
1161      * but without removing them.
1162      * <p/>
1163      * This method is not synchronized. It only guarantees to clear those elements in a cache at the time that the
1164      * {@link Ehcache#removeAll()} method on each cache is called.
1165      *
1166      * @param prefix
1167      *            The prefix the cache name should start with
1168      * @throws CacheException
1169      * @since 1.7.2
1170      */
1171     public void clearAllStartingWith(String prefix) throws CacheException {
1172         // NPE guard
1173         if (prefix == null || prefix.length() == 0) {
1174             return;
1175         }
1176 
1177         for (Object o : ehcaches.entrySet()) {
1178             Map.Entry entry = (Map.Entry) o;
1179             String cacheName = (String) entry.getKey();
1180             if (cacheName.startsWith(prefix)) {
1181                 if (LOG.isDebugEnabled()) {
1182                     LOG.debug("Clearing cache named '" + cacheName + "' (matches '" + prefix + "' prefix");
1183                 }
1184                 ((Ehcache) entry.getValue()).removeAll();
1185             }
1186         }
1187     }
1188 
1189     /***
1190      * Gets the <code>CacheManagerPeerProvider</code>, matching the given scheme
1191      * For distributed caches, the peer provider finds other cache managers and their caches in the same cluster
1192      *
1193      * @param scheme
1194      *            the replication scheme to use. Schemes shipped with ehcache are RMI, JGROUPS, JMS
1195      * @return the provider, or null if one does not exist
1196      */
1197     public CacheManagerPeerProvider getCacheManagerPeerProvider(String scheme) {
1198         return cacheManagerPeerProviders.get(scheme);
1199     }
1200 
1201     /***
1202      * When CacheManage is configured as part of a cluster, a CacheManagerPeerListener will
1203      * be registered in it. Use this to access the individual cache listeners
1204      *
1205      * @param scheme
1206      *            the replication scheme to use. Schemes shipped with ehcache are RMI, JGROUPS, JMS
1207      * @return the listener, or null if one does not exist
1208      */
1209     public CacheManagerPeerListener getCachePeerListener(String scheme) {
1210         return cacheManagerPeerListeners.get(scheme);
1211     }
1212 
1213     /***
1214      * Returns the composite listener. A notification sent to this listener will notify all registered
1215      * listeners.
1216      *
1217      * @return null if none
1218      * @see "getCacheManagerEventListenerRegistry"
1219      */
1220     public CacheManagerEventListener getCacheManagerEventListener() {
1221         return cacheManagerEventListenerRegistry;
1222     }
1223 
1224     /***
1225      * Same as getCacheManagerEventListenerRegistry().registerListener(cacheManagerEventListener);
1226      * Left for backward compatiblity
1227      *
1228      * @param cacheManagerEventListener
1229      *            the listener to set.
1230      * @see "getCacheManagerEventListenerRegistry"
1231      */
1232     public void setCacheManagerEventListener(CacheManagerEventListener cacheManagerEventListener) {
1233         getCacheManagerEventListenerRegistry().registerListener(cacheManagerEventListener);
1234     }
1235 
1236     /***
1237      * Gets the CacheManagerEventListenerRegistry. Add and remove listeners here.
1238      */
1239     public CacheManagerEventListenerRegistry getCacheManagerEventListenerRegistry() {
1240         return cacheManagerEventListenerRegistry;
1241     }
1242 
1243     /***
1244      * Replaces in the map of Caches managed by this CacheManager an Ehcache with a decorated version of the same
1245      * Ehcache. CacheManager can operate fully with a decorated Ehcache.
1246      * <p/>
1247      * Ehcache Decorators can be used to obtain different behaviour from an Ehcache in a very flexible way. Some examples in ehcache are:
1248      * <ol>
1249      * <li>{@link net.sf.ehcache.constructs.blocking.BlockingCache} - A cache that blocks other threads from getting a null element until
1250      * the first thread has placed a value in it.
1251      * <li>{@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache} - A BlockingCache that has the additional property of knowing how
1252      * to load its own entries.
1253      * </ol>
1254      * Many other kinds are possible.
1255      * <p/>
1256      * It is generally required that a decorated cache, once constructed, is made available to other execution threads. The simplest way of
1257      * doing this is to substitute the original cache for the decorated one here.
1258      * <p/>
1259      * Note that any overwritten Ehcache methods will take on new behaviours without casting. Casting is only required for new methods that
1260      * the decorator introduces. For more information see the well known Gang of Four Decorator pattern.
1261      *
1262      * @param ehcache
1263      * @param decoratedCache
1264      *            An implementation of Ehcache that wraps the original cache.
1265      * @throws CacheException
1266      *             if the two caches do not equal each other.
1267      */
1268     public void replaceCacheWithDecoratedCache(Ehcache ehcache, Ehcache decoratedCache) throws CacheException {
1269         if (!ehcache.equals(decoratedCache)) {
1270             throw new CacheException("Cannot replace " + decoratedCache.getName() + " It does not equal the incumbent cache.");
1271         }
1272 
1273         String cacheName = ehcache.getName();
1274         if (!ehcaches.replace(cacheName, ehcache, decoratedCache)) {
1275             if (cacheExists(cacheName)) {
1276                 throw new CacheException("Cache '" + ehcache.getName() + "' managed with this CacheManager doesn't match!");
1277             } else {
1278                 throw new CacheException("Cache '" + cacheName + "' isn't associated with this manager (anymore?)");
1279             }
1280         }
1281     }
1282 
1283     /***
1284      * Gets the name of the CacheManager. This is useful for distinguishing multiple CacheManagers
1285      *
1286      * @return the name, or the output of toString() if it is not set.
1287      * @see #toString() which uses either the name or Object.toString()
1288      */
1289     public String getName() {
1290         if (name != null) {
1291             return name;
1292         } else {
1293             return super.toString();
1294         }
1295     }
1296 
1297     /***
1298      * Indicate whether the CacheManager is named or not.
1299      *
1300      * @return True if named
1301      */
1302     public boolean isNamed() {
1303         return name != null;
1304     }
1305 
1306     /***
1307      * Sets the name of the CacheManager. This is useful for distinguishing multiple CacheManagers
1308      * in a monitoring situation.
1309      *
1310      * @param name
1311      *            a name with characters legal in a JMX ObjectName
1312      */
1313     public void setName(String name) {
1314         this.name = name;
1315         try {
1316             mbeanRegistrationProvider.reinitialize(terracottaClusteredInstanceFactory);
1317         } catch (MBeanRegistrationProviderException e) {
1318             throw new CacheException("Problem in reinitializing MBeanRegistrationProvider - "
1319                     + mbeanRegistrationProvider.getClass().getName(), e);
1320         }
1321     }
1322 
1323     /***
1324      * @return either the name of this CacheManager, or if unset, Object.toString()
1325      */
1326     @Override
1327     public String toString() {
1328         return getName();
1329     }
1330 
1331     /***
1332      * Returns the disk store path. This may be null if no caches need a DiskStore and none was configured.
1333      * The path cannot be changed after creation of the CacheManager. All caches take the disk store path
1334      * from this value.
1335      *
1336      * @return the disk store path.
1337      */
1338     public String getDiskStorePath() {
1339         return diskStorePath;
1340     }
1341 
1342     /***
1343      * Returns a {@link FailSafeTimer} associated with this {@link CacheManager}
1344      *
1345      * @return The {@link FailSafeTimer} associated with this cache manager
1346      * @since 1.7
1347      */
1348     public FailSafeTimer getTimer() {
1349         return cacheManagerTimer;
1350     }
1351 
1352     /***
1353      * Returns access to information about the cache cluster.
1354      *
1355      * @param scheme The clustering scheme to retrieve information about (such as "Terracotta")
1356      * @return Cluster API (never null, but possibly a simple single node implementation)
1357      * @throws ClusterSchemeNotAvailableException If the CacheCluster specified by scheme is not available.
1358      * @see ClusterScheme
1359      * @since 2.0
1360      */
1361     public CacheCluster getCluster(ClusterScheme scheme) throws ClusterSchemeNotAvailableException {
1362         switch (scheme) {
1363         case TERRACOTTA:
1364             if (null == terracottaClusteredInstanceFactory) {
1365                 throw new ClusterSchemeNotAvailableException(ClusterScheme.TERRACOTTA,
1366                         "Terracotta cluster scheme is not available");
1367             }
1368             return terracottaClusteredInstanceFactory.getTopology();
1369         default:
1370             return NoopCacheCluster.INSTANCE;
1371         }
1372     }
1373 
1374     /***
1375      * Returns the original configuration text for this {@link CacheManager}
1376      *
1377      * @return Returns the original configuration text for this {@link CacheManager}
1378      */
1379     public String getOriginalConfigurationText() {
1380         if (configuration.getConfigurationSource() == null) {
1381             return "Originally configured programmatically. No original configuration source text.";
1382         } else {
1383             Configuration originalConfiguration = configuration.getConfigurationSource().createConfiguration();
1384             return ConfigurationUtil.generateCacheManagerConfigurationText(originalConfiguration);
1385         }
1386     }
1387 
1388     /***
1389      * Returns the active configuration text for this {@link CacheManager}
1390      *
1391      * @return Returns the active configuration text for this {@link CacheManager}
1392      */
1393     public String getActiveConfigurationText() {
1394         return ConfigurationUtil.generateCacheManagerConfigurationText(configuration);
1395     }
1396 
1397     /***
1398      * Returns the original configuration text for the input cacheName
1399      *
1400      * @param cacheName
1401      * @return Returns the original configuration text for the input cacheName
1402      * @throws CacheException if the cache with <code>cacheName</code> does not exist in the original config
1403      */
1404     public String getOriginalConfigurationText(String cacheName) throws CacheException {
1405         if (configuration.getConfigurationSource() == null) {
1406             return "Originally configured programmatically. No original configuration source text.";
1407         } else {
1408             Configuration originalConfiguration = configuration.getConfigurationSource().createConfiguration();
1409             CacheConfiguration cacheConfiguration = originalConfiguration.getCacheConfigurations().get(cacheName);
1410             if (cacheConfiguration == null) {
1411                 throw new CacheException("Cache with name '" + cacheName + "' does not exist in the original configuration");
1412             }
1413             return ConfigurationUtil.generateCacheConfigurationText(cacheConfiguration);
1414         }
1415     }
1416 
1417     /***
1418      * Returns the active configuration text for the input cacheName
1419      *
1420      * @param cacheName
1421      * @return Returns the active configuration text for the input cacheName
1422      * @throws CacheException if the cache with <code>cacheName</code> does not exist
1423      */
1424     public String getActiveConfigurationText(String cacheName) throws CacheException {
1425         CacheConfiguration config = configuration.getCacheConfigurations().get(cacheName);
1426         if (config == null) {
1427             throw new CacheException("Cache with name '" + cacheName + "' does not exist");
1428         }
1429         return ConfigurationUtil.generateCacheConfigurationText(config);
1430     }
1431 
1432     /***
1433      * {@inheritDoc}
1434      */
1435     @Override
1436     public int hashCode() {
1437         if (name != null) {
1438             return name.hashCode();
1439         } else {
1440             return super.hashCode();
1441         }
1442     }
1443 
1444     /***
1445      * Only adds the cache to the CacheManager should not one with the same name already be present
1446      * @param cache The Ehcache to be added
1447      * @return the instance registered with the CacheManager, the cache instance passed in if it was added; or null if Ehcache is null
1448      */
1449     public Ehcache addCacheIfAbsent(final Ehcache cache) {
1450         checkStatus();
1451         return cache == null ? null : addCacheNoCheck(cache, false);
1452     }
1453 
1454     /***
1455      * Only creates and adds the cache to the CacheManager should not one with the same name already be present
1456      * @param cacheName the name of the Cache to be created
1457      * @return the Ehcache instance created and registered; null if cacheName was null or of length 0
1458      */
1459     public Ehcache addCacheIfAbsent(final String cacheName) {
1460         checkStatus();
1461 
1462         // NPE guard
1463         if (cacheName == null || cacheName.length() == 0) {
1464             return null;
1465         }
1466 
1467         Ehcache ehcache = ehcaches.get(cacheName);
1468         return ehcache != null ? ehcache : addCacheIfAbsent(cloneDefaultCache(cacheName));
1469     }
1470 
1471     private Ehcache cloneDefaultCache(final String cacheName) {
1472         Ehcache cache;
1473         try {
1474             cache = (Ehcache) defaultCache.clone();
1475         } catch (CloneNotSupportedException e) {
1476             throw new CacheException("Failure adding cache. Initial cause was " + e.getMessage(), e);
1477         }
1478         if (cache != null) {
1479             cache.setName(cacheName);
1480         }
1481         return cache;
1482     }
1483 }