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