View Javadoc

1   /**
2    *  Copyright 2003-2007 Greg Luck
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  
18  package net.sf.ehcache;
19  
20  import net.sf.ehcache.config.Configuration;
21  import net.sf.ehcache.config.ConfigurationFactory;
22  import net.sf.ehcache.config.ConfigurationHelper;
23  import net.sf.ehcache.distribution.CacheManagerPeerListener;
24  import net.sf.ehcache.distribution.CacheManagerPeerProvider;
25  import net.sf.ehcache.event.CacheManagerEventListener;
26  import net.sf.ehcache.event.CacheManagerEventListenerRegistry;
27  import net.sf.ehcache.store.DiskStore;
28  import net.sf.ehcache.util.PropertyUtil;
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  
32  import java.io.File;
33  import java.io.InputStream;
34  import java.net.URL;
35  import java.util.ArrayList;
36  import java.util.Collection;
37  import java.util.Collections;
38  import java.util.HashMap;
39  import java.util.Iterator;
40  import java.util.List;
41  import java.util.Map;
42  import java.util.Set;
43  
44  /**
45   * A container for {@link Ehcache}s that maintain all aspects of their lifecycle.
46   * <p/>
47   * CacheManager is meant to have one singleton per virtual machine. Its creational methods are implemented so as to
48   * make it a singleton. The design reasons for one CacheManager per VM are:
49   * <ol>
50   * <li>The CacheManager will by default look for a resource named ehcache.xml, or failing that ehcache-failsafe.xml
51   * <li>Persistent stores write files to a directory
52   * <li>Event listeners are given cache names as arguments. They are assured the cache is referenceable through a single
53   * CacheManager.
54   * </ol>
55   * <p/>
56   *
57   * @author Greg Luck
58   * @version $Id: CacheManager.java 512 2007-07-10 09:18:45Z gregluck $
59   */
60  public class CacheManager {
61  
62      /**
63       * Keeps track of all known CacheManagers. Used to check on conflicts.
64       * CacheManagers should remove themselves from this list during shut down.
65       */
66      public static final List ALL_CACHE_MANAGERS = Collections.synchronizedList(new ArrayList());
67  
68  
69      /**
70       * System property to enable creation of a shutdown hook for CacheManager.
71       */
72      public static final String ENABLE_SHUTDOWN_HOOK_PROPERTY = "net.sf.ehcache.enableShutdownHook";
73  
74      private static final Log LOG = LogFactory.getLog(CacheManager.class.getName());
75  
76      /**
77       * The Singleton Instance.
78       */
79      private static CacheManager singleton;
80  
81      /**
82       * Caches managed by this manager.
83       */
84      protected final Map caches = new HashMap();
85  
86      /**
87       * Default cache cache.
88       */
89      private Ehcache defaultCache;
90  
91      /**
92       * The path for the directory in which disk caches are created.
93       */
94      private String diskStorePath;
95  
96      /**
97       * A name for this CacheManager to distinguish it from others.
98       */
99      private String name;
100 
101     private Status status;
102 
103     private CacheManagerPeerProvider cacheManagerPeerProvider;
104     private CacheManagerPeerListener cacheManagerPeerListener;
105     private CacheManagerEventListenerRegistry cacheManagerEventListenerRegistry = new CacheManagerEventListenerRegistry();
106 
107     /**
108      * The shutdown hook thread for CacheManager. This ensures that the CacheManager and Caches are left in a
109      * consistent state on a CTRL-C or kill.
110      * <p/>
111      * This thread must be unregistered as a shutdown hook, when the CacheManager is disposed.
112      * Otherwise the CacheManager is not GC-able.
113      * <p/>
114      * Of course kill -9 or abrupt termination will not run the shutdown hook. In this case, various
115      * sanity checks are made at start up.
116      */
117     private Thread shutdownHook;
118 
119     /**
120      * An constructor for CacheManager, which takes a configuration object, rather than one created by parsing
121      * an ehcache.xml file. This constructor gives complete control over the creation of the CacheManager.
122      * <p/>
123      * Care should be taken to ensure that, if multiple CacheManages are created, they do now overwrite each others
124      * disk store files, as would happend if two were created which used the same diskStore path.
125      * <p/>
126      * This method does not act as a singleton. Callers must maintain their own reference to it.
127      * <p/>
128      * Note that if one of the {@link #create()}  methods are called, a new singleton instance will be created,
129      * separate from any instances created in this method.
130      *
131      * @param configuration
132      * @throws CacheException
133      */
134     public CacheManager(Configuration configuration) throws CacheException {
135         status = Status.STATUS_UNINITIALISED;
136         init(configuration, null, null, null);
137     }
138 
139     /**
140      * An ordinary constructor for CacheManager.
141      * This method does not act as a singleton. Callers must maintain a reference to it.
142      * Note that if one of the {@link #create()}  methods are called, a new singleton will be created,
143      * separate from any instances created in this method.
144      *
145      * @param configurationFileName an xml configuration file available through a file name. The configuration
146      *                              {@link File} is created
147      *                              using new <code>File(configurationFileName)</code>
148      * @throws CacheException
149      * @see #create(String)
150      */
151     public CacheManager(String configurationFileName) throws CacheException {
152         status = Status.STATUS_UNINITIALISED;
153         init(null, configurationFileName, null, null);
154     }
155 
156     /**
157      * An ordinary constructor for CacheManager.
158      * This method does not act as a singleton. Callers must maintain a reference to it.
159      * Note that if one of the {@link #create()}  methods are called, a new singleton will be created,
160      * separate from any instances created in this method.
161      * <p/>
162      * This method can be used to specify a configuration resource in the classpath other
163      * than the default of \"/ehcache.xml\":
164      * <pre>
165      * URL url = this.getClass().getResource("/ehcache-2.xml");
166      * </pre>
167      * Note that {@link Class#getResource} will look for resources in the same package unless a leading "/"
168      * is used, in which case it will look in the root of the classpath.
169      * <p/>
170      * You can also load a resource using other class loaders. e.g. {@link Thread#getContextClassLoader()}
171      *
172      * @param configurationURL an xml configuration available through a URL.
173      * @throws CacheException
174      * @see #create(java.net.URL)
175      * @since 1.2
176      */
177     public CacheManager(URL configurationURL) throws CacheException {
178         status = Status.STATUS_UNINITIALISED;
179         init(null, null, configurationURL, null);
180     }
181 
182     /**
183      * An ordinary constructor for CacheManager.
184      * This method does not act as a singleton. Callers must maintain a reference to it.
185      * Note that if one of the {@link #create()}  methods are called, a new singleton will be created,
186      * separate from any instances created in this method.
187      *
188      * @param configurationInputStream an xml configuration file available through an inputstream
189      * @throws CacheException
190      * @see #create(java.io.InputStream)
191      */
192     public CacheManager(InputStream configurationInputStream) throws CacheException {
193         status = Status.STATUS_UNINITIALISED;
194         init(null, null, null, configurationInputStream);
195     }
196 
197     /**
198      * Constructor.
199      *
200      * @throws CacheException
201      */
202     public CacheManager() throws CacheException {
203         //default config will be done
204         status = Status.STATUS_UNINITIALISED;
205         init(null, null, null, null);
206     }
207 
208     private void init(Configuration configuration, String configurationFileName, URL configurationURL,
209                       InputStream configurationInputStream) {
210         Configuration localConfiguration = configuration;
211         if (configuration == null) {
212             localConfiguration = parseConfiguration(configurationFileName, configurationURL, configurationInputStream);
213         } else {
214             localConfiguration.setSource("Programmatically configured.");
215         }
216 
217         ConfigurationHelper configurationHelper = new ConfigurationHelper(this, localConfiguration);
218         configure(configurationHelper);
219         status = Status.STATUS_ALIVE;
220         if (cacheManagerPeerProvider != null) {
221             cacheManagerPeerProvider.init();
222         }
223         cacheManagerEventListenerRegistry.init();
224         addShutdownHookIfRequired();
225 
226         //do this last
227         addConfiguredCaches(configurationHelper);
228 
229     }
230 
231     /**
232      * Loads configuration, either from the supplied {@link ConfigurationHelper} or by creating a new Configuration instance
233      * from the configuration file referred to by file, inputstream or URL.
234      * <p/>
235      * Should only be called once.
236      *
237      * @param configurationFileName     the file name to parse, or null
238      * @param configurationURL          the URL to pass, or null
239      * @param configurationInputStream, the InputStream to parse, or null
240      * @return the loaded configuration
241      * @throws CacheException if the configuration cannot be parsed
242      */
243     private synchronized Configuration parseConfiguration(String configurationFileName, URL configurationURL,
244                                                           InputStream configurationInputStream) throws CacheException {
245         reinitialisationCheck();
246         Configuration configuration;
247         String configurationSource;
248         if (configurationFileName != null) {
249             if (LOG.isDebugEnabled()) {
250                 LOG.debug("Configuring CacheManager from " + configurationFileName);
251             }
252             configuration = ConfigurationFactory.parseConfiguration(new File(configurationFileName));
253             configurationSource = "file located at " + configurationFileName;
254         } else if (configurationURL != null) {
255             configuration = ConfigurationFactory.parseConfiguration(configurationURL);
256             configurationSource = "URL of " + configurationURL;
257         } else if (configurationInputStream != null) {
258             configuration = ConfigurationFactory.parseConfiguration(configurationInputStream);
259             configurationSource = "InputStream " + configurationInputStream;
260         } else {
261             if (LOG.isDebugEnabled()) {
262                 LOG.debug("Configuring ehcache from classpath.");
263             }
264             configuration = ConfigurationFactory.parseConfiguration();
265             configurationSource = "classpath";
266         }
267         configuration.setSource(configurationSource);
268         return configuration;
269 
270     }
271 
272     private void configure(ConfigurationHelper configurationHelper) {
273 
274         diskStorePath = configurationHelper.getDiskStorePath();
275         detectAndFixDiskStorePathConflict(configurationHelper);
276         cacheManagerEventListenerRegistry.registerListener(configurationHelper.createCacheManagerEventListener());
277 
278         cacheManagerPeerListener = configurationHelper.createCachePeerListener();
279         cacheManagerEventListenerRegistry.registerListener(cacheManagerPeerListener);
280         detectAndFixCacheManagerPeerListenerConflict(configurationHelper);
281 
282         ALL_CACHE_MANAGERS.add(this);
283 
284         cacheManagerPeerProvider = configurationHelper.createCachePeerProvider();
285 
286         defaultCache = configurationHelper.createDefaultCache();
287 
288     }
289 
290     private void detectAndFixDiskStorePathConflict(ConfigurationHelper configurationHelper) {
291         if (diskStorePath == null) {
292             if (LOG.isDebugEnabled()) {
293                 LOG.debug("No disk store path defined. Skipping disk store path conflict test.");
294             }
295             return;
296         }
297 
298         for (int i = 0; i < ALL_CACHE_MANAGERS.size(); i++) {
299             CacheManager cacheManager = (CacheManager) ALL_CACHE_MANAGERS.get(i);
300             if (diskStorePath.equals(cacheManager.diskStorePath)) {
301                 String newDiskStorePath = diskStorePath + File.separator + DiskStore.generateUniqueDirectory();
302                 LOG.warn("Creating a new instance of CacheManager using the diskStorePath \""
303                         + diskStorePath + "\" which is already used" +
304                         " by an existing CacheManager.\nThe source of the configuration was "
305                         + configurationHelper.getConfigurationBean().getConfigurationSource() + ".\n" +
306                         "The diskStore path for this CacheManager will be set to " + newDiskStorePath + ".\nTo avoid this" +
307                         " warning consider using the CacheManager factory methods to create a singleton CacheManager " +
308                         "or specifying a separate ehcache configuration (ehcache.xml) for each CacheManager instance.");
309                 diskStorePath = newDiskStorePath;
310                 break;
311             }
312 
313         }
314     }
315 
316     private void detectAndFixCacheManagerPeerListenerConflict(ConfigurationHelper configurationHelper) {
317         if (cacheManagerPeerListener == null) {
318             return;
319         }
320         String uniqueResourceIdentifier = cacheManagerPeerListener.getUniqueResourceIdentifier();
321         for (int i = 0; i < ALL_CACHE_MANAGERS.size(); i++) {
322             CacheManager cacheManager = (CacheManager) ALL_CACHE_MANAGERS.get(i);
323             CacheManagerPeerListener otherCacheManagerPeerListener = cacheManager.cacheManagerPeerListener;
324             if (otherCacheManagerPeerListener == null) {
325                 continue;
326             }
327             String otherUniqueResourceIdentifier = otherCacheManagerPeerListener.getUniqueResourceIdentifier();
328             if (uniqueResourceIdentifier.equals(otherUniqueResourceIdentifier)) {
329                 LOG.warn("Creating a new instance of CacheManager with a CacheManagerPeerListener which " +
330                         "has a conflict on a resource that must be unique.\n" +
331                         "The resource is " + uniqueResourceIdentifier + ".\n" +
332                         "Attempting automatic resolution. The source of the configuration was "
333                         + configurationHelper.getConfigurationBean().getConfigurationSource() + ".\n"
334                         + "To avoid this warning consider using the CacheManager factory methods to create a " +
335                         "singleton CacheManager " +
336                         "or specifying a separate ehcache configuration (ehcache.xml) for each CacheManager instance.");
337                 cacheManagerPeerListener.attemptResolutionOfUniqueResourceConflict();
338                 break;
339             }
340 
341         }
342     }
343 
344     private void addConfiguredCaches(ConfigurationHelper configurationHelper) {
345         Set unitialisedCaches = configurationHelper.createCaches();
346         for (Iterator iterator = unitialisedCaches.iterator(); iterator.hasNext();) {
347             Ehcache unitialisedCache = (Ehcache) iterator.next();
348             addCacheNoCheck(unitialisedCache);
349         }
350     }
351 
352     private void reinitialisationCheck() throws IllegalStateException {
353         if (defaultCache != null || diskStorePath != null || caches.size() != 0
354                 || status.equals(Status.STATUS_SHUTDOWN)) {
355             throw new IllegalStateException("Attempt to reinitialise the CacheManager");
356         }
357     }
358 
359     /**
360      * A factory method to create a singleton CacheManager with default config, or return it if it exists.
361      * <p/>
362      * The configuration will be read, {@link Ehcache}s created and required stores initialized.
363      * When the {@link CacheManager} is no longer required, call shutdown to free resources.
364      *
365      * @return the singleton CacheManager
366      * @throws CacheException if the CacheManager cannot be created
367      */
368     public static CacheManager create() throws CacheException {
369         synchronized (CacheManager.class) {
370             if (singleton == null) {
371                 if (LOG.isDebugEnabled()) {
372                     LOG.debug("Creating new CacheManager with default config");
373                 }
374                 singleton = new CacheManager();
375             } else {
376                 if (LOG.isDebugEnabled()) {
377                     LOG.debug("Attempting to create an existing singleton. Existing singleton returned.");
378                 }
379             }
380             return singleton;
381         }
382     }
383 
384     /**
385      * A factory method to create a singleton CacheManager with default config, or return it if it exists.
386      * <p/>
387      * This has the same effect as {@link CacheManager#create}
388      * <p/>
389      * Same as {@link #create()}
390      *
391      * @return the singleton CacheManager
392      * @throws CacheException if the CacheManager cannot be created
393      */
394     public static CacheManager getInstance() throws CacheException {
395         return CacheManager.create();
396     }
397 
398     /**
399      * A factory method to create a singleton CacheManager with a specified configuration.
400      *
401      * @param configurationFileName an xml file compliant with the ehcache.xsd schema
402      *                              <p/>
403      *                              The configuration will be read, {@link Ehcache}s created and required stores initialized.
404      *                              When the {@link CacheManager} is no longer required, call shutdown to free resources.
405      */
406     public static CacheManager create(String configurationFileName) throws CacheException {
407         synchronized (CacheManager.class) {
408             if (singleton == null) {
409                 if (LOG.isDebugEnabled()) {
410                     LOG.debug("Creating new CacheManager with config file: " + configurationFileName);
411                 }
412                 singleton = new CacheManager(configurationFileName);
413             }
414             return singleton;
415         }
416     }
417 
418     /**
419      * A factory method to create a singleton CacheManager from an URL.
420      * <p/>
421      * This method can be used to specify a configuration resource in the classpath other
422      * than the default of \"/ehcache.xml\":
423      * This method can be used to specify a configuration resource in the classpath other
424      * than the default of \"/ehcache.xml\":
425      * <pre>
426      * URL url = this.getClass().getResource("/ehcache-2.xml");
427      * </pre>
428      * Note that {@link Class#getResource} will look for resources in the same package unless a leading "/"
429      * is used, in which case it will look in the root of the classpath.
430      * <p/>
431      * You can also load a resource using other class loaders. e.g. {@link Thread#getContextClassLoader()}
432      *
433      * @param configurationFileURL an URL to an xml file compliant with the ehcache.xsd schema
434      *                             <p/>
435      *                             The configuration will be read, {@link Ehcache}s created and required stores initialized.
436      *                             When the {@link CacheManager} is no longer required, call shutdown to free resources.
437      */
438     public static CacheManager create(URL configurationFileURL) throws CacheException {
439         synchronized (CacheManager.class) {
440             if (singleton == null) {
441                 if (LOG.isDebugEnabled()) {
442                     LOG.debug("Creating new CacheManager with config URL: " + configurationFileURL);
443                 }
444                 singleton = new CacheManager(configurationFileURL);
445 
446             }
447             return singleton;
448         }
449     }
450 
451     /**
452      * A factory method to create a singleton CacheManager from a java.io.InputStream.
453      * <p/>
454      * This method makes it possible to use an inputstream for configuration.
455      * Note: it is the clients responsibility to close the inputstream.
456      * <p/>
457      *
458      * @param inputStream InputStream of xml compliant with the ehcache.xsd schema
459      *                    <p/>
460      *                    The configuration will be read, {@link Ehcache}s created and required stores initialized.
461      *                    When the {@link CacheManager} is no longer required, call shutdown to free resources.
462      */
463     public static CacheManager create(InputStream inputStream) throws CacheException {
464         synchronized (CacheManager.class) {
465             if (singleton == null) {
466                 if (LOG.isDebugEnabled()) {
467                     LOG.debug("Creating new CacheManager with InputStream");
468                 }
469                 singleton = new CacheManager(inputStream);
470             }
471             return singleton;
472         }
473     }
474 
475     /**
476      * Returns a concrete implementation of Cache.
477      * Consider using the {@link #getEhcache(String)} method which returns an interface
478      *
479      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
480      * @throws ClassCastException    is the Ehcache found is not a Cache
481      * @see #getEhcache(String)
482      */
483     public synchronized Cache getCache(String name) throws IllegalStateException, ClassCastException {
484         checkStatus();
485         return (Cache) caches.get(name);
486     }
487 
488     /**
489      * Gets an Ehcache
490      *
491      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
492      */
493     public synchronized Ehcache getEhcache(String name) throws IllegalStateException {
494         checkStatus();
495         return (Ehcache) caches.get(name);
496     }
497 
498     /**
499      * Some caches might be persistent, so we want to add a shutdown hook if that is the
500      * case, so that the data and index can be written to disk.
501      */
502     private void addShutdownHookIfRequired() {
503 
504         String shutdownHookProperty = System.getProperty(ENABLE_SHUTDOWN_HOOK_PROPERTY);
505         boolean enabled = PropertyUtil.parseBoolean(shutdownHookProperty);
506         if (!enabled) {
507             return;
508         } else {
509             LOG.info("The CacheManager shutdown hook is enabled because " + ENABLE_SHUTDOWN_HOOK_PROPERTY + " is set to true.");
510 
511             Thread localShutdownHook = new Thread() {
512                 public void run() {
513                     synchronized (this) {
514                         if (status.equals(Status.STATUS_ALIVE)) {
515                             // clear shutdown hook reference to prevent
516                             // removeShutdownHook to remove it during shutdown
517                             shutdownHook = null;
518 
519                             if (LOG.isInfoEnabled()) {
520                                 LOG.info("VM shutting down with the CacheManager still active. Calling shutdown.");
521                             }
522                             shutdown();
523                         }
524                     }
525                 }
526             };
527 
528             Runtime.getRuntime().addShutdownHook(localShutdownHook);
529             shutdownHook = localShutdownHook;
530         }
531     }
532 
533 
534     /**
535      * Remove the shutdown hook to prevent leaving orphaned CacheManagers around. This
536      * is called by {@link #shutdown()}} AFTER the status has been set to shutdown.
537      */
538     private void removeShutdownHook() {
539         if (shutdownHook != null) {
540             // remove shutdown hook
541             try {
542                 Runtime.getRuntime().removeShutdownHook(shutdownHook);
543             } catch (IllegalStateException e) {
544                 //This will be thrown if the VM is shutting down. In this case
545                 //we do not need to worry about leaving references to CacheManagers lying
546                 //around and the call is ok to fail.
547                 if (LOG.isDebugEnabled()) {
548                     LOG.debug("IllegalStateException due to attempt to remove a shutdown" +
549                             "hook while the VM is actually shutting down.", e);
550                 }
551             }
552             shutdownHook = null;
553         }
554     }
555 
556     /**
557      * Adds a {@link Ehcache} based on the defaultCache with the given name.
558      * <p/>
559      * Memory and Disk stores will be configured for it and it will be added
560      * to the map of caches.
561      * <p/>
562      * Also notifies the CacheManagerEventListener after the cache was initialised and added.
563      * <p/>
564      * It will be created with the defaultCache attributes specified in ehcache.xml
565      *
566      * @param cacheName the name for the cache
567      * @throws ObjectExistsException if the cache already exists
568      * @throws CacheException        if there was an error creating the cache.
569      */
570     public synchronized void addCache(String cacheName) throws IllegalStateException,
571             ObjectExistsException, CacheException {
572         checkStatus();
573 
574         //NPE guard
575         if (cacheName == null || cacheName.length() == 0) {
576             return;
577         }
578 
579         if (caches.get(cacheName) != null) {
580             throw new ObjectExistsException("Cache " + cacheName + " already exists");
581         }
582         Ehcache cache = null;
583         try {
584             cache = (Ehcache) defaultCache.clone();
585         } catch (CloneNotSupportedException e) {
586             throw new CacheException("Failure adding cache. Initial cause was " + e.getMessage(), e);
587         }
588         if (cache != null) {
589             cache.setName(cacheName);
590         }
591         addCache(cache);
592     }
593 
594     /**
595      * Adds a {@link Cache} to the CacheManager.
596      * <p/>
597      * Memory and Disk stores will be configured for it and it will be added to the map of caches.
598      * Also notifies the CacheManagerEventListener after the cache was initialised and added.
599      *
600      * @param cache
601      * @throws IllegalStateException if the cache is not {@link Status#STATUS_UNINITIALISED} before this method is called.
602      * @throws ObjectExistsException if the cache already exists in the CacheManager
603      * @throws CacheException        if there was an error adding the cache to the CacheManager
604      */
605     public synchronized void addCache(Cache cache) throws IllegalStateException,
606             ObjectExistsException, CacheException {
607         addCache((Ehcache) cache);
608     }
609 
610     /**
611      * Adds an {@link Ehcache} to the CacheManager.
612      * <p/>
613      * Memory and Disk stores will be configured for it and it will be added to the map of caches.
614      * Also notifies the CacheManagerEventListener after the cache was initialised and added.
615      *
616      * @param cache
617      * @throws IllegalStateException if the cache is not {@link Status#STATUS_UNINITIALISED} before this method is called.
618      * @throws ObjectExistsException if the cache already exists in the CacheManager
619      * @throws CacheException        if there was an error adding the cache to the CacheManager
620      */
621     public synchronized void addCache(Ehcache cache) throws IllegalStateException,
622             ObjectExistsException, CacheException {
623         checkStatus();
624         addCacheNoCheck(cache);
625     }
626 
627     private synchronized void addCacheNoCheck(Ehcache cache) throws IllegalStateException,
628             ObjectExistsException, CacheException {
629         if (caches.get(cache.getName()) != null) {
630             throw new ObjectExistsException("Cache " + cache.getName() + " already exists");
631         }
632         cache.setCacheManager(this);
633         cache.setDiskStorePath(diskStorePath);
634         cache.initialise();
635         try {
636             cache.bootstrap();
637         } catch (CacheException e) {
638             LOG.warn("Cache " + cache.getName() + "requested bootstrap but a CacheException occured. " + e.getMessage(), e);
639         }
640         caches.put(cache.getName(), cache);
641 
642         //Don't notify initial config. The init method of each listener should take care of this.
643         if (status.equals(Status.STATUS_ALIVE)) {
644             cacheManagerEventListenerRegistry.notifyCacheAdded(cache.getName());
645         }
646     }
647 
648     /**
649      * Checks whether a cache exists.
650      * <p/>
651      *
652      * @param cacheName the cache name to check for
653      * @return true if it exists
654      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
655      */
656     public synchronized boolean cacheExists(String cacheName) throws IllegalStateException {
657         checkStatus();
658         return (caches.get(cacheName) != null);
659     }
660 
661     /**
662      * Removes all caches using {@link #removeCache} for each cache.
663      */
664     public synchronized void removalAll() {
665         String[] cacheNames = getCacheNames();
666         for (int i = 0; i < cacheNames.length; i++) {
667             String cacheName = cacheNames[i];
668             removeCache(cacheName);
669         }
670     }
671 
672     /**
673      * Remove a cache from the CacheManager. The cache is disposed of.
674      *
675      * @param cacheName the cache name
676      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
677      */
678     public synchronized void removeCache(String cacheName) throws IllegalStateException {
679         checkStatus();
680 
681         //NPE guard
682         if (cacheName == null || cacheName.length() == 0) {
683             return;
684         }
685 
686         Ehcache cache = (Ehcache) caches.remove(cacheName);
687         if (cache != null && cache.getStatus().equals(Status.STATUS_ALIVE)) {
688             cache.dispose();
689             cacheManagerEventListenerRegistry.notifyCacheRemoved(cache.getName());
690         }
691     }
692 
693     /**
694      * Shuts down the CacheManager.
695      * <p/>
696      * If the shutdown occurs on the singleton, then the singleton is removed, so that if a singleton access method
697      * is called, a new singleton will be created.
698      * <p/>
699      * By default there is no shutdown hook (ehcache-1.3-beta2 and higher).
700      * <p/>
701      * Set the system property net.sf.ehcache.enableShutdownHook=true to turn it on.
702      */
703     public void shutdown() {
704         synchronized (CacheManager.class) {
705             if (status.equals(Status.STATUS_SHUTDOWN)) {
706                 if (LOG.isDebugEnabled()) {
707                     LOG.debug("CacheManager already shutdown");
708                 }
709                 return;
710             }
711             if (cacheManagerPeerProvider != null) {
712                 cacheManagerPeerProvider.dispose();
713             }
714 
715             cacheManagerEventListenerRegistry.dispose();
716 
717             synchronized (CacheManager.class) {
718                 ALL_CACHE_MANAGERS.remove(this);
719 
720                 Collection cacheSet = caches.values();
721                 for (Iterator iterator = cacheSet.iterator(); iterator.hasNext();) {
722                     Ehcache cache = (Ehcache) iterator.next();
723                     if (cache != null) {
724                         cache.dispose();
725                     }
726                 }
727                 status = Status.STATUS_SHUTDOWN;
728 
729                 //only delete singleton if the singleton is shutting down.
730                 if (this == singleton) {
731                     singleton = null;
732                 }
733                 removeShutdownHook();
734             }
735         }
736     }
737 
738     /**
739      * Returns a list of the current cache names.
740      *
741      * @return an array of {@link String}s
742      * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
743      */
744     public synchronized String[] getCacheNames() throws IllegalStateException {
745         checkStatus();
746         String[] list = new String[caches.size()];
747         return (String[]) caches.keySet().toArray(list);
748     }
749 
750 
751     private void checkStatus() {
752         if (!(status.equals(Status.STATUS_ALIVE))) {
753             throw new IllegalStateException("The CacheManager is not alive.");
754         }
755     }
756 
757 
758     /**
759      * Gets the status attribute of the Ehcache
760      *
761      * @return The status value from the Status enum class
762      */
763     public Status getStatus() {
764         return status;
765     }
766 
767     /**
768      * Clears  the contents of all caches in the CacheManager, but without
769      * removing any caches.
770      * <p/>
771      * This method is not synchronized. It only guarantees to clear those elements in a cache
772      * at the time that the {@link Ehcache#removeAll()} mehod  on each cache is called.
773      */
774     public void clearAll() throws CacheException {
775         String[] cacheNames = getCacheNames();
776         if (LOG.isDebugEnabled()) {
777             LOG.debug("Clearing all caches");
778         }
779         for (int i = 0; i < cacheNames.length; i++) {
780             String cacheName = cacheNames[i];
781             Ehcache cache = getEhcache(cacheName);
782             cache.removeAll();
783         }
784     }
785 
786     /**
787      * Gets the <code>CacheManagerPeerProvider</code>
788      * For distributed caches, the peer provider finds other cache managers and their caches in the same cluster
789      *
790      * @return the provider, or null if one does not exist
791      */
792     public CacheManagerPeerProvider getCachePeerProvider() {
793         return cacheManagerPeerProvider;
794     }
795 
796     /**
797      * When CacheManage is configured as part of a cluster, a CacheManagerPeerListener will
798      * be registered in it. Use this to access the individual cache listeners
799      *
800      * @return the listener, or null if one does not exist
801      */
802     public CacheManagerPeerListener getCachePeerListener() {
803         return cacheManagerPeerListener;
804     }
805 
806     /**
807      * Returns the composite listener. A notification sent to this listener will notify all registered
808      * listeners.
809      *
810      * @return null if none
811      * @see "getCacheManagerEventListenerRegistry"
812      */
813     public CacheManagerEventListener getCacheManagerEventListener() {
814         return cacheManagerEventListenerRegistry;
815     }
816 
817     /**
818      * Same as getCacheManagerEventListenerRegistry().registerListener(cacheManagerEventListener);
819      * Left for backward compatiblity
820      *
821      * @param cacheManagerEventListener the listener to set.
822      * @see "getCacheManagerEventListenerRegistry"
823      * @deprecated Use getCacheManagerEventListenerRegistry instead
824      */
825     public void setCacheManagerEventListener(CacheManagerEventListener cacheManagerEventListener) {
826         getCacheManagerEventListenerRegistry().registerListener(cacheManagerEventListener);
827     }
828 
829     /**
830      * Gets the CacheManagerEventListenerRegistry. Add and remove listeners here.
831      */
832     public CacheManagerEventListenerRegistry getCacheManagerEventListenerRegistry() {
833         return cacheManagerEventListenerRegistry;
834     }
835 
836     /**
837      * Gets the CacheManagerPeerProvider, which can be useful for programmatically adding peers. Adding peers
838      * will only be useful if the peer providers are manually provided rather than automatically discovered, otherwise
839      * they will go stale.
840      *
841      * @return the CacheManagerPeerProvider, or null if there is not one.
842      */
843     public CacheManagerPeerProvider getCacheManagerPeerProvider() {
844         return cacheManagerPeerProvider;
845     }
846 
847     /**
848      * Replaces in the map of Caches managed by this CacheManager an Ehcache with a decorated version of the same
849      * Ehcache. CacheManager can operate fully with a decorated Ehcache.
850      * <p/>
851      * Decorators can be used to obtain different behaviour from an Ehcache in a very flexible way. Some examples in
852      * ehcache are:
853      * <ol>
854      * <li>{@link net.sf.ehcache.constructs.blocking.BlockingCache} - A cache that blocks other threads from getting a null element until the first thread
855      * has placed a value in it.
856      * <li>{@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache} - A BlockingCache that has the additional
857      * property of knowing how to load its own entries.
858      * </ol>
859      * Many other kinds are possible.
860      * <p/>
861      * It is generally required that a decorated cache, once constructed, is made available to other execution threads.
862      * The simplest way of doing this is to substitute the original cache for the decorated one here.
863      * <p/>
864      * Note that any overwritten Ehcache methods will take on new behaviours without casting. Casting is only required
865      * for new methods that the decorator introduces.
866      * For more information see the well known Gang of Four Decorator pattern.
867      *
868      * @param cache
869      * @param decoratedCache An implementation of Ehcache that wraps the original cache.
870      * @throws CacheException if the two caches do not equal each other.
871      */
872     public synchronized void replaceCacheWithDecoratedCache(Ehcache cache, Ehcache decoratedCache) throws CacheException {
873 
874         if (!cache.equals(decoratedCache)) {
875             throw new CacheException("Cannot replace " + decoratedCache.getName()
876                     + " It does not equal the incumbent cache.");
877         } else {
878             caches.remove(cache.getName());
879             caches.put(cache.getName(), decoratedCache);
880         }
881 
882     }
883 
884     /**
885      * Gets the name of the CacheManager. This is useful for distinguishing multiple CacheManagers
886      *
887      * @return the name, or the output of toString() if it is not set.
888      * @see #toString() which uses either the name or Object.toString()
889      */
890     public String getName() {
891         if (name != null) {
892             return name;
893         } else {
894             return super.toString();
895         }
896     }
897 
898     /**
899      * Sets the name of the CacheManager. This is useful for distinguishing multiple CacheManagers
900      * in a monitoring situation.
901      *
902      * @param name a name with characters legal in a JMX ObjectName
903      */
904     public void setName(String name) {
905         this.name = name;
906     }
907 
908 
909     /**
910      * @return either the name of this CacheManager, or if unset, Object.toString()
911      */
912     public String toString() {
913         return getName();
914     }
915 }
916