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