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