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, nullnullnull);
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, nullnull);
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(&quot;/ehcache-2.xml&quot;);
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(nullnull, 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(nullnullnull, 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(nullnullnullnull);
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(thissuper.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(&quot;/ehcache-2.xml&quot;);
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(&quot;/ehcache-2.xml&quot;);
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