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("/ehcache-2.xml");
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
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
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
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
443
444
445 synchronized (this) {
446
447 if (!terracottaClusteredInstanceFactoryCreated.getAndSet(true)) {
448
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
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("/ehcache-2.xml");
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
821
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
842 try {
843 Runtime.getRuntime().removeShutdownHook(shutdownHook);
844 } catch (IllegalStateException e) {
845
846
847
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
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
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
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
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
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
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
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 }